@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 +77 -32
- package/dist/index.js +40 -6
- package/package.json +1 -1
- package/scripts/sync-agent-models.mjs +8 -0
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
|
-
|
|
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(
|
|
296
|
-
log(` 当前版本 : ${currentVersion}`)
|
|
297
|
-
log(` dry-run : ${dryRun}`)
|
|
298
|
-
hr()
|
|
300
|
+
log(`检查版本...`)
|
|
299
301
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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(
|
|
336
|
+
log(`当前版本:${currentVersion} → 最新版本:${latestVersion}`)
|
|
313
337
|
}
|
|
314
338
|
|
|
315
339
|
hr()
|
|
316
|
-
log(
|
|
340
|
+
log(`正在升级...`)
|
|
317
341
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
330
|
-
|
|
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
|
|
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"
|
|
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.
|
|
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
|
|
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
|
@@ -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
|
/**
|