moflo 4.8.11 → 4.8.12
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/browser/browser-agent.yaml +182 -0
- package/.claude/guidance/moflo-bootstrap.md +129 -0
- package/.claude/helpers/gate-hook.mjs +50 -0
- package/.claude/helpers/gate.cjs +19 -117
- package/.claude/helpers/hook-handler.cjs +66 -324
- package/.claude/helpers/post-commit +16 -0
- package/.claude/helpers/pre-commit +26 -0
- package/.claude/helpers/prompt-hook.mjs +72 -0
- package/.claude/scripts/build-embeddings.mjs +549 -0
- package/.claude/scripts/generate-code-map.mjs +697 -0
- package/.claude/scripts/hooks.mjs +656 -0
- package/.claude/scripts/index-guidance.mjs +893 -0
- package/.claude/scripts/index-tests.mjs +710 -0
- package/.claude/scripts/semantic-search.mjs +473 -0
- package/.claude/scripts/session-start-launcher.mjs +226 -0
- package/.claude/settings.json +351 -290
- package/.claude/settings.local.json +2 -1
- package/.claude/skills/browser/SKILL.md +204 -0
- package/.claude/workflow-state.json +9 -0
- package/bin/index-guidance.mjs +1 -1
- package/bin/index-tests.mjs +1 -1
- package/bin/semantic-search.mjs +1 -1
- package/bin/setup-project.mjs +61 -64
- package/package.json +2 -2
- package/src/@claude-flow/cli/dist/src/init/claudemd-generator.d.ts +29 -24
- package/src/@claude-flow/cli/dist/src/init/claudemd-generator.js +73 -494
- package/src/@claude-flow/cli/dist/src/init/moflo-init.d.ts +30 -30
- package/src/@claude-flow/cli/dist/src/init/moflo-init.js +712 -717
- package/src/@claude-flow/cli/package.json +106 -106
|
@@ -1,341 +1,83 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const origError = console.error;
|
|
17
|
-
console.log = () => {};
|
|
18
|
-
console.error = () => {};
|
|
19
|
-
try {
|
|
20
|
-
const mod = require(modulePath);
|
|
21
|
-
return mod;
|
|
22
|
-
} finally {
|
|
23
|
-
console.log = origLog;
|
|
24
|
-
console.error = origError;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
} catch (e) {
|
|
28
|
-
// silently fail
|
|
29
|
-
}
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const router = safeRequire(path.join(helpersDir, 'router.cjs'));
|
|
34
|
-
const session = safeRequire(path.join(helpersDir, 'session.cjs'));
|
|
35
|
-
const memory = safeRequire(path.join(helpersDir, 'memory.cjs'));
|
|
36
|
-
const intelligence = safeRequire(path.join(helpersDir, 'intelligence.cjs'));
|
|
37
|
-
|
|
38
|
-
const [,, command, ...args] = process.argv;
|
|
39
|
-
|
|
40
|
-
// Read stdin — Claude Code sends hook data as JSON via stdin
|
|
41
|
-
// Uses a timeout to prevent hanging when stdin is in an ambiguous state
|
|
42
|
-
// (not TTY, not a proper pipe) which happens with Claude Code hook invocations.
|
|
43
|
-
async function readStdin() {
|
|
44
|
-
if (process.stdin.isTTY) return '';
|
|
45
|
-
return new Promise((resolve) => {
|
|
46
|
-
let data = '';
|
|
47
|
-
const timer = setTimeout(() => {
|
|
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() {
|
|
48
16
|
process.stdin.removeAllListeners();
|
|
49
17
|
process.stdin.pause();
|
|
50
18
|
resolve(data);
|
|
51
19
|
}, 500);
|
|
52
20
|
process.stdin.setEncoding('utf8');
|
|
53
|
-
process.stdin.on('data', (chunk)
|
|
54
|
-
process.stdin.on('end', ()
|
|
55
|
-
process.stdin.on('error', ()
|
|
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); });
|
|
56
24
|
process.stdin.resume();
|
|
57
25
|
});
|
|
58
26
|
}
|
|
59
27
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
+
}
|
|
63
39
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
40
|
+
readStdin().then(function(stdinData) {
|
|
41
|
+
var hookInput = {};
|
|
42
|
+
if (stdinData && stdinData.trim()) {
|
|
43
|
+
try { hookInput = JSON.parse(stdinData); } catch (e) { /* ignore */ }
|
|
67
44
|
}
|
|
68
45
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (router && router.routeTask) {
|
|
84
|
-
const result = router.routeTask(prompt);
|
|
85
|
-
var output = [];
|
|
86
|
-
output.push('[INFO] Routing task: ' + (prompt.substring(0, 80) || '(no prompt)'));
|
|
87
|
-
output.push('');
|
|
88
|
-
output.push('+------------------- Primary Recommendation -------------------+');
|
|
89
|
-
output.push('| Agent: ' + result.agent.padEnd(53) + '|');
|
|
90
|
-
output.push('| Confidence: ' + (result.confidence * 100).toFixed(1) + '%' + ' '.repeat(44) + '|');
|
|
91
|
-
output.push('| Reason: ' + result.reason.substring(0, 53).padEnd(53) + '|');
|
|
92
|
-
output.push('+--------------------------------------------------------------+');
|
|
93
|
-
console.log(output.join('\n'));
|
|
94
|
-
} else {
|
|
95
|
-
console.log('[INFO] Router not available, using default routing');
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
|
|
99
|
-
'pre-bash': () => {
|
|
100
|
-
var cmd = (hookInput.command || prompt).toLowerCase();
|
|
101
|
-
var dangerous = ['rm -rf /', 'format c:', 'del /s /q c:\\', ':(){:|:&};:'];
|
|
102
|
-
for (var i = 0; i < dangerous.length; i++) {
|
|
103
|
-
if (cmd.includes(dangerous[i])) {
|
|
104
|
-
console.error('[BLOCKED] Dangerous command detected: ' + dangerous[i]);
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
console.log('[OK] Command validated');
|
|
109
|
-
},
|
|
110
|
-
|
|
111
|
-
'post-edit': () => {
|
|
112
|
-
if (session && session.metric) {
|
|
113
|
-
try { session.metric('edits'); } catch (e) { /* no active session */ }
|
|
114
|
-
}
|
|
115
|
-
if (intelligence && intelligence.recordEdit) {
|
|
116
|
-
try {
|
|
117
|
-
var file = hookInput.file_path || (hookInput.toolInput && hookInput.toolInput.file_path)
|
|
118
|
-
|| process.env.TOOL_INPUT_file_path || args[0] || '';
|
|
119
|
-
intelligence.recordEdit(file);
|
|
120
|
-
} catch (e) { /* non-fatal */ }
|
|
121
|
-
}
|
|
122
|
-
console.log('[OK] Edit recorded');
|
|
123
|
-
},
|
|
124
|
-
|
|
125
|
-
'session-restore': async () => {
|
|
126
|
-
if (session) {
|
|
127
|
-
var existing = session.restore && session.restore();
|
|
128
|
-
if (!existing) {
|
|
129
|
-
session.start && session.start();
|
|
130
|
-
}
|
|
131
|
-
} else {
|
|
132
|
-
console.log('No session to restore');
|
|
133
|
-
console.log('Session started: session-' + Date.now());
|
|
134
|
-
}
|
|
135
|
-
if (intelligence && intelligence.init) {
|
|
136
|
-
try {
|
|
137
|
-
var result = intelligence.init();
|
|
138
|
-
if (result && result.nodes > 0) {
|
|
139
|
-
console.log('[INTELLIGENCE] Loaded ' + result.nodes + ' patterns, ' + result.edges + ' edges');
|
|
140
|
-
}
|
|
141
|
-
} catch (e) { /* non-fatal */ }
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Auto-index guidance, code map, and patterns on session start
|
|
145
|
-
// Delegates to shared ProcessManager for dedup + PID tracking + cleanup.
|
|
146
|
-
try {
|
|
147
|
-
var projectDir = path.resolve(path.dirname(helpersDir), '..');
|
|
148
|
-
|
|
149
|
-
// Dynamic import of ESM process-manager from CJS context
|
|
150
|
-
var pmMod = await import(
|
|
151
|
-
'file:///' + path.join(projectDir, 'node_modules', 'moflo', 'bin', 'lib', 'process-manager.mjs').replace(/\\/g, '/')
|
|
152
|
-
).catch(function() {
|
|
153
|
-
// Fallback: try local bin/ (for moflo repo itself)
|
|
154
|
-
return import(
|
|
155
|
-
'file:///' + path.join(projectDir, 'bin', 'lib', 'process-manager.mjs').replace(/\\/g, '/')
|
|
156
|
-
);
|
|
157
|
-
}).catch(function() { return null; });
|
|
158
|
-
|
|
159
|
-
if (!pmMod) return; // No process manager available
|
|
160
|
-
|
|
161
|
-
var pm = pmMod.createProcessManager(projectDir);
|
|
162
|
-
|
|
163
|
-
// Kill stale background processes from previous session
|
|
164
|
-
pm.killAll();
|
|
165
|
-
|
|
166
|
-
// Guard: prevent concurrent session-restores from spawning duplicates
|
|
167
|
-
if (pm.isLocked()) return;
|
|
168
|
-
pm.acquireLock();
|
|
169
|
-
|
|
170
|
-
// Read moflo.yaml auto_index flags (default: both true)
|
|
171
|
-
var autoGuidance = true;
|
|
172
|
-
var autoCodeMap = true;
|
|
173
|
-
var mofloConfigPath = path.join(projectDir, 'moflo.yaml');
|
|
174
|
-
var mofloJsonPath = path.join(projectDir, 'moflo.config.json');
|
|
175
|
-
|
|
176
|
-
if (fs.existsSync(mofloConfigPath)) {
|
|
177
|
-
try {
|
|
178
|
-
var content = fs.readFileSync(mofloConfigPath, 'utf-8');
|
|
179
|
-
if (/auto_index:\s*\n\s+guidance:\s*false/i.test(content)) autoGuidance = false;
|
|
180
|
-
if (/auto_index:\s*\n(?:\s+guidance:\s*\w+\n)?\s+code_map:\s*false/i.test(content)) autoCodeMap = false;
|
|
181
|
-
} catch (e) { /* ignore */ }
|
|
182
|
-
} else if (fs.existsSync(mofloJsonPath)) {
|
|
183
|
-
try {
|
|
184
|
-
var config = JSON.parse(fs.readFileSync(mofloJsonPath, 'utf-8'));
|
|
185
|
-
var ai = config.auto_index || config.autoIndex || {};
|
|
186
|
-
if (ai.guidance === false) autoGuidance = false;
|
|
187
|
-
if (ai.code_map === false || ai.codeMap === false) autoCodeMap = false;
|
|
188
|
-
} catch (e) { /* ignore */ }
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Helper: find a moflo bin script by filename
|
|
192
|
-
function findMofloScript(scriptName) {
|
|
193
|
-
var candidates = [
|
|
194
|
-
path.join(projectDir, 'bin', scriptName),
|
|
195
|
-
path.join(projectDir, 'node_modules', 'moflo', 'bin', scriptName),
|
|
196
|
-
];
|
|
197
|
-
for (var i = 0; i < candidates.length; i++) {
|
|
198
|
-
if (fs.existsSync(candidates[i])) return candidates[i];
|
|
199
|
-
}
|
|
200
|
-
try {
|
|
201
|
-
var resolved = require.resolve('moflo/bin/' + scriptName, { paths: [projectDir] });
|
|
202
|
-
if (fs.existsSync(resolved)) return resolved;
|
|
203
|
-
} catch (e) { /* not installed */ }
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// 1. Index guidance docs (with embeddings for semantic search)
|
|
208
|
-
if (autoGuidance) {
|
|
209
|
-
var guidanceScript = findMofloScript('index-guidance.mjs');
|
|
210
|
-
if (guidanceScript) pm.spawn('node', [guidanceScript], 'index-guidance');
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// 2. Generate code map (structural index of source files)
|
|
214
|
-
if (autoCodeMap) {
|
|
215
|
-
var codeMapScript = findMofloScript('generate-code-map.mjs');
|
|
216
|
-
if (codeMapScript) pm.spawn('node', [codeMapScript], 'generate-code-map');
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// 3. Start learning service (pattern research on codebase)
|
|
220
|
-
var learnScript = findMofloScript('../.claude/helpers/learning-service.mjs');
|
|
221
|
-
if (!learnScript) learnScript = findMofloScript('learning-service.mjs');
|
|
222
|
-
if (!learnScript) {
|
|
223
|
-
var localLearn = path.join(projectDir, '.claude', 'helpers', 'learning-service.mjs');
|
|
224
|
-
if (fs.existsSync(localLearn)) learnScript = localLearn;
|
|
225
|
-
}
|
|
226
|
-
if (!learnScript) {
|
|
227
|
-
var nmLearn = path.join(projectDir, 'node_modules', 'moflo', '.claude', 'helpers', 'learning-service.mjs');
|
|
228
|
-
if (fs.existsSync(nmLearn)) learnScript = nmLearn;
|
|
229
|
-
}
|
|
230
|
-
if (learnScript) pm.spawn('node', [learnScript], 'learning-service');
|
|
231
|
-
|
|
232
|
-
} catch (e) { /* non-fatal: session-start indexing is best-effort */ }
|
|
233
|
-
},
|
|
234
|
-
|
|
235
|
-
'session-end': () => {
|
|
236
|
-
// Kill all tracked background processes using shared sync helper.
|
|
237
|
-
// Must be SYNCHRONOUS — process.exit(0) fires in finally{} so async would never resolve.
|
|
238
|
-
var projectDir = path.resolve(path.dirname(helpersDir), '..');
|
|
239
|
-
try {
|
|
240
|
-
var cleanup = require(path.join(projectDir, 'node_modules', 'moflo', 'bin', 'lib', 'registry-cleanup.cjs'));
|
|
241
|
-
var killed = cleanup.killTrackedSync(projectDir);
|
|
242
|
-
if (killed > 0) console.log('[CLEANUP] Killed ' + killed + ' background process(es)');
|
|
243
|
-
} catch (e) {
|
|
244
|
-
// Fallback: try local bin/ (for moflo repo itself)
|
|
245
|
-
try {
|
|
246
|
-
var cleanup2 = require(path.join(projectDir, 'bin', 'lib', 'registry-cleanup.cjs'));
|
|
247
|
-
var killed2 = cleanup2.killTrackedSync(projectDir);
|
|
248
|
-
if (killed2 > 0) console.log('[CLEANUP] Killed ' + killed2 + ' background process(es)');
|
|
249
|
-
} catch (e2) { /* non-fatal */ }
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (intelligence && intelligence.consolidate) {
|
|
253
|
-
try {
|
|
254
|
-
var result = intelligence.consolidate();
|
|
255
|
-
if (result && result.entries > 0) {
|
|
256
|
-
var msg = '[INTELLIGENCE] Consolidated: ' + result.entries + ' entries, ' + result.edges + ' edges';
|
|
257
|
-
if (result.newEntries > 0) msg += ', ' + result.newEntries + ' new';
|
|
258
|
-
msg += ', PageRank recomputed';
|
|
259
|
-
console.log(msg);
|
|
260
|
-
}
|
|
261
|
-
} catch (e) { /* non-fatal */ }
|
|
262
|
-
}
|
|
263
|
-
if (session && session.end) {
|
|
264
|
-
session.end();
|
|
265
|
-
} else {
|
|
266
|
-
console.log('[OK] Session ended');
|
|
267
|
-
}
|
|
268
|
-
},
|
|
269
|
-
|
|
270
|
-
'pre-task': () => {
|
|
271
|
-
if (session && session.metric) {
|
|
272
|
-
try { session.metric('tasks'); } catch (e) { /* no active session */ }
|
|
273
|
-
}
|
|
274
|
-
if (router && router.routeTask && prompt) {
|
|
275
|
-
var result = router.routeTask(prompt);
|
|
276
|
-
console.log('[INFO] Task routed to: ' + result.agent + ' (confidence: ' + result.confidence + ')');
|
|
277
|
-
} else {
|
|
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');
|
|
278
60
|
console.log('[OK] Task started');
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
|
284
68
|
try {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
},
|
|
299
|
-
|
|
300
|
-
'compact-auto': () => {
|
|
301
|
-
console.log('Auto-Compact Guidance (Context Window Full):');
|
|
302
|
-
console.log('CRITICAL: Before compacting, ensure you understand:');
|
|
303
|
-
console.log(' - All agents available in .claude/agents/ directory');
|
|
304
|
-
console.log(' - Concurrent execution patterns from CLAUDE.md');
|
|
305
|
-
console.log(' - Swarm coordination strategies for complex tasks');
|
|
306
|
-
console.log('Apply GOLDEN RULE: Always batch operations in single messages');
|
|
307
|
-
console.log('Auto-compact proceeding with full agent context');
|
|
308
|
-
},
|
|
309
|
-
|
|
310
|
-
'status': () => {
|
|
311
|
-
console.log('[OK] Status check');
|
|
312
|
-
},
|
|
313
|
-
|
|
314
|
-
'stats': () => {
|
|
315
|
-
if (intelligence && intelligence.stats) {
|
|
316
|
-
intelligence.stats(args.includes('--json'));
|
|
317
|
-
} else {
|
|
318
|
-
console.log('[WARN] Intelligence module not available. Run session-restore first.');
|
|
319
|
-
}
|
|
320
|
-
},
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
if (command && handlers[command]) {
|
|
324
|
-
try {
|
|
325
|
-
await handlers[command]();
|
|
326
|
-
} catch (e) {
|
|
327
|
-
console.log('[WARN] Hook ' + command + ' encountered an error: ' + e.message);
|
|
328
|
-
}
|
|
329
|
-
} else if (command) {
|
|
330
|
-
console.log('[OK] Hook: ' + command);
|
|
331
|
-
} else {
|
|
332
|
-
console.log('Usage: hook-handler.cjs <route|pre-bash|post-edit|session-restore|session-end|pre-task|post-task|compact-manual|compact-auto|status|stats>');
|
|
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;
|
|
333
82
|
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
main().catch(function(e) {
|
|
337
|
-
console.log('[WARN] Hook handler error: ' + e.message);
|
|
338
|
-
}).finally(function() {
|
|
339
|
-
// Ensure clean exit for Claude Code hooks
|
|
340
|
-
process.exit(0);
|
|
341
83
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Claude Flow Post-Commit Hook
|
|
3
|
+
# Records commit metrics and trains patterns
|
|
4
|
+
|
|
5
|
+
COMMIT_HASH=$(git rev-parse HEAD)
|
|
6
|
+
COMMIT_MSG=$(git log -1 --pretty=%B)
|
|
7
|
+
|
|
8
|
+
echo "📊 Recording commit metrics..."
|
|
9
|
+
|
|
10
|
+
# Notify claude-flow of commit
|
|
11
|
+
npx moflo hooks notify \
|
|
12
|
+
--message "Commit: $COMMIT_MSG" \
|
|
13
|
+
--level info \
|
|
14
|
+
--metadata '{"hash": "'$COMMIT_HASH'"}' 2>/dev/null || true
|
|
15
|
+
|
|
16
|
+
echo "✅ Commit recorded"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Claude Flow Pre-Commit Hook
|
|
3
|
+
# Validates code quality before commit
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
echo "🔍 Running Claude Flow pre-commit checks..."
|
|
8
|
+
|
|
9
|
+
# Get staged files
|
|
10
|
+
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
|
|
11
|
+
|
|
12
|
+
# Run validation for each staged file
|
|
13
|
+
for FILE in $STAGED_FILES; do
|
|
14
|
+
if [[ "$FILE" =~ \.(ts|js|tsx|jsx)$ ]]; then
|
|
15
|
+
echo " Validating: $FILE"
|
|
16
|
+
npx moflo hooks pre-edit --file "$FILE" --validate-syntax 2>/dev/null || true
|
|
17
|
+
fi
|
|
18
|
+
done
|
|
19
|
+
|
|
20
|
+
# Run tests if available
|
|
21
|
+
if [ -f "package.json" ] && grep -q '"test"' package.json; then
|
|
22
|
+
echo "🧪 Running tests..."
|
|
23
|
+
npm test --if-present 2>/dev/null || echo " Tests skipped or failed"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
echo "✅ Pre-commit checks complete"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import { resolve } from 'path';
|
|
4
|
+
|
|
5
|
+
// Read stdin JSON from Claude Code
|
|
6
|
+
var stdinData = '';
|
|
7
|
+
try {
|
|
8
|
+
stdinData = await new Promise(function(res) {
|
|
9
|
+
var data = '';
|
|
10
|
+
var timeout = setTimeout(function() { res(data); }, 500);
|
|
11
|
+
process.stdin.setEncoding('utf-8');
|
|
12
|
+
process.stdin.on('data', function(chunk) { data += chunk; });
|
|
13
|
+
process.stdin.on('end', function() { clearTimeout(timeout); res(data); });
|
|
14
|
+
process.stdin.on('error', function() { clearTimeout(timeout); res(''); });
|
|
15
|
+
if (process.stdin.isTTY) { clearTimeout(timeout); res(''); }
|
|
16
|
+
});
|
|
17
|
+
} catch (e) { /* no stdin */ }
|
|
18
|
+
|
|
19
|
+
var hookContext = {};
|
|
20
|
+
try { if (stdinData.trim()) hookContext = JSON.parse(stdinData); } catch (e) {}
|
|
21
|
+
|
|
22
|
+
var userPrompt = hookContext.user_prompt || hookContext.prompt || '';
|
|
23
|
+
var env = Object.assign({}, process.env, { CLAUDE_USER_PROMPT: userPrompt });
|
|
24
|
+
|
|
25
|
+
// Run prompt-reminder via gate.cjs
|
|
26
|
+
var projectDir = (env.CLAUDE_PROJECT_DIR || process.cwd()).replace(/^\/([a-z])\//i, '$1:/');
|
|
27
|
+
var gateScript = resolve(projectDir, '.claude/helpers/gate.cjs');
|
|
28
|
+
var output = '';
|
|
29
|
+
try {
|
|
30
|
+
output = execSync('node "' + gateScript + '" prompt-reminder', {
|
|
31
|
+
env: env, encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe']
|
|
32
|
+
});
|
|
33
|
+
} catch (err) { output = (err && err.stdout) || ''; }
|
|
34
|
+
|
|
35
|
+
// Classify prompt for namespace hint
|
|
36
|
+
var lower = userPrompt.toLowerCase();
|
|
37
|
+
|
|
38
|
+
var KNOWLEDGE_ONLY = /\b(knowledge|remember|recall)\b|we (decid|agree|chose|said)/;
|
|
39
|
+
var EXPLICIT_NS = [
|
|
40
|
+
{ pattern: /\b(pattern|convention|best practice|style|coding rule)\b/, ns: 'patterns', label: 'code patterns and conventions' },
|
|
41
|
+
{ pattern: /\b(code.?map|file structure|project structure|directory)\b/, ns: 'code-map', label: 'codebase navigation' },
|
|
42
|
+
];
|
|
43
|
+
var PATTERN_HINTS = [/\b(template|example|similar to|how do we|how should)\b/];
|
|
44
|
+
var DOMAIN_HINTS = [
|
|
45
|
+
/\b(guidance|guide|docs|documentation|rules|how-to)\b/,
|
|
46
|
+
/\b(architecture|design|domain|tenant|migrat|schema|deploy)/,
|
|
47
|
+
/\b(rule|requirement|constraint|compliance)\b/,
|
|
48
|
+
];
|
|
49
|
+
var NAV_PATTERNS = [
|
|
50
|
+
/\b(find|where|which file|look up|locate|endpoint|route|url|path)\b/,
|
|
51
|
+
/\b(class|function|method|component|service|entity|module)\b/,
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
var nsHint = '';
|
|
55
|
+
if (KNOWLEDGE_ONLY.test(lower)) {
|
|
56
|
+
nsHint = 'Memory namespace hint: use "knowledge" for user-directed project decisions.';
|
|
57
|
+
} else {
|
|
58
|
+
var found = EXPLICIT_NS.find(function(e) { return e.pattern.test(lower); });
|
|
59
|
+
if (found) {
|
|
60
|
+
nsHint = 'Memory namespace hint: use "' + found.ns + '" for ' + found.label + '.';
|
|
61
|
+
} else if (DOMAIN_HINTS.some(function(p) { return p.test(lower); })) {
|
|
62
|
+
nsHint = 'Memory namespace hint: search "guidance" and "knowledge" for domain rules and project decisions.';
|
|
63
|
+
} else if (PATTERN_HINTS.some(function(p) { return p.test(lower); })) {
|
|
64
|
+
nsHint = 'Memory namespace hint: use "patterns" for code patterns and conventions.';
|
|
65
|
+
} else if (NAV_PATTERNS.some(function(p) { return p.test(lower); })) {
|
|
66
|
+
nsHint = 'Memory namespace hint: use "code-map" for codebase navigation.';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var parts = [output.trim(), nsHint].filter(Boolean);
|
|
71
|
+
if (parts.length) process.stdout.write(parts.join('\n') + '\n');
|
|
72
|
+
process.exit(0);
|