everything-claude-code 1.4.3
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/LICENSE +21 -0
- package/README.md +739 -0
- package/README.zh-CN.md +523 -0
- package/crates/ecc-kernel/Cargo.lock +160 -0
- package/crates/ecc-kernel/Cargo.toml +15 -0
- package/crates/ecc-kernel/src/main.rs +710 -0
- package/docs/ecc.md +117 -0
- package/package.json +45 -0
- package/packs/blueprint.json +8 -0
- package/packs/forge.json +16 -0
- package/packs/instinct.json +16 -0
- package/packs/orchestra.json +15 -0
- package/packs/proof.json +8 -0
- package/packs/sentinel.json +8 -0
- package/prompts/ecc/patch.md +25 -0
- package/prompts/ecc/plan.md +28 -0
- package/schemas/ecc.apply.schema.json +35 -0
- package/schemas/ecc.config.schema.json +37 -0
- package/schemas/ecc.lock.schema.json +34 -0
- package/schemas/ecc.patch.schema.json +25 -0
- package/schemas/ecc.plan.schema.json +32 -0
- package/schemas/ecc.run.schema.json +67 -0
- package/schemas/ecc.verify.schema.json +27 -0
- package/schemas/hooks.schema.json +81 -0
- package/schemas/package-manager.schema.json +17 -0
- package/schemas/plugin.schema.json +13 -0
- package/scripts/ecc/catalog.js +82 -0
- package/scripts/ecc/config.js +43 -0
- package/scripts/ecc/diff.js +113 -0
- package/scripts/ecc/exec.js +121 -0
- package/scripts/ecc/fixtures/basic/patches/impl-core.diff +8 -0
- package/scripts/ecc/fixtures/basic/patches/tests.diff +8 -0
- package/scripts/ecc/fixtures/basic/plan.json +23 -0
- package/scripts/ecc/fixtures/unauthorized/patches/impl-core.diff +8 -0
- package/scripts/ecc/fixtures/unauthorized/plan.json +15 -0
- package/scripts/ecc/git.js +139 -0
- package/scripts/ecc/id.js +37 -0
- package/scripts/ecc/install-kernel.js +344 -0
- package/scripts/ecc/json-extract.js +301 -0
- package/scripts/ecc/json.js +26 -0
- package/scripts/ecc/kernel.js +144 -0
- package/scripts/ecc/lock.js +36 -0
- package/scripts/ecc/paths.js +28 -0
- package/scripts/ecc/plan.js +57 -0
- package/scripts/ecc/project.js +37 -0
- package/scripts/ecc/providers/codex.js +168 -0
- package/scripts/ecc/providers/index.js +23 -0
- package/scripts/ecc/providers/mock.js +49 -0
- package/scripts/ecc/report.js +127 -0
- package/scripts/ecc/run.js +105 -0
- package/scripts/ecc/validate.js +325 -0
- package/scripts/ecc/verify.js +125 -0
- package/scripts/ecc.js +532 -0
- package/scripts/lib/package-manager.js +390 -0
- package/scripts/lib/session-aliases.js +432 -0
- package/scripts/lib/session-manager.js +396 -0
- package/scripts/lib/utils.js +426 -0
package/scripts/ecc.js
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ECC (Engineering Change Conveyor) - Codex-first engineering delivery engine.
|
|
4
|
+
*
|
|
5
|
+
* P0 goals:
|
|
6
|
+
* - Patch-only code sovereignty (providers output JSON/patch only; engine applies)
|
|
7
|
+
* - External worktree isolation for exec/verify
|
|
8
|
+
* - Evidence chain on disk under .ecc/runs/<runId>/
|
|
9
|
+
*
|
|
10
|
+
* Dependency-free by design.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { spawnSync } = require('child_process');
|
|
16
|
+
|
|
17
|
+
const { ensureDir } = require('./lib/utils');
|
|
18
|
+
|
|
19
|
+
const catalog = require('./ecc/catalog');
|
|
20
|
+
const configMod = require('./ecc/config');
|
|
21
|
+
const lockMod = require('./ecc/lock');
|
|
22
|
+
const project = require('./ecc/project');
|
|
23
|
+
const runMod = require('./ecc/run');
|
|
24
|
+
const idMod = require('./ecc/id');
|
|
25
|
+
const planMod = require('./ecc/plan');
|
|
26
|
+
const execMod = require('./ecc/exec');
|
|
27
|
+
const verifyMod = require('./ecc/verify');
|
|
28
|
+
const reportMod = require('./ecc/report');
|
|
29
|
+
const git = require('./ecc/git');
|
|
30
|
+
const { readJson } = require('./ecc/json');
|
|
31
|
+
const { validateLock, throwIfErrors } = require('./ecc/validate');
|
|
32
|
+
const { getProvider } = require('./ecc/providers');
|
|
33
|
+
const kernel = require('./ecc/kernel');
|
|
34
|
+
|
|
35
|
+
function parseArgs(argv) {
|
|
36
|
+
const args = { _: [] };
|
|
37
|
+
for (let i = 2; i < argv.length; i++) {
|
|
38
|
+
const a = argv[i];
|
|
39
|
+
if (a === '--help' || a === '-h') {
|
|
40
|
+
args.help = true;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (a.startsWith('--')) {
|
|
44
|
+
const key = a.slice(2);
|
|
45
|
+
const next = argv[i + 1];
|
|
46
|
+
if (!next || next.startsWith('--')) {
|
|
47
|
+
args[key] = true;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
args[key] = next;
|
|
51
|
+
i++;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
args._.push(a);
|
|
55
|
+
}
|
|
56
|
+
return args;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function usage(exitCode = 0) {
|
|
60
|
+
console.log(`
|
|
61
|
+
ecc (Engineering Change Conveyor)
|
|
62
|
+
|
|
63
|
+
Usage:
|
|
64
|
+
ecc <command> [args...]
|
|
65
|
+
node scripts/ecc.js <command> [args...] # fallback if not installed
|
|
66
|
+
|
|
67
|
+
Commands (P0):
|
|
68
|
+
packs
|
|
69
|
+
init [--backend codex|claude] [--packs a,b,c]
|
|
70
|
+
doctor
|
|
71
|
+
plan "<intent>" [--run-id <id>]
|
|
72
|
+
exec <runId> [--commit] [--worktree-root <path>] [--keep-worktree]
|
|
73
|
+
verify <runId> [--worktree-root <path>]
|
|
74
|
+
run "<intent>" [--commit] [--run-id <id>] [--worktree-root <path>] [--keep-worktree]
|
|
75
|
+
|
|
76
|
+
Environment:
|
|
77
|
+
ECC_PROVIDER=mock|codex Override provider selection (tests use mock)
|
|
78
|
+
ECC_FIXTURE=basic|unauthorized (mock provider fixtures)
|
|
79
|
+
`.trim());
|
|
80
|
+
process.exit(exitCode);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function resolveProjectRoot(cwd) {
|
|
84
|
+
try {
|
|
85
|
+
return git.getRepoRoot(cwd) || cwd;
|
|
86
|
+
} catch (_err) {
|
|
87
|
+
return cwd;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function ensureEccGitignore(projectRoot) {
|
|
92
|
+
const content = [
|
|
93
|
+
'# ECC runtime artifacts (do not commit)',
|
|
94
|
+
'runs/',
|
|
95
|
+
'cache/',
|
|
96
|
+
'tmp/',
|
|
97
|
+
''
|
|
98
|
+
].join('\n');
|
|
99
|
+
ensureDir(project.eccDir(projectRoot));
|
|
100
|
+
fs.writeFileSync(project.gitignorePath(projectRoot), content, 'utf8');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function listPacks() {
|
|
104
|
+
const packs = catalog.loadPacks();
|
|
105
|
+
if (!packs.length) {
|
|
106
|
+
console.log('No packs found.');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
console.log('Packs\n=====\n');
|
|
111
|
+
for (const p of packs) {
|
|
112
|
+
const tags = p.tags.length ? ` [${p.tags.join(', ')}]` : '';
|
|
113
|
+
console.log(`- ${p.id}: ${p.name}${tags}\n ${p.description}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function parsePacksArg(arg) {
|
|
118
|
+
if (!arg) return null;
|
|
119
|
+
return String(arg)
|
|
120
|
+
.split(',')
|
|
121
|
+
.map(s => s.trim())
|
|
122
|
+
.filter(Boolean);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function validateSelectedPacks(selected) {
|
|
126
|
+
const known = new Set(catalog.loadPacks().map(p => p.id));
|
|
127
|
+
const unknown = selected.filter(p => !known.has(p));
|
|
128
|
+
if (unknown.length) throw new Error(`Unknown packs: ${unknown.join(', ')}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function cmdInit(args) {
|
|
132
|
+
const cwd = process.cwd();
|
|
133
|
+
const projectRoot = resolveProjectRoot(cwd);
|
|
134
|
+
|
|
135
|
+
const backend = args.backend ? String(args.backend) : 'codex';
|
|
136
|
+
if (!['codex', 'claude'].includes(backend)) {
|
|
137
|
+
throw new Error('init: --backend must be "codex" or "claude"');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const packsArg = parsePacksArg(args.packs);
|
|
141
|
+
const selected = packsArg && packsArg.length ? packsArg : catalog.getDefaultPacks();
|
|
142
|
+
validateSelectedPacks(selected);
|
|
143
|
+
|
|
144
|
+
let cfg = configMod.loadConfig(projectRoot);
|
|
145
|
+
const cfgPath = project.configPath(projectRoot);
|
|
146
|
+
const existed = !!cfg;
|
|
147
|
+
if (!cfg) {
|
|
148
|
+
cfg = configMod.createConfig({ backend, packs: selected });
|
|
149
|
+
configMod.saveConfig(projectRoot, cfg);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
ensureEccGitignore(projectRoot);
|
|
153
|
+
ensureDir(project.locksDir(projectRoot));
|
|
154
|
+
lockMod.writeRegistryLock(projectRoot, { packs: cfg.packs, overwrite: false });
|
|
155
|
+
|
|
156
|
+
console.log(`${existed ? 'Already initialized' : 'Initialized'} ECC: ${path.relative(projectRoot, cfgPath)}`);
|
|
157
|
+
console.log(`Backend: ${cfg.backend}`);
|
|
158
|
+
console.log(`Packs: ${cfg.packs.join(', ')}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function runCmd(cmd, cmdArgs, opts = {}) {
|
|
162
|
+
const res = spawnSync(cmd, cmdArgs, {
|
|
163
|
+
encoding: 'utf8',
|
|
164
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
165
|
+
...opts
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
ok: res.status === 0,
|
|
169
|
+
status: res.status,
|
|
170
|
+
stdout: (res.stdout || '').trimEnd(),
|
|
171
|
+
stderr: (res.stderr || '').trimEnd()
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function cmdDoctor() {
|
|
176
|
+
const cwd = process.cwd();
|
|
177
|
+
const projectRoot = resolveProjectRoot(cwd);
|
|
178
|
+
const checks = [];
|
|
179
|
+
|
|
180
|
+
checks.push({ name: 'node', ok: true, detail: process.version });
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const k = kernel.getKernel();
|
|
184
|
+
const detail = k.enabled ? `rust (${k.bin})` : 'js fallback';
|
|
185
|
+
checks.push({ name: 'kernel', ok: true, detail });
|
|
186
|
+
} catch (err) {
|
|
187
|
+
const msg = err && err.message ? err.message : String(err);
|
|
188
|
+
checks.push({ name: 'kernel', ok: false, detail: msg });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const gitVer = runCmd('git', ['--version']);
|
|
192
|
+
checks.push({ name: 'git', ok: gitVer.ok, detail: gitVer.ok ? gitVer.stdout : gitVer.stderr });
|
|
193
|
+
|
|
194
|
+
const repoRoot = gitVer.ok ? git.getRepoRoot(projectRoot) : null;
|
|
195
|
+
checks.push({ name: 'repo', ok: !!repoRoot, detail: repoRoot ? repoRoot : 'not a git repo' });
|
|
196
|
+
|
|
197
|
+
if (repoRoot) {
|
|
198
|
+
let clean = false;
|
|
199
|
+
let detail = '';
|
|
200
|
+
try {
|
|
201
|
+
clean = git.isClean(repoRoot);
|
|
202
|
+
detail = clean ? 'clean' : 'dirty';
|
|
203
|
+
} catch (err) {
|
|
204
|
+
clean = false;
|
|
205
|
+
detail = err && err.message ? err.message : String(err);
|
|
206
|
+
}
|
|
207
|
+
checks.push({ name: 'clean', ok: clean, detail });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const codex = runCmd('codex', ['--version']);
|
|
211
|
+
checks.push({ name: 'codex', ok: codex.ok, detail: codex.ok ? codex.stdout : codex.stderr });
|
|
212
|
+
|
|
213
|
+
const claude = runCmd('claude', ['--version']);
|
|
214
|
+
checks.push({ name: 'claude', ok: claude.ok, detail: claude.ok ? claude.stdout : claude.stderr });
|
|
215
|
+
|
|
216
|
+
const cfg = configMod.loadConfig(projectRoot);
|
|
217
|
+
checks.push({
|
|
218
|
+
name: 'ecc',
|
|
219
|
+
ok: !!cfg,
|
|
220
|
+
detail: cfg ? `initialized (${path.relative(projectRoot, project.configPath(projectRoot))})` : 'not initialized'
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (cfg) {
|
|
224
|
+
try {
|
|
225
|
+
validateSelectedPacks(cfg.packs);
|
|
226
|
+
checks.push({ name: 'packs', ok: true, detail: cfg.packs.join(', ') });
|
|
227
|
+
} catch (err) {
|
|
228
|
+
checks.push({ name: 'packs', ok: false, detail: err.message });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const lockPath = project.registryLockPath(projectRoot);
|
|
233
|
+
if (fs.existsSync(lockPath)) {
|
|
234
|
+
try {
|
|
235
|
+
const lock = readJson(lockPath);
|
|
236
|
+
throwIfErrors(validateLock(lock), 'registry lock');
|
|
237
|
+
checks.push({ name: 'lock', ok: true, detail: path.relative(projectRoot, lockPath) });
|
|
238
|
+
} catch (err) {
|
|
239
|
+
checks.push({ name: 'lock', ok: false, detail: err.message });
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
checks.push({ name: 'lock', ok: false, detail: `missing (${path.relative(projectRoot, lockPath)})` });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
console.log('ECC Doctor\n==========\n');
|
|
246
|
+
for (const c of checks) {
|
|
247
|
+
const mark = c.ok ? 'OK ' : 'BAD';
|
|
248
|
+
console.log(`${mark} ${c.name.padEnd(8)} ${c.detail}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const failed = checks.filter(c => !c.ok);
|
|
252
|
+
if (failed.length) process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function cmdPlan(args) {
|
|
256
|
+
const intent = args._[1];
|
|
257
|
+
if (!intent) throw new Error('plan: missing <intent>');
|
|
258
|
+
|
|
259
|
+
const cwd = process.cwd();
|
|
260
|
+
const projectRoot = resolveProjectRoot(cwd);
|
|
261
|
+
|
|
262
|
+
const cfg = configMod.loadConfig(projectRoot);
|
|
263
|
+
if (!cfg) throw new Error('ECC is not initialized (run: ecc init)');
|
|
264
|
+
|
|
265
|
+
const requestedRunId = args['run-id'] ? String(args['run-id']) : null;
|
|
266
|
+
const runIdBase = requestedRunId || idMod.defaultRunId(intent);
|
|
267
|
+
const runId = requestedRunId ? idMod.ensureUniqueRunId(projectRoot, requestedRunId) : idMod.ensureUniqueRunId(projectRoot, runIdBase);
|
|
268
|
+
|
|
269
|
+
const repoRoot = git.getRepoRoot(projectRoot);
|
|
270
|
+
const base = repoRoot
|
|
271
|
+
? { repoRoot, branch: git.getCurrentBranch(repoRoot), sha: git.getHeadSha(repoRoot) }
|
|
272
|
+
: { repoRoot: projectRoot, branch: '', sha: '' };
|
|
273
|
+
|
|
274
|
+
const { run } = runMod.initRun({
|
|
275
|
+
projectRoot,
|
|
276
|
+
runId,
|
|
277
|
+
intent,
|
|
278
|
+
backend: cfg.backend,
|
|
279
|
+
packs: cfg.packs,
|
|
280
|
+
base
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const provider = getProvider({ backend: cfg.backend });
|
|
284
|
+
await planMod.generatePlan({ projectRoot, run, provider });
|
|
285
|
+
reportMod.writeReport({ projectRoot, runId });
|
|
286
|
+
|
|
287
|
+
console.log(`Planned runId: ${runId}`);
|
|
288
|
+
console.log(`Artifacts: ${path.relative(projectRoot, runMod.runPaths(projectRoot, runId).root)}`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function cmdExec(args) {
|
|
292
|
+
const runId = args._[1];
|
|
293
|
+
if (!runId) throw new Error('exec: missing <runId>');
|
|
294
|
+
|
|
295
|
+
const cwd = process.cwd();
|
|
296
|
+
const projectRoot = resolveProjectRoot(cwd);
|
|
297
|
+
|
|
298
|
+
const cfg = configMod.loadConfig(projectRoot);
|
|
299
|
+
if (!cfg) throw new Error('ECC is not initialized (run: ecc init)');
|
|
300
|
+
|
|
301
|
+
const provider = getProvider({ backend: cfg.backend });
|
|
302
|
+
const worktreeRoot = args['worktree-root'] ? path.resolve(String(args['worktree-root'])) : null;
|
|
303
|
+
const keepWorktree = !!args['keep-worktree'];
|
|
304
|
+
const commit = !!args.commit;
|
|
305
|
+
|
|
306
|
+
let execResult;
|
|
307
|
+
try {
|
|
308
|
+
execResult = await execMod.execRun({ projectRoot, runId, provider, worktreeRoot });
|
|
309
|
+
} catch (err) {
|
|
310
|
+
const run = runMod.loadRun(projectRoot, runId);
|
|
311
|
+
if (run) runMod.saveRun(projectRoot, runId, runMod.markRunEnded(run, 'failed'));
|
|
312
|
+
reportMod.writeReport({ projectRoot, runId });
|
|
313
|
+
throw err;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const { worktreePath, applyResult, repoRoot } = execResult;
|
|
317
|
+
|
|
318
|
+
console.log(`Worktree: ${worktreePath}`);
|
|
319
|
+
|
|
320
|
+
if (commit) {
|
|
321
|
+
const run = runMod.loadRun(projectRoot, runId);
|
|
322
|
+
if (!run) throw new Error(`unknown runId: ${runId}`);
|
|
323
|
+
|
|
324
|
+
run.status = 'verifying';
|
|
325
|
+
runMod.saveRun(projectRoot, runId, run);
|
|
326
|
+
|
|
327
|
+
const summary = verifyMod.runVerify({
|
|
328
|
+
worktreePath,
|
|
329
|
+
verifyConfig: cfg.verify,
|
|
330
|
+
outDir: runMod.runPaths(projectRoot, runId).verifyDir
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
if (!summary.ok) {
|
|
334
|
+
runMod.saveRun(projectRoot, runId, runMod.markRunEnded(run, 'failed'));
|
|
335
|
+
reportMod.writeReport({ projectRoot, runId });
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const sha = git.commitAll({ repoRoot: worktreePath, message: `ecc: ${runId}` });
|
|
340
|
+
applyResult.commit = { sha, message: `ecc: ${runId}` };
|
|
341
|
+
throwIfErrors(require('./ecc/validate').validateApplyResult(applyResult), 'apply result');
|
|
342
|
+
require('./ecc/json').writeJson(runMod.runPaths(projectRoot, runId).applyJson, applyResult);
|
|
343
|
+
|
|
344
|
+
runMod.saveRun(projectRoot, runId, runMod.markRunEnded(run, 'succeeded'));
|
|
345
|
+
|
|
346
|
+
if (!keepWorktree) {
|
|
347
|
+
try {
|
|
348
|
+
git.removeWorktree({ repoRoot, worktreePath });
|
|
349
|
+
run.worktree.path = '';
|
|
350
|
+
runMod.saveRun(projectRoot, runId, run);
|
|
351
|
+
} catch (_err) {
|
|
352
|
+
// ignore cleanup failures
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
reportMod.writeReport({ projectRoot, runId });
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function cmdVerify(args) {
|
|
361
|
+
const runId = args._[1];
|
|
362
|
+
if (!runId) throw new Error('verify: missing <runId>');
|
|
363
|
+
|
|
364
|
+
const cwd = process.cwd();
|
|
365
|
+
const projectRoot = resolveProjectRoot(cwd);
|
|
366
|
+
const worktreeRoot = args['worktree-root'] ? path.resolve(String(args['worktree-root'])) : null;
|
|
367
|
+
|
|
368
|
+
const cfg = configMod.loadConfig(projectRoot);
|
|
369
|
+
if (!cfg) throw new Error('ECC is not initialized (run: ecc init)');
|
|
370
|
+
|
|
371
|
+
const run = runMod.loadRun(projectRoot, runId);
|
|
372
|
+
if (!run) throw new Error(`unknown runId: ${runId}`);
|
|
373
|
+
|
|
374
|
+
if (!run.worktree.path || !fs.existsSync(run.worktree.path)) {
|
|
375
|
+
const applyPath = runMod.runPaths(projectRoot, runId).applyJson;
|
|
376
|
+
const apply = fs.existsSync(applyPath) ? readJson(applyPath) : null;
|
|
377
|
+
const hasCommit = !!(apply && apply.commit && apply.commit.sha);
|
|
378
|
+
if (!hasCommit) {
|
|
379
|
+
throw new Error(
|
|
380
|
+
'verify: missing worktree and changes were not committed. Re-run: ecc exec <runId> (or use --commit to persist changes).'
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const repoRoot = git.getRepoRoot(projectRoot);
|
|
385
|
+
if (!repoRoot) throw new Error('verify: requires a git repository');
|
|
386
|
+
|
|
387
|
+
const wt = git.ensureWorktree({
|
|
388
|
+
repoRoot,
|
|
389
|
+
worktreePath: git.defaultWorktreePath({ repoRoot, runId, worktreeRoot }),
|
|
390
|
+
branch: run.worktree.branch,
|
|
391
|
+
baseSha: run.base.sha || git.getHeadSha(repoRoot)
|
|
392
|
+
});
|
|
393
|
+
run.worktree.path = wt;
|
|
394
|
+
runMod.saveRun(projectRoot, runId, run);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
run.status = 'verifying';
|
|
398
|
+
runMod.saveRun(projectRoot, runId, run);
|
|
399
|
+
|
|
400
|
+
const summary = verifyMod.runVerify({
|
|
401
|
+
worktreePath: run.worktree.path,
|
|
402
|
+
verifyConfig: cfg.verify,
|
|
403
|
+
outDir: runMod.runPaths(projectRoot, runId).verifyDir
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
if (!summary.ok) {
|
|
407
|
+
runMod.saveRun(projectRoot, runId, runMod.markRunEnded(run, 'failed'));
|
|
408
|
+
reportMod.writeReport({ projectRoot, runId });
|
|
409
|
+
process.exit(1);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
runMod.saveRun(projectRoot, runId, runMod.markRunEnded(run, 'succeeded'));
|
|
413
|
+
reportMod.writeReport({ projectRoot, runId });
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
async function cmdRun(args) {
|
|
417
|
+
const intent = args._[1];
|
|
418
|
+
if (!intent) throw new Error('run: missing <intent>');
|
|
419
|
+
|
|
420
|
+
const cwd = process.cwd();
|
|
421
|
+
const projectRoot = resolveProjectRoot(cwd);
|
|
422
|
+
|
|
423
|
+
const cfg = configMod.loadConfig(projectRoot);
|
|
424
|
+
if (!cfg) throw new Error('ECC is not initialized (run: ecc init)');
|
|
425
|
+
|
|
426
|
+
const requestedRunId = args['run-id'] ? String(args['run-id']) : null;
|
|
427
|
+
const runIdBase = requestedRunId || idMod.defaultRunId(intent);
|
|
428
|
+
const runId = requestedRunId ? idMod.ensureUniqueRunId(projectRoot, requestedRunId) : idMod.ensureUniqueRunId(projectRoot, runIdBase);
|
|
429
|
+
|
|
430
|
+
const repoRoot = git.getRepoRoot(projectRoot);
|
|
431
|
+
const base = repoRoot
|
|
432
|
+
? { repoRoot, branch: git.getCurrentBranch(repoRoot), sha: git.getHeadSha(repoRoot) }
|
|
433
|
+
: { repoRoot: projectRoot, branch: '', sha: '' };
|
|
434
|
+
|
|
435
|
+
const { run } = runMod.initRun({
|
|
436
|
+
projectRoot,
|
|
437
|
+
runId,
|
|
438
|
+
intent,
|
|
439
|
+
backend: cfg.backend,
|
|
440
|
+
packs: cfg.packs,
|
|
441
|
+
base
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
const provider = getProvider({ backend: cfg.backend });
|
|
445
|
+
await planMod.generatePlan({ projectRoot, run, provider });
|
|
446
|
+
reportMod.writeReport({ projectRoot, runId });
|
|
447
|
+
|
|
448
|
+
const worktreeRoot = args['worktree-root'] ? path.resolve(String(args['worktree-root'])) : null;
|
|
449
|
+
const keepWorktree = !!args['keep-worktree'];
|
|
450
|
+
const commit = !!args.commit;
|
|
451
|
+
|
|
452
|
+
let execResult;
|
|
453
|
+
try {
|
|
454
|
+
execResult = await execMod.execRun({ projectRoot, runId, provider, worktreeRoot });
|
|
455
|
+
} catch (err) {
|
|
456
|
+
const loaded = runMod.loadRun(projectRoot, runId);
|
|
457
|
+
if (loaded) runMod.saveRun(projectRoot, runId, runMod.markRunEnded(loaded, 'failed'));
|
|
458
|
+
reportMod.writeReport({ projectRoot, runId });
|
|
459
|
+
throw err;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
{
|
|
463
|
+
const loaded = runMod.loadRun(projectRoot, runId);
|
|
464
|
+
if (loaded) {
|
|
465
|
+
loaded.status = 'verifying';
|
|
466
|
+
runMod.saveRun(projectRoot, runId, loaded);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const summary = verifyMod.runVerify({
|
|
471
|
+
worktreePath: execResult.worktreePath,
|
|
472
|
+
verifyConfig: cfg.verify,
|
|
473
|
+
outDir: runMod.runPaths(projectRoot, runId).verifyDir
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
if (!summary.ok) {
|
|
477
|
+
const loaded = runMod.loadRun(projectRoot, runId);
|
|
478
|
+
if (loaded) runMod.saveRun(projectRoot, runId, runMod.markRunEnded(loaded, 'failed'));
|
|
479
|
+
reportMod.writeReport({ projectRoot, runId });
|
|
480
|
+
process.exit(1);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (commit) {
|
|
484
|
+
const sha = git.commitAll({ repoRoot: execResult.worktreePath, message: `ecc: ${runId}` });
|
|
485
|
+
execResult.applyResult.commit = { sha, message: `ecc: ${runId}` };
|
|
486
|
+
require('./ecc/json').writeJson(runMod.runPaths(projectRoot, runId).applyJson, execResult.applyResult);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const loaded = runMod.loadRun(projectRoot, runId);
|
|
490
|
+
if (loaded) runMod.saveRun(projectRoot, runId, runMod.markRunEnded(loaded, 'succeeded'));
|
|
491
|
+
reportMod.writeReport({ projectRoot, runId });
|
|
492
|
+
|
|
493
|
+
console.log(`RunId: ${runId}`);
|
|
494
|
+
console.log(`Worktree: ${execResult.worktreePath}`);
|
|
495
|
+
|
|
496
|
+
if (commit && !keepWorktree) {
|
|
497
|
+
try {
|
|
498
|
+
git.removeWorktree({ repoRoot: execResult.repoRoot, worktreePath: execResult.worktreePath });
|
|
499
|
+
const loaded2 = runMod.loadRun(projectRoot, runId);
|
|
500
|
+
if (loaded2) {
|
|
501
|
+
loaded2.worktree.path = '';
|
|
502
|
+
runMod.saveRun(projectRoot, runId, loaded2);
|
|
503
|
+
}
|
|
504
|
+
} catch (_err) {
|
|
505
|
+
// ignore cleanup failures
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
async function main() {
|
|
511
|
+
const args = parseArgs(process.argv);
|
|
512
|
+
if (args.help) usage(0);
|
|
513
|
+
|
|
514
|
+
const cmd = args._[0];
|
|
515
|
+
if (!cmd) usage(1);
|
|
516
|
+
|
|
517
|
+
if (cmd === 'packs') return listPacks();
|
|
518
|
+
if (cmd === 'init') return cmdInit(args);
|
|
519
|
+
if (cmd === 'doctor') return cmdDoctor();
|
|
520
|
+
if (cmd === 'plan') return cmdPlan(args);
|
|
521
|
+
if (cmd === 'exec') return cmdExec(args);
|
|
522
|
+
if (cmd === 'verify') return cmdVerify(args);
|
|
523
|
+
if (cmd === 'run') return cmdRun(args);
|
|
524
|
+
|
|
525
|
+
usage(1);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
main().catch(err => {
|
|
529
|
+
const msg = err && err.message ? err.message : String(err);
|
|
530
|
+
console.error(`ERROR: ${msg}`);
|
|
531
|
+
process.exit(1);
|
|
532
|
+
});
|