@andyqiu/codeforge 0.5.12 → 0.5.14

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.
package/bin/codeforge.mjs CHANGED
@@ -95,7 +95,7 @@ function parseArgs(argv) {
95
95
  // ────────────────────────────────────────────────────────────────────
96
96
  // opencode installer:薄壳,调现成的 install.ps1 / install.sh
97
97
  // ────────────────────────────────────────────────────────────────────
98
- function installOpencode({ scope, dryRun, extraArgs }) {
98
+ function installOpencode({ scope, dryRun, extraArgs, quiet = false }) {
99
99
  const isWin = process.platform === "win32"
100
100
  const script = isWin ? path.join(REPO_ROOT, "install.ps1") : path.join(REPO_ROOT, "install.sh")
101
101
  if (!existsSync(script)) {
@@ -116,7 +116,11 @@ function installOpencode({ scope, dryRun, extraArgs }) {
116
116
  }
117
117
  const all = [...baseArgs, ...flagArgs, ...(extraArgs ?? [])]
118
118
  log(`opencode: ${cmd} ${all.join(" ")}`)
119
- const r = spawnSync(cmd, all, { stdio: "inherit", cwd: REPO_ROOT })
119
+ const r = spawnSync(cmd, all, { stdio: quiet ? "pipe" : "inherit", cwd: REPO_ROOT })
120
+ if (quiet && r.status !== 0) {
121
+ if (r.stderr) process.stderr.write(r.stderr)
122
+ if (r.stdout) process.stderr.write(r.stdout)
123
+ }
120
124
  return r.status ?? 1
121
125
  }
122
126
 
@@ -288,46 +292,86 @@ function cmdRollback(args) {
288
292
  // ────────────────────────────────────────────────────────────────────
289
293
  // 子命令:upgrade —— 升级到 npm latest 并重新 install --global
290
294
  // ────────────────────────────────────────────────────────────────────
291
- function cmdUpgrade(args) {
295
+
296
+ async function cmdUpgrade(args) {
292
297
  const dryRun = !!args.flags["dry-run"] || !!args.flags.dryRun
293
298
  const currentVersion = getVersion()
294
299
 
295
- log(`CodeForge upgrade`)
296
- log(` 当前版本 : ${currentVersion}`)
297
- log(` dry-run : ${dryRun}`)
298
- hr()
300
+ log(`检查版本...`)
299
301
 
300
- const npmCmd = "npm"
301
- const npmArgs = ["install", "-g", "@andyqiu/codeforge@latest"]
302
- log(`步骤 1/2:${npmCmd} ${npmArgs.join(" ")}`)
303
-
304
- if (!dryRun) {
305
- const r = spawnSync(npmCmd, npmArgs, { stdio: "inherit", shell: true })
306
- if (r.status !== 0) {
307
- err(`npm install 失败 (exit=${r.status ?? 1})`)
308
- err(`提示:可手动跑 npm install -g @andyqiu/codeforge@latest`)
309
- return 1
310
- }
302
+ // 查询 npm registry 最新版本
303
+ let latestVersion = null
304
+ try {
305
+ latestVersion = await new Promise((resolve) => {
306
+ import("node:https").then((mod) => {
307
+ const https = mod.default ?? mod // node:https ESM default export
308
+ const options = {
309
+ hostname: "registry.npmjs.org",
310
+ path: "/@andyqiu%2Fcodeforge/latest",
311
+ method: "GET",
312
+ headers: { "User-Agent": "codeforge-cli" },
313
+ timeout: 5000,
314
+ }
315
+ const req = https.get(options, (res) => {
316
+ let data = ""
317
+ res.on("data", (chunk) => { data += chunk })
318
+ res.on("end", () => {
319
+ try { resolve(JSON.parse(data).version ?? null) } catch { resolve(null) }
320
+ })
321
+ })
322
+ req.on("timeout", () => { req.destroy(); resolve(null) })
323
+ req.on("error", () => resolve(null))
324
+ }).catch(() => resolve(null))
325
+ })
326
+ } catch {
327
+ latestVersion = null
328
+ }
329
+
330
+ if (latestVersion === null) {
331
+ warn(`版本检查失败,继续升级流程`)
332
+ } else if (latestVersion === currentVersion) {
333
+ ok(`当前已是最新版本 v${currentVersion},无需升级`)
334
+ return 0
311
335
  } else {
312
- log(`[dry-run] 跳过 npm install`)
336
+ log(`当前版本:${currentVersion} 最新版本:${latestVersion}`)
313
337
  }
314
338
 
315
339
  hr()
316
- log(`步骤 2/2:codeforge install --global`)
340
+ log(`正在升级...`)
317
341
 
318
- if (!dryRun) {
319
- const skipBuildFlag = process.platform === "win32" ? "-SkipBuild" : "--skip-build"
320
- const code = installOpencode({ scope: "global", dryRun: false, extraArgs: [skipBuildFlag] })
321
- if (code !== 0) {
322
- err(`install --global 失败 (exit=${code})`)
323
- return 1
324
- }
325
- } else {
326
- log(`[dry-run] 跳过 install --global`)
342
+ const npmCmd = "npm"
343
+ // 有明确版本号时用精确版本,绕过 @latest dist-tag 缓存
344
+ const installTarget = latestVersion
345
+ ? `@andyqiu/codeforge@${latestVersion}`
346
+ : "@andyqiu/codeforge@latest"
347
+ const npmArgs = ["install", "-g", installTarget]
348
+
349
+ if (dryRun) {
350
+ const displayNew = latestVersion ?? "未知(版本查询失败)"
351
+ ok(`[dry-run] 演示升级:${currentVersion} → ${displayNew}`)
352
+ ok(`[dry-run] 实际执行时将自动重启生效提示`)
353
+ return 0
327
354
  }
328
355
 
329
- hr()
330
- ok(`升级完成!运行 codeforge -v 查看新版本`)
356
+ const r = spawnSync(npmCmd, npmArgs, { stdio: "pipe", shell: true })
357
+ if (r.status !== 0) {
358
+ if (r.stderr) process.stderr.write(r.stderr)
359
+ if (r.stdout) process.stderr.write(r.stdout)
360
+ err(`npm install 失败 (exit=${r.status ?? 1})`)
361
+ err(`提示:可手动跑 npm install -g ${installTarget}`)
362
+ return 1
363
+ }
364
+
365
+ const skipBuildFlag = process.platform === "win32" ? "-SkipBuild" : "--skip-build"
366
+ const code = installOpencode({ scope: "global", dryRun: false, extraArgs: [skipBuildFlag], quiet: true })
367
+ if (code !== 0) {
368
+ err(`install --global 失败 (exit=${code})`)
369
+ return 1
370
+ }
371
+
372
+ // 升级后读取新版本(npm install 成功后本地 package.json 已更新)
373
+ const newVersion = latestVersion ?? getVersion()
374
+ ok(`升级完成:${currentVersion} → ${newVersion}`)
331
375
  ok(`重启 opencode 后新版本生效`)
332
376
  return 0
333
377
  }
@@ -437,7 +481,7 @@ function cmdHelp() {
437
481
  codeforge list
438
482
  codeforge version
439
483
  codeforge rollback [--target=<path>] [--dry-run] # 恢复最近 backup(auto_install 失败救场)
440
- codeforge upgrade [--dry-run] # 升级到 npm latest 并重新 install --global
484
+ codeforge upgrade|update [--dry-run] # 升级到 npm latest 并重新 install --global
441
485
  codeforge runtime where [<path>] # 打印当前项目的全局运行时目录
442
486
  codeforge adr-init [--force] [--dry-run] [--write-prepare] [--no-pre-push]
443
487
  # 把 ADR 校验体系(hooks + scripts + 模板)下发到当前 git 项目
@@ -502,6 +546,7 @@ async function main() {
502
546
  case "rollback":
503
547
  return cmdRollback(args)
504
548
  case "upgrade":
549
+ case "update":
505
550
  return cmdUpgrade(args)
506
551
  case "runtime":
507
552
  return cmdRuntime(args)
package/dist/index.js CHANGED
@@ -21355,23 +21355,27 @@ var RISK_PATTERNS = [
21355
21355
  },
21356
21356
  {
21357
21357
  tag: "write_secrets",
21358
+ kinds: ["bash"],
21358
21359
  re: /(\.env(?:\.\w+)?|id_[edr]sa|\.ssh\/id_|\.pem|\.p12|secret\.json)/i,
21359
- matchOn: ["command", "filePath", "path"]
21360
+ matchOn: ["command"]
21360
21361
  },
21361
21362
  {
21362
21363
  tag: "write_etc",
21363
21364
  kinds: ["bash", "edit"],
21364
- re: /(?:^|\s|"|')\/etc\//
21365
+ re: /(?:^|\s|"|')\/etc\//,
21366
+ matchOn: ["command", "filePath", "path"]
21365
21367
  },
21366
21368
  {
21367
21369
  tag: "write_usr",
21368
21370
  kinds: ["bash", "edit"],
21369
- re: /(?:^|\s|"|')\/usr\//
21371
+ re: /(?:^|\s|"|')\/usr\//,
21372
+ matchOn: ["command", "filePath", "path"]
21370
21373
  },
21371
21374
  {
21372
21375
  tag: "write_root_home",
21373
21376
  kinds: ["bash", "edit"],
21374
- re: /(?:^|\s|"|')(\/root|\/home\/root)\//
21377
+ re: /(?:^|\s|"|')(\/root|\/home\/root)\//,
21378
+ matchOn: ["command", "filePath", "path"]
21375
21379
  },
21376
21380
  {
21377
21381
  tag: "internal_url",
@@ -21774,7 +21778,7 @@ import * as zlib from "node:zlib";
21774
21778
  // lib/version-injected.ts
21775
21779
  function getInjectedVersion() {
21776
21780
  try {
21777
- const v = "0.5.12";
21781
+ const v = "0.5.14";
21778
21782
  if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
21779
21783
  return v;
21780
21784
  }
@@ -23082,8 +23086,22 @@ var _touchCache = new Map;
23082
23086
  var CLASS_B_CALLER_WHITELIST = new Set([
23083
23087
  "codeforge",
23084
23088
  "reviewer",
23089
+ "reviewer-lite",
23085
23090
  "general"
23086
23091
  ]);
23092
+ var CODEFORGE_WORKTREE_DIR_NAME = path27.join(".git", "codeforge-worktrees");
23093
+ function worktreesRoot(mainRoot) {
23094
+ return path27.join(mainRoot, CODEFORGE_WORKTREE_DIR_NAME);
23095
+ }
23096
+ function isInsideAnyWorktreeDir(absPath, mainRoot) {
23097
+ if (!path27.isAbsolute(absPath))
23098
+ return false;
23099
+ const root = worktreesRoot(mainRoot);
23100
+ if (absPath === root)
23101
+ return false;
23102
+ const prefix = root.endsWith(path27.sep) ? root : root + path27.sep;
23103
+ return absPath.startsWith(prefix);
23104
+ }
23087
23105
  function rewritePath(value, mainRoot, worktreeRoot) {
23088
23106
  if (!value)
23089
23107
  return null;
@@ -23092,6 +23110,9 @@ function rewritePath(value, mainRoot, worktreeRoot) {
23092
23110
  if (resolved === worktreeRoot || resolved.startsWith(wtPrefix2)) {
23093
23111
  return null;
23094
23112
  }
23113
+ if (isInsideAnyWorktreeDir(resolved, mainRoot)) {
23114
+ return null;
23115
+ }
23095
23116
  if (resolved === mainRoot)
23096
23117
  return worktreeRoot;
23097
23118
  const prefix = mainRoot.endsWith("/") ? mainRoot : mainRoot + "/";
@@ -23121,7 +23142,20 @@ function commandContainsMainRootExcludingWorktree(command, mainRoot, worktreePat
23121
23142
  return true;
23122
23143
  }
23123
23144
  }
23124
- const sanitized = command.split(worktreePath).join("");
23145
+ const wtRoot = worktreesRoot(mainRoot);
23146
+ const wtRootPrefix = wtRoot + path27.sep;
23147
+ const escapedWtRootPrefix = escapeRegex2(wtRootPrefix);
23148
+ const wtPathPattern = escapedWtRootPrefix + `[^\\s'"\\x60)]*`;
23149
+ const allWorktreePathsReForEscape = new RegExp(wtPathPattern, "g");
23150
+ const allWorktreePathsReForReplace = new RegExp(wtPathPattern, "g");
23151
+ for (const match of command.matchAll(allWorktreePathsReForEscape)) {
23152
+ const matchedPath = match[0];
23153
+ const tail = matchedPath.slice(wtRootPrefix.length);
23154
+ if (/(?:^|[/\\])\.\.(?:[/\\]|$)/.test(tail)) {
23155
+ return true;
23156
+ }
23157
+ }
23158
+ const sanitized = command.split(worktreePath).join("").replace(allWorktreePathsReForReplace, "");
23125
23159
  return commandContainsMainRoot(sanitized, mainRoot);
23126
23160
  }
23127
23161
  function detectBashWriteIntent(command, mainRoot) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andyqiu/codeforge",
3
- "version": "0.5.12",
3
+ "version": "0.5.14",
4
4
  "description": "CodeForge — opencode 的零侵入扩展包",
5
5
  "type": "module",
6
6
  "private": false,
@@ -25,6 +25,7 @@
25
25
  import { promises as fs } from "node:fs"
26
26
  import * as path from "node:path"
27
27
  import * as url from "node:url"
28
+ import { syncToGlobalOpencode } from "./dev-sync.mjs"
28
29
 
29
30
  const args = new Set(process.argv.slice(2))
30
31
  const CHECK = args.has("--check")
@@ -99,6 +100,13 @@ async function main() {
99
100
  process.exit(1)
100
101
  }
101
102
  }
103
+
104
+ // 写盘完成后,把更新后的 agents/*.md 同步到 ~/.config/opencode/agents/
105
+ // 仅在非 DRY/CHECK 模式且有 .codeforge/.dev-marker 时生效(dev-sync.mjs 内部检查 marker)
106
+ if (!DRY && summary.changed.length > 0) {
107
+ console.log("[sync-models] 同步已更新 agents/*.md -> 全局 opencode (ADR:dev-sync-to-global)")
108
+ await syncToGlobalOpencode({ projectRoot: ROOT, dryRun: false })
109
+ }
102
110
  }
103
111
 
104
112
  /**