dual-brain 0.2.29 → 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 +122 -51
- 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 -85
- 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 -386
- package/src/pipeline.mjs +0 -1689
- 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 -291
- 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 -200
- package/src/self-correct.mjs +0 -146
- 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 -215
- package/src/signal.mjs +0 -114
- 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/decompose.mjs
DELETED
|
@@ -1,331 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* decompose.mjs — Task graph decomposition for the Dual-Brain Orchestrator HEAD.
|
|
4
|
-
*
|
|
5
|
-
* Splits complex prompts into a dependency-aware task graph using heuristic
|
|
6
|
-
* analysis. Pure data — returns a plan, does NOT spawn agents.
|
|
7
|
-
*
|
|
8
|
-
* Exports: decompose, isSimpleTask, taskGraphToWaves
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { detectTask, classifyIntent, extractPaths } from './detect.mjs';
|
|
12
|
-
import { loadPlaybook } from './playbook.mjs';
|
|
13
|
-
|
|
14
|
-
// ─── Role inference ───────────────────────────────────────────────────────────
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Map a tier + intent to the appropriate agent role.
|
|
18
|
-
* @param {string} tier
|
|
19
|
-
* @param {string} intent
|
|
20
|
-
* @returns {'researcher'|'implementer'|'reviewer'|'verifier'}
|
|
21
|
-
*/
|
|
22
|
-
function inferRole(tier, intent) {
|
|
23
|
-
if (tier === 'search') return 'researcher';
|
|
24
|
-
if (tier === 'think') {
|
|
25
|
-
if (intent === 'review') return 'reviewer';
|
|
26
|
-
return 'researcher'; // architect/planner stays researcher (read-only)
|
|
27
|
-
}
|
|
28
|
-
// execute tier
|
|
29
|
-
if (intent === 'test') return 'verifier';
|
|
30
|
-
if (intent === 'review') return 'reviewer';
|
|
31
|
-
return 'implementer';
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// ─── Conjunction splitter ─────────────────────────────────────────────────────
|
|
35
|
-
|
|
36
|
-
const CONJUNCTIONS = /\s+(?:and(?:\s+also)?|then|also|plus|after\s+that|additionally|as\s+well\s+as)\s+/i;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Split a prompt on natural-language conjunctions into clauses.
|
|
40
|
-
* @param {string} prompt
|
|
41
|
-
* @returns {string[]}
|
|
42
|
-
*/
|
|
43
|
-
function splitClauses(prompt) {
|
|
44
|
-
const parts = prompt.split(CONJUNCTIONS).map(s => s.trim()).filter(Boolean);
|
|
45
|
-
return parts.length > 1 ? parts : [prompt];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// ─── File ownership inference ─────────────────────────────────────────────────
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Extract file globs from a clause, falling back to intent-based heuristics.
|
|
52
|
-
* @param {string} clause
|
|
53
|
-
* @param {string} intent
|
|
54
|
-
* @param {string[]} contextFiles — file paths provided in decompose context
|
|
55
|
-
* @returns {string[]}
|
|
56
|
-
*/
|
|
57
|
-
function inferOwnership(clause, intent, contextFiles = []) {
|
|
58
|
-
const readOnly = ['search', 'explain', 'compare', 'review'];
|
|
59
|
-
if (readOnly.includes(intent)) return []; // read-only roles own nothing
|
|
60
|
-
|
|
61
|
-
// Paths mentioned explicitly in the clause
|
|
62
|
-
const mentioned = extractPaths(clause);
|
|
63
|
-
if (mentioned.length > 0) return mentioned;
|
|
64
|
-
|
|
65
|
-
// Paths from context that relate to this clause (simple keyword match)
|
|
66
|
-
const clauseLower = clause.toLowerCase();
|
|
67
|
-
const related = contextFiles.filter(f => {
|
|
68
|
-
const base = f.split('/').pop() ?? '';
|
|
69
|
-
return clauseLower.includes(base.replace(/\.[^.]+$/, ''));
|
|
70
|
-
});
|
|
71
|
-
if (related.length > 0) return related;
|
|
72
|
-
|
|
73
|
-
// Structural heuristics by intent
|
|
74
|
-
if (intent === 'test') return ['**/*.test.*', '**/*.spec.*'];
|
|
75
|
-
if (intent === 'document') return ['**/*.md'];
|
|
76
|
-
if (intent === 'format') return ['**/*'];
|
|
77
|
-
|
|
78
|
-
return [];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// ─── Dependency ordering ──────────────────────────────────────────────────────
|
|
82
|
-
|
|
83
|
-
const ROLE_ORDER = { researcher: 0, reviewer: 1, implementer: 2, verifier: 3 };
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Build dependency graph: earlier-role tasks must complete before later-role tasks.
|
|
87
|
-
* @param {{ id: string, role: string }[]} tasks
|
|
88
|
-
* @returns {{ [taskId: string]: string[] }} task id → list of dependent task ids
|
|
89
|
-
*/
|
|
90
|
-
function buildDependencies(tasks) {
|
|
91
|
-
const deps = {};
|
|
92
|
-
for (const task of tasks) {
|
|
93
|
-
deps[task.id] = [];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Each task depends on all tasks with a strictly lower role rank
|
|
97
|
-
for (let i = 0; i < tasks.length; i++) {
|
|
98
|
-
for (let j = 0; j < i; j++) {
|
|
99
|
-
if (ROLE_ORDER[tasks[j].role] < ROLE_ORDER[tasks[i].role]) {
|
|
100
|
-
deps[tasks[i].id].push(tasks[j].id);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return deps;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ─── Exported: taskGraphToWaves ───────────────────────────────────────────────
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Topological sort: group tasks into dependency layers (waves).
|
|
112
|
-
* Tasks with no unmet dependencies form the earliest wave.
|
|
113
|
-
* @param {{ id: string, dependsOn: string[] }[]} tasks
|
|
114
|
-
* @returns {string[][]} each element is an array of task IDs in that wave
|
|
115
|
-
*/
|
|
116
|
-
export function taskGraphToWaves(tasks) {
|
|
117
|
-
const remaining = new Set(tasks.map(t => t.id));
|
|
118
|
-
const satisfied = new Set();
|
|
119
|
-
const waves = [];
|
|
120
|
-
|
|
121
|
-
while (remaining.size > 0) {
|
|
122
|
-
const wave = [];
|
|
123
|
-
for (const task of tasks) {
|
|
124
|
-
if (!remaining.has(task.id)) continue;
|
|
125
|
-
const allDepsSatisfied = task.dependsOn.every(dep => satisfied.has(dep));
|
|
126
|
-
if (allDepsSatisfied) wave.push(task.id);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (wave.length === 0) {
|
|
130
|
-
// Cycle or unresolvable — dump remaining tasks into one last wave
|
|
131
|
-
waves.push([...remaining]);
|
|
132
|
-
break;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
waves.push(wave);
|
|
136
|
-
for (const id of wave) {
|
|
137
|
-
remaining.delete(id);
|
|
138
|
-
satisfied.add(id);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return waves;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// ─── Exported: isSimpleTask ───────────────────────────────────────────────────
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Returns true if the detection indicates a trivial or simple task that does
|
|
149
|
-
* not need decomposition.
|
|
150
|
-
* @param {{ complexity: string }} detection
|
|
151
|
-
* @returns {boolean}
|
|
152
|
-
*/
|
|
153
|
-
export function isSimpleTask(detection) {
|
|
154
|
-
return detection.complexity === 'trivial' || detection.complexity === 'simple';
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// ─── Playbook → task graph ────────────────────────────────────────────────────
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Convert a loaded playbook's steps into a task graph.
|
|
161
|
-
* @param {object} playbook
|
|
162
|
-
* @param {string[]} contextFiles
|
|
163
|
-
* @returns {{ id, title, goal, tier, role, owns, dependsOn, consensus, risk }[]}
|
|
164
|
-
*/
|
|
165
|
-
function playbookToTasks(playbook, contextFiles = []) {
|
|
166
|
-
const steps = playbook.steps ?? [];
|
|
167
|
-
const tasks = steps.map(step => {
|
|
168
|
-
const tier = step.tier ?? 'execute';
|
|
169
|
-
const intent = tier === 'think' ? 'architecture' : tier === 'search' ? 'search' : 'edit';
|
|
170
|
-
const role = inferRole(tier, intent);
|
|
171
|
-
return {
|
|
172
|
-
id: step.id,
|
|
173
|
-
title: step.title,
|
|
174
|
-
goal: step.goal,
|
|
175
|
-
tier,
|
|
176
|
-
role,
|
|
177
|
-
owns: inferOwnership(step.goal, intent, contextFiles),
|
|
178
|
-
dependsOn: [], // will be filled below
|
|
179
|
-
consensus: step.consensus === true,
|
|
180
|
-
risk: 'medium',
|
|
181
|
-
};
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// Each task depends on the immediately prior task (sequential playbook flow)
|
|
185
|
-
for (let i = 1; i < tasks.length; i++) {
|
|
186
|
-
tasks[i].dependsOn = [tasks[i - 1].id];
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return tasks;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// ─── Heuristic task graph from clause splitting ───────────────────────────────
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Build a task graph from clause-split sub-prompts via independent detection.
|
|
196
|
-
* @param {string[]} clauses
|
|
197
|
-
* @param {string[]} files
|
|
198
|
-
* @returns {{ id, title, goal, tier, role, owns, dependsOn, consensus, risk }[]}
|
|
199
|
-
*/
|
|
200
|
-
function clausesToTasks(clauses, files = []) {
|
|
201
|
-
const raw = clauses.map((clause, idx) => {
|
|
202
|
-
const detection = detectTask({ prompt: clause, files });
|
|
203
|
-
const { intent, tier, risk } = detection;
|
|
204
|
-
const role = inferRole(tier, intent);
|
|
205
|
-
return {
|
|
206
|
-
id: `task-${idx + 1}`,
|
|
207
|
-
title: clause.length > 60 ? clause.slice(0, 57) + '...' : clause,
|
|
208
|
-
goal: clause,
|
|
209
|
-
tier,
|
|
210
|
-
role,
|
|
211
|
-
owns: inferOwnership(clause, intent, files),
|
|
212
|
-
dependsOn: [], // placeholder
|
|
213
|
-
consensus: risk === 'critical' || (risk === 'high' && tier === 'think'),
|
|
214
|
-
risk,
|
|
215
|
-
};
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
// Build dependency graph and fill in dependsOn
|
|
219
|
-
const deps = buildDependencies(raw);
|
|
220
|
-
for (const task of raw) {
|
|
221
|
-
task.dependsOn = deps[task.id];
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return raw;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// ─── Exported: decompose ──────────────────────────────────────────────────────
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Decompose a prompt into a dependency-aware task graph.
|
|
231
|
-
* Pure data — returns a plan, does NOT spawn agents.
|
|
232
|
-
*
|
|
233
|
-
* @param {string} prompt
|
|
234
|
-
* @param {{ files?: string[], cwd?: string, repo?: object, profile?: object }} [context]
|
|
235
|
-
* @returns {{
|
|
236
|
-
* tasks: object[],
|
|
237
|
-
* waves: string[][],
|
|
238
|
-
* confidence: 'high'|'medium'|'low',
|
|
239
|
-
* needsResearch: boolean,
|
|
240
|
-
* parallelizable: boolean,
|
|
241
|
-
* }}
|
|
242
|
-
*/
|
|
243
|
-
export function decompose(prompt, context = {}) {
|
|
244
|
-
const { files = [], cwd } = context;
|
|
245
|
-
|
|
246
|
-
// 1. Classify the full prompt
|
|
247
|
-
const detection = detectTask({ prompt, files });
|
|
248
|
-
|
|
249
|
-
// 2. Trivial/simple → single-task graph
|
|
250
|
-
if (isSimpleTask(detection)) {
|
|
251
|
-
const { intent, tier, risk } = detection;
|
|
252
|
-
const role = inferRole(tier, intent);
|
|
253
|
-
const task = {
|
|
254
|
-
id: 'task-1',
|
|
255
|
-
title: prompt.length > 60 ? prompt.slice(0, 57) + '...' : prompt,
|
|
256
|
-
goal: prompt,
|
|
257
|
-
tier,
|
|
258
|
-
role,
|
|
259
|
-
owns: inferOwnership(prompt, intent, files),
|
|
260
|
-
dependsOn: [],
|
|
261
|
-
consensus: false,
|
|
262
|
-
risk,
|
|
263
|
-
};
|
|
264
|
-
return {
|
|
265
|
-
tasks: [task],
|
|
266
|
-
waves: [['task-1']],
|
|
267
|
-
confidence: 'high',
|
|
268
|
-
needsResearch: false,
|
|
269
|
-
parallelizable: false,
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// 3. Try to match a playbook
|
|
274
|
-
const playbook = loadPlaybook(detection.intent, cwd);
|
|
275
|
-
if (playbook) {
|
|
276
|
-
const tasks = playbookToTasks(playbook, files);
|
|
277
|
-
const waves = taskGraphToWaves(tasks);
|
|
278
|
-
return {
|
|
279
|
-
tasks,
|
|
280
|
-
waves,
|
|
281
|
-
confidence: 'high',
|
|
282
|
-
needsResearch: false,
|
|
283
|
-
parallelizable: waves.some(w => w.length > 1),
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// 4. Heuristic clause splitting
|
|
288
|
-
const clauses = splitClauses(prompt);
|
|
289
|
-
const hasResearch = files.length === 0 && detection.complexity !== 'trivial';
|
|
290
|
-
|
|
291
|
-
// If splitting didn't help (single clause), add a research task prefix for complex tasks
|
|
292
|
-
let allClauses = clauses;
|
|
293
|
-
if (clauses.length === 1 && hasResearch) {
|
|
294
|
-
allClauses = [`Research: find all files and context relevant to: ${prompt}`, ...clauses];
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const tasks = clausesToTasks(allClauses, files);
|
|
298
|
-
const waves = taskGraphToWaves(tasks);
|
|
299
|
-
|
|
300
|
-
// Confidence: medium if we split cleanly (>1 clause), low if ambiguous
|
|
301
|
-
const confidence = clauses.length > 1 ? 'medium' : 'low';
|
|
302
|
-
|
|
303
|
-
return {
|
|
304
|
-
tasks,
|
|
305
|
-
waves,
|
|
306
|
-
confidence,
|
|
307
|
-
needsResearch: hasResearch,
|
|
308
|
-
parallelizable: waves.some(w => w.length > 1),
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// ─── CLI ──────────────────────────────────────────────────────────────────────
|
|
313
|
-
|
|
314
|
-
if (process.argv[1] && new URL(import.meta.url).pathname === process.argv[1]) {
|
|
315
|
-
const args = process.argv.slice(2);
|
|
316
|
-
const prompt = args.find(a => !a.startsWith('--')) || '';
|
|
317
|
-
|
|
318
|
-
if (!prompt) {
|
|
319
|
-
console.error('Usage: node src/decompose.mjs "task description" [--files a,b]');
|
|
320
|
-
process.exit(1);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const filesFlag = args.find(a => a.startsWith('--files=')) ||
|
|
324
|
-
(args.includes('--files') ? args[args.indexOf('--files') + 1] : null);
|
|
325
|
-
const files = filesFlag
|
|
326
|
-
? filesFlag.replace(/^--files=/, '').split(',').map(f => f.trim()).filter(Boolean)
|
|
327
|
-
: [];
|
|
328
|
-
|
|
329
|
-
const result = decompose(prompt, { files });
|
|
330
|
-
console.log(JSON.stringify(result, null, 2));
|
|
331
|
-
}
|