moflo 4.8.9 → 4.8.11
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/agents/core/coder.md +265 -265
- package/.claude/agents/core/planner.md +167 -167
- package/.claude/agents/core/researcher.md +189 -189
- package/.claude/agents/core/reviewer.md +325 -325
- package/.claude/agents/core/tester.md +318 -318
- package/.claude/agents/dual-mode/codex-coordinator.md +224 -224
- package/.claude/agents/dual-mode/codex-worker.md +211 -211
- package/.claude/agents/dual-mode/dual-orchestrator.md +291 -291
- package/.claude/agents/github/code-review-swarm.md +537 -537
- package/.claude/agents/github/github-modes.md +172 -172
- package/.claude/agents/github/issue-tracker.md +318 -318
- package/.claude/agents/github/multi-repo-swarm.md +552 -552
- package/.claude/agents/github/pr-manager.md +190 -190
- package/.claude/agents/github/project-board-sync.md +508 -508
- package/.claude/agents/github/release-manager.md +366 -366
- package/.claude/agents/github/release-swarm.md +582 -582
- package/.claude/agents/github/repo-architect.md +397 -397
- package/.claude/agents/github/swarm-issue.md +572 -572
- package/.claude/agents/github/swarm-pr.md +427 -427
- package/.claude/agents/github/sync-coordinator.md +451 -451
- package/.claude/agents/github/workflow-automation.md +634 -634
- package/.claude/agents/goal/code-goal-planner.md +445 -445
- package/.claude/agents/hive-mind/collective-intelligence-coordinator.md +129 -129
- package/.claude/agents/hive-mind/queen-coordinator.md +202 -202
- package/.claude/agents/hive-mind/scout-explorer.md +241 -241
- package/.claude/agents/hive-mind/swarm-memory-manager.md +192 -192
- package/.claude/agents/hive-mind/worker-specialist.md +216 -216
- package/.claude/agents/neural/safla-neural.md +73 -73
- package/.claude/agents/reasoning/goal-planner.md +72 -72
- package/.claude/agents/swarm/adaptive-coordinator.md +395 -395
- package/.claude/agents/swarm/hierarchical-coordinator.md +326 -326
- package/.claude/agents/swarm/mesh-coordinator.md +391 -391
- package/.claude/agents/templates/migration-plan.md +745 -745
- package/.claude/commands/agents/agent-spawning.md +28 -28
- package/.claude/commands/analysis/COMMAND_COMPLIANCE_REPORT.md +53 -53
- package/.claude/commands/analysis/bottleneck-detect.md +162 -162
- package/.claude/commands/analysis/performance-bottlenecks.md +58 -58
- package/.claude/commands/analysis/token-efficiency.md +44 -44
- package/.claude/commands/automation/auto-agent.md +122 -122
- package/.claude/commands/automation/self-healing.md +105 -105
- package/.claude/commands/automation/session-memory.md +89 -89
- package/.claude/commands/automation/smart-agents.md +72 -72
- package/.claude/commands/coordination/init.md +44 -44
- package/.claude/commands/coordination/orchestrate.md +43 -43
- package/.claude/commands/coordination/spawn.md +45 -45
- package/.claude/commands/coordination/swarm-init.md +85 -85
- package/.claude/commands/github/github-modes.md +146 -146
- package/.claude/commands/github/github-swarm.md +121 -121
- package/.claude/commands/github/issue-tracker.md +291 -291
- package/.claude/commands/github/pr-manager.md +169 -169
- package/.claude/commands/github/release-manager.md +337 -337
- package/.claude/commands/github/repo-architect.md +366 -366
- package/.claude/commands/github/sync-coordinator.md +300 -300
- package/.claude/commands/memory/neural.md +47 -47
- package/.claude/commands/monitoring/agents.md +44 -44
- package/.claude/commands/monitoring/status.md +46 -46
- package/.claude/commands/optimization/auto-topology.md +61 -61
- package/.claude/commands/optimization/parallel-execution.md +49 -49
- package/.claude/commands/sparc/analyzer.md +51 -51
- package/.claude/commands/sparc/architect.md +53 -53
- package/.claude/commands/sparc/ask.md +97 -97
- package/.claude/commands/sparc/batch-executor.md +54 -54
- package/.claude/commands/sparc/code.md +89 -89
- package/.claude/commands/sparc/coder.md +54 -54
- package/.claude/commands/sparc/debug.md +83 -83
- package/.claude/commands/sparc/debugger.md +54 -54
- package/.claude/commands/sparc/designer.md +53 -53
- package/.claude/commands/sparc/devops.md +109 -109
- package/.claude/commands/sparc/docs-writer.md +80 -80
- package/.claude/commands/sparc/documenter.md +54 -54
- package/.claude/commands/sparc/innovator.md +54 -54
- package/.claude/commands/sparc/integration.md +83 -83
- package/.claude/commands/sparc/mcp.md +117 -117
- package/.claude/commands/sparc/memory-manager.md +54 -54
- package/.claude/commands/sparc/optimizer.md +54 -54
- package/.claude/commands/sparc/orchestrator.md +131 -131
- package/.claude/commands/sparc/post-deployment-monitoring-mode.md +83 -83
- package/.claude/commands/sparc/refinement-optimization-mode.md +83 -83
- package/.claude/commands/sparc/researcher.md +54 -54
- package/.claude/commands/sparc/reviewer.md +54 -54
- package/.claude/commands/sparc/security-review.md +80 -80
- package/.claude/commands/sparc/sparc-modes.md +174 -174
- package/.claude/commands/sparc/sparc.md +111 -111
- package/.claude/commands/sparc/spec-pseudocode.md +80 -80
- package/.claude/commands/sparc/supabase-admin.md +348 -348
- package/.claude/commands/sparc/swarm-coordinator.md +54 -54
- package/.claude/commands/sparc/tdd.md +54 -54
- package/.claude/commands/sparc/tester.md +54 -54
- package/.claude/commands/sparc/tutorial.md +79 -79
- package/.claude/commands/sparc/workflow-manager.md +54 -54
- package/.claude/commands/sparc.md +166 -166
- package/.claude/commands/swarm/analysis.md +95 -95
- package/.claude/commands/swarm/development.md +96 -96
- package/.claude/commands/swarm/examples.md +168 -168
- package/.claude/commands/swarm/maintenance.md +102 -102
- package/.claude/commands/swarm/optimization.md +117 -117
- package/.claude/commands/swarm/research.md +136 -136
- package/.claude/commands/swarm/testing.md +131 -131
- package/.claude/commands/training/neural-patterns.md +73 -73
- package/.claude/commands/training/specialization.md +62 -62
- package/.claude/commands/workflows/development.md +77 -77
- package/.claude/commands/workflows/research.md +62 -62
- package/.claude/guidance/{agent-bootstrap.md → shipped/agent-bootstrap.md} +126 -126
- package/.claude/guidance/{guidance-memory-strategy.md → shipped/guidance-memory-strategy.md} +262 -262
- package/.claude/guidance/{memory-strategy.md → shipped/memory-strategy.md} +204 -204
- package/.claude/guidance/{moflo.md → shipped/moflo.md} +45 -31
- package/.claude/guidance/{task-swarm-integration.md → shipped/task-swarm-integration.md} +441 -348
- package/.claude/helpers/gate.cjs +236 -236
- package/.claude/helpers/hook-handler.cjs +42 -46
- package/.claude/settings.json +2 -2
- package/.claude/settings.local.json +3 -3
- package/.claude/skills/fl/SKILL.md +29 -23
- package/.claude/skills/flo/SKILL.md +29 -23
- package/.claude/skills/github-code-review/SKILL.md +4 -4
- package/.claude/skills/github-multi-repo/SKILL.md +8 -8
- package/.claude/skills/github-project-management/SKILL.md +6 -6
- package/.claude/skills/github-release-management/SKILL.md +12 -12
- package/.claude/skills/github-workflow-automation/SKILL.md +6 -6
- package/.claude/skills/hooks-automation/SKILL.md +1201 -1201
- package/.claude/skills/performance-analysis/SKILL.md +563 -563
- package/.claude/skills/sparc-methodology/SKILL.md +64 -64
- package/.claude/skills/swarm-advanced/SKILL.md +77 -77
- package/.claude-plugin/README.md +3 -3
- package/.claude-plugin/docs/PLUGIN_SUMMARY.md +3 -3
- package/.claude-plugin/docs/QUICKSTART.md +4 -4
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +3 -3
- package/.claude-plugin/scripts/install.sh +9 -9
- package/.claude-plugin/scripts/verify.sh +7 -7
- package/README.md +311 -116
- package/bin/gate-hook.mjs +50 -0
- package/bin/gate.cjs +138 -0
- package/bin/hook-handler.cjs +83 -0
- package/bin/hooks.mjs +72 -12
- package/bin/index-guidance.mjs +28 -34
- package/bin/index-tests.mjs +710 -0
- package/bin/lib/process-manager.mjs +243 -0
- package/bin/lib/registry-cleanup.cjs +41 -0
- package/bin/prompt-hook.mjs +72 -0
- package/bin/semantic-search.mjs +473 -441
- package/bin/session-start-launcher.mjs +81 -31
- package/bin/setup-project.mjs +13 -10
- package/package.json +4 -2
- package/src/@claude-flow/cli/README.md +1 -1
- package/src/@claude-flow/cli/bin/cli.js +175 -175
- package/src/@claude-flow/cli/dist/src/commands/doctor.js +1091 -736
- package/src/@claude-flow/cli/dist/src/commands/github.d.ts +12 -0
- package/src/@claude-flow/cli/dist/src/commands/github.js +505 -0
- package/src/@claude-flow/cli/dist/src/commands/hive-mind.js +90 -90
- package/src/@claude-flow/cli/dist/src/commands/index.d.ts +1 -0
- package/src/@claude-flow/cli/dist/src/commands/index.js +7 -0
- package/src/@claude-flow/cli/dist/src/config-adapter.js +1 -1
- package/src/@claude-flow/cli/dist/src/init/claudemd-generator.js +1 -1
- package/src/@claude-flow/cli/dist/src/init/executor.js +109 -5
- package/src/@claude-flow/cli/dist/src/init/helpers-generator.d.ts +14 -0
- package/src/@claude-flow/cli/dist/src/init/helpers-generator.js +156 -24
- package/src/@claude-flow/cli/dist/src/init/mcp-generator.js +20 -20
- package/src/@claude-flow/cli/dist/src/init/moflo-init.d.ts +7 -0
- package/src/@claude-flow/cli/dist/src/init/moflo-init.js +72 -10
- package/src/@claude-flow/cli/dist/src/init/settings-generator.js +23 -14
- package/src/@claude-flow/cli/dist/src/mcp-server.js +3 -3
- package/src/@claude-flow/cli/dist/src/plugins/manager.js +9 -8
- package/src/@claude-flow/cli/dist/src/services/worker-daemon.d.ts +1 -0
- package/src/@claude-flow/cli/dist/src/services/worker-daemon.js +3 -1
- package/src/@claude-flow/cli/dist/src/services/workflow-gate.js +10 -10
- package/src/@claude-flow/cli/package.json +1 -1
package/bin/gate.cjs
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
|
|
6
|
+
var PROJECT_DIR = (process.env.CLAUDE_PROJECT_DIR || process.cwd()).replace(/^\/([a-z])\//i, '$1:/');
|
|
7
|
+
var STATE_FILE = path.join(PROJECT_DIR, '.claude', 'workflow-state.json');
|
|
8
|
+
|
|
9
|
+
function readState() {
|
|
10
|
+
try {
|
|
11
|
+
if (fs.existsSync(STATE_FILE)) return JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8'));
|
|
12
|
+
} catch (e) { /* reset on corruption */ }
|
|
13
|
+
return { tasksCreated: false, taskCount: 0, memorySearched: false, memoryRequired: true, interactionCount: 0, sessionStart: null, lastBlockedAt: null };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function writeState(s) {
|
|
17
|
+
try {
|
|
18
|
+
var dir = path.dirname(STATE_FILE);
|
|
19
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
20
|
+
fs.writeFileSync(STATE_FILE, JSON.stringify(s, null, 2));
|
|
21
|
+
} catch (e) { /* non-fatal */ }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Load moflo.yaml gate config (defaults: all enabled)
|
|
25
|
+
function loadGateConfig() {
|
|
26
|
+
var defaults = { memory_first: true, task_create_first: true, context_tracking: true };
|
|
27
|
+
try {
|
|
28
|
+
var yamlPath = path.join(PROJECT_DIR, 'moflo.yaml');
|
|
29
|
+
if (fs.existsSync(yamlPath)) {
|
|
30
|
+
var content = fs.readFileSync(yamlPath, 'utf-8');
|
|
31
|
+
if (/memory_first:\s*false/i.test(content)) defaults.memory_first = false;
|
|
32
|
+
if (/task_create_first:\s*false/i.test(content)) defaults.task_create_first = false;
|
|
33
|
+
if (/context_tracking:\s*false/i.test(content)) defaults.context_tracking = false;
|
|
34
|
+
}
|
|
35
|
+
} catch (e) { /* use defaults */ }
|
|
36
|
+
return defaults;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
var config = loadGateConfig();
|
|
40
|
+
var command = process.argv[2];
|
|
41
|
+
|
|
42
|
+
var EXEMPT = ['.claude/', '.claude\\', 'CLAUDE.md', 'MEMORY.md', 'workflow-state', 'node_modules'];
|
|
43
|
+
var DANGEROUS = ['rm -rf /', 'format c:', 'del /s /q c:\\', ':(){:|:&};:', 'mkfs.', '> /dev/sda'];
|
|
44
|
+
var DIRECTIVE_RE = /^(yes|no|yeah|yep|nope|sure|ok|okay|correct|right|exactly|perfect)\b/i;
|
|
45
|
+
var TASK_RE = /\b(fix|bug|error|implement|add|create|build|write|refactor|debug|test|feature|issue|security|optimi)\b/i;
|
|
46
|
+
|
|
47
|
+
switch (command) {
|
|
48
|
+
case 'check-before-agent': {
|
|
49
|
+
var s = readState();
|
|
50
|
+
// Hard gate: memory must be searched
|
|
51
|
+
if (config.memory_first && s.memoryRequired && !s.memorySearched) {
|
|
52
|
+
process.stderr.write('BLOCKED: Search memory (mcp__moflo__memory_search) before spawning agents.\n');
|
|
53
|
+
process.exit(2);
|
|
54
|
+
}
|
|
55
|
+
// Soft gate: TaskCreate recommended but not blocking
|
|
56
|
+
// (TaskCreate PostToolUse doesn't fire in Claude Code, so we can't track it reliably)
|
|
57
|
+
if (config.task_create_first && !s.tasksCreated) {
|
|
58
|
+
process.stdout.write('REMINDER: Use TaskCreate before spawning agents. Task tool is blocked until then.\n');
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case 'check-before-scan': {
|
|
63
|
+
if (!config.memory_first) break;
|
|
64
|
+
var s = readState();
|
|
65
|
+
if (s.memorySearched || !s.memoryRequired) break;
|
|
66
|
+
var target = (process.env.TOOL_INPUT_pattern || '') + ' ' + (process.env.TOOL_INPUT_path || '');
|
|
67
|
+
if (EXEMPT.some(function(p) { return target.indexOf(p) >= 0; })) break;
|
|
68
|
+
process.stderr.write('BLOCKED: Search memory before exploring files. Use mcp__moflo__memory_search.\n');
|
|
69
|
+
process.exit(2);
|
|
70
|
+
}
|
|
71
|
+
case 'check-before-read': {
|
|
72
|
+
if (!config.memory_first) break;
|
|
73
|
+
var s = readState();
|
|
74
|
+
if (s.memorySearched || !s.memoryRequired) break;
|
|
75
|
+
var fp = process.env.TOOL_INPUT_file_path || '';
|
|
76
|
+
if (fp.indexOf('.claude/guidance/') < 0 && fp.indexOf('.claude\\guidance\\') < 0) break;
|
|
77
|
+
process.stderr.write('BLOCKED: Search memory before reading guidance files. Use mcp__moflo__memory_search.\n');
|
|
78
|
+
process.exit(2);
|
|
79
|
+
}
|
|
80
|
+
case 'record-task-created': {
|
|
81
|
+
var s = readState();
|
|
82
|
+
s.tasksCreated = true;
|
|
83
|
+
s.taskCount = (s.taskCount || 0) + 1;
|
|
84
|
+
writeState(s);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
case 'record-memory-searched': {
|
|
88
|
+
var s = readState();
|
|
89
|
+
s.memorySearched = true;
|
|
90
|
+
writeState(s);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case 'check-bash-memory': {
|
|
94
|
+
var cmd = process.env.TOOL_INPUT_command || '';
|
|
95
|
+
if (/semantic-search|memory search|memory retrieve|memory-search/.test(cmd)) {
|
|
96
|
+
var s = readState();
|
|
97
|
+
s.memorySearched = true;
|
|
98
|
+
writeState(s);
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case 'check-dangerous-command': {
|
|
103
|
+
var cmd = (process.env.TOOL_INPUT_command || '').toLowerCase();
|
|
104
|
+
for (var i = 0; i < DANGEROUS.length; i++) {
|
|
105
|
+
if (cmd.indexOf(DANGEROUS[i]) >= 0) {
|
|
106
|
+
console.log('[BLOCKED] Dangerous command: ' + DANGEROUS[i]);
|
|
107
|
+
process.exit(2);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case 'prompt-reminder': {
|
|
113
|
+
var s = readState();
|
|
114
|
+
s.memorySearched = false;
|
|
115
|
+
var prompt = process.env.CLAUDE_USER_PROMPT || '';
|
|
116
|
+
s.memoryRequired = prompt.length >= 4 && !DIRECTIVE_RE.test(prompt) && (TASK_RE.test(prompt) || prompt.length > 80);
|
|
117
|
+
s.interactionCount = (s.interactionCount || 0) + 1;
|
|
118
|
+
writeState(s);
|
|
119
|
+
if (!s.tasksCreated) console.log('REMINDER: Use TaskCreate before spawning agents. Task tool is blocked until then.');
|
|
120
|
+
if (config.context_tracking) {
|
|
121
|
+
var ic = s.interactionCount;
|
|
122
|
+
if (ic > 30) console.log('Context: CRITICAL. Commit, store learnings, suggest new session.');
|
|
123
|
+
else if (ic > 20) console.log('Context: DEPLETED. Checkpoint progress. Recommend /compact or fresh session.');
|
|
124
|
+
else if (ic > 10) console.log('Context: MODERATE. Re-state goal before architectural decisions. Use agents for >300 LOC.');
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
case 'compact-guidance': {
|
|
129
|
+
console.log('Pre-Compact: Check CLAUDE.md for rules. Use memory search to recover context after compact.');
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
case 'session-reset': {
|
|
133
|
+
writeState({ tasksCreated: false, taskCount: 0, memorySearched: false, memoryRequired: true, interactionCount: 0, sessionStart: new Date().toISOString(), lastBlockedAt: null });
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
default:
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
|
|
6
|
+
var PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
7
|
+
var METRICS_FILE = path.join(PROJECT_DIR, '.claude-flow', 'metrics', 'learning.json');
|
|
8
|
+
var command = process.argv[2];
|
|
9
|
+
|
|
10
|
+
// Read stdin (Claude Code sends hook data as JSON)
|
|
11
|
+
function readStdin() {
|
|
12
|
+
if (process.stdin.isTTY) return Promise.resolve('');
|
|
13
|
+
return new Promise(function(resolve) {
|
|
14
|
+
var data = '';
|
|
15
|
+
var timer = setTimeout(function() {
|
|
16
|
+
process.stdin.removeAllListeners();
|
|
17
|
+
process.stdin.pause();
|
|
18
|
+
resolve(data);
|
|
19
|
+
}, 500);
|
|
20
|
+
process.stdin.setEncoding('utf8');
|
|
21
|
+
process.stdin.on('data', function(chunk) { data += chunk; });
|
|
22
|
+
process.stdin.on('end', function() { clearTimeout(timer); resolve(data); });
|
|
23
|
+
process.stdin.on('error', function() { clearTimeout(timer); resolve(data); });
|
|
24
|
+
process.stdin.resume();
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function bumpMetric(key) {
|
|
29
|
+
try {
|
|
30
|
+
var metrics = {};
|
|
31
|
+
if (fs.existsSync(METRICS_FILE)) metrics = JSON.parse(fs.readFileSync(METRICS_FILE, 'utf-8'));
|
|
32
|
+
metrics[key] = (metrics[key] || 0) + 1;
|
|
33
|
+
metrics.lastUpdated = new Date().toISOString();
|
|
34
|
+
var dir = path.dirname(METRICS_FILE);
|
|
35
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
36
|
+
fs.writeFileSync(METRICS_FILE, JSON.stringify(metrics, null, 2));
|
|
37
|
+
} catch (e) { /* non-fatal */ }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
readStdin().then(function(stdinData) {
|
|
41
|
+
var hookInput = {};
|
|
42
|
+
if (stdinData && stdinData.trim()) {
|
|
43
|
+
try { hookInput = JSON.parse(stdinData); } catch (e) { /* ignore */ }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
switch (command) {
|
|
47
|
+
case 'route': {
|
|
48
|
+
var prompt = hookInput.prompt || hookInput.command || process.env.PROMPT || '';
|
|
49
|
+
if (prompt) console.log('[INFO] Routing: ' + prompt.substring(0, 80));
|
|
50
|
+
else console.log('[INFO] Ready');
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case 'pre-edit':
|
|
54
|
+
case 'post-edit':
|
|
55
|
+
bumpMetric('edits');
|
|
56
|
+
console.log('[OK] Edit recorded');
|
|
57
|
+
break;
|
|
58
|
+
case 'pre-task':
|
|
59
|
+
bumpMetric('tasks');
|
|
60
|
+
console.log('[OK] Task started');
|
|
61
|
+
break;
|
|
62
|
+
case 'post-task':
|
|
63
|
+
bumpMetric('tasksCompleted');
|
|
64
|
+
console.log('[OK] Task completed');
|
|
65
|
+
break;
|
|
66
|
+
case 'session-end': {
|
|
67
|
+
// Kill tracked background processes via shared sync helper
|
|
68
|
+
try {
|
|
69
|
+
var cleanup = require('./lib/registry-cleanup.cjs');
|
|
70
|
+
var killed = cleanup.killTrackedSync(PROJECT_DIR);
|
|
71
|
+
if (killed > 0) console.log('[CLEANUP] Killed ' + killed + ' background process(es)');
|
|
72
|
+
} catch (e) { /* non-fatal: cleanup module not available */ }
|
|
73
|
+
console.log('[OK] Session ended');
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
case 'notification':
|
|
77
|
+
// Silent — just acknowledge
|
|
78
|
+
break;
|
|
79
|
+
default:
|
|
80
|
+
if (command) console.log('[OK] Hook: ' + command);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
});
|
package/bin/hooks.mjs
CHANGED
|
@@ -23,11 +23,13 @@ import { spawn } from 'child_process';
|
|
|
23
23
|
import { existsSync, appendFileSync, readFileSync, writeFileSync, mkdirSync, statSync } from 'fs';
|
|
24
24
|
import { resolve, dirname } from 'path';
|
|
25
25
|
import { fileURLToPath } from 'url';
|
|
26
|
+
import { createProcessManager } from './lib/process-manager.mjs';
|
|
26
27
|
|
|
27
28
|
const __filename = fileURLToPath(import.meta.url);
|
|
28
29
|
const __dirname = dirname(__filename);
|
|
29
30
|
const projectRoot = resolve(__dirname, '../..');
|
|
30
31
|
const logFile = resolve(projectRoot, '.swarm/hooks.log');
|
|
32
|
+
const pm = createProcessManager(projectRoot);
|
|
31
33
|
|
|
32
34
|
// Parse command line args
|
|
33
35
|
const args = process.argv.slice(2);
|
|
@@ -263,6 +265,8 @@ async function main() {
|
|
|
263
265
|
runIndexGuidanceBackground();
|
|
264
266
|
// Generate structural code map in background
|
|
265
267
|
runCodeMapBackground();
|
|
268
|
+
// Index test files in background
|
|
269
|
+
runTestIndexBackground();
|
|
266
270
|
// Run pretrain in background to extract patterns from repository
|
|
267
271
|
runBackgroundPretrain();
|
|
268
272
|
// Force HNSW rebuild to ensure all processes use identical fresh index
|
|
@@ -318,6 +322,12 @@ async function main() {
|
|
|
318
322
|
}
|
|
319
323
|
|
|
320
324
|
case 'session-end': {
|
|
325
|
+
// Kill all tracked background processes before ending session
|
|
326
|
+
const killResult = pm.killAll();
|
|
327
|
+
if (killResult.killed > 0) {
|
|
328
|
+
log('info', `Killed ${killResult.killed} background process(es) on session end`);
|
|
329
|
+
}
|
|
330
|
+
|
|
321
331
|
// Run ReasoningBank and MicroLoRA training in background on session end
|
|
322
332
|
log('info', 'Session ending - starting background learning...');
|
|
323
333
|
|
|
@@ -397,19 +407,15 @@ async function runIndexGuidance(specificFile = null) {
|
|
|
397
407
|
return 0;
|
|
398
408
|
}
|
|
399
409
|
|
|
400
|
-
// Spawn a background process
|
|
410
|
+
// Spawn a background process via the shared ProcessManager (dedup + PID tracking).
|
|
401
411
|
function spawnWindowless(cmd, args, description) {
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
proc.unref();
|
|
411
|
-
log('info', `Started ${description} (PID: ${proc.pid})`);
|
|
412
|
-
return proc;
|
|
412
|
+
const result = pm.spawn(cmd, args, description);
|
|
413
|
+
if (result.skipped) {
|
|
414
|
+
log('info', `Skipped ${description} (already running, PID: ${result.pid})`);
|
|
415
|
+
} else if (result.pid) {
|
|
416
|
+
log('info', `Started ${description} (PID: ${result.pid})`);
|
|
417
|
+
}
|
|
418
|
+
return result;
|
|
413
419
|
}
|
|
414
420
|
|
|
415
421
|
// Resolve a moflo npm bin script, falling back to local .claude/scripts/ copy
|
|
@@ -431,6 +437,22 @@ function resolveBinOrLocal(binName, localScript) {
|
|
|
431
437
|
|
|
432
438
|
// Run the guidance indexer in background (non-blocking - used for session start and file changes)
|
|
433
439
|
function runIndexGuidanceBackground(specificFile = null) {
|
|
440
|
+
// Check auto_index.guidance flag in moflo.yaml (default: true)
|
|
441
|
+
// Only gate full indexing on session-start; per-file calls from post-edit always run
|
|
442
|
+
if (!specificFile) {
|
|
443
|
+
const yamlPath = resolve(projectRoot, 'moflo.yaml');
|
|
444
|
+
if (existsSync(yamlPath)) {
|
|
445
|
+
try {
|
|
446
|
+
const content = readFileSync(yamlPath, 'utf-8');
|
|
447
|
+
const match = content.match(/auto_index:\s*\n(?:.*\n)*?\s+guidance:\s*(true|false)/);
|
|
448
|
+
if (match && match[1] === 'false') {
|
|
449
|
+
log('info', 'Guidance indexing disabled (auto_index.guidance: false)');
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
} catch { /* ignore, proceed with indexing */ }
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
434
456
|
const indexScript = resolveBinOrLocal('flo-index', 'index-guidance.mjs');
|
|
435
457
|
|
|
436
458
|
if (!indexScript) {
|
|
@@ -447,6 +469,19 @@ function runIndexGuidanceBackground(specificFile = null) {
|
|
|
447
469
|
|
|
448
470
|
// Run structural code map generator in background (non-blocking)
|
|
449
471
|
function runCodeMapBackground() {
|
|
472
|
+
// Check auto_index.code_map flag in moflo.yaml (default: true)
|
|
473
|
+
const yamlPath = resolve(projectRoot, 'moflo.yaml');
|
|
474
|
+
if (existsSync(yamlPath)) {
|
|
475
|
+
try {
|
|
476
|
+
const content = readFileSync(yamlPath, 'utf-8');
|
|
477
|
+
const match = content.match(/auto_index:\s*\n(?:.*\n)*?\s+code_map:\s*(true|false)/);
|
|
478
|
+
if (match && match[1] === 'false') {
|
|
479
|
+
log('info', 'Code map generation disabled (auto_index.code_map: false)');
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
} catch { /* ignore, proceed with indexing */ }
|
|
483
|
+
}
|
|
484
|
+
|
|
450
485
|
const codeMapScript = resolveBinOrLocal('flo-codemap', 'generate-code-map.mjs');
|
|
451
486
|
|
|
452
487
|
if (!codeMapScript) {
|
|
@@ -457,6 +492,31 @@ function runCodeMapBackground() {
|
|
|
457
492
|
spawnWindowless('node', [codeMapScript], 'background code map generation');
|
|
458
493
|
}
|
|
459
494
|
|
|
495
|
+
// Run test file indexer in background (non-blocking)
|
|
496
|
+
function runTestIndexBackground() {
|
|
497
|
+
// Check auto_index.tests flag in moflo.yaml (default: true)
|
|
498
|
+
const yamlPath = resolve(projectRoot, 'moflo.yaml');
|
|
499
|
+
if (existsSync(yamlPath)) {
|
|
500
|
+
try {
|
|
501
|
+
const content = readFileSync(yamlPath, 'utf-8');
|
|
502
|
+
const match = content.match(/auto_index:\s*\n(?:.*\n)*?\s+tests:\s*(true|false)/);
|
|
503
|
+
if (match && match[1] === 'false') {
|
|
504
|
+
log('info', 'Test indexing disabled (auto_index.tests: false)');
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
} catch { /* ignore, proceed with indexing */ }
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const testIndexScript = resolveBinOrLocal('flo-testmap', 'index-tests.mjs');
|
|
511
|
+
|
|
512
|
+
if (!testIndexScript) {
|
|
513
|
+
log('info', 'Test indexer not found (checked npm bin + .claude/scripts/)');
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
spawnWindowless('node', [testIndexScript], 'background test indexing');
|
|
518
|
+
}
|
|
519
|
+
|
|
460
520
|
// Run ReasoningBank + MicroLoRA training + EWC++ consolidation in background (non-blocking)
|
|
461
521
|
function runBackgroundTraining() {
|
|
462
522
|
const localCli = getLocalCliPath();
|
package/bin/index-guidance.mjs
CHANGED
|
@@ -100,11 +100,16 @@ function loadGuidanceDirs() {
|
|
|
100
100
|
|
|
101
101
|
// 2. Include moflo's own bundled guidance (ships with the package)
|
|
102
102
|
// Only when running inside a consumer project (not moflo itself)
|
|
103
|
-
|
|
103
|
+
// Shipped guidance lives in .claude/guidance/shipped/ — internal/ is excluded from npm
|
|
104
|
+
const bundledShippedDir = resolve(mofloRoot, '.claude/guidance/shipped');
|
|
105
|
+
const bundledGuidanceDir = existsSync(bundledShippedDir)
|
|
106
|
+
? bundledShippedDir
|
|
107
|
+
: resolve(mofloRoot, '.claude/guidance');
|
|
104
108
|
const projectGuidanceDir = resolve(projectRoot, '.claude/guidance');
|
|
105
109
|
if (
|
|
106
110
|
existsSync(bundledGuidanceDir) &&
|
|
107
|
-
resolve(bundledGuidanceDir) !== resolve(projectGuidanceDir)
|
|
111
|
+
resolve(bundledGuidanceDir) !== resolve(projectGuidanceDir) &&
|
|
112
|
+
resolve(bundledGuidanceDir) !== resolve(projectGuidanceDir, 'shipped')
|
|
108
113
|
) {
|
|
109
114
|
dirs.push({ path: bundledGuidanceDir, prefix: 'moflo-bundled', absolute: true });
|
|
110
115
|
}
|
|
@@ -689,9 +694,11 @@ function indexDirectory(db, dirConfig) {
|
|
|
689
694
|
|
|
690
695
|
/**
|
|
691
696
|
* Remove stale entries for files that no longer exist on disk.
|
|
692
|
-
*
|
|
697
|
+
* Uses the set of docKeys seen during the current indexing run to determine
|
|
698
|
+
* which entries are stale, rather than reconstructing file paths from keys
|
|
699
|
+
* (which breaks for files in subdirectories).
|
|
693
700
|
*/
|
|
694
|
-
function cleanStaleEntries(db) {
|
|
701
|
+
function cleanStaleEntries(db, currentDocKeys) {
|
|
695
702
|
const docsStmt = db.prepare(
|
|
696
703
|
`SELECT DISTINCT key FROM memory_entries WHERE namespace = ? AND key LIKE 'doc-%'`
|
|
697
704
|
);
|
|
@@ -702,36 +709,19 @@ function cleanStaleEntries(db) {
|
|
|
702
709
|
|
|
703
710
|
let staleCount = 0;
|
|
704
711
|
|
|
705
|
-
// Build a lookup of all indexed directory configs for stale detection
|
|
706
|
-
const prefixToDirMap = {};
|
|
707
|
-
for (const dirConfig of GUIDANCE_DIRS) {
|
|
708
|
-
const dirPath = dirConfig.absolute ? dirConfig.path : resolve(projectRoot, dirConfig.path);
|
|
709
|
-
prefixToDirMap[dirConfig.prefix] = dirPath;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
712
|
for (const { key } of docs) {
|
|
713
|
-
//
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
const chunkPrefix = key.replace('doc-', 'chunk-');
|
|
726
|
-
const countBefore = db.exec(`SELECT COUNT(*) as cnt FROM memory_entries WHERE namespace = '${NAMESPACE}'`)[0]?.values[0][0] || 0;
|
|
727
|
-
db.run(`DELETE FROM memory_entries WHERE namespace = ? AND key LIKE ?`, [NAMESPACE, `${chunkPrefix}%`]);
|
|
728
|
-
db.run(`DELETE FROM memory_entries WHERE namespace = ? AND key = ?`, [NAMESPACE, key]);
|
|
729
|
-
const countAfter = db.exec(`SELECT COUNT(*) as cnt FROM memory_entries WHERE namespace = '${NAMESPACE}'`)[0]?.values[0][0] || 0;
|
|
730
|
-
const removed = countBefore - countAfter;
|
|
731
|
-
if (removed > 0) {
|
|
732
|
-
log(` Removed ${removed} stale entries for deleted file: ${key}`);
|
|
733
|
-
staleCount += removed;
|
|
734
|
-
}
|
|
713
|
+
// If this doc key was seen during the current indexing run, it's not stale
|
|
714
|
+
if (currentDocKeys.has(key)) continue;
|
|
715
|
+
|
|
716
|
+
const chunkPrefix = key.replace('doc-', 'chunk-');
|
|
717
|
+
const countBefore = db.exec(`SELECT COUNT(*) as cnt FROM memory_entries WHERE namespace = '${NAMESPACE}'`)[0]?.values[0][0] || 0;
|
|
718
|
+
db.run(`DELETE FROM memory_entries WHERE namespace = ? AND key LIKE ?`, [NAMESPACE, `${chunkPrefix}%`]);
|
|
719
|
+
db.run(`DELETE FROM memory_entries WHERE namespace = ? AND key = ?`, [NAMESPACE, key]);
|
|
720
|
+
const countAfter = db.exec(`SELECT COUNT(*) as cnt FROM memory_entries WHERE namespace = '${NAMESPACE}'`)[0]?.values[0][0] || 0;
|
|
721
|
+
const removed = countBefore - countAfter;
|
|
722
|
+
if (removed > 0) {
|
|
723
|
+
log(` Removed ${removed} stale entries for deleted file: ${key}`);
|
|
724
|
+
staleCount += removed;
|
|
735
725
|
}
|
|
736
726
|
}
|
|
737
727
|
|
|
@@ -769,6 +759,7 @@ let docsIndexed = 0;
|
|
|
769
759
|
let chunksIndexed = 0;
|
|
770
760
|
let unchanged = 0;
|
|
771
761
|
let errors = 0;
|
|
762
|
+
const currentDocKeys = new Set();
|
|
772
763
|
|
|
773
764
|
if (specificFile) {
|
|
774
765
|
// Index single file
|
|
@@ -801,6 +792,9 @@ if (specificFile) {
|
|
|
801
792
|
const results = indexDirectory(db, dir);
|
|
802
793
|
|
|
803
794
|
for (const result of results) {
|
|
795
|
+
if (result.status === 'indexed' || result.status === 'unchanged') {
|
|
796
|
+
currentDocKeys.add(result.docKey);
|
|
797
|
+
}
|
|
804
798
|
if (result.status === 'indexed') {
|
|
805
799
|
log(` ✅ ${result.docKey} (${result.chunks} chunks)`);
|
|
806
800
|
docsIndexed++;
|
|
@@ -819,7 +813,7 @@ if (specificFile) {
|
|
|
819
813
|
let staleRemoved = 0;
|
|
820
814
|
if (!specificFile) {
|
|
821
815
|
log('Cleaning stale entries for deleted files...');
|
|
822
|
-
staleRemoved = cleanStaleEntries(db);
|
|
816
|
+
staleRemoved = cleanStaleEntries(db, currentDocKeys);
|
|
823
817
|
if (staleRemoved === 0) {
|
|
824
818
|
log(' No stale entries found');
|
|
825
819
|
}
|