@ttfw/envoi 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +238 -0
- package/dist/commands/app.d.ts +2 -0
- package/dist/commands/app.d.ts.map +1 -0
- package/dist/commands/app.js +31 -0
- package/dist/commands/app.js.map +1 -0
- package/dist/commands/autonomy.d.ts +6 -0
- package/dist/commands/autonomy.d.ts.map +1 -0
- package/dist/commands/autonomy.js +89 -0
- package/dist/commands/autonomy.js.map +1 -0
- package/dist/commands/builder.d.ts +13 -0
- package/dist/commands/builder.d.ts.map +1 -0
- package/dist/commands/builder.js +142 -0
- package/dist/commands/builder.js.map +1 -0
- package/dist/commands/idea.d.ts +12 -0
- package/dist/commands/idea.d.ts.map +1 -0
- package/dist/commands/idea.js +79 -0
- package/dist/commands/idea.js.map +1 -0
- package/dist/commands/init.d.ts +18 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +423 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/mode.d.ts +13 -0
- package/dist/commands/mode.d.ts.map +1 -0
- package/dist/commands/mode.js +96 -0
- package/dist/commands/mode.js.map +1 -0
- package/dist/commands/onboard.d.ts +37 -0
- package/dist/commands/onboard.d.ts.map +1 -0
- package/dist/commands/onboard.js +743 -0
- package/dist/commands/onboard.js.map +1 -0
- package/dist/commands/pr-note.d.ts +8 -0
- package/dist/commands/pr-note.d.ts.map +1 -0
- package/dist/commands/pr-note.js +27 -0
- package/dist/commands/pr-note.js.map +1 -0
- package/dist/commands/undo.d.ts +7 -0
- package/dist/commands/undo.d.ts.map +1 -0
- package/dist/commands/undo.js +59 -0
- package/dist/commands/undo.js.map +1 -0
- package/dist/commands/update.d.ts +24 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +248 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/constants/report_codes.d.ts +29 -0
- package/dist/constants/report_codes.d.ts.map +1 -0
- package/dist/constants/report_codes.js +69 -0
- package/dist/constants/report_codes.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +675 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/autonomy.d.ts +16 -0
- package/dist/lib/autonomy.d.ts.map +1 -0
- package/dist/lib/autonomy.js +38 -0
- package/dist/lib/autonomy.js.map +1 -0
- package/dist/lib/blocked.d.ts +87 -0
- package/dist/lib/blocked.d.ts.map +1 -0
- package/dist/lib/blocked.js +134 -0
- package/dist/lib/blocked.js.map +1 -0
- package/dist/lib/branding.d.ts +13 -0
- package/dist/lib/branding.d.ts.map +1 -0
- package/dist/lib/branding.js +19 -0
- package/dist/lib/branding.js.map +1 -0
- package/dist/lib/claude.d.ts +42 -0
- package/dist/lib/claude.d.ts.map +1 -0
- package/dist/lib/claude.js +291 -0
- package/dist/lib/claude.js.map +1 -0
- package/dist/lib/config.d.ts +71 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +410 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/diff.d.ts +150 -0
- package/dist/lib/diff.d.ts.map +1 -0
- package/dist/lib/diff.js +257 -0
- package/dist/lib/diff.js.map +1 -0
- package/dist/lib/doctor.d.ts +67 -0
- package/dist/lib/doctor.d.ts.map +1 -0
- package/dist/lib/doctor.js +211 -0
- package/dist/lib/doctor.js.map +1 -0
- package/dist/lib/fingerprint.d.ts +27 -0
- package/dist/lib/fingerprint.d.ts.map +1 -0
- package/dist/lib/fingerprint.js +116 -0
- package/dist/lib/fingerprint.js.map +1 -0
- package/dist/lib/fs.d.ts +93 -0
- package/dist/lib/fs.d.ts.map +1 -0
- package/dist/lib/fs.js +179 -0
- package/dist/lib/fs.js.map +1 -0
- package/dist/lib/git.d.ts +177 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +355 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/git_branching.d.ts +84 -0
- package/dist/lib/git_branching.d.ts.map +1 -0
- package/dist/lib/git_branching.js +327 -0
- package/dist/lib/git_branching.js.map +1 -0
- package/dist/lib/gitignore.d.ts +26 -0
- package/dist/lib/gitignore.d.ts.map +1 -0
- package/dist/lib/gitignore.js +119 -0
- package/dist/lib/gitignore.js.map +1 -0
- package/dist/lib/guardrails.d.ts +232 -0
- package/dist/lib/guardrails.d.ts.map +1 -0
- package/dist/lib/guardrails.js +323 -0
- package/dist/lib/guardrails.js.map +1 -0
- package/dist/lib/history.d.ts +110 -0
- package/dist/lib/history.d.ts.map +1 -0
- package/dist/lib/history.js +236 -0
- package/dist/lib/history.js.map +1 -0
- package/dist/lib/index.d.ts +29 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +29 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/json-extract.d.ts +42 -0
- package/dist/lib/json-extract.d.ts.map +1 -0
- package/dist/lib/json-extract.js +201 -0
- package/dist/lib/json-extract.js.map +1 -0
- package/dist/lib/judge.d.ts +237 -0
- package/dist/lib/judge.d.ts.map +1 -0
- package/dist/lib/judge.js +501 -0
- package/dist/lib/judge.js.map +1 -0
- package/dist/lib/lock.d.ts +79 -0
- package/dist/lib/lock.d.ts.map +1 -0
- package/dist/lib/lock.js +254 -0
- package/dist/lib/lock.js.map +1 -0
- package/dist/lib/migration.d.ts +9 -0
- package/dist/lib/migration.d.ts.map +1 -0
- package/dist/lib/migration.js +74 -0
- package/dist/lib/migration.js.map +1 -0
- package/dist/lib/paths.d.ts +18 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +27 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/preflight.d.ts +33 -0
- package/dist/lib/preflight.d.ts.map +1 -0
- package/dist/lib/preflight.js +177 -0
- package/dist/lib/preflight.js.map +1 -0
- package/dist/lib/prompt_budget.d.ts +18 -0
- package/dist/lib/prompt_budget.d.ts.map +1 -0
- package/dist/lib/prompt_budget.js +36 -0
- package/dist/lib/prompt_budget.js.map +1 -0
- package/dist/lib/report.d.ts +102 -0
- package/dist/lib/report.d.ts.map +1 -0
- package/dist/lib/report.js +347 -0
- package/dist/lib/report.js.map +1 -0
- package/dist/lib/reviewer-flow.d.ts +80 -0
- package/dist/lib/reviewer-flow.d.ts.map +1 -0
- package/dist/lib/reviewer-flow.js +138 -0
- package/dist/lib/reviewer-flow.js.map +1 -0
- package/dist/lib/reviewer.d.ts +53 -0
- package/dist/lib/reviewer.d.ts.map +1 -0
- package/dist/lib/reviewer.js +199 -0
- package/dist/lib/reviewer.js.map +1 -0
- package/dist/lib/risk.d.ts +127 -0
- package/dist/lib/risk.d.ts.map +1 -0
- package/dist/lib/risk.js +192 -0
- package/dist/lib/risk.js.map +1 -0
- package/dist/lib/rollback.d.ts +143 -0
- package/dist/lib/rollback.d.ts.map +1 -0
- package/dist/lib/rollback.js +244 -0
- package/dist/lib/rollback.js.map +1 -0
- package/dist/lib/schema.d.ts +47 -0
- package/dist/lib/schema.d.ts.map +1 -0
- package/dist/lib/schema.js +91 -0
- package/dist/lib/schema.js.map +1 -0
- package/dist/lib/scope.d.ts +89 -0
- package/dist/lib/scope.d.ts.map +1 -0
- package/dist/lib/scope.js +135 -0
- package/dist/lib/scope.js.map +1 -0
- package/dist/lib/self_update.d.ts +13 -0
- package/dist/lib/self_update.d.ts.map +1 -0
- package/dist/lib/self_update.js +172 -0
- package/dist/lib/self_update.js.map +1 -0
- package/dist/lib/state.d.ts +143 -0
- package/dist/lib/state.d.ts.map +1 -0
- package/dist/lib/state.js +258 -0
- package/dist/lib/state.js.map +1 -0
- package/dist/lib/tick.d.ts +310 -0
- package/dist/lib/tick.d.ts.map +1 -0
- package/dist/lib/tick.js +424 -0
- package/dist/lib/tick.js.map +1 -0
- package/dist/lib/transport.d.ts +145 -0
- package/dist/lib/transport.d.ts.map +1 -0
- package/dist/lib/transport.js +237 -0
- package/dist/lib/transport.js.map +1 -0
- package/dist/lib/verdict_labels.d.ts +5 -0
- package/dist/lib/verdict_labels.d.ts.map +1 -0
- package/dist/lib/verdict_labels.js +25 -0
- package/dist/lib/verdict_labels.js.map +1 -0
- package/dist/lib/verify-safety.d.ts +63 -0
- package/dist/lib/verify-safety.d.ts.map +1 -0
- package/dist/lib/verify-safety.js +123 -0
- package/dist/lib/verify-safety.js.map +1 -0
- package/dist/lib/verify.d.ts +139 -0
- package/dist/lib/verify.d.ts.map +1 -0
- package/dist/lib/verify.js +311 -0
- package/dist/lib/verify.js.map +1 -0
- package/dist/lib/workspace_state.d.ts +79 -0
- package/dist/lib/workspace_state.d.ts.map +1 -0
- package/dist/lib/workspace_state.js +283 -0
- package/dist/lib/workspace_state.js.map +1 -0
- package/dist/runner/builder.d.ts +58 -0
- package/dist/runner/builder.d.ts.map +1 -0
- package/dist/runner/builder.js +775 -0
- package/dist/runner/builder.js.map +1 -0
- package/dist/runner/builder_parse.d.ts +37 -0
- package/dist/runner/builder_parse.d.ts.map +1 -0
- package/dist/runner/builder_parse.js +76 -0
- package/dist/runner/builder_parse.js.map +1 -0
- package/dist/runner/index.d.ts +9 -0
- package/dist/runner/index.d.ts.map +1 -0
- package/dist/runner/index.js +7 -0
- package/dist/runner/index.js.map +1 -0
- package/dist/runner/loop.d.ts +51 -0
- package/dist/runner/loop.d.ts.map +1 -0
- package/dist/runner/loop.js +221 -0
- package/dist/runner/loop.js.map +1 -0
- package/dist/runner/orchestrator.d.ts +67 -0
- package/dist/runner/orchestrator.d.ts.map +1 -0
- package/dist/runner/orchestrator.js +376 -0
- package/dist/runner/orchestrator.js.map +1 -0
- package/dist/runner/tick.d.ts +10 -0
- package/dist/runner/tick.d.ts.map +1 -0
- package/dist/runner/tick.js +1639 -0
- package/dist/runner/tick.js.map +1 -0
- package/dist/types/blocked.d.ts +52 -0
- package/dist/types/blocked.d.ts.map +1 -0
- package/dist/types/blocked.js +8 -0
- package/dist/types/blocked.js.map +1 -0
- package/dist/types/builder.d.ts +25 -0
- package/dist/types/builder.d.ts.map +1 -0
- package/dist/types/builder.js +7 -0
- package/dist/types/builder.js.map +1 -0
- package/dist/types/claude.d.ts +86 -0
- package/dist/types/claude.d.ts.map +1 -0
- package/dist/types/claude.js +48 -0
- package/dist/types/claude.js.map +1 -0
- package/dist/types/config.d.ts +384 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +7 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/lock.d.ts +21 -0
- package/dist/types/lock.d.ts.map +1 -0
- package/dist/types/lock.js +8 -0
- package/dist/types/lock.js.map +1 -0
- package/dist/types/preflight.d.ts +49 -0
- package/dist/types/preflight.d.ts.map +1 -0
- package/dist/types/preflight.js +8 -0
- package/dist/types/preflight.js.map +1 -0
- package/dist/types/report.d.ts +161 -0
- package/dist/types/report.d.ts.map +1 -0
- package/dist/types/report.js +8 -0
- package/dist/types/report.js.map +1 -0
- package/dist/types/reviewer.d.ts +66 -0
- package/dist/types/reviewer.d.ts.map +1 -0
- package/dist/types/reviewer.js +5 -0
- package/dist/types/reviewer.js.map +1 -0
- package/dist/types/state.d.ts +124 -0
- package/dist/types/state.d.ts.map +1 -0
- package/dist/types/state.js +20 -0
- package/dist/types/state.js.map +1 -0
- package/dist/types/task.d.ts +117 -0
- package/dist/types/task.d.ts.map +1 -0
- package/dist/types/task.js +7 -0
- package/dist/types/task.js.map +1 -0
- package/dist/types/workspace_state.d.ts +125 -0
- package/dist/types/workspace_state.d.ts.map +1 -0
- package/dist/types/workspace_state.js +10 -0
- package/dist/types/workspace_state.js.map +1 -0
- package/envoi.config.json +191 -0
- package/package.json +52 -0
- package/relais/prompts/.gitkeep +0 -0
- package/relais/prompts/builder.system.txt +13 -0
- package/relais/prompts/builder.user.txt +15 -0
- package/relais/prompts/orchestrator.system.txt +37 -0
- package/relais/prompts/orchestrator.user.txt +34 -0
- package/relais/prompts/reviewer.system.txt +33 -0
- package/relais/prompts/reviewer.user.txt +35 -0
- package/relais/schemas/.gitkeep +0 -0
- package/relais/schemas/builder_result.schema.json +29 -0
- package/relais/schemas/report.schema.json +195 -0
- package/relais/schemas/reviewer_result.schema.json +70 -0
- package/relais/schemas/task.schema.json +155 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { basename, join } from "node:path";
|
|
5
|
+
import { loadConfig, findConfigFile, validateConfig, ConfigError, CONFIG_FILE_NAME, chdirToRepoRoot } from "./lib/config.js";
|
|
6
|
+
import { checkCursorAgent } from "./lib/doctor.js";
|
|
7
|
+
import { atomicReadJson } from "./lib/fs.js";
|
|
8
|
+
import { readWorkspaceState } from "./lib/workspace_state.js";
|
|
9
|
+
import { CLI_NAME, LEGACY_CLI_NAME, PRODUCT_NAME } from "./lib/branding.js";
|
|
10
|
+
import { formatDisplayState } from "./lib/verdict_labels.js";
|
|
11
|
+
const program = new Command();
|
|
12
|
+
// Global config option
|
|
13
|
+
let globalConfigPath;
|
|
14
|
+
program
|
|
15
|
+
.name(CLI_NAME)
|
|
16
|
+
.description(`${PRODUCT_NAME} orchestration CLI`)
|
|
17
|
+
.version("1.0.0")
|
|
18
|
+
.option("-c, --config <path>", "Path to configuration file")
|
|
19
|
+
.hook("preAction", (thisCommand) => {
|
|
20
|
+
globalConfigPath = thisCommand.opts().config;
|
|
21
|
+
const invokedBinary = basename(process.argv[1] ?? '');
|
|
22
|
+
if (invokedBinary.includes(LEGACY_CLI_NAME)) {
|
|
23
|
+
console.warn(`[DEPRECATED] Legacy CLI alias is kept for compatibility. Prefer '${CLI_NAME}'.`);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
program
|
|
27
|
+
.command("update")
|
|
28
|
+
.description("Update envoi installation (auto-detect linked dev vs registry install)")
|
|
29
|
+
.option("--mode <mode>", "Update strategy: auto|linked|registry")
|
|
30
|
+
.option("--manager <manager>", "Package manager override: pnpm|npm|yarn|bun")
|
|
31
|
+
.option("--dry-run", "Show strategy and commands without executing")
|
|
32
|
+
.option("--yes", "Skip confirmation prompt")
|
|
33
|
+
.action(async (options) => {
|
|
34
|
+
try {
|
|
35
|
+
const { updateCommand } = await import('./commands/update.js');
|
|
36
|
+
await updateCommand({
|
|
37
|
+
mode: options.mode,
|
|
38
|
+
manager: options.manager,
|
|
39
|
+
dryRun: options.dryRun,
|
|
40
|
+
yes: options.yes,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error(`Failed to update envoi: ${error instanceof Error ? error.message : String(error)}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
program
|
|
49
|
+
.command("pr-note")
|
|
50
|
+
.description("Print an Envoi attribution line for PR descriptions")
|
|
51
|
+
.option("--format <format>", "Output format: markdown|text")
|
|
52
|
+
.option("--url <url>", "Override project URL")
|
|
53
|
+
.action(async (options) => {
|
|
54
|
+
try {
|
|
55
|
+
const { prNoteCommand } = await import('./commands/pr-note.js');
|
|
56
|
+
await prNoteCommand({
|
|
57
|
+
format: options.format,
|
|
58
|
+
url: options.url,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error(`Failed to render PR note: ${error instanceof Error ? error.message : String(error)}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
program
|
|
67
|
+
.command("app")
|
|
68
|
+
.description("Open Envoi desktop app or show quick benefits")
|
|
69
|
+
.action(async () => {
|
|
70
|
+
try {
|
|
71
|
+
const { appCommand } = await import('./commands/app.js');
|
|
72
|
+
await appCommand();
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error(`Failed to open app bridge: ${error instanceof Error ? error.message : String(error)}`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
program
|
|
80
|
+
.command("start")
|
|
81
|
+
.alias("init")
|
|
82
|
+
.description("Scaffold + guided onboarding in one command")
|
|
83
|
+
.option("-f, --force", "Overwrite existing scaffold files")
|
|
84
|
+
.option("--setup-only", "Only scaffold files, skip guided onboarding")
|
|
85
|
+
.option("--prd-file <path>", "Path to PRD markdown file (otherwise prompts or reads stdin)")
|
|
86
|
+
.option("--mode <mode>", "Default loop mode: task|milestone|autonomous")
|
|
87
|
+
.option("--builder <builder>", "Default builder: cursor|claude_code")
|
|
88
|
+
.option("--reviewer <reviewer>", "Reviewer mode: none|codex")
|
|
89
|
+
.option("--no-auto-run", "Do not start execution after onboarding")
|
|
90
|
+
.action(async (options) => {
|
|
91
|
+
try {
|
|
92
|
+
if (options.setupOnly) {
|
|
93
|
+
const { initCommand } = await import('./commands/init.js');
|
|
94
|
+
await initCommand({ force: options.force, showNextSteps: true });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const { onboardCommand } = await import('./commands/onboard.js');
|
|
98
|
+
await onboardCommand({
|
|
99
|
+
configPath: globalConfigPath,
|
|
100
|
+
forceInit: options.force,
|
|
101
|
+
prdFile: options.prdFile,
|
|
102
|
+
mode: options.mode,
|
|
103
|
+
builder: options.builder,
|
|
104
|
+
reviewer: options.reviewer,
|
|
105
|
+
showTourPrompt: true,
|
|
106
|
+
autoRun: options.autoRun,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.error('Failed to start envoi: ' + (error instanceof Error ? error.message : String(error)));
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
program
|
|
115
|
+
.command("brief")
|
|
116
|
+
.alias("onboard")
|
|
117
|
+
.description("Run guided onboarding and save defaults")
|
|
118
|
+
.option("-f, --force-init", "Force re-run init (overwrite existing files)")
|
|
119
|
+
.option("--prd-file <path>", "Path to PRD markdown file (otherwise prompts or reads stdin)")
|
|
120
|
+
.option("--mode <mode>", "Default loop mode: task|milestone|autonomous")
|
|
121
|
+
.option("--builder <builder>", "Default builder: cursor|claude_code")
|
|
122
|
+
.option("--reviewer <reviewer>", "Reviewer mode: none|codex")
|
|
123
|
+
.option("--no-auto-run", "Do not start execution after onboarding")
|
|
124
|
+
.action(async (options) => {
|
|
125
|
+
try {
|
|
126
|
+
const { onboardCommand } = await import('./commands/onboard.js');
|
|
127
|
+
await onboardCommand({
|
|
128
|
+
configPath: globalConfigPath,
|
|
129
|
+
forceInit: options.forceInit,
|
|
130
|
+
prdFile: options.prdFile,
|
|
131
|
+
mode: options.mode,
|
|
132
|
+
builder: options.builder,
|
|
133
|
+
reviewer: options.reviewer,
|
|
134
|
+
showTourPrompt: false,
|
|
135
|
+
autoRun: options.autoRun,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
console.error('Failed to capture brief: ' + (error instanceof Error ? error.message : String(error)));
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
program
|
|
144
|
+
.command("mode")
|
|
145
|
+
.description("Set or prompt for default loop mode (task|milestone|autonomous)")
|
|
146
|
+
.option("--set <mode>", "Set mode without prompting")
|
|
147
|
+
.option("--json", "Output in JSON format")
|
|
148
|
+
.action(async (options) => {
|
|
149
|
+
try {
|
|
150
|
+
await chdirToRepoRoot(globalConfigPath);
|
|
151
|
+
const { modeCommand } = await import('./commands/mode.js');
|
|
152
|
+
await modeCommand({ configPath: globalConfigPath, set: options.set, json: options.json });
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
console.error(`Failed to set mode: ${error instanceof Error ? error.message : String(error)}`);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
program
|
|
160
|
+
.command("builder")
|
|
161
|
+
.description("Set or prompt for default builder (cursor|claude_code)")
|
|
162
|
+
.option("--set <builder>", "Set builder without prompting")
|
|
163
|
+
.option("--json", "Output in JSON format")
|
|
164
|
+
.action(async (options) => {
|
|
165
|
+
try {
|
|
166
|
+
await chdirToRepoRoot(globalConfigPath);
|
|
167
|
+
const { builderCommand } = await import('./commands/builder.js');
|
|
168
|
+
await builderCommand({ configPath: globalConfigPath, set: options.set, json: options.json });
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.error(`Failed to set builder: ${error instanceof Error ? error.message : String(error)}`);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
program
|
|
176
|
+
.command("autonomy")
|
|
177
|
+
.description("Set autonomy profile for approval friction (strict|balanced|fast)")
|
|
178
|
+
.option("--set <profile>", "Set profile without prompting")
|
|
179
|
+
.option("--json", "Output in JSON format")
|
|
180
|
+
.action(async (options) => {
|
|
181
|
+
try {
|
|
182
|
+
await chdirToRepoRoot(globalConfigPath);
|
|
183
|
+
const { autonomyCommand } = await import('./commands/autonomy.js');
|
|
184
|
+
await autonomyCommand({ configPath: globalConfigPath, set: options.set, json: options.json });
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
console.error(`Failed to set autonomy profile: ${error instanceof Error ? error.message : String(error)}`);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
program
|
|
192
|
+
.command("idea")
|
|
193
|
+
.description("Capture a new product idea for orchestrator planning")
|
|
194
|
+
.argument("[text...]", "Idea text (optional; prompts when omitted)")
|
|
195
|
+
.option("--target-by <date>", "Optional target date or milestone hint")
|
|
196
|
+
.option("--testability <need>", "Delivery urgency: soon|later|unknown")
|
|
197
|
+
.option("--source <source>", "Source tag: interactive|cli|api")
|
|
198
|
+
.option("--json", "Output in JSON format")
|
|
199
|
+
.action(async (text, options) => {
|
|
200
|
+
try {
|
|
201
|
+
await chdirToRepoRoot(globalConfigPath);
|
|
202
|
+
const config = await loadConfig(globalConfigPath);
|
|
203
|
+
const { ideaCommand } = await import('./commands/idea.js');
|
|
204
|
+
await ideaCommand({
|
|
205
|
+
text: Array.isArray(text) ? text.join(' ').trim() : '',
|
|
206
|
+
targetBy: options.targetBy,
|
|
207
|
+
testability: options.testability,
|
|
208
|
+
source: options.source,
|
|
209
|
+
json: options.json,
|
|
210
|
+
}, config.workspace_dir);
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
console.error(`Failed to capture idea: ${error instanceof Error ? error.message : String(error)}`);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
program
|
|
218
|
+
.command("status")
|
|
219
|
+
.description("Show current envoi state")
|
|
220
|
+
.option("--preflight", "Run preflight checks")
|
|
221
|
+
.option("--json", "Output in JSON format")
|
|
222
|
+
.action(async (options) => {
|
|
223
|
+
try {
|
|
224
|
+
await chdirToRepoRoot(globalConfigPath);
|
|
225
|
+
const config = await loadConfig(globalConfigPath);
|
|
226
|
+
if (options.json) {
|
|
227
|
+
const output = {
|
|
228
|
+
product_name: config.product_name,
|
|
229
|
+
version: config.version,
|
|
230
|
+
workspace_dir: config.workspace_dir,
|
|
231
|
+
};
|
|
232
|
+
if (options.preflight) {
|
|
233
|
+
const { runPreflight } = await import('./lib/preflight.js');
|
|
234
|
+
output.preflight = await runPreflight(config);
|
|
235
|
+
}
|
|
236
|
+
console.log(JSON.stringify(output, null, 2));
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
console.log(`${CLI_NAME} status (${config.product_name} v${config.version})`);
|
|
240
|
+
console.log(` workspace: ${config.workspace_dir}`);
|
|
241
|
+
if (options.preflight) {
|
|
242
|
+
const { runPreflight } = await import('./lib/preflight.js');
|
|
243
|
+
const result = await runPreflight(config);
|
|
244
|
+
console.log('\nPreflight checks:');
|
|
245
|
+
console.log(` Status: ${result.ok ? 'PASS' : 'FAIL'}`);
|
|
246
|
+
if (!result.ok) {
|
|
247
|
+
console.log(` Blocked: ${result.blocked_code}`);
|
|
248
|
+
console.log(` Reason: ${result.blocked_reason}`);
|
|
249
|
+
}
|
|
250
|
+
if (result.warnings.length > 0) {
|
|
251
|
+
console.log(' Warnings:');
|
|
252
|
+
for (const w of result.warnings) {
|
|
253
|
+
console.log(` - ${w}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (result.base_commit) {
|
|
257
|
+
console.log(` Base commit: ${result.base_commit}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
if (error instanceof ConfigError) {
|
|
263
|
+
console.error(`Configuration error: ${error.message}`);
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
program
|
|
270
|
+
.command("undo")
|
|
271
|
+
.description("Rollback to last safe snapshot")
|
|
272
|
+
.option("--yes", "Skip confirmation prompt")
|
|
273
|
+
.action(async (options) => {
|
|
274
|
+
try {
|
|
275
|
+
await chdirToRepoRoot(globalConfigPath);
|
|
276
|
+
const config = await loadConfig(globalConfigPath);
|
|
277
|
+
const { undoCommand } = await import('./commands/undo.js');
|
|
278
|
+
await undoCommand({
|
|
279
|
+
workspaceDir: config.workspace_dir,
|
|
280
|
+
yes: options.yes,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
console.error(`Failed to undo: ${error instanceof Error ? error.message : String(error)}`);
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
program
|
|
289
|
+
.command("tick")
|
|
290
|
+
.alias("run")
|
|
291
|
+
.description("Execute one bounded cycle (plan -> edit -> verify -> stop)")
|
|
292
|
+
.option("--dry-run", "Show what would happen without executing")
|
|
293
|
+
.option("--continue", "Resume from BLOCKED state if possible")
|
|
294
|
+
.action(async (options) => {
|
|
295
|
+
try {
|
|
296
|
+
await chdirToRepoRoot(globalConfigPath);
|
|
297
|
+
const config = await loadConfig(globalConfigPath);
|
|
298
|
+
const { refreshLinkedInstallIfStale } = await import('./lib/self_update.js');
|
|
299
|
+
const refresh = refreshLinkedInstallIfStale();
|
|
300
|
+
if (refresh.error) {
|
|
301
|
+
console.warn(`Linked install refresh failed: ${refresh.error}`);
|
|
302
|
+
}
|
|
303
|
+
else if (refresh.refreshed) {
|
|
304
|
+
console.log(`Linked install refreshed from ${refresh.linkedRoot} using ${refresh.manager}.`);
|
|
305
|
+
}
|
|
306
|
+
// Handle --dry-run flag
|
|
307
|
+
if (options.dryRun) {
|
|
308
|
+
const { runPreflight } = await import('./lib/preflight.js');
|
|
309
|
+
const result = await runPreflight(config);
|
|
310
|
+
console.log('Dry run - preflight results:');
|
|
311
|
+
console.log(` ok: ${result.ok}`);
|
|
312
|
+
if (!result.ok) {
|
|
313
|
+
console.log(` blocked: ${result.blocked_code}`);
|
|
314
|
+
console.log(` reason: ${result.blocked_reason}`);
|
|
315
|
+
}
|
|
316
|
+
if (result.warnings.length > 0) {
|
|
317
|
+
console.log(' warnings:', result.warnings);
|
|
318
|
+
}
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
// Handle --continue flag
|
|
322
|
+
if (options.continue) {
|
|
323
|
+
const statePath = join(config.workspace_dir, 'STATE.json');
|
|
324
|
+
try {
|
|
325
|
+
const state = await atomicReadJson(statePath);
|
|
326
|
+
console.log('Continue mode - checking state...');
|
|
327
|
+
// For now just print state info
|
|
328
|
+
console.log(` Current state: ${JSON.stringify(state, null, 2)}`);
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
console.log('No previous state found. Running fresh tick.');
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// Set up interrupt handling
|
|
335
|
+
const abortController = new AbortController();
|
|
336
|
+
let sigintCount = 0;
|
|
337
|
+
const signalHandler = (signal) => {
|
|
338
|
+
sigintCount++;
|
|
339
|
+
if (sigintCount === 1) {
|
|
340
|
+
console.log(`\n${signal} received, aborting current operation...`);
|
|
341
|
+
abortController.abort();
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
console.log(`\nForce exit`);
|
|
345
|
+
process.exit(130);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
const sigintHandler = () => signalHandler('SIGINT');
|
|
349
|
+
const sigtermHandler = () => signalHandler('SIGTERM');
|
|
350
|
+
process.on('SIGINT', sigintHandler);
|
|
351
|
+
process.on('SIGTERM', sigtermHandler);
|
|
352
|
+
const { runTick } = await import('./runner/tick.js');
|
|
353
|
+
const report = await runTick(config, abortController.signal);
|
|
354
|
+
// Cleanup signal handlers
|
|
355
|
+
process.off('SIGINT', sigintHandler);
|
|
356
|
+
process.off('SIGTERM', sigtermHandler);
|
|
357
|
+
// Print summary
|
|
358
|
+
console.log(`\n--- Tick Complete ---`);
|
|
359
|
+
console.log(`Run ID: ${report.run_id}`);
|
|
360
|
+
console.log(`State: ${formatDisplayState(report.verdict, report.code)}`);
|
|
361
|
+
console.log(`Duration: ${report.duration_ms}ms`);
|
|
362
|
+
console.log(`Next: ${CLI_NAME} tick (one more bounded cycle)`);
|
|
363
|
+
console.log(`Or: ${CLI_NAME} loop (multi-tick; uses default or --mode)`);
|
|
364
|
+
console.log(`Tips: ${CLI_NAME} mode / ${CLI_NAME} builder`);
|
|
365
|
+
if (report.verdict !== 'success') {
|
|
366
|
+
console.log('Waiting for you: accept, adjust, or undo.');
|
|
367
|
+
}
|
|
368
|
+
// Set exit code for interrupt case
|
|
369
|
+
if (report.code === 'STOP_INTERRUPTED') {
|
|
370
|
+
process.exitCode = 130;
|
|
371
|
+
}
|
|
372
|
+
else if (report.verdict === 'blocked' || report.verdict === 'stop') {
|
|
373
|
+
process.exit(1);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
if (error instanceof ConfigError) {
|
|
378
|
+
console.error(`Configuration error: ${error.message}`);
|
|
379
|
+
process.exit(1);
|
|
380
|
+
}
|
|
381
|
+
console.error(`Fatal error during tick execution:`, error);
|
|
382
|
+
process.exit(1);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
program
|
|
386
|
+
.command("loop")
|
|
387
|
+
.description("Run envoi loop (multiple ticks)")
|
|
388
|
+
.option("--mode <mode>", "Loop mode: task, milestone, or autonomous (defaults to runner.default_loop_mode)")
|
|
389
|
+
.option("--max-ticks <n>", "Maximum number of ticks to run", (v) => parseInt(v, 10))
|
|
390
|
+
.action(async (options) => {
|
|
391
|
+
try {
|
|
392
|
+
await chdirToRepoRoot(globalConfigPath);
|
|
393
|
+
const config = await loadConfig(globalConfigPath);
|
|
394
|
+
const { refreshLinkedInstallIfStale } = await import('./lib/self_update.js');
|
|
395
|
+
const refresh = refreshLinkedInstallIfStale();
|
|
396
|
+
if (refresh.error) {
|
|
397
|
+
console.warn(`Linked install refresh failed: ${refresh.error}`);
|
|
398
|
+
}
|
|
399
|
+
else if (refresh.refreshed) {
|
|
400
|
+
console.log(`Linked install refreshed from ${refresh.linkedRoot} using ${refresh.manager}.`);
|
|
401
|
+
}
|
|
402
|
+
const mode = options.mode ?? config.runner.default_loop_mode;
|
|
403
|
+
if (!mode) {
|
|
404
|
+
console.error(`No loop mode provided. Set a default via '${CLI_NAME} mode' or pass --mode task|milestone|autonomous.`);
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
407
|
+
if (mode !== "task" && mode !== "milestone" && mode !== "autonomous") {
|
|
408
|
+
console.error(`Invalid mode: ${mode}. Must be 'task', 'milestone', or 'autonomous'.`);
|
|
409
|
+
process.exit(1);
|
|
410
|
+
}
|
|
411
|
+
const { runLoop } = await import("./runner/loop.js");
|
|
412
|
+
const result = await runLoop(config, {
|
|
413
|
+
mode: mode,
|
|
414
|
+
max_ticks: options.maxTicks,
|
|
415
|
+
});
|
|
416
|
+
console.log("\n--- Loop Complete ---");
|
|
417
|
+
console.log(`Ticks executed: ${result.ticks_executed}`);
|
|
418
|
+
const lastReport = result.reports[result.reports.length - 1];
|
|
419
|
+
if (lastReport) {
|
|
420
|
+
console.log(`Final state: ${formatDisplayState(lastReport.verdict, lastReport.code)}`);
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
console.log(`Final verdict: ${result.final_verdict}`);
|
|
424
|
+
}
|
|
425
|
+
console.log(`Stop reason: ${result.stop_reason}`);
|
|
426
|
+
if (result.stop_reason === 'orchestrator_stop' && result.orchestrator_stop_reason) {
|
|
427
|
+
console.log(`Completion: ${result.orchestrator_stop_reason}`);
|
|
428
|
+
}
|
|
429
|
+
if (result.stop_reason === 'self_update') {
|
|
430
|
+
console.log(`Linked install was refreshed during loop. Re-run '${CLI_NAME} loop' to continue with the latest code.`);
|
|
431
|
+
}
|
|
432
|
+
if (mode === 'task') {
|
|
433
|
+
console.log(`Follow-up path (task): STANDBY at task boundary. Your call: continue with '${CLI_NAME} tick' when ready.`);
|
|
434
|
+
}
|
|
435
|
+
else if (mode === 'milestone') {
|
|
436
|
+
console.log(`Follow-up path (milestone): STANDBY at milestone boundary. Your call: continue with '${CLI_NAME} loop --mode milestone'.`);
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
console.log(`Follow-up path (autonomous): continues automatically until BLOCKED, LIMIT_HIT, max ticks, or signal.`);
|
|
440
|
+
}
|
|
441
|
+
const boundaryStop = result.stop_reason === 'orchestrator_stop' || result.stop_reason === 'milestone_change';
|
|
442
|
+
if (boundaryStop && process.stdin.isTTY) {
|
|
443
|
+
const { ideaCommand } = await import('./commands/idea.js');
|
|
444
|
+
await ideaCommand({ source: 'interactive' }, config.workspace_dir);
|
|
445
|
+
}
|
|
446
|
+
if (result.stop_reason === "blocked" || result.stop_reason === "verdict") {
|
|
447
|
+
process.exit(1);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
catch (error) {
|
|
451
|
+
if (error instanceof ConfigError) {
|
|
452
|
+
console.error(`Configuration error: ${error.message}`);
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
console.error("Fatal error during loop execution:", error);
|
|
456
|
+
process.exit(1);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
program
|
|
460
|
+
.command("check")
|
|
461
|
+
.alias("doctor")
|
|
462
|
+
.description("Check environment + constraints health")
|
|
463
|
+
.action(async () => {
|
|
464
|
+
console.log(`${CLI_NAME} check - checking configuration and environment\n`);
|
|
465
|
+
const issues = [];
|
|
466
|
+
let config = null;
|
|
467
|
+
// Change to repo root before checking config
|
|
468
|
+
try {
|
|
469
|
+
await chdirToRepoRoot(globalConfigPath);
|
|
470
|
+
}
|
|
471
|
+
catch (error) {
|
|
472
|
+
// If we can't find config, continue with checks below
|
|
473
|
+
}
|
|
474
|
+
// Check 1: Config file exists
|
|
475
|
+
const configPath = globalConfigPath || await findConfigFile();
|
|
476
|
+
if (configPath) {
|
|
477
|
+
console.log(`[OK] Config file found: ${configPath}`);
|
|
478
|
+
// Check 2: Config is valid JSON
|
|
479
|
+
try {
|
|
480
|
+
const rawConfig = await atomicReadJson(configPath);
|
|
481
|
+
console.log("[OK] Config file is valid JSON");
|
|
482
|
+
// Check 3: Config structure is valid
|
|
483
|
+
if (validateConfig(rawConfig)) {
|
|
484
|
+
console.log("[OK] Config structure is valid");
|
|
485
|
+
config = rawConfig;
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
console.log("[FAIL] Config structure is invalid");
|
|
489
|
+
issues.push("Configuration file structure does not match expected schema");
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
catch (error) {
|
|
493
|
+
console.log("[FAIL] Config file is not valid JSON");
|
|
494
|
+
issues.push(`Failed to parse config file: ${error instanceof Error ? error.message : String(error)}`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
console.log(`[FAIL] Config file not found (expected ${CONFIG_FILE_NAME})`);
|
|
499
|
+
issues.push(`Configuration file ${CONFIG_FILE_NAME} not found in current directory`);
|
|
500
|
+
}
|
|
501
|
+
// Check 4: Git availability (if require_git is true)
|
|
502
|
+
const requireGit = config?.runner?.require_git ?? true;
|
|
503
|
+
if (requireGit) {
|
|
504
|
+
try {
|
|
505
|
+
execSync("git --version", { stdio: "pipe" });
|
|
506
|
+
console.log("[OK] Git is available");
|
|
507
|
+
// Check if we're in a git repository
|
|
508
|
+
try {
|
|
509
|
+
execSync("git rev-parse --git-dir", { stdio: "pipe" });
|
|
510
|
+
console.log("[OK] Current directory is a git repository");
|
|
511
|
+
}
|
|
512
|
+
catch {
|
|
513
|
+
console.log("[WARN] Current directory is not a git repository");
|
|
514
|
+
issues.push("Current directory is not a git repository (require_git is true)");
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
catch {
|
|
518
|
+
console.log("[FAIL] Git is not available");
|
|
519
|
+
issues.push("Git is not installed or not in PATH (require_git is true)");
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
console.log("[SKIP] Git check (require_git is false)");
|
|
524
|
+
}
|
|
525
|
+
// Check 5: Claude Code CLI availability
|
|
526
|
+
if (config) {
|
|
527
|
+
const cliCommand = config.claude_code_cli.command;
|
|
528
|
+
try {
|
|
529
|
+
execSync(`${cliCommand} --version`, { stdio: "pipe" });
|
|
530
|
+
console.log(`[OK] Claude Code CLI (${cliCommand}) is available`);
|
|
531
|
+
}
|
|
532
|
+
catch {
|
|
533
|
+
console.log(`[WARN] Claude Code CLI (${cliCommand}) is not available`);
|
|
534
|
+
issues.push(`Claude Code CLI command '${cliCommand}' is not installed or not in PATH`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
// Check 5b: Cursor Agent availability (only if cursor builder is configured or selected)
|
|
538
|
+
if (config && (config.builder.default_mode === "cursor" || !!config.builder.cursor)) {
|
|
539
|
+
const cursorModeSelected = config.builder.default_mode === "cursor";
|
|
540
|
+
const needsCursorAgent = config.builder.cursor?.driver_kind !== "external";
|
|
541
|
+
try {
|
|
542
|
+
const result = await checkCursorAgent(config);
|
|
543
|
+
const versionSuffix = result.version ? ` (${result.version})` : "";
|
|
544
|
+
if (!result.cli_available) {
|
|
545
|
+
const msg = `Cursor CLI '${result.command}' not available in PATH`;
|
|
546
|
+
console.log(`[${cursorModeSelected ? "FAIL" : "WARN"}] ${msg}`);
|
|
547
|
+
if (result.details)
|
|
548
|
+
console.log(` ${result.details.split("\n")[0]}`);
|
|
549
|
+
if (cursorModeSelected) {
|
|
550
|
+
issues.push(`${msg}. Install Cursor and ensure '${result.command}' is executable, or set builder.cursor.command.`);
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
issues.push(`${msg} (cursor builder configured but not selected).`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
else if (!result.agent_available) {
|
|
557
|
+
const msg = `Cursor CLI is available${versionSuffix} but 'agent' subcommand is not`;
|
|
558
|
+
console.log(`[${cursorModeSelected ? "FAIL" : "WARN"}] ${msg}`);
|
|
559
|
+
if (result.details)
|
|
560
|
+
console.log(` ${result.details.split("\n")[0]}`);
|
|
561
|
+
issues.push(`${msg}. Update Cursor to a version that supports 'cursor agent'.`);
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
console.log(`[OK] Cursor agent is available${versionSuffix}`);
|
|
565
|
+
if (needsCursorAgent) {
|
|
566
|
+
if (result.auth_status === "authenticated") {
|
|
567
|
+
console.log("[OK] Cursor agent auth: authenticated");
|
|
568
|
+
}
|
|
569
|
+
else if (result.auth_status === "api_key_present") {
|
|
570
|
+
console.log("[OK] Cursor agent auth: CURSOR_API_KEY is set");
|
|
571
|
+
}
|
|
572
|
+
else if (result.auth_status === "unauthenticated") {
|
|
573
|
+
console.log("[WARN] Cursor agent auth: not authenticated");
|
|
574
|
+
if (result.details)
|
|
575
|
+
console.log(` ${result.details.split("\n")[0]}`);
|
|
576
|
+
issues.push("Cursor agent is not authenticated. Run 'cursor agent login' (OAuth) or set CURSOR_API_KEY in your environment.");
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
console.log("[WARN] Cursor agent auth: unknown");
|
|
580
|
+
issues.push("Cursor agent authentication status is unknown; run 'cursor agent whoami' to verify.");
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
console.log("[SKIP] Cursor auth check (driver_kind is external)");
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
catch (error) {
|
|
589
|
+
console.log("[WARN] Cursor agent check failed");
|
|
590
|
+
issues.push(`Cursor agent check failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
// Check 6: Workspace directory exists
|
|
594
|
+
if (config) {
|
|
595
|
+
const { existsSync } = await import('node:fs');
|
|
596
|
+
const workspaceDir = config.workspace_dir;
|
|
597
|
+
if (existsSync(workspaceDir)) {
|
|
598
|
+
console.log(`[OK] Workspace directory exists: ${workspaceDir}`);
|
|
599
|
+
// Check prompts directory
|
|
600
|
+
const promptsDir = join(workspaceDir, 'prompts');
|
|
601
|
+
if (existsSync(promptsDir)) {
|
|
602
|
+
console.log('[OK] Prompts directory exists');
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
console.log('[WARN] Prompts directory missing');
|
|
606
|
+
issues.push(`Prompts directory not found: ${promptsDir}`);
|
|
607
|
+
}
|
|
608
|
+
// Check schemas directory
|
|
609
|
+
const schemasDir = join(workspaceDir, 'schemas');
|
|
610
|
+
if (existsSync(schemasDir)) {
|
|
611
|
+
console.log('[OK] Schemas directory exists');
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
console.log('[WARN] Schemas directory missing');
|
|
615
|
+
issues.push(`Schemas directory not found: ${schemasDir}`);
|
|
616
|
+
}
|
|
617
|
+
// Check STATE.json
|
|
618
|
+
const statePath = join(workspaceDir, 'STATE.json');
|
|
619
|
+
if (existsSync(statePath)) {
|
|
620
|
+
try {
|
|
621
|
+
await atomicReadJson(statePath);
|
|
622
|
+
console.log('[OK] STATE.json exists and is valid JSON');
|
|
623
|
+
}
|
|
624
|
+
catch {
|
|
625
|
+
console.log('[WARN] STATE.json exists but is not valid JSON');
|
|
626
|
+
issues.push('STATE.json is not valid JSON');
|
|
627
|
+
}
|
|
628
|
+
// Show budget ledger summary
|
|
629
|
+
try {
|
|
630
|
+
const wsState = await readWorkspaceState(config.workspace_dir);
|
|
631
|
+
const caps = config.budgets.per_milestone;
|
|
632
|
+
console.log('\n[INFO] Budget ledger:');
|
|
633
|
+
console.log(` Milestone: ${wsState.milestone_id ?? '(none)'}`);
|
|
634
|
+
console.log(` Ticks: ${wsState.budgets.ticks} / ${caps.max_ticks}`);
|
|
635
|
+
console.log(` Orchestrator calls: ${wsState.budgets.orchestrator_calls} / ${caps.max_orchestrator_calls}`);
|
|
636
|
+
console.log(` Builder calls: ${wsState.budgets.builder_calls} / ${caps.max_builder_calls}`);
|
|
637
|
+
console.log(` Verify runs: ${wsState.budgets.verify_runs} / ${caps.max_verify_runs}`);
|
|
638
|
+
if (wsState.budget_warning) {
|
|
639
|
+
console.log(' [WARN] Budget warning active - approaching limit');
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
catch {
|
|
643
|
+
console.log('[WARN] Could not read budget ledger');
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
console.log('[INFO] STATE.json not found (will be created on first run)');
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
console.log(`[FAIL] Workspace directory not found: ${workspaceDir}`);
|
|
652
|
+
issues.push(`Workspace directory not found: ${workspaceDir}`);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
// Summary
|
|
656
|
+
console.log("\n--- Summary ---");
|
|
657
|
+
if (issues.length === 0) {
|
|
658
|
+
console.log(`All checks passed. ${PRODUCT_NAME} is ready.`);
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
console.log(`Found ${issues.length} issue(s):\n`);
|
|
662
|
+
for (const issue of issues) {
|
|
663
|
+
console.log(` - ${issue}`);
|
|
664
|
+
}
|
|
665
|
+
process.exit(1);
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
export async function main() {
|
|
669
|
+
await program.parseAsync(process.argv);
|
|
670
|
+
}
|
|
671
|
+
main().catch((error) => {
|
|
672
|
+
console.error("Fatal error:", error);
|
|
673
|
+
process.exit(1);
|
|
674
|
+
});
|
|
675
|
+
//# sourceMappingURL=index.js.map
|