karajan-code 2.5.0 → 2.6.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 +20 -4
- package/package.json +5 -2
- package/src/agents/aider-agent.js +1 -2
- package/src/agents/base-agent.js +63 -1
- package/src/agents/claude-agent.js +3 -4
- package/src/agents/codex-agent.js +1 -2
- package/src/agents/gemini-agent.js +1 -2
- package/src/agents/index.js +11 -2
- package/src/agents/opencode-agent.js +1 -2
- package/src/brain/decision-tracker.js +123 -0
- package/src/brain/decisor.js +243 -0
- package/src/brain/solomon-consult.js +163 -0
- package/src/budget/comparison.js +96 -0
- package/src/checks/binaries.js +123 -0
- package/src/checks/ci.js +105 -0
- package/src/checks/config-files.js +217 -0
- package/src/checks/dir-setup.js +63 -0
- package/src/checks/mcp-health.js +157 -0
- package/src/checks/node.js +61 -0
- package/src/checks/ports.js +110 -0
- package/src/checks/rtk.js +36 -0
- package/src/checks/runner.js +267 -0
- package/src/checks/skills.js +49 -0
- package/src/checks/sonar.js +67 -0
- package/src/checks/system.js +53 -0
- package/src/checks/tokens.js +96 -0
- package/src/checks/types.js +105 -0
- package/src/cli.js +29 -3
- package/src/commands/board.js +23 -0
- package/src/commands/doctor.js +130 -362
- package/src/commands/resume.js +1 -1
- package/src/commands/run.js +2 -1
- package/src/commands/skills.js +49 -0
- package/src/config/schema.js +222 -0
- package/src/config.js +32 -3
- package/src/guards/policy-resolver.js +8 -0
- package/src/hu/store.js +9 -0
- package/src/infrastructure/command-runner.js +44 -0
- package/src/infrastructure/environment.js +38 -0
- package/src/infrastructure/file-system-service.js +63 -0
- package/src/infrastructure/mocks.js +182 -0
- package/src/mcp/handlers/management-handlers.js +7 -2
- package/src/mcp/tools.js +10 -3
- package/src/orchestrator/config-init.js +16 -1
- package/src/orchestrator/flow-runner.js +2085 -0
- package/src/orchestrator/pipeline-context.js +17 -0
- package/src/orchestrator/preflight-checks.js +78 -4
- package/src/orchestrator/solomon-escalation.js +21 -0
- package/src/orchestrator/stages/stage-executor.js +155 -0
- package/src/orchestrator.js +19 -1923
- package/src/prompts/architect.js +2 -2
- package/src/prompts/coder.js +2 -2
- package/src/prompts/prompt-resolver.js +127 -0
- package/src/prompts/reviewer.js +2 -2
- package/src/roles/architect-role.js +3 -1
- package/src/roles/base-role.js +23 -2
- package/src/roles/coder-role.js +3 -1
- package/src/roles/security-role.js +12 -0
- package/src/roles/tester-role.js +11 -0
- package/src/session/journal/decisions-writer.js +200 -0
- package/src/session/journal/iteration-logger.js +194 -0
- package/src/session/journal/summary-writer.js +176 -0
- package/src/session/journal/tree-writer.js +164 -0
- package/src/skills/semantic-detector.js +147 -0
- package/src/skills/skill-detector.js +186 -8
- package/src/skills/skill-loader.js +131 -1
- package/src/skills/skills-cache.js +128 -0
- package/src/types/agent.js +55 -0
- package/src/types/config.js +114 -0
- package/src/types/hu.js +47 -0
- package/src/types/index.js +34 -0
- package/src/types/policy.js +43 -0
- package/src/types/session.js +74 -0
- package/src/types/stage.js +68 -0
- package/src/utils/display/event-handlers.js +389 -0
- package/src/utils/display/formatters.js +132 -0
- package/src/utils/display/header.js +48 -0
- package/src/utils/display/session-summary.js +108 -0
- package/src/utils/display/solomon.js +42 -0
- package/src/utils/port-check.js +45 -0
- package/src/utils/port-occupant.js +95 -0
- package/src/utils/provider-env.js +116 -0
- package/src/utils/with-timeout.js +42 -0
- package/templates/roles/architect/anthropic.md +65 -0
- package/templates/roles/architect/default.md +63 -0
- package/templates/roles/architect/google.md +52 -0
- package/templates/roles/architect/openai.md +51 -0
- package/templates/roles/architect/opencode.md +50 -0
- package/templates/roles/coder/anthropic.md +72 -0
- package/templates/roles/coder/default.md +69 -0
- package/templates/roles/coder/google.md +78 -0
- package/templates/roles/coder/openai.md +59 -0
- package/templates/roles/coder/opencode.md +49 -0
- package/templates/roles/planner/anthropic.md +50 -0
- package/templates/roles/planner/default.md +48 -0
- package/templates/roles/planner/google.md +66 -0
- package/templates/roles/planner/openai.md +46 -0
- package/templates/roles/planner/opencode.md +53 -0
- package/templates/roles/reviewer/anthropic.md +61 -0
- package/templates/roles/reviewer/default.md +62 -0
- package/templates/roles/reviewer/google.md +63 -0
- package/templates/roles/reviewer/openai.md +57 -0
- package/templates/roles/reviewer/opencode.md +42 -0
- package/templates/roles/solomon/anthropic.md +71 -0
- package/templates/roles/solomon/default.md +83 -0
- package/templates/roles/solomon/google.md +61 -0
- package/templates/roles/solomon/openai.md +53 -0
- package/templates/roles/solomon/opencode.md +55 -0
- package/src/utils/display.js +0 -669
package/src/utils/display.js
DELETED
|
@@ -1,669 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { printBanner } from "./banner.js";
|
|
5
|
-
|
|
6
|
-
// TODO: i18n display messages
|
|
7
|
-
const DISPLAY_PKG_PATH = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../package.json");
|
|
8
|
-
const DISPLAY_VERSION = JSON.parse(readFileSync(DISPLAY_PKG_PATH, "utf8")).version;
|
|
9
|
-
|
|
10
|
-
const ANSI = {
|
|
11
|
-
reset: "\x1b[0m",
|
|
12
|
-
bold: "\x1b[1m",
|
|
13
|
-
dim: "\x1b[2m",
|
|
14
|
-
cyan: "\x1b[36m",
|
|
15
|
-
green: "\x1b[32m",
|
|
16
|
-
yellow: "\x1b[33m",
|
|
17
|
-
red: "\x1b[31m",
|
|
18
|
-
gray: "\x1b[90m",
|
|
19
|
-
white: "\x1b[37m",
|
|
20
|
-
magenta: "\x1b[35m"
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const ICONS = {
|
|
24
|
-
"session:start": "\u25b6",
|
|
25
|
-
"planner:start": "\ud83e\udde0",
|
|
26
|
-
"planner:end": "\ud83e\udde0",
|
|
27
|
-
"researcher:start": "\ud83d\udd2c",
|
|
28
|
-
"researcher:end": "\ud83d\udd2c",
|
|
29
|
-
"iteration:start": "\u25b6",
|
|
30
|
-
"coder:start": "\ud83d\udd28",
|
|
31
|
-
"coder:end": "\ud83d\udd28",
|
|
32
|
-
"refactorer:start": "\u267b\ufe0f",
|
|
33
|
-
"refactorer:end": "\u267b\ufe0f",
|
|
34
|
-
"tdd:result": "\ud83d\udccb",
|
|
35
|
-
"sonar:start": "\ud83d\udd0d",
|
|
36
|
-
"sonar:end": "\ud83d\udd0d",
|
|
37
|
-
"reviewer:start": "\ud83d\udc41\ufe0f",
|
|
38
|
-
"reviewer:end": "\ud83d\udc41\ufe0f",
|
|
39
|
-
"tester:start": "\ud83e\uddea",
|
|
40
|
-
"tester:end": "\ud83e\uddea",
|
|
41
|
-
"security:start": "\ud83d\udd12",
|
|
42
|
-
"security:end": "\ud83d\udd12",
|
|
43
|
-
"solomon:start": "\u2696\ufe0f",
|
|
44
|
-
"solomon:end": "\u2696\ufe0f",
|
|
45
|
-
"solomon:escalate": "\u26a0\ufe0f",
|
|
46
|
-
"coder:standby": "\u23f3",
|
|
47
|
-
"coder:standby_heartbeat": "\u23f3",
|
|
48
|
-
"agent:heartbeat": "\u23f3",
|
|
49
|
-
"coder:standby_resume": "\u25b6\ufe0f",
|
|
50
|
-
"budget:update": "\ud83d\udcb8",
|
|
51
|
-
"iteration:end": "\u23f1\ufe0f",
|
|
52
|
-
"session:end": "\ud83c\udfc1",
|
|
53
|
-
"discover:start": "\ud83d\udd0e",
|
|
54
|
-
"discover:end": "\ud83d\udd0e",
|
|
55
|
-
"architect:start": "\ud83c\udfdb\ufe0f",
|
|
56
|
-
"architect:end": "\ud83c\udfdb\ufe0f",
|
|
57
|
-
"hu-reviewer:start": "\ud83d\udcdd",
|
|
58
|
-
"hu-reviewer:end": "\ud83d\udcdd",
|
|
59
|
-
"impeccable:start": "\ud83c\udfa8",
|
|
60
|
-
"impeccable:end": "\ud83c\udfa8",
|
|
61
|
-
"audit:start": "\ud83d\udccb",
|
|
62
|
-
"audit:end": "\ud83d\udccb",
|
|
63
|
-
"sonarcloud:start": "\u2601\ufe0f",
|
|
64
|
-
"sonarcloud:end": "\u2601\ufe0f",
|
|
65
|
-
"preflight:start": "\ud83d\udee1\ufe0f",
|
|
66
|
-
"preflight:end": "\ud83d\udee1\ufe0f",
|
|
67
|
-
question: "\u2753"
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const STATUS_ICON = {
|
|
71
|
-
ok: `${ANSI.green}\u2705${ANSI.reset}`,
|
|
72
|
-
fail: `${ANSI.red}\u274c${ANSI.reset}`,
|
|
73
|
-
paused: `${ANSI.yellow}\u23f8\ufe0f${ANSI.reset}`,
|
|
74
|
-
running: `${ANSI.cyan}\u25b6${ANSI.reset}`
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export function formatElapsed(ms) {
|
|
78
|
-
const totalSec = Math.floor((ms || 0) / 1000);
|
|
79
|
-
const min = String(Math.floor(totalSec / 60)).padStart(2, "0");
|
|
80
|
-
const sec = String(totalSec % 60).padStart(2, "0");
|
|
81
|
-
return `${min}:${sec}`;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const BAR = `${ANSI.dim}\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${ANSI.reset}`;
|
|
85
|
-
|
|
86
|
-
export function printHeader({ task, config }) {
|
|
87
|
-
const version = DISPLAY_VERSION;
|
|
88
|
-
// Force banner: caller already gates on !jsonMode, so if we're here it's a human-readable run
|
|
89
|
-
printBanner(version, { force: true });
|
|
90
|
-
console.log(`${ANSI.bold}Task:${ANSI.reset} ${task}`);
|
|
91
|
-
console.log(
|
|
92
|
-
`${ANSI.bold}Coder:${ANSI.reset} ${config.roles?.coder?.provider || config.coder} ${ANSI.dim}|${ANSI.reset} ${ANSI.bold}Reviewer:${ANSI.reset} ${config.roles?.reviewer?.provider || config.reviewer}`
|
|
93
|
-
);
|
|
94
|
-
console.log(
|
|
95
|
-
`${ANSI.bold}Max iterations:${ANSI.reset} ${config.max_iterations} ${ANSI.dim}|${ANSI.reset} ${ANSI.bold}Timeout:${ANSI.reset} ${config.session.max_total_minutes}min`
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
const pipeline = config.pipeline || {};
|
|
99
|
-
const activeRoles = [];
|
|
100
|
-
if (pipeline.planner?.enabled) activeRoles.push(`Planner (${config.roles?.planner?.provider || "?"})`);
|
|
101
|
-
if (pipeline.researcher?.enabled) activeRoles.push(`Researcher (${config.roles?.researcher?.provider || "?"})`);
|
|
102
|
-
if (pipeline.tester?.enabled) activeRoles.push("Tester");
|
|
103
|
-
if (pipeline.security?.enabled) activeRoles.push("Security");
|
|
104
|
-
if (pipeline.solomon?.enabled) {
|
|
105
|
-
const solomonProvider = config.roles?.solomon?.provider;
|
|
106
|
-
activeRoles.push(solomonProvider ? `Solomon (${solomonProvider})` : "Solomon");
|
|
107
|
-
}
|
|
108
|
-
if (activeRoles.length > 0) {
|
|
109
|
-
const separator = ` ${ANSI.dim}|${ANSI.reset} `;
|
|
110
|
-
console.log(`${ANSI.bold}Pipeline:${ANSI.reset} ${activeRoles.join(separator)}`);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
console.log(BAR);
|
|
114
|
-
console.log();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/* ── Helper: executor/provider tag ───────────────────────────── */
|
|
118
|
-
|
|
119
|
-
function formatExecutor(detail) {
|
|
120
|
-
if (!detail) return '';
|
|
121
|
-
const provider = detail.provider || '';
|
|
122
|
-
const type = detail.executorType || '';
|
|
123
|
-
if (provider) return ` ${ANSI.dim}${provider}${ANSI.reset}`;
|
|
124
|
-
if (type === 'local') return ` ${ANSI.dim}local${ANSI.reset}`;
|
|
125
|
-
if (type === 'system') return ` ${ANSI.dim}system${ANSI.reset}`;
|
|
126
|
-
return '';
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/* ── Helper: role start/end one-liners ───────────────────────── */
|
|
130
|
-
|
|
131
|
-
function roleStart(icon, label, provider) {
|
|
132
|
-
console.log(` \u251c\u2500 ${icon} ${label} (${provider || "?"}) running...`);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function roleEnd(status, label, elapsed, executor) {
|
|
136
|
-
console.log(` \u251c\u2500 ${status} ${label} completed${executor || ''} ${elapsed}`);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/* ── Helper: pass/fail stage result ─────────────────────────── */
|
|
140
|
-
|
|
141
|
-
function passFailStage(detail, label, failDefault, elapsed) {
|
|
142
|
-
const executor = formatExecutor(detail);
|
|
143
|
-
if (detail?.ok === false) {
|
|
144
|
-
const summary = detail?.summary || failDefault;
|
|
145
|
-
console.log(` \u251c\u2500 ${ANSI.red}\u274c ${label}: ${summary}${ANSI.reset}${executor} ${elapsed}`);
|
|
146
|
-
} else {
|
|
147
|
-
console.log(` \u251c\u2500 ${ANSI.green}\u2705 ${label}: passed${ANSI.reset}${executor} ${elapsed}`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/* ── Helper: solomon ruling display ─────────────────────────── */
|
|
152
|
-
|
|
153
|
-
const SOLOMON_RULING_HANDLERS = {
|
|
154
|
-
approve(detail, elapsed) {
|
|
155
|
-
const dismissedCount = detail?.dismissed?.length || 0;
|
|
156
|
-
const dismissedSuffix = dismissedCount > 0 ? ` (${dismissedCount} dismissed)` : "";
|
|
157
|
-
console.log(` \u251c\u2500 ${ANSI.green}\u2696\ufe0f Solomon: APPROVE${dismissedSuffix}${ANSI.reset} ${elapsed}`);
|
|
158
|
-
},
|
|
159
|
-
approve_with_conditions(detail, elapsed) {
|
|
160
|
-
const condCount = detail?.conditions?.length || 0;
|
|
161
|
-
console.log(` \u251c\u2500 ${ANSI.yellow}\u2696\ufe0f Solomon: ${condCount} condition${condCount === 1 ? "" : "s"}${ANSI.reset} ${elapsed}`);
|
|
162
|
-
if (detail?.conditions) {
|
|
163
|
-
for (const cond of detail.conditions) {
|
|
164
|
-
console.log(` \u2502 ${ANSI.dim}${cond}${ANSI.reset}`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
escalate_human(detail, elapsed) {
|
|
169
|
-
const reason = detail?.escalate_reason || "unknown reason";
|
|
170
|
-
console.log(` \u251c\u2500 ${ANSI.red}\u2696\ufe0f Solomon: ESCALATE \u2014 ${reason}${ANSI.reset} ${elapsed}`);
|
|
171
|
-
},
|
|
172
|
-
create_subtask(detail, elapsed) {
|
|
173
|
-
const subtaskTitle = detail?.subtask?.title || "untitled";
|
|
174
|
-
console.log(` \u251c\u2500 ${ANSI.magenta}\u2696\ufe0f Solomon: SUBTASK \u2014 ${subtaskTitle}${ANSI.reset} ${elapsed}`);
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
function printSolomonRuling(detail, elapsed) {
|
|
179
|
-
const ruling = detail?.ruling || "unknown";
|
|
180
|
-
const handler = SOLOMON_RULING_HANDLERS[ruling];
|
|
181
|
-
if (handler) {
|
|
182
|
-
handler(detail, elapsed);
|
|
183
|
-
} else {
|
|
184
|
-
const rulingUpper = ruling.toUpperCase().replaceAll("_", " ");
|
|
185
|
-
console.log(` \u251c\u2500 \u2696\ufe0f Solomon: ${rulingUpper} ${elapsed}`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/* ── Helper: budget color selection ─────────────────────────── */
|
|
190
|
-
|
|
191
|
-
function budgetColor(max, pct, warn) {
|
|
192
|
-
if (max > 0 && pct >= 100) return ANSI.red;
|
|
193
|
-
if (max > 0 && pct >= warn) return ANSI.yellow;
|
|
194
|
-
return ANSI.green;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/* ── Helpers: session:end sub-sections ──────────────────────── */
|
|
198
|
-
|
|
199
|
-
function printSessionStages(stages) {
|
|
200
|
-
if (!stages) return;
|
|
201
|
-
if (stages.researcher?.summary) {
|
|
202
|
-
console.log(` ${ANSI.dim}\ud83d\udd2c Research: ${stages.researcher.summary}${ANSI.reset}`);
|
|
203
|
-
}
|
|
204
|
-
printSessionPlanner(stages.planner);
|
|
205
|
-
if (stages.tester?.summary) {
|
|
206
|
-
console.log(` ${ANSI.dim}\ud83e\uddea Tester: ${stages.tester.summary}${ANSI.reset}`);
|
|
207
|
-
}
|
|
208
|
-
if (stages.security?.summary) {
|
|
209
|
-
console.log(` ${ANSI.dim}\ud83d\udd12 Security: ${stages.security.summary}${ANSI.reset}`);
|
|
210
|
-
}
|
|
211
|
-
printSessionSonar(stages.sonar);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function printSessionPlanner(planner) {
|
|
215
|
-
if (!planner?.title && !planner?.approach && !planner?.completedSteps?.length) return;
|
|
216
|
-
const planParts = [];
|
|
217
|
-
if (planner.title) planParts.push(planner.title);
|
|
218
|
-
if (planner.approach) planParts.push(`approach: ${planner.approach}`);
|
|
219
|
-
console.log(` ${ANSI.dim}\ud83d\uddfa Plan: ${planParts.join(" | ")}${ANSI.reset}`);
|
|
220
|
-
for (const step of planner.completedSteps || []) {
|
|
221
|
-
console.log(` ${ANSI.dim} \u2713 ${step}${ANSI.reset}`);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function printSessionSonar(sonar) {
|
|
226
|
-
if (!sonar) return;
|
|
227
|
-
const gateLabel = sonar.gateStatus === "OK" ? ANSI.green : ANSI.red;
|
|
228
|
-
console.log(` ${ANSI.dim}\ud83d\udd0d Sonar: ${gateLabel}${sonar.gateStatus}${ANSI.reset}${ANSI.dim} (${sonar.openIssues ?? 0} issues)${ANSI.reset}`);
|
|
229
|
-
if (typeof sonar.issuesInitial === "number" || typeof sonar.issuesResolved === "number") {
|
|
230
|
-
const issuesInitial = sonar.issuesInitial ?? sonar.openIssues ?? 0;
|
|
231
|
-
const issuesFinal = sonar.issuesFinal ?? sonar.openIssues ?? 0;
|
|
232
|
-
const issuesResolved = sonar.issuesResolved ?? Math.max(issuesInitial - issuesFinal, 0);
|
|
233
|
-
console.log(` ${ANSI.dim}\ud83d\udee0 Issues: ${issuesInitial} detected, ${issuesFinal} open, ${issuesResolved} resolved${ANSI.reset}`);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function printSessionGit(git) {
|
|
238
|
-
if (!git?.branch) return;
|
|
239
|
-
const parts = [`branch: ${git.branch}`];
|
|
240
|
-
if (git.committed) parts.push("committed");
|
|
241
|
-
if (git.pushed) parts.push("pushed");
|
|
242
|
-
if (git.pr || git.prUrl) parts.push(`PR: ${git.pr || git.prUrl}`);
|
|
243
|
-
console.log(` ${ANSI.dim}\ud83d\udcce Git: ${parts.join(", ")}${ANSI.reset}`);
|
|
244
|
-
if (Array.isArray(git.commits) && git.commits.length > 0) {
|
|
245
|
-
console.log(` ${ANSI.dim}\ud83e\uddfe Commits:${ANSI.reset}`);
|
|
246
|
-
for (const commit of git.commits) {
|
|
247
|
-
const shortHash = (commit.hash || "").slice(0, 7) || "unknown";
|
|
248
|
-
const message = commit.message || "";
|
|
249
|
-
console.log(` ${ANSI.dim} - ${shortHash} ${message}${ANSI.reset}`);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function isBudgetUnavailable(budget) {
|
|
255
|
-
return budget.usage_available === false ||
|
|
256
|
-
(budget.total_tokens === 0 && budget.total_cost_usd === 0 && Object.keys(budget.breakdown_by_role || {}).length > 0);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function printSessionRtkSavings(rtkSavings) {
|
|
260
|
-
if (!rtkSavings || !rtkSavings.callCount) return;
|
|
261
|
-
const tokens = rtkSavings.estimatedTokensSaved ?? 0;
|
|
262
|
-
const ratio = rtkSavings.savedPct ?? 0;
|
|
263
|
-
const commands = rtkSavings.callCount ?? 0;
|
|
264
|
-
const original = rtkSavings.originalBytes ?? 0;
|
|
265
|
-
const compressed = rtkSavings.rtkBytes ?? 0;
|
|
266
|
-
|
|
267
|
-
if (tokens === 0 || ratio === 0) {
|
|
268
|
-
console.log(` ${ANSI.dim}⚡ RTK: ${commands} commands wrapped, 0% savings — output was already minimal (${original} bytes in, ${compressed} bytes out)${ANSI.reset}`);
|
|
269
|
-
} else {
|
|
270
|
-
console.log(` ${ANSI.dim}⚡ RTK: saved ~${tokens.toLocaleString()} tokens (${ratio}% compression, ${commands} commands, ${original.toLocaleString()} → ${compressed.toLocaleString()} bytes)${ANSI.reset}`);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function printSessionBudget(budget) {
|
|
275
|
-
if (!budget) return;
|
|
276
|
-
if (isBudgetUnavailable(budget)) {
|
|
277
|
-
console.log(` ${ANSI.dim}\ud83d\udcb0 Budget: N/A (provider does not report usage)${ANSI.reset}`);
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
const estPrefix = budget.includes_estimates ? "~" : "";
|
|
281
|
-
const estNote = budget.includes_estimates ? " (includes estimates)" : "";
|
|
282
|
-
const fmtTokens = (n) => Number(n || 0).toLocaleString("en-US");
|
|
283
|
-
console.log(` ${ANSI.dim}\ud83d\udcb0 Total tokens: ${estPrefix}${fmtTokens(budget.total_tokens)}${estNote}${ANSI.reset}`);
|
|
284
|
-
console.log(` ${ANSI.dim}\ud83d\udcb0 Total cost: ${estPrefix}$${Number(budget.total_cost_usd || 0).toFixed(2)}${ANSI.reset}`);
|
|
285
|
-
for (const [role, metrics] of Object.entries(budget.breakdown_by_role || {})) {
|
|
286
|
-
const tokens = Number(metrics.total_tokens || 0);
|
|
287
|
-
const cost = Number(metrics.total_cost_usd || 0);
|
|
288
|
-
if (tokens === 0 && cost === 0) continue; // skip roles with no usage
|
|
289
|
-
console.log(
|
|
290
|
-
` ${ANSI.dim} - ${role}: ${estPrefix}${fmtTokens(tokens)} tokens, ${estPrefix}$${cost.toFixed(2)}${ANSI.reset}`
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/* ── Helper: pipeline tracker stage icon/color ──────────────── */
|
|
296
|
-
|
|
297
|
-
const TRACKER_STATUS = {
|
|
298
|
-
done: { icon: "\u2713", color: ANSI.green },
|
|
299
|
-
running: { icon: "\u25b6", color: ANSI.cyan },
|
|
300
|
-
failed: { icon: "\u2717", color: ANSI.red }
|
|
301
|
-
};
|
|
302
|
-
const TRACKER_DEFAULT = { icon: "\u00b7", color: ANSI.dim };
|
|
303
|
-
|
|
304
|
-
/* ── Event handler map ──────────────────────────────────────── */
|
|
305
|
-
|
|
306
|
-
const EVENT_HANDLERS = {
|
|
307
|
-
"session:start": () => {},
|
|
308
|
-
|
|
309
|
-
"iteration:start": (event, icon, elapsed) => {
|
|
310
|
-
const retryCount = event.detail?.reviewerRetryCount || 0;
|
|
311
|
-
const maxRetries = event.detail?.maxReviewerRetries;
|
|
312
|
-
const retrySuffix = retryCount > 0 && maxRetries
|
|
313
|
-
? ` ${ANSI.dim}\u2014 reviewer retry ${retryCount}/${maxRetries}${ANSI.reset}`
|
|
314
|
-
: "";
|
|
315
|
-
console.log(
|
|
316
|
-
`\n${ANSI.bold}${icon} Iteration ${event.detail?.iteration}/${event.detail?.maxIterations}${ANSI.reset}${retrySuffix} ${elapsed}`
|
|
317
|
-
);
|
|
318
|
-
},
|
|
319
|
-
|
|
320
|
-
"planner:start": (event, icon) => {
|
|
321
|
-
roleStart(icon, "Planner", event.detail?.planner);
|
|
322
|
-
},
|
|
323
|
-
|
|
324
|
-
"planner:end": (event, _icon, elapsed, status) => {
|
|
325
|
-
roleEnd(status, "Planner", elapsed, formatExecutor(event.detail));
|
|
326
|
-
},
|
|
327
|
-
|
|
328
|
-
"coder:start": (event, icon) => {
|
|
329
|
-
roleStart(icon, "Coder", event.detail?.coder);
|
|
330
|
-
},
|
|
331
|
-
|
|
332
|
-
"coder:end": (event, _icon, elapsed, status) => {
|
|
333
|
-
roleEnd(status, "Coder", elapsed, formatExecutor(event.detail));
|
|
334
|
-
},
|
|
335
|
-
|
|
336
|
-
"refactorer:start": (event, icon) => {
|
|
337
|
-
roleStart(icon, "Refactorer", event.detail?.refactorer);
|
|
338
|
-
},
|
|
339
|
-
|
|
340
|
-
"refactorer:end": (event, _icon, elapsed, status) => {
|
|
341
|
-
roleEnd(status, "Refactorer", elapsed, formatExecutor(event.detail));
|
|
342
|
-
},
|
|
343
|
-
|
|
344
|
-
"tdd:result": (event, icon) => {
|
|
345
|
-
const tdd = event.detail || {};
|
|
346
|
-
if (tdd.reason === "no_diff_available") {
|
|
347
|
-
console.log(` \u251c\u2500 ${icon} TDD policy: ${ANSI.yellow}SKIP${ANSI.reset} ${ANSI.dim}(no diff available)${ANSI.reset}`);
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
const label = tdd.ok ? `${ANSI.green}PASS${ANSI.reset}` : `${ANSI.red}FAIL${ANSI.reset}`;
|
|
351
|
-
const files = tdd.sourceFiles === undefined ? "" : ` (${tdd.sourceFiles} src, ${tdd.testFiles} test)`;
|
|
352
|
-
const executor = formatExecutor(event.detail);
|
|
353
|
-
console.log(` \u251c\u2500 ${icon} TDD policy: ${label}${files}${executor}`);
|
|
354
|
-
},
|
|
355
|
-
|
|
356
|
-
"researcher:start": (event, icon) => {
|
|
357
|
-
console.log(` \u251c\u2500 ${icon} Researcher (${event.detail?.researcher || "?"}) investigating...`);
|
|
358
|
-
},
|
|
359
|
-
|
|
360
|
-
"researcher:end": (event, _icon, elapsed, status) => {
|
|
361
|
-
roleEnd(status, "Researcher", elapsed, formatExecutor(event.detail));
|
|
362
|
-
},
|
|
363
|
-
|
|
364
|
-
"sonar:start": (event, icon) => {
|
|
365
|
-
const executor = formatExecutor(event.detail);
|
|
366
|
-
console.log(` \u251c\u2500 ${icon} SonarQube scanning...${executor}`);
|
|
367
|
-
},
|
|
368
|
-
|
|
369
|
-
"sonar:end": (event, _icon, elapsed, status) => {
|
|
370
|
-
const gate = event.detail?.gateStatus || "?";
|
|
371
|
-
const gateColor = gate === "OK" ? ANSI.green : ANSI.red;
|
|
372
|
-
const executor = formatExecutor(event.detail);
|
|
373
|
-
console.log(` \u251c\u2500 ${status} Quality gate: ${gateColor}${gate}${ANSI.reset}${executor} ${elapsed}`);
|
|
374
|
-
},
|
|
375
|
-
|
|
376
|
-
"reviewer:start": (event, icon) => {
|
|
377
|
-
console.log(` \u251c\u2500 ${icon} Reviewer (${event.detail?.reviewer || "?"}) running...`);
|
|
378
|
-
},
|
|
379
|
-
|
|
380
|
-
"reviewer:end": (event, _icon, elapsed) => {
|
|
381
|
-
const review = event.detail || {};
|
|
382
|
-
const executor = formatExecutor(event.detail);
|
|
383
|
-
if (review.approved) {
|
|
384
|
-
console.log(` \u251c\u2500 ${ANSI.green}\u2705 Review: APPROVED${ANSI.reset}${executor} ${elapsed}`);
|
|
385
|
-
} else {
|
|
386
|
-
const count = review.blockingCount || 0;
|
|
387
|
-
console.log(` \u251c\u2500 ${ANSI.red}\u274c Review: REJECTED (${count} blocking)${ANSI.reset}${executor}`);
|
|
388
|
-
if (review.issues) {
|
|
389
|
-
for (const issue of review.issues) {
|
|
390
|
-
console.log(` \u2502 ${ANSI.dim}${issue}${ANSI.reset}`);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
},
|
|
395
|
-
|
|
396
|
-
"tester:start": (event, icon) => {
|
|
397
|
-
const executor = formatExecutor(event.detail);
|
|
398
|
-
console.log(` \u251c\u2500 ${icon} Tester evaluating...${executor}`);
|
|
399
|
-
},
|
|
400
|
-
|
|
401
|
-
"tester:end": (event, _icon, elapsed) => {
|
|
402
|
-
passFailStage(event.detail, "Tester", "issues found", elapsed);
|
|
403
|
-
},
|
|
404
|
-
|
|
405
|
-
"security:start": (event, icon) => {
|
|
406
|
-
const executor = formatExecutor(event.detail);
|
|
407
|
-
console.log(` \u251c\u2500 ${icon} Security auditing...${executor}`);
|
|
408
|
-
},
|
|
409
|
-
|
|
410
|
-
"security:end": (event, _icon, elapsed) => {
|
|
411
|
-
passFailStage(event.detail, "Security", "vulnerabilities found", elapsed);
|
|
412
|
-
},
|
|
413
|
-
|
|
414
|
-
"solomon:start": (event, icon) => {
|
|
415
|
-
const executor = formatExecutor(event.detail);
|
|
416
|
-
console.log(` \u251c\u2500 ${icon} Solomon arbitrating ${event.detail?.conflictStage || "?"} conflict...${executor}`);
|
|
417
|
-
},
|
|
418
|
-
|
|
419
|
-
"solomon:end": (event, _icon, elapsed) => {
|
|
420
|
-
printSolomonRuling(event.detail, elapsed);
|
|
421
|
-
},
|
|
422
|
-
|
|
423
|
-
"solomon:escalate": (event, icon) => {
|
|
424
|
-
const subloop = event.detail?.subloop || "?";
|
|
425
|
-
const retryCount = event.detail?.retryCount || 0;
|
|
426
|
-
const limit = event.detail?.limit || "?";
|
|
427
|
-
console.log(` \u251c\u2500 ${icon} ${subloop} sub-loop limit reached (${retryCount}/${limit}), invoking Solomon...`);
|
|
428
|
-
},
|
|
429
|
-
|
|
430
|
-
"coder:standby": (event, icon) => {
|
|
431
|
-
const until = event.detail?.cooldownUntil || "?";
|
|
432
|
-
const attempt = event.detail?.retryCount || "?";
|
|
433
|
-
const maxRetries = event.detail?.maxRetries || "?";
|
|
434
|
-
console.log(` \u251c\u2500 ${ANSI.yellow}${icon} Rate limited \u2014 standby until ${until} (attempt ${attempt}/${maxRetries})${ANSI.reset}`);
|
|
435
|
-
},
|
|
436
|
-
|
|
437
|
-
"coder:standby_heartbeat": (event, icon) => {
|
|
438
|
-
const remaining = event.detail?.remainingMs === undefined ? "?" : Math.round(event.detail.remainingMs / 1000);
|
|
439
|
-
console.log(` \u251c\u2500 ${ANSI.yellow}${icon} Standby: ${remaining}s remaining${ANSI.reset}`);
|
|
440
|
-
},
|
|
441
|
-
|
|
442
|
-
"agent:heartbeat": (event, icon) => {
|
|
443
|
-
const d = event.detail || {};
|
|
444
|
-
const provider = d.provider || "?";
|
|
445
|
-
const elapsed = d.elapsedMs ? Math.round(d.elapsedMs / 1000) : 0;
|
|
446
|
-
const silent = d.silenceMs ? Math.round(d.silenceMs / 1000) : 0;
|
|
447
|
-
const status = d.status === "waiting" ? `${ANSI.yellow}waiting (silent ${silent}s)` : `${ANSI.green}working`;
|
|
448
|
-
console.log(` \u251c\u2500 ${ANSI.dim}${icon} ${provider} ${status}${ANSI.dim} — ${elapsed}s elapsed${ANSI.reset}`);
|
|
449
|
-
},
|
|
450
|
-
|
|
451
|
-
"coder:standby_resume": (event, icon) => {
|
|
452
|
-
console.log(` \u251c\u2500 ${ANSI.green}${icon} Cooldown expired \u2014 resuming with ${event.detail?.coder || event.detail?.provider || "?"}${ANSI.reset}`);
|
|
453
|
-
},
|
|
454
|
-
|
|
455
|
-
"iteration:end": (event, icon, elapsed) => {
|
|
456
|
-
console.log(` \u2514\u2500 ${icon} Duration: ${formatElapsed(event.detail?.duration)} ${elapsed}`);
|
|
457
|
-
},
|
|
458
|
-
|
|
459
|
-
"budget:update": (event, icon) => {
|
|
460
|
-
const d = event.detail || {};
|
|
461
|
-
const total = Number(d.total_cost_usd || 0);
|
|
462
|
-
const totalTokens = Number(d.total_tokens || 0);
|
|
463
|
-
const max = Number(d.max_budget_usd);
|
|
464
|
-
const pct = Number(d.pct_used ?? 0);
|
|
465
|
-
const warn = Number(d.warn_threshold_pct ?? 80);
|
|
466
|
-
// Don't show N/A — just skip budget display when there's nothing to report
|
|
467
|
-
if (total === 0 && totalTokens === 0) return;
|
|
468
|
-
const fmtTokens = (n) => n.toLocaleString("en-US");
|
|
469
|
-
const tokenStr = totalTokens > 0 ? ` / ${fmtTokens(totalTokens)} tokens` : "";
|
|
470
|
-
const costStr = `$${total.toFixed(2)}`;
|
|
471
|
-
const color = budgetColor(max, pct, warn);
|
|
472
|
-
if (Number.isFinite(max) && max > 0) {
|
|
473
|
-
console.log(` \u251c\u2500 ${icon} Budget: ${color}${costStr}${tokenStr} / $${max.toFixed(2)} (${pct.toFixed(1)}%)${ANSI.reset}`);
|
|
474
|
-
} else {
|
|
475
|
-
console.log(` \u251c\u2500 ${icon} Budget: ${color}${costStr}${tokenStr}${ANSI.reset}`);
|
|
476
|
-
}
|
|
477
|
-
},
|
|
478
|
-
|
|
479
|
-
"session:end": (event, icon, elapsed) => {
|
|
480
|
-
console.log();
|
|
481
|
-
const resultLabel = event.detail?.approved
|
|
482
|
-
? `${ANSI.bold}${ANSI.green}APPROVED${ANSI.reset}`
|
|
483
|
-
: `${ANSI.bold}${ANSI.red}${event.detail?.reason || "FAILED"}${ANSI.reset}`;
|
|
484
|
-
console.log(`${icon} Result: ${resultLabel} ${elapsed}`);
|
|
485
|
-
printSessionStages(event.detail?.stages);
|
|
486
|
-
printSessionGit(event.detail?.git);
|
|
487
|
-
printSessionBudget(event.detail?.budget);
|
|
488
|
-
printSessionRtkSavings(event.detail?.rtk_savings);
|
|
489
|
-
console.log(`${ANSI.dim}Session: ${event.sessionId}${ANSI.reset}`);
|
|
490
|
-
},
|
|
491
|
-
|
|
492
|
-
question: (event, icon) => {
|
|
493
|
-
console.log();
|
|
494
|
-
console.log(`${ANSI.bold}${ANSI.yellow}${icon} Paused - question:${ANSI.reset}`);
|
|
495
|
-
const questionText = event.detail?.question || event.message || "";
|
|
496
|
-
const questionLines = questionText.split("\n");
|
|
497
|
-
for (const line of questionLines) {
|
|
498
|
-
console.log(` ${line}`);
|
|
499
|
-
}
|
|
500
|
-
console.log(`${ANSI.dim}Resume with: kj resume ${event.sessionId} --answer "<response>"${ANSI.reset}`);
|
|
501
|
-
},
|
|
502
|
-
|
|
503
|
-
"preflight:start": (event) => {
|
|
504
|
-
const executor = formatExecutor(event.detail);
|
|
505
|
-
console.log(` \u251c\u2500 \ud83d\udee1\ufe0f Preflight checks running...${executor}`);
|
|
506
|
-
},
|
|
507
|
-
|
|
508
|
-
"preflight:end": (event, _icon, elapsed) => {
|
|
509
|
-
const executor = formatExecutor(event.detail);
|
|
510
|
-
const ok = event.status === "ok";
|
|
511
|
-
const statusIcon = ok ? `${ANSI.green}\u2705` : `${ANSI.red}\u274c`;
|
|
512
|
-
console.log(` \u251c\u2500 ${statusIcon} Preflight: ${event.message}${ANSI.reset}${executor} ${elapsed}`);
|
|
513
|
-
},
|
|
514
|
-
|
|
515
|
-
"discover:start": (event, icon) => {
|
|
516
|
-
const provider = event.detail?.provider || event.detail?.discover || "?";
|
|
517
|
-
console.log(` \u251c\u2500 ${icon || "\ud83d\udd0e"} Discover (${provider}) analyzing...`);
|
|
518
|
-
},
|
|
519
|
-
|
|
520
|
-
"discover:end": (event, _icon, elapsed, status) => {
|
|
521
|
-
roleEnd(status, "Discover", elapsed, formatExecutor(event.detail));
|
|
522
|
-
},
|
|
523
|
-
|
|
524
|
-
"architect:start": (event) => {
|
|
525
|
-
const provider = event.detail?.provider || event.detail?.architect || "?";
|
|
526
|
-
console.log(` \u251c\u2500 \ud83c\udfdb\ufe0f Architect (${provider}) designing...`);
|
|
527
|
-
},
|
|
528
|
-
|
|
529
|
-
"architect:end": (event, _icon, elapsed, status) => {
|
|
530
|
-
roleEnd(status, "Architect", elapsed, formatExecutor(event.detail));
|
|
531
|
-
},
|
|
532
|
-
|
|
533
|
-
"hu-reviewer:start": (event) => {
|
|
534
|
-
const executor = formatExecutor(event.detail);
|
|
535
|
-
console.log(` \u251c\u2500 \ud83d\udcdd HU Reviewer certifying...${executor}`);
|
|
536
|
-
},
|
|
537
|
-
|
|
538
|
-
"hu-reviewer:end": (event, _icon, elapsed, status) => {
|
|
539
|
-
roleEnd(status, "HU Reviewer", elapsed, formatExecutor(event.detail));
|
|
540
|
-
},
|
|
541
|
-
|
|
542
|
-
"impeccable:start": (event) => {
|
|
543
|
-
const executor = formatExecutor(event.detail);
|
|
544
|
-
console.log(` \u251c\u2500 \ud83c\udfa8 Impeccable auditing design...${executor}`);
|
|
545
|
-
},
|
|
546
|
-
|
|
547
|
-
"impeccable:end": (event, _icon, elapsed, status) => {
|
|
548
|
-
roleEnd(status, "Impeccable", elapsed, formatExecutor(event.detail));
|
|
549
|
-
},
|
|
550
|
-
|
|
551
|
-
"audit:start": (event) => {
|
|
552
|
-
const executor = formatExecutor(event.detail);
|
|
553
|
-
console.log(` \u251c\u2500 \ud83d\udccb Final audit running...${executor}`);
|
|
554
|
-
},
|
|
555
|
-
|
|
556
|
-
"audit:end": (event, _icon, elapsed, status) => {
|
|
557
|
-
roleEnd(status, "Audit", elapsed, formatExecutor(event.detail));
|
|
558
|
-
},
|
|
559
|
-
|
|
560
|
-
"sonarcloud:start": (event) => {
|
|
561
|
-
const executor = formatExecutor(event.detail);
|
|
562
|
-
console.log(` \u251c\u2500 \u2601\ufe0f SonarCloud scanning...${executor}`);
|
|
563
|
-
},
|
|
564
|
-
|
|
565
|
-
"sonarcloud:end": (event, _icon, elapsed, status) => {
|
|
566
|
-
roleEnd(status, "SonarCloud", elapsed, formatExecutor(event.detail));
|
|
567
|
-
},
|
|
568
|
-
|
|
569
|
-
"guard:output": (event) => {
|
|
570
|
-
const executor = formatExecutor(event.detail);
|
|
571
|
-
console.log(` \u251c\u2500 \ud83d\udee1\ufe0f ${event.message}${executor}`);
|
|
572
|
-
},
|
|
573
|
-
|
|
574
|
-
"guard:perf": (event) => {
|
|
575
|
-
const executor = formatExecutor(event.detail);
|
|
576
|
-
console.log(` \u251c\u2500 \u26a1 ${event.message}${executor}`);
|
|
577
|
-
},
|
|
578
|
-
|
|
579
|
-
"pipeline:tracker": (event) => {
|
|
580
|
-
const trackerStages = event.detail?.stages || [];
|
|
581
|
-
console.log(` ${ANSI.dim}\u250c Pipeline${ANSI.reset}`);
|
|
582
|
-
for (const stage of trackerStages) {
|
|
583
|
-
const { icon: stIcon, color: stColor } = TRACKER_STATUS[stage.status] || TRACKER_DEFAULT;
|
|
584
|
-
let suffix = "";
|
|
585
|
-
if (stage.summary) {
|
|
586
|
-
suffix = stage.status === "running" ? ` (${stage.summary})` : ` \u2192 ${stage.summary}`;
|
|
587
|
-
}
|
|
588
|
-
console.log(` ${ANSI.dim}\u2502${ANSI.reset} ${stColor}${stIcon} ${stage.name}${suffix}${ANSI.reset}`);
|
|
589
|
-
}
|
|
590
|
-
console.log(` ${ANSI.dim}\u2514${ANSI.reset}`);
|
|
591
|
-
},
|
|
592
|
-
|
|
593
|
-
"session:checkpoint": (event) => {
|
|
594
|
-
const d = event.detail || {};
|
|
595
|
-
if (d.auto_continued) {
|
|
596
|
-
console.log(` \u251c\u2500 ${ANSI.dim}\u2714 Checkpoint: auto-continuing (${d.reason || "progress"})${ANSI.reset}`);
|
|
597
|
-
} else {
|
|
598
|
-
console.log(` \u251c\u2500 ${ANSI.yellow}\u23f8\ufe0f Interactive checkpoint at ${d.elapsed_minutes?.toFixed(1) || "?"}min${ANSI.reset}`);
|
|
599
|
-
}
|
|
600
|
-
},
|
|
601
|
-
|
|
602
|
-
"ci:pr-created": (event) => {
|
|
603
|
-
const url = event.detail?.prUrl || "";
|
|
604
|
-
console.log(` \u251c\u2500 ${ANSI.green}\ud83d\ude80 CI PR created: ${url}${ANSI.reset}`);
|
|
605
|
-
},
|
|
606
|
-
|
|
607
|
-
"brain:rules-alert": (event) => {
|
|
608
|
-
console.log(` \u251c\u2500 ${ANSI.yellow}\u26a0\ufe0f Rules alert: ${event.message || "anomaly detected"}${ANSI.reset}`);
|
|
609
|
-
},
|
|
610
|
-
|
|
611
|
-
"agent:output": (event) => {
|
|
612
|
-
console.log(` \u2502 ${ANSI.dim}${event.message}${ANSI.reset}`);
|
|
613
|
-
},
|
|
614
|
-
|
|
615
|
-
"agent:action": (event) => {
|
|
616
|
-
console.log(` \u2502 ${ANSI.dim}\u2937 ${event.message}${ANSI.reset}`);
|
|
617
|
-
}
|
|
618
|
-
};
|
|
619
|
-
|
|
620
|
-
/* ── Quiet-mode filter ──────────────────────────────────────── */
|
|
621
|
-
|
|
622
|
-
/** Event types suppressed in quiet mode (internal/noise events). */
|
|
623
|
-
const QUIET_SUPPRESSED = new Set([
|
|
624
|
-
"agent:output",
|
|
625
|
-
"agent:stall",
|
|
626
|
-
"pipeline:simplify",
|
|
627
|
-
"pipeline:analysis-only",
|
|
628
|
-
"policies:resolved",
|
|
629
|
-
"intent:classified",
|
|
630
|
-
"skills:auto-install",
|
|
631
|
-
"context:loaded",
|
|
632
|
-
"rtk:detected",
|
|
633
|
-
"board:started",
|
|
634
|
-
"plan:loaded",
|
|
635
|
-
"tdd:auto-detect",
|
|
636
|
-
"dry-run:summary",
|
|
637
|
-
"pg:decompose",
|
|
638
|
-
"tester:auto-continue",
|
|
639
|
-
"security:auto-continue",
|
|
640
|
-
"hu:sub-pipeline:start",
|
|
641
|
-
"hu:sub-pipeline:end",
|
|
642
|
-
"resilient:auto_resume",
|
|
643
|
-
"guard:blocked",
|
|
644
|
-
]);
|
|
645
|
-
|
|
646
|
-
/* ── Main entry point ───────────────────────────────────────── */
|
|
647
|
-
|
|
648
|
-
/**
|
|
649
|
-
* @param {object} event
|
|
650
|
-
* @param {object} [opts]
|
|
651
|
-
* @param {boolean} [opts.quiet] - When true, suppress raw agent output lines.
|
|
652
|
-
*/
|
|
653
|
-
export function printEvent(event, opts = {}) {
|
|
654
|
-
if (opts.quiet && QUIET_SUPPRESSED.has(event.type)) {
|
|
655
|
-
return;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
const icon = ICONS[event.type] || "\u2022";
|
|
659
|
-
const elapsed = event.elapsed === undefined ? "" : `${ANSI.dim}[${formatElapsed(event.elapsed)}]${ANSI.reset}`;
|
|
660
|
-
const status = event.status ? STATUS_ICON[event.status] || "" : "";
|
|
661
|
-
|
|
662
|
-
const handler = EVENT_HANDLERS[event.type];
|
|
663
|
-
if (handler) {
|
|
664
|
-
handler(event, icon, elapsed, status);
|
|
665
|
-
} else if (event.message) {
|
|
666
|
-
console.log(` \u251c\u2500 ${icon} ${event.message} ${elapsed}`);
|
|
667
|
-
}
|
|
668
|
-
// Events without handler AND without message are silently dropped
|
|
669
|
-
}
|