@andyqiu/codeforge 0.3.10 → 0.3.12
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/README.md +150 -5
- package/agents/codeforge.md +40 -5
- package/agents/coder-deep.md +96 -0
- package/agents/coder-quick.md +95 -0
- package/agents/coder.md +8 -4
- package/agents/planner.md +13 -8
- package/agents/reviewer.md +4 -0
- package/assets/adr-init/.github/pull_request_template.md +22 -0
- package/assets/adr-init/docs/adr/README.md +105 -0
- package/assets/adr-init/docs/adr/template.md +83 -0
- package/assets/adr-init/githooks/pre-commit.sh +46 -0
- package/assets/adr-init/githooks/pre-push.sh +23 -0
- package/assets/adr-init/scripts/adr-check.mjs +428 -0
- package/assets/adr-init/scripts/adr-index-sync.mjs +151 -0
- package/bin/codeforge.mjs +96 -3
- package/codeforge.json +28 -2
- package/commands/adr-init.md +67 -0
- package/commands/deep.md +87 -0
- package/commands/quick.md +92 -0
- package/dist/adr-init.js +207 -0
- package/dist/index.js +2232 -654
- package/install.sh +8 -0
- package/package.json +16 -5
- package/schemas/codeforge.schema.json +230 -224
- package/scripts/sync-agent-models.mjs +22 -3
- package/workflows/bugfix.yaml +3 -3
- package/workflows/feature-dev.yaml +3 -3
- package/workflows/parallel-explore.yaml +1 -1
package/dist/adr-init.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// lib/adr-init.ts
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { existsSync, promises as fsp } from "node:fs";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import * as url from "node:url";
|
|
6
|
+
function resolveAssetsRoot() {
|
|
7
|
+
const here = path.dirname(url.fileURLToPath(import.meta.url));
|
|
8
|
+
let dir = here;
|
|
9
|
+
for (let i = 0;i < 6; i++) {
|
|
10
|
+
if (existsSync(path.join(dir, "package.json")) && existsSync(path.join(dir, "assets", "adr-init"))) {
|
|
11
|
+
return path.join(dir, "assets", "adr-init");
|
|
12
|
+
}
|
|
13
|
+
const parent = path.dirname(dir);
|
|
14
|
+
if (parent === dir)
|
|
15
|
+
break;
|
|
16
|
+
dir = parent;
|
|
17
|
+
}
|
|
18
|
+
throw new Error(`assets/adr-init/ 未找到(从 ${here} 上溯 6 层);` + `如果是 npm 安装请检查 package.json "files" 字段是否包含 "assets/"`);
|
|
19
|
+
}
|
|
20
|
+
function isGitRepo(cwd) {
|
|
21
|
+
try {
|
|
22
|
+
const r = spawnSync("git", ["rev-parse", "--show-toplevel"], {
|
|
23
|
+
cwd,
|
|
24
|
+
stdio: "pipe",
|
|
25
|
+
encoding: "utf8"
|
|
26
|
+
});
|
|
27
|
+
return r.status === 0;
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function runGitConfigHooksPath(cwd) {
|
|
33
|
+
try {
|
|
34
|
+
const r = spawnSync("git", ["config", "core.hooksPath", ".githooks"], {
|
|
35
|
+
cwd,
|
|
36
|
+
stdio: "pipe",
|
|
37
|
+
encoding: "utf8"
|
|
38
|
+
});
|
|
39
|
+
if (r.status === 0)
|
|
40
|
+
return { ok: true };
|
|
41
|
+
return { ok: false, error: (r.stderr ?? "").trim() || `exit=${r.status}` };
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function runAdrInit(opts = {}) {
|
|
47
|
+
const cwd = path.resolve(opts.cwd ?? process.cwd());
|
|
48
|
+
const force = !!opts.force;
|
|
49
|
+
const dryRun = !!opts.dryRun;
|
|
50
|
+
const writePrepare = !!opts.writePrepare;
|
|
51
|
+
const installPrePush = opts.installPrePush !== false;
|
|
52
|
+
const result = {
|
|
53
|
+
ok: true,
|
|
54
|
+
wrote: [],
|
|
55
|
+
skipped: [],
|
|
56
|
+
backedUp: [],
|
|
57
|
+
suggestions: [],
|
|
58
|
+
warnings: [],
|
|
59
|
+
dryRun
|
|
60
|
+
};
|
|
61
|
+
if (!isGitRepo(cwd)) {
|
|
62
|
+
result.ok = false;
|
|
63
|
+
result.reason = "not_git_repo";
|
|
64
|
+
result.warnings.push(`${cwd} 不是 git 仓库;adr-init 需要 git 才能下发 hooks`);
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
let assetsRoot;
|
|
68
|
+
try {
|
|
69
|
+
assetsRoot = resolveAssetsRoot();
|
|
70
|
+
} catch (e) {
|
|
71
|
+
result.ok = false;
|
|
72
|
+
result.reason = "assets_not_found";
|
|
73
|
+
result.warnings.push(e instanceof Error ? e.message : String(e));
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
77
|
+
const plan = [
|
|
78
|
+
{ src: "scripts/adr-check.mjs", dst: "scripts/adr-check.mjs" },
|
|
79
|
+
{ src: "scripts/adr-index-sync.mjs", dst: "scripts/adr-index-sync.mjs" },
|
|
80
|
+
{ src: "docs/adr/README.md", dst: "docs/adr/README.md" },
|
|
81
|
+
{ src: "docs/adr/template.md", dst: "docs/adr/template.md" },
|
|
82
|
+
{
|
|
83
|
+
src: ".github/pull_request_template.md",
|
|
84
|
+
dst: ".github/pull_request_template.md"
|
|
85
|
+
},
|
|
86
|
+
{ src: "githooks/pre-commit.sh", dst: ".githooks/pre-commit", chmod: 493 }
|
|
87
|
+
];
|
|
88
|
+
if (installPrePush) {
|
|
89
|
+
plan.push({
|
|
90
|
+
src: "githooks/pre-push.sh",
|
|
91
|
+
dst: ".githooks/pre-push",
|
|
92
|
+
chmod: 493
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
for (const item of plan) {
|
|
96
|
+
const srcAbs = path.join(assetsRoot, item.src);
|
|
97
|
+
const dstAbs = path.join(cwd, item.dst);
|
|
98
|
+
if (!existsSync(srcAbs)) {
|
|
99
|
+
result.warnings.push(`资产缺失:${item.src}(跳过 ${item.dst})`);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (existsSync(dstAbs)) {
|
|
103
|
+
if (!force) {
|
|
104
|
+
result.skipped.push(item.dst);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const bakRel = `${item.dst}.bak.${ts}`;
|
|
108
|
+
if (!dryRun) {
|
|
109
|
+
try {
|
|
110
|
+
await fsp.copyFile(dstAbs, path.join(cwd, bakRel));
|
|
111
|
+
} catch (e) {
|
|
112
|
+
result.ok = false;
|
|
113
|
+
result.reason = "io_error";
|
|
114
|
+
result.warnings.push(`备份 ${item.dst} 失败:${e instanceof Error ? e.message : String(e)}`);
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
result.backedUp.push(bakRel);
|
|
119
|
+
}
|
|
120
|
+
if (!dryRun) {
|
|
121
|
+
try {
|
|
122
|
+
await fsp.mkdir(path.dirname(dstAbs), { recursive: true });
|
|
123
|
+
await fsp.copyFile(srcAbs, dstAbs);
|
|
124
|
+
if (item.chmod !== undefined) {
|
|
125
|
+
try {
|
|
126
|
+
await fsp.chmod(dstAbs, item.chmod);
|
|
127
|
+
} catch (e) {
|
|
128
|
+
if (process.platform !== "win32") {
|
|
129
|
+
result.warnings.push(`chmod ${item.dst} 失败:${e instanceof Error ? e.message : String(e)}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch (e) {
|
|
134
|
+
result.ok = false;
|
|
135
|
+
result.reason = "io_error";
|
|
136
|
+
result.warnings.push(`写入 ${item.dst} 失败:${e instanceof Error ? e.message : String(e)}`);
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
result.wrote.push(item.dst);
|
|
141
|
+
}
|
|
142
|
+
if (!dryRun) {
|
|
143
|
+
const r = runGitConfigHooksPath(cwd);
|
|
144
|
+
if (r.ok) {
|
|
145
|
+
result.suggestions.push("已运行:git config core.hooksPath .githooks(hooks 已启用)");
|
|
146
|
+
} else {
|
|
147
|
+
result.warnings.push(`自动启用 hooks 失败:${r.error ?? "未知错误"};请手动执行 git config core.hooksPath .githooks`);
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
result.suggestions.push("[dry-run] 将运行:git config core.hooksPath .githooks");
|
|
151
|
+
}
|
|
152
|
+
const pkgPath = path.join(cwd, "package.json");
|
|
153
|
+
const isNpm = existsSync(pkgPath);
|
|
154
|
+
if (isNpm) {
|
|
155
|
+
if (writePrepare) {
|
|
156
|
+
try {
|
|
157
|
+
const raw = await fsp.readFile(pkgPath, "utf8");
|
|
158
|
+
const pkg = JSON.parse(raw);
|
|
159
|
+
const existing = (pkg.scripts && typeof pkg.scripts.prepare === "string" ? pkg.scripts.prepare : "") ?? "";
|
|
160
|
+
const target = "git config core.hooksPath .githooks";
|
|
161
|
+
if (existing.includes(target)) {
|
|
162
|
+
result.suggestions.push(`package.json scripts.prepare 已含目标命令,无需改动`);
|
|
163
|
+
} else {
|
|
164
|
+
const bakRel = `package.json.bak.${ts}`;
|
|
165
|
+
if (!dryRun) {
|
|
166
|
+
try {
|
|
167
|
+
await fsp.copyFile(pkgPath, path.join(cwd, bakRel));
|
|
168
|
+
} catch (e) {
|
|
169
|
+
result.warnings.push(`备份 package.json 失败:${e instanceof Error ? e.message : String(e)}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
result.backedUp.push(bakRel);
|
|
173
|
+
if (existing.includes("husky")) {
|
|
174
|
+
result.warnings.push(`package.json scripts.prepare 已含 husky,仍合并写入;如有问题可回滚 ${bakRel}`);
|
|
175
|
+
}
|
|
176
|
+
const merged = existing ? `${existing} && ${target}` : target;
|
|
177
|
+
pkg.scripts = { ...pkg.scripts ?? {}, prepare: merged };
|
|
178
|
+
if (!dryRun) {
|
|
179
|
+
try {
|
|
180
|
+
await fsp.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + `
|
|
181
|
+
`, "utf8");
|
|
182
|
+
} catch (e) {
|
|
183
|
+
result.ok = false;
|
|
184
|
+
result.reason = "io_error";
|
|
185
|
+
result.warnings.push(`写入 package.json 失败:${e instanceof Error ? e.message : String(e)}`);
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
result.wrote.push("package.json");
|
|
190
|
+
}
|
|
191
|
+
} catch (e) {
|
|
192
|
+
result.warnings.push(`读取 package.json 失败:${e instanceof Error ? e.message : String(e)}`);
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
result.suggestions.push(`(可选) 在 package.json scripts.prepare 追加:"git config core.hooksPath .githooks",` + `或下次跑 codeforge adr-init --write-prepare 自动合并(写前自动 backup package.json)`);
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
result.suggestions.push("非 npm 项目:请在 README / AGENTS.md 提醒首次 clone 后跑 git config core.hooksPath .githooks");
|
|
199
|
+
}
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
var adr_init_default = { runAdrInit, resolveAssetsRoot };
|
|
203
|
+
export {
|
|
204
|
+
runAdrInit,
|
|
205
|
+
resolveAssetsRoot,
|
|
206
|
+
adr_init_default as default
|
|
207
|
+
};
|