@stackmemoryai/stackmemory 0.5.49 ā 0.5.52
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/README.md +17 -3
- package/dist/cli/claude-sm.js +246 -5
- package/dist/cli/claude-sm.js.map +3 -3
- package/dist/cli/commands/handoff.js +27 -12
- package/dist/cli/commands/handoff.js.map +2 -2
- package/dist/cli/commands/sweep.js +190 -421
- package/dist/cli/commands/sweep.js.map +3 -3
- package/dist/cli/index.js +10 -2
- package/dist/cli/index.js.map +2 -2
- package/dist/core/config/feature-flags.js +7 -1
- package/dist/core/config/feature-flags.js.map +2 -2
- package/dist/core/context/enhanced-rehydration.js +355 -9
- package/dist/core/context/enhanced-rehydration.js.map +3 -3
- package/dist/core/context/shared-context-layer.js +229 -0
- package/dist/core/context/shared-context-layer.js.map +2 -2
- package/dist/features/sweep/index.js +20 -0
- package/dist/features/sweep/index.js.map +7 -0
- package/dist/features/sweep/prediction-client.js +155 -0
- package/dist/features/sweep/prediction-client.js.map +7 -0
- package/dist/features/sweep/prompt-builder.js +85 -0
- package/dist/features/sweep/prompt-builder.js.map +7 -0
- package/dist/features/sweep/pty-wrapper.js +171 -0
- package/dist/features/sweep/pty-wrapper.js.map +7 -0
- package/dist/features/sweep/state-watcher.js +87 -0
- package/dist/features/sweep/state-watcher.js.map +7 -0
- package/dist/features/sweep/status-bar.js +88 -0
- package/dist/features/sweep/status-bar.js.map +7 -0
- package/dist/features/sweep/sweep-server-manager.js +226 -0
- package/dist/features/sweep/sweep-server-manager.js.map +7 -0
- package/dist/features/sweep/tab-interceptor.js +38 -0
- package/dist/features/sweep/tab-interceptor.js.map +7 -0
- package/dist/features/sweep/types.js +18 -0
- package/dist/features/sweep/types.js.map +7 -0
- package/dist/integrations/claude-code/lifecycle-hooks.js +3 -3
- package/dist/integrations/claude-code/lifecycle-hooks.js.map +1 -1
- package/package.json +1 -1
- package/scripts/auto-handoff.sh +1 -1
- package/scripts/claude-sm-autostart.js +174 -132
- package/scripts/setup-claude-integration.js +14 -10
- package/scripts/stackmemory-auto-handoff.sh +3 -3
- package/scripts/test-session-handoff.sh +2 -2
- package/scripts/test-setup-e2e.sh +154 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* StackMemory Claude Auto-Start Daemon Manager
|
|
5
5
|
* Automatically starts essential daemons when Claude loads the project
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* Daemons managed:
|
|
8
8
|
* 1. Context Monitor - Saves context every 15 min
|
|
9
9
|
* 2. Linear Sync - Syncs tasks hourly
|
|
@@ -24,10 +24,10 @@ import dotenv from 'dotenv';
|
|
|
24
24
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
25
25
|
|
|
26
26
|
// Load .env first (as per CLAUDE.md)
|
|
27
|
-
dotenv.config({
|
|
27
|
+
dotenv.config({
|
|
28
28
|
path: path.join(__dirname, '..', '.env'),
|
|
29
29
|
override: true,
|
|
30
|
-
silent: true
|
|
30
|
+
silent: true,
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
class ClaudeAutoStartManager {
|
|
@@ -36,8 +36,12 @@ class ClaudeAutoStartManager {
|
|
|
36
36
|
this.watchers = new Map();
|
|
37
37
|
this.projectRoot = path.dirname(__dirname);
|
|
38
38
|
this.logDir = path.join(process.env.HOME, '.stackmemory', 'logs');
|
|
39
|
-
this.pidFile = path.join(
|
|
40
|
-
|
|
39
|
+
this.pidFile = path.join(
|
|
40
|
+
process.env.HOME,
|
|
41
|
+
'.stackmemory',
|
|
42
|
+
'claude-daemons.pid'
|
|
43
|
+
);
|
|
44
|
+
|
|
41
45
|
// Ensure log directory exists
|
|
42
46
|
if (!fs.existsSync(this.logDir)) {
|
|
43
47
|
fs.mkdirSync(this.logDir, { recursive: true });
|
|
@@ -48,7 +52,7 @@ class ClaudeAutoStartManager {
|
|
|
48
52
|
const timestamp = new Date().toISOString();
|
|
49
53
|
const logMessage = `[${timestamp}] [${level}] ${message}`;
|
|
50
54
|
console.log(logMessage);
|
|
51
|
-
|
|
55
|
+
|
|
52
56
|
const logFile = path.join(this.logDir, 'claude-autostart.log');
|
|
53
57
|
fs.appendFileSync(logFile, logMessage + '\n');
|
|
54
58
|
}
|
|
@@ -60,61 +64,64 @@ class ClaudeAutoStartManager {
|
|
|
60
64
|
*/
|
|
61
65
|
startContextMonitor() {
|
|
62
66
|
this.log('Starting Context Monitor...');
|
|
63
|
-
|
|
64
|
-
const contextInterval = setInterval(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
67
|
+
|
|
68
|
+
const contextInterval = setInterval(
|
|
69
|
+
async () => {
|
|
70
|
+
try {
|
|
71
|
+
// Check if stackmemory is available
|
|
72
|
+
const { exec } = await import('child_process');
|
|
73
|
+
const { promisify } = await import('util');
|
|
74
|
+
const execAsync = promisify(exec);
|
|
75
|
+
|
|
76
|
+
// Save current context
|
|
77
|
+
const { stdout } = await execAsync(
|
|
78
|
+
`cd ${this.projectRoot} && ~/.stackmemory/bin/stackmemory context add decision "Auto-checkpoint at ${new Date().toISOString()}"`
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
this.log('Context checkpoint saved');
|
|
82
|
+
|
|
83
|
+
// Load context from ChromaDB if available
|
|
84
|
+
if (process.env.CHROMADB_API_KEY) {
|
|
85
|
+
try {
|
|
86
|
+
await execAsync(
|
|
87
|
+
`cd ${this.projectRoot} && node scripts/chromadb-context-loader.js load 1`
|
|
88
|
+
);
|
|
89
|
+
this.log('ChromaDB context loaded');
|
|
90
|
+
|
|
91
|
+
// Check for important changes
|
|
92
|
+
await execAsync(
|
|
93
|
+
`cd ${this.projectRoot} && node scripts/chromadb-context-loader.js changes`
|
|
94
|
+
);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
// Silent fail for ChromaDB
|
|
97
|
+
}
|
|
92
98
|
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
this.log(`Context monitor error: ${error.message}`, 'ERROR');
|
|
93
101
|
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
},
|
|
103
|
+
15 * 60 * 1000
|
|
104
|
+
); // Every 15 minutes
|
|
105
|
+
|
|
99
106
|
// Also load context immediately on start
|
|
100
107
|
this.loadInitialContext();
|
|
101
|
-
|
|
108
|
+
|
|
102
109
|
this.daemons.set('context-monitor', contextInterval);
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
async loadInitialContext() {
|
|
106
113
|
if (!process.env.CHROMADB_API_KEY) return;
|
|
107
|
-
|
|
114
|
+
|
|
108
115
|
try {
|
|
109
116
|
const { exec } = await import('child_process');
|
|
110
117
|
const { promisify } = await import('util');
|
|
111
118
|
const execAsync = promisify(exec);
|
|
112
|
-
|
|
119
|
+
|
|
113
120
|
// Load last 24 hours of context
|
|
114
121
|
await execAsync(
|
|
115
122
|
`cd ${this.projectRoot} && node scripts/chromadb-context-loader.js auto`
|
|
116
123
|
);
|
|
117
|
-
|
|
124
|
+
|
|
118
125
|
this.log('Initial ChromaDB context loaded');
|
|
119
126
|
} catch (error) {
|
|
120
127
|
this.log(`Initial context load error: ${error.message}`, 'WARN');
|
|
@@ -126,20 +133,25 @@ class ClaudeAutoStartManager {
|
|
|
126
133
|
* Already created, just ensure it's running
|
|
127
134
|
*/
|
|
128
135
|
startLinearSync() {
|
|
129
|
-
if (
|
|
136
|
+
if (
|
|
137
|
+
!process.env.STACKMEMORY_LINEAR_API_KEY &&
|
|
138
|
+
!process.env.LINEAR_API_KEY
|
|
139
|
+
) {
|
|
130
140
|
this.log('Linear sync skipped - no API key', 'WARN');
|
|
131
141
|
return;
|
|
132
142
|
}
|
|
133
|
-
|
|
143
|
+
|
|
134
144
|
this.log('Starting Linear Sync Daemon...');
|
|
135
|
-
|
|
136
|
-
const linearSync = spawn(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
145
|
+
|
|
146
|
+
const linearSync = spawn(
|
|
147
|
+
'node',
|
|
148
|
+
[path.join(this.projectRoot, 'scripts', 'linear-sync-daemon.js')],
|
|
149
|
+
{
|
|
150
|
+
detached: true,
|
|
151
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
|
|
143
155
|
linearSync.unref();
|
|
144
156
|
this.daemons.set('linear-sync', linearSync);
|
|
145
157
|
this.log(`Linear sync started (PID: ${linearSync.pid})`);
|
|
@@ -151,13 +163,13 @@ class ClaudeAutoStartManager {
|
|
|
151
163
|
*/
|
|
152
164
|
startFileWatcher() {
|
|
153
165
|
this.log('Starting File Watcher...');
|
|
154
|
-
|
|
166
|
+
|
|
155
167
|
const watchPaths = [
|
|
156
168
|
path.join(this.projectRoot, 'src'),
|
|
157
169
|
path.join(this.projectRoot, 'scripts'),
|
|
158
|
-
path.join(this.projectRoot, '.stackmemory', 'tasks.jsonl')
|
|
170
|
+
path.join(this.projectRoot, '.stackmemory', 'tasks.jsonl'),
|
|
159
171
|
];
|
|
160
|
-
|
|
172
|
+
|
|
161
173
|
const watcher = chokidar.watch(watchPaths, {
|
|
162
174
|
persistent: true,
|
|
163
175
|
ignoreInitial: true,
|
|
@@ -166,25 +178,25 @@ class ClaudeAutoStartManager {
|
|
|
166
178
|
'**/.git/**',
|
|
167
179
|
'**/dist/**',
|
|
168
180
|
'**/build/**',
|
|
169
|
-
'**/*.log'
|
|
170
|
-
]
|
|
181
|
+
'**/*.log',
|
|
182
|
+
],
|
|
171
183
|
});
|
|
172
|
-
|
|
184
|
+
|
|
173
185
|
let changeTimeout;
|
|
174
|
-
|
|
186
|
+
|
|
175
187
|
watcher.on('change', (filepath) => {
|
|
176
188
|
// Debounce changes
|
|
177
189
|
clearTimeout(changeTimeout);
|
|
178
190
|
changeTimeout = setTimeout(() => {
|
|
179
191
|
this.log(`File changed: ${path.relative(this.projectRoot, filepath)}`);
|
|
180
|
-
|
|
192
|
+
|
|
181
193
|
// Auto-save context on significant changes
|
|
182
194
|
if (filepath.endsWith('.ts') || filepath.endsWith('.js')) {
|
|
183
195
|
this.saveFileChangeContext(filepath);
|
|
184
196
|
}
|
|
185
197
|
}, 1000);
|
|
186
198
|
});
|
|
187
|
-
|
|
199
|
+
|
|
188
200
|
this.watchers.set('file-watcher', watcher);
|
|
189
201
|
this.log('File watcher active');
|
|
190
202
|
}
|
|
@@ -194,7 +206,7 @@ class ClaudeAutoStartManager {
|
|
|
194
206
|
const { exec } = await import('child_process');
|
|
195
207
|
const { promisify } = await import('util');
|
|
196
208
|
const execAsync = promisify(exec);
|
|
197
|
-
|
|
209
|
+
|
|
198
210
|
const filename = path.basename(filepath);
|
|
199
211
|
await execAsync(
|
|
200
212
|
`cd ${this.projectRoot} && ~/.stackmemory/bin/stackmemory context add observation "Modified: ${filename}"`
|
|
@@ -210,47 +222,50 @@ class ClaudeAutoStartManager {
|
|
|
210
222
|
*/
|
|
211
223
|
startErrorMonitor() {
|
|
212
224
|
this.log('Starting Error Monitor...');
|
|
213
|
-
|
|
225
|
+
|
|
214
226
|
const errorPatterns = [
|
|
215
227
|
/ERROR/i,
|
|
216
228
|
/FAILED/i,
|
|
217
229
|
/Exception/,
|
|
218
230
|
/TypeError/,
|
|
219
231
|
/ReferenceError/,
|
|
220
|
-
/SyntaxError
|
|
232
|
+
/SyntaxError/,
|
|
221
233
|
];
|
|
222
|
-
|
|
234
|
+
|
|
223
235
|
const monitorInterval = setInterval(() => {
|
|
224
236
|
// Check recent logs for errors
|
|
225
237
|
const logsToCheck = [
|
|
226
238
|
path.join(this.logDir, 'linear-sync.log'),
|
|
227
239
|
path.join(this.logDir, 'sync-manager.log'),
|
|
228
|
-
path.join(this.projectRoot, 'npm-debug.log')
|
|
240
|
+
path.join(this.projectRoot, 'npm-debug.log'),
|
|
229
241
|
];
|
|
230
|
-
|
|
231
|
-
logsToCheck.forEach(logFile => {
|
|
242
|
+
|
|
243
|
+
logsToCheck.forEach((logFile) => {
|
|
232
244
|
if (fs.existsSync(logFile)) {
|
|
233
245
|
const stats = fs.statSync(logFile);
|
|
234
246
|
const lastCheck = this.lastErrorCheck || 0;
|
|
235
|
-
|
|
247
|
+
|
|
236
248
|
if (stats.mtimeMs > lastCheck) {
|
|
237
249
|
const content = fs.readFileSync(logFile, 'utf8');
|
|
238
250
|
const lines = content.split('\n').slice(-100); // Last 100 lines
|
|
239
|
-
|
|
240
|
-
lines.forEach(line => {
|
|
241
|
-
errorPatterns.forEach(pattern => {
|
|
251
|
+
|
|
252
|
+
lines.forEach((line) => {
|
|
253
|
+
errorPatterns.forEach((pattern) => {
|
|
242
254
|
if (pattern.test(line)) {
|
|
243
|
-
this.log(
|
|
255
|
+
this.log(
|
|
256
|
+
`Error detected: ${line.substring(0, 100)}...`,
|
|
257
|
+
'WARN'
|
|
258
|
+
);
|
|
244
259
|
}
|
|
245
260
|
});
|
|
246
261
|
});
|
|
247
262
|
}
|
|
248
263
|
}
|
|
249
264
|
});
|
|
250
|
-
|
|
265
|
+
|
|
251
266
|
this.lastErrorCheck = Date.now();
|
|
252
267
|
}, 60 * 1000); // Every minute
|
|
253
|
-
|
|
268
|
+
|
|
254
269
|
this.daemons.set('error-monitor', monitorInterval);
|
|
255
270
|
}
|
|
256
271
|
|
|
@@ -260,30 +275,30 @@ class ClaudeAutoStartManager {
|
|
|
260
275
|
*/
|
|
261
276
|
startWebhookListener() {
|
|
262
277
|
this.log('Starting Webhook Listener...');
|
|
263
|
-
|
|
278
|
+
|
|
264
279
|
const express = require('express');
|
|
265
280
|
const app = express();
|
|
266
281
|
app.use(express.json());
|
|
267
|
-
|
|
282
|
+
|
|
268
283
|
const PORT = process.env.WEBHOOK_PORT || 3456;
|
|
269
|
-
|
|
284
|
+
|
|
270
285
|
app.post('/webhooks/linear', (req, res) => {
|
|
271
286
|
const { action, data } = req.body;
|
|
272
287
|
this.log(`Linear webhook: ${action} - ${data.identifier || data.id}`);
|
|
273
|
-
|
|
288
|
+
|
|
274
289
|
// Process webhook
|
|
275
290
|
if (action === 'create' || action === 'update') {
|
|
276
291
|
// Trigger sync
|
|
277
292
|
this.triggerLinearSync();
|
|
278
293
|
}
|
|
279
|
-
|
|
294
|
+
|
|
280
295
|
res.status(200).send('OK');
|
|
281
296
|
});
|
|
282
|
-
|
|
297
|
+
|
|
283
298
|
const server = app.listen(PORT, () => {
|
|
284
299
|
this.log(`Webhook listener on port ${PORT}`);
|
|
285
300
|
});
|
|
286
|
-
|
|
301
|
+
|
|
287
302
|
this.daemons.set('webhook-listener', server);
|
|
288
303
|
}
|
|
289
304
|
|
|
@@ -292,7 +307,7 @@ class ClaudeAutoStartManager {
|
|
|
292
307
|
const { exec } = await import('child_process');
|
|
293
308
|
const { promisify } = await import('util');
|
|
294
309
|
const execAsync = promisify(exec);
|
|
295
|
-
|
|
310
|
+
|
|
296
311
|
await execAsync(
|
|
297
312
|
`cd ${this.projectRoot} && node scripts/sync-linear-graphql.js`
|
|
298
313
|
);
|
|
@@ -308,47 +323,57 @@ class ClaudeAutoStartManager {
|
|
|
308
323
|
*/
|
|
309
324
|
startQualityGates() {
|
|
310
325
|
this.log('Starting Quality Gates Monitor...');
|
|
311
|
-
|
|
326
|
+
|
|
312
327
|
// Watch for task completion patterns
|
|
313
328
|
const taskWatcher = chokidar.watch(
|
|
314
329
|
path.join(this.projectRoot, '.stackmemory', 'tasks.jsonl'),
|
|
315
330
|
{ persistent: true }
|
|
316
331
|
);
|
|
317
|
-
|
|
332
|
+
|
|
318
333
|
taskWatcher.on('change', async () => {
|
|
319
334
|
// Check last task status
|
|
320
335
|
try {
|
|
321
|
-
const tasksFile = path.join(
|
|
322
|
-
|
|
336
|
+
const tasksFile = path.join(
|
|
337
|
+
this.projectRoot,
|
|
338
|
+
'.stackmemory',
|
|
339
|
+
'tasks.jsonl'
|
|
340
|
+
);
|
|
341
|
+
const lines = fs
|
|
342
|
+
.readFileSync(tasksFile, 'utf8')
|
|
343
|
+
.split('\n')
|
|
344
|
+
.filter(Boolean);
|
|
323
345
|
const lastTask = JSON.parse(lines[lines.length - 1]);
|
|
324
|
-
|
|
325
|
-
if (
|
|
326
|
-
|
|
346
|
+
|
|
347
|
+
if (
|
|
348
|
+
lastTask.status === 'completed' &&
|
|
349
|
+
lastTask.timestamp > Date.now() - 60000
|
|
350
|
+
) {
|
|
351
|
+
// Within last minute
|
|
327
352
|
await this.runQualityChecks();
|
|
328
353
|
}
|
|
329
354
|
} catch (error) {
|
|
330
355
|
// Silent fail
|
|
331
356
|
}
|
|
332
357
|
});
|
|
333
|
-
|
|
358
|
+
|
|
334
359
|
this.watchers.set('quality-gates', taskWatcher);
|
|
335
360
|
}
|
|
336
361
|
|
|
337
362
|
async runQualityChecks() {
|
|
338
363
|
this.log('Running quality checks...');
|
|
339
|
-
|
|
364
|
+
|
|
340
365
|
const checks = [
|
|
341
366
|
{ name: 'Lint', cmd: 'npm run lint' },
|
|
342
367
|
{ name: 'Tests', cmd: 'npm test' },
|
|
343
|
-
{ name: 'Build', cmd: 'npm run build' }
|
|
368
|
+
{ name: 'Build', cmd: 'npm run build' },
|
|
344
369
|
];
|
|
345
|
-
|
|
370
|
+
|
|
346
371
|
for (const check of checks) {
|
|
347
372
|
try {
|
|
348
373
|
const { exec } = await import('child_process');
|
|
349
374
|
const { promisify } = await import('util');
|
|
350
375
|
const execAsync = promisify(exec);
|
|
351
|
-
|
|
376
|
+
|
|
352
377
|
await execAsync(`cd ${this.projectRoot} && ${check.cmd}`);
|
|
353
378
|
this.log(`ā
${check.name} passed`);
|
|
354
379
|
} catch (error) {
|
|
@@ -363,42 +388,46 @@ class ClaudeAutoStartManager {
|
|
|
363
388
|
*/
|
|
364
389
|
startAutoHandoff() {
|
|
365
390
|
this.log('Starting Auto-handoff Monitor...');
|
|
366
|
-
|
|
391
|
+
|
|
367
392
|
// Monitor for session end signals
|
|
368
393
|
process.on('SIGINT', () => this.prepareHandoff('interrupt'));
|
|
369
394
|
process.on('SIGTERM', () => this.prepareHandoff('terminate'));
|
|
370
|
-
|
|
395
|
+
|
|
371
396
|
// Also monitor for idle time
|
|
372
397
|
let lastActivity = Date.now();
|
|
373
|
-
|
|
374
|
-
const idleChecker = setInterval(
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
398
|
+
|
|
399
|
+
const idleChecker = setInterval(
|
|
400
|
+
() => {
|
|
401
|
+
const idleTime = Date.now() - lastActivity;
|
|
402
|
+
if (idleTime > 30 * 60 * 1000) {
|
|
403
|
+
// 30 minutes idle
|
|
404
|
+
this.prepareHandoff('idle');
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
5 * 60 * 1000
|
|
408
|
+
); // Check every 5 minutes
|
|
409
|
+
|
|
381
410
|
// Update activity on any file change
|
|
382
411
|
this.watchers.get('file-watcher')?.on('all', () => {
|
|
383
412
|
lastActivity = Date.now();
|
|
384
413
|
});
|
|
385
|
-
|
|
414
|
+
|
|
386
415
|
this.daemons.set('auto-handoff', idleChecker);
|
|
387
416
|
}
|
|
388
417
|
|
|
389
418
|
async prepareHandoff(reason) {
|
|
390
419
|
this.log(`Preparing handoff (${reason})...`);
|
|
391
|
-
|
|
420
|
+
|
|
392
421
|
try {
|
|
393
422
|
const { exec } = await import('child_process');
|
|
394
423
|
const { promisify } = await import('util');
|
|
395
424
|
const execAsync = promisify(exec);
|
|
396
|
-
|
|
425
|
+
|
|
397
426
|
// Generate handoff
|
|
398
427
|
await execAsync(
|
|
399
|
-
`cd ${this.projectRoot} && ~/.stackmemory/bin/stackmemory
|
|
428
|
+
`cd ${this.projectRoot} && ~/.stackmemory/bin/stackmemory capture`
|
|
400
429
|
);
|
|
401
|
-
|
|
430
|
+
|
|
402
431
|
this.log('Handoff prepared successfully');
|
|
403
432
|
} catch (error) {
|
|
404
433
|
this.log(`Handoff error: ${error.message}`, 'ERROR');
|
|
@@ -411,37 +440,37 @@ class ClaudeAutoStartManager {
|
|
|
411
440
|
async start() {
|
|
412
441
|
this.log('š Claude StackMemory Auto-Start Manager');
|
|
413
442
|
this.log('=========================================\n');
|
|
414
|
-
|
|
443
|
+
|
|
415
444
|
// Save PID for management
|
|
416
445
|
fs.writeFileSync(this.pidFile, process.pid.toString());
|
|
417
|
-
|
|
446
|
+
|
|
418
447
|
// Start all daemons
|
|
419
448
|
this.startContextMonitor();
|
|
420
449
|
this.startLinearSync();
|
|
421
450
|
this.startFileWatcher();
|
|
422
451
|
this.startErrorMonitor();
|
|
423
|
-
|
|
452
|
+
|
|
424
453
|
// Optional daemons (only if configured)
|
|
425
454
|
if (process.env.ENABLE_WEBHOOKS === 'true') {
|
|
426
455
|
this.startWebhookListener();
|
|
427
456
|
}
|
|
428
|
-
|
|
457
|
+
|
|
429
458
|
if (process.env.ENABLE_QUALITY_GATES === 'true') {
|
|
430
459
|
this.startQualityGates();
|
|
431
460
|
}
|
|
432
|
-
|
|
461
|
+
|
|
433
462
|
this.startAutoHandoff();
|
|
434
|
-
|
|
463
|
+
|
|
435
464
|
this.log('\nā
All daemons started successfully');
|
|
436
465
|
this.log('š Active daemons:');
|
|
437
466
|
this.daemons.forEach((daemon, name) => {
|
|
438
467
|
this.log(` - ${name}`);
|
|
439
468
|
});
|
|
440
|
-
|
|
469
|
+
|
|
441
470
|
// Handle shutdown
|
|
442
471
|
process.on('SIGINT', () => this.stop());
|
|
443
472
|
process.on('SIGTERM', () => this.stop());
|
|
444
|
-
|
|
473
|
+
|
|
445
474
|
// Keep process alive
|
|
446
475
|
process.stdin.resume();
|
|
447
476
|
}
|
|
@@ -451,7 +480,7 @@ class ClaudeAutoStartManager {
|
|
|
451
480
|
*/
|
|
452
481
|
stop() {
|
|
453
482
|
this.log('\nš Stopping all daemons...');
|
|
454
|
-
|
|
483
|
+
|
|
455
484
|
// Clear intervals
|
|
456
485
|
this.daemons.forEach((daemon, name) => {
|
|
457
486
|
if (typeof daemon.kill === 'function') {
|
|
@@ -463,18 +492,18 @@ class ClaudeAutoStartManager {
|
|
|
463
492
|
}
|
|
464
493
|
this.log(` - ${name} stopped`);
|
|
465
494
|
});
|
|
466
|
-
|
|
495
|
+
|
|
467
496
|
// Close watchers
|
|
468
497
|
this.watchers.forEach((watcher, name) => {
|
|
469
498
|
watcher.close();
|
|
470
499
|
this.log(` - ${name} closed`);
|
|
471
500
|
});
|
|
472
|
-
|
|
501
|
+
|
|
473
502
|
// Remove PID file
|
|
474
503
|
if (fs.existsSync(this.pidFile)) {
|
|
475
504
|
fs.unlinkSync(this.pidFile);
|
|
476
505
|
}
|
|
477
|
-
|
|
506
|
+
|
|
478
507
|
this.log('š All daemons stopped');
|
|
479
508
|
process.exit(0);
|
|
480
509
|
}
|
|
@@ -483,22 +512,31 @@ class ClaudeAutoStartManager {
|
|
|
483
512
|
* Check status
|
|
484
513
|
*/
|
|
485
514
|
static status() {
|
|
486
|
-
const pidFile = path.join(
|
|
487
|
-
|
|
515
|
+
const pidFile = path.join(
|
|
516
|
+
process.env.HOME,
|
|
517
|
+
'.stackmemory',
|
|
518
|
+
'claude-daemons.pid'
|
|
519
|
+
);
|
|
520
|
+
|
|
488
521
|
if (fs.existsSync(pidFile)) {
|
|
489
522
|
const pid = fs.readFileSync(pidFile, 'utf8').trim();
|
|
490
|
-
|
|
523
|
+
|
|
491
524
|
try {
|
|
492
525
|
// Check if process is running
|
|
493
526
|
process.kill(pid, 0);
|
|
494
527
|
console.log(`ā
Claude daemons running (PID: ${pid})`);
|
|
495
|
-
|
|
528
|
+
|
|
496
529
|
// Show recent logs
|
|
497
|
-
const logFile = path.join(
|
|
530
|
+
const logFile = path.join(
|
|
531
|
+
process.env.HOME,
|
|
532
|
+
'.stackmemory',
|
|
533
|
+
'logs',
|
|
534
|
+
'claude-autostart.log'
|
|
535
|
+
);
|
|
498
536
|
if (fs.existsSync(logFile)) {
|
|
499
537
|
const logs = fs.readFileSync(logFile, 'utf8').split('\n').slice(-10);
|
|
500
538
|
console.log('\nRecent activity:');
|
|
501
|
-
logs.forEach(line => console.log(line));
|
|
539
|
+
logs.forEach((line) => console.log(line));
|
|
502
540
|
}
|
|
503
541
|
return true;
|
|
504
542
|
} catch (error) {
|
|
@@ -519,7 +557,11 @@ const command = process.argv[2];
|
|
|
519
557
|
if (command === 'status') {
|
|
520
558
|
ClaudeAutoStartManager.status();
|
|
521
559
|
} else if (command === 'stop') {
|
|
522
|
-
const pidFile = path.join(
|
|
560
|
+
const pidFile = path.join(
|
|
561
|
+
process.env.HOME,
|
|
562
|
+
'.stackmemory',
|
|
563
|
+
'claude-daemons.pid'
|
|
564
|
+
);
|
|
523
565
|
if (fs.existsSync(pidFile)) {
|
|
524
566
|
const pid = fs.readFileSync(pidFile, 'utf8').trim();
|
|
525
567
|
process.kill(pid, 'SIGTERM');
|
|
@@ -529,4 +571,4 @@ if (command === 'status') {
|
|
|
529
571
|
// Start the manager
|
|
530
572
|
const manager = new ClaudeAutoStartManager();
|
|
531
573
|
manager.start();
|
|
532
|
-
}
|
|
574
|
+
}
|
|
@@ -34,13 +34,13 @@ console.log('āļø Creating StackMemory MCP configuration...');
|
|
|
34
34
|
const mcpConfig = {
|
|
35
35
|
mcpServers: {
|
|
36
36
|
stackmemory: {
|
|
37
|
-
command:
|
|
38
|
-
args: [
|
|
37
|
+
command: 'stackmemory',
|
|
38
|
+
args: ['mcp-server'],
|
|
39
39
|
env: {
|
|
40
|
-
NODE_ENV:
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
40
|
+
NODE_ENV: 'production',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
writeFileSync(STACKMEMORY_MCP_CONFIG, JSON.stringify(mcpConfig, null, 2));
|
|
@@ -90,7 +90,7 @@ fi
|
|
|
90
90
|
|
|
91
91
|
# Load previous handoff if exists
|
|
92
92
|
if [ -d ".stackmemory/handoffs" ]; then
|
|
93
|
-
stackmemory
|
|
93
|
+
stackmemory restore --no-copy 2>/dev/null || true
|
|
94
94
|
fi
|
|
95
95
|
|
|
96
96
|
# Check and restore from ledger if needed
|
|
@@ -163,7 +163,9 @@ try {
|
|
|
163
163
|
execSync('stackmemory --version', { stdio: 'ignore' });
|
|
164
164
|
console.log('ā
StackMemory CLI available');
|
|
165
165
|
} catch {
|
|
166
|
-
console.log(
|
|
166
|
+
console.log(
|
|
167
|
+
'ā ļø StackMemory CLI not in PATH - you may need to restart your terminal'
|
|
168
|
+
);
|
|
167
169
|
}
|
|
168
170
|
|
|
169
171
|
try {
|
|
@@ -171,7 +173,9 @@ try {
|
|
|
171
173
|
execSync('claude --help', { stdio: 'ignore' });
|
|
172
174
|
console.log('ā
Claude Code available');
|
|
173
175
|
} catch {
|
|
174
|
-
console.log(
|
|
176
|
+
console.log(
|
|
177
|
+
'ā ļø Claude Code not found - install from https://claude.ai/code'
|
|
178
|
+
);
|
|
175
179
|
}
|
|
176
180
|
|
|
177
181
|
// 7. Usage instructions
|
|
@@ -201,4 +205,4 @@ console.log(`
|
|
|
201
205
|
š§ To reconfigure: npm run claude:setup
|
|
202
206
|
`);
|
|
203
207
|
|
|
204
|
-
console.log('⨠Integration setup successful!');
|
|
208
|
+
console.log('⨠Integration setup successful!');
|