claude-flow 3.5.75 → 3.5.77
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/package.json +1 -1
- package/v3/@claude-flow/cli/bin/cli.js +11 -4
- package/v3/@claude-flow/cli/dist/src/autopilot-state.js +37 -45
- package/v3/@claude-flow/cli/dist/src/commands/autopilot.js +3 -3
- package/v3/@claude-flow/cli/dist/src/commands/claims.js +12 -16
- package/v3/@claude-flow/cli/dist/src/commands/cleanup.js +36 -4
- package/v3/@claude-flow/cli/dist/src/commands/daemon.js +46 -1
- package/v3/@claude-flow/cli/dist/src/commands/embeddings.js +76 -51
- package/v3/@claude-flow/cli/dist/src/commands/hive-mind.js +4 -2
- package/v3/@claude-flow/cli/dist/src/commands/index.d.ts +7 -13
- package/v3/@claude-flow/cli/dist/src/commands/index.js +48 -99
- package/v3/@claude-flow/cli/dist/src/commands/init.js +22 -6
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/backup.js +2 -1
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/benchmark.js +2 -1
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/import.js +30 -6
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/init.js +2 -1
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/migrate.js +2 -1
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/optimize.js +2 -1
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/pg-utils.d.ts +14 -0
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/pg-utils.js +41 -0
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/status.js +2 -1
- package/v3/@claude-flow/cli/dist/src/index.js +42 -32
- package/v3/@claude-flow/cli/dist/src/mcp-server.js +23 -5
- package/v3/@claude-flow/cli/dist/src/plugins/manager.js +2 -0
- package/v3/@claude-flow/cli/package.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.77",
|
|
4
4
|
"description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -158,8 +158,15 @@ if (isMCPMode) {
|
|
|
158
158
|
// Run normal CLI mode
|
|
159
159
|
const { CLI } = await import('../dist/src/index.js');
|
|
160
160
|
const cli = new CLI();
|
|
161
|
-
cli.run()
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
cli.run()
|
|
162
|
+
.then(() => {
|
|
163
|
+
// #1552: Exit cleanly after one-shot commands.
|
|
164
|
+
// Long-running commands (daemon foreground, mcp, status --watch) never resolve,
|
|
165
|
+
// so this only fires for normal CLI commands.
|
|
166
|
+
process.exit(0);
|
|
167
|
+
})
|
|
168
|
+
.catch((error) => {
|
|
169
|
+
console.error('Fatal error:', error.message);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
});
|
|
165
172
|
}
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
* ADR-072: Autopilot Integration
|
|
8
8
|
* Security: Addresses prototype pollution, NaN bypass, input validation
|
|
9
9
|
*/
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, renameSync } from 'node:fs';
|
|
12
|
+
import { resolve, join } from 'node:path';
|
|
13
|
+
import { homedir } from 'node:os';
|
|
10
14
|
// ── Constants ─────────────────────────────────────────────────
|
|
11
15
|
export const STATE_DIR = '.claude-flow/data';
|
|
12
16
|
export const STATE_FILE = `${STATE_DIR}/autopilot-state.json`;
|
|
@@ -71,9 +75,8 @@ export function validateTaskSources(sources) {
|
|
|
71
75
|
}
|
|
72
76
|
// ── State Management ──────────────────────────────────────────
|
|
73
77
|
export function getDefaultState() {
|
|
74
|
-
const crypto = require('crypto');
|
|
75
78
|
return {
|
|
76
|
-
sessionId:
|
|
79
|
+
sessionId: randomUUID(),
|
|
77
80
|
enabled: false,
|
|
78
81
|
startTime: Date.now(),
|
|
79
82
|
iterations: 0,
|
|
@@ -85,13 +88,11 @@ export function getDefaultState() {
|
|
|
85
88
|
};
|
|
86
89
|
}
|
|
87
90
|
export function loadState() {
|
|
88
|
-
const
|
|
89
|
-
const path = require('path');
|
|
90
|
-
const filePath = path.resolve(STATE_FILE);
|
|
91
|
+
const filePath = resolve(STATE_FILE);
|
|
91
92
|
const defaults = getDefaultState();
|
|
92
93
|
try {
|
|
93
|
-
if (
|
|
94
|
-
const raw = safeJsonParse(
|
|
94
|
+
if (existsSync(filePath)) {
|
|
95
|
+
const raw = safeJsonParse(readFileSync(filePath, 'utf-8'));
|
|
95
96
|
const merged = { ...defaults, ...raw };
|
|
96
97
|
// Re-validate fields that could be tampered with
|
|
97
98
|
merged.maxIterations = validateNumber(merged.maxIterations, 1, 1000, 50);
|
|
@@ -111,30 +112,26 @@ export function loadState() {
|
|
|
111
112
|
return defaults;
|
|
112
113
|
}
|
|
113
114
|
export function saveState(state) {
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (!fs.existsSync(dir))
|
|
118
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
115
|
+
const dir = resolve(STATE_DIR);
|
|
116
|
+
if (!existsSync(dir))
|
|
117
|
+
mkdirSync(dir, { recursive: true });
|
|
119
118
|
// Cap history before saving
|
|
120
119
|
if (state.history.length > MAX_HISTORY_ENTRIES) {
|
|
121
120
|
state.history = state.history.slice(-MAX_HISTORY_ENTRIES);
|
|
122
121
|
}
|
|
123
|
-
const tmpFile =
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
const tmpFile = resolve(STATE_FILE) + '.tmp';
|
|
123
|
+
writeFileSync(tmpFile, JSON.stringify(state, null, 2));
|
|
124
|
+
renameSync(tmpFile, resolve(STATE_FILE));
|
|
126
125
|
}
|
|
127
126
|
export function appendLog(entry) {
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (!fs.existsSync(dir))
|
|
133
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
127
|
+
const filePath = resolve(LOG_FILE);
|
|
128
|
+
const dir = resolve(STATE_DIR);
|
|
129
|
+
if (!existsSync(dir))
|
|
130
|
+
mkdirSync(dir, { recursive: true });
|
|
134
131
|
let log = [];
|
|
135
132
|
try {
|
|
136
|
-
if (
|
|
137
|
-
log = safeJsonParse(
|
|
133
|
+
if (existsSync(filePath)) {
|
|
134
|
+
log = safeJsonParse(readFileSync(filePath, 'utf-8'));
|
|
138
135
|
if (!Array.isArray(log))
|
|
139
136
|
log = [];
|
|
140
137
|
}
|
|
@@ -146,16 +143,14 @@ export function appendLog(entry) {
|
|
|
146
143
|
if (log.length > MAX_LOG_ENTRIES)
|
|
147
144
|
log = log.slice(-MAX_LOG_ENTRIES);
|
|
148
145
|
const tmpFile = filePath + '.tmp';
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
writeFileSync(tmpFile, JSON.stringify(log, null, 2));
|
|
147
|
+
renameSync(tmpFile, filePath);
|
|
151
148
|
}
|
|
152
149
|
export function loadLog() {
|
|
153
|
-
const
|
|
154
|
-
const path = require('path');
|
|
155
|
-
const filePath = path.resolve(LOG_FILE);
|
|
150
|
+
const filePath = resolve(LOG_FILE);
|
|
156
151
|
try {
|
|
157
|
-
if (
|
|
158
|
-
const result = safeJsonParse(
|
|
152
|
+
if (existsSync(filePath)) {
|
|
153
|
+
const result = safeJsonParse(readFileSync(filePath, 'utf-8'));
|
|
159
154
|
return Array.isArray(result) ? result : [];
|
|
160
155
|
}
|
|
161
156
|
}
|
|
@@ -166,26 +161,23 @@ export function loadLog() {
|
|
|
166
161
|
}
|
|
167
162
|
// ── Task Discovery ────────────────────────────────────────────
|
|
168
163
|
export function discoverTasks(sources) {
|
|
169
|
-
const fs = require('fs');
|
|
170
|
-
const path = require('path');
|
|
171
|
-
const os = require('os');
|
|
172
164
|
const tasks = [];
|
|
173
165
|
// Only process valid sources
|
|
174
166
|
const validSources = sources.filter(s => VALID_TASK_SOURCES.has(s));
|
|
175
167
|
for (const source of validSources) {
|
|
176
168
|
if (source === 'team-tasks') {
|
|
177
|
-
const tasksDir =
|
|
169
|
+
const tasksDir = join(homedir(), '.claude', 'tasks');
|
|
178
170
|
try {
|
|
179
|
-
if (
|
|
180
|
-
const teams =
|
|
171
|
+
if (existsSync(tasksDir)) {
|
|
172
|
+
const teams = readdirSync(tasksDir, { withFileTypes: true });
|
|
181
173
|
for (const team of teams) {
|
|
182
174
|
if (!team.isDirectory())
|
|
183
175
|
continue;
|
|
184
|
-
const teamDir =
|
|
185
|
-
const files =
|
|
176
|
+
const teamDir = join(tasksDir, team.name);
|
|
177
|
+
const files = readdirSync(teamDir).filter((f) => f.endsWith('.json'));
|
|
186
178
|
for (const file of files) {
|
|
187
179
|
try {
|
|
188
|
-
const data = safeJsonParse(
|
|
180
|
+
const data = safeJsonParse(readFileSync(join(teamDir, file), 'utf-8'));
|
|
189
181
|
tasks.push({
|
|
190
182
|
id: String(data.id || file.replace('.json', '')),
|
|
191
183
|
subject: String(data.subject || data.title || file),
|
|
@@ -201,10 +193,10 @@ export function discoverTasks(sources) {
|
|
|
201
193
|
catch { /* skip source */ }
|
|
202
194
|
}
|
|
203
195
|
if (source === 'swarm-tasks') {
|
|
204
|
-
const swarmFile =
|
|
196
|
+
const swarmFile = resolve('.claude-flow/swarm-tasks.json');
|
|
205
197
|
try {
|
|
206
|
-
if (
|
|
207
|
-
const data = safeJsonParse(
|
|
198
|
+
if (existsSync(swarmFile)) {
|
|
199
|
+
const data = safeJsonParse(readFileSync(swarmFile, 'utf-8'));
|
|
208
200
|
const swarmTasks = Array.isArray(data) ? data : (data.tasks || []);
|
|
209
201
|
for (const t of swarmTasks) {
|
|
210
202
|
if (t && typeof t === 'object') {
|
|
@@ -222,10 +214,10 @@ export function discoverTasks(sources) {
|
|
|
222
214
|
catch { /* skip source */ }
|
|
223
215
|
}
|
|
224
216
|
if (source === 'file-checklist') {
|
|
225
|
-
const checklistFile =
|
|
217
|
+
const checklistFile = resolve('.claude-flow/data/checklist.json');
|
|
226
218
|
try {
|
|
227
|
-
if (
|
|
228
|
-
const data = safeJsonParse(
|
|
219
|
+
if (existsSync(checklistFile)) {
|
|
220
|
+
const data = safeJsonParse(readFileSync(checklistFile, 'utf-8'));
|
|
229
221
|
const items = Array.isArray(data) ? data : (data.items || []);
|
|
230
222
|
for (const item of items) {
|
|
231
223
|
if (item && typeof item === 'object') {
|
|
@@ -182,10 +182,10 @@ const logCommand = {
|
|
|
182
182
|
],
|
|
183
183
|
action: async (ctx) => {
|
|
184
184
|
if (ctx.flags?.clear) {
|
|
185
|
-
const
|
|
186
|
-
const
|
|
185
|
+
const { writeFileSync } = await import('node:fs');
|
|
186
|
+
const { resolve } = await import('node:path');
|
|
187
187
|
try {
|
|
188
|
-
|
|
188
|
+
writeFileSync(resolve(LOG_FILE), '[]');
|
|
189
189
|
}
|
|
190
190
|
catch { /* ignore */ }
|
|
191
191
|
output.writeln('Autopilot log cleared');
|
|
@@ -4,27 +4,25 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Created with ❤️ by ruv.io
|
|
6
6
|
*/
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from 'node:fs';
|
|
8
|
+
import { resolve, dirname } from 'node:path';
|
|
7
9
|
import { output } from '../output.js';
|
|
8
10
|
const CLAIMS_CONFIG_PATHS = [
|
|
9
11
|
'.claude-flow/claims.json',
|
|
10
12
|
'claude-flow.claims.json',
|
|
11
13
|
];
|
|
12
14
|
function getClaimsConfigPaths() {
|
|
13
|
-
// Lazy import to keep top-level synchronous
|
|
14
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
15
|
-
const path = require('path');
|
|
16
15
|
return [
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
resolve(CLAIMS_CONFIG_PATHS[0]),
|
|
17
|
+
resolve(CLAIMS_CONFIG_PATHS[1]),
|
|
18
|
+
resolve(process.env.HOME || '~', '.config/claude-flow/claims.json'),
|
|
20
19
|
];
|
|
21
20
|
}
|
|
22
21
|
function loadClaimsConfig() {
|
|
23
|
-
const fs = require('fs');
|
|
24
22
|
const configPaths = getClaimsConfigPaths();
|
|
25
23
|
for (const configPath of configPaths) {
|
|
26
|
-
if (
|
|
27
|
-
const content =
|
|
24
|
+
if (existsSync(configPath)) {
|
|
25
|
+
const content = readFileSync(configPath, 'utf-8');
|
|
28
26
|
return { config: JSON.parse(content), path: configPath };
|
|
29
27
|
}
|
|
30
28
|
}
|
|
@@ -41,15 +39,13 @@ function loadClaimsConfig() {
|
|
|
41
39
|
return { config: defaultConfig, path: configPaths[0] };
|
|
42
40
|
}
|
|
43
41
|
function saveClaimsConfig(config, configPath) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (!fs.existsSync(dir)) {
|
|
48
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
42
|
+
const dir = dirname(configPath);
|
|
43
|
+
if (!existsSync(dir)) {
|
|
44
|
+
mkdirSync(dir, { recursive: true });
|
|
49
45
|
}
|
|
50
46
|
const tmpPath = configPath + '.tmp';
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
48
|
+
renameSync(tmpPath, configPath);
|
|
53
49
|
}
|
|
54
50
|
// List subcommand
|
|
55
51
|
const listCommand = {
|
|
@@ -5,13 +5,21 @@
|
|
|
5
5
|
* Created with ruv.io
|
|
6
6
|
*/
|
|
7
7
|
import { output } from '../output.js';
|
|
8
|
-
import { existsSync, statSync, rmSync, readdirSync } from 'fs';
|
|
8
|
+
import { existsSync, statSync, rmSync, readdirSync, readFileSync, writeFileSync } from 'fs';
|
|
9
9
|
import { join } from 'path';
|
|
10
|
+
/**
|
|
11
|
+
* Ruflo-owned subdirectories within .claude/ that are safe to delete.
|
|
12
|
+
* Everything else in .claude/ (agents, skills, commands, settings.local.json,
|
|
13
|
+
* memory.db, worktrees, launch.json) belongs to Claude Code and must be preserved.
|
|
14
|
+
* See: https://github.com/ruvnet/ruflo/issues/1557
|
|
15
|
+
*/
|
|
16
|
+
const CLAUDE_OWNED_SUBDIRS = [
|
|
17
|
+
{ path: join('.claude', 'helpers'), description: 'Ruflo hook scripts' },
|
|
18
|
+
];
|
|
10
19
|
/**
|
|
11
20
|
* Artifact directories and files that claude-flow/ruflo may create
|
|
12
21
|
*/
|
|
13
22
|
const ARTIFACT_DIRS = [
|
|
14
|
-
{ path: '.claude', description: 'Claude settings, helpers, agents' },
|
|
15
23
|
{ path: '.claude-flow', description: 'Capabilities and configuration' },
|
|
16
24
|
{ path: 'data', description: 'Memory databases' },
|
|
17
25
|
{ path: '.swarm', description: 'Swarm state' },
|
|
@@ -119,7 +127,21 @@ export const cleanupCommand = {
|
|
|
119
127
|
output.writeln();
|
|
120
128
|
const found = [];
|
|
121
129
|
let totalSize = 0;
|
|
122
|
-
// Scan
|
|
130
|
+
// Scan ruflo-owned subdirs within .claude/ (surgical — preserves Claude Code files)
|
|
131
|
+
for (const artifact of CLAUDE_OWNED_SUBDIRS) {
|
|
132
|
+
const fullPath = join(cwd, artifact.path);
|
|
133
|
+
if (existsSync(fullPath)) {
|
|
134
|
+
const size = getSize(fullPath);
|
|
135
|
+
found.push({ path: artifact.path, description: artifact.description, size, type: 'dir' });
|
|
136
|
+
totalSize += size;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Check if .claude/settings.json has ruflo hooks/claudeFlow blocks to clean
|
|
140
|
+
const settingsPath = join(cwd, '.claude', 'settings.json');
|
|
141
|
+
if (existsSync(settingsPath)) {
|
|
142
|
+
found.push({ path: join('.claude', 'settings.json'), description: 'Remove ruflo hooks/claudeFlow blocks (preserves rest)', size: 0, type: 'file' });
|
|
143
|
+
}
|
|
144
|
+
// Scan standalone artifact directories
|
|
123
145
|
for (const artifact of ARTIFACT_DIRS) {
|
|
124
146
|
const fullPath = join(cwd, artifact.path);
|
|
125
147
|
if (existsSync(fullPath)) {
|
|
@@ -170,7 +192,17 @@ export const cleanupCommand = {
|
|
|
170
192
|
// Actually delete
|
|
171
193
|
try {
|
|
172
194
|
const fullPath = join(cwd, item.path);
|
|
173
|
-
|
|
195
|
+
// Special handling: surgically clean settings.json instead of deleting
|
|
196
|
+
if (item.path === join('.claude', 'settings.json')) {
|
|
197
|
+
try {
|
|
198
|
+
const raw = JSON.parse(readFileSync(fullPath, 'utf-8'));
|
|
199
|
+
delete raw.hooks;
|
|
200
|
+
delete raw.claudeFlow;
|
|
201
|
+
writeFileSync(fullPath, JSON.stringify(raw, null, 2) + '\n', 'utf-8');
|
|
202
|
+
}
|
|
203
|
+
catch { /* settings.json parse failed, skip */ }
|
|
204
|
+
}
|
|
205
|
+
else if (item.type === 'dir') {
|
|
174
206
|
rmSync(fullPath, { recursive: true, force: true });
|
|
175
207
|
}
|
|
176
208
|
else {
|
|
@@ -69,10 +69,12 @@ const startCommand = {
|
|
|
69
69
|
const bgPid = getBackgroundDaemonPid(projectRoot);
|
|
70
70
|
if (bgPid && isProcessRunning(bgPid)) {
|
|
71
71
|
if (!quiet) {
|
|
72
|
-
output.printWarning(`Daemon already running in background (PID: ${bgPid})`);
|
|
72
|
+
output.printWarning(`Daemon already running in background (PID: ${bgPid}). Stop it first with: daemon stop`);
|
|
73
73
|
}
|
|
74
74
|
return { success: true };
|
|
75
75
|
}
|
|
76
|
+
// #1551: Kill any stale daemon processes that weren't tracked by PID file
|
|
77
|
+
await killStaleDaemons(projectRoot, quiet);
|
|
76
78
|
}
|
|
77
79
|
// Background mode (default): fork a detached process
|
|
78
80
|
if (!foreground) {
|
|
@@ -302,11 +304,14 @@ const stopCommand = {
|
|
|
302
304
|
await stopDaemon();
|
|
303
305
|
// Also kill any background daemon by PID
|
|
304
306
|
const killed = await killBackgroundDaemon(projectRoot);
|
|
307
|
+
// #1551: Also kill stale daemon processes not tracked by PID file
|
|
308
|
+
await killStaleDaemons(projectRoot, true);
|
|
305
309
|
spinner.succeed(killed ? 'Worker daemon stopped' : 'Worker daemon was not running');
|
|
306
310
|
}
|
|
307
311
|
else {
|
|
308
312
|
await stopDaemon();
|
|
309
313
|
await killBackgroundDaemon(projectRoot);
|
|
314
|
+
await killStaleDaemons(projectRoot, true);
|
|
310
315
|
}
|
|
311
316
|
return { success: true };
|
|
312
317
|
}
|
|
@@ -365,6 +370,46 @@ async function killBackgroundDaemon(projectRoot) {
|
|
|
365
370
|
return false;
|
|
366
371
|
}
|
|
367
372
|
}
|
|
373
|
+
/**
|
|
374
|
+
* Kill stale daemon processes not tracked by the PID file (#1551).
|
|
375
|
+
* Uses `ps` to find all daemon processes for this project and kills them.
|
|
376
|
+
*/
|
|
377
|
+
async function killStaleDaemons(projectRoot, quiet) {
|
|
378
|
+
try {
|
|
379
|
+
const { execFileSync } = await import('child_process');
|
|
380
|
+
const psOutput = execFileSync('ps', ['-eo', 'pid,command'], { encoding: 'utf-8', timeout: 5000 });
|
|
381
|
+
const lines = psOutput.split('\n');
|
|
382
|
+
const currentPid = process.pid;
|
|
383
|
+
const trackedPid = getBackgroundDaemonPid(projectRoot);
|
|
384
|
+
let killed = 0;
|
|
385
|
+
for (const line of lines) {
|
|
386
|
+
if (!line.includes('daemon start --foreground'))
|
|
387
|
+
continue;
|
|
388
|
+
if (!line.includes('claude-flow') && !line.includes('@claude-flow/cli'))
|
|
389
|
+
continue;
|
|
390
|
+
const pidStr = line.trim().split(/\s+/)[0];
|
|
391
|
+
const pid = parseInt(pidStr, 10);
|
|
392
|
+
if (isNaN(pid) || pid === currentPid || pid === trackedPid)
|
|
393
|
+
continue;
|
|
394
|
+
if (!isProcessRunning(pid))
|
|
395
|
+
continue;
|
|
396
|
+
try {
|
|
397
|
+
process.kill(pid, 'SIGTERM');
|
|
398
|
+
killed++;
|
|
399
|
+
if (!quiet) {
|
|
400
|
+
output.printWarning(`Killed stale daemon process (PID: ${pid})`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
catch { /* ignore — may have exited between check and kill */ }
|
|
404
|
+
}
|
|
405
|
+
if (killed > 0 && !quiet) {
|
|
406
|
+
output.printInfo(`Cleaned up ${killed} stale daemon process(es)`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
// ps not available or failed — skip stale cleanup
|
|
411
|
+
}
|
|
412
|
+
}
|
|
368
413
|
/**
|
|
369
414
|
* Get PID of background daemon from PID file
|
|
370
415
|
*/
|
|
@@ -145,62 +145,87 @@ const searchCommand = {
|
|
|
145
145
|
const queryResult = await generateEmbedding(query);
|
|
146
146
|
const queryEmbedding = queryResult.embedding;
|
|
147
147
|
// Get all entries with embeddings from database
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
148
|
+
// Parameterized query to prevent SQL injection (CRIT-01)
|
|
149
|
+
const embeddingSql = namespace !== 'all'
|
|
150
|
+
? `SELECT id, key, namespace, content, embedding, embedding_dimensions
|
|
151
|
+
FROM memory_entries
|
|
152
|
+
WHERE status = 'active' AND embedding IS NOT NULL AND namespace = ?
|
|
153
|
+
LIMIT 1000`
|
|
154
|
+
: `SELECT id, key, namespace, content, embedding, embedding_dimensions
|
|
155
|
+
FROM memory_entries
|
|
156
|
+
WHERE status = 'active' AND embedding IS NOT NULL
|
|
157
|
+
LIMIT 1000`;
|
|
158
|
+
const embeddingStmt = db.prepare(embeddingSql);
|
|
159
|
+
if (namespace !== 'all') {
|
|
160
|
+
embeddingStmt.bind([namespace]);
|
|
161
|
+
}
|
|
162
|
+
const entryRows = [];
|
|
163
|
+
while (embeddingStmt.step()) {
|
|
164
|
+
entryRows.push(embeddingStmt.get());
|
|
165
|
+
}
|
|
166
|
+
embeddingStmt.free();
|
|
156
167
|
const results = [];
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
catch {
|
|
177
|
-
// Skip entries with invalid embeddings
|
|
168
|
+
for (const row of entryRows) {
|
|
169
|
+
const [id, key, ns, content, embeddingJson] = row;
|
|
170
|
+
if (!embeddingJson)
|
|
171
|
+
continue;
|
|
172
|
+
try {
|
|
173
|
+
const embedding = JSON.parse(embeddingJson);
|
|
174
|
+
// Calculate cosine similarity
|
|
175
|
+
const similarity = cosineSimilarity(queryEmbedding, embedding);
|
|
176
|
+
if (similarity >= threshold) {
|
|
177
|
+
results.push({
|
|
178
|
+
score: similarity,
|
|
179
|
+
id: id.substring(0, 10),
|
|
180
|
+
key: key || id.substring(0, 15),
|
|
181
|
+
content: (content || '').substring(0, 45) + ((content || '').length > 45 ? '...' : ''),
|
|
182
|
+
namespace: ns || 'default'
|
|
183
|
+
});
|
|
178
184
|
}
|
|
179
185
|
}
|
|
186
|
+
catch {
|
|
187
|
+
// Skip entries with invalid embeddings
|
|
188
|
+
}
|
|
180
189
|
}
|
|
181
|
-
//
|
|
190
|
+
// Keyword search fallback with parameterized query (CRIT-01)
|
|
182
191
|
if (results.length < limit) {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
192
|
+
const likePattern = `%${query}%`;
|
|
193
|
+
const remainingLimit = Math.max(0, limit - results.length);
|
|
194
|
+
const keywordSql = namespace !== 'all'
|
|
195
|
+
? `SELECT id, key, namespace, content
|
|
196
|
+
FROM memory_entries
|
|
197
|
+
WHERE status = 'active'
|
|
198
|
+
AND (content LIKE ? OR key LIKE ?)
|
|
199
|
+
AND namespace = ?
|
|
200
|
+
LIMIT ?`
|
|
201
|
+
: `SELECT id, key, namespace, content
|
|
202
|
+
FROM memory_entries
|
|
203
|
+
WHERE status = 'active'
|
|
204
|
+
AND (content LIKE ? OR key LIKE ?)
|
|
205
|
+
LIMIT ?`;
|
|
206
|
+
const keywordStmt = db.prepare(keywordSql);
|
|
207
|
+
if (namespace !== 'all') {
|
|
208
|
+
keywordStmt.bind([likePattern, likePattern, namespace, remainingLimit]);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
keywordStmt.bind([likePattern, likePattern, remainingLimit]);
|
|
212
|
+
}
|
|
213
|
+
const keywordRows = [];
|
|
214
|
+
while (keywordStmt.step()) {
|
|
215
|
+
keywordRows.push(keywordStmt.get());
|
|
216
|
+
}
|
|
217
|
+
keywordStmt.free();
|
|
218
|
+
for (const row of keywordRows) {
|
|
219
|
+
const [id, key, ns, content] = row;
|
|
220
|
+
// Avoid duplicates
|
|
221
|
+
if (!results.some(r => r.id === id.substring(0, 10))) {
|
|
222
|
+
results.push({
|
|
223
|
+
score: 0.5, // Keyword match base score
|
|
224
|
+
id: id.substring(0, 10),
|
|
225
|
+
key: key || id.substring(0, 15),
|
|
226
|
+
content: (content || '').substring(0, 45) + ((content || '').length > 45 ? '...' : ''),
|
|
227
|
+
namespace: ns || 'default'
|
|
228
|
+
});
|
|
204
229
|
}
|
|
205
230
|
}
|
|
206
231
|
}
|
|
@@ -208,8 +208,10 @@ async function spawnClaudeCodeInstance(swarmId, swarmName, objective, workers, f
|
|
|
208
208
|
claudeArgs.push('--verbose');
|
|
209
209
|
output.printInfo('Running in non-interactive mode');
|
|
210
210
|
}
|
|
211
|
-
//
|
|
212
|
-
|
|
211
|
+
// HIGH-02: Strict boolean check (=== true) instead of loose truthiness (!== false)
|
|
212
|
+
// to prevent undefined/null from being treated as "skip permissions".
|
|
213
|
+
// Behavior change: only explicit --dangerously-skip-permissions flag triggers skip.
|
|
214
|
+
const skipPermissions = flags['dangerously-skip-permissions'] === true && !flags['no-auto-permissions'];
|
|
213
215
|
if (skipPermissions) {
|
|
214
216
|
claudeArgs.push('--dangerously-skip-permissions');
|
|
215
217
|
if (!isNonInteractive) {
|
|
@@ -19,18 +19,6 @@ export { swarmCommand } from './swarm.js';
|
|
|
19
19
|
export { memoryCommand } from './memory.js';
|
|
20
20
|
export { mcpCommand } from './mcp.js';
|
|
21
21
|
export { hooksCommand } from './hooks.js';
|
|
22
|
-
export { daemonCommand } from './daemon.js';
|
|
23
|
-
export { doctorCommand } from './doctor.js';
|
|
24
|
-
export { embeddingsCommand } from './embeddings.js';
|
|
25
|
-
export { neuralCommand } from './neural.js';
|
|
26
|
-
export { performanceCommand } from './performance.js';
|
|
27
|
-
export { securityCommand } from './security.js';
|
|
28
|
-
export { ruvectorCommand } from './ruvector/index.js';
|
|
29
|
-
export { hiveMindCommand } from './hive-mind.js';
|
|
30
|
-
export { guidanceCommand } from './guidance.js';
|
|
31
|
-
export { applianceCommand } from './appliance.js';
|
|
32
|
-
export { cleanupCommand } from './cleanup.js';
|
|
33
|
-
export { autopilotCommand } from './autopilot.js';
|
|
34
22
|
export declare function getConfigCommand(): Promise<Command | undefined>;
|
|
35
23
|
export declare function getMigrateCommand(): Promise<Command | undefined>;
|
|
36
24
|
export declare function getWorkflowCommand(): Promise<Command | undefined>;
|
|
@@ -62,7 +50,8 @@ export declare function getAutopilotCommand(): Promise<Command | undefined>;
|
|
|
62
50
|
*/
|
|
63
51
|
export declare const commands: Command[];
|
|
64
52
|
/**
|
|
65
|
-
* Commands organized by category for help display
|
|
53
|
+
* Commands organized by category for help display (synchronous core only).
|
|
54
|
+
* @deprecated Use getCommandsByCategory() for full categorized listing.
|
|
66
55
|
*/
|
|
67
56
|
export declare const commandsByCategory: {
|
|
68
57
|
primary: Command[];
|
|
@@ -71,6 +60,11 @@ export declare const commandsByCategory: {
|
|
|
71
60
|
analysis: Command[];
|
|
72
61
|
management: Command[];
|
|
73
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Async version that loads all commands by category (PERF-03).
|
|
65
|
+
* Use this for help display and full command listings.
|
|
66
|
+
*/
|
|
67
|
+
export declare function getCommandsByCategory(): Promise<Record<string, Command[]>>;
|
|
74
68
|
/**
|
|
75
69
|
* Command registry map for quick lookup
|
|
76
70
|
* Supports both sync (core commands) and async (lazy-loaded) commands
|