@monoes/monomindcli 1.10.28 → 1.10.30
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/.claude/helpers/auto-memory-hook.mjs +39 -4
- package/.claude/helpers/handlers/edit-handler.cjs +145 -0
- package/.claude/helpers/handlers/route-handler.cjs +393 -0
- package/.claude/helpers/handlers/session-handler.cjs +167 -0
- package/.claude/helpers/handlers/session-restore-handler.cjs +343 -0
- package/.claude/helpers/handlers/task-handler.cjs +329 -0
- package/.claude/helpers/hook-handler.cjs +114 -2247
- package/.claude/helpers/intelligence.cjs +21 -2
- package/.claude/helpers/learning-service.mjs +166 -8
- package/.claude/helpers/memory-palace.cjs +72 -12
- package/.claude/helpers/router.cjs +79 -5
- package/.claude/helpers/statusline.cjs +193 -399
- package/.claude/helpers/utils/micro-agents.cjs +338 -0
- package/.claude/helpers/utils/monograph.cjs +349 -0
- package/.claude/helpers/utils/telemetry.cjs +144 -0
- package/.claude/skills/agent-browser-testing/SKILL.md +3 -2
- package/.claude/skills/monomind/browse-agentcore.md +116 -0
- package/.claude/skills/monomind/browse-electron.md +189 -0
- package/.claude/skills/monomind/browse-qa.md +229 -0
- package/.claude/skills/monomind/browse-references/authentication.md +162 -0
- package/.claude/skills/monomind/browse-references/trust-boundaries.md +41 -0
- package/.claude/skills/monomind/browse-references/video-recording.md +84 -0
- package/.claude/skills/monomind/browse-slack.md +189 -0
- package/.claude/skills/monomind/browse-vercel.md +240 -0
- package/.claude/skills/monomind/browse.md +724 -0
- package/dist/src/browser/actions.d.ts +13 -0
- package/dist/src/browser/actions.d.ts.map +1 -0
- package/dist/src/browser/actions.js +201 -0
- package/dist/src/browser/actions.js.map +1 -0
- package/dist/src/browser/browser.d.ts +14 -0
- package/dist/src/browser/browser.d.ts.map +1 -0
- package/dist/src/browser/browser.js +198 -0
- package/dist/src/browser/browser.js.map +1 -0
- package/dist/src/browser/cdp.d.ts +17 -0
- package/dist/src/browser/cdp.d.ts.map +1 -0
- package/dist/src/browser/cdp.js +106 -0
- package/dist/src/browser/cdp.js.map +1 -0
- package/dist/src/browser/index.d.ts +11 -0
- package/dist/src/browser/index.d.ts.map +1 -0
- package/dist/src/browser/index.js +11 -0
- package/dist/src/browser/index.js.map +1 -0
- package/dist/src/browser/network.d.ts +11 -0
- package/dist/src/browser/network.d.ts.map +1 -0
- package/dist/src/browser/network.js +81 -0
- package/dist/src/browser/network.js.map +1 -0
- package/dist/src/browser/screenshot.d.ts +15 -0
- package/dist/src/browser/screenshot.d.ts.map +1 -0
- package/dist/src/browser/screenshot.js +36 -0
- package/dist/src/browser/screenshot.js.map +1 -0
- package/dist/src/browser/session.d.ts +8 -0
- package/dist/src/browser/session.d.ts.map +1 -0
- package/dist/src/browser/session.js +50 -0
- package/dist/src/browser/session.js.map +1 -0
- package/dist/src/browser/snapshot.d.ts +12 -0
- package/dist/src/browser/snapshot.d.ts.map +1 -0
- package/dist/src/browser/snapshot.js +147 -0
- package/dist/src/browser/snapshot.js.map +1 -0
- package/dist/src/browser/tabs.d.ts +8 -0
- package/dist/src/browser/tabs.d.ts.map +1 -0
- package/dist/src/browser/tabs.js +25 -0
- package/dist/src/browser/tabs.js.map +1 -0
- package/dist/src/browser/types.d.ts +109 -0
- package/dist/src/browser/types.d.ts.map +1 -0
- package/dist/src/browser/types.js +16 -0
- package/dist/src/browser/types.js.map +1 -0
- package/dist/src/browser/wait.d.ts +4 -0
- package/dist/src/browser/wait.d.ts.map +1 -0
- package/dist/src/browser/wait.js +122 -0
- package/dist/src/browser/wait.js.map +1 -0
- package/dist/src/commands/browse.d.ts +8 -0
- package/dist/src/commands/browse.d.ts.map +1 -0
- package/dist/src/commands/browse.js +573 -0
- package/dist/src/commands/browse.js.map +1 -0
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +2 -0
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +25 -1
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/init/executor.d.ts.map +1 -1
- package/dist/src/init/executor.js +27 -0
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/ui/dashboard-v2.html +1692 -0
- package/dist/src/ui/server.mjs +15 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/scripts/understand-analyze.mjs +14 -1
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Extracted from hook-handler.cjs — receives hCtx from dispatcher.
|
|
3
|
+
// Handles 'pre-task' and 'post-task' hook events.
|
|
4
|
+
// See route-handler.cjs for full hCtx field documentation.
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
handlePreTask: async function(hCtx) {
|
|
11
|
+
var hookInput = hCtx.hookInput;
|
|
12
|
+
var prompt = hCtx.prompt;
|
|
13
|
+
var router = hCtx.router;
|
|
14
|
+
var session = hCtx.session;
|
|
15
|
+
var CWD = hCtx.CWD;
|
|
16
|
+
|
|
17
|
+
if (session && session.metric) {
|
|
18
|
+
try { session.metric('tasks'); } catch (e) { /* no active session */ }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ── Task 27: PerRunModelTier — inline complexity scoring ───────────────
|
|
22
|
+
var taskStr = typeof prompt === 'string' ? prompt : '';
|
|
23
|
+
if (taskStr) {
|
|
24
|
+
var score = 50;
|
|
25
|
+
var lower = taskStr.toLowerCase();
|
|
26
|
+
var words = taskStr.trim().split(/\s+/).length;
|
|
27
|
+
if (words < 20) score -= 20;
|
|
28
|
+
if (words > 100) score += 20;
|
|
29
|
+
if (words > 200) score += 10;
|
|
30
|
+
var highKw = ['architecture','distributed','security audit','cve','consensus','fault-tolerant','migrate','refactor across','orchestrat','design system','database schema','performance optim','threat model','encryption','zero-knowledge'];
|
|
31
|
+
var lowKwRe = /\b(format|list|rename|sort|typo|lint|log|comment|print|echo|delete unused|remove import)\b/i;
|
|
32
|
+
if (highKw.some(function(k) { return lower.includes(k); })) score += 10;
|
|
33
|
+
if (lowKwRe.test(lower)) score -= 10;
|
|
34
|
+
if (/(?:step\s*\d|first[\s,].*then[\s,]|phase\s*\d)/i.test(taskStr)) score += 10;
|
|
35
|
+
if (/```[\s\S]*?```/.test(taskStr) || /\b[\w.-]+\/[\w./-]+\b/.test(taskStr)) score += 5;
|
|
36
|
+
score = Math.max(0, Math.min(100, score));
|
|
37
|
+
var tier = score < 30 ? 'haiku' : score > 70 ? 'opus' : 'sonnet';
|
|
38
|
+
console.log('[TASK_MODEL_RECOMMENDATION] Use model="' + tier + '" (complexity=' + score + ')');
|
|
39
|
+
}
|
|
40
|
+
// Task 06: AutoRetry — signal retry policy only if coordinator path is active
|
|
41
|
+
if (hookInput.swarmCoordinator || hookInput.coordinator || hookInput.useRetry) {
|
|
42
|
+
console.log('[AUTO_RETRY_ENABLED] maxAttempts=3 strategy=exponential-backoff backoffMs=1000');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (router && prompt) {
|
|
46
|
+
var routeFn = router.routeTaskSemantic || router.routeTask;
|
|
47
|
+
var result = await Promise.resolve(routeFn(prompt));
|
|
48
|
+
console.log('[INFO] Task routed to: ' + result.agent + ' (confidence: ' + result.confidence + ')');
|
|
49
|
+
} else {
|
|
50
|
+
console.log('[OK] Task started');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Task 24: PromptVersioning — resolve prompt variant before agent spawn
|
|
54
|
+
try {
|
|
55
|
+
var memMod = await import('@monomind/memory');
|
|
56
|
+
if (memMod && memMod.PromptVersionStore) {
|
|
57
|
+
var pvStore = new memMod.PromptVersionStore(path.join(CWD, '.monomind', 'prompt-versions'));
|
|
58
|
+
var pvMod = await import('file://' + path.join(CWD, 'packages/@monomind/cli/dist/src/agents/prompt-experiment.js'));
|
|
59
|
+
if (pvMod && pvMod.PromptExperimentRouter) {
|
|
60
|
+
var pvRouter = new pvMod.PromptExperimentRouter(pvStore);
|
|
61
|
+
var agentSlug24 = hookInput.agentSlug || hookInput.agentType || hookInput.agent_type || 'unknown';
|
|
62
|
+
if (agentSlug24 !== 'unknown') {
|
|
63
|
+
var resolved = pvRouter.resolvePromptForSpawn(agentSlug24);
|
|
64
|
+
if (resolved.version) {
|
|
65
|
+
console.log('[PROMPT_VERSION] ' + agentSlug24 + ' v' + resolved.version + (resolved.isCandidate ? ' (experiment candidate)' : ''));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (e) { /* not available or no experiment */ }
|
|
71
|
+
|
|
72
|
+
// Monograph impact — detect changed files and surface their dependents
|
|
73
|
+
try {
|
|
74
|
+
var mgDbPath1 = path.join(CWD, '.monomind', 'monograph.db');
|
|
75
|
+
if (fs.existsSync(mgDbPath1)) {
|
|
76
|
+
var execSync1 = require('child_process').execSync;
|
|
77
|
+
var changedFiles1 = '';
|
|
78
|
+
try { changedFiles1 = execSync1('git diff --name-only HEAD 2>/dev/null || git diff --name-only 2>/dev/null', { cwd: CWD, timeout: 3000, shell: true }).toString().trim(); } catch(e) {}
|
|
79
|
+
if (changedFiles1) {
|
|
80
|
+
var mgMod1 = null;
|
|
81
|
+
mgMod1 = hCtx._requireMonograph();
|
|
82
|
+
if (mgMod1 && mgMod1.openDb) {
|
|
83
|
+
var db1 = mgMod1.openDb(mgDbPath1);
|
|
84
|
+
try {
|
|
85
|
+
var fileList1 = changedFiles1.split('\n').filter(Boolean).slice(0, 8);
|
|
86
|
+
var impacted1 = [];
|
|
87
|
+
for (var fi = 0; fi < fileList1.length; fi++) {
|
|
88
|
+
var fBase = path.basename(fileList1[fi]);
|
|
89
|
+
var fNode = db1.prepare("SELECT id, name, label FROM nodes WHERE file_path LIKE ? LIMIT 1").get('%' + fBase);
|
|
90
|
+
if (fNode) {
|
|
91
|
+
var fImporters = db1.prepare(
|
|
92
|
+
'SELECT n2.name FROM edges e JOIN nodes n2 ON n2.id = e.source_id WHERE e.target_id = ? LIMIT 5'
|
|
93
|
+
).all(fNode.id);
|
|
94
|
+
var entry = fNode.name + ' (' + fNode.label + ')';
|
|
95
|
+
if (fImporters.length) entry += ' ← ' + fImporters.map(function(i){ return i.name; }).join(', ');
|
|
96
|
+
impacted1.push(entry);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (impacted1.length > 0) {
|
|
100
|
+
console.log('[MONOGRAPH_IMPACT] Changed files and their dependents: ' + impacted1.join(' | '));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Effective blast radius — second pass using first impacted node
|
|
104
|
+
try {
|
|
105
|
+
if (fileList1.length > 0 && mgMod1.effectiveBlastRadius) {
|
|
106
|
+
var firstFile1 = path.basename(fileList1[0]);
|
|
107
|
+
var firstNode1 = db1.prepare("SELECT id, name, label FROM nodes WHERE file_path LIKE ? LIMIT 1").get('%' + firstFile1);
|
|
108
|
+
if (firstNode1) {
|
|
109
|
+
var blastResults1 = mgMod1.effectiveBlastRadius(db1, firstNode1.id, { maxDepth: 4 });
|
|
110
|
+
if (blastResults1 && blastResults1.length > 0) {
|
|
111
|
+
var bwdCount1 = blastResults1.filter(function(r){ return r.direction === 'backward' || r.direction === 'both'; }).length;
|
|
112
|
+
var fwdCount1 = blastResults1.filter(function(r){ return r.direction === 'forward' || r.direction === 'both'; }).length;
|
|
113
|
+
var topBlast1 = blastResults1.slice(0, 8).map(function(r){
|
|
114
|
+
return '[' + r.direction + ':' + r.hops + '] ' + r.nodeName + ' (' + r.nodeLabel + ')';
|
|
115
|
+
});
|
|
116
|
+
console.log('[MONOGRAPH_BLAST_RADIUS] Node: ' + firstNode1.name + ' | forward=' + fwdCount1 + ' backward=' + bwdCount1 + ' | ' + topBlast1.join(', '));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} catch(blastErr) { /* non-fatal */ }
|
|
121
|
+
} finally { if (mgMod1.closeDb) mgMod1.closeDb(db1); }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch(e) { /* non-fatal */ }
|
|
126
|
+
|
|
127
|
+
// Bridge to @monomind/hooks registry — fires Tasks 26 (PromptAssembler) and any other PreTask hooks
|
|
128
|
+
var _hooksModule = hCtx._hooksModule;
|
|
129
|
+
if (_hooksModule && _hooksModule.executeHooks && _hooksModule.HookEvent) {
|
|
130
|
+
try {
|
|
131
|
+
await _hooksModule.executeHooks(_hooksModule.HookEvent.PreTask, {
|
|
132
|
+
task: typeof prompt === 'string' ? { description: prompt, id: hookInput.taskId || '' } : null,
|
|
133
|
+
sessionId: hookInput.sessionId || hookInput.session_id || 'default',
|
|
134
|
+
}, { continueOnError: true, timeout: 2000 });
|
|
135
|
+
} catch (e) { /* non-fatal */ }
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
handlePostTask: async function(hCtx) {
|
|
140
|
+
var hookInput = hCtx.hookInput;
|
|
141
|
+
var prompt = hCtx.prompt;
|
|
142
|
+
var intelligence = hCtx.intelligence;
|
|
143
|
+
var CWD = hCtx.CWD;
|
|
144
|
+
|
|
145
|
+
var taskSuccess = hookInput.success !== false && hookInput.status !== 'failed';
|
|
146
|
+
if (intelligence && intelligence.feedback) {
|
|
147
|
+
try {
|
|
148
|
+
intelligence.feedback(true);
|
|
149
|
+
} catch (e) { /* non-fatal */ }
|
|
150
|
+
}
|
|
151
|
+
// Each TeammateIdle/TaskCompleted = one agent done → remove oldest registration (FIFO)
|
|
152
|
+
const regDir = path.join(CWD, '.monomind', 'agents', 'registrations');
|
|
153
|
+
try {
|
|
154
|
+
if (fs.existsSync(regDir)) {
|
|
155
|
+
const files = fs.readdirSync(regDir).filter(f => f.endsWith('.json'));
|
|
156
|
+
if (files.length > 0) {
|
|
157
|
+
// Sort by mtime ascending (oldest first) and remove the oldest one
|
|
158
|
+
const sorted = files
|
|
159
|
+
.map(f => ({ f, mtime: (() => { try { return fs.statSync(path.join(regDir, f)).mtimeMs; } catch { return 0; } })() }))
|
|
160
|
+
.sort((a, b) => a.mtime - b.mtime);
|
|
161
|
+
try { fs.unlinkSync(path.join(regDir, sorted[0].f)); } catch { /* ignore */ }
|
|
162
|
+
}
|
|
163
|
+
// Also purge any stragglers older than 30 min
|
|
164
|
+
const now = Date.now();
|
|
165
|
+
for (const f of fs.readdirSync(regDir).filter(f => f.endsWith('.json'))) {
|
|
166
|
+
try { if (now - fs.statSync(path.join(regDir, f)).mtimeMs > 30 * 60 * 1000) fs.unlinkSync(path.join(regDir, f)); } catch { /* ignore */ }
|
|
167
|
+
}
|
|
168
|
+
const remaining = fs.readdirSync(regDir).filter(f => f.endsWith('.json')).length;
|
|
169
|
+
const _actPath = path.join(CWD, '.monomind', 'metrics', 'swarm-activity.json');
|
|
170
|
+
let _prevLastActive = 0;
|
|
171
|
+
try { _prevLastActive = (JSON.parse(fs.readFileSync(_actPath, 'utf-8'))?.swarm?.lastActive) || 0; } catch { /* ignore */ }
|
|
172
|
+
fs.writeFileSync(_actPath, JSON.stringify({
|
|
173
|
+
timestamp: new Date().toISOString(),
|
|
174
|
+
swarm: {
|
|
175
|
+
active: remaining > 0,
|
|
176
|
+
agent_count: remaining,
|
|
177
|
+
coordination_active: remaining > 0,
|
|
178
|
+
lastActive: Math.max(remaining, _prevLastActive), // preserve peak across completion
|
|
179
|
+
},
|
|
180
|
+
}));
|
|
181
|
+
}
|
|
182
|
+
} catch (e) { /* non-fatal */ }
|
|
183
|
+
|
|
184
|
+
// Bridge to @monomind/hooks registry — fires Tasks 39 (SpecializationScorer) and any other PostTask hooks
|
|
185
|
+
var _hooksModule = hCtx._hooksModule;
|
|
186
|
+
if (_hooksModule && _hooksModule.executeHooks && _hooksModule.HookEvent) {
|
|
187
|
+
try {
|
|
188
|
+
await _hooksModule.executeHooks(_hooksModule.HookEvent.PostTask, {
|
|
189
|
+
task: {
|
|
190
|
+
id: hookInput.taskId || hookInput.task_id || '',
|
|
191
|
+
status: taskSuccess ? 'completed' : 'failed',
|
|
192
|
+
agentSlug: hookInput.agentSlug || hookInput.agent_slug || 'unknown',
|
|
193
|
+
type: hookInput.taskType || hookInput.task_type || 'general',
|
|
194
|
+
},
|
|
195
|
+
success: taskSuccess,
|
|
196
|
+
latencyMs: hookInput.latencyMs || hookInput.latency_ms || 0,
|
|
197
|
+
qualityScore: hookInput.qualityScore || hookInput.quality_score,
|
|
198
|
+
}, { continueOnError: true, timeout: 2000 });
|
|
199
|
+
} catch (e) { /* non-fatal */ }
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Task 35: TerminationConditions — detect halted swarms via halt-signal
|
|
203
|
+
try {
|
|
204
|
+
var haltMod = await import('file://' + path.join(CWD, 'packages/@monomind/cli/dist/src/agents/halt-signal.js'));
|
|
205
|
+
if (haltMod && haltMod.isHalted) {
|
|
206
|
+
var swarmId35 = hookInput.swarmId || hookInput.swarm_id || 'default';
|
|
207
|
+
if (haltMod.isHalted(swarmId35)) {
|
|
208
|
+
console.warn('[HALT_DETECTED] Swarm ' + swarmId35 + ' has an active halt signal — agents should stop');
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} catch (e) {
|
|
212
|
+
// Try direct file check
|
|
213
|
+
try {
|
|
214
|
+
var haltFile = path.join(CWD, 'data', 'halt-signals.jsonl');
|
|
215
|
+
if (fs.existsSync(haltFile)) {
|
|
216
|
+
var haltLines = fs.readFileSync(haltFile, 'utf-8').trim().split('\n').filter(Boolean);
|
|
217
|
+
if (haltLines.length > 0) {
|
|
218
|
+
console.warn('[HALT_DETECTED] ' + haltLines.length + ' halt signal(s) present');
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
} catch (e2) { /* non-fatal */ }
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Task 37: DeadLetterQueue — enqueue failed tasks when retries exhausted
|
|
225
|
+
try {
|
|
226
|
+
if (!taskSuccess) {
|
|
227
|
+
var dlqMod = await import('file://' + path.join(CWD, 'packages/@monomind/cli/dist/src/dlq/dlq-writer.js'));
|
|
228
|
+
if (dlqMod && dlqMod.DLQWriter) {
|
|
229
|
+
var dlqDir = path.join(CWD, '.monomind', 'dlq');
|
|
230
|
+
var dlqWriter = new dlqMod.DLQWriter(dlqDir);
|
|
231
|
+
dlqWriter.enqueue({
|
|
232
|
+
toolName: 'post-task',
|
|
233
|
+
originalPayload: { taskId: hookInput.taskId || '', agentSlug: hookInput.agentSlug || 'unknown' },
|
|
234
|
+
deliveryAttempts: [{ attempt: 1, timestamp: new Date().toISOString(), error: hookInput.error || 'task failed' }],
|
|
235
|
+
agentId: hookInput.agentSlug || hookInput.agent_slug,
|
|
236
|
+
swarmId: hookInput.swarmId || hookInput.swarm_id,
|
|
237
|
+
});
|
|
238
|
+
console.log('[DLQ_ENQUEUED] Failed task ' + (hookInput.taskId || 'unknown') + ' sent to dead-letter queue');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
} catch (e) { /* non-fatal */ }
|
|
242
|
+
|
|
243
|
+
// Memory Palace task drawer writes removed — use auto-memory files for task context
|
|
244
|
+
|
|
245
|
+
// ── Worker Auto-Dispatch ──────────────────────────────────────────────
|
|
246
|
+
// Auto-dispatch background workers based on task outcome
|
|
247
|
+
try {
|
|
248
|
+
var taskDesc = (typeof prompt === 'string' ? prompt : hookInput.description || '').toLowerCase();
|
|
249
|
+
var workersToDispatch = [];
|
|
250
|
+
|
|
251
|
+
// Always consolidate memory after any task
|
|
252
|
+
workersToDispatch.push('consolidate');
|
|
253
|
+
|
|
254
|
+
// Security-related task → dispatch audit worker
|
|
255
|
+
if (/\b(security|auth|vuln|cve|threat|token|permission|crypto)\b/.test(taskDesc)) {
|
|
256
|
+
workersToDispatch.push('audit');
|
|
257
|
+
}
|
|
258
|
+
// Performance-related → dispatch benchmark worker
|
|
259
|
+
if (/\b(performance|optimiz|benchmark|latency|throughput)\b/.test(taskDesc)) {
|
|
260
|
+
workersToDispatch.push('benchmark');
|
|
261
|
+
}
|
|
262
|
+
// Code changes → dispatch testgaps worker
|
|
263
|
+
if (/\b(implement|feature|refactor|fix|build|add|create|modify)\b/.test(taskDesc)) {
|
|
264
|
+
workersToDispatch.push('testgaps');
|
|
265
|
+
}
|
|
266
|
+
// Any significant task → dispatch map worker for codebase indexing
|
|
267
|
+
if (taskDesc.length > 50) {
|
|
268
|
+
workersToDispatch.push('map');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Dispatch via @monomind/hooks if available, otherwise write dispatch file
|
|
272
|
+
if (workersToDispatch.length > 0) {
|
|
273
|
+
var dispatchDir = path.join(CWD, '.monomind', 'worker-dispatch');
|
|
274
|
+
fs.mkdirSync(dispatchDir, { recursive: true });
|
|
275
|
+
var dispatchPayload = {
|
|
276
|
+
workers: workersToDispatch,
|
|
277
|
+
trigger: 'post-task',
|
|
278
|
+
taskDesc: taskDesc.substring(0, 100),
|
|
279
|
+
success: taskSuccess,
|
|
280
|
+
timestamp: new Date().toISOString(),
|
|
281
|
+
};
|
|
282
|
+
fs.writeFileSync(
|
|
283
|
+
path.join(dispatchDir, 'pending-' + Date.now() + '-' + Math.random().toString(36).slice(2, 7) + '.json'),
|
|
284
|
+
JSON.stringify(dispatchPayload), 'utf-8'
|
|
285
|
+
);
|
|
286
|
+
console.log('[WORKER_DISPATCH] Queued: ' + workersToDispatch.join(', '));
|
|
287
|
+
}
|
|
288
|
+
} catch (e) { /* non-fatal */ }
|
|
289
|
+
|
|
290
|
+
// ── ADR Auto-Generation ────────────────────────────────────────────────
|
|
291
|
+
// When adr.autoGenerate is true and task involved architect-level work,
|
|
292
|
+
// create an ADR stub in the configured directory
|
|
293
|
+
try {
|
|
294
|
+
var settingsPath = path.join(CWD, '.claude', 'settings.json');
|
|
295
|
+
var adrCfg = {};
|
|
296
|
+
if (fs.existsSync(settingsPath)) {
|
|
297
|
+
var s = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
298
|
+
adrCfg = (s.monomind && s.monomind.adr) || {};
|
|
299
|
+
}
|
|
300
|
+
if (adrCfg.autoGenerate) {
|
|
301
|
+
var taskAgent = hookInput.agentSlug || hookInput.agent_slug || '';
|
|
302
|
+
var taskDescAdr = (typeof prompt === 'string' ? prompt : hookInput.description || '').toLowerCase();
|
|
303
|
+
var isArchitectLevel = ['architect', 'system-architect', 'software-architect'].includes(taskAgent)
|
|
304
|
+
|| /\b(architecture|design decision|adr|trade-?off|migration strategy)\b/.test(taskDescAdr);
|
|
305
|
+
if (isArchitectLevel && taskDescAdr.length > 30) {
|
|
306
|
+
var adrDir = path.join(CWD, adrCfg.directory || 'docs/adrs');
|
|
307
|
+
fs.mkdirSync(adrDir, { recursive: true });
|
|
308
|
+
var adrNum = (fs.readdirSync(adrDir).filter(function(f) { return f.endsWith('.md'); }).length + 1)
|
|
309
|
+
.toString().padStart(4, '0');
|
|
310
|
+
var adrTitle = taskDescAdr.substring(0, 60).replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
311
|
+
var adrFile = path.join(adrDir, 'ADR-' + adrNum + '-' + adrTitle + '.md');
|
|
312
|
+
if (!fs.existsSync(adrFile)) {
|
|
313
|
+
var adrContent = '# ADR-' + adrNum + ': ' + (typeof prompt === 'string' ? prompt.substring(0, 80) : adrTitle) + '\n\n'
|
|
314
|
+
+ '**Date:** ' + new Date().toISOString().slice(0, 10) + '\n'
|
|
315
|
+
+ '**Status:** Accepted\n'
|
|
316
|
+
+ '**Agent:** ' + (taskAgent || 'unknown') + '\n\n'
|
|
317
|
+
+ '## Context\n\nAuto-generated from task completion.\n\n'
|
|
318
|
+
+ '## Decision\n\n_Fill in the decision made._\n\n'
|
|
319
|
+
+ '## Consequences\n\n_Fill in the consequences._\n';
|
|
320
|
+
fs.writeFileSync(adrFile, adrContent, 'utf-8');
|
|
321
|
+
console.log('[ADR_GENERATED] ' + path.basename(adrFile));
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
} catch (e) { /* non-fatal */ }
|
|
326
|
+
|
|
327
|
+
console.log('[OK] Task completed');
|
|
328
|
+
}
|
|
329
|
+
};
|