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
package/src/prompt-audit.mjs
DELETED
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, appendFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
|
|
4
|
-
const AUDIT_DIR = join(process.cwd(), '.dualbrain', 'prompt-audit');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Score a prompt for quality before sending to a provider.
|
|
8
|
-
* Returns score 0-100 and specific feedback.
|
|
9
|
-
*/
|
|
10
|
-
export function scorePrompt(prompt, opts = {}) {
|
|
11
|
-
const { type = 'think', maxTokenBudget = 2000 } = opts;
|
|
12
|
-
|
|
13
|
-
const issues = [];
|
|
14
|
-
const strengths = [];
|
|
15
|
-
let score = 100;
|
|
16
|
-
|
|
17
|
-
// Length efficiency
|
|
18
|
-
const words = prompt.split(/\s+/).length;
|
|
19
|
-
const chars = prompt.length;
|
|
20
|
-
|
|
21
|
-
if (words < 20) {
|
|
22
|
-
issues.push({ rule: 'too-short', msg: 'Prompt under 20 words — likely missing context', penalty: 15 });
|
|
23
|
-
score -= 15;
|
|
24
|
-
}
|
|
25
|
-
if (words > 500) {
|
|
26
|
-
issues.push({ rule: 'too-long', msg: `Prompt is ${words} words — consider trimming`, penalty: 10 });
|
|
27
|
-
score -= 10;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Structure checks
|
|
31
|
-
if (type === 'think') {
|
|
32
|
-
if (!prompt.includes('?')) {
|
|
33
|
-
issues.push({ rule: 'no-question', msg: 'Think prompt has no question mark — unclear what decision is needed', penalty: 10 });
|
|
34
|
-
score -= 10;
|
|
35
|
-
}
|
|
36
|
-
if (!/\b(should|how|what|which|why|when|where|recommend|decide|choose|compare|tradeoff)\b/i.test(prompt)) {
|
|
37
|
-
issues.push({ rule: 'no-decision-language', msg: 'No decision-making language found', penalty: 5 });
|
|
38
|
-
score -= 5;
|
|
39
|
-
}
|
|
40
|
-
if (/\b(at least \d+ ideas|generate.*list|brainstorm)\b/i.test(prompt)) {
|
|
41
|
-
strengths.push('Requests specific output quantity');
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Context quality
|
|
46
|
-
if (/\b(this project|the codebase|our system)\b/i.test(prompt) && !/\b(module|file|function|export|import)\b/i.test(prompt)) {
|
|
47
|
-
issues.push({ rule: 'vague-context', msg: 'References "the project" without naming specific modules/files', penalty: 10 });
|
|
48
|
-
score -= 10;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (/\b(src\/\w+|\.mjs|\.js|\.ts)\b/.test(prompt)) {
|
|
52
|
-
strengths.push('Names specific files/modules');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Constraint quality
|
|
56
|
-
if (/\b(at least|minimum|maximum|no more than|ranked|ordered|prioritized)\b/i.test(prompt)) {
|
|
57
|
-
strengths.push('Includes output constraints');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Anti-patterns
|
|
61
|
-
if (/\b(please|could you|would you mind)\b/i.test(prompt)) {
|
|
62
|
-
issues.push({ rule: 'politeness-waste', msg: 'Politeness tokens wasted on AI — be direct', penalty: 2 });
|
|
63
|
-
score -= 2;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (/\b(I think|I believe|maybe|perhaps|possibly)\b/i.test(prompt) && type === 'think') {
|
|
67
|
-
issues.push({ rule: 'hedging', msg: 'Hedging language in think prompt — state positions directly', penalty: 5 });
|
|
68
|
-
score -= 5;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Token efficiency estimate
|
|
72
|
-
const estimatedTokens = Math.ceil(chars / 4);
|
|
73
|
-
const efficiency = Math.min(100, Math.round((words / estimatedTokens) * 100));
|
|
74
|
-
|
|
75
|
-
// Duplication check
|
|
76
|
-
const sentences = prompt.split(/[.!?]+/).filter(s => s.trim().length > 10);
|
|
77
|
-
const unique = new Set(sentences.map(s => s.trim().toLowerCase()));
|
|
78
|
-
if (sentences.length > 3 && unique.size < sentences.length * 0.7) {
|
|
79
|
-
issues.push({ rule: 'repetitive', msg: `${sentences.length - unique.size} near-duplicate sentences`, penalty: 10 });
|
|
80
|
-
score -= 10;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
score = Math.max(0, Math.min(100, score));
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
score,
|
|
87
|
-
grade: score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 70 ? 'C' : score >= 60 ? 'D' : 'F',
|
|
88
|
-
issues,
|
|
89
|
-
strengths,
|
|
90
|
-
stats: {
|
|
91
|
-
words,
|
|
92
|
-
chars,
|
|
93
|
-
estimatedTokens,
|
|
94
|
-
efficiency,
|
|
95
|
-
sentences: sentences.length,
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Log a prompt exchange for auditing.
|
|
102
|
-
*/
|
|
103
|
-
export function logPromptExchange(exchange) {
|
|
104
|
-
const {
|
|
105
|
-
type = 'think',
|
|
106
|
-
round = 1,
|
|
107
|
-
prompt,
|
|
108
|
-
response,
|
|
109
|
-
provider = 'gpt',
|
|
110
|
-
model,
|
|
111
|
-
durationMs,
|
|
112
|
-
promptScore,
|
|
113
|
-
} = exchange;
|
|
114
|
-
|
|
115
|
-
mkdirSync(AUDIT_DIR, { recursive: true });
|
|
116
|
-
|
|
117
|
-
const entry = {
|
|
118
|
-
timestamp: new Date().toISOString(),
|
|
119
|
-
type,
|
|
120
|
-
round,
|
|
121
|
-
provider,
|
|
122
|
-
model,
|
|
123
|
-
durationMs,
|
|
124
|
-
promptScore: promptScore?.score,
|
|
125
|
-
promptGrade: promptScore?.grade,
|
|
126
|
-
promptWords: promptScore?.stats?.words,
|
|
127
|
-
promptTokens: promptScore?.stats?.estimatedTokens,
|
|
128
|
-
responseWords: response ? response.split(/\s+/).length : 0,
|
|
129
|
-
responseTokens: response ? Math.ceil(response.length / 4) : 0,
|
|
130
|
-
issues: promptScore?.issues?.map(i => i.rule) || [],
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const logFile = join(AUDIT_DIR, 'exchanges.jsonl');
|
|
134
|
-
appendFileSync(logFile, JSON.stringify(entry) + '\n');
|
|
135
|
-
|
|
136
|
-
return entry;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Get prompt quality statistics over time.
|
|
141
|
-
*/
|
|
142
|
-
export function getPromptStats(opts = {}) {
|
|
143
|
-
const { days = 7 } = opts;
|
|
144
|
-
const logFile = join(AUDIT_DIR, 'exchanges.jsonl');
|
|
145
|
-
|
|
146
|
-
if (!existsSync(logFile)) return { available: false };
|
|
147
|
-
|
|
148
|
-
const cutoff = new Date(Date.now() - days * 86400000).toISOString();
|
|
149
|
-
const lines = readFileSync(logFile, 'utf8').trim().split('\n').filter(Boolean);
|
|
150
|
-
|
|
151
|
-
const entries = [];
|
|
152
|
-
for (const line of lines) {
|
|
153
|
-
try {
|
|
154
|
-
const entry = JSON.parse(line);
|
|
155
|
-
if (entry.timestamp >= cutoff) entries.push(entry);
|
|
156
|
-
} catch {}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (entries.length === 0) return { available: true, count: 0 };
|
|
160
|
-
|
|
161
|
-
const avgScore = entries.reduce((s, e) => s + (e.promptScore || 0), 0) / entries.length;
|
|
162
|
-
const avgPromptTokens = entries.reduce((s, e) => s + (e.promptTokens || 0), 0) / entries.length;
|
|
163
|
-
const avgResponseTokens = entries.reduce((s, e) => s + (e.responseTokens || 0), 0) / entries.length;
|
|
164
|
-
const avgDuration = entries.reduce((s, e) => s + (e.durationMs || 0), 0) / entries.length;
|
|
165
|
-
|
|
166
|
-
const grades = {};
|
|
167
|
-
for (const e of entries) {
|
|
168
|
-
grades[e.promptGrade] = (grades[e.promptGrade] || 0) + 1;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const commonIssues = {};
|
|
172
|
-
for (const e of entries) {
|
|
173
|
-
for (const issue of (e.issues || [])) {
|
|
174
|
-
commonIssues[issue] = (commonIssues[issue] || 0) + 1;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const topIssues = Object.entries(commonIssues)
|
|
179
|
-
.sort((a, b) => b[1] - a[1])
|
|
180
|
-
.slice(0, 5)
|
|
181
|
-
.map(([rule, count]) => ({ rule, count, pct: Math.round(count / entries.length * 100) }));
|
|
182
|
-
|
|
183
|
-
return {
|
|
184
|
-
available: true,
|
|
185
|
-
count: entries.length,
|
|
186
|
-
avgScore: Math.round(avgScore),
|
|
187
|
-
avgPromptTokens: Math.round(avgPromptTokens),
|
|
188
|
-
avgResponseTokens: Math.round(avgResponseTokens),
|
|
189
|
-
avgDurationMs: Math.round(avgDuration),
|
|
190
|
-
totalPromptTokens: entries.reduce((s, e) => s + (e.promptTokens || 0), 0),
|
|
191
|
-
totalResponseTokens: entries.reduce((s, e) => s + (e.responseTokens || 0), 0),
|
|
192
|
-
grades,
|
|
193
|
-
topIssues,
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Suggest improvements for a prompt.
|
|
199
|
-
*/
|
|
200
|
-
export function suggestImprovements(prompt, type = 'think') {
|
|
201
|
-
const score = scorePrompt(prompt, { type });
|
|
202
|
-
const suggestions = [];
|
|
203
|
-
|
|
204
|
-
for (const issue of score.issues) {
|
|
205
|
-
switch (issue.rule) {
|
|
206
|
-
case 'too-short':
|
|
207
|
-
suggestions.push('Add context: what modules are involved, what decision is needed, what constraints exist');
|
|
208
|
-
break;
|
|
209
|
-
case 'too-long':
|
|
210
|
-
suggestions.push('Trim: remove background the AI already knows from CLAUDE.md. Focus on what\'s unique to this question');
|
|
211
|
-
break;
|
|
212
|
-
case 'no-question':
|
|
213
|
-
suggestions.push('End with a clear question or decision point');
|
|
214
|
-
break;
|
|
215
|
-
case 'vague-context':
|
|
216
|
-
suggestions.push('Name specific files, functions, or modules instead of "the project"');
|
|
217
|
-
break;
|
|
218
|
-
case 'politeness-waste':
|
|
219
|
-
suggestions.push('Remove "please", "could you" — direct prompts produce better output');
|
|
220
|
-
break;
|
|
221
|
-
case 'hedging':
|
|
222
|
-
suggestions.push('State positions directly — "X is better because Y" not "I think maybe X"');
|
|
223
|
-
break;
|
|
224
|
-
case 'repetitive':
|
|
225
|
-
suggestions.push('Remove duplicate sentences — each sentence should add new information');
|
|
226
|
-
break;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return { score, suggestions };
|
|
231
|
-
}
|
package/src/prompt-intel.mjs
DELETED
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
// prompt-intel.mjs — Layer 3 prompt analysis, enrichment, risk detection, and intervention routing.
|
|
2
|
-
// Pure functions only. No I/O, no exec. Caller provides projectBrief and calibration.
|
|
3
|
-
|
|
4
|
-
const INTENT_PATTERNS = {
|
|
5
|
-
fix: /\b(?:fix|bug|broken|error|crash|failing|fails|wrong|issue|problem|broken|not\s+working|doesn't\s+work|doesn't\s+work)\b/i,
|
|
6
|
-
feature: /\b(?:add|create|build|implement|new|introduce|support|enable)\b/i,
|
|
7
|
-
refactor: /\b(?:refactor|clean\s+up|reorganize|simplify|extract|restructure|dedup|consolidate|move)\b/i,
|
|
8
|
-
review: /\b(?:review|check|audit|look\s+at|examine|inspect|assess|evaluate)\b/i,
|
|
9
|
-
ship: /\b(?:ship|deploy|publish|release|push|merge|go\s+live|launch)\b/i,
|
|
10
|
-
explore: /\b(?:what|how|why|where|find|search|explain|show\s+me|tell\s+me|list|which)\b/i,
|
|
11
|
-
test: /\b(?:test|spec|coverage|assert|jest|mocha|vitest|unit\s+test|integration)\b/i,
|
|
12
|
-
docs: /\b(?:doc|readme|comment|jsdoc|document|explain|annotate)\b/i,
|
|
13
|
-
deploy: /\b(?:deploy|provision|infrastructure|ci|cd|pipeline|k8s|kubernetes|docker)\b/i,
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const RISK_PATTERNS = [
|
|
17
|
-
{ type: 'destructive', severity: 'block', re: /\b(?:delete\s+all|remove\s+all|drop\s+all|wipe|destroy|rm\s+-rf|truncate\s+all|nuke)\b/i, detail: 'Prompt contains mass-destructive operation' },
|
|
18
|
-
{ type: 'force_push', severity: 'block', re: /(?:force\s+push|--force|-f\s+origin|reset\s+--hard)/i, detail: 'Prompt implies forced git operation' },
|
|
19
|
-
{ type: 'secret', severity: 'warn', re: /\b(?:api\s+key|access\s+token|secret\s+key|\.env|private\s+key|bearer\s+token)\b/i, detail: 'Touches secret or credential material' },
|
|
20
|
-
{ type: 'auth', severity: 'warn', re: /\b(?:auth(?:entication)?|login|logout|password|credential|jwt|oauth|session\s+token)\b/i, detail: 'Touches authentication code' },
|
|
21
|
-
{ type: 'deploy', severity: 'warn', re: /\b(?:ship\s+to|deploy\s+to|release\s+to|push\s+to\s+prod)\b/i, detail: 'Targets a deployment action' },
|
|
22
|
-
{ type: 'data_loss', severity: 'warn', re: /\b(?:drop\s+table|alter\s+table|schema\s+migration|migrate\s+data|database\s+reset)\b/i, detail: 'Touches schema or migration — data loss risk' },
|
|
23
|
-
{ type: 'production', severity: 'warn', re: /\b(?:production|prod\b|live\s+environment|live\s+site)\b/i, detail: 'References production environment' },
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
const FILE_REF_RE = /(?:src\/|\.mjs|\.tsx?|\.jsx?|\.json|\.ya?ml|\.sh|line\s+\d+|\bL\d+\b)/i;
|
|
27
|
-
const FUNC_REF_RE = /\b\w+\((?:\)|[^)]{0,40}\))/;
|
|
28
|
-
const STEP_RE = /\b(?:step\s+\d|first[,\s]|then[,\s]|finally[,\s]|must|should\s+(?:use|call|return|handle))\b/i;
|
|
29
|
-
const CRITERIA_RE = /\b(?:accept(?:ance)?\s+criteria|definition\s+of\s+done|constraints?|requirements?|must\s+(?:not|be|have)|should\s+not)\b/i;
|
|
30
|
-
|
|
31
|
-
function clamp(v, min = 1, max = 5) {
|
|
32
|
-
return Math.min(max, Math.max(min, v));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function scoreSpecificity(prompt) {
|
|
36
|
-
const hasFile = FILE_REF_RE.test(prompt);
|
|
37
|
-
const hasFunc = FUNC_REF_RE.test(prompt);
|
|
38
|
-
const hasLine = /\bL\d+\b|\bline\s+\d+/i.test(prompt);
|
|
39
|
-
const words = prompt.trim().split(/\s+/).length;
|
|
40
|
-
|
|
41
|
-
if (hasFile && (hasFunc || hasLine)) return 5;
|
|
42
|
-
if (hasFile || hasFunc) return 4;
|
|
43
|
-
if (words >= 10) return 3;
|
|
44
|
-
if (words >= 5) return 2;
|
|
45
|
-
return 1;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function scoreActionability(prompt) {
|
|
49
|
-
const hasStep = STEP_RE.test(prompt);
|
|
50
|
-
const hasVerb = /\b(?:fix|add|remove|refactor|create|update|write|delete|move|rename|replace|ensure|make)\b/i.test(prompt);
|
|
51
|
-
const hasOutcome = /\b(?:so\s+that|in\s+order\s+to|result(?:ing)?\s+in|should\s+(?:return|output|produce|show))\b/i.test(prompt);
|
|
52
|
-
const words = prompt.trim().split(/\s+/).length;
|
|
53
|
-
|
|
54
|
-
if (hasStep && hasVerb) return 5;
|
|
55
|
-
if (hasOutcome && hasVerb) return 4;
|
|
56
|
-
if (hasVerb && words >= 6) return 3;
|
|
57
|
-
if (hasVerb) return 2;
|
|
58
|
-
return 1;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function scoreSafety(risks) {
|
|
62
|
-
if (risks.some(r => r.severity === 'block')) return 1;
|
|
63
|
-
if (risks.some(r => r.type === 'auth' || r.type === 'secret' || r.type === 'production')) return 2;
|
|
64
|
-
if (risks.some(r => r.type === 'deploy' || r.type === 'data_loss')) return 3;
|
|
65
|
-
if (risks.length > 0) return 4;
|
|
66
|
-
return 5;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function scoreCompleteness(prompt) {
|
|
70
|
-
const hasCriteria = CRITERIA_RE.test(prompt);
|
|
71
|
-
const hasContext = /\b(?:because|since|currently|right\s+now|the\s+issue\s+is|error\s+is|it\s+(?:crashes|fails|returns))\b/i.test(prompt);
|
|
72
|
-
const hasScope = FILE_REF_RE.test(prompt);
|
|
73
|
-
const words = prompt.trim().split(/\s+/).length;
|
|
74
|
-
|
|
75
|
-
if (hasCriteria && hasContext && hasScope) return 5;
|
|
76
|
-
if ((hasCriteria || hasContext) && hasScope) return 4;
|
|
77
|
-
if (hasContext || (hasScope && words >= 8)) return 3;
|
|
78
|
-
if (words >= 6) return 2;
|
|
79
|
-
return 1;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function detectIntent(prompt) {
|
|
83
|
-
const counts = {};
|
|
84
|
-
for (const [type, re] of Object.entries(INTENT_PATTERNS)) {
|
|
85
|
-
const matches = prompt.match(new RegExp(re.source, 'gi')) ?? [];
|
|
86
|
-
if (matches.length > 0) counts[type] = matches.length;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const entries = Object.entries(counts).sort((a, b) => b[1] - a[1]);
|
|
90
|
-
if (entries.length === 0) {
|
|
91
|
-
return { type: 'unknown', confidence: 0, keywords: [] };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const [topType, topCount] = entries[0];
|
|
95
|
-
const totalMatches = Object.values(counts).reduce((s, n) => s + n, 0);
|
|
96
|
-
const confidence = Math.min(1, topCount / Math.max(totalMatches, 1) * (entries.length === 1 ? 1.5 : 1));
|
|
97
|
-
|
|
98
|
-
const allWords = prompt.match(new RegExp(INTENT_PATTERNS[topType].source, 'gi')) ?? [];
|
|
99
|
-
const keywords = [...new Set(allWords.map(w => w.toLowerCase().trim()))].slice(0, 5);
|
|
100
|
-
|
|
101
|
-
return { type: topType, confidence: Math.round(confidence * 100) / 100, keywords };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function detectRisks(prompt) {
|
|
105
|
-
const found = [];
|
|
106
|
-
for (const { type, severity, re, detail } of RISK_PATTERNS) {
|
|
107
|
-
if (re.test(prompt)) {
|
|
108
|
-
found.push({ type, severity, detail });
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return found;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function findMissingInfo(prompt, intent, specificity, completeness) {
|
|
115
|
-
const missing = [];
|
|
116
|
-
|
|
117
|
-
if (intent.type === 'fix') {
|
|
118
|
-
if (!/error|exception|crash|fail|wrong|issue/i.test(prompt)) missing.push('error description or failure symptom');
|
|
119
|
-
if (!FILE_REF_RE.test(prompt)) missing.push('affected file or component');
|
|
120
|
-
if (!/repro|reproduce|steps|trigger|when\s+I/i.test(prompt)) missing.push('reproduction steps');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (intent.type === 'feature') {
|
|
124
|
-
if (!FILE_REF_RE.test(prompt)) missing.push('target file or module to add feature to');
|
|
125
|
-
if (!/user\s+(?:can|should|will)|should\s+(?:allow|enable|support)/i.test(prompt)) missing.push('user-facing outcome');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (intent.type === 'refactor' && !FILE_REF_RE.test(prompt)) {
|
|
129
|
-
missing.push('which file or function to refactor');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (specificity < 2) missing.push('file name or component reference');
|
|
133
|
-
if (completeness < 2) missing.push('context about current behavior');
|
|
134
|
-
|
|
135
|
-
return [...new Set(missing)].slice(0, 3);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function chooseIntervention(quality, risks, calibration) {
|
|
139
|
-
if (risks.some(r => r.severity === 'block')) return 'block';
|
|
140
|
-
|
|
141
|
-
const autonomy = calibration?.autonomy ?? 3;
|
|
142
|
-
const specificity = calibration?.specificity ?? 3;
|
|
143
|
-
|
|
144
|
-
if (quality.score >= 4 && risks.length === 0 && autonomy > 4) return 'pass';
|
|
145
|
-
if (quality.score < 2 && specificity < 3) return 'confirm_rewrite';
|
|
146
|
-
if (quality.score < 3 && quality.completeness <= 2) return 'clarify_once';
|
|
147
|
-
return 'silent_enrich';
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export function analyzePrompt(prompt, projectBrief, calibration) {
|
|
151
|
-
const risks = detectRisks(prompt);
|
|
152
|
-
const intent = detectIntent(prompt);
|
|
153
|
-
const specificity = scoreSpecificity(prompt);
|
|
154
|
-
const actionability= scoreActionability(prompt);
|
|
155
|
-
const safety = scoreSafety(risks);
|
|
156
|
-
const completeness = scoreCompleteness(prompt);
|
|
157
|
-
const score = Math.round(((specificity + actionability + safety + completeness) / 4) * 10) / 10;
|
|
158
|
-
|
|
159
|
-
const quality = { score, specificity, actionability, safety, completeness };
|
|
160
|
-
const missingInfo = findMissingInfo(prompt, intent, specificity, completeness);
|
|
161
|
-
const intervention = chooseIntervention(quality, risks, calibration);
|
|
162
|
-
|
|
163
|
-
return {
|
|
164
|
-
original: prompt,
|
|
165
|
-
quality,
|
|
166
|
-
intent,
|
|
167
|
-
risks,
|
|
168
|
-
missingInfo,
|
|
169
|
-
intervention,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function relevantDirtyFiles(dirtyFiles, intent) {
|
|
174
|
-
if (!Array.isArray(dirtyFiles) || dirtyFiles.length === 0) return [];
|
|
175
|
-
|
|
176
|
-
const INTENT_HINTS = {
|
|
177
|
-
fix: /\bfix|bug|error\b/i,
|
|
178
|
-
ship: /.*/,
|
|
179
|
-
review: /.*/,
|
|
180
|
-
feature: /src\//i,
|
|
181
|
-
refactor: /src\//i,
|
|
182
|
-
test: /test|spec/i,
|
|
183
|
-
docs: /\.md$|readme/i,
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const re = INTENT_HINTS[intent.type] ?? /src\//i;
|
|
187
|
-
return dirtyFiles.filter(f => re.test(f)).slice(0, 4);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export function enrichPrompt(prompt, projectBrief, analysis) {
|
|
191
|
-
if (!projectBrief) return prompt;
|
|
192
|
-
|
|
193
|
-
const lines = [prompt, ''];
|
|
194
|
-
const { branch, dirtyFiles = [], recentCommits = [], aheadOfRemote = 0, recentFailures = [] } = projectBrief;
|
|
195
|
-
const { intent } = analysis;
|
|
196
|
-
|
|
197
|
-
const uncommitted = dirtyFiles.length;
|
|
198
|
-
if (branch || uncommitted > 0) {
|
|
199
|
-
const parts = [];
|
|
200
|
-
if (branch) parts.push(`${branch} branch`);
|
|
201
|
-
if (uncommitted > 0) parts.push(`${uncommitted} uncommitted file${uncommitted !== 1 ? 's' : ''}`);
|
|
202
|
-
if (aheadOfRemote > 0) parts.push(`${aheadOfRemote} ahead of remote`);
|
|
203
|
-
lines.push(`[Context: ${parts.join(', ')}]`);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const relFiles = relevantDirtyFiles(dirtyFiles, intent);
|
|
207
|
-
if (relFiles.length > 0) {
|
|
208
|
-
lines.push(`[Files: ${relFiles.join(', ')}]`);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (recentCommits.length > 0 && ['fix', 'review', 'ship'].includes(intent.type)) {
|
|
212
|
-
lines.push(`[Recent: ${recentCommits[0].slice(0, 80)}]`);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (recentFailures.length > 0) {
|
|
216
|
-
const related = recentFailures.find(f => {
|
|
217
|
-
const fp = (f.prompt ?? '').toLowerCase();
|
|
218
|
-
const pp = prompt.toLowerCase();
|
|
219
|
-
const words = pp.split(/\s+/).filter(w => w.length > 3);
|
|
220
|
-
return words.some(w => fp.includes(w));
|
|
221
|
-
});
|
|
222
|
-
if (related) {
|
|
223
|
-
lines.push(`[Failures: previous attempt failed — ${(related.error ?? 'unknown error').slice(0, 80)}]`);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return lines.slice(0, lines.length).join('\n');
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
export function formatRiskWarning(risks) {
|
|
231
|
-
if (!risks || risks.length === 0) return '';
|
|
232
|
-
const lines = ['⚠️ RISK DETECTED'];
|
|
233
|
-
for (const risk of risks) {
|
|
234
|
-
const icon = risk.severity === 'block' ? '🔴' : '🟡';
|
|
235
|
-
const suffix = risk.severity === 'block' ? ' (BLOCKED)' : ' (proceed with caution)';
|
|
236
|
-
lines.push(` ${icon} ${risk.type}: ${risk.detail}${suffix}`);
|
|
237
|
-
}
|
|
238
|
-
return lines.join('\n');
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export function formatQuality(analysis) {
|
|
242
|
-
const { quality, intent, intervention } = analysis;
|
|
243
|
-
const q = quality;
|
|
244
|
-
return `Quality: ${q.score}/5 (specificity:${q.specificity} action:${q.actionability} safety:${q.safety} complete:${q.completeness})\nIntent: ${intent.type} (${intent.confidence}) | Intervention: ${intervention}`;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export function suggestImprovement(analysis) {
|
|
248
|
-
const { intent, missingInfo, quality } = analysis;
|
|
249
|
-
|
|
250
|
-
const templates = {
|
|
251
|
-
fix: `Fix the [specific function or behavior] in [file path] — it [symptom/error], causing [impact]`,
|
|
252
|
-
feature: `Add [feature name] to [file/component] — it should [user outcome] when [condition]`,
|
|
253
|
-
refactor: `Refactor [function/module] in [file] to [desired outcome] — keep [what to preserve]`,
|
|
254
|
-
review: `Review [file or PR] for [concern] — focus on [specific area or risk]`,
|
|
255
|
-
ship: `Ship [branch/feature] — verify [test status], then merge and publish`,
|
|
256
|
-
explore: `Explain how [specific mechanism] works in [file/component]`,
|
|
257
|
-
test: `Write tests for [function/module] in [file] covering [edge cases]`,
|
|
258
|
-
docs: `Document [function or module] in [file] — cover [params, return, examples]`,
|
|
259
|
-
deploy: `Deploy [service] to [environment] — confirm [readiness checks]`,
|
|
260
|
-
unknown: `Describe what you want changed, which file it's in, and what the expected result is`,
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
const base = templates[intent.type] ?? templates.unknown;
|
|
264
|
-
const tips = missingInfo.length > 0
|
|
265
|
-
? ` (missing: ${missingInfo.join(', ')})`
|
|
266
|
-
: '';
|
|
267
|
-
|
|
268
|
-
return `Try: '${base}'${tips}`;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
export function getTaskTemplate(intentType) {
|
|
272
|
-
const templates = {
|
|
273
|
-
fix: {
|
|
274
|
-
needs: ['error_description', 'affected_files', 'reproduction_steps'],
|
|
275
|
-
auto_add: ['test_expectations', 'rollback_plan'],
|
|
276
|
-
},
|
|
277
|
-
feature: {
|
|
278
|
-
needs: ['feature_description', 'affected_area'],
|
|
279
|
-
auto_add: ['test_plan', 'acceptance_criteria'],
|
|
280
|
-
},
|
|
281
|
-
refactor: {
|
|
282
|
-
needs: ['target_code', 'desired_outcome'],
|
|
283
|
-
auto_add: ['test_preservation', 'scope_boundary'],
|
|
284
|
-
},
|
|
285
|
-
review: {
|
|
286
|
-
needs: ['scope'],
|
|
287
|
-
auto_add: ['recent_changes', 'risk_areas'],
|
|
288
|
-
},
|
|
289
|
-
ship: {
|
|
290
|
-
needs: ['readiness_check'],
|
|
291
|
-
auto_add: ['test_status', 'git_status', 'uncommitted_changes'],
|
|
292
|
-
},
|
|
293
|
-
explore: {
|
|
294
|
-
needs: ['topic'],
|
|
295
|
-
auto_add: ['related_files', 'recent_changes'],
|
|
296
|
-
},
|
|
297
|
-
test: {
|
|
298
|
-
needs: ['target_function', 'test_framework'],
|
|
299
|
-
auto_add: ['edge_cases', 'existing_coverage'],
|
|
300
|
-
},
|
|
301
|
-
docs: {
|
|
302
|
-
needs: ['target_module'],
|
|
303
|
-
auto_add: ['param_descriptions', 'usage_example'],
|
|
304
|
-
},
|
|
305
|
-
deploy: {
|
|
306
|
-
needs: ['target_environment', 'service_name'],
|
|
307
|
-
auto_add: ['pre_deploy_checks', 'rollback_plan'],
|
|
308
|
-
},
|
|
309
|
-
unknown: {
|
|
310
|
-
needs: ['prompt_clarification'],
|
|
311
|
-
auto_add: [],
|
|
312
|
-
},
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
return templates[intentType] ?? templates.unknown;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
export function shouldBlock(analysis) {
|
|
319
|
-
return analysis.risks.some(r => r.severity === 'block');
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
export function getBlockReason(analysis) {
|
|
323
|
-
const blocking = analysis.risks.find(r => r.severity === 'block');
|
|
324
|
-
return blocking ? blocking.detail : null;
|
|
325
|
-
}
|