dual-brain 0.2.30 → 0.3.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/.dual-brain/docs/claude-code-extension-points.md +32 -0
- package/.dual-brain/docs/data-tools-capabilities.md +181 -0
- package/.dual-brain/docs/ecosystem-tools.md +91 -0
- package/.dual-brain/docs/panel-handoff.md +124 -0
- package/.dual-brain/docs/ruflo-analysis.md +48 -0
- package/bin/dual-brain.mjs +56 -56
- package/dist/mcp-server/index.d.ts +27 -0
- package/dist/mcp-server/index.js +359 -0
- package/dist/mcp-server/index.js.map +1 -0
- package/dist/src/agent-protocol.d.ts +163 -0
- package/dist/src/agent-protocol.js +368 -0
- package/dist/src/agent-protocol.js.map +1 -0
- package/dist/src/agents/registry.d.ts +52 -0
- package/dist/src/agents/registry.js +393 -0
- package/dist/src/agents/registry.js.map +1 -0
- package/dist/src/awareness.d.ts +93 -0
- package/dist/src/awareness.js +406 -0
- package/dist/src/awareness.js.map +1 -0
- package/dist/src/brief.d.ts +48 -0
- package/dist/src/brief.js +179 -0
- package/dist/src/brief.js.map +1 -0
- package/dist/src/calibration.d.ts +32 -0
- package/dist/src/calibration.js +133 -0
- package/dist/src/calibration.js.map +1 -0
- package/dist/src/checkpoint.d.ts +33 -0
- package/dist/src/checkpoint.js +99 -0
- package/dist/src/checkpoint.js.map +1 -0
- package/dist/src/ci-triage.d.ts +33 -0
- package/dist/src/ci-triage.js +193 -0
- package/dist/src/ci-triage.js.map +1 -0
- package/dist/src/cognitive-loop.d.ts +56 -0
- package/dist/src/cognitive-loop.js +495 -0
- package/dist/src/cognitive-loop.js.map +1 -0
- package/dist/src/collaboration.d.ts +147 -0
- package/dist/src/collaboration.js +438 -0
- package/dist/src/collaboration.js.map +1 -0
- package/dist/src/context-intel.d.ts +47 -0
- package/dist/src/context-intel.js +156 -0
- package/dist/src/context-intel.js.map +1 -0
- package/dist/src/context.d.ts +53 -0
- package/dist/src/context.js +332 -0
- package/dist/src/context.js.map +1 -0
- package/dist/src/continuity.d.ts +89 -0
- package/dist/src/continuity.js +230 -0
- package/dist/src/continuity.js.map +1 -0
- package/dist/src/cost-tracker.d.ts +47 -0
- package/dist/src/cost-tracker.js +170 -0
- package/dist/src/cost-tracker.js.map +1 -0
- package/dist/src/debrief.d.ts +53 -0
- package/dist/src/debrief.js +222 -0
- package/dist/src/debrief.js.map +1 -0
- package/dist/src/decide.d.ts +96 -0
- package/dist/src/decide.js +744 -0
- package/dist/src/decide.js.map +1 -0
- package/dist/src/decompose.d.ts +39 -0
- package/dist/src/decompose.js +218 -0
- package/dist/src/decompose.js.map +1 -0
- package/dist/src/detect.d.ts +91 -0
- package/dist/src/detect.js +544 -0
- package/dist/src/detect.js.map +1 -0
- package/dist/src/dispatch.d.ts +154 -0
- package/dist/src/dispatch.js +1306 -0
- package/dist/src/dispatch.js.map +1 -0
- package/dist/src/doctor.d.ts +421 -0
- package/dist/src/doctor.js +1689 -0
- package/dist/src/doctor.js.map +1 -0
- package/dist/src/engine.d.ts +70 -0
- package/dist/src/engine.js +155 -0
- package/dist/src/engine.js.map +1 -0
- package/dist/src/envelope.d.ts +36 -0
- package/dist/src/envelope.js +80 -0
- package/dist/src/envelope.js.map +1 -0
- package/dist/src/failure-memory.d.ts +55 -0
- package/dist/src/failure-memory.js +175 -0
- package/dist/src/failure-memory.js.map +1 -0
- package/dist/src/fx.d.ts +87 -0
- package/dist/src/fx.js +272 -0
- package/dist/src/fx.js.map +1 -0
- package/dist/src/governance.d.ts +93 -0
- package/dist/src/governance.js +261 -0
- package/dist/src/governance.js.map +1 -0
- package/dist/src/handoff.d.ts +11 -0
- package/dist/src/handoff.js +90 -0
- package/dist/src/handoff.js.map +1 -0
- package/dist/src/head-protocol.d.ts +76 -0
- package/dist/src/head-protocol.js +109 -0
- package/dist/src/head-protocol.js.map +1 -0
- package/dist/src/head.d.ts +222 -0
- package/dist/src/head.js +765 -0
- package/dist/src/head.js.map +1 -0
- package/dist/src/health.d.ts +132 -0
- package/dist/src/health.js +435 -0
- package/dist/src/health.js.map +1 -0
- package/dist/src/inbox.d.ts +70 -0
- package/dist/src/inbox.js +218 -0
- package/dist/src/inbox.js.map +1 -0
- package/dist/src/index.d.ts +33 -0
- package/dist/src/index.js +38 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/install-hooks.d.ts +13 -0
- package/dist/src/install-hooks.js +88 -0
- package/dist/src/install-hooks.js.map +1 -0
- package/dist/src/integrity.d.ts +59 -0
- package/dist/src/integrity.js +206 -0
- package/dist/src/integrity.js.map +1 -0
- package/dist/src/intelligence.d.ts +104 -0
- package/dist/src/intelligence.js +391 -0
- package/dist/src/intelligence.js.map +1 -0
- package/dist/src/ledger.d.ts +54 -0
- package/dist/src/ledger.js +179 -0
- package/dist/src/ledger.js.map +1 -0
- package/dist/src/living-docs.d.ts +14 -0
- package/dist/src/living-docs.js +197 -0
- package/dist/src/living-docs.js.map +1 -0
- package/dist/src/memory-tiers.d.ts +37 -0
- package/dist/src/memory-tiers.js +160 -0
- package/dist/src/memory-tiers.js.map +1 -0
- package/dist/src/model-profiles.d.ts +65 -0
- package/dist/src/model-profiles.js +568 -0
- package/dist/src/model-profiles.js.map +1 -0
- package/dist/src/models.d.ts +58 -0
- package/dist/src/models.js +327 -0
- package/dist/src/models.js.map +1 -0
- package/dist/src/narrative.d.ts +54 -0
- package/dist/src/narrative.js +163 -0
- package/dist/src/narrative.js.map +1 -0
- package/dist/src/nextstep.d.ts +16 -0
- package/dist/src/nextstep.js +103 -0
- package/dist/src/nextstep.js.map +1 -0
- package/dist/src/observer.d.ts +18 -0
- package/dist/src/observer.js +251 -0
- package/dist/src/observer.js.map +1 -0
- package/dist/src/outcome.d.ts +110 -0
- package/dist/src/outcome.js +377 -0
- package/dist/src/outcome.js.map +1 -0
- package/dist/src/pipeline.d.ts +167 -0
- package/dist/src/pipeline.js +1503 -0
- package/dist/src/pipeline.js.map +1 -0
- package/dist/src/playbook.d.ts +59 -0
- package/dist/src/playbook.js +238 -0
- package/dist/src/playbook.js.map +1 -0
- package/dist/src/pr-agent.d.ts +97 -0
- package/dist/src/pr-agent.js +195 -0
- package/dist/src/pr-agent.js.map +1 -0
- package/dist/src/predictive.d.ts +57 -0
- package/dist/src/predictive.js +230 -0
- package/dist/src/predictive.js.map +1 -0
- package/dist/src/profile.d.ts +294 -0
- package/dist/src/profile.js +1347 -0
- package/dist/src/profile.js.map +1 -0
- package/dist/src/prompt-audit.d.ts +22 -0
- package/dist/src/prompt-audit.js +194 -0
- package/dist/src/prompt-audit.js.map +1 -0
- package/dist/src/prompt-intel.d.ts +12 -0
- package/dist/src/prompt-intel.js +321 -0
- package/dist/src/prompt-intel.js.map +1 -0
- package/dist/src/provider-context.d.ts +121 -0
- package/dist/src/provider-context.js +222 -0
- package/dist/src/provider-context.js.map +1 -0
- package/dist/src/provider-manager.d.ts +92 -0
- package/dist/src/provider-manager.js +428 -0
- package/dist/src/provider-manager.js.map +1 -0
- package/dist/src/receipt.d.ts +87 -0
- package/dist/src/receipt.js +326 -0
- package/dist/src/receipt.js.map +1 -0
- package/dist/src/recommendations.d.ts +13 -0
- package/dist/src/recommendations.js +291 -0
- package/dist/src/recommendations.js.map +1 -0
- package/dist/src/redact.d.ts +15 -0
- package/dist/src/redact.js +129 -0
- package/dist/src/redact.js.map +1 -0
- package/dist/src/replit.d.ts +397 -0
- package/dist/src/replit.js +1160 -0
- package/dist/src/replit.js.map +1 -0
- package/dist/src/repo.d.ts +149 -0
- package/dist/src/repo.js +416 -0
- package/dist/src/repo.js.map +1 -0
- package/dist/src/revert.d.ts +30 -0
- package/dist/src/revert.js +166 -0
- package/dist/src/revert.js.map +1 -0
- package/dist/src/room.d.ts +102 -0
- package/dist/src/room.js +212 -0
- package/dist/src/room.js.map +1 -0
- package/dist/src/routing-advisor.d.ts +57 -0
- package/dist/src/routing-advisor.js +221 -0
- package/dist/src/routing-advisor.js.map +1 -0
- package/dist/src/self-correct.d.ts +40 -0
- package/dist/src/self-correct.js +137 -0
- package/dist/src/self-correct.js.map +1 -0
- package/dist/src/session-lock.d.ts +35 -0
- package/dist/src/session-lock.js +134 -0
- package/dist/src/session-lock.js.map +1 -0
- package/dist/src/session.d.ts +267 -0
- package/dist/src/session.js +1660 -0
- package/dist/src/session.js.map +1 -0
- package/dist/src/settings-tui.d.ts +5 -0
- package/dist/src/settings-tui.js +422 -0
- package/dist/src/settings-tui.js.map +1 -0
- package/dist/src/setup-flow.d.ts +63 -0
- package/dist/src/setup-flow.js +233 -0
- package/dist/src/setup-flow.js.map +1 -0
- package/dist/src/signal.d.ts +19 -0
- package/dist/src/signal.js +122 -0
- package/dist/src/signal.js.map +1 -0
- package/dist/src/simmer.d.ts +85 -0
- package/dist/src/simmer.js +224 -0
- package/dist/src/simmer.js.map +1 -0
- package/dist/src/state-export.d.ts +129 -0
- package/dist/src/state-export.js +233 -0
- package/dist/src/state-export.js.map +1 -0
- package/dist/src/strategy.d.ts +54 -0
- package/dist/src/strategy.js +95 -0
- package/dist/src/strategy.js.map +1 -0
- package/dist/src/subscription.d.ts +40 -0
- package/dist/src/subscription.js +189 -0
- package/dist/src/subscription.js.map +1 -0
- package/dist/src/templates.d.ts +208 -0
- package/dist/src/templates.js +238 -0
- package/dist/src/templates.js.map +1 -0
- package/dist/src/test.d.ts +9 -0
- package/dist/src/test.js +1173 -0
- package/dist/src/test.js.map +1 -0
- package/dist/src/think-engine.d.ts +67 -0
- package/dist/src/think-engine.js +412 -0
- package/dist/src/think-engine.js.map +1 -0
- package/dist/src/tui.d.ts +71 -0
- package/dist/src/tui.js +242 -0
- package/dist/src/tui.js.map +1 -0
- package/dist/src/types.d.ts +177 -0
- package/dist/src/types.js +6 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/update-check.d.ts +7 -0
- package/dist/src/update-check.js +36 -0
- package/dist/src/update-check.js.map +1 -0
- package/dist/src/wave-planner.d.ts +30 -0
- package/dist/src/wave-planner.js +281 -0
- package/dist/src/wave-planner.js.map +1 -0
- package/hooks/head-guard.sh +41 -0
- package/hooks/task-classifier.mjs +328 -0
- package/hooks/vibe-router.mjs +387 -0
- package/package.json +29 -153
- package/src/agents/registry.mjs +0 -405
- package/src/awareness.mjs +0 -425
- package/src/brief.mjs +0 -266
- package/src/calibration.mjs +0 -148
- package/src/checkpoint.mjs +0 -109
- package/src/ci-triage.mjs +0 -191
- package/src/cognitive-loop.mjs +0 -562
- package/src/collaboration.mjs +0 -545
- package/src/context-intel.mjs +0 -158
- package/src/context.mjs +0 -389
- package/src/continuity.mjs +0 -298
- package/src/cost-tracker.mjs +0 -184
- package/src/debrief.mjs +0 -228
- package/src/decide.mjs +0 -1099
- package/src/decompose.mjs +0 -331
- package/src/detect.mjs +0 -702
- package/src/dispatch.mjs +0 -1447
- package/src/doctor.mjs +0 -1607
- package/src/envelope.mjs +0 -139
- package/src/failure-memory.mjs +0 -178
- package/src/fx.mjs +0 -276
- package/src/governance.mjs +0 -279
- package/src/handoff.mjs +0 -87
- package/src/head-protocol.mjs +0 -128
- package/src/head.mjs +0 -952
- package/src/health.mjs +0 -528
- package/src/inbox.mjs +0 -195
- package/src/index.mjs +0 -44
- package/src/install-hooks.mjs +0 -100
- package/src/integrity.mjs +0 -245
- package/src/intelligence.mjs +0 -447
- package/src/ledger.mjs +0 -196
- package/src/living-docs.mjs +0 -210
- package/src/memory-tiers.mjs +0 -193
- package/src/models.mjs +0 -363
- package/src/narrative.mjs +0 -169
- package/src/nextstep.mjs +0 -100
- package/src/observer.mjs +0 -241
- package/src/outcome.mjs +0 -400
- package/src/pipeline.mjs +0 -1711
- package/src/playbook.mjs +0 -257
- package/src/pr-agent.mjs +0 -214
- package/src/predictive.mjs +0 -250
- package/src/profile.mjs +0 -1411
- package/src/prompt-audit.mjs +0 -231
- package/src/prompt-intel.mjs +0 -325
- package/src/provider-context.mjs +0 -257
- package/src/receipt.mjs +0 -344
- package/src/recommendations.mjs +0 -296
- package/src/redact.mjs +0 -192
- package/src/replit.mjs +0 -1210
- package/src/repo.mjs +0 -445
- package/src/revert.mjs +0 -149
- package/src/routing-advisor.mjs +0 -204
- package/src/self-correct.mjs +0 -147
- package/src/session-lock.mjs +0 -160
- package/src/session.mjs +0 -1655
- package/src/settings-tui.mjs +0 -373
- package/src/setup-flow.mjs +0 -223
- package/src/signal.mjs +0 -115
- package/src/simmer.mjs +0 -241
- package/src/strategy.mjs +0 -235
- package/src/subscription.mjs +0 -212
- package/src/templates.mjs +0 -260
- package/src/think-engine.mjs +0 -428
- package/src/tui.mjs +0 -276
- package/src/update-check.mjs +0 -35
- package/src/wave-planner.mjs +0 -294
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync, appendFileSync, readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
const DIM = '\x1b[2m';
|
|
5
|
+
const GREEN = '\x1b[32m';
|
|
6
|
+
const YELLOW = '\x1b[33m';
|
|
7
|
+
const RED = '\x1b[31m';
|
|
8
|
+
const RESET = '\x1b[0m';
|
|
9
|
+
const SEP = `${DIM}──────────────────────────────────${RESET}`;
|
|
10
|
+
const AUTH_PAT = /\b(auth|credential|secret|token|password|encrypt|permission|oauth|jwt|api.?key)\b/i;
|
|
11
|
+
function classifyRisk(plan, result) {
|
|
12
|
+
if (plan.risk)
|
|
13
|
+
return plan.risk;
|
|
14
|
+
const files = result.filesChanged ?? [];
|
|
15
|
+
if (files.some(f => AUTH_PAT.test(f)))
|
|
16
|
+
return 'critical';
|
|
17
|
+
if (plan.tier === 'think')
|
|
18
|
+
return 'high';
|
|
19
|
+
if (plan.tier === 'execute')
|
|
20
|
+
return 'medium';
|
|
21
|
+
return 'low';
|
|
22
|
+
}
|
|
23
|
+
function classifyChallenger(plan, result) {
|
|
24
|
+
const policy = plan.challengerPolicy;
|
|
25
|
+
if (!plan.useChallenger && (!policy || policy === 'none'))
|
|
26
|
+
return 'not used';
|
|
27
|
+
if (!result.success)
|
|
28
|
+
return 'blocked';
|
|
29
|
+
if (result.output && /concern|issue|warn|problem/i.test(String(result.output)))
|
|
30
|
+
return 'concerns raised';
|
|
31
|
+
return 'pass';
|
|
32
|
+
}
|
|
33
|
+
function nextStep(result, plan, verification) {
|
|
34
|
+
const files = result.filesChanged ?? [];
|
|
35
|
+
const changed = files.length > 0;
|
|
36
|
+
const authFiles = files.some(f => AUTH_PAT.test(f));
|
|
37
|
+
if (!result.success) {
|
|
38
|
+
const retry = result.error && /test/i.test(String(result.error));
|
|
39
|
+
return retry ? 'fix failing tests' : 'retry with deeper analysis';
|
|
40
|
+
}
|
|
41
|
+
if (authFiles)
|
|
42
|
+
return 'security review recommended';
|
|
43
|
+
if (changed) {
|
|
44
|
+
if (!verification.testsRun)
|
|
45
|
+
return 'run tests to verify';
|
|
46
|
+
if (verification.testsPassed === false)
|
|
47
|
+
return 'fix failing tests';
|
|
48
|
+
if (verification.testsPassed === true)
|
|
49
|
+
return 'commit this patch';
|
|
50
|
+
return 'review the diff';
|
|
51
|
+
}
|
|
52
|
+
return 'review the output';
|
|
53
|
+
}
|
|
54
|
+
export function buildReceipt(result, plan, verification) {
|
|
55
|
+
const files = result.filesChanged ?? [];
|
|
56
|
+
const changed = files.length > 0 ? files.join(', ') : 'no files changed';
|
|
57
|
+
let verified;
|
|
58
|
+
if (verification.testsPassed === true)
|
|
59
|
+
verified = 'tests passed';
|
|
60
|
+
else if (verification.filesVerified)
|
|
61
|
+
verified = 'files confirmed changed';
|
|
62
|
+
else
|
|
63
|
+
verified = 'not verified';
|
|
64
|
+
return {
|
|
65
|
+
changed,
|
|
66
|
+
verified,
|
|
67
|
+
risk: classifyRisk(plan, result),
|
|
68
|
+
challenger: classifyChallenger(plan, result),
|
|
69
|
+
next: nextStep(result, plan, verification),
|
|
70
|
+
success: result.success ?? false,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function colorRisk(risk) {
|
|
74
|
+
if (risk === 'low')
|
|
75
|
+
return `${GREEN}${risk}${RESET}`;
|
|
76
|
+
if (risk === 'medium')
|
|
77
|
+
return `${YELLOW}${risk}${RESET}`;
|
|
78
|
+
return `${RED}${risk}${RESET}`;
|
|
79
|
+
}
|
|
80
|
+
function colorChallenger(ch) {
|
|
81
|
+
if (ch === 'pass')
|
|
82
|
+
return `${GREEN}${ch}${RESET}`;
|
|
83
|
+
if (ch === 'concerns raised')
|
|
84
|
+
return `${YELLOW}${ch}${RESET}`;
|
|
85
|
+
if (ch === 'blocked')
|
|
86
|
+
return `${RED}${ch}${RESET}`;
|
|
87
|
+
return `${DIM}${ch}${RESET}`;
|
|
88
|
+
}
|
|
89
|
+
export function formatReceipt(receipt) {
|
|
90
|
+
return [
|
|
91
|
+
SEP,
|
|
92
|
+
` Changed: ${receipt.changed}`,
|
|
93
|
+
` Verified: ${receipt.verified}`,
|
|
94
|
+
` Risk: ${colorRisk(receipt.risk)}`,
|
|
95
|
+
` Challenger: ${colorChallenger(receipt.challenger)}`,
|
|
96
|
+
` Next: ${receipt.next}`,
|
|
97
|
+
SEP,
|
|
98
|
+
].join('\n');
|
|
99
|
+
}
|
|
100
|
+
export function formatFailureReceipt(receipt, failureContext) {
|
|
101
|
+
const errorLine = failureContext ? ` Error: ${failureContext}` : null;
|
|
102
|
+
const lines = [
|
|
103
|
+
SEP,
|
|
104
|
+
` Changed: ${receipt.changed}`,
|
|
105
|
+
` Verified: ${receipt.verified}`,
|
|
106
|
+
` Risk: ${colorRisk(receipt.risk)}`,
|
|
107
|
+
` Challenger: ${colorChallenger(receipt.challenger)}`,
|
|
108
|
+
];
|
|
109
|
+
if (errorLine)
|
|
110
|
+
lines.push(errorLine);
|
|
111
|
+
lines.push(` Next: ${receipt.next}`, SEP);
|
|
112
|
+
return lines.join('\n');
|
|
113
|
+
}
|
|
114
|
+
// --- Persistent session receipt ---
|
|
115
|
+
const RECEIPTS_DIR = '.dualbrain/receipts';
|
|
116
|
+
function receiptsDir(cwd) {
|
|
117
|
+
return join(cwd, RECEIPTS_DIR);
|
|
118
|
+
}
|
|
119
|
+
function gitChangedFiles(cwd) {
|
|
120
|
+
try {
|
|
121
|
+
const out = execSync('git diff --name-only HEAD', { cwd, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
122
|
+
.toString().trim();
|
|
123
|
+
if (!out)
|
|
124
|
+
return [];
|
|
125
|
+
return out.split('\n').filter(Boolean);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function readDecisionsRecent(cwd, limit = 5) {
|
|
132
|
+
try {
|
|
133
|
+
const raw = readFileSync(join(cwd, '.dualbrain', 'decisions.jsonl'), 'utf8');
|
|
134
|
+
const lines = raw.split('\n').filter(l => l.trim());
|
|
135
|
+
return lines.slice(-limit).map(l => { try {
|
|
136
|
+
return JSON.parse(l);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return null;
|
|
140
|
+
} }).filter(Boolean);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function ageLabel(ms) {
|
|
147
|
+
const mins = Math.round(ms / 60000);
|
|
148
|
+
if (mins < 60)
|
|
149
|
+
return `${mins}m ago`;
|
|
150
|
+
const hours = Math.round(mins / 60);
|
|
151
|
+
if (hours < 24)
|
|
152
|
+
return `${hours}h ago`;
|
|
153
|
+
return `${Math.round(hours / 24)}d ago`;
|
|
154
|
+
}
|
|
155
|
+
export function generateReceipt(run = {}, cwd = process.cwd()) {
|
|
156
|
+
const now = new Date();
|
|
157
|
+
const ts = now.toISOString();
|
|
158
|
+
// Derive files changed — prefer run.result, fall back to git diff
|
|
159
|
+
const filesChanged = (run.result?.filesChanged && run.result.filesChanged.length > 0)
|
|
160
|
+
? run.result.filesChanged
|
|
161
|
+
: gitChangedFiles(cwd);
|
|
162
|
+
// Recent decisions from living docs
|
|
163
|
+
const decisionEntries = readDecisionsRecent(cwd, 5);
|
|
164
|
+
const decisions = decisionEntries.map(d => d.question || d.decision || '').filter(Boolean).slice(0, 3);
|
|
165
|
+
// Test results
|
|
166
|
+
const testsRun = run.verification?.ok !== undefined
|
|
167
|
+
? (run.verification.ok ? 'passed' : 'failed')
|
|
168
|
+
: null;
|
|
169
|
+
// Unresolved risks from plan
|
|
170
|
+
const risksUnresolved = [];
|
|
171
|
+
if (run.plan?.approvalRequired && !run.outcome?.approved) {
|
|
172
|
+
risksUnresolved.push('approval required but not obtained');
|
|
173
|
+
}
|
|
174
|
+
if (run.verification && !run.verification.ok) {
|
|
175
|
+
risksUnresolved.push('verification failed');
|
|
176
|
+
}
|
|
177
|
+
const verNotes = run.verification?.notes ?? [];
|
|
178
|
+
for (const note of verNotes) {
|
|
179
|
+
if (/warn|risk|unverif|no file changes/i.test(note))
|
|
180
|
+
risksUnresolved.push(note.slice(0, 80));
|
|
181
|
+
}
|
|
182
|
+
// Blockers — gates that failed
|
|
183
|
+
const blockers = [];
|
|
184
|
+
for (const [name, g] of Object.entries(run.gates ?? {})) {
|
|
185
|
+
if (g && !g.passed)
|
|
186
|
+
blockers.push(`${name}: ${g.reason?.slice(0, 80)}`);
|
|
187
|
+
}
|
|
188
|
+
if (run.result?.error)
|
|
189
|
+
blockers.push(run.result.error.slice(0, 80));
|
|
190
|
+
// Derive status
|
|
191
|
+
const success = run.result && !run.result.error && (run.verification?.ok !== false);
|
|
192
|
+
const status = !run.result ? 'incomplete'
|
|
193
|
+
: blockers.length > 0 ? 'failed'
|
|
194
|
+
: success ? 'success'
|
|
195
|
+
: 'partial';
|
|
196
|
+
// Next action (reuse existing logic)
|
|
197
|
+
let nextAction = 'review the output';
|
|
198
|
+
if (status === 'success' && filesChanged.length > 0) {
|
|
199
|
+
nextAction = run.verification?.testsRun ? 'commit changes' : 'run tests, then commit';
|
|
200
|
+
}
|
|
201
|
+
else if (status === 'failed') {
|
|
202
|
+
nextAction = 'investigate failure, retry with adjusted approach';
|
|
203
|
+
}
|
|
204
|
+
else if (status === 'partial') {
|
|
205
|
+
nextAction = 'check partial output, verify manually';
|
|
206
|
+
}
|
|
207
|
+
const duration = (run.completedAt && run.startedAt)
|
|
208
|
+
? Math.round((run.completedAt - run.startedAt) / 1000)
|
|
209
|
+
: null;
|
|
210
|
+
const receipt = {
|
|
211
|
+
timestamp: ts,
|
|
212
|
+
goal: (run.prompt ?? '').slice(0, 200),
|
|
213
|
+
filesChanged,
|
|
214
|
+
decisions,
|
|
215
|
+
testsRun,
|
|
216
|
+
risksUnresolved,
|
|
217
|
+
blockers,
|
|
218
|
+
nextAction,
|
|
219
|
+
provider: run.plan?.primaryProvider ?? run.result?.provider ?? null,
|
|
220
|
+
model: run.plan?.primaryModel ?? run.result?.model ?? null,
|
|
221
|
+
duration,
|
|
222
|
+
status,
|
|
223
|
+
};
|
|
224
|
+
// Store receipt
|
|
225
|
+
try {
|
|
226
|
+
const dir = receiptsDir(cwd);
|
|
227
|
+
mkdirSync(dir, { recursive: true });
|
|
228
|
+
const filename = ts.replace(/[:.]/g, '-').slice(0, 19) + '.json';
|
|
229
|
+
writeFileSync(join(dir, filename), JSON.stringify(receipt, null, 2));
|
|
230
|
+
// One-line summary for fast scanning
|
|
231
|
+
const summary = {
|
|
232
|
+
ts,
|
|
233
|
+
goal: receipt.goal.slice(0, 80),
|
|
234
|
+
status,
|
|
235
|
+
files: filesChanged.length,
|
|
236
|
+
next: nextAction.slice(0, 60),
|
|
237
|
+
};
|
|
238
|
+
appendFileSync(join(dir, 'index.jsonl'), JSON.stringify(summary) + '\n');
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
// Storage failure is non-blocking
|
|
242
|
+
}
|
|
243
|
+
return receipt;
|
|
244
|
+
}
|
|
245
|
+
export function buildResumeBrief(cwd = process.cwd()) {
|
|
246
|
+
try {
|
|
247
|
+
const dir = receiptsDir(cwd);
|
|
248
|
+
if (!existsSync(dir))
|
|
249
|
+
return null;
|
|
250
|
+
// Find the most recent receipt JSON file
|
|
251
|
+
const files = readdirSync(dir)
|
|
252
|
+
.filter(f => f.endsWith('.json') && f !== 'index.json')
|
|
253
|
+
.sort()
|
|
254
|
+
.reverse();
|
|
255
|
+
if (files.length === 0)
|
|
256
|
+
return null;
|
|
257
|
+
const raw = readFileSync(join(dir, files[0]), 'utf8');
|
|
258
|
+
const r = JSON.parse(raw);
|
|
259
|
+
const age = ageLabel(Date.now() - Date.parse(r.timestamp));
|
|
260
|
+
const filesSummary = r.filesChanged?.length > 0
|
|
261
|
+
? r.filesChanged.slice(0, 3).map(f => f.split('/').pop()).join(', ')
|
|
262
|
+
+ (r.filesChanged.length > 3 ? ` +${r.filesChanged.length - 3}` : '')
|
|
263
|
+
: 'no files changed';
|
|
264
|
+
const riskLine = r.risksUnresolved?.length > 0
|
|
265
|
+
? `Risk: ${r.risksUnresolved[0].slice(0, 60)}`
|
|
266
|
+
: null;
|
|
267
|
+
const lines = [
|
|
268
|
+
'RESUME CONTEXT:',
|
|
269
|
+
`Last session: ${age}`,
|
|
270
|
+
`Goal: ${(r.goal || 'unknown').slice(0, 80)}`,
|
|
271
|
+
`Done: ${filesSummary}`,
|
|
272
|
+
`Status: ${r.status}${r.testsRun ? ', tests ' + r.testsRun : ''}`,
|
|
273
|
+
];
|
|
274
|
+
if (riskLine)
|
|
275
|
+
lines.push(riskLine);
|
|
276
|
+
lines.push(`Next: ${(r.nextAction || '').slice(0, 80)}`);
|
|
277
|
+
const brief = lines.join('\n');
|
|
278
|
+
return brief.length > 500 ? brief.slice(0, 497) + '...' : brief;
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
export function getLatestReceipt(cwd = process.cwd()) {
|
|
285
|
+
try {
|
|
286
|
+
const dir = receiptsDir(cwd);
|
|
287
|
+
if (!existsSync(dir))
|
|
288
|
+
return null;
|
|
289
|
+
const files = readdirSync(dir)
|
|
290
|
+
.filter(f => f.endsWith('.json') && f !== 'index.json')
|
|
291
|
+
.sort()
|
|
292
|
+
.reverse();
|
|
293
|
+
if (files.length === 0)
|
|
294
|
+
return null;
|
|
295
|
+
const raw = readFileSync(join(dir, files[0]), 'utf8');
|
|
296
|
+
return JSON.parse(raw);
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
export function buildReceiptFromOutcome(outcome = {}) {
|
|
303
|
+
const result = {
|
|
304
|
+
success: outcome.success ?? outcome.result?.success ?? false,
|
|
305
|
+
filesChanged: outcome.filesChanged ?? outcome.result?.filesChanged ?? [],
|
|
306
|
+
error: outcome.error ?? outcome.result?.error ?? null,
|
|
307
|
+
duration: outcome.duration ?? outcome.result?.duration ?? 0,
|
|
308
|
+
output: outcome.output ?? null,
|
|
309
|
+
};
|
|
310
|
+
const plan = {
|
|
311
|
+
primaryModel: outcome.primaryModel ?? '',
|
|
312
|
+
reasoningDepth: outcome.reasoningDepth ?? '',
|
|
313
|
+
challengerPolicy: outcome.challengerPolicy ?? 'none',
|
|
314
|
+
useChallenger: !!(outcome.challengerPolicy && outcome.challengerPolicy !== 'none'),
|
|
315
|
+
tier: outcome.tier ?? '',
|
|
316
|
+
workStyle: outcome.workStyle ?? '',
|
|
317
|
+
risk: outcome.risk ?? '',
|
|
318
|
+
};
|
|
319
|
+
const verification = {
|
|
320
|
+
filesVerified: outcome.verification?.filesVerified ?? false,
|
|
321
|
+
testsRun: outcome.verification?.testsRun ?? false,
|
|
322
|
+
testsPassed: outcome.verification?.testsPassed ?? null,
|
|
323
|
+
};
|
|
324
|
+
return buildReceipt(result, plan, verification);
|
|
325
|
+
}
|
|
326
|
+
//# sourceMappingURL=receipt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.js","sourceRoot":"","sources":["../../src/receipt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface Recommendation {
|
|
2
|
+
id: string;
|
|
3
|
+
priority: 'high' | 'medium' | 'low';
|
|
4
|
+
category: string;
|
|
5
|
+
title: string;
|
|
6
|
+
description: string;
|
|
7
|
+
action: string;
|
|
8
|
+
impact: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function generateRecommendations(cwd: string | undefined): Recommendation[];
|
|
11
|
+
export declare function formatRecommendations(recs: Recommendation[]): string;
|
|
12
|
+
export declare function getTopRecommendation(cwd: string | undefined): Recommendation | null;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
// recommendations.ts — Proactive settings recommendations from HEAD
|
|
2
|
+
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
function readJSON(path) {
|
|
5
|
+
try {
|
|
6
|
+
return existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) : null;
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function dbPath(cwd, ...parts) {
|
|
13
|
+
return join(cwd || process.cwd(), '.dualbrain', ...parts);
|
|
14
|
+
}
|
|
15
|
+
function loadRoutingState(cwd) {
|
|
16
|
+
return (readJSON(dbPath(cwd, 'routing-state.json')) || {});
|
|
17
|
+
}
|
|
18
|
+
function loadThinkMetrics(cwd) {
|
|
19
|
+
return (readJSON(dbPath(cwd, 'think-metrics.json')) || {});
|
|
20
|
+
}
|
|
21
|
+
function loadGovernance(cwd) {
|
|
22
|
+
return (readJSON(dbPath(cwd, 'governance-state.json')) || {});
|
|
23
|
+
}
|
|
24
|
+
function loadSubscription(cwd) {
|
|
25
|
+
return (readJSON(dbPath(cwd, 'subscription.json')) || {});
|
|
26
|
+
}
|
|
27
|
+
function loadOutcomes(cwd) {
|
|
28
|
+
try {
|
|
29
|
+
const dir = dbPath(cwd, 'outcomes');
|
|
30
|
+
if (!existsSync(dir))
|
|
31
|
+
return [];
|
|
32
|
+
return readdirSync(dir)
|
|
33
|
+
.filter(f => f.endsWith('.json'))
|
|
34
|
+
.map(f => readJSON(join(dir, f)))
|
|
35
|
+
.filter((v) => v !== null);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// --- Recommendation rules ---
|
|
42
|
+
function thinkROI(metrics) {
|
|
43
|
+
const { hitRate, totalHits, totalMisses, avgTokensSaved } = metrics;
|
|
44
|
+
if (hitRate == null)
|
|
45
|
+
return null;
|
|
46
|
+
const observations = (totalHits || 0) + (totalMisses || 0);
|
|
47
|
+
if (observations < 5)
|
|
48
|
+
return null;
|
|
49
|
+
if (hitRate < 0.4) {
|
|
50
|
+
return {
|
|
51
|
+
id: 'think-roi-low',
|
|
52
|
+
priority: 'medium',
|
|
53
|
+
category: 'efficiency',
|
|
54
|
+
title: 'Think agent underperforming',
|
|
55
|
+
description: `${Math.round(hitRate * 100)}% hit rate — think preflight isn't saving tokens.`,
|
|
56
|
+
action: 'Consider disabling think triggers or narrowing trigger conditions.',
|
|
57
|
+
impact: 'Reduce latency and token overhead on low-complexity tasks.',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (hitRate > 0.7) {
|
|
61
|
+
const savings = avgTokensSaved ? `~${Math.round(avgTokensSaved / 1000)}K tokens` : 'tokens';
|
|
62
|
+
return {
|
|
63
|
+
id: 'think-roi-high',
|
|
64
|
+
priority: 'medium',
|
|
65
|
+
category: 'efficiency',
|
|
66
|
+
title: 'Think agent performing well',
|
|
67
|
+
description: `${Math.round(hitRate * 100)}% hit rate, saving ${savings} per refined task.`,
|
|
68
|
+
action: 'No action needed, keep enabled.',
|
|
69
|
+
impact: 'Sustained token efficiency on complex dispatches.',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
function modelMismatch(routingState) {
|
|
75
|
+
const recs = [];
|
|
76
|
+
for (const [taskType, models] of Object.entries(routingState)) {
|
|
77
|
+
if (taskType.startsWith('_'))
|
|
78
|
+
continue; // skip metadata keys
|
|
79
|
+
for (const [model, stats] of Object.entries(models)) {
|
|
80
|
+
const { ema, observations } = stats || {};
|
|
81
|
+
if (observations >= 10 && ema < 0.4) {
|
|
82
|
+
recs.push({
|
|
83
|
+
id: `model-mismatch-low-${taskType}-${model}`,
|
|
84
|
+
priority: 'high',
|
|
85
|
+
category: 'routing',
|
|
86
|
+
title: 'Model mismatch detected',
|
|
87
|
+
description: `${model} scores ${ema.toFixed(2)} on ${taskType} tasks.`,
|
|
88
|
+
action: `Route ${taskType} tasks away from ${model}.`,
|
|
89
|
+
impact: 'Better task outcomes by avoiding poor model-task fit.',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
else if (observations >= 10 && ema > 0.8 && (model === 'haiku' || model.includes('haiku'))) {
|
|
93
|
+
recs.push({
|
|
94
|
+
id: `model-mismatch-promote-${taskType}-${model}`,
|
|
95
|
+
priority: 'high',
|
|
96
|
+
category: 'routing',
|
|
97
|
+
title: 'Cheap model excelling',
|
|
98
|
+
description: `${model} scores ${ema.toFixed(2)} on ${taskType} tasks.`,
|
|
99
|
+
action: `Promote ${model} as default for ${taskType} tier — quality without the cost.`,
|
|
100
|
+
impact: 'Same output quality at lower token cost.',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return recs;
|
|
106
|
+
}
|
|
107
|
+
function budgetTrajectory(governance) {
|
|
108
|
+
const { budgetUsedPct, sessionProgressPct, workStyle } = governance;
|
|
109
|
+
if (budgetUsedPct == null)
|
|
110
|
+
return null;
|
|
111
|
+
if (budgetUsedPct > 60 && sessionProgressPct != null && sessionProgressPct < 50) {
|
|
112
|
+
return {
|
|
113
|
+
id: 'budget-critical',
|
|
114
|
+
priority: 'high',
|
|
115
|
+
category: 'budget',
|
|
116
|
+
title: 'Budget burning fast',
|
|
117
|
+
description: `${Math.round(budgetUsedPct)}% budget used, ~${Math.round(sessionProgressPct)}% through estimated work.`,
|
|
118
|
+
action: 'Switch to cost-saver mode: `dual-brain config set workStyle cost-saver`.',
|
|
119
|
+
impact: 'Avoid hitting budget ceiling before work completes.',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (budgetUsedPct < 20 && workStyle === 'cost-saver') {
|
|
123
|
+
return {
|
|
124
|
+
id: 'budget-underutilized',
|
|
125
|
+
priority: 'low',
|
|
126
|
+
category: 'budget',
|
|
127
|
+
title: 'Budget well under control',
|
|
128
|
+
description: `Only ${Math.round(budgetUsedPct)}% budget used in cost-saver mode.`,
|
|
129
|
+
action: 'You could afford quality-first mode for this session.',
|
|
130
|
+
impact: 'Better output quality while staying within budget.',
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
function failurePattern(outcomes) {
|
|
136
|
+
if (!outcomes.length)
|
|
137
|
+
return null;
|
|
138
|
+
const recent = outcomes.slice(-20);
|
|
139
|
+
const failures = recent.filter(o => o.success === false || (o.reward != null && o.reward < 0.3));
|
|
140
|
+
const failRate = failures.length / recent.length;
|
|
141
|
+
if (failRate > 0.3) {
|
|
142
|
+
const modelCounts = {};
|
|
143
|
+
failures.forEach(o => { if (o.model)
|
|
144
|
+
modelCounts[o.model] = (modelCounts[o.model] || 0) + 1; });
|
|
145
|
+
const worstModel = Object.entries(modelCounts).sort((a, b) => b[1] - a[1])[0];
|
|
146
|
+
const modelNote = worstModel && worstModel[1] >= 3
|
|
147
|
+
? ` Failures cluster on ${worstModel[0]}.`
|
|
148
|
+
: '';
|
|
149
|
+
return {
|
|
150
|
+
id: 'failure-pattern',
|
|
151
|
+
priority: 'high',
|
|
152
|
+
category: 'quality',
|
|
153
|
+
title: 'High failure rate detected',
|
|
154
|
+
description: `${Math.round(failRate * 100)}% of recent tasks failed.${modelNote}`,
|
|
155
|
+
action: worstModel && worstModel[1] >= 3
|
|
156
|
+
? `Route away from ${worstModel[0]} — or check task ambiguity.`
|
|
157
|
+
: 'Review task clarity and model-task fit.',
|
|
158
|
+
impact: 'Fewer retries, less wasted compute.',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
function subscriptionUtilization(subscription, routingState) {
|
|
164
|
+
const { tier, maxMultiplier } = subscription;
|
|
165
|
+
if (!tier)
|
|
166
|
+
return null;
|
|
167
|
+
const routingCells = Object.entries(routingState)
|
|
168
|
+
.filter(([k]) => !k.startsWith('_')) // skip metadata keys
|
|
169
|
+
.map(([, v]) => v);
|
|
170
|
+
const opusUses = routingCells
|
|
171
|
+
.flatMap(m => Object.entries(m))
|
|
172
|
+
.filter(([model]) => model === 'opus' || model.includes('opus'))
|
|
173
|
+
.reduce((s, [, stats]) => s + (stats.observations || 0), 0);
|
|
174
|
+
const totalUses = routingCells
|
|
175
|
+
.flatMap(m => Object.values(m))
|
|
176
|
+
.reduce((s, stats) => s + (stats.observations || 0), 0);
|
|
177
|
+
if (!totalUses)
|
|
178
|
+
return null;
|
|
179
|
+
const opusPct = opusUses / totalUses;
|
|
180
|
+
if ((tier === 'max' || (maxMultiplier && maxMultiplier >= 20)) && opusPct < 0.15) {
|
|
181
|
+
return {
|
|
182
|
+
id: 'subscription-underutilized',
|
|
183
|
+
priority: 'medium',
|
|
184
|
+
category: 'profile',
|
|
185
|
+
title: 'Subscription underutilized',
|
|
186
|
+
description: `Max ${maxMultiplier || ''}x plan but opus used only ${Math.round(opusPct * 100)}% of dispatches.`,
|
|
187
|
+
action: 'Consider quality-first mode for better output.',
|
|
188
|
+
impact: 'Get more value from your subscription tier.',
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if ((tier === 'free' || tier === 'pro') && opusPct > 0.4) {
|
|
192
|
+
return {
|
|
193
|
+
id: 'subscription-aggressive',
|
|
194
|
+
priority: 'medium',
|
|
195
|
+
category: 'profile',
|
|
196
|
+
title: 'Routing aggressively for plan',
|
|
197
|
+
description: `${Math.round(opusPct * 100)}% opus usage on a ${tier} plan.`,
|
|
198
|
+
action: 'Switch to balanced or cost-saver to stay within limits.',
|
|
199
|
+
impact: 'Avoid rate limits and unexpected cost overruns.',
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
function cascadeEffectiveness(metrics, outcomes) {
|
|
205
|
+
const { cascadeHits } = metrics;
|
|
206
|
+
if (!cascadeHits || cascadeHits < 3)
|
|
207
|
+
return null;
|
|
208
|
+
const cascaded = outcomes.filter(o => o.cascaded === true);
|
|
209
|
+
if (cascaded.length < 3)
|
|
210
|
+
return null;
|
|
211
|
+
const avgReward = cascaded.reduce((s, o) => s + (o.reward || 0), 0) / cascaded.length;
|
|
212
|
+
if (avgReward > 0.7) {
|
|
213
|
+
return {
|
|
214
|
+
id: 'cascade-effective',
|
|
215
|
+
priority: 'low',
|
|
216
|
+
category: 'efficiency',
|
|
217
|
+
title: 'Cascade routing working well',
|
|
218
|
+
description: `${cascadeHits} cascade hits, ${avgReward.toFixed(2)} avg reward on cascaded tasks.`,
|
|
219
|
+
action: 'Keep cascade enabled — it\'s delivering quality results.',
|
|
220
|
+
impact: 'Continued token efficiency on eligible tasks.',
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
id: 'cascade-poor',
|
|
225
|
+
priority: 'low',
|
|
226
|
+
category: 'efficiency',
|
|
227
|
+
title: 'Cascade delivering poor results',
|
|
228
|
+
description: `${cascadeHits} cascade hits but only ${avgReward.toFixed(2)} avg reward.`,
|
|
229
|
+
action: 'Consider disabling cascade: `dual-brain config set cascade false`.',
|
|
230
|
+
impact: 'Better outcomes by routing cascade tasks to full models.',
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
// --- Export 1: generateRecommendations ---
|
|
234
|
+
export function generateRecommendations(cwd) {
|
|
235
|
+
try {
|
|
236
|
+
const routingState = loadRoutingState(cwd);
|
|
237
|
+
const thinkMetrics = loadThinkMetrics(cwd);
|
|
238
|
+
const governance = loadGovernance(cwd);
|
|
239
|
+
const subscription = loadSubscription(cwd);
|
|
240
|
+
const outcomes = loadOutcomes(cwd);
|
|
241
|
+
const recs = [
|
|
242
|
+
...modelMismatch(routingState),
|
|
243
|
+
failurePattern(outcomes),
|
|
244
|
+
budgetTrajectory(governance),
|
|
245
|
+
thinkROI(thinkMetrics),
|
|
246
|
+
subscriptionUtilization(subscription, routingState),
|
|
247
|
+
cascadeEffectiveness(thinkMetrics, outcomes),
|
|
248
|
+
];
|
|
249
|
+
const filtered = recs.filter((r) => r !== null);
|
|
250
|
+
const order = { high: 0, medium: 1, low: 2 };
|
|
251
|
+
return filtered.sort((a, b) => (order[a.priority] ?? 9) - (order[b.priority] ?? 9));
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
return [];
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// --- Export 2: formatRecommendations ---
|
|
258
|
+
const ICONS = { high: '⚡', medium: '💡', low: '📊' };
|
|
259
|
+
export function formatRecommendations(recs) {
|
|
260
|
+
const top = recs.slice(0, 4);
|
|
261
|
+
if (!top.length) {
|
|
262
|
+
return '╭─ Recommendations ─────────────────────────────────────────────╮\n' +
|
|
263
|
+
'│ No recommendations — configuration looks healthy. │\n' +
|
|
264
|
+
'╰───────────────────────────────────────────────────────────────╯';
|
|
265
|
+
}
|
|
266
|
+
const WIDTH = 63;
|
|
267
|
+
// Truncate + pad to fit inside box: WIDTH - 4 accounts for '| ' and ' |'
|
|
268
|
+
const INNER = WIDTH - 4;
|
|
269
|
+
const clip = (str) => str.length > INNER ? str.slice(0, INNER - 1) + '…' : str;
|
|
270
|
+
const pad = (str) => clip(str).padEnd(INNER);
|
|
271
|
+
const line = (content) => `│ ${pad(content)} │`;
|
|
272
|
+
const lines = [
|
|
273
|
+
'╭─ Recommendations ' + '─'.repeat(WIDTH - 20) + '╮',
|
|
274
|
+
line(''),
|
|
275
|
+
];
|
|
276
|
+
for (const rec of top) {
|
|
277
|
+
const icon = ICONS[rec.priority] || '•';
|
|
278
|
+
lines.push(line(`${icon} ${rec.priority.toUpperCase()}: ${rec.title}`));
|
|
279
|
+
lines.push(line(` ${rec.description}`));
|
|
280
|
+
lines.push(line(` → ${rec.action}`));
|
|
281
|
+
lines.push(line(''));
|
|
282
|
+
}
|
|
283
|
+
lines.push('╰' + '─'.repeat(WIDTH - 2) + '╯');
|
|
284
|
+
return lines.join('\n');
|
|
285
|
+
}
|
|
286
|
+
// --- Export 3: getTopRecommendation ---
|
|
287
|
+
export function getTopRecommendation(cwd) {
|
|
288
|
+
const recs = generateRecommendations(cwd);
|
|
289
|
+
return recs.length ? recs[0] : null;
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=recommendations.js.map
|