-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconvert-opencode.cjs
More file actions
195 lines (167 loc) · 9.2 KB
/
Copy pathconvert-opencode.cjs
File metadata and controls
195 lines (167 loc) · 9.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// convert-opencode.cjs — 把 _inbox/opencode-book-main/chapters/ 整合进 src/content/opencode-book-main/
// 源已经是 MD 格式(H1「第 N 章 标题」、H2「1.1 / 1.2」),只需加 attribution + 末尾翻页
// 总产物 26 个 .md(1 overview + 22 主章 + 3 附录)
const fs = require('fs')
const path = require('path')
const ROOT = __dirname
const INBOX = path.join(ROOT, '_inbox', 'opencode-book-main')
const CHAPTERS_DIR = path.join(INBOX, 'chapters')
const OUT = path.join(ROOT, 'src', 'content', 'opencode-book-main')
const BASE = 'https://github.com/anomalyco/opencode'
// 章节清单(按 9 part 分组)
const CHAPTERS = [
{ no: '01', file: '01-overview', title: '项目概览与设计哲学', short: '项目概览', part: '第一部分:宏观认知' },
{ no: '02', file: '02-repo-structure', title: '仓库结构与模块依赖', short: '仓库结构', part: '第一部分:宏观认知' },
{ no: '03', file: '03-quick-start', title: '快速上手与开发环境', short: '快速上手', part: '第一部分:宏观认知' },
{ no: '04', file: '04-agent', title: 'Agent 架构与内置角色', short: 'Agent 架构', part: '第二部分:Agent 核心' },
{ no: '05', file: '05-session', title: 'Session 生命周期', short: 'Session 系统', part: '第二部分:Agent 核心' },
{ no: '06', file: '06-compaction', title: 'Context 压缩与 Token 管理', short: 'Context 压缩', part: '第二部分:Agent 核心' },
{ no: '07', file: '07-file-tools', title: '文件操作工具:Read / Edit / Write', short: '文件工具', part: '第三部分:工具集' },
{ no: '08', file: '08-exec-tools', title: '执行与集成工具:Bash / Task / LSP', short: '执行与集成', part: '第三部分:工具集' },
{ no: '09', file: '09-tool-registry', title: '工具注册机制与权限模型', short: '工具注册', part: '第三部分:工具集' },
{ no: '10', file: '10-skill-arch', title: 'Skill 架构与加载机制', short: 'Skill 架构', part: '第四部分:Skills 系统' },
{ no: '11', file: '11-skill-custom', title: '内置 Skill 与自定义扩展', short: 'Skill 自定义', part: '第四部分:Skills 系统' },
{ no: '12', file: '12-acp-design', title: 'ACP 协议设计与类型体系', short: 'ACP 设计', part: '第五部分:ACP 协议' },
{ no: '13', file: '13-acp-practice', title: 'ACP Agent 实战', short: 'ACP 实战', part: '第五部分:ACP 协议' },
{ no: '14', file: '14-server', title: 'HTTP Server 与 API 设计', short: 'HTTP Server', part: '第六部分:Server 与 Bus' },
{ no: '15', file: '15-bus', title: '事件总线与消息驱动', short: '事件总线', part: '第六部分:Server 与 Bus' },
{ no: '16', file: '16-worktree', title: 'Git Worktree 隔离执行', short: 'Worktree', part: '第七部分:高级功能' },
{ no: '17', file: '17-mcp', title: 'MCP 集成与扩展', short: 'MCP 集成', part: '第七部分:高级功能' },
{ no: '18', file: '18-provider', title: 'Provider 抽象与多模型支持', short: 'Provider 系统', part: '第七部分:高级功能' },
{ no: '19', file: '19-tui', title: 'TUI 终端界面', short: 'TUI', part: '第八部分:界面与生态' },
{ no: '20', file: '20-desktop-web', title: 'Desktop 与 Web 客户端', short: 'Desktop/Web', part: '第八部分:界面与生态' },
{ no: '21', file: '21-plugin', title: 'Plugin 系统与社区生态', short: 'Plugin 系统', part: '第八部分:界面与生态' },
{ no: '22', file: '22-config-deploy', title: '配置体系与生产部署', short: '配置与部署', part: '第九部分:配置与生产化' },
]
const APPENDICES = [
{ no: 'A', file: 'appendix-a-reading-path', title: '推荐阅读路径', part: '附录' },
{ no: 'B', file: 'appendix-b-tool-reference', title: '工具速查表', part: '附录' },
{ no: 'C', file: 'appendix-c-glossary', title: '术语表', part: '附录' },
]
const ALL = [...CHAPTERS, ...APPENDICES]
const ATTRIBUTION = (srcFile) =>
`> **本部分**:本节对应的 OpenCode 章节\n` +
`> **来源**:[_inbox/opencode-book-main/chapters/${srcFile}](${BASE})\n` +
`> **在线阅读**:[https://opencode-book.myhubs.dev](https://opencode-book.myhubs.dev)\n` +
`> **版本**:OpenCode v1.2.27 · **许可**:CC BY-NC-SA 4.0([本书许可](https://creativecommons.org/licenses/by-nc-sa/4.0/))\n` +
`> **阅读时间**:约 7-8 分钟`
function readFile(p) {
return fs.readFileSync(p, 'utf8')
}
function buildChapterMd(meta, body, srcFile) {
const lines = []
// body(保留 H1 在最前面)+ attribution(紧跟 H1 之后)
const bodyLines = body.trim().split('\n')
// 第 1 行是 H1
lines.push(bodyLines[0])
lines.push('')
lines.push(`> **短标题**:${meta.short}`)
lines.push(`> **本部分**:${meta.part}`)
lines.push(`> **来源**:[_inbox/opencode-book-main/chapters/${srcFile}](${BASE})`)
lines.push(`> **在线阅读**:[https://opencode-book.myhubs.dev](https://opencode-book.myhubs.dev)`)
lines.push(`> **版本**:OpenCode v1.2.27 · **许可**:CC BY-NC-SA 4.0`)
lines.push(`> **阅读时间**:约 7-8 分钟`)
lines.push('')
// 第 2 行起为正文(跳过 H1)
for (let i = 1; i < bodyLines.length; i++) {
lines.push(bodyLines[i])
}
lines.push('')
// prev/next nav
const idx = ALL.findIndex((c) => c.file === meta.file)
const prev = idx > 0 ? ALL[idx - 1] : null
const next = idx < ALL.length - 1 ? ALL[idx + 1] : null
lines.push('---')
lines.push('')
if (prev) {
lines.push(`**上一节**:[${prevLabel(prev)} ${prev.title}](${prev.file}.md)`)
} else {
lines.push(`**上一节**:[项目概览](overview.md)`)
}
if (next) {
lines.push(`**下一节**:[${nextLabel(next)} ${next.title}](${next.file}.md)`)
}
lines.push('')
return lines.join('\n')
}
function prevLabel(meta) {
if (meta.no === 'A' || meta.no === 'B' || meta.no === 'C') return `附录 ${meta.no}`
return `第 ${meta.no} 章`
}
function nextLabel(meta) {
if (meta.no === 'A' || meta.no === 'B' || meta.no === 'C') return `附录 ${meta.no}`
return `第 ${meta.no} 章`
}
function buildOverviewMd() {
// 简化 README:保留"关于本书"和"目录"两段
const readme = readFile(path.join(INBOX, 'README.md'))
const lines = []
lines.push('# OpenCode 源码解析')
lines.push('')
lines.push('> **副题**:开源 AI 编程助手深度解析——从 Agent 架构到 ACP 协议')
lines.push(`> **来源**:[_inbox/opencode-book-main/README.md](${BASE})`)
lines.push('> **在线阅读**:[https://opencode-book.myhubs.dev](https://opencode-book.myhubs.dev)')
lines.push('> **版本**:OpenCode v1.2.27 · **许可**:CC BY-NC-SA 4.0')
lines.push('')
lines.push('*OpenCode 是一个 100% 开源的 AI 编程助手(MIT 协议),用 TypeScript + Bun 构建,定位为 Claude Code 的开源替代方案。本板块是其完整中文源码解析(22 章 + 3 附录)。*')
lines.push('')
// 9 parts 目录
lines.push('---')
lines.push('')
lines.push('## 全书结构')
lines.push('')
const partGroups = {}
for (const ch of CHAPTERS) {
if (!partGroups[ch.part]) partGroups[ch.part] = []
partGroups[ch.part].push(ch)
}
for (const [part, chs] of Object.entries(partGroups)) {
lines.push(`### ${part}`)
lines.push('')
for (const ch of chs) {
lines.push(`- [第 ${ch.no} 章 ${ch.title}](${ch.file}.md)`)
}
lines.push('')
}
// 附录
lines.push('### 附录')
lines.push('')
for (const ap of APPENDICES) {
lines.push(`- [附录 ${ap.no} ${ap.title}](${ap.file}.md)`)
}
lines.push('')
lines.push('---')
lines.push('')
lines.push(`**下一节**:[第 1 章 ${CHAPTERS[0].title}](${CHAPTERS[0].file}.md)`)
lines.push('')
return lines.join('\n')
}
// ========== 主流程 ==========
function main() {
if (!fs.existsSync(OUT)) fs.mkdirSync(OUT, { recursive: true })
let written = 0
let bytes = 0
// 1. overview.md
const overviewMd = buildOverviewMd()
fs.writeFileSync(path.join(OUT, 'overview.md'), overviewMd, 'utf8')
written++
bytes += Buffer.byteLength(overviewMd, 'utf8')
console.log(` [${String(written).padStart(2, '0')}/26] overview.md (${(Buffer.byteLength(overviewMd, 'utf8') / 1024).toFixed(1)}KB)`)
// 2. 22 主章 + 3 附录
for (const meta of ALL) {
const srcPath = path.join(CHAPTERS_DIR, meta.file + '.md')
if (!fs.existsSync(srcPath)) {
console.error(` ERR: ${meta.file}.md 不存在`)
continue
}
const body = readFile(srcPath)
const chapterMd = buildChapterMd(meta, body, meta.file + '.md')
const outName = meta.file + '.md'
fs.writeFileSync(path.join(OUT, outName), chapterMd, 'utf8')
written++
bytes += Buffer.byteLength(chapterMd, 'utf8')
console.log(` [${String(written).padStart(2, '0')}/26] ${outName} (${(Buffer.byteLength(chapterMd, 'utf8') / 1024).toFixed(1)}KB)`)
}
console.log(`\nDone: wrote ${written} files, ${(bytes / 1024).toFixed(1)}KB total`)
}
main()