moflo 4.5.0 โ 4.6.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/package.json +1 -1
- package/src/@claude-flow/cli/dist/src/commands/appliance.js +12 -12
- package/src/@claude-flow/cli/dist/src/commands/benchmark.js +2 -2
- package/src/@claude-flow/cli/dist/src/commands/claims.js +1 -1
- package/src/@claude-flow/cli/dist/src/commands/config.js +1 -1
- package/src/@claude-flow/cli/dist/src/commands/daemon.js +25 -3
- package/src/@claude-flow/cli/dist/src/commands/deployment.js +1 -1
- package/src/@claude-flow/cli/dist/src/commands/doctor.js +23 -6
- package/src/@claude-flow/cli/dist/src/commands/embeddings.js +1 -1
- package/src/@claude-flow/cli/dist/src/commands/hooks.js +1 -1
- package/src/@claude-flow/cli/dist/src/commands/init.js +14 -12
- package/src/@claude-flow/cli/dist/src/commands/neural.js +1 -1
- package/src/@claude-flow/cli/dist/src/commands/orc.d.ts +2 -2
- package/src/@claude-flow/cli/dist/src/commands/orc.js +12 -12
- package/src/@claude-flow/cli/dist/src/commands/performance.js +1 -1
- package/src/@claude-flow/cli/dist/src/commands/plugins.js +1 -1
- package/src/@claude-flow/cli/dist/src/commands/providers.js +1 -1
- package/src/@claude-flow/cli/dist/src/commands/security.js +1 -1
- package/src/@claude-flow/cli/dist/src/commands/start.js +11 -11
- package/src/@claude-flow/cli/dist/src/commands/status.js +3 -3
- package/src/@claude-flow/cli/dist/src/commands/transfer-store.js +1 -1
- package/src/@claude-flow/cli/dist/src/config/moflo-config.d.ts +30 -0
- package/src/@claude-flow/cli/dist/src/config/moflo-config.js +103 -7
- package/src/@claude-flow/cli/dist/src/index.d.ts +1 -1
- package/src/@claude-flow/cli/dist/src/index.js +3 -3
- package/src/@claude-flow/cli/dist/src/init/claudemd-generator.js +1 -1
- package/src/@claude-flow/cli/dist/src/init/executor.js +9 -12
- package/src/@claude-flow/cli/dist/src/init/helpers-generator.js +640 -640
- package/src/@claude-flow/cli/dist/src/init/moflo-init.js +646 -192
- package/src/@claude-flow/cli/dist/src/init/settings-generator.js +7 -12
- package/src/@claude-flow/cli/dist/src/init/statusline-generator.d.ts +1 -1
- package/src/@claude-flow/cli/dist/src/init/statusline-generator.js +784 -784
- package/src/@claude-flow/cli/dist/src/mcp-tools/agentdb-tools.js +12 -12
- package/src/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +122 -66
- package/src/@claude-flow/cli/dist/src/memory/intelligence.js +5 -1
- package/src/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +1 -1
- package/src/@claude-flow/cli/dist/src/memory/memory-initializer.js +371 -371
- package/src/@claude-flow/cli/dist/src/parser.d.ts +10 -0
- package/src/@claude-flow/cli/dist/src/parser.js +49 -3
- package/src/@claude-flow/cli/dist/src/plugins/store/discovery.js +1 -1
- package/src/@claude-flow/cli/dist/src/runtime/headless.js +30 -30
- package/src/@claude-flow/cli/dist/src/services/claim-service.js +1 -1
- package/src/@claude-flow/cli/dist/src/services/ruvector-training.js +11 -5
- package/src/@claude-flow/cli/dist/src/services/workflow-gate.d.ts +13 -3
- package/src/@claude-flow/cli/dist/src/services/workflow-gate.js +73 -5
- package/src/@claude-flow/cli/dist/src/types.d.ts +1 -1
- package/src/@claude-flow/cli/dist/src/types.js +1 -1
|
@@ -7,348 +7,348 @@ import { generateStatuslineScript, generateStatuslineHook } from './statusline-g
|
|
|
7
7
|
* Generate pre-commit hook script
|
|
8
8
|
*/
|
|
9
9
|
export function generatePreCommitHook() {
|
|
10
|
-
return `#!/bin/bash
|
|
11
|
-
# Claude Flow Pre-Commit Hook
|
|
12
|
-
# Validates code quality before commit
|
|
13
|
-
|
|
14
|
-
set -e
|
|
15
|
-
|
|
16
|
-
echo "๐ Running Claude Flow pre-commit checks..."
|
|
17
|
-
|
|
18
|
-
# Get staged files
|
|
19
|
-
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
|
|
20
|
-
|
|
21
|
-
# Run validation for each staged file
|
|
22
|
-
for FILE in $STAGED_FILES; do
|
|
23
|
-
if [[ "$FILE" =~ \\.(ts|js|tsx|jsx)$ ]]; then
|
|
24
|
-
echo " Validating: $FILE"
|
|
25
|
-
npx @claude-flow/cli hooks pre-edit --file "$FILE" --validate-syntax 2>/dev/null || true
|
|
26
|
-
fi
|
|
27
|
-
done
|
|
28
|
-
|
|
29
|
-
# Run tests if available
|
|
30
|
-
if [ -f "package.json" ] && grep -q '"test"' package.json; then
|
|
31
|
-
echo "๐งช Running tests..."
|
|
32
|
-
npm test --if-present 2>/dev/null || echo " Tests skipped or failed"
|
|
33
|
-
fi
|
|
34
|
-
|
|
35
|
-
echo "โ
Pre-commit checks complete"
|
|
10
|
+
return `#!/bin/bash
|
|
11
|
+
# Claude Flow Pre-Commit Hook
|
|
12
|
+
# Validates code quality before commit
|
|
13
|
+
|
|
14
|
+
set -e
|
|
15
|
+
|
|
16
|
+
echo "๐ Running Claude Flow pre-commit checks..."
|
|
17
|
+
|
|
18
|
+
# Get staged files
|
|
19
|
+
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
|
|
20
|
+
|
|
21
|
+
# Run validation for each staged file
|
|
22
|
+
for FILE in $STAGED_FILES; do
|
|
23
|
+
if [[ "$FILE" =~ \\.(ts|js|tsx|jsx)$ ]]; then
|
|
24
|
+
echo " Validating: $FILE"
|
|
25
|
+
npx @claude-flow/cli hooks pre-edit --file "$FILE" --validate-syntax 2>/dev/null || true
|
|
26
|
+
fi
|
|
27
|
+
done
|
|
28
|
+
|
|
29
|
+
# Run tests if available
|
|
30
|
+
if [ -f "package.json" ] && grep -q '"test"' package.json; then
|
|
31
|
+
echo "๐งช Running tests..."
|
|
32
|
+
npm test --if-present 2>/dev/null || echo " Tests skipped or failed"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
echo "โ
Pre-commit checks complete"
|
|
36
36
|
`;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* Generate post-commit hook script
|
|
40
40
|
*/
|
|
41
41
|
export function generatePostCommitHook() {
|
|
42
|
-
return `#!/bin/bash
|
|
43
|
-
# Claude Flow Post-Commit Hook
|
|
44
|
-
# Records commit metrics and trains patterns
|
|
45
|
-
|
|
46
|
-
COMMIT_HASH=$(git rev-parse HEAD)
|
|
47
|
-
COMMIT_MSG=$(git log -1 --pretty=%B)
|
|
48
|
-
|
|
49
|
-
echo "๐ Recording commit metrics..."
|
|
50
|
-
|
|
51
|
-
# Notify claude-flow of commit
|
|
52
|
-
npx @claude-flow/cli hooks notify \\
|
|
53
|
-
--message "Commit: $COMMIT_MSG" \\
|
|
54
|
-
--level info \\
|
|
55
|
-
--metadata '{"hash": "'$COMMIT_HASH'"}' 2>/dev/null || true
|
|
56
|
-
|
|
57
|
-
echo "โ
Commit recorded"
|
|
42
|
+
return `#!/bin/bash
|
|
43
|
+
# Claude Flow Post-Commit Hook
|
|
44
|
+
# Records commit metrics and trains patterns
|
|
45
|
+
|
|
46
|
+
COMMIT_HASH=$(git rev-parse HEAD)
|
|
47
|
+
COMMIT_MSG=$(git log -1 --pretty=%B)
|
|
48
|
+
|
|
49
|
+
echo "๐ Recording commit metrics..."
|
|
50
|
+
|
|
51
|
+
# Notify claude-flow of commit
|
|
52
|
+
npx @claude-flow/cli hooks notify \\
|
|
53
|
+
--message "Commit: $COMMIT_MSG" \\
|
|
54
|
+
--level info \\
|
|
55
|
+
--metadata '{"hash": "'$COMMIT_HASH'"}' 2>/dev/null || true
|
|
56
|
+
|
|
57
|
+
echo "โ
Commit recorded"
|
|
58
58
|
`;
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
61
|
* Generate session manager script
|
|
62
62
|
*/
|
|
63
63
|
export function generateSessionManager() {
|
|
64
|
-
return `#!/usr/bin/env node
|
|
65
|
-
/**
|
|
66
|
-
* Claude Flow Session Manager
|
|
67
|
-
* Handles session lifecycle: start, restore, end
|
|
68
|
-
*/
|
|
69
|
-
|
|
70
|
-
const fs = require('fs');
|
|
71
|
-
const path = require('path');
|
|
72
|
-
|
|
73
|
-
const SESSION_DIR = path.join(process.cwd(), '.claude-flow', 'sessions');
|
|
74
|
-
const SESSION_FILE = path.join(SESSION_DIR, 'current.json');
|
|
75
|
-
|
|
76
|
-
const commands = {
|
|
77
|
-
start: () => {
|
|
78
|
-
const sessionId = \`session-\${Date.now()}\`;
|
|
79
|
-
const session = {
|
|
80
|
-
id: sessionId,
|
|
81
|
-
startedAt: new Date().toISOString(),
|
|
82
|
-
cwd: process.cwd(),
|
|
83
|
-
context: {},
|
|
84
|
-
metrics: {
|
|
85
|
-
edits: 0,
|
|
86
|
-
commands: 0,
|
|
87
|
-
tasks: 0,
|
|
88
|
-
errors: 0,
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
fs.mkdirSync(SESSION_DIR, { recursive: true });
|
|
93
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
94
|
-
|
|
95
|
-
console.log(\`Session started: \${sessionId}\`);
|
|
96
|
-
return session;
|
|
97
|
-
},
|
|
98
|
-
|
|
99
|
-
restore: () => {
|
|
100
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
101
|
-
console.log('No session to restore');
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
106
|
-
session.restoredAt = new Date().toISOString();
|
|
107
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
108
|
-
|
|
109
|
-
console.log(\`Session restored: \${session.id}\`);
|
|
110
|
-
return session;
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
end: () => {
|
|
114
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
115
|
-
console.log('No active session');
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
120
|
-
session.endedAt = new Date().toISOString();
|
|
121
|
-
session.duration = Date.now() - new Date(session.startedAt).getTime();
|
|
122
|
-
|
|
123
|
-
// Archive session
|
|
124
|
-
const archivePath = path.join(SESSION_DIR, \`\${session.id}.json\`);
|
|
125
|
-
fs.writeFileSync(archivePath, JSON.stringify(session, null, 2));
|
|
126
|
-
fs.unlinkSync(SESSION_FILE);
|
|
127
|
-
|
|
128
|
-
console.log(\`Session ended: \${session.id}\`);
|
|
129
|
-
console.log(\`Duration: \${Math.round(session.duration / 1000 / 60)} minutes\`);
|
|
130
|
-
console.log(\`Metrics: \${JSON.stringify(session.metrics)}\`);
|
|
131
|
-
|
|
132
|
-
return session;
|
|
133
|
-
},
|
|
134
|
-
|
|
135
|
-
status: () => {
|
|
136
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
137
|
-
console.log('No active session');
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
142
|
-
const duration = Date.now() - new Date(session.startedAt).getTime();
|
|
143
|
-
|
|
144
|
-
console.log(\`Session: \${session.id}\`);
|
|
145
|
-
console.log(\`Started: \${session.startedAt}\`);
|
|
146
|
-
console.log(\`Duration: \${Math.round(duration / 1000 / 60)} minutes\`);
|
|
147
|
-
console.log(\`Metrics: \${JSON.stringify(session.metrics)}\`);
|
|
148
|
-
|
|
149
|
-
return session;
|
|
150
|
-
},
|
|
151
|
-
|
|
152
|
-
update: (key, value) => {
|
|
153
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
154
|
-
console.log('No active session');
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
159
|
-
session.context[key] = value;
|
|
160
|
-
session.updatedAt = new Date().toISOString();
|
|
161
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
162
|
-
|
|
163
|
-
return session;
|
|
164
|
-
},
|
|
165
|
-
|
|
166
|
-
metric: (name) => {
|
|
167
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
172
|
-
if (session.metrics[name] !== undefined) {
|
|
173
|
-
session.metrics[name]++;
|
|
174
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return session;
|
|
178
|
-
},
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
// CLI
|
|
182
|
-
const [,, command, ...args] = process.argv;
|
|
183
|
-
|
|
184
|
-
if (command && commands[command]) {
|
|
185
|
-
commands[command](...args);
|
|
186
|
-
} else {
|
|
187
|
-
console.log('Usage: session.js <start|restore|end|status|update|metric> [args]');
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
module.exports = commands;
|
|
64
|
+
return `#!/usr/bin/env node
|
|
65
|
+
/**
|
|
66
|
+
* Claude Flow Session Manager
|
|
67
|
+
* Handles session lifecycle: start, restore, end
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
const fs = require('fs');
|
|
71
|
+
const path = require('path');
|
|
72
|
+
|
|
73
|
+
const SESSION_DIR = path.join(process.cwd(), '.claude-flow', 'sessions');
|
|
74
|
+
const SESSION_FILE = path.join(SESSION_DIR, 'current.json');
|
|
75
|
+
|
|
76
|
+
const commands = {
|
|
77
|
+
start: () => {
|
|
78
|
+
const sessionId = \`session-\${Date.now()}\`;
|
|
79
|
+
const session = {
|
|
80
|
+
id: sessionId,
|
|
81
|
+
startedAt: new Date().toISOString(),
|
|
82
|
+
cwd: process.cwd(),
|
|
83
|
+
context: {},
|
|
84
|
+
metrics: {
|
|
85
|
+
edits: 0,
|
|
86
|
+
commands: 0,
|
|
87
|
+
tasks: 0,
|
|
88
|
+
errors: 0,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
fs.mkdirSync(SESSION_DIR, { recursive: true });
|
|
93
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
94
|
+
|
|
95
|
+
console.log(\`Session started: \${sessionId}\`);
|
|
96
|
+
return session;
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
restore: () => {
|
|
100
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
101
|
+
console.log('No session to restore');
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
106
|
+
session.restoredAt = new Date().toISOString();
|
|
107
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
108
|
+
|
|
109
|
+
console.log(\`Session restored: \${session.id}\`);
|
|
110
|
+
return session;
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
end: () => {
|
|
114
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
115
|
+
console.log('No active session');
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
120
|
+
session.endedAt = new Date().toISOString();
|
|
121
|
+
session.duration = Date.now() - new Date(session.startedAt).getTime();
|
|
122
|
+
|
|
123
|
+
// Archive session
|
|
124
|
+
const archivePath = path.join(SESSION_DIR, \`\${session.id}.json\`);
|
|
125
|
+
fs.writeFileSync(archivePath, JSON.stringify(session, null, 2));
|
|
126
|
+
fs.unlinkSync(SESSION_FILE);
|
|
127
|
+
|
|
128
|
+
console.log(\`Session ended: \${session.id}\`);
|
|
129
|
+
console.log(\`Duration: \${Math.round(session.duration / 1000 / 60)} minutes\`);
|
|
130
|
+
console.log(\`Metrics: \${JSON.stringify(session.metrics)}\`);
|
|
131
|
+
|
|
132
|
+
return session;
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
status: () => {
|
|
136
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
137
|
+
console.log('No active session');
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
142
|
+
const duration = Date.now() - new Date(session.startedAt).getTime();
|
|
143
|
+
|
|
144
|
+
console.log(\`Session: \${session.id}\`);
|
|
145
|
+
console.log(\`Started: \${session.startedAt}\`);
|
|
146
|
+
console.log(\`Duration: \${Math.round(duration / 1000 / 60)} minutes\`);
|
|
147
|
+
console.log(\`Metrics: \${JSON.stringify(session.metrics)}\`);
|
|
148
|
+
|
|
149
|
+
return session;
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
update: (key, value) => {
|
|
153
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
154
|
+
console.log('No active session');
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
159
|
+
session.context[key] = value;
|
|
160
|
+
session.updatedAt = new Date().toISOString();
|
|
161
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
162
|
+
|
|
163
|
+
return session;
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
metric: (name) => {
|
|
167
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
172
|
+
if (session.metrics[name] !== undefined) {
|
|
173
|
+
session.metrics[name]++;
|
|
174
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return session;
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// CLI
|
|
182
|
+
const [,, command, ...args] = process.argv;
|
|
183
|
+
|
|
184
|
+
if (command && commands[command]) {
|
|
185
|
+
commands[command](...args);
|
|
186
|
+
} else {
|
|
187
|
+
console.log('Usage: session.js <start|restore|end|status|update|metric> [args]');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = commands;
|
|
191
191
|
`;
|
|
192
192
|
}
|
|
193
193
|
/**
|
|
194
194
|
* Generate agent router script
|
|
195
195
|
*/
|
|
196
196
|
export function generateAgentRouter() {
|
|
197
|
-
return `#!/usr/bin/env node
|
|
198
|
-
/**
|
|
199
|
-
* Claude Flow Agent Router
|
|
200
|
-
* Routes tasks to optimal agents based on learned patterns
|
|
201
|
-
*/
|
|
202
|
-
|
|
203
|
-
const AGENT_CAPABILITIES = {
|
|
204
|
-
coder: ['code-generation', 'refactoring', 'debugging', 'implementation'],
|
|
205
|
-
tester: ['unit-testing', 'integration-testing', 'coverage', 'test-generation'],
|
|
206
|
-
reviewer: ['code-review', 'security-audit', 'quality-check', 'best-practices'],
|
|
207
|
-
researcher: ['web-search', 'documentation', 'analysis', 'summarization'],
|
|
208
|
-
architect: ['system-design', 'architecture', 'patterns', 'scalability'],
|
|
209
|
-
'backend-dev': ['api', 'database', 'server', 'authentication'],
|
|
210
|
-
'frontend-dev': ['ui', 'react', 'css', 'components'],
|
|
211
|
-
devops: ['ci-cd', 'docker', 'deployment', 'infrastructure'],
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
const TASK_PATTERNS = {
|
|
215
|
-
// Code patterns
|
|
216
|
-
'implement|create|build|add|write code': 'coder',
|
|
217
|
-
'test|spec|coverage|unit test|integration': 'tester',
|
|
218
|
-
'review|audit|check|validate|security': 'reviewer',
|
|
219
|
-
'research|find|search|documentation|explore': 'researcher',
|
|
220
|
-
'design|architect|structure|plan': 'architect',
|
|
221
|
-
|
|
222
|
-
// Domain patterns
|
|
223
|
-
'api|endpoint|server|backend|database': 'backend-dev',
|
|
224
|
-
'ui|frontend|component|react|css|style': 'frontend-dev',
|
|
225
|
-
'deploy|docker|ci|cd|pipeline|infrastructure': 'devops',
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
function routeTask(task) {
|
|
229
|
-
const taskLower = task.toLowerCase();
|
|
230
|
-
|
|
231
|
-
// Check patterns
|
|
232
|
-
for (const [pattern, agent] of Object.entries(TASK_PATTERNS)) {
|
|
233
|
-
const regex = new RegExp(pattern, 'i');
|
|
234
|
-
if (regex.test(taskLower)) {
|
|
235
|
-
return {
|
|
236
|
-
agent,
|
|
237
|
-
confidence: 0.8,
|
|
238
|
-
reason: \`Matched pattern: \${pattern}\`,
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Default to coder for unknown tasks
|
|
244
|
-
return {
|
|
245
|
-
agent: 'coder',
|
|
246
|
-
confidence: 0.5,
|
|
247
|
-
reason: 'Default routing - no specific pattern matched',
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// CLI
|
|
252
|
-
const task = process.argv.slice(2).join(' ');
|
|
253
|
-
|
|
254
|
-
if (task) {
|
|
255
|
-
const result = routeTask(task);
|
|
256
|
-
console.log(JSON.stringify(result, null, 2));
|
|
257
|
-
} else {
|
|
258
|
-
console.log('Usage: router.js <task description>');
|
|
259
|
-
console.log('\\nAvailable agents:', Object.keys(AGENT_CAPABILITIES).join(', '));
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
module.exports = { routeTask, AGENT_CAPABILITIES, TASK_PATTERNS };
|
|
197
|
+
return `#!/usr/bin/env node
|
|
198
|
+
/**
|
|
199
|
+
* Claude Flow Agent Router
|
|
200
|
+
* Routes tasks to optimal agents based on learned patterns
|
|
201
|
+
*/
|
|
202
|
+
|
|
203
|
+
const AGENT_CAPABILITIES = {
|
|
204
|
+
coder: ['code-generation', 'refactoring', 'debugging', 'implementation'],
|
|
205
|
+
tester: ['unit-testing', 'integration-testing', 'coverage', 'test-generation'],
|
|
206
|
+
reviewer: ['code-review', 'security-audit', 'quality-check', 'best-practices'],
|
|
207
|
+
researcher: ['web-search', 'documentation', 'analysis', 'summarization'],
|
|
208
|
+
architect: ['system-design', 'architecture', 'patterns', 'scalability'],
|
|
209
|
+
'backend-dev': ['api', 'database', 'server', 'authentication'],
|
|
210
|
+
'frontend-dev': ['ui', 'react', 'css', 'components'],
|
|
211
|
+
devops: ['ci-cd', 'docker', 'deployment', 'infrastructure'],
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const TASK_PATTERNS = {
|
|
215
|
+
// Code patterns
|
|
216
|
+
'implement|create|build|add|write code': 'coder',
|
|
217
|
+
'test|spec|coverage|unit test|integration': 'tester',
|
|
218
|
+
'review|audit|check|validate|security': 'reviewer',
|
|
219
|
+
'research|find|search|documentation|explore': 'researcher',
|
|
220
|
+
'design|architect|structure|plan': 'architect',
|
|
221
|
+
|
|
222
|
+
// Domain patterns
|
|
223
|
+
'api|endpoint|server|backend|database': 'backend-dev',
|
|
224
|
+
'ui|frontend|component|react|css|style': 'frontend-dev',
|
|
225
|
+
'deploy|docker|ci|cd|pipeline|infrastructure': 'devops',
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
function routeTask(task) {
|
|
229
|
+
const taskLower = task.toLowerCase();
|
|
230
|
+
|
|
231
|
+
// Check patterns
|
|
232
|
+
for (const [pattern, agent] of Object.entries(TASK_PATTERNS)) {
|
|
233
|
+
const regex = new RegExp(pattern, 'i');
|
|
234
|
+
if (regex.test(taskLower)) {
|
|
235
|
+
return {
|
|
236
|
+
agent,
|
|
237
|
+
confidence: 0.8,
|
|
238
|
+
reason: \`Matched pattern: \${pattern}\`,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Default to coder for unknown tasks
|
|
244
|
+
return {
|
|
245
|
+
agent: 'coder',
|
|
246
|
+
confidence: 0.5,
|
|
247
|
+
reason: 'Default routing - no specific pattern matched',
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// CLI
|
|
252
|
+
const task = process.argv.slice(2).join(' ');
|
|
253
|
+
|
|
254
|
+
if (task) {
|
|
255
|
+
const result = routeTask(task);
|
|
256
|
+
console.log(JSON.stringify(result, null, 2));
|
|
257
|
+
} else {
|
|
258
|
+
console.log('Usage: router.js <task description>');
|
|
259
|
+
console.log('\\nAvailable agents:', Object.keys(AGENT_CAPABILITIES).join(', '));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
module.exports = { routeTask, AGENT_CAPABILITIES, TASK_PATTERNS };
|
|
263
263
|
`;
|
|
264
264
|
}
|
|
265
265
|
/**
|
|
266
266
|
* Generate memory helper script
|
|
267
267
|
*/
|
|
268
268
|
export function generateMemoryHelper() {
|
|
269
|
-
return `#!/usr/bin/env node
|
|
270
|
-
/**
|
|
271
|
-
* Claude Flow Memory Helper
|
|
272
|
-
* Simple key-value memory for cross-session context
|
|
273
|
-
*/
|
|
274
|
-
|
|
275
|
-
const fs = require('fs');
|
|
276
|
-
const path = require('path');
|
|
277
|
-
|
|
278
|
-
const MEMORY_DIR = path.join(process.cwd(), '.claude-flow', 'data');
|
|
279
|
-
const MEMORY_FILE = path.join(MEMORY_DIR, 'memory.json');
|
|
280
|
-
|
|
281
|
-
function loadMemory() {
|
|
282
|
-
try {
|
|
283
|
-
if (fs.existsSync(MEMORY_FILE)) {
|
|
284
|
-
return JSON.parse(fs.readFileSync(MEMORY_FILE, 'utf-8'));
|
|
285
|
-
}
|
|
286
|
-
} catch (e) {
|
|
287
|
-
// Ignore
|
|
288
|
-
}
|
|
289
|
-
return {};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function saveMemory(memory) {
|
|
293
|
-
fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
|
294
|
-
fs.writeFileSync(MEMORY_FILE, JSON.stringify(memory, null, 2));
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const commands = {
|
|
298
|
-
get: (key) => {
|
|
299
|
-
const memory = loadMemory();
|
|
300
|
-
const value = key ? memory[key] : memory;
|
|
301
|
-
console.log(JSON.stringify(value, null, 2));
|
|
302
|
-
return value;
|
|
303
|
-
},
|
|
304
|
-
|
|
305
|
-
set: (key, value) => {
|
|
306
|
-
if (!key) {
|
|
307
|
-
console.error('Key required');
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
const memory = loadMemory();
|
|
311
|
-
memory[key] = value;
|
|
312
|
-
memory._updated = new Date().toISOString();
|
|
313
|
-
saveMemory(memory);
|
|
314
|
-
console.log(\`Set: \${key}\`);
|
|
315
|
-
},
|
|
316
|
-
|
|
317
|
-
delete: (key) => {
|
|
318
|
-
if (!key) {
|
|
319
|
-
console.error('Key required');
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
const memory = loadMemory();
|
|
323
|
-
delete memory[key];
|
|
324
|
-
saveMemory(memory);
|
|
325
|
-
console.log(\`Deleted: \${key}\`);
|
|
326
|
-
},
|
|
327
|
-
|
|
328
|
-
clear: () => {
|
|
329
|
-
saveMemory({});
|
|
330
|
-
console.log('Memory cleared');
|
|
331
|
-
},
|
|
332
|
-
|
|
333
|
-
keys: () => {
|
|
334
|
-
const memory = loadMemory();
|
|
335
|
-
const keys = Object.keys(memory).filter(k => !k.startsWith('_'));
|
|
336
|
-
console.log(keys.join('\\n'));
|
|
337
|
-
return keys;
|
|
338
|
-
},
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
// CLI
|
|
342
|
-
const [,, command, key, ...valueParts] = process.argv;
|
|
343
|
-
const value = valueParts.join(' ');
|
|
344
|
-
|
|
345
|
-
if (command && commands[command]) {
|
|
346
|
-
commands[command](key, value);
|
|
347
|
-
} else {
|
|
348
|
-
console.log('Usage: memory.js <get|set|delete|clear|keys> [key] [value]');
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
module.exports = commands;
|
|
269
|
+
return `#!/usr/bin/env node
|
|
270
|
+
/**
|
|
271
|
+
* Claude Flow Memory Helper
|
|
272
|
+
* Simple key-value memory for cross-session context
|
|
273
|
+
*/
|
|
274
|
+
|
|
275
|
+
const fs = require('fs');
|
|
276
|
+
const path = require('path');
|
|
277
|
+
|
|
278
|
+
const MEMORY_DIR = path.join(process.cwd(), '.claude-flow', 'data');
|
|
279
|
+
const MEMORY_FILE = path.join(MEMORY_DIR, 'memory.json');
|
|
280
|
+
|
|
281
|
+
function loadMemory() {
|
|
282
|
+
try {
|
|
283
|
+
if (fs.existsSync(MEMORY_FILE)) {
|
|
284
|
+
return JSON.parse(fs.readFileSync(MEMORY_FILE, 'utf-8'));
|
|
285
|
+
}
|
|
286
|
+
} catch (e) {
|
|
287
|
+
// Ignore
|
|
288
|
+
}
|
|
289
|
+
return {};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function saveMemory(memory) {
|
|
293
|
+
fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
|
294
|
+
fs.writeFileSync(MEMORY_FILE, JSON.stringify(memory, null, 2));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const commands = {
|
|
298
|
+
get: (key) => {
|
|
299
|
+
const memory = loadMemory();
|
|
300
|
+
const value = key ? memory[key] : memory;
|
|
301
|
+
console.log(JSON.stringify(value, null, 2));
|
|
302
|
+
return value;
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
set: (key, value) => {
|
|
306
|
+
if (!key) {
|
|
307
|
+
console.error('Key required');
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const memory = loadMemory();
|
|
311
|
+
memory[key] = value;
|
|
312
|
+
memory._updated = new Date().toISOString();
|
|
313
|
+
saveMemory(memory);
|
|
314
|
+
console.log(\`Set: \${key}\`);
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
delete: (key) => {
|
|
318
|
+
if (!key) {
|
|
319
|
+
console.error('Key required');
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const memory = loadMemory();
|
|
323
|
+
delete memory[key];
|
|
324
|
+
saveMemory(memory);
|
|
325
|
+
console.log(\`Deleted: \${key}\`);
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
clear: () => {
|
|
329
|
+
saveMemory({});
|
|
330
|
+
console.log('Memory cleared');
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
keys: () => {
|
|
334
|
+
const memory = loadMemory();
|
|
335
|
+
const keys = Object.keys(memory).filter(k => !k.startsWith('_'));
|
|
336
|
+
console.log(keys.join('\\n'));
|
|
337
|
+
return keys;
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// CLI
|
|
342
|
+
const [,, command, key, ...valueParts] = process.argv;
|
|
343
|
+
const value = valueParts.join(' ');
|
|
344
|
+
|
|
345
|
+
if (command && commands[command]) {
|
|
346
|
+
commands[command](key, value);
|
|
347
|
+
} else {
|
|
348
|
+
console.log('Usage: memory.js <get|set|delete|clear|keys> [key] [value]');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
module.exports = commands;
|
|
352
352
|
`;
|
|
353
353
|
}
|
|
354
354
|
/**
|
|
@@ -798,346 +798,346 @@ export function generateIntelligenceStub() {
|
|
|
798
798
|
* @claude-flow/memory is not installed. Gets overwritten when source copy succeeds.
|
|
799
799
|
*/
|
|
800
800
|
export function generateAutoMemoryHook() {
|
|
801
|
-
return `#!/usr/bin/env node
|
|
802
|
-
/**
|
|
803
|
-
* Auto Memory Bridge Hook (ADR-048/049) โ Minimal Fallback
|
|
804
|
-
* Full version is copied from package source when available.
|
|
805
|
-
*
|
|
806
|
-
* Usage:
|
|
807
|
-
* node auto-memory-hook.mjs import # SessionStart
|
|
808
|
-
* node auto-memory-hook.mjs sync # SessionEnd / Stop
|
|
809
|
-
* node auto-memory-hook.mjs status # Show bridge status
|
|
810
|
-
*/
|
|
811
|
-
|
|
812
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
813
|
-
import { join, dirname } from 'path';
|
|
814
|
-
import { fileURLToPath } from 'url';
|
|
815
|
-
|
|
816
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
817
|
-
const __dirname = dirname(__filename);
|
|
818
|
-
const PROJECT_ROOT = join(__dirname, '../..');
|
|
819
|
-
const DATA_DIR = join(PROJECT_ROOT, '.claude-flow', 'data');
|
|
820
|
-
const STORE_PATH = join(DATA_DIR, 'auto-memory-store.json');
|
|
821
|
-
|
|
822
|
-
const DIM = '\\x1b[2m';
|
|
823
|
-
const RESET = '\\x1b[0m';
|
|
824
|
-
const dim = (msg) => console.log(\` \${DIM}\${msg}\${RESET}\`);
|
|
825
|
-
|
|
826
|
-
// Ensure data dir
|
|
827
|
-
if (!existsSync(DATA_DIR)) mkdirSync(DATA_DIR, { recursive: true });
|
|
828
|
-
|
|
829
|
-
async function loadMemoryPackage() {
|
|
830
|
-
// Strategy 1: Use createRequire for CJS-style resolution (handles nested node_modules
|
|
831
|
-
// when installed as a transitive dependency via npx ruflo / npx claude-flow)
|
|
832
|
-
try {
|
|
833
|
-
const { createRequire } = await import('module');
|
|
834
|
-
const require = createRequire(join(PROJECT_ROOT, 'package.json'));
|
|
835
|
-
return require('@claude-flow/memory');
|
|
836
|
-
} catch { /* fall through */ }
|
|
837
|
-
|
|
838
|
-
// Strategy 2: ESM import (works when @claude-flow/memory is a direct dependency)
|
|
839
|
-
try { return await import('@claude-flow/memory'); } catch { /* fall through */ }
|
|
840
|
-
|
|
841
|
-
// Strategy 3: Walk up from PROJECT_ROOT looking for the package in any node_modules
|
|
842
|
-
let searchDir = PROJECT_ROOT;
|
|
843
|
-
const { parse } = await import('path');
|
|
844
|
-
while (searchDir !== parse(searchDir).root) {
|
|
845
|
-
const candidate = join(searchDir, 'node_modules', '@claude-flow', 'memory', 'dist', 'index.js');
|
|
846
|
-
if (existsSync(candidate)) {
|
|
847
|
-
try { return await import(\`file://\${candidate}\`); } catch { /* fall through */ }
|
|
848
|
-
}
|
|
849
|
-
searchDir = dirname(searchDir);
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
return null;
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
async function doImport() {
|
|
856
|
-
const memPkg = await loadMemoryPackage();
|
|
857
|
-
|
|
858
|
-
if (!memPkg || !memPkg.AutoMemoryBridge) {
|
|
859
|
-
dim('Memory package not available โ auto memory import skipped (non-critical)');
|
|
860
|
-
return;
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
// Full implementation deferred to copied version
|
|
864
|
-
dim('Auto memory import available โ run init --upgrade for full support');
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
async function doSync() {
|
|
868
|
-
if (!existsSync(STORE_PATH)) {
|
|
869
|
-
dim('No entries to sync');
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
const memPkg = await loadMemoryPackage();
|
|
874
|
-
|
|
875
|
-
if (!memPkg || !memPkg.AutoMemoryBridge) {
|
|
876
|
-
dim('Memory package not available โ sync skipped (non-critical)');
|
|
877
|
-
return;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
dim('Auto memory sync available โ run init --upgrade for full support');
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
function doStatus() {
|
|
884
|
-
console.log('\\n=== Auto Memory Bridge Status ===\\n');
|
|
885
|
-
console.log(' Package: Fallback mode (run init --upgrade for full)');
|
|
886
|
-
console.log(\` Store: \${existsSync(STORE_PATH) ? 'Initialized' : 'Not initialized'}\`);
|
|
887
|
-
console.log('');
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
const command = process.argv[2] || 'status';
|
|
891
|
-
|
|
892
|
-
try {
|
|
893
|
-
switch (command) {
|
|
894
|
-
case 'import': await doImport(); break;
|
|
895
|
-
case 'sync': await doSync(); break;
|
|
896
|
-
case 'status': doStatus(); break;
|
|
897
|
-
default:
|
|
898
|
-
console.log('Usage: auto-memory-hook.mjs <import|sync|status>');
|
|
899
|
-
process.exit(1);
|
|
900
|
-
}
|
|
901
|
-
} catch (err) {
|
|
902
|
-
// Hooks must never crash Claude Code - fail silently
|
|
903
|
-
dim(\`Error (non-critical): \${err.message}\`);
|
|
904
|
-
}
|
|
801
|
+
return `#!/usr/bin/env node
|
|
802
|
+
/**
|
|
803
|
+
* Auto Memory Bridge Hook (ADR-048/049) โ Minimal Fallback
|
|
804
|
+
* Full version is copied from package source when available.
|
|
805
|
+
*
|
|
806
|
+
* Usage:
|
|
807
|
+
* node auto-memory-hook.mjs import # SessionStart
|
|
808
|
+
* node auto-memory-hook.mjs sync # SessionEnd / Stop
|
|
809
|
+
* node auto-memory-hook.mjs status # Show bridge status
|
|
810
|
+
*/
|
|
811
|
+
|
|
812
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
813
|
+
import { join, dirname } from 'path';
|
|
814
|
+
import { fileURLToPath } from 'url';
|
|
815
|
+
|
|
816
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
817
|
+
const __dirname = dirname(__filename);
|
|
818
|
+
const PROJECT_ROOT = join(__dirname, '../..');
|
|
819
|
+
const DATA_DIR = join(PROJECT_ROOT, '.claude-flow', 'data');
|
|
820
|
+
const STORE_PATH = join(DATA_DIR, 'auto-memory-store.json');
|
|
821
|
+
|
|
822
|
+
const DIM = '\\x1b[2m';
|
|
823
|
+
const RESET = '\\x1b[0m';
|
|
824
|
+
const dim = (msg) => console.log(\` \${DIM}\${msg}\${RESET}\`);
|
|
825
|
+
|
|
826
|
+
// Ensure data dir
|
|
827
|
+
if (!existsSync(DATA_DIR)) mkdirSync(DATA_DIR, { recursive: true });
|
|
828
|
+
|
|
829
|
+
async function loadMemoryPackage() {
|
|
830
|
+
// Strategy 1: Use createRequire for CJS-style resolution (handles nested node_modules
|
|
831
|
+
// when installed as a transitive dependency via npx ruflo / npx claude-flow)
|
|
832
|
+
try {
|
|
833
|
+
const { createRequire } = await import('module');
|
|
834
|
+
const require = createRequire(join(PROJECT_ROOT, 'package.json'));
|
|
835
|
+
return require('@claude-flow/memory');
|
|
836
|
+
} catch { /* fall through */ }
|
|
837
|
+
|
|
838
|
+
// Strategy 2: ESM import (works when @claude-flow/memory is a direct dependency)
|
|
839
|
+
try { return await import('@claude-flow/memory'); } catch { /* fall through */ }
|
|
840
|
+
|
|
841
|
+
// Strategy 3: Walk up from PROJECT_ROOT looking for the package in any node_modules
|
|
842
|
+
let searchDir = PROJECT_ROOT;
|
|
843
|
+
const { parse } = await import('path');
|
|
844
|
+
while (searchDir !== parse(searchDir).root) {
|
|
845
|
+
const candidate = join(searchDir, 'node_modules', '@claude-flow', 'memory', 'dist', 'index.js');
|
|
846
|
+
if (existsSync(candidate)) {
|
|
847
|
+
try { return await import(\`file://\${candidate}\`); } catch { /* fall through */ }
|
|
848
|
+
}
|
|
849
|
+
searchDir = dirname(searchDir);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
async function doImport() {
|
|
856
|
+
const memPkg = await loadMemoryPackage();
|
|
857
|
+
|
|
858
|
+
if (!memPkg || !memPkg.AutoMemoryBridge) {
|
|
859
|
+
dim('Memory package not available โ auto memory import skipped (non-critical)');
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// Full implementation deferred to copied version
|
|
864
|
+
dim('Auto memory import available โ run init --upgrade for full support');
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
async function doSync() {
|
|
868
|
+
if (!existsSync(STORE_PATH)) {
|
|
869
|
+
dim('No entries to sync');
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
const memPkg = await loadMemoryPackage();
|
|
874
|
+
|
|
875
|
+
if (!memPkg || !memPkg.AutoMemoryBridge) {
|
|
876
|
+
dim('Memory package not available โ sync skipped (non-critical)');
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
dim('Auto memory sync available โ run init --upgrade for full support');
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
function doStatus() {
|
|
884
|
+
console.log('\\n=== Auto Memory Bridge Status ===\\n');
|
|
885
|
+
console.log(' Package: Fallback mode (run init --upgrade for full)');
|
|
886
|
+
console.log(\` Store: \${existsSync(STORE_PATH) ? 'Initialized' : 'Not initialized'}\`);
|
|
887
|
+
console.log('');
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
const command = process.argv[2] || 'status';
|
|
891
|
+
|
|
892
|
+
try {
|
|
893
|
+
switch (command) {
|
|
894
|
+
case 'import': await doImport(); break;
|
|
895
|
+
case 'sync': await doSync(); break;
|
|
896
|
+
case 'status': doStatus(); break;
|
|
897
|
+
default:
|
|
898
|
+
console.log('Usage: auto-memory-hook.mjs <import|sync|status>');
|
|
899
|
+
process.exit(1);
|
|
900
|
+
}
|
|
901
|
+
} catch (err) {
|
|
902
|
+
// Hooks must never crash Claude Code - fail silently
|
|
903
|
+
dim(\`Error (non-critical): \${err.message}\`);
|
|
904
|
+
}
|
|
905
905
|
`;
|
|
906
906
|
}
|
|
907
907
|
/**
|
|
908
908
|
* Generate Windows PowerShell daemon manager
|
|
909
909
|
*/
|
|
910
910
|
export function generateWindowsDaemonManager() {
|
|
911
|
-
return `#
|
|
912
|
-
# PowerShell script for managing background processes
|
|
913
|
-
|
|
914
|
-
param(
|
|
915
|
-
[Parameter(Position=0)]
|
|
916
|
-
[ValidateSet('start', 'stop', 'status', 'restart')]
|
|
917
|
-
[string]$Action = 'status'
|
|
918
|
-
)
|
|
919
|
-
|
|
920
|
-
$ErrorActionPreference = 'SilentlyContinue'
|
|
921
|
-
$ClaudeFlowDir = Join-Path $PWD '.claude-flow'
|
|
922
|
-
$PidDir = Join-Path $ClaudeFlowDir 'pids'
|
|
923
|
-
|
|
924
|
-
# Ensure directories exist
|
|
925
|
-
if (-not (Test-Path $PidDir)) {
|
|
926
|
-
New-Item -ItemType Directory -Path $PidDir -Force | Out-Null
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
function Get-DaemonStatus {
|
|
930
|
-
param([string]$Name, [string]$PidFile)
|
|
931
|
-
|
|
932
|
-
if (Test-Path $PidFile) {
|
|
933
|
-
$pid = Get-Content $PidFile
|
|
934
|
-
$process = Get-Process -Id $pid -ErrorAction SilentlyContinue
|
|
935
|
-
if ($process) {
|
|
936
|
-
return @{ Running = $true; Pid = $pid }
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
return @{ Running = $false; Pid = $null }
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
function Start-SwarmMonitor {
|
|
943
|
-
$pidFile = Join-Path $PidDir 'swarm-monitor.pid'
|
|
944
|
-
$status = Get-DaemonStatus -Name 'swarm-monitor' -PidFile $pidFile
|
|
945
|
-
|
|
946
|
-
if ($status.Running) {
|
|
947
|
-
Write-Host "Swarm monitor already running (PID: $($status.Pid))" -ForegroundColor Yellow
|
|
948
|
-
return
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
Write-Host "Starting swarm monitor..." -ForegroundColor Cyan
|
|
952
|
-
$process = Start-Process -FilePath 'node' -ArgumentList @(
|
|
953
|
-
'-e',
|
|
954
|
-
'setInterval(() => { require("fs").writeFileSync(".claude-flow/metrics/swarm-activity.json", JSON.stringify({swarm:{active:true,agent_count:0},timestamp:Date.now()})) }, 5000)'
|
|
955
|
-
) -PassThru -WindowStyle Hidden
|
|
956
|
-
|
|
957
|
-
$process.Id | Out-File $pidFile
|
|
958
|
-
Write-Host "Swarm monitor started (PID: $($process.Id))" -ForegroundColor Green
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
function Stop-SwarmMonitor {
|
|
962
|
-
$pidFile = Join-Path $PidDir 'swarm-monitor.pid'
|
|
963
|
-
$status = Get-DaemonStatus -Name 'swarm-monitor' -PidFile $pidFile
|
|
964
|
-
|
|
965
|
-
if (-not $status.Running) {
|
|
966
|
-
Write-Host "Swarm monitor not running" -ForegroundColor Yellow
|
|
967
|
-
return
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
Stop-Process -Id $status.Pid -Force
|
|
971
|
-
Remove-Item $pidFile -Force
|
|
972
|
-
Write-Host "Swarm monitor stopped" -ForegroundColor Green
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
function Show-Status {
|
|
976
|
-
Write-Host ""
|
|
977
|
-
Write-Host "
|
|
978
|
-
Write-Host "=============================" -ForegroundColor Cyan
|
|
979
|
-
|
|
980
|
-
$swarmPid = Join-Path $PidDir 'swarm-monitor.pid'
|
|
981
|
-
$swarmStatus = Get-DaemonStatus -Name 'swarm-monitor' -PidFile $swarmPid
|
|
982
|
-
|
|
983
|
-
if ($swarmStatus.Running) {
|
|
984
|
-
Write-Host " Swarm Monitor: RUNNING (PID: $($swarmStatus.Pid))" -ForegroundColor Green
|
|
985
|
-
} else {
|
|
986
|
-
Write-Host " Swarm Monitor: STOPPED" -ForegroundColor Red
|
|
987
|
-
}
|
|
988
|
-
Write-Host ""
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
switch ($Action) {
|
|
992
|
-
'start' {
|
|
993
|
-
Start-SwarmMonitor
|
|
994
|
-
Show-Status
|
|
995
|
-
}
|
|
996
|
-
'stop' {
|
|
997
|
-
Stop-SwarmMonitor
|
|
998
|
-
Show-Status
|
|
999
|
-
}
|
|
1000
|
-
'restart' {
|
|
1001
|
-
Stop-SwarmMonitor
|
|
1002
|
-
Start-Sleep -Seconds 1
|
|
1003
|
-
Start-SwarmMonitor
|
|
1004
|
-
Show-Status
|
|
1005
|
-
}
|
|
1006
|
-
'status' {
|
|
1007
|
-
Show-Status
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
911
|
+
return `# MoFlo V4 Daemon Manager for Windows
|
|
912
|
+
# PowerShell script for managing background processes
|
|
913
|
+
|
|
914
|
+
param(
|
|
915
|
+
[Parameter(Position=0)]
|
|
916
|
+
[ValidateSet('start', 'stop', 'status', 'restart')]
|
|
917
|
+
[string]$Action = 'status'
|
|
918
|
+
)
|
|
919
|
+
|
|
920
|
+
$ErrorActionPreference = 'SilentlyContinue'
|
|
921
|
+
$ClaudeFlowDir = Join-Path $PWD '.claude-flow'
|
|
922
|
+
$PidDir = Join-Path $ClaudeFlowDir 'pids'
|
|
923
|
+
|
|
924
|
+
# Ensure directories exist
|
|
925
|
+
if (-not (Test-Path $PidDir)) {
|
|
926
|
+
New-Item -ItemType Directory -Path $PidDir -Force | Out-Null
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function Get-DaemonStatus {
|
|
930
|
+
param([string]$Name, [string]$PidFile)
|
|
931
|
+
|
|
932
|
+
if (Test-Path $PidFile) {
|
|
933
|
+
$pid = Get-Content $PidFile
|
|
934
|
+
$process = Get-Process -Id $pid -ErrorAction SilentlyContinue
|
|
935
|
+
if ($process) {
|
|
936
|
+
return @{ Running = $true; Pid = $pid }
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
return @{ Running = $false; Pid = $null }
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
function Start-SwarmMonitor {
|
|
943
|
+
$pidFile = Join-Path $PidDir 'swarm-monitor.pid'
|
|
944
|
+
$status = Get-DaemonStatus -Name 'swarm-monitor' -PidFile $pidFile
|
|
945
|
+
|
|
946
|
+
if ($status.Running) {
|
|
947
|
+
Write-Host "Swarm monitor already running (PID: $($status.Pid))" -ForegroundColor Yellow
|
|
948
|
+
return
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
Write-Host "Starting swarm monitor..." -ForegroundColor Cyan
|
|
952
|
+
$process = Start-Process -FilePath 'node' -ArgumentList @(
|
|
953
|
+
'-e',
|
|
954
|
+
'setInterval(() => { require("fs").writeFileSync(".claude-flow/metrics/swarm-activity.json", JSON.stringify({swarm:{active:true,agent_count:0},timestamp:Date.now()})) }, 5000)'
|
|
955
|
+
) -PassThru -WindowStyle Hidden
|
|
956
|
+
|
|
957
|
+
$process.Id | Out-File $pidFile
|
|
958
|
+
Write-Host "Swarm monitor started (PID: $($process.Id))" -ForegroundColor Green
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
function Stop-SwarmMonitor {
|
|
962
|
+
$pidFile = Join-Path $PidDir 'swarm-monitor.pid'
|
|
963
|
+
$status = Get-DaemonStatus -Name 'swarm-monitor' -PidFile $pidFile
|
|
964
|
+
|
|
965
|
+
if (-not $status.Running) {
|
|
966
|
+
Write-Host "Swarm monitor not running" -ForegroundColor Yellow
|
|
967
|
+
return
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
Stop-Process -Id $status.Pid -Force
|
|
971
|
+
Remove-Item $pidFile -Force
|
|
972
|
+
Write-Host "Swarm monitor stopped" -ForegroundColor Green
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
function Show-Status {
|
|
976
|
+
Write-Host ""
|
|
977
|
+
Write-Host "MoFlo V4 Daemon Status" -ForegroundColor Cyan
|
|
978
|
+
Write-Host "=============================" -ForegroundColor Cyan
|
|
979
|
+
|
|
980
|
+
$swarmPid = Join-Path $PidDir 'swarm-monitor.pid'
|
|
981
|
+
$swarmStatus = Get-DaemonStatus -Name 'swarm-monitor' -PidFile $swarmPid
|
|
982
|
+
|
|
983
|
+
if ($swarmStatus.Running) {
|
|
984
|
+
Write-Host " Swarm Monitor: RUNNING (PID: $($swarmStatus.Pid))" -ForegroundColor Green
|
|
985
|
+
} else {
|
|
986
|
+
Write-Host " Swarm Monitor: STOPPED" -ForegroundColor Red
|
|
987
|
+
}
|
|
988
|
+
Write-Host ""
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
switch ($Action) {
|
|
992
|
+
'start' {
|
|
993
|
+
Start-SwarmMonitor
|
|
994
|
+
Show-Status
|
|
995
|
+
}
|
|
996
|
+
'stop' {
|
|
997
|
+
Stop-SwarmMonitor
|
|
998
|
+
Show-Status
|
|
999
|
+
}
|
|
1000
|
+
'restart' {
|
|
1001
|
+
Stop-SwarmMonitor
|
|
1002
|
+
Start-Sleep -Seconds 1
|
|
1003
|
+
Start-SwarmMonitor
|
|
1004
|
+
Show-Status
|
|
1005
|
+
}
|
|
1006
|
+
'status' {
|
|
1007
|
+
Show-Status
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
1010
|
`;
|
|
1011
1011
|
}
|
|
1012
1012
|
/**
|
|
1013
1013
|
* Generate Windows batch file wrapper
|
|
1014
1014
|
*/
|
|
1015
1015
|
export function generateWindowsBatchWrapper() {
|
|
1016
|
-
return `@echo off
|
|
1017
|
-
REM
|
|
1018
|
-
REM Routes to PowerShell daemon manager
|
|
1019
|
-
|
|
1020
|
-
PowerShell -ExecutionPolicy Bypass -File "%~dp0daemon-manager.ps1" %*
|
|
1016
|
+
return `@echo off
|
|
1017
|
+
REM MoFlo V4 - Windows Batch Wrapper
|
|
1018
|
+
REM Routes to PowerShell daemon manager
|
|
1019
|
+
|
|
1020
|
+
PowerShell -ExecutionPolicy Bypass -File "%~dp0daemon-manager.ps1" %*
|
|
1021
1021
|
`;
|
|
1022
1022
|
}
|
|
1023
1023
|
/**
|
|
1024
1024
|
* Generate cross-platform session manager
|
|
1025
1025
|
*/
|
|
1026
1026
|
export function generateCrossPlatformSessionManager() {
|
|
1027
|
-
return `#!/usr/bin/env node
|
|
1028
|
-
/**
|
|
1029
|
-
* Claude Flow Cross-Platform Session Manager
|
|
1030
|
-
* Works on Windows, macOS, and Linux
|
|
1031
|
-
*/
|
|
1032
|
-
|
|
1033
|
-
const fs = require('fs');
|
|
1034
|
-
const path = require('path');
|
|
1035
|
-
const os = require('os');
|
|
1036
|
-
|
|
1037
|
-
// Platform-specific paths
|
|
1038
|
-
const platform = os.platform();
|
|
1039
|
-
const homeDir = os.homedir();
|
|
1040
|
-
|
|
1041
|
-
// Get data directory based on platform
|
|
1042
|
-
function getDataDir() {
|
|
1043
|
-
const localDir = path.join(process.cwd(), '.claude-flow', 'sessions');
|
|
1044
|
-
if (fs.existsSync(path.dirname(localDir))) {
|
|
1045
|
-
return localDir;
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
switch (platform) {
|
|
1049
|
-
case 'win32':
|
|
1050
|
-
return path.join(process.env.APPDATA || homeDir, 'claude-flow', 'sessions');
|
|
1051
|
-
case 'darwin':
|
|
1052
|
-
return path.join(homeDir, 'Library', 'Application Support', 'claude-flow', 'sessions');
|
|
1053
|
-
default:
|
|
1054
|
-
return path.join(homeDir, '.claude-flow', 'sessions');
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
const SESSION_DIR = getDataDir();
|
|
1059
|
-
const SESSION_FILE = path.join(SESSION_DIR, 'current.json');
|
|
1060
|
-
|
|
1061
|
-
// Ensure directory exists
|
|
1062
|
-
function ensureDir(dir) {
|
|
1063
|
-
if (!fs.existsSync(dir)) {
|
|
1064
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
const commands = {
|
|
1069
|
-
start: () => {
|
|
1070
|
-
ensureDir(SESSION_DIR);
|
|
1071
|
-
const sessionId = \`session-\${Date.now()}\`;
|
|
1072
|
-
const session = {
|
|
1073
|
-
id: sessionId,
|
|
1074
|
-
startedAt: new Date().toISOString(),
|
|
1075
|
-
platform: platform,
|
|
1076
|
-
cwd: process.cwd(),
|
|
1077
|
-
context: {},
|
|
1078
|
-
metrics: { edits: 0, commands: 0, tasks: 0, errors: 0 }
|
|
1079
|
-
};
|
|
1080
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
1081
|
-
console.log(\`Session started: \${sessionId}\`);
|
|
1082
|
-
return session;
|
|
1083
|
-
},
|
|
1084
|
-
|
|
1085
|
-
restore: () => {
|
|
1086
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
1087
|
-
console.log('No session to restore');
|
|
1088
|
-
return null;
|
|
1089
|
-
}
|
|
1090
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
1091
|
-
session.restoredAt = new Date().toISOString();
|
|
1092
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
1093
|
-
console.log(\`Session restored: \${session.id}\`);
|
|
1094
|
-
return session;
|
|
1095
|
-
},
|
|
1096
|
-
|
|
1097
|
-
end: () => {
|
|
1098
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
1099
|
-
console.log('No active session');
|
|
1100
|
-
return null;
|
|
1101
|
-
}
|
|
1102
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
1103
|
-
session.endedAt = new Date().toISOString();
|
|
1104
|
-
session.duration = Date.now() - new Date(session.startedAt).getTime();
|
|
1105
|
-
|
|
1106
|
-
const archivePath = path.join(SESSION_DIR, \`\${session.id}.json\`);
|
|
1107
|
-
fs.writeFileSync(archivePath, JSON.stringify(session, null, 2));
|
|
1108
|
-
fs.unlinkSync(SESSION_FILE);
|
|
1109
|
-
|
|
1110
|
-
console.log(\`Session ended: \${session.id}\`);
|
|
1111
|
-
console.log(\`Duration: \${Math.round(session.duration / 1000 / 60)} minutes\`);
|
|
1112
|
-
return session;
|
|
1113
|
-
},
|
|
1114
|
-
|
|
1115
|
-
status: () => {
|
|
1116
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
1117
|
-
console.log('No active session');
|
|
1118
|
-
return null;
|
|
1119
|
-
}
|
|
1120
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
1121
|
-
const duration = Date.now() - new Date(session.startedAt).getTime();
|
|
1122
|
-
console.log(\`Session: \${session.id}\`);
|
|
1123
|
-
console.log(\`Platform: \${session.platform}\`);
|
|
1124
|
-
console.log(\`Started: \${session.startedAt}\`);
|
|
1125
|
-
console.log(\`Duration: \${Math.round(duration / 1000 / 60)} minutes\`);
|
|
1126
|
-
return session;
|
|
1127
|
-
}
|
|
1128
|
-
};
|
|
1129
|
-
|
|
1130
|
-
// CLI
|
|
1131
|
-
const [,, command, ...args] = process.argv;
|
|
1132
|
-
if (command && commands[command]) {
|
|
1133
|
-
commands[command](...args);
|
|
1134
|
-
} else {
|
|
1135
|
-
console.log('Usage: session.js <start|restore|end|status>');
|
|
1136
|
-
console.log(\`Platform: \${platform}\`);
|
|
1137
|
-
console.log(\`Data dir: \${SESSION_DIR}\`);
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
module.exports = commands;
|
|
1027
|
+
return `#!/usr/bin/env node
|
|
1028
|
+
/**
|
|
1029
|
+
* Claude Flow Cross-Platform Session Manager
|
|
1030
|
+
* Works on Windows, macOS, and Linux
|
|
1031
|
+
*/
|
|
1032
|
+
|
|
1033
|
+
const fs = require('fs');
|
|
1034
|
+
const path = require('path');
|
|
1035
|
+
const os = require('os');
|
|
1036
|
+
|
|
1037
|
+
// Platform-specific paths
|
|
1038
|
+
const platform = os.platform();
|
|
1039
|
+
const homeDir = os.homedir();
|
|
1040
|
+
|
|
1041
|
+
// Get data directory based on platform
|
|
1042
|
+
function getDataDir() {
|
|
1043
|
+
const localDir = path.join(process.cwd(), '.claude-flow', 'sessions');
|
|
1044
|
+
if (fs.existsSync(path.dirname(localDir))) {
|
|
1045
|
+
return localDir;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
switch (platform) {
|
|
1049
|
+
case 'win32':
|
|
1050
|
+
return path.join(process.env.APPDATA || homeDir, 'claude-flow', 'sessions');
|
|
1051
|
+
case 'darwin':
|
|
1052
|
+
return path.join(homeDir, 'Library', 'Application Support', 'claude-flow', 'sessions');
|
|
1053
|
+
default:
|
|
1054
|
+
return path.join(homeDir, '.claude-flow', 'sessions');
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const SESSION_DIR = getDataDir();
|
|
1059
|
+
const SESSION_FILE = path.join(SESSION_DIR, 'current.json');
|
|
1060
|
+
|
|
1061
|
+
// Ensure directory exists
|
|
1062
|
+
function ensureDir(dir) {
|
|
1063
|
+
if (!fs.existsSync(dir)) {
|
|
1064
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
const commands = {
|
|
1069
|
+
start: () => {
|
|
1070
|
+
ensureDir(SESSION_DIR);
|
|
1071
|
+
const sessionId = \`session-\${Date.now()}\`;
|
|
1072
|
+
const session = {
|
|
1073
|
+
id: sessionId,
|
|
1074
|
+
startedAt: new Date().toISOString(),
|
|
1075
|
+
platform: platform,
|
|
1076
|
+
cwd: process.cwd(),
|
|
1077
|
+
context: {},
|
|
1078
|
+
metrics: { edits: 0, commands: 0, tasks: 0, errors: 0 }
|
|
1079
|
+
};
|
|
1080
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
1081
|
+
console.log(\`Session started: \${sessionId}\`);
|
|
1082
|
+
return session;
|
|
1083
|
+
},
|
|
1084
|
+
|
|
1085
|
+
restore: () => {
|
|
1086
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
1087
|
+
console.log('No session to restore');
|
|
1088
|
+
return null;
|
|
1089
|
+
}
|
|
1090
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
1091
|
+
session.restoredAt = new Date().toISOString();
|
|
1092
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
1093
|
+
console.log(\`Session restored: \${session.id}\`);
|
|
1094
|
+
return session;
|
|
1095
|
+
},
|
|
1096
|
+
|
|
1097
|
+
end: () => {
|
|
1098
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
1099
|
+
console.log('No active session');
|
|
1100
|
+
return null;
|
|
1101
|
+
}
|
|
1102
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
1103
|
+
session.endedAt = new Date().toISOString();
|
|
1104
|
+
session.duration = Date.now() - new Date(session.startedAt).getTime();
|
|
1105
|
+
|
|
1106
|
+
const archivePath = path.join(SESSION_DIR, \`\${session.id}.json\`);
|
|
1107
|
+
fs.writeFileSync(archivePath, JSON.stringify(session, null, 2));
|
|
1108
|
+
fs.unlinkSync(SESSION_FILE);
|
|
1109
|
+
|
|
1110
|
+
console.log(\`Session ended: \${session.id}\`);
|
|
1111
|
+
console.log(\`Duration: \${Math.round(session.duration / 1000 / 60)} minutes\`);
|
|
1112
|
+
return session;
|
|
1113
|
+
},
|
|
1114
|
+
|
|
1115
|
+
status: () => {
|
|
1116
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
1117
|
+
console.log('No active session');
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
1121
|
+
const duration = Date.now() - new Date(session.startedAt).getTime();
|
|
1122
|
+
console.log(\`Session: \${session.id}\`);
|
|
1123
|
+
console.log(\`Platform: \${session.platform}\`);
|
|
1124
|
+
console.log(\`Started: \${session.startedAt}\`);
|
|
1125
|
+
console.log(\`Duration: \${Math.round(duration / 1000 / 60)} minutes\`);
|
|
1126
|
+
return session;
|
|
1127
|
+
}
|
|
1128
|
+
};
|
|
1129
|
+
|
|
1130
|
+
// CLI
|
|
1131
|
+
const [,, command, ...args] = process.argv;
|
|
1132
|
+
if (command && commands[command]) {
|
|
1133
|
+
commands[command](...args);
|
|
1134
|
+
} else {
|
|
1135
|
+
console.log('Usage: session.js <start|restore|end|status>');
|
|
1136
|
+
console.log(\`Platform: \${platform}\`);
|
|
1137
|
+
console.log(\`Data dir: \${SESSION_DIR}\`);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
module.exports = commands;
|
|
1141
1141
|
`;
|
|
1142
1142
|
}
|
|
1143
1143
|
/**
|