claude-flow 2.5.0-alpha.139 ā 2.5.0-alpha.141
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/settings.json +3 -2
- package/README.md +50 -55
- package/bin/claude-flow +1 -1
- package/dist/src/cli/commands/hive-mind/pause.js +2 -9
- package/dist/src/cli/commands/hive-mind/pause.js.map +1 -1
- package/dist/src/cli/commands/index.js +1 -114
- package/dist/src/cli/commands/index.js.map +1 -1
- package/dist/src/cli/commands/swarm-spawn.js +5 -33
- package/dist/src/cli/commands/swarm-spawn.js.map +1 -1
- package/dist/src/cli/help-formatter.js.map +1 -1
- package/dist/src/cli/help-text.js +16 -2
- package/dist/src/cli/help-text.js.map +1 -1
- package/dist/src/cli/simple-commands/hooks.js +233 -0
- package/dist/src/cli/simple-commands/hooks.js.map +1 -1
- package/dist/src/cli/validation-helper.js.map +1 -1
- package/dist/src/core/version.js +1 -1
- package/dist/src/hooks/index.js +0 -3
- package/dist/src/hooks/index.js.map +1 -1
- package/dist/src/mcp/claude-flow-tools.js +205 -150
- package/dist/src/mcp/claude-flow-tools.js.map +1 -1
- package/dist/src/mcp/mcp-server.js +125 -0
- package/dist/src/mcp/mcp-server.js.map +1 -1
- package/dist/src/memory/swarm-memory.js +421 -340
- package/dist/src/memory/swarm-memory.js.map +1 -1
- package/dist/src/sdk/query-control.js +293 -139
- package/dist/src/sdk/query-control.js.map +1 -1
- package/dist/src/sdk/session-forking.js +206 -129
- package/dist/src/sdk/session-forking.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/hive-mind/pause.ts +2 -15
- package/src/cli/commands/index.ts +1 -84
- package/src/cli/commands/swarm-spawn.ts +3 -47
- package/src/cli/help-text.js +16 -2
- package/src/cli/simple-cli.ts +0 -1
- package/src/cli/simple-commands/hooks.js +310 -0
- package/src/hooks/index.ts +0 -5
- package/src/mcp/claude-flow-tools.ts +203 -120
- package/src/mcp/mcp-server.js +86 -0
- package/src/sdk/query-control.ts +377 -223
- package/src/sdk/session-forking.ts +312 -207
- package/.claude/commands/coordination/README.md +0 -9
- package/.claude/commands/memory/README.md +0 -9
|
@@ -22,7 +22,6 @@ import { startCommand } from './start.js';
|
|
|
22
22
|
import { statusCommand } from './status.js';
|
|
23
23
|
import { monitorCommand } from './monitor.js';
|
|
24
24
|
import { sessionCommand } from './session.js';
|
|
25
|
-
import { checkpointCommand } from './checkpoint.js';
|
|
26
25
|
|
|
27
26
|
let orchestrator: Orchestrator | null = null;
|
|
28
27
|
let configManager: ConfigManager | null = null;
|
|
@@ -2134,33 +2133,7 @@ Now, please proceed with the task: ${task}`;
|
|
|
2134
2133
|
action: (ctx: CommandContext) => {
|
|
2135
2134
|
const command = ctx.args[0];
|
|
2136
2135
|
|
|
2137
|
-
if (command === '
|
|
2138
|
-
console.log(bold(blue('Checkpoint Management (SDK Integration)')));
|
|
2139
|
-
console.log();
|
|
2140
|
-
console.log('Manage session checkpoints with Git-like time travel for AI sessions.');
|
|
2141
|
-
console.log();
|
|
2142
|
-
console.log(bold('Subcommands:'));
|
|
2143
|
-
console.log(' create <session-id> [description] Create checkpoint for session');
|
|
2144
|
-
console.log(' list <session-id> List checkpoints for session');
|
|
2145
|
-
console.log(' info <checkpoint-id> Get checkpoint details');
|
|
2146
|
-
console.log(' rollback <checkpoint-id> Rollback to checkpoint');
|
|
2147
|
-
console.log(' delete <checkpoint-id> Delete checkpoint');
|
|
2148
|
-
console.log();
|
|
2149
|
-
console.log(bold('Examples:'));
|
|
2150
|
-
console.log(` ${blue('claude-flow checkpoint create')} my-session "Before deployment"`);
|
|
2151
|
-
console.log(` ${blue('claude-flow checkpoint list')} my-session`);
|
|
2152
|
-
console.log(` ${blue('claude-flow checkpoint rollback')} cp-abc123`);
|
|
2153
|
-
console.log();
|
|
2154
|
-
console.log(bold('MCP Tools (Available through claude-flow MCP server):'));
|
|
2155
|
-
console.log(' checkpoint/create Create checkpoint via MCP');
|
|
2156
|
-
console.log(' checkpoint/list List checkpoints via MCP');
|
|
2157
|
-
console.log(' checkpoint/rollback Rollback via MCP');
|
|
2158
|
-
console.log(' session/fork Fork session for parallel exploration');
|
|
2159
|
-
console.log(' session/info Get session info');
|
|
2160
|
-
console.log(' query/pause Pause query with SDK');
|
|
2161
|
-
console.log(' query/resume Resume paused query');
|
|
2162
|
-
console.log();
|
|
2163
|
-
} else if (command === 'claude') {
|
|
2136
|
+
if (command === 'claude') {
|
|
2164
2137
|
console.log(bold(blue('Claude Instance Management')));
|
|
2165
2138
|
console.log();
|
|
2166
2139
|
console.log('Spawn and manage Claude Code instances with specific configurations.');
|
|
@@ -2558,62 +2531,6 @@ Now, please proceed with the task: ${task}`;
|
|
|
2558
2531
|
},
|
|
2559
2532
|
});
|
|
2560
2533
|
|
|
2561
|
-
// Checkpoint command for SDK session management
|
|
2562
|
-
cli.command({
|
|
2563
|
-
name: 'checkpoint',
|
|
2564
|
-
description: 'Manage session checkpoints (Git-like time travel for AI sessions)',
|
|
2565
|
-
subcommands: [
|
|
2566
|
-
{
|
|
2567
|
-
name: 'create',
|
|
2568
|
-
description: 'Create a checkpoint for a session',
|
|
2569
|
-
action: async (ctx: CommandContext) => {
|
|
2570
|
-
const { checkpointCommand } = await import('./checkpoint.js');
|
|
2571
|
-
// Delegate to the checkpoint command implementation
|
|
2572
|
-
await checkpointCommand.parseAsync(['node', 'checkpoint', 'create', ...ctx.args], { from: 'user' });
|
|
2573
|
-
},
|
|
2574
|
-
},
|
|
2575
|
-
{
|
|
2576
|
-
name: 'list',
|
|
2577
|
-
description: 'List checkpoints for a session',
|
|
2578
|
-
action: async (ctx: CommandContext) => {
|
|
2579
|
-
const { checkpointCommand } = await import('./checkpoint.js');
|
|
2580
|
-
await checkpointCommand.parseAsync(['node', 'checkpoint', 'list', ...ctx.args], { from: 'user' });
|
|
2581
|
-
},
|
|
2582
|
-
},
|
|
2583
|
-
{
|
|
2584
|
-
name: 'info',
|
|
2585
|
-
description: 'Get checkpoint information',
|
|
2586
|
-
action: async (ctx: CommandContext) => {
|
|
2587
|
-
const { checkpointCommand } = await import('./checkpoint.js');
|
|
2588
|
-
await checkpointCommand.parseAsync(['node', 'checkpoint', 'info', ...ctx.args], { from: 'user' });
|
|
2589
|
-
},
|
|
2590
|
-
},
|
|
2591
|
-
{
|
|
2592
|
-
name: 'rollback',
|
|
2593
|
-
description: 'Rollback session to a checkpoint',
|
|
2594
|
-
action: async (ctx: CommandContext) => {
|
|
2595
|
-
const { checkpointCommand } = await import('./checkpoint.js');
|
|
2596
|
-
await checkpointCommand.parseAsync(['node', 'checkpoint', 'rollback', ...ctx.args], { from: 'user' });
|
|
2597
|
-
},
|
|
2598
|
-
},
|
|
2599
|
-
{
|
|
2600
|
-
name: 'delete',
|
|
2601
|
-
description: 'Delete a checkpoint',
|
|
2602
|
-
action: async (ctx: CommandContext) => {
|
|
2603
|
-
const { checkpointCommand } = await import('./checkpoint.js');
|
|
2604
|
-
await checkpointCommand.parseAsync(['node', 'checkpoint', 'delete', ...ctx.args], { from: 'user' });
|
|
2605
|
-
},
|
|
2606
|
-
},
|
|
2607
|
-
],
|
|
2608
|
-
action: async (ctx: CommandContext) => {
|
|
2609
|
-
// Show help if no subcommand
|
|
2610
|
-
if (ctx.args.length === 0) {
|
|
2611
|
-
const { checkpointCommand } = await import('./checkpoint.js');
|
|
2612
|
-
checkpointCommand.help();
|
|
2613
|
-
}
|
|
2614
|
-
},
|
|
2615
|
-
});
|
|
2616
|
-
|
|
2617
2534
|
// Add enterprise commands
|
|
2618
2535
|
for (const command of enterpriseCommands) {
|
|
2619
2536
|
cli.command(command);
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Swarm spawning utilities
|
|
3
|
-
* Enhanced with SDK session forking for REAL parallel exploration
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
|
-
import { sessionForking } from '../../sdk/session-forking.js';
|
|
7
|
-
import { checkpointManager } from '../../sdk/checkpoint-manager.js';
|
|
8
|
-
|
|
9
5
|
interface Agent {
|
|
10
6
|
id: string;
|
|
11
7
|
type: string;
|
|
@@ -14,7 +10,6 @@ interface Agent {
|
|
|
14
10
|
task: string;
|
|
15
11
|
parentId?: string;
|
|
16
12
|
terminalId?: string;
|
|
17
|
-
sessionId?: string; // SDK session ID for forking
|
|
18
13
|
}
|
|
19
14
|
|
|
20
15
|
interface SwarmState {
|
|
@@ -22,81 +17,42 @@ interface SwarmState {
|
|
|
22
17
|
objective: string;
|
|
23
18
|
agents: Map<string, Agent>;
|
|
24
19
|
startTime: number;
|
|
25
|
-
sessionId: string; // Main session ID
|
|
26
|
-
forks: string[]; // Forked session IDs
|
|
27
20
|
}
|
|
28
21
|
|
|
29
22
|
const swarmStates = new Map<string, SwarmState>();
|
|
30
23
|
|
|
31
|
-
export
|
|
24
|
+
export function initializeSwarm(swarmId: string, objective: string): void {
|
|
32
25
|
swarmStates.set(swarmId, {
|
|
33
26
|
swarmId: swarmId,
|
|
34
27
|
objective,
|
|
35
28
|
agents: new Map<string, Agent>(),
|
|
36
29
|
startTime: Date.now(),
|
|
37
|
-
sessionId: swarmId, // Use swarmId as main session
|
|
38
|
-
forks: [],
|
|
39
30
|
});
|
|
40
|
-
|
|
41
|
-
// Create initial checkpoint
|
|
42
|
-
console.log('[SWARM] Creating initial checkpoint...');
|
|
43
|
-
// Checkpoint will be created when session starts tracking
|
|
44
31
|
}
|
|
45
32
|
|
|
46
33
|
export async function spawnSwarmAgent(
|
|
47
34
|
swarmId: string,
|
|
48
35
|
agentType: string,
|
|
49
36
|
task: string,
|
|
50
|
-
options?: { fork?: boolean; checkpointBefore?: boolean }
|
|
51
37
|
): Promise<string> {
|
|
52
38
|
const swarm = swarmStates.get(swarmId);
|
|
53
39
|
if (!swarm) {
|
|
54
40
|
throw new Error(`Swarm ${swarmId} not found`);
|
|
55
41
|
}
|
|
56
42
|
|
|
57
|
-
// Checkpoint before spawning if requested
|
|
58
|
-
if (options?.checkpointBefore) {
|
|
59
|
-
try {
|
|
60
|
-
await checkpointManager.createCheckpoint(
|
|
61
|
-
swarm.sessionId,
|
|
62
|
-
`Before spawning ${agentType} agent`
|
|
63
|
-
);
|
|
64
|
-
console.log(`[SWARM] Checkpoint created before spawn`);
|
|
65
|
-
} catch (error) {
|
|
66
|
-
console.log(`[SWARM] Note: Checkpoint creation skipped (session not tracked)`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
43
|
const agentId = `${swarmId}-agent-${Date.now()}`;
|
|
71
|
-
let agentSessionId = swarm.sessionId;
|
|
72
|
-
|
|
73
|
-
// Fork session for parallel exploration if requested
|
|
74
|
-
if (options?.fork) {
|
|
75
|
-
try {
|
|
76
|
-
const fork = await sessionForking.fork(swarm.sessionId, {});
|
|
77
|
-
agentSessionId = fork.sessionId;
|
|
78
|
-
swarm.forks.push(fork.sessionId);
|
|
79
|
-
console.log(`[SWARM] Forked session for agent: ${fork.sessionId.substring(0, 8)}...`);
|
|
80
|
-
} catch (error) {
|
|
81
|
-
console.log(`[SWARM] Note: Fork creation skipped (session not tracked)`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
44
|
const agent: Agent = {
|
|
86
45
|
id: agentId,
|
|
87
46
|
type: agentType,
|
|
88
47
|
status: 'active',
|
|
89
48
|
name: `${agentType}-${agentId}`,
|
|
90
49
|
task: task,
|
|
91
|
-
sessionId: agentSessionId,
|
|
92
50
|
};
|
|
93
51
|
swarm.agents.set(agentId, agent);
|
|
94
52
|
|
|
53
|
+
// In a real implementation, this would spawn actual Claude instances
|
|
95
54
|
console.log(`[SWARM] Spawned ${agentType} agent: ${agentId}`);
|
|
96
|
-
|
|
97
|
-
console.log(`[SWARM] Session: ${agentSessionId.substring(0, 8)}... (forked)`);
|
|
98
|
-
}
|
|
99
|
-
console.log(`[SWARM] Task: ${task}`);
|
|
55
|
+
console.log(`[SWARM] Task: ${task}`);
|
|
100
56
|
|
|
101
57
|
return agentId;
|
|
102
58
|
}
|
package/src/cli/help-text.js
CHANGED
|
@@ -95,11 +95,25 @@ USAGE:
|
|
|
95
95
|
batch <action> Batch operations
|
|
96
96
|
stream-chain <workflow> Stream-JSON chaining for multi-agent pipelines
|
|
97
97
|
|
|
98
|
-
š„ MCP TOOLS
|
|
98
|
+
š„ NEW MCP TOOLS (v2.5.0-alpha.130):
|
|
99
99
|
Available via Claude Code after installing:
|
|
100
100
|
claude mcp add claude-flow npx claude-flow@alpha mcp start
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
mcp__claude-flow__agents_spawn_parallel Spawn agents in parallel (10-20x faster)
|
|
103
|
+
⢠Spawn multiple agents concurrently
|
|
104
|
+
⢠10-20x speedup vs sequential spawning
|
|
105
|
+
⢠Example: 3 agents in 150ms instead of 2250ms
|
|
106
|
+
|
|
107
|
+
mcp__claude-flow__query_control Control running queries in real-time
|
|
108
|
+
⢠Actions: pause, resume, terminate
|
|
109
|
+
⢠Change model mid-execution (Sonnet ā Haiku for cost savings)
|
|
110
|
+
⢠Change permissions dynamically
|
|
111
|
+
⢠Execute commands in query context
|
|
112
|
+
|
|
113
|
+
mcp__claude-flow__query_list List active queries with status
|
|
114
|
+
⢠View all running queries
|
|
115
|
+
⢠Monitor query status and performance
|
|
116
|
+
⢠Filter by active or include history
|
|
103
117
|
|
|
104
118
|
š GET HELP:
|
|
105
119
|
npx claude-flow --help Show this help
|
package/src/cli/simple-cli.ts
CHANGED
|
@@ -58,7 +58,6 @@ USAGE:
|
|
|
58
58
|
agent <subcommand> # Advanced agent management with neural patterns
|
|
59
59
|
sparc <subcommand> # 17 SPARC modes with neural enhancement
|
|
60
60
|
memory <subcommand> # Cross-session persistent memory with neural learning
|
|
61
|
-
checkpoint <subcommand> # SDK session checkpoints (Git-like time travel)
|
|
62
61
|
status # Comprehensive system status with performance metrics
|
|
63
62
|
|
|
64
63
|
š¤ NEURAL AGENT TYPES (ruv-swarm Integration):
|
|
@@ -86,6 +86,17 @@ export async function hooksAction(subArgs, flags) {
|
|
|
86
86
|
await notifyCommand(subArgs, flags);
|
|
87
87
|
break;
|
|
88
88
|
|
|
89
|
+
// NEW: PreToolUse Modification Hooks (v2.0.10+)
|
|
90
|
+
case 'modify-bash':
|
|
91
|
+
await modifyBashCommand(subArgs, flags);
|
|
92
|
+
break;
|
|
93
|
+
case 'modify-file':
|
|
94
|
+
await modifyFileCommand(subArgs, flags);
|
|
95
|
+
break;
|
|
96
|
+
case 'modify-git-commit':
|
|
97
|
+
await modifyGitCommitCommand(subArgs, flags);
|
|
98
|
+
break;
|
|
99
|
+
|
|
89
100
|
default:
|
|
90
101
|
printError(`Unknown hooks command: ${subcommand}`);
|
|
91
102
|
showHooksHelp();
|
|
@@ -1155,6 +1166,282 @@ async function notifyCommand(subArgs, flags) {
|
|
|
1155
1166
|
}
|
|
1156
1167
|
}
|
|
1157
1168
|
|
|
1169
|
+
// ===== PRETOOLUSE MODIFICATION HOOKS (v2.0.10+) =====
|
|
1170
|
+
|
|
1171
|
+
async function modifyBashCommand(subArgs, flags) {
|
|
1172
|
+
// Read JSON from stdin with timeout to detect if data is piped
|
|
1173
|
+
let input = '';
|
|
1174
|
+
let hasInput = false;
|
|
1175
|
+
|
|
1176
|
+
const timeout = setTimeout(() => {
|
|
1177
|
+
if (!hasInput) {
|
|
1178
|
+
console.log('Usage: echo \'{"tool_input":{"command":"your command"}}\' | claude-flow hooks modify-bash');
|
|
1179
|
+
console.log('\nThis hook reads JSON from stdin and outputs modified JSON.');
|
|
1180
|
+
console.log('It is designed for use with Claude Code v2.0.10+ PreToolUse feature.');
|
|
1181
|
+
console.log('\nExample:');
|
|
1182
|
+
console.log(' echo \'{"tool_input":{"command":"rm test.txt"}}\' | claude-flow hooks modify-bash');
|
|
1183
|
+
process.exit(0);
|
|
1184
|
+
}
|
|
1185
|
+
}, 100); // 100ms timeout
|
|
1186
|
+
|
|
1187
|
+
for await (const chunk of process.stdin) {
|
|
1188
|
+
hasInput = true;
|
|
1189
|
+
clearTimeout(timeout);
|
|
1190
|
+
input += chunk;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
if (!input.trim()) {
|
|
1194
|
+
return; // Silently exit if no input
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
const toolInput = JSON.parse(input);
|
|
1198
|
+
const command = toolInput.tool_input?.command || '';
|
|
1199
|
+
|
|
1200
|
+
if (!command) {
|
|
1201
|
+
console.log(input); // Pass through if no command
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
let modifiedCommand = command;
|
|
1206
|
+
const notes = [];
|
|
1207
|
+
|
|
1208
|
+
// 1. Safety: Add -i flag to rm commands
|
|
1209
|
+
if (/^rm\s/.test(command) && !/-[iI]/.test(command)) {
|
|
1210
|
+
modifiedCommand = command.replace(/^rm /, 'rm -i ');
|
|
1211
|
+
notes.push('[Safety: Added -i flag for interactive confirmation]');
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
// 2. Aliases
|
|
1215
|
+
if (/^ll(\s|$)/.test(command)) {
|
|
1216
|
+
modifiedCommand = command.replace(/^ll/, 'ls -lah');
|
|
1217
|
+
notes.push('[Alias: ll ā ls -lah]');
|
|
1218
|
+
} else if (/^la(\s|$)/.test(command)) {
|
|
1219
|
+
modifiedCommand = command.replace(/^la/, 'ls -la');
|
|
1220
|
+
notes.push('[Alias: la ā ls -la]');
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
// 3. Path correction: Redirect test files to /tmp
|
|
1224
|
+
if (/>\s*test.*\.(txt|log|tmp|json|md)/.test(command) && !/\/tmp\//.test(command)) {
|
|
1225
|
+
modifiedCommand = command.replace(/>\s*(test[^/]*\.(txt|log|tmp|json|md))/, '> /tmp/$1');
|
|
1226
|
+
notes.push('[Path: Redirected test file to /tmp/]');
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// 4. Secret detection
|
|
1230
|
+
if (/(password|secret|token|api[-_]?key|auth)/i.test(command) && !/#\s*SECRETS_OK/.test(command)) {
|
|
1231
|
+
notes.push('[Security: Command contains sensitive keywords. Add "# SECRETS_OK" to bypass]');
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// Output modified JSON
|
|
1235
|
+
const output = {
|
|
1236
|
+
...toolInput,
|
|
1237
|
+
tool_input: {
|
|
1238
|
+
...toolInput.tool_input,
|
|
1239
|
+
command: modifiedCommand,
|
|
1240
|
+
},
|
|
1241
|
+
};
|
|
1242
|
+
|
|
1243
|
+
if (notes.length > 0) {
|
|
1244
|
+
output.modification_notes = notes.join(' ');
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
console.log(JSON.stringify(output, null, 2));
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
async function modifyFileCommand(subArgs, flags) {
|
|
1251
|
+
// Read JSON from stdin with timeout to detect if data is piped
|
|
1252
|
+
let input = '';
|
|
1253
|
+
let hasInput = false;
|
|
1254
|
+
|
|
1255
|
+
const timeout = setTimeout(() => {
|
|
1256
|
+
if (!hasInput) {
|
|
1257
|
+
console.log('Usage: echo \'{"tool_input":{"file_path":"your/file.js"}}\' | claude-flow hooks modify-file');
|
|
1258
|
+
console.log('\nThis hook reads JSON from stdin and outputs modified JSON.');
|
|
1259
|
+
console.log('It is designed for use with Claude Code v2.0.10+ PreToolUse feature.');
|
|
1260
|
+
console.log('\nExample:');
|
|
1261
|
+
console.log(' echo \'{"tool_input":{"file_path":"test.js"}}\' | claude-flow hooks modify-file');
|
|
1262
|
+
process.exit(0);
|
|
1263
|
+
}
|
|
1264
|
+
}, 100); // 100ms timeout
|
|
1265
|
+
|
|
1266
|
+
for await (const chunk of process.stdin) {
|
|
1267
|
+
hasInput = true;
|
|
1268
|
+
clearTimeout(timeout);
|
|
1269
|
+
input += chunk;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
if (!input.trim()) {
|
|
1273
|
+
return; // Silently exit if no input
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
const toolInput = JSON.parse(input);
|
|
1277
|
+
const filePath = toolInput.tool_input?.file_path || toolInput.tool_input?.path || '';
|
|
1278
|
+
|
|
1279
|
+
if (!filePath) {
|
|
1280
|
+
console.log(input); // Pass through if no file path
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
let modifiedPath = filePath;
|
|
1285
|
+
let shouldModify = false;
|
|
1286
|
+
const notes = [];
|
|
1287
|
+
|
|
1288
|
+
// 1. Root folder protection
|
|
1289
|
+
const isRootFile = /^[^/]*\.(js|ts|jsx|tsx|py|java|go|rs|cpp|c|h)$/.test(filePath) ||
|
|
1290
|
+
/^test.*\.(txt|log|tmp|json|md)$/.test(filePath) ||
|
|
1291
|
+
/^(temp|tmp|working)/.test(filePath);
|
|
1292
|
+
|
|
1293
|
+
if (isRootFile) {
|
|
1294
|
+
if (/test.*\.(test|spec)\.|\.test\.|\.spec\./.test(filePath)) {
|
|
1295
|
+
modifiedPath = `tests/${filePath}`;
|
|
1296
|
+
shouldModify = true;
|
|
1297
|
+
notes.push('[Organization: Moved test file to /tests/]');
|
|
1298
|
+
} else if (/test.*\.md|temp.*\.md|working.*\.md|scratch.*\.md/.test(filePath)) {
|
|
1299
|
+
modifiedPath = `docs/working/${filePath}`;
|
|
1300
|
+
shouldModify = true;
|
|
1301
|
+
notes.push('[Organization: Moved working document to /docs/working/]');
|
|
1302
|
+
} else if (/\.(js|ts|jsx|tsx|py)$/.test(filePath)) {
|
|
1303
|
+
modifiedPath = `src/${filePath}`;
|
|
1304
|
+
shouldModify = true;
|
|
1305
|
+
notes.push('[Organization: Moved source file to /src/]');
|
|
1306
|
+
} else if (/^(temp|tmp|scratch)/.test(filePath)) {
|
|
1307
|
+
modifiedPath = `/tmp/${filePath}`;
|
|
1308
|
+
shouldModify = true;
|
|
1309
|
+
notes.push('[Organization: Redirected temporary file to /tmp/]');
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// 2. Format hints
|
|
1314
|
+
if (/\.(ts|tsx|js|jsx)$/.test(modifiedPath)) {
|
|
1315
|
+
notes.push('[Tip: Auto-format with Prettier/ESLint recommended]');
|
|
1316
|
+
} else if (/\.py$/.test(modifiedPath)) {
|
|
1317
|
+
notes.push('[Tip: Auto-format with Black/autopep8 recommended]');
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// Output modified JSON
|
|
1321
|
+
const output = {
|
|
1322
|
+
...toolInput,
|
|
1323
|
+
tool_input: {
|
|
1324
|
+
...toolInput.tool_input,
|
|
1325
|
+
},
|
|
1326
|
+
};
|
|
1327
|
+
|
|
1328
|
+
if (shouldModify) {
|
|
1329
|
+
if (toolInput.tool_input.file_path) {
|
|
1330
|
+
output.tool_input.file_path = modifiedPath;
|
|
1331
|
+
} else {
|
|
1332
|
+
output.tool_input.path = modifiedPath;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
if (notes.length > 0) {
|
|
1337
|
+
output.modification_notes = notes.join(' ');
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
console.log(JSON.stringify(output, null, 2));
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
async function modifyGitCommitCommand(subArgs, flags) {
|
|
1344
|
+
// Read JSON from stdin with timeout to detect if data is piped
|
|
1345
|
+
let input = '';
|
|
1346
|
+
let hasInput = false;
|
|
1347
|
+
|
|
1348
|
+
const timeout = setTimeout(() => {
|
|
1349
|
+
if (!hasInput) {
|
|
1350
|
+
console.log('Usage: echo \'{"tool_input":{"command":"git commit -m \\"message\\""}}\' | claude-flow hooks modify-git-commit');
|
|
1351
|
+
console.log('\nThis hook reads JSON from stdin and outputs modified JSON.');
|
|
1352
|
+
console.log('It is designed for use with Claude Code v2.0.10+ PreToolUse feature.');
|
|
1353
|
+
console.log('\nExample:');
|
|
1354
|
+
console.log(' echo \'{"tool_input":{"command":"git commit -m \\"fix bug\\""}}\' | claude-flow hooks modify-git-commit');
|
|
1355
|
+
process.exit(0);
|
|
1356
|
+
}
|
|
1357
|
+
}, 100); // 100ms timeout
|
|
1358
|
+
|
|
1359
|
+
for await (const chunk of process.stdin) {
|
|
1360
|
+
hasInput = true;
|
|
1361
|
+
clearTimeout(timeout);
|
|
1362
|
+
input += chunk;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
if (!input.trim()) {
|
|
1366
|
+
return; // Silently exit if no input
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
const toolInput = JSON.parse(input);
|
|
1370
|
+
const command = toolInput.tool_input?.command || '';
|
|
1371
|
+
|
|
1372
|
+
if (!command || !/git commit/.test(command)) {
|
|
1373
|
+
console.log(input); // Pass through if not git commit
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// Extract commit message
|
|
1378
|
+
const msgMatch = command.match(/-m\s+["']([^"']+)["']/) || command.match(/-m\s+(\S+)/);
|
|
1379
|
+
const commitMsg = msgMatch ? msgMatch[1] : '';
|
|
1380
|
+
|
|
1381
|
+
if (!commitMsg || /^\[(feat|fix|docs|style|refactor|test|chore|perf)\]/.test(commitMsg)) {
|
|
1382
|
+
console.log(input); // Already formatted or no message
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
const notes = [];
|
|
1387
|
+
|
|
1388
|
+
// Get current branch
|
|
1389
|
+
let branch = 'main';
|
|
1390
|
+
let ticket = '';
|
|
1391
|
+
try {
|
|
1392
|
+
const { execSync } = await import('child_process');
|
|
1393
|
+
branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim();
|
|
1394
|
+
|
|
1395
|
+
// Extract JIRA ticket
|
|
1396
|
+
const ticketMatch = branch.match(/[A-Z]+-[0-9]+/);
|
|
1397
|
+
if (ticketMatch) {
|
|
1398
|
+
ticket = ticketMatch[0];
|
|
1399
|
+
}
|
|
1400
|
+
} catch {
|
|
1401
|
+
// Git not available, continue
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
// Detect conventional commit type
|
|
1405
|
+
let type = 'chore';
|
|
1406
|
+
if (/^(add|implement|create|new)/i.test(commitMsg)) type = 'feat';
|
|
1407
|
+
else if (/^(fix|resolve|patch|correct)/i.test(commitMsg)) type = 'fix';
|
|
1408
|
+
else if (/^(update|modify|change|improve)/i.test(commitMsg)) type = 'refactor';
|
|
1409
|
+
else if (/^(doc|documentation|readme)/i.test(commitMsg)) type = 'docs';
|
|
1410
|
+
else if (/^(test|testing|spec)/i.test(commitMsg)) type = 'test';
|
|
1411
|
+
else if (/^(style|format|lint)/i.test(commitMsg)) type = 'style';
|
|
1412
|
+
else if (/^(perf|optimize|speed)/i.test(commitMsg)) type = 'perf';
|
|
1413
|
+
|
|
1414
|
+
// Format message
|
|
1415
|
+
let formattedMsg = ticket
|
|
1416
|
+
? `[${type}] ${commitMsg} (${ticket})`
|
|
1417
|
+
: `[${type}] ${commitMsg}`;
|
|
1418
|
+
|
|
1419
|
+
// Add co-author
|
|
1420
|
+
if (!/Co-Authored-By/.test(command)) {
|
|
1421
|
+
formattedMsg += `\n\nš¤ Generated with Claude Flow\nCo-Authored-By: claude-flow <noreply@ruv.io>`;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
// Replace message in command
|
|
1425
|
+
const modifiedCommand = command.replace(
|
|
1426
|
+
/-m\s+["'][^"']+["']|-m\s+\S+/,
|
|
1427
|
+
`-m "$(cat <<'EOF'\n${formattedMsg}\nEOF\n)"`
|
|
1428
|
+
);
|
|
1429
|
+
|
|
1430
|
+
notes.push(`[Auto-formatted: ${type} type${ticket ? ` + ${ticket}` : ''}]`);
|
|
1431
|
+
|
|
1432
|
+
// Output modified JSON
|
|
1433
|
+
const output = {
|
|
1434
|
+
...toolInput,
|
|
1435
|
+
tool_input: {
|
|
1436
|
+
...toolInput.tool_input,
|
|
1437
|
+
command: modifiedCommand,
|
|
1438
|
+
},
|
|
1439
|
+
modification_notes: notes.join(' '),
|
|
1440
|
+
};
|
|
1441
|
+
|
|
1442
|
+
console.log(JSON.stringify(output, null, 2));
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1158
1445
|
function showHooksHelp() {
|
|
1159
1446
|
console.log('Claude Flow Hooks (with .swarm/memory.db persistence):\n');
|
|
1160
1447
|
|
|
@@ -1194,6 +1481,23 @@ function showHooksHelp() {
|
|
|
1194
1481
|
console.log(' session-restore Load previous session state');
|
|
1195
1482
|
console.log(' notify Custom notifications');
|
|
1196
1483
|
|
|
1484
|
+
console.log('\n===== NEW: PreToolUse Modification Hooks (v2.0.10+) =====');
|
|
1485
|
+
console.log(' modify-bash Modify Bash tool inputs (reads/writes JSON via stdin/stdout)');
|
|
1486
|
+
console.log(' ⢠Safety: Adds -i flag to rm commands');
|
|
1487
|
+
console.log(' ⢠Aliases: ll ā ls -lah, la ā ls -la');
|
|
1488
|
+
console.log(' ⢠Path correction: Redirects test files to /tmp');
|
|
1489
|
+
console.log(' ⢠Secret detection: Warns about sensitive keywords');
|
|
1490
|
+
console.log('');
|
|
1491
|
+
console.log(' modify-file Modify Write/Edit tool inputs (reads/writes JSON via stdin/stdout)');
|
|
1492
|
+
console.log(' ⢠Root folder protection: Moves files to appropriate directories');
|
|
1493
|
+
console.log(' ⢠Organization: Tests ā /tests/, Sources ā /src/, Docs ā /docs/');
|
|
1494
|
+
console.log(' ⢠Format hints: Suggests Prettier, Black, etc.');
|
|
1495
|
+
console.log('');
|
|
1496
|
+
console.log(' modify-git-commit Modify git commit messages (reads/writes JSON via stdin/stdout)');
|
|
1497
|
+
console.log(' ⢠Conventional commits: Auto-adds [feat], [fix], [docs], etc.');
|
|
1498
|
+
console.log(' ⢠Ticket extraction: Extracts JIRA tickets from branch names');
|
|
1499
|
+
console.log(' ⢠Co-author: Adds Claude Flow co-author footer');
|
|
1500
|
+
|
|
1197
1501
|
console.log('\nExamples:');
|
|
1198
1502
|
console.log(' hooks pre-command --command "npm test" --validate-safety true');
|
|
1199
1503
|
console.log(' hooks pre-edit --file "src/app.js" --auto-assign-agents true');
|
|
@@ -1202,12 +1506,18 @@ function showHooksHelp() {
|
|
|
1202
1506
|
console.log(' hooks session-end --generate-summary true --export-metrics true');
|
|
1203
1507
|
console.log(' hooks agent-spawned --name "CodeReviewer" --type "reviewer"');
|
|
1204
1508
|
console.log(' hooks notify --message "Build completed" --level "success"');
|
|
1509
|
+
console.log('');
|
|
1510
|
+
console.log(' # New modification hooks (stdin/stdout JSON):');
|
|
1511
|
+
console.log(' echo \'{"tool_input":{"command":"rm test.txt"}}\' | hooks modify-bash');
|
|
1512
|
+
console.log(' echo \'{"tool_input":{"file_path":"test.js"}}\' | hooks modify-file');
|
|
1513
|
+
console.log(' echo \'{"tool_input":{"command":"git commit -m \\"fix bug\\""}}\' | hooks modify-git-commit');
|
|
1205
1514
|
|
|
1206
1515
|
console.log('\nCompatibility:');
|
|
1207
1516
|
console.log(' ⢠pre-command and pre-bash are aliases');
|
|
1208
1517
|
console.log(' ⢠post-command and post-bash are aliases');
|
|
1209
1518
|
console.log(' ⢠Both --dash-case and camelCase parameters supported');
|
|
1210
1519
|
console.log(' ⢠All parameters from settings.json template supported');
|
|
1520
|
+
console.log(' ⢠New modification hooks work with Claude Code v2.0.10+ PreToolUse feature');
|
|
1211
1521
|
}
|
|
1212
1522
|
|
|
1213
1523
|
export default hooksAction;
|
package/src/hooks/index.ts
CHANGED
|
@@ -14,11 +14,6 @@ export {
|
|
|
14
14
|
initializeAgenticFlowHooks,
|
|
15
15
|
} from '../services/agentic-flow-hooks/index.js';
|
|
16
16
|
|
|
17
|
-
// Export SDK checkpoint manager for hooks
|
|
18
|
-
export { checkpointManager } from '../sdk/checkpoint-manager.js';
|
|
19
|
-
export { queryController } from '../sdk/query-control.js';
|
|
20
|
-
export { sessionForking } from '../sdk/session-forking.js';
|
|
21
|
-
|
|
22
17
|
// Re-export verification system
|
|
23
18
|
export {
|
|
24
19
|
verificationHookManager,
|