@andyqiu/codeforge 0.6.12 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,7 +48,7 @@ fallback_models:
48
48
  - **收到 discover-spec-suggest plugin 注入的 candidate-specs 提示后,必须明文跟用户确认**是否走该 spec 路径 —— 用户确认前**不允许**静默把 `spec=<slug>` 塞进派 planner/coder 的 prompt
49
49
  - **方案 review 门控**(ADR:full-chain-auto-review-gating):派 planner 拿到 `plan_id` 后,**默认自动派 reviewer 审方案**(`review_target=plan_only`,prompt 含 plan_id),按 reviewer Decision 走「Review 门控行为说明」章节流程
50
50
  - **决策 review 门控(Q3-a 范围)**:向用户询问并收到选择后,若用户选择**直接驱动「派哪个 agent 做什么任务」**的决策,在派下一棒之前先派 reviewer 审决策合理性(`review_target=decision_only`)
51
- - **fallback 补写审批记录**(ADR:apply-hard-gate + ADR:decision-token-vs-approval-verdict-layering):解析 reviewer boomerang 摘要时,若看到 `## Decision` 节首行经 parseDecision 解析为 `APPROVE`(含 `APPROVE_WITH_NOTES` 字面量归一情况)但对应审批在 `<runtimeDir>/approvals/` 下没有记录 → 立即调 `review_approval({ verdict: "APPROVE", pendingIds: [...], notes: "<摘要>", source: "codeforge-fallback" })` 补写,并通过 `tui.showToast` 提醒用户。
51
+ - **fallback 补写审批记录**(ADR:apply-hard-gate + ADR:decision-token-vs-approval-verdict-layering + ADR:review-approval-auto-covered-sha):解析 reviewer boomerang 摘要时,若看到 `## Decision` 节首行经 parseDecision 解析为 `APPROVE`(含 `APPROVE_WITH_NOTES` 字面量归一情况)但对应审批在 `<runtimeDir>/approvals/` 下没有记录 → 立即调 `review_approval({ verdict: "APPROVE", pendingIds: [...], notes: "<摘要>", source: "codeforge-fallback" })` 补写,并通过 `tui.showToast` 提醒用户。(`coveredSha` / `reviewTarget` 由 `review_approval` 工具对 `session:<sid>` id 自动补全,无需手传)
52
52
  - **session_merge 前置条件**(ADR:session-merge-approval-hard-gate):调用 `session_merge action=merge` 前**必须**确认当前 session 已通过 reviewer APPROVE(即 approval-store 已有 `session:<sid>` 或 `plan:<plan_id>` 的 APPROVE / APPROVE_WITH_NOTES 记录);若 `runMergeLoop` 报 `block_pause` 提示 approval-store 缺记录,**必须**先按 fallback 约束补写 `review_approval` 再让用户重试 `/merge`,若用户拒绝补写,转告"重派 reviewer / `/merge --force` / 改方案"三选一。
53
53
 
54
54
  **MUST NOT**
@@ -80,7 +80,7 @@ fallback_models:
80
80
  | **reviewer 报 REQUEST_CHANGES(loop = 3)** | 转告用户「reviewer 3 次仍 REQUEST_CHANGES」,问「接受 `/merge` / 手动改 / `/discard-session`」三选一 | ❌ 继续派 coder |
81
81
  | **reviewer 报 BLOCK** | 转告用户 + 建议派 planner 重设计(带原 plan_id + BLOCK 理由),等用户拍板 | ❌ 派 coder 强行绕过 BLOCK |
82
82
  | **review-fix-review 全部通过(APPROVE)** | codeforge orchestrator 自动调 `session_merge action=merge` 完成合入(ADR:codeforge-merge-permission);用户也可通过 `/merge` 命令触发 | ❌ force=true 不告知用户;❌ 派其他 sub-agent 调 session_merge action=merge(会被 guard 拦截) |
83
- | **runMergeLoop 报 `block_pause`(reviewer 摘要 APPROVE 但 approval-store 无记录,ADR:session-merge-approval-hard-gate)** | 立即按 fallback 补写约束调 `review_approval({verdict:"APPROVE", pendingIds:["session:<sid>"], source:"codeforge-fallback"})` 补写;补写后请用户重试 `/merge`;若用户拒绝补写,转告"重派 reviewer 确认调 `review_approval` / `/merge --force` 跳过 review / 改方案"三选一 | ❌ 静默忽略 block_pause;❌ 不告知用户原因直接走 force |
83
+ | **runMergeLoop 报 `block_pause`(reviewer 摘要 APPROVE 但 approval-store 无记录,ADR:session-merge-approval-hard-gate)** | 立即按 fallback 补写约束调 `review_approval({verdict:"APPROVE", pendingIds:["session:<sid>"], source:"codeforge-fallback"})` 补写(`coveredSha`/`reviewTarget` 工具自动补全,无需手传,ADR:review-approval-auto-covered-sha);补写后请用户重试 `/merge`;若用户拒绝补写,转告"重派 reviewer 确认调 `review_approval` / `/merge --force` 跳过 review / 改方案"三选一 | ❌ 静默忽略 block_pause;❌ 不告知用户原因直接走 force |
84
84
  | **coder 回报「PRE 阻断、拒绝启动」** | 转告用户阻断点 + 解除路径,等用户拍板,**不自动派下一棒** | ❌ 自动重派 coder 并强塞 `pre_ack=` |
85
85
  | 用户中途插入新需求(原 task 未结束) | 询问用户「先取消 / 等当前完 / 并行」三选一 | ❌ 默默丢弃;❌ 同时派多个不告知 |
86
86
  | **可并行任务** | 自动判断依赖,无强依赖时自动并行调度 | ❌ 串行派 N 个独立 task |
@@ -148,7 +148,7 @@ codeforge 在 session 内维护两个 loop 计数器:
148
148
 
149
149
  - `smart_search` / `repo_map` / `read` / `plan_read`:只读上下文准备;**互不依赖时必须并发 emit**
150
150
  - `task`:派 subagent(按「难度分级」选变体)
151
- - `review_approval`:仅用于 **fallback 补写审批记录** —— reviewer 漏调时,codeforge 补写并传 `source: "codeforge-fallback"`
151
+ - `review_approval`:仅用于 **fallback 补写审批记录** —— reviewer 漏调时,codeforge 补写并传 `source: "codeforge-fallback"`(`coveredSha`/`reviewTarget` 工具对 `session:<sid>` id 自动补全,无需手传;ADR:review-approval-auto-covered-sha)
152
152
  - `session_merge`:
153
153
  - `action=diff stat=true session_id=<id>`:**核查某 session worktree 相对 baseSha 的改动文件列表**(用于校验 coder 写入;优先信任 boomerang 的 diff --stat,仅在 boomerang 缺证据时调)。`stat=false` 返回完整 diff
154
154
  - `action=status session_id=<id>`:查 session worktree 绑定状态(worktree_branch 提取失败时的兜底核查)
package/bin/codeforge.mjs CHANGED
@@ -1,18 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * codeforge — opencode 零侵入扩展包的安装入口
4
+ * ADR:cli-deadcode-cleanup
4
5
  *
5
6
  * 一行命令安装:
6
7
  * npm install -g @andyqiu/codeforge
7
8
  *
8
9
  * 子命令:
9
- * install [--dry-run] [--enable-legacy-tools]
10
+ * install [--dry-run]
10
11
  * 把 CodeForge 单 bundle 注入到 opencode(写 opencode.json plugin entry)
11
12
  * uninstall 卸载(不动 opencode 自身)
12
13
  * list 探测 opencode 是否在机器上
13
14
  * version 打印版本
14
- * rollback [--target=<path>] [--dry-run]
15
- * 恢复到上一次 backup(auto_install 失败时手动救场)
16
15
  * runtime where [<path>] 打印当前项目对应的全局运行时目录
17
16
  * adr-init [--force] [--dry-run] [--write-prepare] [--no-pre-push]
18
17
  * 把 ADR 体系(hooks + scripts + 模板)下发到当前 git 项目
@@ -25,8 +24,7 @@
25
24
  */
26
25
 
27
26
  import { spawnSync } from "node:child_process"
28
- import { copyFileSync, existsSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs"
29
- import { homedir } from "node:os"
27
+ import { existsSync, readFileSync } from "node:fs"
30
28
  import * as path from "node:path"
31
29
  import * as url from "node:url"
32
30
 
@@ -94,15 +92,12 @@ function parseArgs(argv) {
94
92
 
95
93
  // ────────────────────────────────────────────────────────────────────
96
94
  // opencode installer:薄壳,统一调 install.mjs(Node ESM 跨平台)
97
- // ADR:unify-install-to-node-mjs
98
- // 自举兼容:优先找 install.mjs(v0.6+),fallback install.sh(v0.5 过渡期)
99
- // 用于 upgrade 命令:旧版 CLI 调新包时,新包里只有 install.mjs
95
+ // ADR:unify-install-to-node-mjs / ADR:cli-deadcode-cleanup
96
+ // install.sh/.ps1 已合并进 install.mjs,installer 永远走 node。
100
97
  // ────────────────────────────────────────────────────────────────────
101
98
  function resolveInstallerScript() {
102
99
  const mjsScript = path.join(REPO_ROOT, "install.mjs")
103
- if (existsSync(mjsScript)) return { script: mjsScript, useNode: true }
104
- const shScript = path.join(REPO_ROOT, "install.sh")
105
- if (existsSync(shScript)) return { script: shScript, useNode: false }
100
+ if (existsSync(mjsScript)) return { script: mjsScript }
106
101
  return null
107
102
  }
108
103
 
@@ -112,24 +107,14 @@ function installOpencode({ scope, dryRun, extraArgs, quiet = false, verbose = fa
112
107
  err(`installer 脚本不存在:${path.join(REPO_ROOT, "install.mjs")}`)
113
108
  return 2
114
109
  }
115
- const { script, useNode } = resolved
116
- let cmd, args
117
- if (useNode) {
118
- cmd = process.execPath
119
- args = [script]
120
- if (scope === "global") args.push("--global")
121
- if (dryRun) args.push("--dry-run")
122
- if (verbose) args.push("--verbose")
123
- if (extraArgs) args.push(...extraArgs)
124
- } else {
125
- // fallback:旧版包里只有 install.sh(v0.5 过渡期)
126
- cmd = "bash"
127
- args = [script]
128
- if (scope === "global") args.push("--global")
129
- if (dryRun) args.push("--dry-run")
130
- if (extraArgs) args.push(...extraArgs)
131
- }
132
- if (!quiet) log(`opencode: ${useNode ? "node" : "bash"} ${args.join(" ")}`)
110
+ const { script } = resolved
111
+ const cmd = process.execPath
112
+ const args = [script]
113
+ if (scope === "global") args.push("--global")
114
+ if (dryRun) args.push("--dry-run")
115
+ if (verbose) args.push("--verbose")
116
+ if (extraArgs) args.push(...extraArgs)
117
+ if (!quiet) log(`opencode: node ${args.join(" ")}`)
133
118
  const r = spawnSync(cmd, args, { stdio: quiet ? "pipe" : "inherit", cwd: REPO_ROOT })
134
119
  if (quiet && r.status !== 0) {
135
120
  if (r.stderr) process.stderr.write(r.stderr)
@@ -144,15 +129,11 @@ function uninstallOpencode({ scope }) {
144
129
  err(`installer 脚本不存在:${path.join(REPO_ROOT, "install.mjs")}`)
145
130
  return 2
146
131
  }
147
- const { script, useNode } = resolved
148
- if (!useNode && process.platform === "win32") {
149
- err("Windows 下旧版 install.sh 不支持,请重新安装:npm install -g @andyqiu/codeforge")
150
- return 2
151
- }
152
- const cmd = useNode ? process.execPath : "bash"
132
+ const { script } = resolved
133
+ const cmd = process.execPath
153
134
  const args = [script, "--uninstall"]
154
135
  if (scope === "global") args.push("--global")
155
- log(`opencode uninstall: ${useNode ? "node" : "bash"} ${args.join(" ")}`)
136
+ log(`opencode uninstall: node ${args.join(" ")}`)
156
137
  const r = spawnSync(cmd, args, { stdio: "inherit", cwd: REPO_ROOT })
157
138
  return r.status ?? 1
158
139
  }
@@ -175,7 +156,6 @@ function cmdInstall(args) {
175
156
  const autoSkipBuild = fromNpm
176
157
  const verbose = !!args.flags.verbose
177
158
  const extraArgs = []
178
- if (args.flags["enable-legacy-tools"]) extraArgs.push("--enable-legacy-tools")
179
159
  if (args.flags["skip-build"] || autoSkipBuild) extraArgs.push("--skip-build")
180
160
 
181
161
  // npm 场景:dist/index.js 必须已经被 prepack 打好;找不到就直接报错并提示
@@ -222,80 +202,6 @@ function cmdList() {
222
202
  return 0
223
203
  }
224
204
 
225
- // ────────────────────────────────────────────────────────────────────
226
- // 子命令:rollback(ADR-0047)—— 把最近的 .bak.<ver> 文件恢复回 index.js
227
- // ────────────────────────────────────────────────────────────────────
228
- function defaultBundlePath() {
229
- const candidates = [path.join(homedir(), ".config", "opencode", "codeforge", "index.js")]
230
- if (process.platform === "win32") {
231
- if (process.env.APPDATA) candidates.push(path.join(process.env.APPDATA, "opencode", "codeforge", "index.js"))
232
- if (process.env.LOCALAPPDATA) candidates.push(path.join(process.env.LOCALAPPDATA, "opencode", "codeforge", "index.js"))
233
- }
234
- for (const c of candidates) {
235
- if (existsSync(c)) return c
236
- }
237
- return candidates[0]
238
- }
239
-
240
- function cmdRollback(args) {
241
- const target = typeof args.flags.target === "string" ? args.flags.target : defaultBundlePath()
242
- const dryRun = !!args.flags["dry-run"] || !!args.flags.dryRun
243
-
244
- log(`CodeForge rollback —— 恢复到上一次 backup`)
245
- log(` target : ${target}`)
246
- log(` dry-run : ${dryRun}`)
247
- hr()
248
-
249
- const dir = path.dirname(target)
250
- const base = path.basename(target)
251
- if (!existsSync(dir)) {
252
- err(`目标目录不存在:${dir}`)
253
- err(`提示:先跑 codeforge install 安装一次,自动更新失败时再 rollback。`)
254
- return 1
255
- }
256
-
257
- let entries
258
- try {
259
- entries = readdirSync(dir)
260
- } catch (e) {
261
- err(`读取目录失败:${e.message}`)
262
- return 1
263
- }
264
- const prefix = `${base}.bak.`
265
- const backups = entries
266
- .filter((f) => f.startsWith(prefix))
267
- .map((f) => {
268
- const full = path.join(dir, f)
269
- let mtimeMs = 0
270
- try { mtimeMs = statSync(full).mtimeMs } catch {}
271
- return { full, name: f, mtimeMs, ver: f.substring(prefix.length) }
272
- })
273
- .sort((a, b) => b.mtimeMs - a.mtimeMs)
274
-
275
- if (backups.length === 0) {
276
- err(`找不到任何 backup(pattern: ${prefix}*)`)
277
- err(`提示:自动更新尚未执行过 / backup 已被清理。可跑 npm install -g @andyqiu/codeforge 重装。`)
278
- return 1
279
- }
280
-
281
- const latest = backups[0]
282
- log(`找到 ${backups.length} 个 backup,最新:${latest.name}`)
283
- if (dryRun) {
284
- ok(`[dry-run] 会执行:cp ${latest.full} → ${target}`)
285
- return 0
286
- }
287
-
288
- try {
289
- copyFileSync(latest.full, target)
290
- } catch (e) {
291
- err(`恢复失败:${e.message}`)
292
- return 1
293
- }
294
- ok(`已恢复 ${latest.name} → ${base}(版本:${latest.ver})`)
295
- ok(`下次启动 opencode 即可生效`)
296
- return 0
297
- }
298
-
299
205
  // ────────────────────────────────────────────────────────────────────
300
206
  // 子命令:upgrade —— 升级到 npm latest 并重新 install --global
301
207
  // ────────────────────────────────────────────────────────────────────
@@ -366,17 +272,6 @@ async function cmdUpgrade(args) {
366
272
  return 1
367
273
  }
368
274
 
369
- // npm install 成功后刷新 opencode plugin 缓存,确保重启后加载新版本
370
- try {
371
- const cacheDir = path.join(homedir(), ".cache", "opencode", "packages", "@andyqiu", "teamkit@latest")
372
- if (existsSync(cacheDir)) {
373
- rmSync(cacheDir, { recursive: true, force: true })
374
- log("已清除 opencode plugin 缓存,重启 opencode 后生效")
375
- }
376
- } catch {
377
- warn("清除 opencode 缓存失败(非致命,可手动重启 opencode)")
378
- }
379
-
380
275
  const code = installOpencode({ scope: "global", dryRun: false, extraArgs: ["--skip-build"], quiet: true })
381
276
  if (code !== 0) {
382
277
  err(`install --global 失败 (exit=${code})`)
@@ -512,7 +407,6 @@ function cmdHelp() {
512
407
  codeforge uninstall
513
408
  codeforge list
514
409
  codeforge version
515
- codeforge rollback [--target=<path>] [--dry-run] # 恢复最近 backup(auto_install 失败救场)
516
410
  codeforge upgrade|update [--dry-run] # 升级到 npm latest 并重新 install --global
517
411
  codeforge doctor [--project] # 诊断 CodeForge 安装健康状态(manifest/disk/source 三方比对)
518
412
  codeforge runtime where [<path>] # 打印当前项目的全局运行时目录
@@ -525,8 +419,6 @@ function cmdHelp() {
525
419
  参数:
526
420
  --dry-run 只打印操作,不真改
527
421
  --skip-build 跳过 npm run build(已有 dist/index.js 时增量装)
528
- --enable-legacy-tools 启用旧版 file-based tools(默认禁用,避免 zod 跨实例 bug)
529
- --target rollback 子命令:指定 index.js 路径(默认自动探测)
530
422
 
531
423
  参数(adr-init 专用):
532
424
  --force 覆盖已存在文件(覆盖前自动 .bak.<ts> 备份)
@@ -573,8 +465,6 @@ async function main() {
573
465
  case "-v":
574
466
  console.log(getVersion())
575
467
  return 0
576
- case "rollback":
577
- return cmdRollback(args)
578
468
  case "upgrade":
579
469
  case "update":
580
470
  return cmdUpgrade(args)
@@ -31,7 +31,6 @@ install:
31
31
  # 哪些目录拷贝(用户可自定义)
32
32
  copy:
33
33
  - workflows
34
- - context-templates
35
34
  # 安装目标
36
35
  targets:
37
36
  project: ".opencode"
package/codeforge.json CHANGED
@@ -152,7 +152,7 @@
152
152
  },
153
153
 
154
154
  "update": {
155
- "_doc": "自动更新检查(ADR-0047)。opencode 启动时由 plugins/update-checker.ts 后台拉 npm registry latest,auto_install=true 时自动替换 ~/.config/opencode/codeforge/index.js(下次启动生效),失败/离线静默回退到 GitHub Releases。手动回滚:codeforge rollback。",
155
+ "_doc": "自动更新检查(ADR-0047)。opencode 启动时由 plugins/update-checker.ts 后台拉 npm registry latest,auto_install=true 时自动替换 ~/.config/opencode/codeforge/index.js(下次启动生效),失败/离线静默回退到 GitHub Releases。手动降级:npm install -g @andyqiu/codeforge@<旧版本>。",
156
156
  "auto_check_enabled": true,
157
157
  "interval_hours": 6,
158
158
  "package": "@andyqiu/codeforge",
@@ -40,7 +40,7 @@ codeforge 元数据(opencode 不读,由 plugins / workflow-engine 解析)
40
40
  或单行 `;` 分隔:
41
41
 
42
42
  ```
43
- /parallel 改 README; 同步 PRD; 跑 phase4-check
43
+ /parallel 改 README; 同步 PRD; 跑测试
44
44
  ```
45
45
 
46
46
  走 workflow(`parallel-explore.yaml`):