claude-symphony 0.8.0 → 0.8.1
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/dist/cli/index.js
CHANGED
|
@@ -1,30 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
2
6
|
|
|
3
|
-
//
|
|
4
|
-
import
|
|
5
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
6
|
-
import path10 from "path";
|
|
7
|
-
import { Command } from "commander";
|
|
8
|
-
|
|
9
|
-
// src/cli/commands/create.ts
|
|
10
|
-
import fs2 from "fs";
|
|
11
|
-
import path2 from "path";
|
|
7
|
+
// node_modules/tsup/assets/esm_shims.js
|
|
8
|
+
import path from "path";
|
|
12
9
|
import { fileURLToPath } from "url";
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
var init_esm_shims = __esm({
|
|
11
|
+
"node_modules/tsup/assets/esm_shims.js"() {
|
|
12
|
+
"use strict";
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
15
|
|
|
16
16
|
// src/utils/logger.ts
|
|
17
17
|
import chalk from "chalk";
|
|
18
|
-
var colorMap = {
|
|
19
|
-
red: chalk.red,
|
|
20
|
-
green: chalk.green,
|
|
21
|
-
yellow: chalk.yellow,
|
|
22
|
-
blue: chalk.blue,
|
|
23
|
-
cyan: chalk.cyan,
|
|
24
|
-
magenta: chalk.magenta,
|
|
25
|
-
white: chalk.white,
|
|
26
|
-
gray: chalk.gray
|
|
27
|
-
};
|
|
28
18
|
function log(message, color) {
|
|
29
19
|
if (color && colorMap[color]) {
|
|
30
20
|
console.log(colorMap[color](message));
|
|
@@ -44,30 +34,248 @@ function logWarning(message) {
|
|
|
44
34
|
function logError(message) {
|
|
45
35
|
console.log(`${chalk.red("[ERROR]")} ${message}`);
|
|
46
36
|
}
|
|
47
|
-
var result
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
37
|
+
var colorMap, result;
|
|
38
|
+
var init_logger = __esm({
|
|
39
|
+
"src/utils/logger.ts"() {
|
|
40
|
+
"use strict";
|
|
41
|
+
init_esm_shims();
|
|
42
|
+
colorMap = {
|
|
43
|
+
red: chalk.red,
|
|
44
|
+
green: chalk.green,
|
|
45
|
+
yellow: chalk.yellow,
|
|
46
|
+
blue: chalk.blue,
|
|
47
|
+
cyan: chalk.cyan,
|
|
48
|
+
magenta: chalk.magenta,
|
|
49
|
+
white: chalk.white,
|
|
50
|
+
gray: chalk.gray
|
|
51
|
+
};
|
|
52
|
+
result = {
|
|
53
|
+
critical: (message) => {
|
|
54
|
+
console.log(` ${chalk.red("\u2717 [CRITICAL]")} ${message}`);
|
|
55
|
+
},
|
|
56
|
+
high: (message) => {
|
|
57
|
+
console.log(` ${chalk.yellow("\u26A0 [HIGH]")} ${message}`);
|
|
58
|
+
},
|
|
59
|
+
medium: (message) => {
|
|
60
|
+
console.log(` ${chalk.magenta("\u25CB [MEDIUM]")} ${message}`);
|
|
61
|
+
},
|
|
62
|
+
pass: (message, verbose = false) => {
|
|
63
|
+
if (verbose) {
|
|
64
|
+
console.log(` ${chalk.green("\u2713")} ${message}`);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
fixed: (message) => {
|
|
68
|
+
console.log(` ${chalk.green("\u26A1 [FIXED]")} ${message}`);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
64
71
|
}
|
|
65
|
-
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// src/utils/shell.ts
|
|
75
|
+
import { execa } from "execa";
|
|
76
|
+
async function exec(command, args = [], options = {}) {
|
|
77
|
+
try {
|
|
78
|
+
const execaOptions = {
|
|
79
|
+
cwd: options.cwd,
|
|
80
|
+
env: { ...process.env, ...options.env },
|
|
81
|
+
timeout: options.timeout,
|
|
82
|
+
shell: options.shell ?? false,
|
|
83
|
+
stdio: options.stdio ?? "pipe",
|
|
84
|
+
reject: false,
|
|
85
|
+
...options.input !== void 0 && { input: options.input }
|
|
86
|
+
};
|
|
87
|
+
const result2 = await execa(command, args, execaOptions);
|
|
88
|
+
return {
|
|
89
|
+
stdout: typeof result2.stdout === "string" ? result2.stdout : "",
|
|
90
|
+
stderr: typeof result2.stderr === "string" ? result2.stderr : "",
|
|
91
|
+
exitCode: result2.exitCode ?? 0,
|
|
92
|
+
success: result2.exitCode === 0
|
|
93
|
+
};
|
|
94
|
+
} catch (error) {
|
|
95
|
+
const err = error;
|
|
96
|
+
return {
|
|
97
|
+
stdout: "",
|
|
98
|
+
stderr: err.message || err.stderr || "Unknown error",
|
|
99
|
+
exitCode: err.exitCode ?? 1,
|
|
100
|
+
success: false
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function commandExists(cmd) {
|
|
105
|
+
const result2 = await exec("which", [cmd]);
|
|
106
|
+
return result2.success;
|
|
107
|
+
}
|
|
108
|
+
function getTimestamp() {
|
|
109
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
110
|
+
}
|
|
111
|
+
var init_shell = __esm({
|
|
112
|
+
"src/utils/shell.ts"() {
|
|
113
|
+
"use strict";
|
|
114
|
+
init_esm_shims();
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// src/integrations/gemini.ts
|
|
119
|
+
async function isGeminiAvailable() {
|
|
120
|
+
return commandExists("gemini");
|
|
121
|
+
}
|
|
122
|
+
async function callGemini(prompt, options = {}) {
|
|
123
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
124
|
+
if (!await isGeminiAvailable()) {
|
|
125
|
+
logWarning("gemini CLI is not installed \u2014 falling back to claudecode.");
|
|
126
|
+
return {
|
|
127
|
+
success: false,
|
|
128
|
+
fallbackRequired: true,
|
|
129
|
+
fallbackSignal: "CLI_NOT_FOUND",
|
|
130
|
+
fallbackReason: "Gemini CLI not installed"
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
logInfo(`Calling Gemini CLI (timeout: ${timeout}s)...`);
|
|
135
|
+
const result2 = await exec("gemini", ["-p", prompt, "--yolo"], {
|
|
136
|
+
timeout: timeout * 1e3,
|
|
137
|
+
cwd: options.cwd
|
|
138
|
+
});
|
|
139
|
+
const output = result2.stdout.trim();
|
|
140
|
+
if (!output) {
|
|
141
|
+
logWarning("Gemini returned empty response \u2014 falling back.");
|
|
142
|
+
return {
|
|
143
|
+
success: false,
|
|
144
|
+
fallbackRequired: true,
|
|
145
|
+
fallbackSignal: "TIMEOUT",
|
|
146
|
+
fallbackReason: "Empty response from Gemini CLI"
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const errorPatterns = /rate.limit|quota.exceeded|authentication.failed/i;
|
|
150
|
+
if (!result2.success || errorPatterns.test(output)) {
|
|
151
|
+
logWarning("Gemini API error detected \u2014 falling back.");
|
|
152
|
+
return {
|
|
153
|
+
success: false,
|
|
154
|
+
output,
|
|
155
|
+
fallbackRequired: true,
|
|
156
|
+
fallbackSignal: "API_ERROR",
|
|
157
|
+
fallbackReason: result2.stderr || "API error detected in response"
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
logSuccess("Gemini call completed.");
|
|
161
|
+
return {
|
|
162
|
+
success: true,
|
|
163
|
+
output,
|
|
164
|
+
fallbackRequired: false
|
|
165
|
+
};
|
|
166
|
+
} catch (error) {
|
|
167
|
+
logError(`Gemini call failed: ${error}`);
|
|
168
|
+
return {
|
|
169
|
+
success: false,
|
|
170
|
+
fallbackRequired: true,
|
|
171
|
+
fallbackSignal: "API_ERROR",
|
|
172
|
+
fallbackReason: String(error)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
var DEFAULT_TIMEOUT;
|
|
177
|
+
var init_gemini = __esm({
|
|
178
|
+
"src/integrations/gemini.ts"() {
|
|
179
|
+
"use strict";
|
|
180
|
+
init_esm_shims();
|
|
181
|
+
init_shell();
|
|
182
|
+
init_logger();
|
|
183
|
+
DEFAULT_TIMEOUT = 300;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// src/integrations/codex.ts
|
|
188
|
+
async function isCodexAvailable() {
|
|
189
|
+
return commandExists("codex");
|
|
190
|
+
}
|
|
191
|
+
async function callCodex(prompt, options = {}) {
|
|
192
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT2;
|
|
193
|
+
const fullAuto = options.fullAuto ?? true;
|
|
194
|
+
if (!await isCodexAvailable()) {
|
|
195
|
+
logWarning("codex CLI is not installed \u2014 falling back to claudecode.");
|
|
196
|
+
return {
|
|
197
|
+
success: false,
|
|
198
|
+
fallbackRequired: true,
|
|
199
|
+
fallbackSignal: "CLI_NOT_FOUND",
|
|
200
|
+
fallbackReason: "Codex CLI not installed"
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
const args = fullAuto ? ["exec", "--full-auto", prompt] : ["exec", prompt];
|
|
205
|
+
logInfo(`Calling Codex CLI${fullAuto ? " (--full-auto)" : ""} (timeout: ${timeout}s)...`);
|
|
206
|
+
const result2 = await exec("codex", args, {
|
|
207
|
+
timeout: timeout * 1e3,
|
|
208
|
+
cwd: options.cwd
|
|
209
|
+
});
|
|
210
|
+
const output = result2.stdout.trim();
|
|
211
|
+
if (!output) {
|
|
212
|
+
logWarning("Codex returned empty response \u2014 falling back.");
|
|
213
|
+
return {
|
|
214
|
+
success: false,
|
|
215
|
+
fallbackRequired: true,
|
|
216
|
+
fallbackSignal: "TIMEOUT",
|
|
217
|
+
fallbackReason: "Empty response from Codex CLI"
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const errorPatterns = /rate.limit|quota.exceeded|authentication.failed/i;
|
|
221
|
+
if (!result2.success || errorPatterns.test(output)) {
|
|
222
|
+
logWarning("Codex API error detected \u2014 falling back.");
|
|
223
|
+
return {
|
|
224
|
+
success: false,
|
|
225
|
+
output,
|
|
226
|
+
fallbackRequired: true,
|
|
227
|
+
fallbackSignal: "API_ERROR",
|
|
228
|
+
fallbackReason: result2.stderr || "API error detected in response"
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
logSuccess("Codex call completed.");
|
|
232
|
+
return {
|
|
233
|
+
success: true,
|
|
234
|
+
output,
|
|
235
|
+
fallbackRequired: false
|
|
236
|
+
};
|
|
237
|
+
} catch (error) {
|
|
238
|
+
logError(`Codex call failed: ${error}`);
|
|
239
|
+
return {
|
|
240
|
+
success: false,
|
|
241
|
+
fallbackRequired: true,
|
|
242
|
+
fallbackSignal: "API_ERROR",
|
|
243
|
+
fallbackReason: String(error)
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
var DEFAULT_TIMEOUT2;
|
|
248
|
+
var init_codex = __esm({
|
|
249
|
+
"src/integrations/codex.ts"() {
|
|
250
|
+
"use strict";
|
|
251
|
+
init_esm_shims();
|
|
252
|
+
init_shell();
|
|
253
|
+
init_logger();
|
|
254
|
+
DEFAULT_TIMEOUT2 = 300;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// src/cli/index.ts
|
|
259
|
+
init_esm_shims();
|
|
260
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
261
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
262
|
+
import path13 from "path";
|
|
263
|
+
import { Command } from "commander";
|
|
264
|
+
|
|
265
|
+
// src/cli/commands/create.ts
|
|
266
|
+
init_esm_shims();
|
|
267
|
+
init_logger();
|
|
268
|
+
import fs2 from "fs";
|
|
269
|
+
import path3 from "path";
|
|
270
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
271
|
+
import { input } from "@inquirer/prompts";
|
|
272
|
+
import chalk2 from "chalk";
|
|
66
273
|
|
|
67
274
|
// src/utils/fs.ts
|
|
275
|
+
init_esm_shims();
|
|
68
276
|
import fs from "fs/promises";
|
|
69
277
|
import { existsSync, statSync, mkdirSync, copyFileSync, readdirSync } from "fs";
|
|
70
|
-
import
|
|
278
|
+
import path2 from "path";
|
|
71
279
|
function pathExists(filePath) {
|
|
72
280
|
return existsSync(filePath);
|
|
73
281
|
}
|
|
@@ -90,7 +298,7 @@ async function readFile(filePath) {
|
|
|
90
298
|
}
|
|
91
299
|
async function writeFile(filePath, content) {
|
|
92
300
|
try {
|
|
93
|
-
await ensureDirAsync(
|
|
301
|
+
await ensureDirAsync(path2.dirname(filePath));
|
|
94
302
|
await fs.writeFile(filePath, content, "utf8");
|
|
95
303
|
return true;
|
|
96
304
|
} catch (error) {
|
|
@@ -108,7 +316,7 @@ async function readJson(filePath) {
|
|
|
108
316
|
}
|
|
109
317
|
async function writeJson(filePath, data, pretty = true) {
|
|
110
318
|
try {
|
|
111
|
-
await ensureDirAsync(
|
|
319
|
+
await ensureDirAsync(path2.dirname(filePath));
|
|
112
320
|
const content = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
|
|
113
321
|
await fs.writeFile(filePath, content, "utf8");
|
|
114
322
|
return true;
|
|
@@ -124,7 +332,7 @@ function copyDirSync(src, dest) {
|
|
|
124
332
|
ensureDir(dest);
|
|
125
333
|
const entries = readdirSync(src);
|
|
126
334
|
for (const entry of entries) {
|
|
127
|
-
copyDirSync(
|
|
335
|
+
copyDirSync(path2.join(src, entry), path2.join(dest, entry));
|
|
128
336
|
}
|
|
129
337
|
} else {
|
|
130
338
|
copyFileSync(src, dest);
|
|
@@ -142,10 +350,10 @@ async function remove(filePath) {
|
|
|
142
350
|
}
|
|
143
351
|
|
|
144
352
|
// src/cli/commands/create.ts
|
|
145
|
-
var __filename2 =
|
|
146
|
-
var __dirname2 =
|
|
353
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
354
|
+
var __dirname2 = path3.dirname(__filename2);
|
|
147
355
|
function displayAsciiBanner() {
|
|
148
|
-
const bannerPath =
|
|
356
|
+
const bannerPath = path3.join(__dirname2, "..", "..", "assets", "claude_symphony_ascii.txt");
|
|
149
357
|
if (fs2.existsSync(bannerPath)) {
|
|
150
358
|
const banner = fs2.readFileSync(bannerPath, "utf8");
|
|
151
359
|
console.log(chalk2.cyan(banner));
|
|
@@ -183,7 +391,7 @@ function removeNestedGitDirs(dir, isRoot = true) {
|
|
|
183
391
|
if (!fs2.existsSync(dir)) return;
|
|
184
392
|
const items = fs2.readdirSync(dir);
|
|
185
393
|
for (const item of items) {
|
|
186
|
-
const itemPath =
|
|
394
|
+
const itemPath = path3.join(dir, item);
|
|
187
395
|
const stat = fs2.statSync(itemPath);
|
|
188
396
|
if (stat.isDirectory()) {
|
|
189
397
|
if (item === ".git" && !isRoot) {
|
|
@@ -209,10 +417,10 @@ async function createProject(options) {
|
|
|
209
417
|
}
|
|
210
418
|
});
|
|
211
419
|
}
|
|
212
|
-
const packageRoot =
|
|
213
|
-
const templateDir =
|
|
214
|
-
const targetDir =
|
|
215
|
-
const actualProjectName = projectName === "." ?
|
|
420
|
+
const packageRoot = path3.resolve(__dirname2, "..", "..");
|
|
421
|
+
const templateDir = path3.join(packageRoot, "template");
|
|
422
|
+
const targetDir = path3.resolve(projectName);
|
|
423
|
+
const actualProjectName = projectName === "." ? path3.basename(targetDir) : projectName;
|
|
216
424
|
if (!fs2.existsSync(templateDir)) {
|
|
217
425
|
log(`Error: Template directory not found: ${templateDir}`, "red");
|
|
218
426
|
process.exit(1);
|
|
@@ -242,8 +450,8 @@ async function createProject(options) {
|
|
|
242
450
|
copyDirSync(templateDir, targetDir);
|
|
243
451
|
log(" Template installed", "green");
|
|
244
452
|
removeNestedGitDirs(targetDir);
|
|
245
|
-
const progressTemplatePath =
|
|
246
|
-
const progressPath =
|
|
453
|
+
const progressTemplatePath = path3.join(targetDir, "state", "progress.json.template");
|
|
454
|
+
const progressPath = path3.join(targetDir, "state", "progress.json");
|
|
247
455
|
if (fs2.existsSync(progressTemplatePath)) {
|
|
248
456
|
let progressContent = fs2.readFileSync(progressTemplatePath, "utf8");
|
|
249
457
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -252,8 +460,8 @@ async function createProject(options) {
|
|
|
252
460
|
fs2.unlinkSync(progressTemplatePath);
|
|
253
461
|
}
|
|
254
462
|
log(" Progress tracking initialized", "green");
|
|
255
|
-
const briefPath =
|
|
256
|
-
const briefDir =
|
|
463
|
+
const briefPath = path3.join(targetDir, "stages", "01-brainstorm", "inputs", "project_brief.md");
|
|
464
|
+
const briefDir = path3.dirname(briefPath);
|
|
257
465
|
ensureDir(briefDir);
|
|
258
466
|
const briefContent = generateBriefContent(actualProjectName, description);
|
|
259
467
|
fs2.writeFileSync(briefPath, briefContent);
|
|
@@ -283,10 +491,12 @@ async function createProject(options) {
|
|
|
283
491
|
}
|
|
284
492
|
|
|
285
493
|
// src/cli/commands/stage.ts
|
|
286
|
-
|
|
494
|
+
init_esm_shims();
|
|
495
|
+
import path5 from "path";
|
|
287
496
|
import { existsSync as existsSync2 } from "fs";
|
|
288
497
|
|
|
289
498
|
// src/utils/yaml.ts
|
|
499
|
+
init_esm_shims();
|
|
290
500
|
import fs3 from "fs/promises";
|
|
291
501
|
import yaml from "js-yaml";
|
|
292
502
|
async function loadYaml(filePath) {
|
|
@@ -298,50 +508,21 @@ async function loadYaml(filePath) {
|
|
|
298
508
|
}
|
|
299
509
|
}
|
|
300
510
|
|
|
301
|
-
// src/
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
try {
|
|
305
|
-
const execaOptions = {
|
|
306
|
-
cwd: options.cwd,
|
|
307
|
-
env: { ...process.env, ...options.env },
|
|
308
|
-
timeout: options.timeout,
|
|
309
|
-
shell: options.shell ?? false,
|
|
310
|
-
stdio: options.stdio ?? "pipe",
|
|
311
|
-
reject: false
|
|
312
|
-
};
|
|
313
|
-
const result2 = await execa(command, args, execaOptions);
|
|
314
|
-
return {
|
|
315
|
-
stdout: typeof result2.stdout === "string" ? result2.stdout : "",
|
|
316
|
-
stderr: typeof result2.stderr === "string" ? result2.stderr : "",
|
|
317
|
-
exitCode: result2.exitCode ?? 0,
|
|
318
|
-
success: result2.exitCode === 0
|
|
319
|
-
};
|
|
320
|
-
} catch (error) {
|
|
321
|
-
const err = error;
|
|
322
|
-
return {
|
|
323
|
-
stdout: "",
|
|
324
|
-
stderr: err.message || err.stderr || "Unknown error",
|
|
325
|
-
exitCode: err.exitCode ?? 1,
|
|
326
|
-
success: false
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
async function commandExists(cmd) {
|
|
331
|
-
const result2 = await exec("which", [cmd]);
|
|
332
|
-
return result2.success;
|
|
333
|
-
}
|
|
334
|
-
function getTimestamp() {
|
|
335
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
336
|
-
}
|
|
511
|
+
// src/cli/commands/stage.ts
|
|
512
|
+
init_logger();
|
|
513
|
+
init_shell();
|
|
337
514
|
|
|
338
515
|
// src/core/state/progress.ts
|
|
339
|
-
|
|
516
|
+
init_esm_shims();
|
|
517
|
+
import path4 from "path";
|
|
518
|
+
init_shell();
|
|
340
519
|
|
|
341
520
|
// src/types/state.ts
|
|
521
|
+
init_esm_shims();
|
|
342
522
|
import { z as z2 } from "zod";
|
|
343
523
|
|
|
344
524
|
// src/types/stage.ts
|
|
525
|
+
init_esm_shims();
|
|
345
526
|
import { z } from "zod";
|
|
346
527
|
var STAGE_IDS = [
|
|
347
528
|
"01-brainstorm",
|
|
@@ -519,7 +700,7 @@ function createInitialProgress(projectName) {
|
|
|
519
700
|
|
|
520
701
|
// src/core/state/progress.ts
|
|
521
702
|
function getProgressPath(projectRoot) {
|
|
522
|
-
return
|
|
703
|
+
return path4.join(projectRoot, "state", "progress.json");
|
|
523
704
|
}
|
|
524
705
|
async function loadProgress(projectRoot) {
|
|
525
706
|
const progressPath = getProgressPath(projectRoot);
|
|
@@ -658,7 +839,7 @@ var ProgressManager = class {
|
|
|
658
839
|
|
|
659
840
|
// src/cli/commands/stage.ts
|
|
660
841
|
async function runStage(projectRoot, stageId, options = {}) {
|
|
661
|
-
const stageDir =
|
|
842
|
+
const stageDir = path5.join(projectRoot, "stages", stageId);
|
|
662
843
|
if (options.complete) {
|
|
663
844
|
return completeStage(projectRoot, stageId);
|
|
664
845
|
}
|
|
@@ -670,7 +851,7 @@ async function runStage(projectRoot, stageId, options = {}) {
|
|
|
670
851
|
console.log(`\u{1F680} Stage Execution: ${stageId}`);
|
|
671
852
|
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
672
853
|
console.log("\n[1/4] Executing Pre-Stage Hook");
|
|
673
|
-
const preStageHook =
|
|
854
|
+
const preStageHook = path5.join(projectRoot, ".claude", "hooks", "pre-stage.sh");
|
|
674
855
|
if (existsSync2(preStageHook)) {
|
|
675
856
|
logInfo(`Pre-stage hook found: ${preStageHook}`);
|
|
676
857
|
console.log(" (Hook execution delegated to shell wrapper)");
|
|
@@ -686,7 +867,7 @@ async function runStage(projectRoot, stageId, options = {}) {
|
|
|
686
867
|
logWarning("Could not update progress.json");
|
|
687
868
|
}
|
|
688
869
|
console.log("\n[3/4] AI Model Enforcement Check");
|
|
689
|
-
const stageConfig = await loadYaml(
|
|
870
|
+
const stageConfig = await loadYaml(path5.join(stageDir, "config.yaml"));
|
|
690
871
|
if (stageConfig?.auto_invoke?.enabled) {
|
|
691
872
|
const { model, message, required } = stageConfig.auto_invoke;
|
|
692
873
|
console.log("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
@@ -705,7 +886,7 @@ async function runStage(projectRoot, stageId, options = {}) {
|
|
|
705
886
|
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
706
887
|
}
|
|
707
888
|
console.log("\n[4/4] Loading Stage Instructions");
|
|
708
|
-
const claudeMd =
|
|
889
|
+
const claudeMd = path5.join(stageDir, "CLAUDE.md");
|
|
709
890
|
if (existsSync2(claudeMd)) {
|
|
710
891
|
console.log("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
711
892
|
logSuccess("Stage CLAUDE.md:");
|
|
@@ -771,7 +952,7 @@ async function nextStage(projectRoot, options = {}) {
|
|
|
771
952
|
}
|
|
772
953
|
}
|
|
773
954
|
if (!options.noHandoff && !options.force) {
|
|
774
|
-
const handoffPath =
|
|
955
|
+
const handoffPath = path5.join(projectRoot, "stages", currentStage, "HANDOFF.md");
|
|
775
956
|
if (!existsSync2(handoffPath)) {
|
|
776
957
|
logWarning("HANDOFF.md not found for current stage.");
|
|
777
958
|
console.log("Generate HANDOFF.md with /handoff command or use --no-handoff to skip.");
|
|
@@ -789,7 +970,7 @@ async function nextStage(projectRoot, options = {}) {
|
|
|
789
970
|
return true;
|
|
790
971
|
}
|
|
791
972
|
async function handleSprintTransition(projectRoot, currentStage) {
|
|
792
|
-
const pipelineConfig = await loadYaml(
|
|
973
|
+
const pipelineConfig = await loadYaml(path5.join(projectRoot, "config", "pipeline.yaml"));
|
|
793
974
|
if (!pipelineConfig?.sprint_mode?.enabled) {
|
|
794
975
|
return false;
|
|
795
976
|
}
|
|
@@ -797,7 +978,7 @@ async function handleSprintTransition(projectRoot, currentStage) {
|
|
|
797
978
|
if (!stageIterative) {
|
|
798
979
|
return false;
|
|
799
980
|
}
|
|
800
|
-
const progressPath =
|
|
981
|
+
const progressPath = path5.join(projectRoot, "state", "progress.json");
|
|
801
982
|
const progress = await readJson(progressPath);
|
|
802
983
|
const currentSprint = progress?.current_iteration?.current_sprint ?? 1;
|
|
803
984
|
const totalSprints = progress?.current_iteration?.total_sprints ?? 3;
|
|
@@ -855,7 +1036,7 @@ async function gotoStage(projectRoot, targetStage, options = {}) {
|
|
|
855
1036
|
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
856
1037
|
console.log("Loop-back History");
|
|
857
1038
|
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
858
|
-
const historyPath2 =
|
|
1039
|
+
const historyPath2 = path5.join(projectRoot, "state", "loopback_history.json");
|
|
859
1040
|
const history2 = await readJson(historyPath2);
|
|
860
1041
|
if (history2 && history2.length > 0) {
|
|
861
1042
|
for (const entry of history2) {
|
|
@@ -882,7 +1063,7 @@ async function gotoStage(projectRoot, targetStage, options = {}) {
|
|
|
882
1063
|
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
883
1064
|
console.log(` From: ${currentStage} (${getStageName(currentStage)})`);
|
|
884
1065
|
console.log(` To: ${targetStage} (${getStageName(targetStage)})`);
|
|
885
|
-
const historyPath =
|
|
1066
|
+
const historyPath = path5.join(projectRoot, "state", "loopback_history.json");
|
|
886
1067
|
let history = await readJson(historyPath) ?? [];
|
|
887
1068
|
const timestamp = getTimestamp();
|
|
888
1069
|
history.push({
|
|
@@ -891,9 +1072,9 @@ async function gotoStage(projectRoot, targetStage, options = {}) {
|
|
|
891
1072
|
timestamp,
|
|
892
1073
|
reason: options.reason
|
|
893
1074
|
});
|
|
894
|
-
await ensureDirAsync(
|
|
1075
|
+
await ensureDirAsync(path5.join(projectRoot, "state"));
|
|
895
1076
|
await writeJson(historyPath, history);
|
|
896
|
-
const handoffPath =
|
|
1077
|
+
const handoffPath = path5.join(projectRoot, "stages", currentStage, "HANDOFF.md");
|
|
897
1078
|
if (existsSync2(handoffPath)) {
|
|
898
1079
|
const handoffContent = await readFile(handoffPath);
|
|
899
1080
|
if (handoffContent) {
|
|
@@ -940,22 +1121,25 @@ async function listStages(projectRoot) {
|
|
|
940
1121
|
}
|
|
941
1122
|
|
|
942
1123
|
// src/cli/commands/status.ts
|
|
943
|
-
|
|
1124
|
+
init_esm_shims();
|
|
1125
|
+
import path7 from "path";
|
|
944
1126
|
import { existsSync as existsSync4, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
945
1127
|
|
|
946
1128
|
// src/core/state/checkpoint.ts
|
|
947
|
-
|
|
1129
|
+
init_esm_shims();
|
|
1130
|
+
import path6 from "path";
|
|
948
1131
|
import fs4 from "fs/promises";
|
|
949
1132
|
import { existsSync as existsSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
1133
|
+
init_shell();
|
|
950
1134
|
function generateCheckpointId(stageId) {
|
|
951
1135
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
952
1136
|
return `checkpoint_${stageId}_${timestamp}`;
|
|
953
1137
|
}
|
|
954
1138
|
function getCheckpointsDir(projectRoot) {
|
|
955
|
-
return
|
|
1139
|
+
return path6.join(projectRoot, "state", "checkpoints");
|
|
956
1140
|
}
|
|
957
1141
|
function getCheckpointPath(projectRoot, checkpointId) {
|
|
958
|
-
return
|
|
1142
|
+
return path6.join(getCheckpointsDir(projectRoot), checkpointId);
|
|
959
1143
|
}
|
|
960
1144
|
async function listCheckpoints(projectRoot) {
|
|
961
1145
|
const checkpointsDir = getCheckpointsDir(projectRoot);
|
|
@@ -965,8 +1149,8 @@ async function listCheckpoints(projectRoot) {
|
|
|
965
1149
|
const entries = readdirSync2(checkpointsDir);
|
|
966
1150
|
const checkpoints = [];
|
|
967
1151
|
for (const entry of entries) {
|
|
968
|
-
const checkpointPath =
|
|
969
|
-
const metadataPath =
|
|
1152
|
+
const checkpointPath = path6.join(checkpointsDir, entry);
|
|
1153
|
+
const metadataPath = path6.join(checkpointPath, "metadata.json");
|
|
970
1154
|
if (statSync2(checkpointPath).isDirectory() && existsSync3(metadataPath)) {
|
|
971
1155
|
const metadata = await readJson(metadataPath);
|
|
972
1156
|
if (metadata) {
|
|
@@ -979,7 +1163,7 @@ async function listCheckpoints(projectRoot) {
|
|
|
979
1163
|
);
|
|
980
1164
|
}
|
|
981
1165
|
async function getCheckpointMetadata(projectRoot, checkpointId) {
|
|
982
|
-
const metadataPath =
|
|
1166
|
+
const metadataPath = path6.join(
|
|
983
1167
|
getCheckpointPath(projectRoot, checkpointId),
|
|
984
1168
|
"metadata.json"
|
|
985
1169
|
);
|
|
@@ -998,23 +1182,23 @@ async function createCheckpoint(projectRoot, stageId, options = {}) {
|
|
|
998
1182
|
ensureDir(checkpointPath);
|
|
999
1183
|
const files = [];
|
|
1000
1184
|
if (includeStages) {
|
|
1001
|
-
const stagesDir =
|
|
1185
|
+
const stagesDir = path6.join(projectRoot, "stages");
|
|
1002
1186
|
if (pathExists(stagesDir)) {
|
|
1003
|
-
const destStagesDir =
|
|
1187
|
+
const destStagesDir = path6.join(checkpointPath, "stages");
|
|
1004
1188
|
copyDirSync(stagesDir, destStagesDir);
|
|
1005
1189
|
files.push("stages/");
|
|
1006
1190
|
}
|
|
1007
1191
|
}
|
|
1008
1192
|
if (includeState) {
|
|
1009
|
-
const stateDir =
|
|
1193
|
+
const stateDir = path6.join(projectRoot, "state");
|
|
1010
1194
|
if (pathExists(stateDir)) {
|
|
1011
|
-
const destStateDir =
|
|
1195
|
+
const destStateDir = path6.join(checkpointPath, "state");
|
|
1012
1196
|
ensureDir(destStateDir);
|
|
1013
1197
|
const stateFiles = readdirSync2(stateDir);
|
|
1014
1198
|
for (const file of stateFiles) {
|
|
1015
1199
|
if (file === "checkpoints") continue;
|
|
1016
|
-
const srcPath =
|
|
1017
|
-
const destPath =
|
|
1200
|
+
const srcPath = path6.join(stateDir, file);
|
|
1201
|
+
const destPath = path6.join(destStateDir, file);
|
|
1018
1202
|
if (statSync2(srcPath).isDirectory()) {
|
|
1019
1203
|
copyDirSync(srcPath, destPath);
|
|
1020
1204
|
} else {
|
|
@@ -1026,9 +1210,9 @@ async function createCheckpoint(projectRoot, stageId, options = {}) {
|
|
|
1026
1210
|
}
|
|
1027
1211
|
}
|
|
1028
1212
|
if (includeConfig) {
|
|
1029
|
-
const configDir =
|
|
1213
|
+
const configDir = path6.join(projectRoot, "config");
|
|
1030
1214
|
if (pathExists(configDir)) {
|
|
1031
|
-
const destConfigDir =
|
|
1215
|
+
const destConfigDir = path6.join(checkpointPath, "config");
|
|
1032
1216
|
copyDirSync(configDir, destConfigDir);
|
|
1033
1217
|
files.push("config/");
|
|
1034
1218
|
}
|
|
@@ -1040,7 +1224,7 @@ async function createCheckpoint(projectRoot, stageId, options = {}) {
|
|
|
1040
1224
|
description,
|
|
1041
1225
|
files
|
|
1042
1226
|
};
|
|
1043
|
-
await writeJson(
|
|
1227
|
+
await writeJson(path6.join(checkpointPath, "metadata.json"), metadata);
|
|
1044
1228
|
await addCheckpointToProgress(projectRoot, {
|
|
1045
1229
|
id: checkpointId,
|
|
1046
1230
|
stage: stageId,
|
|
@@ -1069,13 +1253,13 @@ async function restoreCheckpoint(projectRoot, checkpointId, options = {}) {
|
|
|
1069
1253
|
try {
|
|
1070
1254
|
if (partial && files.length > 0) {
|
|
1071
1255
|
for (const file of files) {
|
|
1072
|
-
const srcPath =
|
|
1073
|
-
const destPath =
|
|
1256
|
+
const srcPath = path6.join(checkpointPath, file);
|
|
1257
|
+
const destPath = path6.join(projectRoot, file);
|
|
1074
1258
|
if (pathExists(srcPath)) {
|
|
1075
1259
|
if (statSync2(srcPath).isDirectory()) {
|
|
1076
1260
|
copyDirSync(srcPath, destPath);
|
|
1077
1261
|
} else {
|
|
1078
|
-
ensureDir(
|
|
1262
|
+
ensureDir(path6.dirname(destPath));
|
|
1079
1263
|
const content = await fs4.readFile(srcPath);
|
|
1080
1264
|
await fs4.writeFile(destPath, content);
|
|
1081
1265
|
}
|
|
@@ -1083,21 +1267,21 @@ async function restoreCheckpoint(projectRoot, checkpointId, options = {}) {
|
|
|
1083
1267
|
}
|
|
1084
1268
|
} else {
|
|
1085
1269
|
if (restoreStages) {
|
|
1086
|
-
const srcStagesDir =
|
|
1270
|
+
const srcStagesDir = path6.join(checkpointPath, "stages");
|
|
1087
1271
|
if (pathExists(srcStagesDir)) {
|
|
1088
|
-
const destStagesDir =
|
|
1272
|
+
const destStagesDir = path6.join(projectRoot, "stages");
|
|
1089
1273
|
await remove(destStagesDir);
|
|
1090
1274
|
copyDirSync(srcStagesDir, destStagesDir);
|
|
1091
1275
|
}
|
|
1092
1276
|
}
|
|
1093
1277
|
if (restoreState) {
|
|
1094
|
-
const srcStateDir =
|
|
1278
|
+
const srcStateDir = path6.join(checkpointPath, "state");
|
|
1095
1279
|
if (pathExists(srcStateDir)) {
|
|
1096
|
-
const destStateDir =
|
|
1280
|
+
const destStateDir = path6.join(projectRoot, "state");
|
|
1097
1281
|
const stateFiles = readdirSync2(srcStateDir);
|
|
1098
1282
|
for (const file of stateFiles) {
|
|
1099
|
-
const srcPath =
|
|
1100
|
-
const destPath =
|
|
1283
|
+
const srcPath = path6.join(srcStateDir, file);
|
|
1284
|
+
const destPath = path6.join(destStateDir, file);
|
|
1101
1285
|
if (statSync2(srcPath).isDirectory()) {
|
|
1102
1286
|
if (pathExists(destPath)) await remove(destPath);
|
|
1103
1287
|
copyDirSync(srcPath, destPath);
|
|
@@ -1109,9 +1293,9 @@ async function restoreCheckpoint(projectRoot, checkpointId, options = {}) {
|
|
|
1109
1293
|
}
|
|
1110
1294
|
}
|
|
1111
1295
|
if (restoreConfig) {
|
|
1112
|
-
const srcConfigDir =
|
|
1296
|
+
const srcConfigDir = path6.join(checkpointPath, "config");
|
|
1113
1297
|
if (pathExists(srcConfigDir)) {
|
|
1114
|
-
const destConfigDir =
|
|
1298
|
+
const destConfigDir = path6.join(projectRoot, "config");
|
|
1115
1299
|
await remove(destConfigDir);
|
|
1116
1300
|
copyDirSync(srcConfigDir, destConfigDir);
|
|
1117
1301
|
}
|
|
@@ -1145,6 +1329,7 @@ async function cleanupCheckpoints(projectRoot, maxRetention = 10, preserveMilest
|
|
|
1145
1329
|
}
|
|
1146
1330
|
|
|
1147
1331
|
// src/cli/commands/status.ts
|
|
1332
|
+
init_logger();
|
|
1148
1333
|
async function showStatus(projectRoot) {
|
|
1149
1334
|
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
1150
1335
|
console.log("\u{1F4CA} Pipeline Status");
|
|
@@ -1246,17 +1431,17 @@ async function showDashboard(projectRoot) {
|
|
|
1246
1431
|
console.log("\n\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
1247
1432
|
console.log("\u2502 \u{1F4C1} Stage Outputs \u2502");
|
|
1248
1433
|
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
1249
|
-
const stagesDir =
|
|
1434
|
+
const stagesDir = path7.join(projectRoot, "stages");
|
|
1250
1435
|
if (existsSync4(stagesDir)) {
|
|
1251
1436
|
for (const stage of STAGE_IDS) {
|
|
1252
|
-
const outputsDir =
|
|
1437
|
+
const outputsDir = path7.join(stagesDir, stage, "outputs");
|
|
1253
1438
|
if (existsSync4(outputsDir)) {
|
|
1254
1439
|
const files = readdirSync3(outputsDir).filter((f) => f.endsWith(".md"));
|
|
1255
1440
|
if (files.length > 0) {
|
|
1256
1441
|
console.log(`
|
|
1257
1442
|
${stage}:`);
|
|
1258
1443
|
for (const file of files.slice(0, 3)) {
|
|
1259
|
-
const filePath =
|
|
1444
|
+
const filePath = path7.join(outputsDir, file);
|
|
1260
1445
|
const stat = statSync3(filePath);
|
|
1261
1446
|
const size = formatFileSize(stat.size);
|
|
1262
1447
|
console.log(` \u2022 ${file} (${size})`);
|
|
@@ -1286,12 +1471,16 @@ function formatFileSize(bytes) {
|
|
|
1286
1471
|
}
|
|
1287
1472
|
|
|
1288
1473
|
// src/cli/commands/validate.ts
|
|
1474
|
+
init_esm_shims();
|
|
1289
1475
|
import { existsSync as existsSync6 } from "fs";
|
|
1290
|
-
import
|
|
1476
|
+
import path9 from "path";
|
|
1291
1477
|
|
|
1292
1478
|
// src/core/config/validator.ts
|
|
1293
|
-
|
|
1479
|
+
init_esm_shims();
|
|
1480
|
+
import path8 from "path";
|
|
1294
1481
|
import { existsSync as existsSync5, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
1482
|
+
init_shell();
|
|
1483
|
+
init_logger();
|
|
1295
1484
|
var VALIDATION_RULES = [
|
|
1296
1485
|
"model_references",
|
|
1297
1486
|
"parallel_alignment",
|
|
@@ -1315,8 +1504,8 @@ var ConfigValidator = class {
|
|
|
1315
1504
|
summary;
|
|
1316
1505
|
constructor(projectRoot, options = {}) {
|
|
1317
1506
|
this.projectRoot = projectRoot;
|
|
1318
|
-
this.configDir =
|
|
1319
|
-
this.stagesDir =
|
|
1507
|
+
this.configDir = path8.join(projectRoot, "config");
|
|
1508
|
+
this.stagesDir = path8.join(projectRoot, "stages");
|
|
1320
1509
|
this.options = options;
|
|
1321
1510
|
this.summary = {
|
|
1322
1511
|
critical: 0,
|
|
@@ -1382,7 +1571,7 @@ var ConfigValidator = class {
|
|
|
1382
1571
|
*/
|
|
1383
1572
|
async getDefinedModels() {
|
|
1384
1573
|
const modelsConfig = await loadYaml(
|
|
1385
|
-
|
|
1574
|
+
path8.join(this.configDir, "models.yaml")
|
|
1386
1575
|
);
|
|
1387
1576
|
if (!modelsConfig?.models) return [];
|
|
1388
1577
|
return Object.keys(modelsConfig.models);
|
|
@@ -1391,7 +1580,7 @@ var ConfigValidator = class {
|
|
|
1391
1580
|
* Get parallel capable stages from ai_collaboration.yaml
|
|
1392
1581
|
*/
|
|
1393
1582
|
async getParallelStages() {
|
|
1394
|
-
const collabConfig = await loadYaml(
|
|
1583
|
+
const collabConfig = await loadYaml(path8.join(this.configDir, "ai_collaboration.yaml"));
|
|
1395
1584
|
return collabConfig?.execution_policy?.stage_classification?.parallel_capable ?? [];
|
|
1396
1585
|
}
|
|
1397
1586
|
/**
|
|
@@ -1403,7 +1592,7 @@ var ConfigValidator = class {
|
|
|
1403
1592
|
const definedModels = await this.getDefinedModels();
|
|
1404
1593
|
const stages = this.getStagesToValidate();
|
|
1405
1594
|
for (const stage of stages) {
|
|
1406
|
-
const configPath =
|
|
1595
|
+
const configPath = path8.join(this.stagesDir, stage, "config.yaml");
|
|
1407
1596
|
if (!existsSync5(configPath)) continue;
|
|
1408
1597
|
const stageConfig = await loadYaml(configPath);
|
|
1409
1598
|
if (!stageConfig) continue;
|
|
@@ -1450,7 +1639,7 @@ var ConfigValidator = class {
|
|
|
1450
1639
|
const parallelStages = await this.getParallelStages();
|
|
1451
1640
|
const stages = this.getStagesToValidate();
|
|
1452
1641
|
for (const stage of stages) {
|
|
1453
|
-
const configPath =
|
|
1642
|
+
const configPath = path8.join(this.stagesDir, stage, "config.yaml");
|
|
1454
1643
|
if (!existsSync5(configPath)) continue;
|
|
1455
1644
|
const stageConfig = await loadYaml(configPath);
|
|
1456
1645
|
if (!stageConfig) continue;
|
|
@@ -1491,7 +1680,7 @@ var ConfigValidator = class {
|
|
|
1491
1680
|
const parallelStages = await this.getParallelStages();
|
|
1492
1681
|
for (const stage of parallelStages) {
|
|
1493
1682
|
if (this.options.stage && this.options.stage !== stage) continue;
|
|
1494
|
-
const configPath =
|
|
1683
|
+
const configPath = path8.join(this.stagesDir, stage, "config.yaml");
|
|
1495
1684
|
if (!existsSync5(configPath)) {
|
|
1496
1685
|
this.addResult("critical", "collaboration_consistency", `parallel stage missing config.yaml`, stage);
|
|
1497
1686
|
continue;
|
|
@@ -1521,17 +1710,17 @@ var ConfigValidator = class {
|
|
|
1521
1710
|
log("\n\u25B8 Rule: file_references [HIGH]", "blue");
|
|
1522
1711
|
const stages = this.getStagesToValidate();
|
|
1523
1712
|
for (const stage of stages) {
|
|
1524
|
-
const configPath =
|
|
1713
|
+
const configPath = path8.join(this.stagesDir, stage, "config.yaml");
|
|
1525
1714
|
if (!existsSync5(configPath)) continue;
|
|
1526
1715
|
const stageConfig = await loadYaml(configPath);
|
|
1527
1716
|
if (!stageConfig) continue;
|
|
1528
1717
|
const requiredInputs = stageConfig.inputs?.required ?? [];
|
|
1529
1718
|
for (const input2 of requiredInputs) {
|
|
1530
|
-
const inputPath =
|
|
1719
|
+
const inputPath = path8.join(this.stagesDir, stage, input2);
|
|
1531
1720
|
if (existsSync5(inputPath)) {
|
|
1532
1721
|
this.addPass(`input file exists: ${input2}`, stage);
|
|
1533
1722
|
} else if (input2.startsWith("../")) {
|
|
1534
|
-
const refPath =
|
|
1723
|
+
const refPath = path8.join(this.stagesDir, stage, input2);
|
|
1535
1724
|
if (existsSync5(refPath)) {
|
|
1536
1725
|
this.addPass(`cross-stage input exists: ${input2}`, stage);
|
|
1537
1726
|
} else {
|
|
@@ -1541,14 +1730,14 @@ var ConfigValidator = class {
|
|
|
1541
1730
|
this.addResult("high", "file_references", `required input missing: ${input2}`, stage);
|
|
1542
1731
|
}
|
|
1543
1732
|
}
|
|
1544
|
-
const templatesDir =
|
|
1733
|
+
const templatesDir = path8.join(this.stagesDir, stage, "templates");
|
|
1545
1734
|
if (existsSync5(templatesDir)) {
|
|
1546
1735
|
const templates = readdirSync4(templatesDir).filter((f) => f.endsWith(".md"));
|
|
1547
1736
|
if (templates.length > 0) {
|
|
1548
1737
|
this.addPass(`${templates.length} output template(s) found`, stage);
|
|
1549
1738
|
}
|
|
1550
1739
|
}
|
|
1551
|
-
const claudeMd =
|
|
1740
|
+
const claudeMd = path8.join(this.stagesDir, stage, "CLAUDE.md");
|
|
1552
1741
|
if (existsSync5(claudeMd)) {
|
|
1553
1742
|
this.addPass(`CLAUDE.md exists`, stage);
|
|
1554
1743
|
} else {
|
|
@@ -1564,13 +1753,13 @@ var ConfigValidator = class {
|
|
|
1564
1753
|
log("\n\u25B8 Rule: auto_invoke [HIGH]", "blue");
|
|
1565
1754
|
const stages = this.getStagesToValidate();
|
|
1566
1755
|
for (const stage of stages) {
|
|
1567
|
-
const configPath =
|
|
1756
|
+
const configPath = path8.join(this.stagesDir, stage, "config.yaml");
|
|
1568
1757
|
if (!existsSync5(configPath)) continue;
|
|
1569
1758
|
const stageConfig = await loadYaml(configPath);
|
|
1570
1759
|
if (!stageConfig?.auto_invoke?.enabled) continue;
|
|
1571
1760
|
const wrapper = stageConfig.auto_invoke.wrapper;
|
|
1572
1761
|
if (wrapper) {
|
|
1573
|
-
const wrapperPath =
|
|
1762
|
+
const wrapperPath = path8.join(this.projectRoot, wrapper);
|
|
1574
1763
|
if (existsSync5(wrapperPath)) {
|
|
1575
1764
|
try {
|
|
1576
1765
|
const stat = statSync4(wrapperPath);
|
|
@@ -1589,7 +1778,7 @@ var ConfigValidator = class {
|
|
|
1589
1778
|
}
|
|
1590
1779
|
const promptFile = stageConfig.auto_invoke.prompt_file;
|
|
1591
1780
|
if (promptFile) {
|
|
1592
|
-
const promptPath =
|
|
1781
|
+
const promptPath = path8.join(this.stagesDir, stage, promptFile);
|
|
1593
1782
|
if (existsSync5(promptPath)) {
|
|
1594
1783
|
this.addPass(`prompt file exists: ${promptFile}`, stage);
|
|
1595
1784
|
} else {
|
|
@@ -1613,9 +1802,9 @@ var ConfigValidator = class {
|
|
|
1613
1802
|
async validateExecutionMode() {
|
|
1614
1803
|
log("\n\u25B8 Rule: execution_mode [HIGH]", "blue");
|
|
1615
1804
|
const stages = this.getStagesToValidate();
|
|
1616
|
-
const modelsConfig = await loadYaml(
|
|
1805
|
+
const modelsConfig = await loadYaml(path8.join(this.configDir, "models.yaml"));
|
|
1617
1806
|
for (const stage of stages) {
|
|
1618
|
-
const configPath =
|
|
1807
|
+
const configPath = path8.join(this.stagesDir, stage, "config.yaml");
|
|
1619
1808
|
if (!existsSync5(configPath)) continue;
|
|
1620
1809
|
const stageConfig = await loadYaml(configPath);
|
|
1621
1810
|
if (!stageConfig?.execution?.mode) continue;
|
|
@@ -1638,7 +1827,7 @@ var ConfigValidator = class {
|
|
|
1638
1827
|
log("\n\u25B8 Rule: ai_wrapper_health [HIGH]", "blue");
|
|
1639
1828
|
const wrappers = ["ai-call.sh", "gemini-wrapper.sh", "codex-wrapper.sh"];
|
|
1640
1829
|
for (const wrapper of wrappers) {
|
|
1641
|
-
const wrapperPath =
|
|
1830
|
+
const wrapperPath = path8.join(this.projectRoot, "scripts", wrapper);
|
|
1642
1831
|
if (!existsSync5(wrapperPath)) {
|
|
1643
1832
|
this.addResult("high", "ai_wrapper_health", `Wrapper script not found: scripts/${wrapper}`);
|
|
1644
1833
|
continue;
|
|
@@ -1667,14 +1856,14 @@ var ConfigValidator = class {
|
|
|
1667
1856
|
*/
|
|
1668
1857
|
async validateMCPServers() {
|
|
1669
1858
|
log("\n\u25B8 Rule: mcp_servers [MEDIUM]", "blue");
|
|
1670
|
-
const fallbackConfig = await loadYaml(
|
|
1859
|
+
const fallbackConfig = await loadYaml(path8.join(this.configDir, "mcp_fallbacks.yaml"));
|
|
1671
1860
|
if (!fallbackConfig) {
|
|
1672
1861
|
this.addResult("medium", "mcp_servers", `mcp_fallbacks.yaml not found, cannot validate MCP fallbacks`);
|
|
1673
1862
|
return;
|
|
1674
1863
|
}
|
|
1675
1864
|
const stages = this.getStagesToValidate();
|
|
1676
1865
|
for (const stage of stages) {
|
|
1677
|
-
const configPath =
|
|
1866
|
+
const configPath = path8.join(this.stagesDir, stage, "config.yaml");
|
|
1678
1867
|
if (!existsSync5(configPath)) continue;
|
|
1679
1868
|
const stageConfig = await loadYaml(configPath);
|
|
1680
1869
|
const mcpServers = stageConfig?.mcp_servers ?? [];
|
|
@@ -1694,7 +1883,7 @@ var ConfigValidator = class {
|
|
|
1694
1883
|
*/
|
|
1695
1884
|
async validateEpicCycles() {
|
|
1696
1885
|
log("\n\u25B8 Rule: epic_cycles [MEDIUM]", "blue");
|
|
1697
|
-
const epicConfig = await loadYaml(
|
|
1886
|
+
const epicConfig = await loadYaml(path8.join(this.configDir, "epic_cycles.yaml"));
|
|
1698
1887
|
if (!epicConfig) {
|
|
1699
1888
|
this.addResult("medium", "epic_cycles", `epic_cycles.yaml not found`);
|
|
1700
1889
|
return;
|
|
@@ -1725,7 +1914,7 @@ var ConfigValidator = class {
|
|
|
1725
1914
|
*/
|
|
1726
1915
|
async validateRequirementsRefinement() {
|
|
1727
1916
|
log("\n\u25B8 Rule: requirements_refinement [MEDIUM]", "blue");
|
|
1728
|
-
const reqConfig = await loadYaml(
|
|
1917
|
+
const reqConfig = await loadYaml(path8.join(this.configDir, "requirements_refinement.yaml"));
|
|
1729
1918
|
if (!reqConfig) {
|
|
1730
1919
|
this.addResult("medium", "requirements_refinement", `requirements_refinement.yaml not found`);
|
|
1731
1920
|
return;
|
|
@@ -1755,7 +1944,7 @@ var ConfigValidator = class {
|
|
|
1755
1944
|
*/
|
|
1756
1945
|
async validateImplementationOrder() {
|
|
1757
1946
|
log("\n\u25B8 Rule: implementation_order [MEDIUM]", "blue");
|
|
1758
|
-
const implConfig = await loadYaml(
|
|
1947
|
+
const implConfig = await loadYaml(path8.join(this.configDir, "implementation_order.yaml"));
|
|
1759
1948
|
if (!implConfig) {
|
|
1760
1949
|
this.addResult("medium", "implementation_order", `implementation_order.yaml not found`);
|
|
1761
1950
|
return;
|
|
@@ -1787,7 +1976,7 @@ var ConfigValidator = class {
|
|
|
1787
1976
|
*/
|
|
1788
1977
|
async validateNotionIntegration() {
|
|
1789
1978
|
log("\n\u25B8 Rule: notion_integration [MEDIUM]", "blue");
|
|
1790
|
-
const fallbackConfig = await loadYaml(
|
|
1979
|
+
const fallbackConfig = await loadYaml(path8.join(this.configDir, "mcp_fallbacks.yaml"));
|
|
1791
1980
|
if (!fallbackConfig) {
|
|
1792
1981
|
this.addResult("medium", "notion_integration", `mcp_fallbacks.yaml not found, cannot validate Notion integration`);
|
|
1793
1982
|
return;
|
|
@@ -1826,7 +2015,7 @@ var ConfigValidator = class {
|
|
|
1826
2015
|
log("\n\u25B8 Rule: prerequisites [MEDIUM]", "blue");
|
|
1827
2016
|
const stages = this.getStagesToValidate();
|
|
1828
2017
|
for (const stage of stages) {
|
|
1829
|
-
const configPath =
|
|
2018
|
+
const configPath = path8.join(this.stagesDir, stage, "config.yaml");
|
|
1830
2019
|
if (!existsSync5(configPath)) continue;
|
|
1831
2020
|
const stageConfig = await loadYaml(configPath);
|
|
1832
2021
|
const prereqs = stageConfig?.transition?.prerequisites ?? [];
|
|
@@ -1964,13 +2153,14 @@ function printRecoveryGuide() {
|
|
|
1964
2153
|
}
|
|
1965
2154
|
|
|
1966
2155
|
// src/cli/commands/validate.ts
|
|
2156
|
+
init_logger();
|
|
1967
2157
|
async function validateConfig(projectRoot, options = {}) {
|
|
1968
2158
|
if (options.recoveryGuide) {
|
|
1969
2159
|
printRecoveryGuide();
|
|
1970
2160
|
return 0;
|
|
1971
2161
|
}
|
|
1972
|
-
const configDir =
|
|
1973
|
-
const stagesDir =
|
|
2162
|
+
const configDir = path9.join(projectRoot, "config");
|
|
2163
|
+
const stagesDir = path9.join(projectRoot, "stages");
|
|
1974
2164
|
if (!existsSync6(configDir)) {
|
|
1975
2165
|
logError(`Config directory not found: ${configDir}`);
|
|
1976
2166
|
return 1;
|
|
@@ -1979,7 +2169,7 @@ async function validateConfig(projectRoot, options = {}) {
|
|
|
1979
2169
|
logError(`Stages directory not found: ${stagesDir}`);
|
|
1980
2170
|
return 1;
|
|
1981
2171
|
}
|
|
1982
|
-
if (options.stage && !existsSync6(
|
|
2172
|
+
if (options.stage && !existsSync6(path9.join(stagesDir, options.stage))) {
|
|
1983
2173
|
logError(`Invalid stage: ${options.stage}`);
|
|
1984
2174
|
return 1;
|
|
1985
2175
|
}
|
|
@@ -2019,6 +2209,8 @@ async function validateConfig(projectRoot, options = {}) {
|
|
|
2019
2209
|
}
|
|
2020
2210
|
|
|
2021
2211
|
// src/cli/commands/checkpoint.ts
|
|
2212
|
+
init_esm_shims();
|
|
2213
|
+
init_logger();
|
|
2022
2214
|
async function createCheckpointCommand(projectRoot, options = {}) {
|
|
2023
2215
|
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
2024
2216
|
console.log("\u{1F4BE} Create Checkpoint");
|
|
@@ -2165,17 +2357,19 @@ async function cleanupCheckpointsCommand(projectRoot, maxRetention = 10) {
|
|
|
2165
2357
|
}
|
|
2166
2358
|
|
|
2167
2359
|
// src/cli/commands/import.ts
|
|
2360
|
+
init_esm_shims();
|
|
2361
|
+
init_logger();
|
|
2168
2362
|
import fs5 from "fs";
|
|
2169
|
-
import
|
|
2170
|
-
import { fileURLToPath as
|
|
2363
|
+
import path10 from "path";
|
|
2364
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2171
2365
|
import chalk3 from "chalk";
|
|
2172
|
-
var __filename3 =
|
|
2173
|
-
var __dirname3 =
|
|
2366
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
2367
|
+
var __dirname3 = path10.dirname(__filename3);
|
|
2174
2368
|
function detectImplementation(projectRoot) {
|
|
2175
2369
|
const evidence = [];
|
|
2176
2370
|
const srcPatterns = ["src/", "app/", "lib/", "pages/", "components/"];
|
|
2177
2371
|
for (const pattern of srcPatterns) {
|
|
2178
|
-
const dir =
|
|
2372
|
+
const dir = path10.join(projectRoot, pattern);
|
|
2179
2373
|
if (fs5.existsSync(dir)) {
|
|
2180
2374
|
const files = fs5.readdirSync(dir);
|
|
2181
2375
|
const codeFiles = files.filter(
|
|
@@ -2188,7 +2382,7 @@ function detectImplementation(projectRoot) {
|
|
|
2188
2382
|
}
|
|
2189
2383
|
const projectFiles = ["package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile"];
|
|
2190
2384
|
for (const file of projectFiles) {
|
|
2191
|
-
if (fs5.existsSync(
|
|
2385
|
+
if (fs5.existsSync(path10.join(projectRoot, file))) {
|
|
2192
2386
|
evidence.push(`Found ${file}`);
|
|
2193
2387
|
}
|
|
2194
2388
|
}
|
|
@@ -2203,7 +2397,7 @@ function detectTests(projectRoot) {
|
|
|
2203
2397
|
const evidence = [];
|
|
2204
2398
|
const testDirs = ["test/", "tests/", "__tests__/", "spec/", "e2e/"];
|
|
2205
2399
|
for (const dir of testDirs) {
|
|
2206
|
-
const testPath =
|
|
2400
|
+
const testPath = path10.join(projectRoot, dir);
|
|
2207
2401
|
if (fs5.existsSync(testPath)) {
|
|
2208
2402
|
evidence.push(`Found test directory: ${dir}`);
|
|
2209
2403
|
}
|
|
@@ -2220,11 +2414,11 @@ function detectTests(projectRoot) {
|
|
|
2220
2414
|
"setup.cfg"
|
|
2221
2415
|
];
|
|
2222
2416
|
for (const config of testConfigs) {
|
|
2223
|
-
if (fs5.existsSync(
|
|
2417
|
+
if (fs5.existsSync(path10.join(projectRoot, config))) {
|
|
2224
2418
|
evidence.push(`Found test config: ${config}`);
|
|
2225
2419
|
}
|
|
2226
2420
|
}
|
|
2227
|
-
const pkgPath =
|
|
2421
|
+
const pkgPath = path10.join(projectRoot, "package.json");
|
|
2228
2422
|
if (fs5.existsSync(pkgPath)) {
|
|
2229
2423
|
try {
|
|
2230
2424
|
const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
@@ -2259,7 +2453,7 @@ function detectDeployment(projectRoot) {
|
|
|
2259
2453
|
"render.yaml"
|
|
2260
2454
|
];
|
|
2261
2455
|
for (const ciPath of ciPaths) {
|
|
2262
|
-
const fullPath =
|
|
2456
|
+
const fullPath = path10.join(projectRoot, ciPath);
|
|
2263
2457
|
if (fs5.existsSync(fullPath)) {
|
|
2264
2458
|
evidence.push(`Found CI/CD config: ${ciPath}`);
|
|
2265
2459
|
}
|
|
@@ -2273,12 +2467,12 @@ function detectDeployment(projectRoot) {
|
|
|
2273
2467
|
}
|
|
2274
2468
|
function detectDocumentation(projectRoot) {
|
|
2275
2469
|
const detections = [];
|
|
2276
|
-
const hasReadme = fs5.existsSync(
|
|
2277
|
-
const hasArchDocs = ["ARCHITECTURE.md", "docs/architecture.md", "docs/ARCHITECTURE.md"].some((p) => fs5.existsSync(
|
|
2278
|
-
const hasDocs = fs5.existsSync(
|
|
2470
|
+
const hasReadme = fs5.existsSync(path10.join(projectRoot, "README.md"));
|
|
2471
|
+
const hasArchDocs = ["ARCHITECTURE.md", "docs/architecture.md", "docs/ARCHITECTURE.md"].some((p) => fs5.existsSync(path10.join(projectRoot, p)));
|
|
2472
|
+
const hasDocs = fs5.existsSync(path10.join(projectRoot, "docs/"));
|
|
2279
2473
|
const brainstormEvidence = [];
|
|
2280
2474
|
if (hasReadme) {
|
|
2281
|
-
const readme = fs5.readFileSync(
|
|
2475
|
+
const readme = fs5.readFileSync(path10.join(projectRoot, "README.md"), "utf-8");
|
|
2282
2476
|
if (readme.length > 500) {
|
|
2283
2477
|
brainstormEvidence.push("README.md with substantial content");
|
|
2284
2478
|
}
|
|
@@ -2311,7 +2505,7 @@ function detectUIUX(projectRoot) {
|
|
|
2311
2505
|
"src/styles/"
|
|
2312
2506
|
];
|
|
2313
2507
|
for (const dir of uiDirs) {
|
|
2314
|
-
if (fs5.existsSync(
|
|
2508
|
+
if (fs5.existsSync(path10.join(projectRoot, dir))) {
|
|
2315
2509
|
evidence.push(`Found UI directory: ${dir}`);
|
|
2316
2510
|
}
|
|
2317
2511
|
}
|
|
@@ -2323,7 +2517,7 @@ function detectUIUX(projectRoot) {
|
|
|
2323
2517
|
".storybook/"
|
|
2324
2518
|
];
|
|
2325
2519
|
for (const config of styleConfigs) {
|
|
2326
|
-
if (fs5.existsSync(
|
|
2520
|
+
if (fs5.existsSync(path10.join(projectRoot, config))) {
|
|
2327
2521
|
evidence.push(`Found style config: ${config}`);
|
|
2328
2522
|
}
|
|
2329
2523
|
}
|
|
@@ -2350,11 +2544,11 @@ function detectQA(projectRoot) {
|
|
|
2350
2544
|
".stylelintrc"
|
|
2351
2545
|
];
|
|
2352
2546
|
for (const config of qaConfigs) {
|
|
2353
|
-
if (fs5.existsSync(
|
|
2547
|
+
if (fs5.existsSync(path10.join(projectRoot, config))) {
|
|
2354
2548
|
evidence.push(`Found QA config: ${config}`);
|
|
2355
2549
|
}
|
|
2356
2550
|
}
|
|
2357
|
-
const tsconfigPath =
|
|
2551
|
+
const tsconfigPath = path10.join(projectRoot, "tsconfig.json");
|
|
2358
2552
|
if (fs5.existsSync(tsconfigPath)) {
|
|
2359
2553
|
try {
|
|
2360
2554
|
const tsconfig = fs5.readFileSync(tsconfigPath, "utf-8");
|
|
@@ -2401,17 +2595,17 @@ function analyzeProject(projectRoot) {
|
|
|
2401
2595
|
return detections.sort((a, b) => STAGE_IDS.indexOf(a.stageId) - STAGE_IDS.indexOf(b.stageId));
|
|
2402
2596
|
}
|
|
2403
2597
|
async function importProject(targetPath, options = {}) {
|
|
2404
|
-
const projectRoot =
|
|
2598
|
+
const projectRoot = path10.resolve(targetPath);
|
|
2405
2599
|
if (!fs5.existsSync(projectRoot)) {
|
|
2406
2600
|
log(`Error: Directory not found: ${projectRoot}`, "red");
|
|
2407
2601
|
process.exit(1);
|
|
2408
2602
|
}
|
|
2409
|
-
if (fs5.existsSync(
|
|
2603
|
+
if (fs5.existsSync(path10.join(projectRoot, "state", "progress.json"))) {
|
|
2410
2604
|
log("This project already has claude-symphony installed.", "yellow");
|
|
2411
2605
|
log("Use /auto-pilot or /resume to continue the pipeline.", "yellow");
|
|
2412
2606
|
return;
|
|
2413
2607
|
}
|
|
2414
|
-
const projectName =
|
|
2608
|
+
const projectName = path10.basename(projectRoot);
|
|
2415
2609
|
console.log("");
|
|
2416
2610
|
log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501", "cyan");
|
|
2417
2611
|
log(` Analyzing project: ${projectName}`, "cyan");
|
|
@@ -2439,8 +2633,8 @@ async function importProject(targetPath, options = {}) {
|
|
|
2439
2633
|
return;
|
|
2440
2634
|
}
|
|
2441
2635
|
log("Installing claude-symphony...", "cyan");
|
|
2442
|
-
const packageRoot =
|
|
2443
|
-
const templateDir =
|
|
2636
|
+
const packageRoot = path10.resolve(__dirname3, "..", "..");
|
|
2637
|
+
const templateDir = path10.join(packageRoot, "template");
|
|
2444
2638
|
if (!fs5.existsSync(templateDir)) {
|
|
2445
2639
|
log(`Error: Template directory not found: ${templateDir}`, "red");
|
|
2446
2640
|
process.exit(1);
|
|
@@ -2454,7 +2648,7 @@ async function importProject(targetPath, options = {}) {
|
|
|
2454
2648
|
".claude/agents"
|
|
2455
2649
|
];
|
|
2456
2650
|
for (const dir of symphonyDirs) {
|
|
2457
|
-
ensureDir(
|
|
2651
|
+
ensureDir(path10.join(projectRoot, dir));
|
|
2458
2652
|
}
|
|
2459
2653
|
const templateFiles = [
|
|
2460
2654
|
"CLAUDE.md",
|
|
@@ -2466,45 +2660,45 @@ async function importProject(targetPath, options = {}) {
|
|
|
2466
2660
|
".claude/settings.json"
|
|
2467
2661
|
];
|
|
2468
2662
|
for (const file of templateFiles) {
|
|
2469
|
-
const src =
|
|
2470
|
-
const dest =
|
|
2663
|
+
const src = path10.join(templateDir, file);
|
|
2664
|
+
const dest = path10.join(projectRoot, file);
|
|
2471
2665
|
if (fs5.existsSync(src) && !fs5.existsSync(dest)) {
|
|
2472
|
-
ensureDir(
|
|
2666
|
+
ensureDir(path10.dirname(dest));
|
|
2473
2667
|
fs5.copyFileSync(src, dest);
|
|
2474
2668
|
}
|
|
2475
2669
|
}
|
|
2476
|
-
const commandsDir =
|
|
2670
|
+
const commandsDir = path10.join(templateDir, ".claude", "commands");
|
|
2477
2671
|
if (fs5.existsSync(commandsDir)) {
|
|
2478
|
-
const destCommands =
|
|
2672
|
+
const destCommands = path10.join(projectRoot, ".claude", "commands");
|
|
2479
2673
|
ensureDir(destCommands);
|
|
2480
2674
|
for (const cmd of fs5.readdirSync(commandsDir)) {
|
|
2481
|
-
const dest =
|
|
2675
|
+
const dest = path10.join(destCommands, cmd);
|
|
2482
2676
|
if (!fs5.existsSync(dest)) {
|
|
2483
|
-
fs5.copyFileSync(
|
|
2677
|
+
fs5.copyFileSync(path10.join(commandsDir, cmd), dest);
|
|
2484
2678
|
}
|
|
2485
2679
|
}
|
|
2486
2680
|
}
|
|
2487
|
-
const agentsDir =
|
|
2681
|
+
const agentsDir = path10.join(templateDir, ".claude", "agents");
|
|
2488
2682
|
if (fs5.existsSync(agentsDir)) {
|
|
2489
2683
|
for (const agent of fs5.readdirSync(agentsDir)) {
|
|
2490
|
-
const agentSrc =
|
|
2491
|
-
const agentDest =
|
|
2684
|
+
const agentSrc = path10.join(agentsDir, agent);
|
|
2685
|
+
const agentDest = path10.join(projectRoot, ".claude", "agents", agent);
|
|
2492
2686
|
if (fs5.statSync(agentSrc).isDirectory() && !fs5.existsSync(agentDest)) {
|
|
2493
2687
|
copyDirSync(agentSrc, agentDest);
|
|
2494
2688
|
}
|
|
2495
2689
|
}
|
|
2496
2690
|
}
|
|
2497
2691
|
for (const stageId of STAGE_IDS) {
|
|
2498
|
-
const stageDir =
|
|
2499
|
-
ensureDir(
|
|
2500
|
-
const claudeSrc =
|
|
2501
|
-
const claudeDest =
|
|
2692
|
+
const stageDir = path10.join(projectRoot, "stages", stageId);
|
|
2693
|
+
ensureDir(path10.join(stageDir, "outputs"));
|
|
2694
|
+
const claudeSrc = path10.join(templateDir, "stages", stageId, "CLAUDE.md");
|
|
2695
|
+
const claudeDest = path10.join(stageDir, "CLAUDE.md");
|
|
2502
2696
|
if (fs5.existsSync(claudeSrc) && !fs5.existsSync(claudeDest)) {
|
|
2503
2697
|
fs5.copyFileSync(claudeSrc, claudeDest);
|
|
2504
2698
|
}
|
|
2505
2699
|
}
|
|
2506
2700
|
for (const stageId of STAGE_IDS) {
|
|
2507
|
-
ensureDir(
|
|
2701
|
+
ensureDir(path10.join(projectRoot, "references", stageId));
|
|
2508
2702
|
}
|
|
2509
2703
|
log(" Template installed (non-destructive)", "green");
|
|
2510
2704
|
const progress = createInitialProgress(projectName);
|
|
@@ -2533,13 +2727,280 @@ async function importProject(targetPath, options = {}) {
|
|
|
2533
2727
|
console.log("");
|
|
2534
2728
|
}
|
|
2535
2729
|
|
|
2730
|
+
// src/cli/commands/ai-call.ts
|
|
2731
|
+
init_esm_shims();
|
|
2732
|
+
import { readFileSync } from "fs";
|
|
2733
|
+
import path12 from "path";
|
|
2734
|
+
|
|
2735
|
+
// src/core/ai/orchestrator.ts
|
|
2736
|
+
init_esm_shims();
|
|
2737
|
+
init_gemini();
|
|
2738
|
+
init_codex();
|
|
2739
|
+
init_logger();
|
|
2740
|
+
import path11 from "path";
|
|
2741
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2742
|
+
init_shell();
|
|
2743
|
+
async function logFallback(projectRoot, originalAi, exitCode, reason) {
|
|
2744
|
+
const stateDir = path11.join(projectRoot, "state");
|
|
2745
|
+
const logPath = path11.join(stateDir, "fallback_log.json");
|
|
2746
|
+
try {
|
|
2747
|
+
await ensureDirAsync(stateDir);
|
|
2748
|
+
let log2 = { fallbacks: [] };
|
|
2749
|
+
if (existsSync7(logPath)) {
|
|
2750
|
+
const existing = await readJson(logPath);
|
|
2751
|
+
if (existing) {
|
|
2752
|
+
log2 = existing;
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
log2.fallbacks.push({
|
|
2756
|
+
timestamp: getTimestamp(),
|
|
2757
|
+
originalAi,
|
|
2758
|
+
exitCode,
|
|
2759
|
+
reason
|
|
2760
|
+
});
|
|
2761
|
+
await writeJson(logPath, log2);
|
|
2762
|
+
} catch {
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
async function callAI(model, prompt, options = {}) {
|
|
2766
|
+
const { timeout = 300, projectRoot, quiet = false } = options;
|
|
2767
|
+
const originalLog = console.log;
|
|
2768
|
+
if (quiet) {
|
|
2769
|
+
console.log = console.error;
|
|
2770
|
+
}
|
|
2771
|
+
try {
|
|
2772
|
+
return await callAIInner(model, prompt, { timeout, projectRoot, quiet });
|
|
2773
|
+
} finally {
|
|
2774
|
+
if (quiet) {
|
|
2775
|
+
console.log = originalLog;
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
async function callAIInner(model, prompt, options) {
|
|
2780
|
+
const { timeout, projectRoot } = options;
|
|
2781
|
+
const normalizedModel = model.toLowerCase();
|
|
2782
|
+
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
2783
|
+
logInfo("AI Call Router");
|
|
2784
|
+
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
2785
|
+
console.log(` Model: ${normalizedModel}`);
|
|
2786
|
+
console.log(` Timeout: ${timeout}s
|
|
2787
|
+
`);
|
|
2788
|
+
if (normalizedModel === "claudecode" || normalizedModel === "claude") {
|
|
2789
|
+
console.log("Routing to ClaudeCode...\n");
|
|
2790
|
+
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
2791
|
+
logSuccess("EXECUTE WITH CLAUDECODE");
|
|
2792
|
+
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
|
|
2793
|
+
console.log(prompt);
|
|
2794
|
+
console.log("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
2795
|
+
console.log("ACTION_REQUIRED: CLAUDECODE_EXECUTE");
|
|
2796
|
+
return {
|
|
2797
|
+
success: true,
|
|
2798
|
+
model: "claudecode",
|
|
2799
|
+
output: prompt,
|
|
2800
|
+
fallbackUsed: false
|
|
2801
|
+
};
|
|
2802
|
+
}
|
|
2803
|
+
let result2;
|
|
2804
|
+
switch (normalizedModel) {
|
|
2805
|
+
case "gemini":
|
|
2806
|
+
console.log("Executing gemini wrapper...\n");
|
|
2807
|
+
result2 = await callGemini(prompt, { timeout });
|
|
2808
|
+
break;
|
|
2809
|
+
case "codex":
|
|
2810
|
+
console.log("Executing codex wrapper...\n");
|
|
2811
|
+
result2 = await callCodex(prompt, { timeout });
|
|
2812
|
+
break;
|
|
2813
|
+
default:
|
|
2814
|
+
logError(`Unknown AI model: ${normalizedModel}`);
|
|
2815
|
+
console.log(" Supported: gemini, codex, claudecode");
|
|
2816
|
+
return {
|
|
2817
|
+
success: false,
|
|
2818
|
+
model: normalizedModel,
|
|
2819
|
+
fallbackUsed: false
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2822
|
+
if (result2.fallbackRequired) {
|
|
2823
|
+
console.log("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
2824
|
+
logWarning("AUTO-FALLBACK TRIGGERED");
|
|
2825
|
+
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
|
|
2826
|
+
console.log(` Primary AI: ${normalizedModel}`);
|
|
2827
|
+
console.log(` Fallback Signal: ${result2.fallbackSignal}`);
|
|
2828
|
+
console.log(` Reason: ${result2.fallbackReason}`);
|
|
2829
|
+
if (projectRoot) {
|
|
2830
|
+
const exitCode = getExitCodeForSignal(result2.fallbackSignal);
|
|
2831
|
+
await logFallback(projectRoot, normalizedModel, exitCode, result2.fallbackReason);
|
|
2832
|
+
}
|
|
2833
|
+
console.log("\nFalling back to ClaudeCode...\n");
|
|
2834
|
+
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
2835
|
+
logSuccess("EXECUTE WITH CLAUDECODE");
|
|
2836
|
+
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
|
|
2837
|
+
console.log(prompt);
|
|
2838
|
+
console.log("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
2839
|
+
console.log("ACTION_REQUIRED: CLAUDECODE_FALLBACK");
|
|
2840
|
+
console.log(`ORIGINAL_AI: ${normalizedModel}`);
|
|
2841
|
+
console.log(`FALLBACK_REASON: ${result2.fallbackSignal}`);
|
|
2842
|
+
return {
|
|
2843
|
+
success: false,
|
|
2844
|
+
model: normalizedModel,
|
|
2845
|
+
output: prompt,
|
|
2846
|
+
fallbackUsed: true,
|
|
2847
|
+
fallbackModel: "claudecode",
|
|
2848
|
+
fallbackReason: result2.fallbackReason
|
|
2849
|
+
};
|
|
2850
|
+
}
|
|
2851
|
+
return {
|
|
2852
|
+
success: true,
|
|
2853
|
+
model: normalizedModel,
|
|
2854
|
+
output: result2.output,
|
|
2855
|
+
fallbackUsed: false
|
|
2856
|
+
};
|
|
2857
|
+
}
|
|
2858
|
+
function getExitCodeForSignal(signal) {
|
|
2859
|
+
switch (signal) {
|
|
2860
|
+
case "CLI_NOT_FOUND":
|
|
2861
|
+
return 100;
|
|
2862
|
+
case "TIMEOUT":
|
|
2863
|
+
return 101;
|
|
2864
|
+
case "API_ERROR":
|
|
2865
|
+
return 102;
|
|
2866
|
+
case "OUTPUT_FAILED":
|
|
2867
|
+
return 103;
|
|
2868
|
+
default:
|
|
2869
|
+
return 1;
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
// src/cli/commands/ai-call.ts
|
|
2874
|
+
init_logger();
|
|
2875
|
+
|
|
2876
|
+
// src/utils/jsonc.ts
|
|
2877
|
+
init_esm_shims();
|
|
2878
|
+
import fs6 from "fs/promises";
|
|
2879
|
+
import { parse as parseJsonc, modify, applyEdits, format } from "jsonc-parser";
|
|
2880
|
+
async function loadJsonc(filePath) {
|
|
2881
|
+
try {
|
|
2882
|
+
const content = await fs6.readFile(filePath, "utf8");
|
|
2883
|
+
const errors = [];
|
|
2884
|
+
const result2 = parseJsonc(content, errors, {
|
|
2885
|
+
allowTrailingComma: true,
|
|
2886
|
+
allowEmptyContent: true
|
|
2887
|
+
});
|
|
2888
|
+
if (errors.length > 0) {
|
|
2889
|
+
console.error(`JSONC parse errors in ${filePath}:`, errors);
|
|
2890
|
+
}
|
|
2891
|
+
return result2;
|
|
2892
|
+
} catch {
|
|
2893
|
+
return null;
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
// src/cli/commands/ai-call.ts
|
|
2898
|
+
var EXIT_SUCCESS = 0;
|
|
2899
|
+
var EXIT_FALLBACK = 10;
|
|
2900
|
+
var EXIT_ERROR = 1;
|
|
2901
|
+
async function getStageModels(projectRoot, stageId) {
|
|
2902
|
+
const pipelinePath = path12.join(projectRoot, "config", "pipeline.jsonc");
|
|
2903
|
+
const config = await loadJsonc(pipelinePath);
|
|
2904
|
+
if (!config) return [];
|
|
2905
|
+
const stages = config.stages ?? [];
|
|
2906
|
+
const stage = stages.find((s) => s.id === stageId);
|
|
2907
|
+
return stage?.models ?? [];
|
|
2908
|
+
}
|
|
2909
|
+
function findExternalModel(models) {
|
|
2910
|
+
for (const m of models) {
|
|
2911
|
+
const lower = m.toLowerCase();
|
|
2912
|
+
if (lower !== "claudecode" && lower !== "claude") {
|
|
2913
|
+
return lower;
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
return null;
|
|
2917
|
+
}
|
|
2918
|
+
async function aiCallCommand(options) {
|
|
2919
|
+
const startTime = Date.now();
|
|
2920
|
+
const projectRoot = process.cwd();
|
|
2921
|
+
const { stage, timeout: timeoutStr } = options;
|
|
2922
|
+
const timeout = parseInt(timeoutStr ?? "300", 10);
|
|
2923
|
+
let prompt;
|
|
2924
|
+
if (options.promptFile) {
|
|
2925
|
+
try {
|
|
2926
|
+
prompt = readFileSync(path12.resolve(options.promptFile), "utf8");
|
|
2927
|
+
} catch (err) {
|
|
2928
|
+
logError(`Failed to read prompt file: ${options.promptFile}`);
|
|
2929
|
+
return EXIT_ERROR;
|
|
2930
|
+
}
|
|
2931
|
+
} else if (options.prompt) {
|
|
2932
|
+
prompt = options.prompt;
|
|
2933
|
+
} else {
|
|
2934
|
+
logError("Either --prompt or --prompt-file is required");
|
|
2935
|
+
return EXIT_ERROR;
|
|
2936
|
+
}
|
|
2937
|
+
const models = await getStageModels(projectRoot, stage);
|
|
2938
|
+
const externalModel = findExternalModel(models);
|
|
2939
|
+
if (!externalModel) {
|
|
2940
|
+
const output = JSON.stringify({
|
|
2941
|
+
success: false,
|
|
2942
|
+
model: null,
|
|
2943
|
+
output: null,
|
|
2944
|
+
fallback: { used: true, reason: "claudecode-only stage" },
|
|
2945
|
+
stage,
|
|
2946
|
+
duration_ms: Date.now() - startTime
|
|
2947
|
+
});
|
|
2948
|
+
process.stdout.write(output + "\n");
|
|
2949
|
+
return EXIT_FALLBACK;
|
|
2950
|
+
}
|
|
2951
|
+
try {
|
|
2952
|
+
const result2 = await callAI(externalModel, prompt, {
|
|
2953
|
+
timeout,
|
|
2954
|
+
projectRoot,
|
|
2955
|
+
quiet: true
|
|
2956
|
+
});
|
|
2957
|
+
if (result2.success) {
|
|
2958
|
+
const output2 = JSON.stringify({
|
|
2959
|
+
success: true,
|
|
2960
|
+
model: result2.model,
|
|
2961
|
+
output: result2.output,
|
|
2962
|
+
fallback: { used: false },
|
|
2963
|
+
stage,
|
|
2964
|
+
duration_ms: Date.now() - startTime
|
|
2965
|
+
});
|
|
2966
|
+
process.stdout.write(output2 + "\n");
|
|
2967
|
+
return EXIT_SUCCESS;
|
|
2968
|
+
}
|
|
2969
|
+
const output = JSON.stringify({
|
|
2970
|
+
success: false,
|
|
2971
|
+
model: result2.model,
|
|
2972
|
+
output: null,
|
|
2973
|
+
fallback: {
|
|
2974
|
+
used: true,
|
|
2975
|
+
reason: result2.fallbackReason ?? "model call failed"
|
|
2976
|
+
},
|
|
2977
|
+
stage,
|
|
2978
|
+
duration_ms: Date.now() - startTime
|
|
2979
|
+
});
|
|
2980
|
+
process.stdout.write(output + "\n");
|
|
2981
|
+
return result2.fallbackUsed ? EXIT_FALLBACK : EXIT_ERROR;
|
|
2982
|
+
} catch (err) {
|
|
2983
|
+
logError(`ai-call error: ${err instanceof Error ? err.message : String(err)}`);
|
|
2984
|
+
const output = JSON.stringify({
|
|
2985
|
+
success: false,
|
|
2986
|
+
model: externalModel,
|
|
2987
|
+
output: null,
|
|
2988
|
+
fallback: { used: true, reason: String(err) },
|
|
2989
|
+
stage,
|
|
2990
|
+
duration_ms: Date.now() - startTime
|
|
2991
|
+
});
|
|
2992
|
+
process.stdout.write(output + "\n");
|
|
2993
|
+
return EXIT_ERROR;
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
|
|
2536
2997
|
// src/cli/index.ts
|
|
2537
|
-
var __filename4 =
|
|
2538
|
-
var __dirname4 =
|
|
2998
|
+
var __filename4 = fileURLToPath4(import.meta.url);
|
|
2999
|
+
var __dirname4 = path13.dirname(__filename4);
|
|
2539
3000
|
function getPackageVersion() {
|
|
2540
3001
|
try {
|
|
2541
|
-
const pkgPath =
|
|
2542
|
-
const pkg = JSON.parse(
|
|
3002
|
+
const pkgPath = path13.resolve(__dirname4, "../../package.json");
|
|
3003
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
|
|
2543
3004
|
return pkg.version || "0.0.0";
|
|
2544
3005
|
} catch {
|
|
2545
3006
|
return "0.0.0";
|
|
@@ -2613,4 +3074,8 @@ program.command("checkpoint-cleanup").description("Cleanup old checkpoints").opt
|
|
|
2613
3074
|
const maxRetention = parseInt(options.max, 10);
|
|
2614
3075
|
await cleanupCheckpointsCommand(projectRoot, maxRetention);
|
|
2615
3076
|
});
|
|
3077
|
+
program.command("ai-call").description("Call external AI model for a pipeline stage").requiredOption("--stage <id>", "Stage ID (e.g. 01-brainstorm)").option("--prompt <text>", "Prompt text").option("--prompt-file <path>", "Path to prompt file").option("--timeout <seconds>", "Timeout in seconds", "300").action(async (options) => {
|
|
3078
|
+
const exitCode = await aiCallCommand(options);
|
|
3079
|
+
process.exit(exitCode);
|
|
3080
|
+
});
|
|
2616
3081
|
program.parse();
|