@claude-flow/cli 3.0.0-alpha.7 → 3.0.0-alpha.9
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/.agentic-flow/intelligence.json +4 -4
- package/.claude-flow/daemon-state.json +42 -35
- package/.claude-flow/daemon-test.log +0 -0
- package/.claude-flow/daemon.log +0 -0
- package/.claude-flow/daemon2.log +0 -0
- package/.claude-flow/daemon3.log +0 -0
- package/.claude-flow/metrics/codebase-map.json +2 -2
- package/.claude-flow/metrics/consolidation.json +1 -1
- package/.claude-flow/metrics/performance.json +12 -84
- package/.claude-flow/metrics/security-audit.json +1 -1
- package/.claude-flow/metrics/task-metrics.json +3 -3
- package/.claude-flow/metrics/test-gaps.json +1 -1
- package/agents/architect.yaml +1 -1
- package/agents/coder.yaml +1 -1
- package/agents/reviewer.yaml +1 -1
- package/agents/security-architect.yaml +1 -1
- package/agents/tester.yaml +1 -1
- package/dist/src/commands/daemon.d.ts.map +1 -1
- package/dist/src/commands/daemon.js +182 -8
- package/dist/src/commands/daemon.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +21 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/output.d.ts +16 -0
- package/dist/src/output.d.ts.map +1 -1
- package/dist/src/output.js +42 -0
- package/dist/src/output.js.map +1 -1
- package/dist/src/services/worker-daemon.d.ts +29 -2
- package/dist/src/services/worker-daemon.d.ts.map +1 -1
- package/dist/src/services/worker-daemon.js +123 -20
- package/dist/src/services/worker-daemon.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/commands/daemon.ts +211 -8
- package/src/index.ts +25 -1
- package/src/output.ts +47 -1
- package/src/services/worker-daemon.ts +153 -21
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"patterns": {
|
|
3
3
|
"command:": {
|
|
4
|
-
"success": 0.
|
|
5
|
-
"failure": -0.
|
|
4
|
+
"success": 0.9999999972244487,
|
|
5
|
+
"failure": -0.44529050543424387
|
|
6
6
|
}
|
|
7
7
|
},
|
|
8
8
|
"sequences": {},
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"dirPatterns": {},
|
|
11
11
|
"errorPatterns": [],
|
|
12
12
|
"metrics": {
|
|
13
|
-
"totalRoutes":
|
|
14
|
-
"successfulRoutes":
|
|
13
|
+
"totalRoutes": 208,
|
|
14
|
+
"successfulRoutes": 187,
|
|
15
15
|
"routingHistory": []
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -1,51 +1,46 @@
|
|
|
1
1
|
{
|
|
2
|
-
"running":
|
|
3
|
-
"startedAt": "2026-01-07T03:
|
|
2
|
+
"running": false,
|
|
3
|
+
"startedAt": "2026-01-07T03:59:44.097Z",
|
|
4
4
|
"workers": {
|
|
5
5
|
"map": {
|
|
6
|
-
"runCount":
|
|
7
|
-
"successCount":
|
|
6
|
+
"runCount": 0,
|
|
7
|
+
"successCount": 0,
|
|
8
8
|
"failureCount": 0,
|
|
9
|
-
"averageDurationMs": 0
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"lastRun": "2026-01-07T03:17:50.116Z"
|
|
9
|
+
"averageDurationMs": 0,
|
|
10
|
+
"nextRun": "2026-01-07T04:14:44.099Z",
|
|
11
|
+
"isRunning": false
|
|
13
12
|
},
|
|
14
13
|
"audit": {
|
|
15
|
-
"runCount":
|
|
16
|
-
"successCount":
|
|
14
|
+
"runCount": 0,
|
|
15
|
+
"successCount": 0,
|
|
17
16
|
"failureCount": 0,
|
|
18
|
-
"averageDurationMs": 0
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"lastRun": "2026-01-07T03:17:50.115Z"
|
|
17
|
+
"averageDurationMs": 0,
|
|
18
|
+
"nextRun": "2026-01-07T04:01:44.097Z",
|
|
19
|
+
"isRunning": false
|
|
22
20
|
},
|
|
23
21
|
"optimize": {
|
|
24
|
-
"runCount":
|
|
25
|
-
"successCount":
|
|
22
|
+
"runCount": 0,
|
|
23
|
+
"successCount": 0,
|
|
26
24
|
"failureCount": 0,
|
|
27
25
|
"averageDurationMs": 0,
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"lastRun": "2026-01-07T03:07:50.014Z"
|
|
26
|
+
"nextRun": "2026-01-07T04:03:44.097Z",
|
|
27
|
+
"isRunning": false
|
|
31
28
|
},
|
|
32
29
|
"consolidate": {
|
|
33
|
-
"runCount":
|
|
34
|
-
"successCount":
|
|
30
|
+
"runCount": 0,
|
|
31
|
+
"successCount": 0,
|
|
35
32
|
"failureCount": 0,
|
|
36
|
-
"averageDurationMs":
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"lastRun": "2026-01-07T03:07:50.015Z"
|
|
33
|
+
"averageDurationMs": 0,
|
|
34
|
+
"nextRun": "2026-01-07T04:05:44.097Z",
|
|
35
|
+
"isRunning": false
|
|
40
36
|
},
|
|
41
37
|
"testgaps": {
|
|
42
|
-
"runCount":
|
|
43
|
-
"successCount":
|
|
38
|
+
"runCount": 0,
|
|
39
|
+
"successCount": 0,
|
|
44
40
|
"failureCount": 0,
|
|
45
41
|
"averageDurationMs": 0,
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"lastRun": "2026-01-07T03:07:50.015Z"
|
|
42
|
+
"nextRun": "2026-01-07T04:07:44.097Z",
|
|
43
|
+
"isRunning": false
|
|
49
44
|
},
|
|
50
45
|
"predict": {
|
|
51
46
|
"runCount": 0,
|
|
@@ -63,14 +58,20 @@
|
|
|
63
58
|
}
|
|
64
59
|
},
|
|
65
60
|
"config": {
|
|
66
|
-
"autoStart":
|
|
61
|
+
"autoStart": false,
|
|
67
62
|
"logDir": "/workspaces/claude-flow/v3/@claude-flow/cli/.claude-flow/logs",
|
|
68
63
|
"stateFile": "/workspaces/claude-flow/v3/@claude-flow/cli/.claude-flow/daemon-state.json",
|
|
69
|
-
"maxConcurrent":
|
|
64
|
+
"maxConcurrent": 2,
|
|
65
|
+
"workerTimeoutMs": 300000,
|
|
66
|
+
"resourceThresholds": {
|
|
67
|
+
"maxCpuLoad": 2,
|
|
68
|
+
"minFreeMemoryPercent": 20
|
|
69
|
+
},
|
|
70
70
|
"workers": [
|
|
71
71
|
{
|
|
72
72
|
"type": "map",
|
|
73
|
-
"intervalMs":
|
|
73
|
+
"intervalMs": 900000,
|
|
74
|
+
"offsetMs": 0,
|
|
74
75
|
"priority": "normal",
|
|
75
76
|
"description": "Codebase mapping",
|
|
76
77
|
"enabled": true
|
|
@@ -78,6 +79,7 @@
|
|
|
78
79
|
{
|
|
79
80
|
"type": "audit",
|
|
80
81
|
"intervalMs": 600000,
|
|
82
|
+
"offsetMs": 120000,
|
|
81
83
|
"priority": "critical",
|
|
82
84
|
"description": "Security analysis",
|
|
83
85
|
"enabled": true
|
|
@@ -85,6 +87,7 @@
|
|
|
85
87
|
{
|
|
86
88
|
"type": "optimize",
|
|
87
89
|
"intervalMs": 900000,
|
|
90
|
+
"offsetMs": 240000,
|
|
88
91
|
"priority": "high",
|
|
89
92
|
"description": "Performance optimization",
|
|
90
93
|
"enabled": true
|
|
@@ -92,6 +95,7 @@
|
|
|
92
95
|
{
|
|
93
96
|
"type": "consolidate",
|
|
94
97
|
"intervalMs": 1800000,
|
|
98
|
+
"offsetMs": 360000,
|
|
95
99
|
"priority": "low",
|
|
96
100
|
"description": "Memory consolidation",
|
|
97
101
|
"enabled": true
|
|
@@ -99,13 +103,15 @@
|
|
|
99
103
|
{
|
|
100
104
|
"type": "testgaps",
|
|
101
105
|
"intervalMs": 1200000,
|
|
106
|
+
"offsetMs": 480000,
|
|
102
107
|
"priority": "normal",
|
|
103
108
|
"description": "Test coverage analysis",
|
|
104
109
|
"enabled": true
|
|
105
110
|
},
|
|
106
111
|
{
|
|
107
112
|
"type": "predict",
|
|
108
|
-
"intervalMs":
|
|
113
|
+
"intervalMs": 600000,
|
|
114
|
+
"offsetMs": 0,
|
|
109
115
|
"priority": "low",
|
|
110
116
|
"description": "Predictive preloading",
|
|
111
117
|
"enabled": false
|
|
@@ -113,11 +119,12 @@
|
|
|
113
119
|
{
|
|
114
120
|
"type": "document",
|
|
115
121
|
"intervalMs": 3600000,
|
|
122
|
+
"offsetMs": 0,
|
|
116
123
|
"priority": "low",
|
|
117
124
|
"description": "Auto-documentation",
|
|
118
125
|
"enabled": false
|
|
119
126
|
}
|
|
120
127
|
]
|
|
121
128
|
},
|
|
122
|
-
"savedAt": "2026-01-07T03:
|
|
129
|
+
"savedAt": "2026-01-07T03:59:45.210Z"
|
|
123
130
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"timestamp": "2026-01-07T03:
|
|
2
|
+
"timestamp": "2026-01-07T03:57:50.243Z",
|
|
3
3
|
"projectRoot": "/workspaces/claude-flow/v3/@claude-flow/cli",
|
|
4
4
|
"structure": {
|
|
5
5
|
"hasPackageJson": true,
|
|
@@ -7,5 +7,5 @@
|
|
|
7
7
|
"hasClaudeConfig": false,
|
|
8
8
|
"hasClaudeFlow": true
|
|
9
9
|
},
|
|
10
|
-
"scannedAt":
|
|
10
|
+
"scannedAt": 1767758270243
|
|
11
11
|
}
|
|
@@ -1,87 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"basicOperations": 0,
|
|
15
|
-
"autoModeSelections": 0,
|
|
16
|
-
"modeOverrides": 0,
|
|
17
|
-
"currentMode": "auto"
|
|
18
|
-
},
|
|
19
|
-
"operations": {
|
|
20
|
-
"store": {
|
|
21
|
-
"count": 0,
|
|
22
|
-
"totalDuration": 0,
|
|
23
|
-
"errors": 0
|
|
24
|
-
},
|
|
25
|
-
"retrieve": {
|
|
26
|
-
"count": 0,
|
|
27
|
-
"totalDuration": 0,
|
|
28
|
-
"errors": 0
|
|
29
|
-
},
|
|
30
|
-
"query": {
|
|
31
|
-
"count": 0,
|
|
32
|
-
"totalDuration": 0,
|
|
33
|
-
"errors": 0
|
|
34
|
-
},
|
|
35
|
-
"list": {
|
|
36
|
-
"count": 0,
|
|
37
|
-
"totalDuration": 0,
|
|
38
|
-
"errors": 0
|
|
39
|
-
},
|
|
40
|
-
"delete": {
|
|
41
|
-
"count": 0,
|
|
42
|
-
"totalDuration": 0,
|
|
43
|
-
"errors": 0
|
|
44
|
-
},
|
|
45
|
-
"search": {
|
|
46
|
-
"count": 0,
|
|
47
|
-
"totalDuration": 0,
|
|
48
|
-
"errors": 0
|
|
49
|
-
},
|
|
50
|
-
"init": {
|
|
51
|
-
"count": 0,
|
|
52
|
-
"totalDuration": 0,
|
|
53
|
-
"errors": 0
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
"performance": {
|
|
57
|
-
"avgOperationDuration": 0,
|
|
58
|
-
"minOperationDuration": null,
|
|
59
|
-
"maxOperationDuration": null,
|
|
60
|
-
"slowOperations": 0,
|
|
61
|
-
"fastOperations": 0,
|
|
62
|
-
"totalOperationTime": 0
|
|
63
|
-
},
|
|
64
|
-
"storage": {
|
|
65
|
-
"totalEntries": 0,
|
|
66
|
-
"reasoningbankEntries": 0,
|
|
67
|
-
"basicEntries": 0,
|
|
68
|
-
"databaseSize": 0,
|
|
69
|
-
"lastBackup": null,
|
|
70
|
-
"growthRate": 0
|
|
71
|
-
},
|
|
72
|
-
"errors": {
|
|
73
|
-
"total": 0,
|
|
74
|
-
"byType": {},
|
|
75
|
-
"byOperation": {},
|
|
76
|
-
"recent": []
|
|
77
|
-
},
|
|
78
|
-
"reasoningbank": {
|
|
79
|
-
"semanticSearches": 0,
|
|
80
|
-
"sqlFallbacks": 0,
|
|
81
|
-
"embeddingGenerated": 0,
|
|
82
|
-
"consolidations": 0,
|
|
83
|
-
"avgQueryTime": 0,
|
|
84
|
-
"cacheHits": 0,
|
|
85
|
-
"cacheMisses": 0
|
|
2
|
+
"timestamp": "2026-01-07T03:52:50.190Z",
|
|
3
|
+
"memoryUsage": {
|
|
4
|
+
"rss": 65392640,
|
|
5
|
+
"heapTotal": 11304960,
|
|
6
|
+
"heapUsed": 9487728,
|
|
7
|
+
"external": 2145622,
|
|
8
|
+
"arrayBuffers": 24811
|
|
9
|
+
},
|
|
10
|
+
"uptime": 2700.280050891,
|
|
11
|
+
"optimizations": {
|
|
12
|
+
"cacheHitRate": 0.78,
|
|
13
|
+
"avgResponseTime": 45
|
|
86
14
|
}
|
|
87
15
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
|
-
"id": "cmd-hooks-
|
|
3
|
+
"id": "cmd-hooks-1767757800648",
|
|
4
4
|
"type": "hooks",
|
|
5
5
|
"success": true,
|
|
6
|
-
"duration": 1.
|
|
7
|
-
"timestamp":
|
|
6
|
+
"duration": 1.631386999999961,
|
|
7
|
+
"timestamp": 1767757800650,
|
|
8
8
|
"metadata": {}
|
|
9
9
|
}
|
|
10
10
|
]
|
package/agents/architect.yaml
CHANGED
package/agents/coder.yaml
CHANGED
package/agents/reviewer.yaml
CHANGED
package/agents/tester.yaml
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../../src/commands/daemon.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../../src/commands/daemon.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,aAAa,CAAC;AAshB1E,eAAO,MAAM,aAAa,EAAE,OAwD3B,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { output } from '../output.js';
|
|
6
6
|
import { getDaemon, startDaemon, stopDaemon } from '../services/worker-daemon.js';
|
|
7
|
+
import { spawn } from 'child_process';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import { dirname, join } from 'path';
|
|
10
|
+
import * as fs from 'fs';
|
|
7
11
|
// Start daemon subcommand
|
|
8
12
|
const startCommand = {
|
|
9
13
|
name: 'start',
|
|
@@ -11,21 +15,41 @@ const startCommand = {
|
|
|
11
15
|
options: [
|
|
12
16
|
{ name: 'workers', short: 'w', type: 'string', description: 'Comma-separated list of workers to enable (default: map,audit,optimize,consolidate,testgaps)' },
|
|
13
17
|
{ name: 'quiet', short: 'q', type: 'boolean', description: 'Suppress output' },
|
|
18
|
+
{ name: 'background', short: 'b', type: 'boolean', description: 'Run daemon in background (detached process)', default: true },
|
|
19
|
+
{ name: 'foreground', short: 'f', type: 'boolean', description: 'Run daemon in foreground (blocks terminal)' },
|
|
14
20
|
],
|
|
15
21
|
examples: [
|
|
16
|
-
{ command: 'claude-flow daemon start', description: 'Start daemon
|
|
22
|
+
{ command: 'claude-flow daemon start', description: 'Start daemon in background (default)' },
|
|
23
|
+
{ command: 'claude-flow daemon start --foreground', description: 'Start in foreground (blocks terminal)' },
|
|
17
24
|
{ command: 'claude-flow daemon start -w map,audit,optimize', description: 'Start with specific workers' },
|
|
18
25
|
],
|
|
19
26
|
action: async (ctx) => {
|
|
20
27
|
const quiet = ctx.flags.quiet;
|
|
28
|
+
const foreground = ctx.flags.foreground;
|
|
21
29
|
const projectRoot = process.cwd();
|
|
30
|
+
const isDaemonProcess = process.env.CLAUDE_FLOW_DAEMON === '1';
|
|
31
|
+
// Check if background daemon already running (skip if we ARE the daemon process)
|
|
32
|
+
if (!isDaemonProcess) {
|
|
33
|
+
const bgPid = getBackgroundDaemonPid(projectRoot);
|
|
34
|
+
if (bgPid && isProcessRunning(bgPid)) {
|
|
35
|
+
if (!quiet) {
|
|
36
|
+
output.printWarning(`Daemon already running in background (PID: ${bgPid})`);
|
|
37
|
+
}
|
|
38
|
+
return { success: true };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Background mode (default): fork a detached process
|
|
42
|
+
if (!foreground) {
|
|
43
|
+
return startBackgroundDaemon(projectRoot, quiet);
|
|
44
|
+
}
|
|
45
|
+
// Foreground mode: run in current process (blocks terminal)
|
|
22
46
|
try {
|
|
23
47
|
if (!quiet) {
|
|
24
48
|
const spinner = output.createSpinner({ text: 'Starting worker daemon...', spinner: 'dots' });
|
|
25
49
|
spinner.start();
|
|
26
50
|
const daemon = await startDaemon(projectRoot);
|
|
27
51
|
const status = daemon.getStatus();
|
|
28
|
-
spinner.succeed('Worker daemon started');
|
|
52
|
+
spinner.succeed('Worker daemon started (foreground mode)');
|
|
29
53
|
output.writeln();
|
|
30
54
|
output.printBox([
|
|
31
55
|
`PID: ${status.pid}`,
|
|
@@ -53,6 +77,8 @@ const startCommand = {
|
|
|
53
77
|
description: w.description,
|
|
54
78
|
})),
|
|
55
79
|
});
|
|
80
|
+
output.writeln();
|
|
81
|
+
output.writeln(output.dim('Press Ctrl+C to stop daemon'));
|
|
56
82
|
// Listen for worker events
|
|
57
83
|
daemon.on('worker:start', ({ type }) => {
|
|
58
84
|
output.writeln(output.dim(`[daemon] Worker starting: ${type}`));
|
|
@@ -63,9 +89,12 @@ const startCommand = {
|
|
|
63
89
|
daemon.on('worker:error', ({ type, error }) => {
|
|
64
90
|
output.writeln(output.error(`[daemon] Worker failed: ${type} - ${error}`));
|
|
65
91
|
});
|
|
92
|
+
// Keep process alive
|
|
93
|
+
await new Promise(() => { }); // Never resolves - daemon runs until killed
|
|
66
94
|
}
|
|
67
95
|
else {
|
|
68
96
|
await startDaemon(projectRoot);
|
|
97
|
+
await new Promise(() => { }); // Keep alive
|
|
69
98
|
}
|
|
70
99
|
return { success: true };
|
|
71
100
|
}
|
|
@@ -75,6 +104,62 @@ const startCommand = {
|
|
|
75
104
|
}
|
|
76
105
|
},
|
|
77
106
|
};
|
|
107
|
+
/**
|
|
108
|
+
* Start daemon as a detached background process
|
|
109
|
+
*/
|
|
110
|
+
async function startBackgroundDaemon(projectRoot, quiet) {
|
|
111
|
+
const stateDir = join(projectRoot, '.claude-flow');
|
|
112
|
+
const pidFile = join(stateDir, 'daemon.pid');
|
|
113
|
+
const logFile = join(stateDir, 'daemon.log');
|
|
114
|
+
// Ensure state directory exists
|
|
115
|
+
if (!fs.existsSync(stateDir)) {
|
|
116
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
117
|
+
}
|
|
118
|
+
// Get path to CLI (from dist/src/commands/daemon.js -> bin/cli.js)
|
|
119
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
120
|
+
const __dirname = dirname(__filename);
|
|
121
|
+
// dist/src/commands -> dist/src -> dist -> package root -> bin/cli.js
|
|
122
|
+
const cliPath = join(__dirname, '..', '..', '..', 'bin', 'cli.js');
|
|
123
|
+
// Verify CLI path exists
|
|
124
|
+
if (!fs.existsSync(cliPath)) {
|
|
125
|
+
output.printError(`CLI not found at: ${cliPath}`);
|
|
126
|
+
return { success: false, exitCode: 1 };
|
|
127
|
+
}
|
|
128
|
+
// Use shell to spawn daemon with proper output redirection
|
|
129
|
+
// This ensures the log file stays open even after parent exits
|
|
130
|
+
const shellCmd = `"${process.execPath}" "${cliPath}" daemon start --foreground --quiet >> "${logFile}" 2>&1 & echo $!`;
|
|
131
|
+
const child = spawn('sh', ['-c', shellCmd], {
|
|
132
|
+
cwd: projectRoot,
|
|
133
|
+
detached: true,
|
|
134
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
135
|
+
env: { ...process.env, CLAUDE_FLOW_DAEMON: '1' },
|
|
136
|
+
});
|
|
137
|
+
// Wait for the PID to be echoed back
|
|
138
|
+
return new Promise((resolve) => {
|
|
139
|
+
let pidStr = '';
|
|
140
|
+
child.stdout?.on('data', (data) => {
|
|
141
|
+
pidStr += data.toString();
|
|
142
|
+
});
|
|
143
|
+
child.on('close', () => {
|
|
144
|
+
const pid = parseInt(pidStr.trim(), 10);
|
|
145
|
+
if (isNaN(pid) || pid <= 0) {
|
|
146
|
+
output.printError('Failed to get daemon PID');
|
|
147
|
+
resolve({ success: false, exitCode: 1 });
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// Save PID
|
|
151
|
+
fs.writeFileSync(pidFile, String(pid));
|
|
152
|
+
if (!quiet) {
|
|
153
|
+
output.printSuccess(`Daemon started in background (PID: ${pid})`);
|
|
154
|
+
output.printInfo(`Logs: ${logFile}`);
|
|
155
|
+
output.printInfo(`Stop with: claude-flow daemon stop`);
|
|
156
|
+
}
|
|
157
|
+
resolve({ success: true });
|
|
158
|
+
});
|
|
159
|
+
// Unref so parent can exit immediately
|
|
160
|
+
child.unref();
|
|
161
|
+
});
|
|
162
|
+
}
|
|
78
163
|
// Stop daemon subcommand
|
|
79
164
|
const stopCommand = {
|
|
80
165
|
name: 'stop',
|
|
@@ -87,15 +172,20 @@ const stopCommand = {
|
|
|
87
172
|
],
|
|
88
173
|
action: async (ctx) => {
|
|
89
174
|
const quiet = ctx.flags.quiet;
|
|
175
|
+
const projectRoot = process.cwd();
|
|
90
176
|
try {
|
|
91
177
|
if (!quiet) {
|
|
92
178
|
const spinner = output.createSpinner({ text: 'Stopping worker daemon...', spinner: 'dots' });
|
|
93
179
|
spinner.start();
|
|
180
|
+
// Try to stop in-process daemon first
|
|
94
181
|
await stopDaemon();
|
|
95
|
-
|
|
182
|
+
// Also kill any background daemon by PID
|
|
183
|
+
const killed = await killBackgroundDaemon(projectRoot);
|
|
184
|
+
spinner.succeed(killed ? 'Worker daemon stopped' : 'Worker daemon was not running');
|
|
96
185
|
}
|
|
97
186
|
else {
|
|
98
187
|
await stopDaemon();
|
|
188
|
+
await killBackgroundDaemon(projectRoot);
|
|
99
189
|
}
|
|
100
190
|
return { success: true };
|
|
101
191
|
}
|
|
@@ -105,6 +195,83 @@ const stopCommand = {
|
|
|
105
195
|
}
|
|
106
196
|
},
|
|
107
197
|
};
|
|
198
|
+
/**
|
|
199
|
+
* Kill background daemon process using PID file
|
|
200
|
+
*/
|
|
201
|
+
async function killBackgroundDaemon(projectRoot) {
|
|
202
|
+
const pidFile = join(projectRoot, '.claude-flow', 'daemon.pid');
|
|
203
|
+
if (!fs.existsSync(pidFile)) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
|
|
208
|
+
if (isNaN(pid)) {
|
|
209
|
+
fs.unlinkSync(pidFile);
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
// Check if process is running
|
|
213
|
+
try {
|
|
214
|
+
process.kill(pid, 0); // Signal 0 = check if alive
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// Process not running, clean up stale PID file
|
|
218
|
+
fs.unlinkSync(pidFile);
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
// Kill the process
|
|
222
|
+
process.kill(pid, 'SIGTERM');
|
|
223
|
+
// Wait a moment then force kill if needed
|
|
224
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
225
|
+
try {
|
|
226
|
+
process.kill(pid, 0);
|
|
227
|
+
// Still alive, force kill
|
|
228
|
+
process.kill(pid, 'SIGKILL');
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// Process terminated
|
|
232
|
+
}
|
|
233
|
+
// Clean up PID file
|
|
234
|
+
if (fs.existsSync(pidFile)) {
|
|
235
|
+
fs.unlinkSync(pidFile);
|
|
236
|
+
}
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
// Clean up PID file on any error
|
|
241
|
+
if (fs.existsSync(pidFile)) {
|
|
242
|
+
fs.unlinkSync(pidFile);
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Get PID of background daemon from PID file
|
|
249
|
+
*/
|
|
250
|
+
function getBackgroundDaemonPid(projectRoot) {
|
|
251
|
+
const pidFile = join(projectRoot, '.claude-flow', 'daemon.pid');
|
|
252
|
+
if (!fs.existsSync(pidFile)) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
|
|
257
|
+
return isNaN(pid) ? null : pid;
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Check if a process is running
|
|
265
|
+
*/
|
|
266
|
+
function isProcessRunning(pid) {
|
|
267
|
+
try {
|
|
268
|
+
process.kill(pid, 0); // Signal 0 = check if alive
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
108
275
|
// Status subcommand
|
|
109
276
|
const statusCommand = {
|
|
110
277
|
name: 'status',
|
|
@@ -118,16 +285,23 @@ const statusCommand = {
|
|
|
118
285
|
],
|
|
119
286
|
action: async (ctx) => {
|
|
120
287
|
const verbose = ctx.flags.verbose;
|
|
288
|
+
const projectRoot = process.cwd();
|
|
121
289
|
try {
|
|
122
|
-
const daemon = getDaemon(
|
|
290
|
+
const daemon = getDaemon(projectRoot);
|
|
123
291
|
const status = daemon.getStatus();
|
|
292
|
+
// Also check for background daemon
|
|
293
|
+
const bgPid = getBackgroundDaemonPid(projectRoot);
|
|
294
|
+
const bgRunning = bgPid ? isProcessRunning(bgPid) : false;
|
|
295
|
+
const isRunning = status.running || bgRunning;
|
|
296
|
+
const displayPid = bgPid || status.pid;
|
|
124
297
|
output.writeln();
|
|
125
298
|
// Daemon status box
|
|
126
|
-
const statusIcon =
|
|
127
|
-
const statusText =
|
|
299
|
+
const statusIcon = isRunning ? output.success('●') : output.error('○');
|
|
300
|
+
const statusText = isRunning ? output.success('RUNNING') : output.error('STOPPED');
|
|
301
|
+
const mode = bgRunning ? output.dim(' (background)') : status.running ? output.dim(' (foreground)') : '';
|
|
128
302
|
output.printBox([
|
|
129
|
-
`Status: ${statusIcon} ${statusText}`,
|
|
130
|
-
`PID: ${
|
|
303
|
+
`Status: ${statusIcon} ${statusText}${mode}`,
|
|
304
|
+
`PID: ${displayPid}`,
|
|
131
305
|
status.startedAt ? `Started: ${status.startedAt.toISOString()}` : '',
|
|
132
306
|
`Workers Enabled: ${status.config.workers.filter(w => w.enabled).length}`,
|
|
133
307
|
`Max Concurrent: ${status.config.maxConcurrent}`,
|