karajan-code 1.15.0 → 1.17.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/package.json +1 -1
- package/src/activity-log.js +13 -13
- package/src/agents/availability.js +2 -3
- package/src/agents/claude-agent.js +42 -21
- package/src/agents/model-registry.js +1 -1
- package/src/becaria/dispatch.js +1 -1
- package/src/becaria/repo.js +3 -3
- package/src/cli.js +6 -2
- package/src/commands/doctor.js +154 -108
- package/src/commands/init.js +101 -90
- package/src/commands/plan.js +1 -1
- package/src/commands/report.js +77 -71
- package/src/commands/roles.js +0 -1
- package/src/commands/run.js +2 -3
- package/src/config.js +157 -89
- package/src/git/automation.js +3 -4
- package/src/guards/policy-resolver.js +3 -3
- package/src/mcp/orphan-guard.js +1 -2
- package/src/mcp/progress.js +4 -3
- package/src/mcp/run-kj.js +2 -0
- package/src/mcp/server-handlers.js +294 -241
- package/src/mcp/server.js +4 -3
- package/src/mcp/tools.js +19 -0
- package/src/orchestrator/agent-fallback.js +1 -3
- package/src/orchestrator/iteration-stages.js +206 -170
- package/src/orchestrator/pre-loop-stages.js +266 -34
- package/src/orchestrator/solomon-rules.js +2 -2
- package/src/orchestrator.js +820 -739
- package/src/planning-game/adapter.js +23 -20
- package/src/planning-game/architect-adrs.js +45 -0
- package/src/planning-game/client.js +15 -1
- package/src/planning-game/decomposition.js +7 -5
- package/src/prompts/architect.js +88 -0
- package/src/prompts/discover.js +228 -0
- package/src/prompts/planner.js +53 -33
- package/src/prompts/triage.js +8 -16
- package/src/review/parser.js +18 -19
- package/src/review/profiles.js +2 -2
- package/src/review/schema.js +3 -3
- package/src/review/scope-filter.js +3 -4
- package/src/roles/architect-role.js +122 -0
- package/src/roles/commiter-role.js +2 -2
- package/src/roles/discover-role.js +122 -0
- package/src/roles/index.js +2 -0
- package/src/roles/planner-role.js +54 -38
- package/src/roles/refactorer-role.js +8 -7
- package/src/roles/researcher-role.js +6 -7
- package/src/roles/reviewer-role.js +4 -5
- package/src/roles/security-role.js +3 -4
- package/src/roles/solomon-role.js +6 -18
- package/src/roles/sonar-role.js +5 -1
- package/src/roles/tester-role.js +8 -5
- package/src/roles/triage-role.js +2 -2
- package/src/session-cleanup.js +29 -24
- package/src/session-store.js +1 -1
- package/src/sonar/api.js +1 -1
- package/src/sonar/manager.js +1 -1
- package/src/sonar/project-key.js +5 -5
- package/src/sonar/scanner.js +34 -65
- package/src/utils/display.js +312 -272
- package/src/utils/git.js +3 -3
- package/src/utils/logger.js +6 -1
- package/src/utils/model-selector.js +5 -5
- package/src/utils/process.js +80 -102
- package/src/utils/rate-limit-detector.js +13 -13
- package/src/utils/run-log.js +55 -52
- package/templates/roles/architect.md +62 -0
- package/templates/roles/discover.md +167 -0
- package/templates/roles/planner.md +1 -0
package/src/commands/init.js
CHANGED
|
@@ -85,18 +85,7 @@ async function runWizard(config, logger) {
|
|
|
85
85
|
return config;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
const karajanHome = getKarajanHome();
|
|
90
|
-
await ensureDir(karajanHome);
|
|
91
|
-
logger.info(`Ensured ${karajanHome} exists`);
|
|
92
|
-
|
|
93
|
-
const configPath = getConfigPath();
|
|
94
|
-
const reviewRulesPath = path.resolve(process.cwd(), "review-rules.md");
|
|
95
|
-
const coderRulesPath = path.resolve(process.cwd(), "coder-rules.md");
|
|
96
|
-
|
|
97
|
-
const { config, exists: configExists } = await loadConfig();
|
|
98
|
-
const interactive = flags.noInteractive !== true && isTTY();
|
|
99
|
-
|
|
88
|
+
async function handleConfigSetup({ config, configExists, interactive, configPath, logger }) {
|
|
100
89
|
if (configExists && interactive) {
|
|
101
90
|
const wizard = createWizard();
|
|
102
91
|
try {
|
|
@@ -119,95 +108,117 @@ export async function initCommand({ logger, flags = {} }) {
|
|
|
119
108
|
await writeConfig(configPath, config);
|
|
120
109
|
logger.info(`Created ${configPath}`);
|
|
121
110
|
}
|
|
111
|
+
}
|
|
122
112
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
113
|
+
async function ensureReviewRules(reviewRulesPath, logger) {
|
|
114
|
+
if (await exists(reviewRulesPath)) return;
|
|
115
|
+
await fs.writeFile(
|
|
116
|
+
reviewRulesPath,
|
|
117
|
+
"# Review Rules\n\n- Focus on security, correctness, and test coverage.\n",
|
|
118
|
+
"utf8"
|
|
119
|
+
);
|
|
120
|
+
logger.info("Created review-rules.md");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function ensureCoderRules(coderRulesPath, logger) {
|
|
124
|
+
if (await exists(coderRulesPath)) return;
|
|
125
|
+
const templatePath = path.resolve(import.meta.dirname, "../../templates/coder-rules.md");
|
|
126
|
+
let content;
|
|
127
|
+
try {
|
|
128
|
+
content = await fs.readFile(templatePath, "utf8");
|
|
129
|
+
} catch {
|
|
130
|
+
content = [
|
|
131
|
+
"# Coder Rules",
|
|
132
|
+
"",
|
|
133
|
+
"## File modification safety",
|
|
134
|
+
"",
|
|
135
|
+
"- NEVER overwrite existing files entirely. Always make targeted, minimal edits.",
|
|
136
|
+
"- After each edit, verify with `git diff` that ONLY the intended lines changed.",
|
|
137
|
+
"- Do not modify code unrelated to the task.",
|
|
138
|
+
""
|
|
139
|
+
].join("\n");
|
|
130
140
|
}
|
|
141
|
+
await fs.writeFile(coderRulesPath, content, "utf8");
|
|
142
|
+
logger.info("Created coder-rules.md");
|
|
143
|
+
}
|
|
131
144
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"",
|
|
143
|
-
"- NEVER overwrite existing files entirely. Always make targeted, minimal edits.",
|
|
144
|
-
"- After each edit, verify with `git diff` that ONLY the intended lines changed.",
|
|
145
|
-
"- Do not modify code unrelated to the task.",
|
|
146
|
-
""
|
|
147
|
-
].join("\n");
|
|
145
|
+
async function setupSonarQube(config, logger) {
|
|
146
|
+
if (config.sonarqube?.enabled === false) {
|
|
147
|
+
logger.info("SonarQube disabled — skipping container setup.");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const vmCheck = await checkVmMaxMapCount(os.platform());
|
|
151
|
+
if (!vmCheck.ok) {
|
|
152
|
+
logger.warn(`vm.max_map_count check failed: ${vmCheck.reason}`);
|
|
153
|
+
if (vmCheck.fix) {
|
|
154
|
+
logger.warn(`Fix: ${vmCheck.fix}`);
|
|
148
155
|
}
|
|
149
|
-
await fs.writeFile(coderRulesPath, content, "utf8");
|
|
150
|
-
logger.info("Created coder-rules.md");
|
|
151
156
|
}
|
|
152
157
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (vmCheck.fix) {
|
|
158
|
-
logger.warn(`Fix: ${vmCheck.fix}`);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
158
|
+
const sonar = await sonarUp();
|
|
159
|
+
if (sonar.exitCode !== 0) {
|
|
160
|
+
throw new Error(`Failed to start SonarQube: ${sonar.stderr || sonar.stdout}`);
|
|
161
|
+
}
|
|
161
162
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
logger.info("SonarQube container started");
|
|
164
|
+
logger.info("");
|
|
165
|
+
logger.info("To configure the SonarQube token:");
|
|
166
|
+
logger.info(" 1. Open http://localhost:9000");
|
|
167
|
+
logger.info(" 2. Log in (default credentials: admin / admin)");
|
|
168
|
+
logger.info(" 3. Go to: My Account > Security > Generate Token");
|
|
169
|
+
logger.info(" 4. Name: karajan-cli, Type: Global Analysis Token");
|
|
170
|
+
logger.info(" 5. Set the token in ~/.karajan/kj.config.yml under sonarqube.token");
|
|
171
|
+
logger.info(' or export KJ_SONAR_TOKEN="<your-token>"');
|
|
172
|
+
}
|
|
166
173
|
|
|
167
|
-
|
|
174
|
+
async function scaffoldBecariaGateway(config, flags, logger) {
|
|
175
|
+
if (!config.becaria?.enabled && !flags.scaffoldBecaria) return;
|
|
176
|
+
const projectDir = process.cwd();
|
|
177
|
+
const workflowDir = path.join(projectDir, ".github", "workflows");
|
|
178
|
+
await ensureDir(workflowDir);
|
|
168
179
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
logger.info(" 1. Open http://localhost:9000");
|
|
172
|
-
logger.info(" 2. Log in (default credentials: admin / admin)");
|
|
173
|
-
logger.info(" 3. Go to: My Account > Security > Generate Token");
|
|
174
|
-
logger.info(" 4. Name: karajan-cli, Type: Global Analysis Token");
|
|
175
|
-
logger.info(" 5. Set the token in ~/.karajan/kj.config.yml under sonarqube.token");
|
|
176
|
-
logger.info(' or export KJ_SONAR_TOKEN="<your-token>"');
|
|
177
|
-
} else {
|
|
178
|
-
logger.info("SonarQube disabled — skipping container setup.");
|
|
179
|
-
}
|
|
180
|
+
const templatesDir = path.resolve(import.meta.dirname, "../../templates/workflows");
|
|
181
|
+
const workflows = ["becaria-gateway.yml", "automerge.yml", "houston-override.yml"];
|
|
180
182
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const srcPath = path.join(templatesDir, wf);
|
|
194
|
-
try {
|
|
195
|
-
const content = await fs.readFile(srcPath, "utf8");
|
|
196
|
-
await fs.writeFile(destPath, content, "utf8");
|
|
197
|
-
logger.info(`Created ${path.relative(projectDir, destPath)}`);
|
|
198
|
-
} catch (err) {
|
|
199
|
-
logger.warn(`Could not scaffold ${wf}: ${err.message}`);
|
|
200
|
-
}
|
|
201
|
-
} else {
|
|
202
|
-
logger.info(`${wf} already exists — skipping`);
|
|
183
|
+
for (const wf of workflows) {
|
|
184
|
+
const destPath = path.join(workflowDir, wf);
|
|
185
|
+
if (await exists(destPath)) {
|
|
186
|
+
logger.info(`${wf} already exists — skipping`);
|
|
187
|
+
} else {
|
|
188
|
+
const srcPath = path.join(templatesDir, wf);
|
|
189
|
+
try {
|
|
190
|
+
const content = await fs.readFile(srcPath, "utf8");
|
|
191
|
+
await fs.writeFile(destPath, content, "utf8");
|
|
192
|
+
logger.info(`Created ${path.relative(projectDir, destPath)}`);
|
|
193
|
+
} catch (err) {
|
|
194
|
+
logger.warn(`Could not scaffold ${wf}: ${err.message}`);
|
|
203
195
|
}
|
|
204
196
|
}
|
|
205
|
-
|
|
206
|
-
logger.info("");
|
|
207
|
-
logger.info("BecarIA Gateway scaffolded. Next steps:");
|
|
208
|
-
logger.info(" 1. Create a GitHub App named 'becaria-reviewer' with pull_request write permissions");
|
|
209
|
-
logger.info(" 2. Install the App on your repository");
|
|
210
|
-
logger.info(" 3. Add secrets: BECARIA_APP_ID and BECARIA_APP_PRIVATE_KEY");
|
|
211
|
-
logger.info(" 4. Push the workflow files and enable 'kj run --enable-becaria'");
|
|
212
197
|
}
|
|
198
|
+
|
|
199
|
+
logger.info("");
|
|
200
|
+
logger.info("BecarIA Gateway scaffolded. Next steps:");
|
|
201
|
+
logger.info(" 1. Create a GitHub App named 'becaria-reviewer' with pull_request write permissions");
|
|
202
|
+
logger.info(" 2. Install the App on your repository");
|
|
203
|
+
logger.info(" 3. Add secrets: BECARIA_APP_ID and BECARIA_APP_PRIVATE_KEY");
|
|
204
|
+
logger.info(" 4. Push the workflow files and enable 'kj run --enable-becaria'");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export async function initCommand({ logger, flags = {} }) {
|
|
208
|
+
const karajanHome = getKarajanHome();
|
|
209
|
+
await ensureDir(karajanHome);
|
|
210
|
+
logger.info(`Ensured ${karajanHome} exists`);
|
|
211
|
+
|
|
212
|
+
const configPath = getConfigPath();
|
|
213
|
+
const reviewRulesPath = path.resolve(process.cwd(), "review-rules.md");
|
|
214
|
+
const coderRulesPath = path.resolve(process.cwd(), "coder-rules.md");
|
|
215
|
+
|
|
216
|
+
const { config, exists: configExists } = await loadConfig();
|
|
217
|
+
const interactive = flags.noInteractive !== true && isTTY();
|
|
218
|
+
|
|
219
|
+
await handleConfigSetup({ config, configExists, interactive, configPath, logger });
|
|
220
|
+
await ensureReviewRules(reviewRulesPath, logger);
|
|
221
|
+
await ensureCoderRules(coderRulesPath, logger);
|
|
222
|
+
await setupSonarQube(config, logger);
|
|
223
|
+
await scaffoldBecariaGateway(config, flags, logger);
|
|
213
224
|
}
|
package/src/commands/plan.js
CHANGED
package/src/commands/report.js
CHANGED
|
@@ -9,7 +9,7 @@ function parseBudgetFromActivityLog(logText) {
|
|
|
9
9
|
return { consumed_usd: null, limit_usd: null };
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const regex = /Budget:\s*\$(
|
|
12
|
+
const regex = /Budget:\s*\$(\d+(?:\.\d+)?)\s*\/\s*\$(\d+(?:\.\d+)?)/g;
|
|
13
13
|
let match;
|
|
14
14
|
let last = null;
|
|
15
15
|
while ((match = regex.exec(logText)) !== null) {
|
|
@@ -61,7 +61,7 @@ function summarizePlan(checkpoints = []) {
|
|
|
61
61
|
|
|
62
62
|
const uniqueOrdered = [];
|
|
63
63
|
for (const stage of stages) {
|
|
64
|
-
if (uniqueOrdered
|
|
64
|
+
if (uniqueOrdered.at(-1) !== stage) {
|
|
65
65
|
uniqueOrdered.push(stage);
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -79,7 +79,7 @@ function summarizeSonar(checkpoints = []) {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
const initial = sonarPoints[0];
|
|
82
|
-
const final = sonarPoints
|
|
82
|
+
const final = sonarPoints.at(-1);
|
|
83
83
|
return {
|
|
84
84
|
initial,
|
|
85
85
|
final,
|
|
@@ -156,14 +156,13 @@ async function buildReport(dir, sessionId) {
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
function printTextReport(report) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
: "N/A";
|
|
159
|
+
let budgetText = "N/A";
|
|
160
|
+
if (typeof report.budget_consumed?.consumed_usd === "number") {
|
|
161
|
+
const limitSuffix = typeof report.budget_consumed?.limit_usd === "number"
|
|
162
|
+
? ` / $${report.budget_consumed.limit_usd.toFixed(2)}`
|
|
163
|
+
: "";
|
|
164
|
+
budgetText = `$${report.budget_consumed.consumed_usd.toFixed(2)}${limitSuffix}`;
|
|
165
|
+
}
|
|
167
166
|
|
|
168
167
|
const planText = report.plan_executed.length > 0 ? report.plan_executed.join(" -> ") : "N/A";
|
|
169
168
|
const iterationText =
|
|
@@ -318,62 +317,60 @@ async function findSessionsByPgTask(dir, pgTask) {
|
|
|
318
317
|
// skip malformed sessions
|
|
319
318
|
}
|
|
320
319
|
}
|
|
321
|
-
return matches.sort();
|
|
320
|
+
return matches.sort((a, b) => a.localeCompare(b));
|
|
322
321
|
}
|
|
323
322
|
|
|
324
|
-
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
323
|
+
async function resolveTraceOptions(currency) {
|
|
324
|
+
const { config } = await loadConfig();
|
|
325
|
+
const cur = currency?.toLowerCase() || config?.budget?.currency || "usd";
|
|
326
|
+
const rate = config?.budget?.exchange_rate_eur ?? 0.92;
|
|
327
|
+
return { cur, rate };
|
|
328
|
+
}
|
|
330
329
|
|
|
331
|
-
|
|
330
|
+
function printTraceReport(report, currency, exchangeRate) {
|
|
331
|
+
console.log(`Session: ${report.session_id}`);
|
|
332
|
+
if (report.pg_task_id) {
|
|
333
|
+
const projectLabel = report.pg_project_id ? ` (${report.pg_project_id})` : "";
|
|
334
|
+
console.log(`Planning Game Card: ${report.pg_task_id}${projectLabel}`);
|
|
335
|
+
}
|
|
336
|
+
console.log(`Status: ${report.status}`);
|
|
337
|
+
console.log(`Task: ${report.task_description || "N/A"}`);
|
|
338
|
+
console.log("");
|
|
339
|
+
printTraceTable(report.budget_trace, { currency, exchangeRate });
|
|
340
|
+
}
|
|
332
341
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
if (list) {
|
|
340
|
-
for (const item of matches) console.log(item);
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
// Show all reports for this card, or the latest if no list flag
|
|
344
|
-
const targetId = sessionId || matches.at(-1);
|
|
345
|
-
const report = await buildReport(dir, targetId);
|
|
346
|
-
if (format === "json") {
|
|
347
|
-
console.log(JSON.stringify({ card: pgTask, sessions: matches, report }, null, 2));
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
console.log(`Card ${pgTask}: ${matches.length} session${matches.length === 1 ? "" : "s"}`);
|
|
351
|
-
for (const m of matches) {
|
|
352
|
-
const marker = m === targetId ? " <--" : "";
|
|
353
|
-
console.log(` ${m}${marker}`);
|
|
354
|
-
}
|
|
355
|
-
console.log("");
|
|
356
|
-
if (trace) {
|
|
357
|
-
const { config } = await loadConfig();
|
|
358
|
-
const cur = currency?.toLowerCase() || config?.budget?.currency || "usd";
|
|
359
|
-
const rate = config?.budget?.exchange_rate_eur ?? 0.92;
|
|
360
|
-
console.log(`Session: ${report.session_id}`);
|
|
361
|
-
console.log(`Status: ${report.status}`);
|
|
362
|
-
console.log(`Task: ${report.task_description || "N/A"}`);
|
|
363
|
-
console.log("");
|
|
364
|
-
printTraceTable(report.budget_trace, { currency: cur, exchangeRate: rate });
|
|
365
|
-
} else {
|
|
366
|
-
printTextReport(report);
|
|
367
|
-
}
|
|
342
|
+
async function handlePgTaskReport({ dir, pgTask, list, sessionId, format, trace, currency }) {
|
|
343
|
+
const matches = await findSessionsByPgTask(dir, pgTask);
|
|
344
|
+
if (matches.length === 0) {
|
|
345
|
+
console.log(`No sessions found for card: ${pgTask}`);
|
|
368
346
|
return;
|
|
369
347
|
}
|
|
370
|
-
|
|
371
348
|
if (list) {
|
|
372
|
-
for (const item of
|
|
349
|
+
for (const item of matches) console.log(item);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const targetId = sessionId || matches.at(-1);
|
|
353
|
+
const report = await buildReport(dir, targetId);
|
|
354
|
+
if (format === "json") {
|
|
355
|
+
console.log(JSON.stringify({ card: pgTask, sessions: matches, report }, null, 2));
|
|
373
356
|
return;
|
|
374
357
|
}
|
|
358
|
+
console.log(`Card ${pgTask}: ${matches.length} session${matches.length === 1 ? "" : "s"}`);
|
|
359
|
+
for (const m of matches) {
|
|
360
|
+
const marker = m === targetId ? " <--" : "";
|
|
361
|
+
console.log(` ${m}${marker}`);
|
|
362
|
+
}
|
|
363
|
+
console.log("");
|
|
364
|
+
if (trace) {
|
|
365
|
+
const { cur, rate } = await resolveTraceOptions(currency);
|
|
366
|
+
printTraceReport(report, cur, rate);
|
|
367
|
+
} else {
|
|
368
|
+
printTextReport(report);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
375
371
|
|
|
376
|
-
|
|
372
|
+
async function handleSingleSessionReport({ dir, entries, sessionId, format, trace, currency }) {
|
|
373
|
+
const ids = [...entries].sort((a, b) => a.localeCompare(b));
|
|
377
374
|
const selectedSessionId = sessionId || ids.at(-1);
|
|
378
375
|
if (!selectedSessionId) {
|
|
379
376
|
console.log("No reports yet");
|
|
@@ -388,24 +385,33 @@ export async function reportCommand({ list = false, sessionId = null, format = "
|
|
|
388
385
|
console.log(JSON.stringify(report, null, 2));
|
|
389
386
|
return;
|
|
390
387
|
}
|
|
391
|
-
|
|
392
388
|
if (trace) {
|
|
393
|
-
const {
|
|
394
|
-
|
|
395
|
-
const rate = config?.budget?.exchange_rate_eur ?? 0.92;
|
|
396
|
-
console.log(`Session: ${report.session_id}`);
|
|
397
|
-
if (report.pg_task_id) {
|
|
398
|
-
const projectLabel = report.pg_project_id ? ` (${report.pg_project_id})` : "";
|
|
399
|
-
console.log(`Planning Game Card: ${report.pg_task_id}${projectLabel}`);
|
|
400
|
-
}
|
|
401
|
-
console.log(`Status: ${report.status}`);
|
|
402
|
-
console.log(`Task: ${report.task_description || "N/A"}`);
|
|
403
|
-
console.log("");
|
|
404
|
-
printTraceTable(report.budget_trace, { currency: cur, exchangeRate: rate });
|
|
389
|
+
const { cur, rate } = await resolveTraceOptions(currency);
|
|
390
|
+
printTraceReport(report, cur, rate);
|
|
405
391
|
return;
|
|
406
392
|
}
|
|
407
|
-
|
|
408
393
|
printTextReport(report);
|
|
409
394
|
}
|
|
410
395
|
|
|
396
|
+
export async function reportCommand({ list = false, sessionId = null, format = "text", trace = false, currency = "usd", pgTask = null }) {
|
|
397
|
+
const dir = getSessionRoot();
|
|
398
|
+
if (!(await exists(dir))) {
|
|
399
|
+
console.log("No reports yet");
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const entries = await fs.readdir(dir);
|
|
404
|
+
|
|
405
|
+
if (pgTask) {
|
|
406
|
+
return handlePgTaskReport({ dir, pgTask, list, sessionId, format, trace, currency });
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (list) {
|
|
410
|
+
for (const item of entries) console.log(item);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return handleSingleSessionReport({ dir, entries, sessionId, format, trace, currency });
|
|
415
|
+
}
|
|
416
|
+
|
|
411
417
|
export { formatDuration, convertCost, formatCost, printTraceTable, buildReport, findSessionsByPgTask };
|
package/src/commands/roles.js
CHANGED
package/src/commands/run.js
CHANGED
|
@@ -25,7 +25,6 @@ export async function runCommandHandler({ task, config, logger, flags }) {
|
|
|
25
25
|
// --- Planning Game: resolve card ID ---
|
|
26
26
|
const pgCardId = flags?.pgTask || parseCardId(task);
|
|
27
27
|
const pgProject = flags?.pgProject || config.planning_game?.project_id || null;
|
|
28
|
-
const enrichedTask = task;
|
|
29
28
|
|
|
30
29
|
const jsonMode = flags?.json;
|
|
31
30
|
|
|
@@ -48,10 +47,10 @@ export async function runCommandHandler({ task, config, logger, flags }) {
|
|
|
48
47
|
});
|
|
49
48
|
|
|
50
49
|
if (!jsonMode) {
|
|
51
|
-
printHeader({ task:
|
|
50
|
+
printHeader({ task: task, config });
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
const result = await runFlow({ task:
|
|
53
|
+
const result = await runFlow({ task: task, config, logger, flags, emitter, pgTaskId: pgCardId || null, pgProject: pgProject || null });
|
|
55
54
|
|
|
56
55
|
if (jsonMode) {
|
|
57
56
|
console.log(JSON.stringify(result, null, 2));
|