moflo 4.7.7 → 4.8.0
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/helpers/statusline.cjs +34 -26
- package/.claude/settings.json +2 -2
- package/README.md +1 -1
- package/bin/hooks.mjs +33 -3
- package/bin/session-start-launcher.mjs +88 -3
- package/package.json +3 -5
- package/src/@claude-flow/cli/README.md +1 -1
- package/src/@claude-flow/cli/dist/src/commands/daemon.js +42 -95
- package/src/@claude-flow/cli/dist/src/commands/doctor.js +11 -5
- package/src/@claude-flow/cli/dist/src/commands/init.js +0 -145
- package/src/@claude-flow/cli/dist/src/config/moflo-config.d.ts +5 -0
- package/src/@claude-flow/cli/dist/src/config/moflo-config.js +16 -0
- package/src/@claude-flow/cli/dist/src/config-adapter.d.ts +1 -1
- package/src/@claude-flow/cli/dist/src/init/executor.js +74 -7
- package/src/@claude-flow/cli/dist/src/init/mcp-generator.d.ts +3 -4
- package/src/@claude-flow/cli/dist/src/init/mcp-generator.js +65 -22
- package/src/@claude-flow/cli/dist/src/init/types.d.ts +0 -4
- package/src/@claude-flow/cli/dist/src/init/types.js +0 -5
- package/src/@claude-flow/cli/dist/src/mcp-server.js +36 -0
- package/src/@claude-flow/cli/dist/src/memory/memory-bridge.d.ts +6 -0
- package/src/@claude-flow/cli/dist/src/memory/memory-bridge.js +66 -0
- package/src/@claude-flow/cli/dist/src/memory/memory-initializer.js +52 -1
- package/src/@claude-flow/cli/dist/src/services/daemon-lock.d.ts +39 -0
- package/src/@claude-flow/cli/dist/src/services/daemon-lock.js +213 -0
- package/src/@claude-flow/cli/package.json +2 -6
- package/.claude/helpers/README.md +0 -97
- package/.claude/helpers/adr-compliance.sh +0 -186
- package/.claude/helpers/aggressive-microcompact.mjs +0 -36
- package/.claude/helpers/auto-commit.sh +0 -178
- package/.claude/helpers/checkpoint-manager.sh +0 -251
- package/.claude/helpers/context-persistence-hook.mjs +0 -1979
- package/.claude/helpers/daemon-manager.sh +0 -252
- package/.claude/helpers/ddd-tracker.sh +0 -144
- package/.claude/helpers/github-safe.js +0 -106
- package/.claude/helpers/github-setup.sh +0 -28
- package/.claude/helpers/guidance-hook.sh +0 -13
- package/.claude/helpers/guidance-hooks.sh +0 -102
- package/.claude/helpers/health-monitor.sh +0 -108
- package/.claude/helpers/learning-hooks.sh +0 -329
- package/.claude/helpers/learning-optimizer.sh +0 -127
- package/.claude/helpers/learning-service.mjs +0 -1211
- package/.claude/helpers/memory.cjs +0 -84
- package/.claude/helpers/metrics-db.mjs +0 -492
- package/.claude/helpers/patch-aggressive-prune.mjs +0 -184
- package/.claude/helpers/pattern-consolidator.sh +0 -86
- package/.claude/helpers/perf-worker.sh +0 -160
- package/.claude/helpers/quick-start.sh +0 -19
- package/.claude/helpers/router.cjs +0 -62
- package/.claude/helpers/security-scanner.sh +0 -127
- package/.claude/helpers/session.cjs +0 -125
- package/.claude/helpers/setup-mcp.sh +0 -18
- package/.claude/helpers/standard-checkpoint-hooks.sh +0 -189
- package/.claude/helpers/swarm-comms.sh +0 -353
- package/.claude/helpers/swarm-hooks.sh +0 -761
- package/.claude/helpers/swarm-monitor.sh +0 -211
- package/.claude/helpers/sync-v3-metrics.sh +0 -245
- package/.claude/helpers/update-v3-progress.sh +0 -166
- package/.claude/helpers/v3-quick-status.sh +0 -58
- package/.claude/helpers/v3.sh +0 -111
- package/.claude/helpers/validate-v3-config.sh +0 -216
- package/.claude/helpers/worker-manager.sh +0 -170
|
@@ -388,6 +388,9 @@ export async function bridgeStoreEntry(options) {
|
|
|
388
388
|
await cacheSet(registry, cacheKey, { id, key, namespace, content: value, embedding: embeddingJson });
|
|
389
389
|
// Phase 4: AttestationLog write audit
|
|
390
390
|
await logAttestation(registry, 'store', id, { key, namespace, hasEmbedding: !!embeddingJson });
|
|
391
|
+
// Update statusline vector stats cache (debounced)
|
|
392
|
+
if (embeddingJson)
|
|
393
|
+
refreshVectorStatsCache();
|
|
391
394
|
return {
|
|
392
395
|
success: true,
|
|
393
396
|
id,
|
|
@@ -690,6 +693,9 @@ export async function bridgeDeleteEntry(options) {
|
|
|
690
693
|
catch {
|
|
691
694
|
// Non-fatal
|
|
692
695
|
}
|
|
696
|
+
// Update statusline vector stats cache (debounced)
|
|
697
|
+
if (changes > 0)
|
|
698
|
+
refreshVectorStatsCache();
|
|
693
699
|
return {
|
|
694
700
|
success: true,
|
|
695
701
|
deleted: changes > 0,
|
|
@@ -1547,4 +1553,64 @@ function cosineSim(a, b) {
|
|
|
1547
1553
|
const mag = Math.sqrt(normA * normB);
|
|
1548
1554
|
return mag === 0 ? 0 : dot / mag;
|
|
1549
1555
|
}
|
|
1556
|
+
// ===== Vector stats cache for statusline =====
|
|
1557
|
+
// Written after memory mutations so the statusline can read stats without
|
|
1558
|
+
// spawning a subprocess. Debounced to avoid thrashing during batch operations.
|
|
1559
|
+
/**
|
|
1560
|
+
* Write vector-stats.json cache file used by the statusline.
|
|
1561
|
+
* Synchronous — safe to call from short-lived CLI commands.
|
|
1562
|
+
* Uses the already-initialized registry; no-ops if registry isn't loaded.
|
|
1563
|
+
*/
|
|
1564
|
+
export function refreshVectorStatsCache(dbPathOverride) {
|
|
1565
|
+
try {
|
|
1566
|
+
const registry = registryInstance; // Use existing instance only, don't init
|
|
1567
|
+
if (!registry)
|
|
1568
|
+
return;
|
|
1569
|
+
const ctx = getDb(registry);
|
|
1570
|
+
if (!ctx?.db)
|
|
1571
|
+
return;
|
|
1572
|
+
let vectorCount = 0;
|
|
1573
|
+
let namespaces = 0;
|
|
1574
|
+
let dbSizeKB = 0;
|
|
1575
|
+
let hasHnsw = false;
|
|
1576
|
+
try {
|
|
1577
|
+
const countRow = ctx.db.prepare('SELECT COUNT(*) as c FROM memory_entries WHERE status = ? AND embedding IS NOT NULL').get('active');
|
|
1578
|
+
vectorCount = countRow?.c ?? 0;
|
|
1579
|
+
const nsRow = ctx.db.prepare('SELECT COUNT(DISTINCT namespace) as n FROM memory_entries WHERE status = ?').get('active');
|
|
1580
|
+
namespaces = nsRow?.n ?? 0;
|
|
1581
|
+
}
|
|
1582
|
+
catch {
|
|
1583
|
+
// Table may not exist yet
|
|
1584
|
+
}
|
|
1585
|
+
// DB file size
|
|
1586
|
+
const dbFile = dbPathOverride || getDbPath();
|
|
1587
|
+
try {
|
|
1588
|
+
const stat = fs.statSync(dbFile);
|
|
1589
|
+
dbSizeKB = Math.floor(stat.size / 1024);
|
|
1590
|
+
}
|
|
1591
|
+
catch { /* file may not exist */ }
|
|
1592
|
+
// HNSW index presence
|
|
1593
|
+
const root = getProjectRoot();
|
|
1594
|
+
const hnswPaths = [
|
|
1595
|
+
path.join(root, '.swarm', 'hnsw.index'),
|
|
1596
|
+
path.join(root, '.claude-flow', 'hnsw.index'),
|
|
1597
|
+
];
|
|
1598
|
+
for (const p of hnswPaths) {
|
|
1599
|
+
try {
|
|
1600
|
+
fs.statSync(p);
|
|
1601
|
+
hasHnsw = true;
|
|
1602
|
+
break;
|
|
1603
|
+
}
|
|
1604
|
+
catch { /* nope */ }
|
|
1605
|
+
}
|
|
1606
|
+
// Write cache file
|
|
1607
|
+
const cacheDir = path.join(root, '.claude-flow');
|
|
1608
|
+
if (!fs.existsSync(cacheDir))
|
|
1609
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
1610
|
+
fs.writeFileSync(path.join(cacheDir, 'vector-stats.json'), JSON.stringify({ vectorCount, dbSizeKB, namespaces, hasHnsw, updatedAt: Date.now() }));
|
|
1611
|
+
}
|
|
1612
|
+
catch {
|
|
1613
|
+
// Non-fatal — statusline falls back to file size estimate
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1550
1616
|
//# sourceMappingURL=memory-bridge.js.map
|
|
@@ -10,6 +10,41 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import * as fs from 'fs';
|
|
12
12
|
import * as path from 'path';
|
|
13
|
+
/**
|
|
14
|
+
* Write vector-stats.json cache for the statusline (no subprocess needed).
|
|
15
|
+
* Called after memory store/delete to keep the cache fresh.
|
|
16
|
+
* @param dbPath - path to the SQLite database file
|
|
17
|
+
* @param stats - optional exact counts from a db query already in progress
|
|
18
|
+
*/
|
|
19
|
+
function writeVectorStatsCache(dbPath, stats) {
|
|
20
|
+
try {
|
|
21
|
+
const fileStat = fs.statSync(dbPath);
|
|
22
|
+
const dbSizeKB = Math.floor(fileStat.size / 1024);
|
|
23
|
+
const vectorCount = stats?.vectorCount ?? 0;
|
|
24
|
+
const namespaces = stats?.namespaces ?? 0;
|
|
25
|
+
// Check HNSW index presence
|
|
26
|
+
const dbDir = path.dirname(dbPath);
|
|
27
|
+
const projectDir = path.dirname(dbDir); // .swarm -> project root
|
|
28
|
+
let hasHnsw = false;
|
|
29
|
+
for (const p of [
|
|
30
|
+
path.join(dbDir, 'hnsw.index'),
|
|
31
|
+
path.join(projectDir, '.claude-flow', 'hnsw.index'),
|
|
32
|
+
]) {
|
|
33
|
+
try {
|
|
34
|
+
fs.statSync(p);
|
|
35
|
+
hasHnsw = true;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
catch { /* nope */ }
|
|
39
|
+
}
|
|
40
|
+
// Write to .claude-flow dir next to the .swarm dir
|
|
41
|
+
const cacheDir = path.join(projectDir, '.claude-flow');
|
|
42
|
+
if (!fs.existsSync(cacheDir))
|
|
43
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
44
|
+
fs.writeFileSync(path.join(cacheDir, 'vector-stats.json'), JSON.stringify({ vectorCount, dbSizeKB, namespaces, hasHnsw, updatedAt: Date.now() }));
|
|
45
|
+
}
|
|
46
|
+
catch { /* Non-fatal */ }
|
|
47
|
+
}
|
|
13
48
|
// ADR-053: Lazy import of AgentDB v3 bridge
|
|
14
49
|
let _bridge;
|
|
15
50
|
async function getBridge() {
|
|
@@ -1747,8 +1782,13 @@ export async function storeEntry(options) {
|
|
|
1747
1782
|
const bridge = await getBridge();
|
|
1748
1783
|
if (bridge) {
|
|
1749
1784
|
const bridgeResult = await bridge.bridgeStoreEntry(options);
|
|
1750
|
-
if (bridgeResult)
|
|
1785
|
+
if (bridgeResult) {
|
|
1786
|
+
// Update statusline cache after successful bridge store
|
|
1787
|
+
const swarmDir = path.join(process.cwd(), '.swarm');
|
|
1788
|
+
const dbFile = options.dbPath || path.join(swarmDir, 'memory.db');
|
|
1789
|
+
writeVectorStatsCache(dbFile);
|
|
1751
1790
|
return bridgeResult;
|
|
1791
|
+
}
|
|
1752
1792
|
}
|
|
1753
1793
|
// Fallback: raw sql.js
|
|
1754
1794
|
const { key, value, namespace = 'default', generateEmbeddingFlag = true, tags = [], ttl, dbPath: customPath, upsert = false } = options;
|
|
@@ -1805,6 +1845,15 @@ export async function storeEntry(options) {
|
|
|
1805
1845
|
// Save
|
|
1806
1846
|
const data = db.export();
|
|
1807
1847
|
fs.writeFileSync(dbPath, Buffer.from(data));
|
|
1848
|
+
// Query exact stats while DB is still open
|
|
1849
|
+
let vecCount = 0, nsCount = 0;
|
|
1850
|
+
try {
|
|
1851
|
+
const vc = db.exec("SELECT COUNT(*) FROM memory_entries WHERE status='active' AND embedding IS NOT NULL");
|
|
1852
|
+
vecCount = vc[0]?.values?.[0]?.[0] ?? 0;
|
|
1853
|
+
const nc = db.exec("SELECT COUNT(DISTINCT namespace) FROM memory_entries WHERE status='active'");
|
|
1854
|
+
nsCount = nc[0]?.values?.[0]?.[0] ?? 0;
|
|
1855
|
+
}
|
|
1856
|
+
catch { /* table may not have status column in older DBs */ }
|
|
1808
1857
|
db.close();
|
|
1809
1858
|
// Add to HNSW index for faster future searches
|
|
1810
1859
|
if (embeddingJson) {
|
|
@@ -1816,6 +1865,8 @@ export async function storeEntry(options) {
|
|
|
1816
1865
|
content: value
|
|
1817
1866
|
});
|
|
1818
1867
|
}
|
|
1868
|
+
// Update statusline cache with exact counts
|
|
1869
|
+
writeVectorStatsCache(dbPath, { vectorCount: vecCount, namespaces: nsCount });
|
|
1819
1870
|
return {
|
|
1820
1871
|
success: true,
|
|
1821
1872
|
id,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic daemon lock — prevents duplicate daemon processes.
|
|
3
|
+
*
|
|
4
|
+
* Uses fs.writeFileSync with { flag: 'wx' } (O_CREAT | O_EXCL) which is
|
|
5
|
+
* atomic on all platforms: the write fails immediately if the file exists,
|
|
6
|
+
* eliminating the TOCTOU race in the old PID-file approach.
|
|
7
|
+
*
|
|
8
|
+
* Also solves Windows PID recycling by storing a label in the lock payload
|
|
9
|
+
* and verifying the process command line before trusting a "live" PID.
|
|
10
|
+
*/
|
|
11
|
+
export interface DaemonLockPayload {
|
|
12
|
+
pid: number;
|
|
13
|
+
startedAt: number;
|
|
14
|
+
label: string;
|
|
15
|
+
}
|
|
16
|
+
/** Resolve the lock file path for a project root. */
|
|
17
|
+
export declare function lockPath(projectRoot: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Try to acquire the daemon lock atomically.
|
|
20
|
+
*
|
|
21
|
+
* @returns `{ acquired: true }` on success,
|
|
22
|
+
* `{ acquired: false, holder: pid }` if another daemon owns the lock.
|
|
23
|
+
*/
|
|
24
|
+
export declare function acquireDaemonLock(projectRoot: string, pid?: number): {
|
|
25
|
+
acquired: true;
|
|
26
|
+
} | {
|
|
27
|
+
acquired: false;
|
|
28
|
+
holder: number;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Release the daemon lock. Only removes if we own it (or force = true).
|
|
32
|
+
*/
|
|
33
|
+
export declare function releaseDaemonLock(projectRoot: string, pid?: number, force?: boolean): void;
|
|
34
|
+
/**
|
|
35
|
+
* Check if the daemon lock is currently held by a live daemon.
|
|
36
|
+
* Returns the holder PID or null.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getDaemonLockHolder(projectRoot: string): number | null;
|
|
39
|
+
//# sourceMappingURL=daemon-lock.d.ts.map
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic daemon lock — prevents duplicate daemon processes.
|
|
3
|
+
*
|
|
4
|
+
* Uses fs.writeFileSync with { flag: 'wx' } (O_CREAT | O_EXCL) which is
|
|
5
|
+
* atomic on all platforms: the write fails immediately if the file exists,
|
|
6
|
+
* eliminating the TOCTOU race in the old PID-file approach.
|
|
7
|
+
*
|
|
8
|
+
* Also solves Windows PID recycling by storing a label in the lock payload
|
|
9
|
+
* and verifying the process command line before trusting a "live" PID.
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
import { execSync } from 'child_process';
|
|
14
|
+
const LOCK_FILENAME = 'daemon.lock';
|
|
15
|
+
const LOCK_LABEL = 'moflo-daemon';
|
|
16
|
+
/** Resolve the lock file path for a project root. */
|
|
17
|
+
export function lockPath(projectRoot) {
|
|
18
|
+
return join(projectRoot, '.claude-flow', LOCK_FILENAME);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Try to acquire the daemon lock atomically.
|
|
22
|
+
*
|
|
23
|
+
* @returns `{ acquired: true }` on success,
|
|
24
|
+
* `{ acquired: false, holder: pid }` if another daemon owns the lock.
|
|
25
|
+
*/
|
|
26
|
+
export function acquireDaemonLock(projectRoot, pid = process.pid) {
|
|
27
|
+
const lock = lockPath(projectRoot);
|
|
28
|
+
const stateDir = join(projectRoot, '.claude-flow');
|
|
29
|
+
// Ensure state directory exists
|
|
30
|
+
if (!fs.existsSync(stateDir)) {
|
|
31
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
const payload = {
|
|
34
|
+
pid,
|
|
35
|
+
startedAt: Date.now(),
|
|
36
|
+
label: LOCK_LABEL,
|
|
37
|
+
};
|
|
38
|
+
// Attempt 1: atomic exclusive create
|
|
39
|
+
const result = tryExclusiveWrite(lock, payload);
|
|
40
|
+
if (result === 'ok') {
|
|
41
|
+
return { acquired: true };
|
|
42
|
+
}
|
|
43
|
+
// File already exists — check if the holder is still a live daemon
|
|
44
|
+
const existing = readLockPayload(lock);
|
|
45
|
+
if (!existing) {
|
|
46
|
+
// Corrupt or unreadable — remove and retry once
|
|
47
|
+
safeUnlink(lock);
|
|
48
|
+
return tryExclusiveWrite(lock, payload) === 'ok'
|
|
49
|
+
? { acquired: true }
|
|
50
|
+
: { acquired: false, holder: -1 };
|
|
51
|
+
}
|
|
52
|
+
// Same PID as us? We already hold it (re-entrant).
|
|
53
|
+
if (existing.pid === pid) {
|
|
54
|
+
return { acquired: true };
|
|
55
|
+
}
|
|
56
|
+
// Is the process alive AND actually a moflo daemon?
|
|
57
|
+
if (isProcessAlive(existing.pid) && isDaemonProcess(existing.pid)) {
|
|
58
|
+
return { acquired: false, holder: existing.pid };
|
|
59
|
+
}
|
|
60
|
+
// Stale lock (dead process or recycled PID) — remove and retry once
|
|
61
|
+
safeUnlink(lock);
|
|
62
|
+
return tryExclusiveWrite(lock, payload) === 'ok'
|
|
63
|
+
? { acquired: true }
|
|
64
|
+
: { acquired: false, holder: -1 };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Release the daemon lock. Only removes if we own it (or force = true).
|
|
68
|
+
*/
|
|
69
|
+
export function releaseDaemonLock(projectRoot, pid = process.pid, force = false) {
|
|
70
|
+
const lock = lockPath(projectRoot);
|
|
71
|
+
if (!fs.existsSync(lock))
|
|
72
|
+
return;
|
|
73
|
+
if (force) {
|
|
74
|
+
safeUnlink(lock);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const existing = readLockPayload(lock);
|
|
78
|
+
if (existing && existing.pid === pid) {
|
|
79
|
+
safeUnlink(lock);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if the daemon lock is currently held by a live daemon.
|
|
84
|
+
* Returns the holder PID or null.
|
|
85
|
+
*/
|
|
86
|
+
export function getDaemonLockHolder(projectRoot) {
|
|
87
|
+
const lock = lockPath(projectRoot);
|
|
88
|
+
if (!fs.existsSync(lock))
|
|
89
|
+
return null;
|
|
90
|
+
const existing = readLockPayload(lock);
|
|
91
|
+
if (!existing) {
|
|
92
|
+
// Corrupt lock file — clean it up
|
|
93
|
+
safeUnlink(lock);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
if (isProcessAlive(existing.pid) && isDaemonProcess(existing.pid)) {
|
|
97
|
+
return existing.pid;
|
|
98
|
+
}
|
|
99
|
+
// Stale — clean it up opportunistically
|
|
100
|
+
safeUnlink(lock);
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Internal helpers
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
function tryExclusiveWrite(path, payload) {
|
|
107
|
+
try {
|
|
108
|
+
fs.writeFileSync(path, JSON.stringify(payload), { flag: 'wx' });
|
|
109
|
+
return 'ok';
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
if (err.code === 'EEXIST')
|
|
113
|
+
return 'exists';
|
|
114
|
+
// Other errors (permissions, disk full) — treat as failure to acquire
|
|
115
|
+
return 'exists';
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function readLockPayload(path) {
|
|
119
|
+
try {
|
|
120
|
+
const raw = fs.readFileSync(path, 'utf-8');
|
|
121
|
+
const data = JSON.parse(raw);
|
|
122
|
+
if (typeof data.pid === 'number' && typeof data.startedAt === 'number') {
|
|
123
|
+
return data;
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function safeUnlink(path) {
|
|
132
|
+
try {
|
|
133
|
+
fs.unlinkSync(path);
|
|
134
|
+
}
|
|
135
|
+
catch { /* ignore — file may already be gone */ }
|
|
136
|
+
}
|
|
137
|
+
function isProcessAlive(pid) {
|
|
138
|
+
try {
|
|
139
|
+
process.kill(pid, 0);
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Cross-platform check: is this PID actually a moflo/claude-flow daemon?
|
|
148
|
+
*
|
|
149
|
+
* This prevents false positives from Windows PID recycling, where a dead
|
|
150
|
+
* daemon's PID gets reused by an unrelated process (e.g. Chrome).
|
|
151
|
+
*
|
|
152
|
+
* - Windows: uses `tasklist /FI` to check the process image + command line
|
|
153
|
+
* - Linux: reads /proc/<pid>/cmdline
|
|
154
|
+
* - macOS: uses `ps -p <pid> -o command=`
|
|
155
|
+
*
|
|
156
|
+
* Falls back to `true` (trust process.kill) if the platform check fails,
|
|
157
|
+
* to avoid accidentally allowing duplicates on exotic platforms.
|
|
158
|
+
*/
|
|
159
|
+
function isDaemonProcess(pid) {
|
|
160
|
+
try {
|
|
161
|
+
if (process.platform === 'win32') {
|
|
162
|
+
return isDaemonProcessWindows(pid);
|
|
163
|
+
}
|
|
164
|
+
else if (process.platform === 'linux') {
|
|
165
|
+
return isDaemonProcessLinux(pid);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// macOS and others
|
|
169
|
+
return isDaemonProcessUnix(pid);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// If platform check fails, trust the kill(0) result to avoid
|
|
174
|
+
// accidentally allowing duplicates
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function isDaemonProcessWindows(pid) {
|
|
179
|
+
try {
|
|
180
|
+
const result = execSync(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { encoding: 'utf-8', timeout: 3000, windowsHide: true });
|
|
181
|
+
// tasklist returns the image name + PID in CSV; check it's a node process
|
|
182
|
+
// and then verify via wmic/powershell that the command line contains daemon keywords
|
|
183
|
+
if (!result.includes('node'))
|
|
184
|
+
return false;
|
|
185
|
+
const cmdResult = execSync(`powershell -NoProfile -Command "(Get-CimInstance Win32_Process -Filter \\"ProcessId=${pid}\\").CommandLine"`, { encoding: 'utf-8', timeout: 5000, windowsHide: true });
|
|
186
|
+
return /daemon\s+start|moflo|claude-flow/i.test(cmdResult);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return true; // fallback: trust kill(0)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function isDaemonProcessLinux(pid) {
|
|
193
|
+
try {
|
|
194
|
+
const cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, 'utf-8');
|
|
195
|
+
return /daemon.*start|moflo|claude-flow/i.test(cmdline);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return true; // fallback
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function isDaemonProcessUnix(pid) {
|
|
202
|
+
try {
|
|
203
|
+
const result = execSync(`ps -p ${pid} -o command=`, {
|
|
204
|
+
encoding: 'utf-8',
|
|
205
|
+
timeout: 3000,
|
|
206
|
+
});
|
|
207
|
+
return /daemon.*start|moflo|claude-flow/i.test(result);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return true; // fallback
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=daemon-lock.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moflo/cli",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MoFlo CLI — AI agent orchestration with specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -81,18 +81,14 @@
|
|
|
81
81
|
"publish:all": "./scripts/publish.sh"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
|
-
"typescript": "^5.3.0"
|
|
85
|
-
"vitest": "^4.0.16"
|
|
84
|
+
"typescript": "^5.3.0"
|
|
86
85
|
},
|
|
87
86
|
"dependencies": {
|
|
88
|
-
"@claude-flow/mcp": "^3.0.0-alpha.8",
|
|
89
|
-
"@claude-flow/shared": "^3.0.0-alpha.1",
|
|
90
87
|
"@noble/ed25519": "^2.1.0",
|
|
91
88
|
"semver": "^7.6.0"
|
|
92
89
|
},
|
|
93
90
|
"optionalDependencies": {
|
|
94
91
|
"@claude-flow/aidefence": "^3.0.2",
|
|
95
|
-
"@claude-flow/codex": "^3.0.0-alpha.8",
|
|
96
92
|
"@claude-flow/embeddings": "^3.0.0-alpha.12",
|
|
97
93
|
"@claude-flow/guidance": "^3.0.0-alpha.1",
|
|
98
94
|
"@claude-flow/memory": "^3.0.0-alpha.11",
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# RuFlo V3 Helpers
|
|
2
|
-
|
|
3
|
-
This directory contains helper scripts and utilities for V3 development.
|
|
4
|
-
|
|
5
|
-
## 🚀 Quick Start
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Initialize V3 development environment
|
|
9
|
-
.claude/helpers/v3.sh init
|
|
10
|
-
|
|
11
|
-
# Quick status check
|
|
12
|
-
.claude/helpers/v3.sh status
|
|
13
|
-
|
|
14
|
-
# Update progress metrics
|
|
15
|
-
.claude/helpers/v3.sh update domain 3
|
|
16
|
-
.claude/helpers/v3.sh update agent 8
|
|
17
|
-
.claude/helpers/v3.sh update security 2
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Available Helpers
|
|
21
|
-
|
|
22
|
-
### 🎛️ V3 Master Tool
|
|
23
|
-
- **`v3.sh`** - Main command-line interface for all V3 operations
|
|
24
|
-
```bash
|
|
25
|
-
.claude/helpers/v3.sh help # Show all commands
|
|
26
|
-
.claude/helpers/v3.sh status # Quick development status
|
|
27
|
-
.claude/helpers/v3.sh update domain 3 # Update specific metrics
|
|
28
|
-
.claude/helpers/v3.sh validate # Validate configuration
|
|
29
|
-
.claude/helpers/v3.sh full-status # Complete status overview
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### 📊 V3 Progress Management
|
|
33
|
-
- **`update-v3-progress.sh`** - Update V3 development metrics
|
|
34
|
-
```bash
|
|
35
|
-
# Usage examples:
|
|
36
|
-
.claude/helpers/update-v3-progress.sh domain 3 # Mark 3 domains complete
|
|
37
|
-
.claude/helpers/update-v3-progress.sh agent 8 # 8 agents active
|
|
38
|
-
.claude/helpers/update-v3-progress.sh security 2 # 2 CVEs fixed
|
|
39
|
-
.claude/helpers/update-v3-progress.sh performance 2.5x # Performance boost
|
|
40
|
-
.claude/helpers/update-v3-progress.sh status # Show current status
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### 🔍 Configuration Validation
|
|
44
|
-
- **`validate-v3-config.sh`** - Comprehensive environment validation
|
|
45
|
-
- Checks all required directories and files
|
|
46
|
-
- Validates JSON configuration files
|
|
47
|
-
- Verifies Node.js and development tools
|
|
48
|
-
- Confirms Git repository status
|
|
49
|
-
- Validates file permissions
|
|
50
|
-
|
|
51
|
-
### ⚡ Quick Status
|
|
52
|
-
- **`v3-quick-status.sh`** - Compact development progress overview
|
|
53
|
-
- Shows domain, agent, and DDD progress
|
|
54
|
-
- Displays security and performance metrics
|
|
55
|
-
- Color-coded status indicators
|
|
56
|
-
- Current Git branch information
|
|
57
|
-
|
|
58
|
-
## Helper Script Standards
|
|
59
|
-
|
|
60
|
-
### File Naming
|
|
61
|
-
- Use kebab-case: `update-v3-progress.sh`
|
|
62
|
-
- Include version prefix: `v3-*` for V3-specific helpers
|
|
63
|
-
- Use descriptive names that indicate purpose
|
|
64
|
-
|
|
65
|
-
### Script Requirements
|
|
66
|
-
- Must be executable (`chmod +x`)
|
|
67
|
-
- Include proper error handling (`set -e`)
|
|
68
|
-
- Provide usage help when called without arguments
|
|
69
|
-
- Use consistent exit codes (0 = success, non-zero = error)
|
|
70
|
-
|
|
71
|
-
### Configuration Integration
|
|
72
|
-
Helpers are configured in `.claude/settings.json`:
|
|
73
|
-
```json
|
|
74
|
-
{
|
|
75
|
-
"helpers": {
|
|
76
|
-
"directory": ".claude/helpers",
|
|
77
|
-
"enabled": true,
|
|
78
|
-
"v3ProgressUpdater": ".claude/helpers/update-v3-progress.sh"
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## Development Guidelines
|
|
84
|
-
|
|
85
|
-
1. **Security First**: All helpers must validate inputs
|
|
86
|
-
2. **Idempotent**: Scripts should be safe to run multiple times
|
|
87
|
-
3. **Fast Execution**: Keep helper execution under 1 second when possible
|
|
88
|
-
4. **Clear Output**: Provide clear success/error messages
|
|
89
|
-
5. **JSON Safe**: When updating JSON files, use `jq` for safety
|
|
90
|
-
|
|
91
|
-
## Adding New Helpers
|
|
92
|
-
|
|
93
|
-
1. Create script in `.claude/helpers/`
|
|
94
|
-
2. Make executable: `chmod +x script-name.sh`
|
|
95
|
-
3. Add to settings.json helpers section
|
|
96
|
-
4. Test thoroughly before committing
|
|
97
|
-
5. Update this README with usage documentation
|