@stackmemoryai/stackmemory 0.3.17 ā 0.3.18
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/dist/cli/commands/skills.js +15 -2
- package/dist/cli/commands/skills.js.map +2 -2
- package/dist/cli/index.js +113 -834
- package/dist/cli/index.js.map +3 -3
- package/dist/core/context/dual-stack-manager.js +1 -1
- package/dist/core/context/dual-stack-manager.js.map +1 -1
- package/dist/core/context/frame-manager.js +3 -0
- package/dist/core/context/frame-manager.js.map +2 -2
- package/dist/integrations/claude-code/subagent-client.js +106 -3
- package/dist/integrations/claude-code/subagent-client.js.map +2 -2
- package/dist/servers/railway/config.js +51 -0
- package/dist/servers/railway/config.js.map +7 -0
- package/dist/servers/railway/index-enhanced.js +156 -0
- package/dist/servers/railway/index-enhanced.js.map +7 -0
- package/dist/servers/railway/minimal.js +48 -3
- package/dist/servers/railway/minimal.js.map +2 -2
- package/dist/servers/railway/storage-test.js +455 -0
- package/dist/servers/railway/storage-test.js.map +7 -0
- package/dist/skills/claude-skills.js +13 -12
- package/dist/skills/claude-skills.js.map +2 -2
- package/dist/skills/recursive-agent-orchestrator.js +27 -18
- package/dist/skills/recursive-agent-orchestrator.js.map +2 -2
- package/dist/skills/unified-rlm-orchestrator.js.map +2 -2
- package/package.json +6 -18
- package/scripts/README-TESTING.md +186 -0
- package/scripts/analyze-cli-security.js +288 -0
- package/scripts/archive/add-phase-tasks-to-linear.js +163 -0
- package/scripts/archive/analyze-linear-duplicates.js +214 -0
- package/scripts/archive/analyze-remaining-duplicates.js +230 -0
- package/scripts/archive/analyze-sta-duplicates.js +292 -0
- package/scripts/archive/analyze-sta-graphql.js +399 -0
- package/scripts/archive/cancel-duplicate-tasks.ts +246 -0
- package/scripts/archive/check-all-duplicates.ts +419 -0
- package/scripts/archive/clean-duplicate-tasks.js +114 -0
- package/scripts/archive/cleanup-duplicate-tasks.ts +286 -0
- package/scripts/archive/create-phase-tasks.js +387 -0
- package/scripts/archive/delete-linear-duplicates.js +182 -0
- package/scripts/archive/delete-remaining-duplicates.js +158 -0
- package/scripts/archive/delete-sta-duplicates.js +201 -0
- package/scripts/archive/delete-sta-oauth.js +201 -0
- package/scripts/archive/export-sta-tasks.js +62 -0
- package/scripts/archive/install-auto-sync.js +266 -0
- package/scripts/archive/install-chromadb-hooks.sh +133 -0
- package/scripts/archive/install-enhanced-clear-hooks.sh +431 -0
- package/scripts/archive/install-post-task-hooks.sh +289 -0
- package/scripts/archive/install-stackmemory-hooks.sh +420 -0
- package/scripts/archive/merge-linear-duplicates-safe.ts +362 -0
- package/scripts/archive/merge-linear-duplicates.ts +180 -0
- package/scripts/archive/remove-sta-tasks.js +70 -0
- package/scripts/archive/setup-background-sync.sh +168 -0
- package/scripts/archive/setup-claude-auto-triggers.sh +181 -0
- package/scripts/archive/setup-claude-autostart.sh +305 -0
- package/scripts/archive/setup-git-hooks.sh +25 -0
- package/scripts/archive/setup-linear-oauth.sh +46 -0
- package/scripts/archive/setup-mcp.sh +113 -0
- package/scripts/archive/setup-railway-deployment.sh +81 -0
- package/scripts/auto-handoff.sh +262 -0
- package/scripts/background-sync-manager.js +416 -0
- package/scripts/benchmark-performance.ts +57 -0
- package/scripts/check-redis.ts +48 -0
- package/scripts/chromadb-auto-loader.sh +128 -0
- package/scripts/chromadb-context-loader.js +479 -0
- package/scripts/claude-chromadb-hook.js +460 -0
- package/scripts/claude-code-wrapper.sh +66 -0
- package/scripts/claude-linear-skill.js +455 -0
- package/scripts/claude-pre-commit.sh +302 -0
- package/scripts/claude-sm-autostart.js +532 -0
- package/scripts/claude-sm-setup.sh +367 -0
- package/scripts/claude-with-chromadb.sh +69 -0
- package/scripts/claude-worktree-manager.sh +323 -0
- package/scripts/claude-worktree-monitor.sh +371 -0
- package/scripts/claude-worktree-setup.sh +327 -0
- package/scripts/clean-linear-backlog.js +273 -0
- package/scripts/cleanup-old-sessions.sh +57 -0
- package/scripts/codex-wrapper.sh +88 -0
- package/scripts/create-sandbox.sh +269 -0
- package/scripts/debug-linear-update.js +174 -0
- package/scripts/delete-linear-tasks.js +167 -0
- package/scripts/deploy.sh +89 -0
- package/scripts/deployment/railway.sh +352 -0
- package/scripts/deployment/test-deployment.js +194 -0
- package/scripts/detect-and-rehydrate.js +162 -0
- package/scripts/detect-and-rehydrate.mjs +165 -0
- package/scripts/development/create-demo-tasks.js +143 -0
- package/scripts/development/debug-frame-test.js +16 -0
- package/scripts/development/demo-auto-sync.js +128 -0
- package/scripts/development/fix-all-imports.js +213 -0
- package/scripts/development/fix-imports.js +229 -0
- package/scripts/development/fix-lint-loop.cjs +103 -0
- package/scripts/development/fix-project-id.ts +161 -0
- package/scripts/development/fix-strict-mode-issues.ts +291 -0
- package/scripts/development/reorganize-structure.sh +228 -0
- package/scripts/development/test-persistence-direct.js +148 -0
- package/scripts/development/test-persistence.js +114 -0
- package/scripts/development/test-tasks.js +93 -0
- package/scripts/development/update-imports.js +212 -0
- package/scripts/fetch-linear-status.js +125 -0
- package/scripts/git-hooks/README.md +310 -0
- package/scripts/git-hooks/branch-context-manager.sh +342 -0
- package/scripts/git-hooks/post-checkout-stackmemory.sh +63 -0
- package/scripts/git-hooks/post-commit-stackmemory.sh +305 -0
- package/scripts/git-hooks/pre-commit-stackmemory.sh +275 -0
- package/scripts/hooks/cleanup-shell.sh +130 -0
- package/scripts/hooks/task-complete.sh +114 -0
- package/scripts/initialize.ts +129 -0
- package/scripts/install-claude-hooks-auto.js +104 -0
- package/scripts/install-claude-hooks.sh +133 -0
- package/scripts/install-global.sh +296 -0
- package/scripts/install.sh +235 -0
- package/scripts/linear-auto-sync.js +262 -0
- package/scripts/linear-auto-sync.sh +161 -0
- package/scripts/linear-sync-daemon.js +150 -0
- package/scripts/linear-task-review.js +237 -0
- package/scripts/list-linear-tasks.ts +178 -0
- package/scripts/mcp-proxy.js +66 -0
- package/scripts/opencode-wrapper.sh +85 -0
- package/scripts/publish-local.js +74 -0
- package/scripts/query-chromadb.ts +201 -0
- package/scripts/railway-env-setup.sh +39 -0
- package/scripts/reconcile-local-tasks.js +170 -0
- package/scripts/recreate-frames-db.js +89 -0
- package/scripts/setup/claude-integration.js +138 -0
- package/scripts/setup/configure-alias.js +125 -0
- package/scripts/setup/configure-codex-alias.js +161 -0
- package/scripts/setup/configure-opencode-alias.js +175 -0
- package/scripts/setup-claude-integration.js +204 -0
- package/scripts/setup-claude-integration.sh +183 -0
- package/scripts/setup.sh +31 -0
- package/scripts/show-linear-summary.ts +172 -0
- package/scripts/stackmemory-auto-handoff.sh +231 -0
- package/scripts/stackmemory-daemon.sh +40 -0
- package/scripts/start-linear-sync-daemon.sh +141 -0
- package/scripts/start-temporal-paradox.sh +214 -0
- package/scripts/status.ts +159 -0
- package/scripts/sync-and-clean-tasks.js +258 -0
- package/scripts/sync-frames-from-railway.js +228 -0
- package/scripts/sync-linear-graphql.js +303 -0
- package/scripts/sync-linear-tasks.js +186 -0
- package/scripts/test-auto-triggers.sh +57 -0
- package/scripts/test-browser-mcp.js +74 -0
- package/scripts/test-chromadb-full.js +115 -0
- package/scripts/test-chromadb-hooks.sh +28 -0
- package/scripts/test-chromadb-sync.ts +245 -0
- package/scripts/test-cli-security.js +293 -0
- package/scripts/test-hooks-persistence.sh +220 -0
- package/scripts/test-installation-scenarios.sh +359 -0
- package/scripts/test-installation.sh +224 -0
- package/scripts/test-mcp.js +163 -0
- package/scripts/test-pre-publish-quick.sh +75 -0
- package/scripts/test-quality-gates.sh +263 -0
- package/scripts/test-railway-db.js +222 -0
- package/scripts/test-redis-storage.ts +490 -0
- package/scripts/test-rlm-basic.sh +122 -0
- package/scripts/test-rlm-comprehensive.sh +260 -0
- package/scripts/test-rlm-e2e.sh +268 -0
- package/scripts/test-rlm-simple.js +90 -0
- package/scripts/test-rlm.js +110 -0
- package/scripts/test-session-handoff.sh +165 -0
- package/scripts/test-shell-integration.sh +275 -0
- package/scripts/testing/ab-test-runner.ts +508 -0
- package/scripts/testing/collect-metrics.ts +457 -0
- package/scripts/testing/quick-effectiveness-demo.js +187 -0
- package/scripts/testing/real-performance-test.js +422 -0
- package/scripts/testing/run-effectiveness-tests.sh +176 -0
- package/scripts/testing/scripts/testing/ab-test-runner.js +363 -0
- package/scripts/testing/scripts/testing/collect-metrics.js +292 -0
- package/scripts/testing/simple-effectiveness-test.js +310 -0
- package/scripts/testing/src/core/context/context-bridge.js +253 -0
- package/scripts/testing/src/core/context/frame-manager.js +746 -0
- package/scripts/testing/src/core/context/shared-context-layer.js +437 -0
- package/scripts/testing/src/core/database/database-adapter.js +54 -0
- package/scripts/testing/src/core/errors/index.js +291 -0
- package/scripts/testing/src/core/errors/recovery.js +268 -0
- package/scripts/testing/src/core/monitoring/logger.js +145 -0
- package/scripts/testing/src/core/retrieval/context-retriever.js +516 -0
- package/scripts/testing/src/core/session/index.js +1 -0
- package/scripts/testing/src/core/session/session-manager.js +323 -0
- package/scripts/testing/src/core/trace/cli-trace-wrapper.js +140 -0
- package/scripts/testing/src/core/trace/db-trace-wrapper.js +251 -0
- package/scripts/testing/src/core/trace/debug-trace.js +398 -0
- package/scripts/testing/src/core/trace/index.js +120 -0
- package/scripts/testing/src/core/trace/linear-api-wrapper.js +204 -0
- package/scripts/update-linear-status.js +268 -0
- package/scripts/update-linear-tasks-fixed.js +284 -0
- package/templates/claude-hooks/hooks.json +5 -0
- package/templates/claude-hooks/on-clear.js +56 -0
- package/templates/claude-hooks/on-startup.js +56 -0
- package/templates/claude-hooks/tool-use-trace.js +67 -0
- package/dist/features/tui/components/analytics-panel.js +0 -157
- package/dist/features/tui/components/analytics-panel.js.map +0 -7
- package/dist/features/tui/components/frame-visualizer.js +0 -377
- package/dist/features/tui/components/frame-visualizer.js.map +0 -7
- package/dist/features/tui/components/pr-tracker.js +0 -135
- package/dist/features/tui/components/pr-tracker.js.map +0 -7
- package/dist/features/tui/components/session-monitor.js +0 -299
- package/dist/features/tui/components/session-monitor.js.map +0 -7
- package/dist/features/tui/components/subagent-fleet.js +0 -395
- package/dist/features/tui/components/subagent-fleet.js.map +0 -7
- package/dist/features/tui/components/task-board.js +0 -1139
- package/dist/features/tui/components/task-board.js.map +0 -7
- package/dist/features/tui/index.js +0 -408
- package/dist/features/tui/index.js.map +0 -7
- package/dist/features/tui/services/data-service.js +0 -641
- package/dist/features/tui/services/data-service.js.map +0 -7
- package/dist/features/tui/services/linear-task-reader.js +0 -102
- package/dist/features/tui/services/linear-task-reader.js.map +0 -7
- package/dist/features/tui/services/websocket-client.js +0 -162
- package/dist/features/tui/services/websocket-client.js.map +0 -7
- package/dist/features/tui/terminal-compat.js +0 -220
- package/dist/features/tui/terminal-compat.js.map +0 -7
- package/dist/features/tui/types.js +0 -1
- package/dist/features/tui/types.js.map +0 -7
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* StackMemory Claude Auto-Start Daemon Manager
|
|
5
|
+
* Automatically starts essential daemons when Claude loads the project
|
|
6
|
+
*
|
|
7
|
+
* Daemons managed:
|
|
8
|
+
* 1. Context Monitor - Saves context every 15 min
|
|
9
|
+
* 2. Linear Sync - Syncs tasks hourly
|
|
10
|
+
* 3. File Watcher - Auto-syncs on file changes
|
|
11
|
+
* 4. Error Monitor - Tracks and logs errors
|
|
12
|
+
* 5. Webhook Listener - Receives Linear webhooks
|
|
13
|
+
* 6. Quality Gates - Post-task validation
|
|
14
|
+
* 7. Auto-handoff - Session transition helper
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from 'fs';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
import { spawn } from 'child_process';
|
|
21
|
+
import chokidar from 'chokidar';
|
|
22
|
+
import dotenv from 'dotenv';
|
|
23
|
+
|
|
24
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
|
|
26
|
+
// Load .env first (as per CLAUDE.md)
|
|
27
|
+
dotenv.config({
|
|
28
|
+
path: path.join(__dirname, '..', '.env'),
|
|
29
|
+
override: true,
|
|
30
|
+
silent: true
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
class ClaudeAutoStartManager {
|
|
34
|
+
constructor() {
|
|
35
|
+
this.daemons = new Map();
|
|
36
|
+
this.watchers = new Map();
|
|
37
|
+
this.projectRoot = path.dirname(__dirname);
|
|
38
|
+
this.logDir = path.join(process.env.HOME, '.stackmemory', 'logs');
|
|
39
|
+
this.pidFile = path.join(process.env.HOME, '.stackmemory', 'claude-daemons.pid');
|
|
40
|
+
|
|
41
|
+
// Ensure log directory exists
|
|
42
|
+
if (!fs.existsSync(this.logDir)) {
|
|
43
|
+
fs.mkdirSync(this.logDir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
log(message, level = 'INFO') {
|
|
48
|
+
const timestamp = new Date().toISOString();
|
|
49
|
+
const logMessage = `[${timestamp}] [${level}] ${message}`;
|
|
50
|
+
console.log(logMessage);
|
|
51
|
+
|
|
52
|
+
const logFile = path.join(this.logDir, 'claude-autostart.log');
|
|
53
|
+
fs.appendFileSync(logFile, logMessage + '\n');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 1. Context Monitor Daemon
|
|
58
|
+
* Saves context and decisions every 15 minutes
|
|
59
|
+
* Also loads context from ChromaDB
|
|
60
|
+
*/
|
|
61
|
+
startContextMonitor() {
|
|
62
|
+
this.log('Starting Context Monitor...');
|
|
63
|
+
|
|
64
|
+
const contextInterval = setInterval(async () => {
|
|
65
|
+
try {
|
|
66
|
+
// Check if stackmemory is available
|
|
67
|
+
const { exec } = await import('child_process');
|
|
68
|
+
const { promisify } = await import('util');
|
|
69
|
+
const execAsync = promisify(exec);
|
|
70
|
+
|
|
71
|
+
// Save current context
|
|
72
|
+
const { stdout } = await execAsync(
|
|
73
|
+
`cd ${this.projectRoot} && ~/.stackmemory/bin/stackmemory context add decision "Auto-checkpoint at ${new Date().toISOString()}"`
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
this.log('Context checkpoint saved');
|
|
77
|
+
|
|
78
|
+
// Load context from ChromaDB if available
|
|
79
|
+
if (process.env.CHROMADB_API_KEY) {
|
|
80
|
+
try {
|
|
81
|
+
await execAsync(
|
|
82
|
+
`cd ${this.projectRoot} && node scripts/chromadb-context-loader.js load 1`
|
|
83
|
+
);
|
|
84
|
+
this.log('ChromaDB context loaded');
|
|
85
|
+
|
|
86
|
+
// Check for important changes
|
|
87
|
+
await execAsync(
|
|
88
|
+
`cd ${this.projectRoot} && node scripts/chromadb-context-loader.js changes`
|
|
89
|
+
);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
// Silent fail for ChromaDB
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
this.log(`Context monitor error: ${error.message}`, 'ERROR');
|
|
96
|
+
}
|
|
97
|
+
}, 15 * 60 * 1000); // Every 15 minutes
|
|
98
|
+
|
|
99
|
+
// Also load context immediately on start
|
|
100
|
+
this.loadInitialContext();
|
|
101
|
+
|
|
102
|
+
this.daemons.set('context-monitor', contextInterval);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async loadInitialContext() {
|
|
106
|
+
if (!process.env.CHROMADB_API_KEY) return;
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const { exec } = await import('child_process');
|
|
110
|
+
const { promisify } = await import('util');
|
|
111
|
+
const execAsync = promisify(exec);
|
|
112
|
+
|
|
113
|
+
// Load last 24 hours of context
|
|
114
|
+
await execAsync(
|
|
115
|
+
`cd ${this.projectRoot} && node scripts/chromadb-context-loader.js auto`
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
this.log('Initial ChromaDB context loaded');
|
|
119
|
+
} catch (error) {
|
|
120
|
+
this.log(`Initial context load error: ${error.message}`, 'WARN');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 2. Linear Sync Daemon
|
|
126
|
+
* Already created, just ensure it's running
|
|
127
|
+
*/
|
|
128
|
+
startLinearSync() {
|
|
129
|
+
if (!process.env.LINEAR_API_KEY) {
|
|
130
|
+
this.log('Linear sync skipped - no API key', 'WARN');
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.log('Starting Linear Sync Daemon...');
|
|
135
|
+
|
|
136
|
+
const linearSync = spawn('node', [
|
|
137
|
+
path.join(this.projectRoot, 'scripts', 'linear-sync-daemon.js')
|
|
138
|
+
], {
|
|
139
|
+
detached: true,
|
|
140
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
linearSync.unref();
|
|
144
|
+
this.daemons.set('linear-sync', linearSync);
|
|
145
|
+
this.log(`Linear sync started (PID: ${linearSync.pid})`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 3. File Watcher Daemon
|
|
150
|
+
* Watches for changes and auto-syncs
|
|
151
|
+
*/
|
|
152
|
+
startFileWatcher() {
|
|
153
|
+
this.log('Starting File Watcher...');
|
|
154
|
+
|
|
155
|
+
const watchPaths = [
|
|
156
|
+
path.join(this.projectRoot, 'src'),
|
|
157
|
+
path.join(this.projectRoot, 'scripts'),
|
|
158
|
+
path.join(this.projectRoot, '.stackmemory', 'tasks.jsonl')
|
|
159
|
+
];
|
|
160
|
+
|
|
161
|
+
const watcher = chokidar.watch(watchPaths, {
|
|
162
|
+
persistent: true,
|
|
163
|
+
ignoreInitial: true,
|
|
164
|
+
ignored: [
|
|
165
|
+
'**/node_modules/**',
|
|
166
|
+
'**/.git/**',
|
|
167
|
+
'**/dist/**',
|
|
168
|
+
'**/build/**',
|
|
169
|
+
'**/*.log'
|
|
170
|
+
]
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
let changeTimeout;
|
|
174
|
+
|
|
175
|
+
watcher.on('change', (filepath) => {
|
|
176
|
+
// Debounce changes
|
|
177
|
+
clearTimeout(changeTimeout);
|
|
178
|
+
changeTimeout = setTimeout(() => {
|
|
179
|
+
this.log(`File changed: ${path.relative(this.projectRoot, filepath)}`);
|
|
180
|
+
|
|
181
|
+
// Auto-save context on significant changes
|
|
182
|
+
if (filepath.endsWith('.ts') || filepath.endsWith('.js')) {
|
|
183
|
+
this.saveFileChangeContext(filepath);
|
|
184
|
+
}
|
|
185
|
+
}, 1000);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
this.watchers.set('file-watcher', watcher);
|
|
189
|
+
this.log('File watcher active');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async saveFileChangeContext(filepath) {
|
|
193
|
+
try {
|
|
194
|
+
const { exec } = await import('child_process');
|
|
195
|
+
const { promisify } = await import('util');
|
|
196
|
+
const execAsync = promisify(exec);
|
|
197
|
+
|
|
198
|
+
const filename = path.basename(filepath);
|
|
199
|
+
await execAsync(
|
|
200
|
+
`cd ${this.projectRoot} && ~/.stackmemory/bin/stackmemory context add observation "Modified: ${filename}"`
|
|
201
|
+
);
|
|
202
|
+
} catch (error) {
|
|
203
|
+
// Silent fail - don't interrupt workflow
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 4. Error Monitor Daemon
|
|
209
|
+
* Monitors logs for errors and patterns
|
|
210
|
+
*/
|
|
211
|
+
startErrorMonitor() {
|
|
212
|
+
this.log('Starting Error Monitor...');
|
|
213
|
+
|
|
214
|
+
const errorPatterns = [
|
|
215
|
+
/ERROR/i,
|
|
216
|
+
/FAILED/i,
|
|
217
|
+
/Exception/,
|
|
218
|
+
/TypeError/,
|
|
219
|
+
/ReferenceError/,
|
|
220
|
+
/SyntaxError/
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
const monitorInterval = setInterval(() => {
|
|
224
|
+
// Check recent logs for errors
|
|
225
|
+
const logsToCheck = [
|
|
226
|
+
path.join(this.logDir, 'linear-sync.log'),
|
|
227
|
+
path.join(this.logDir, 'sync-manager.log'),
|
|
228
|
+
path.join(this.projectRoot, 'npm-debug.log')
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
logsToCheck.forEach(logFile => {
|
|
232
|
+
if (fs.existsSync(logFile)) {
|
|
233
|
+
const stats = fs.statSync(logFile);
|
|
234
|
+
const lastCheck = this.lastErrorCheck || 0;
|
|
235
|
+
|
|
236
|
+
if (stats.mtimeMs > lastCheck) {
|
|
237
|
+
const content = fs.readFileSync(logFile, 'utf8');
|
|
238
|
+
const lines = content.split('\n').slice(-100); // Last 100 lines
|
|
239
|
+
|
|
240
|
+
lines.forEach(line => {
|
|
241
|
+
errorPatterns.forEach(pattern => {
|
|
242
|
+
if (pattern.test(line)) {
|
|
243
|
+
this.log(`Error detected: ${line.substring(0, 100)}...`, 'WARN');
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
this.lastErrorCheck = Date.now();
|
|
252
|
+
}, 60 * 1000); // Every minute
|
|
253
|
+
|
|
254
|
+
this.daemons.set('error-monitor', monitorInterval);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* 5. Webhook Listener Daemon
|
|
259
|
+
* Listens for Linear webhooks
|
|
260
|
+
*/
|
|
261
|
+
startWebhookListener() {
|
|
262
|
+
this.log('Starting Webhook Listener...');
|
|
263
|
+
|
|
264
|
+
const express = require('express');
|
|
265
|
+
const app = express();
|
|
266
|
+
app.use(express.json());
|
|
267
|
+
|
|
268
|
+
const PORT = process.env.WEBHOOK_PORT || 3456;
|
|
269
|
+
|
|
270
|
+
app.post('/webhooks/linear', (req, res) => {
|
|
271
|
+
const { action, data } = req.body;
|
|
272
|
+
this.log(`Linear webhook: ${action} - ${data.identifier || data.id}`);
|
|
273
|
+
|
|
274
|
+
// Process webhook
|
|
275
|
+
if (action === 'create' || action === 'update') {
|
|
276
|
+
// Trigger sync
|
|
277
|
+
this.triggerLinearSync();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
res.status(200).send('OK');
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const server = app.listen(PORT, () => {
|
|
284
|
+
this.log(`Webhook listener on port ${PORT}`);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
this.daemons.set('webhook-listener', server);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async triggerLinearSync() {
|
|
291
|
+
try {
|
|
292
|
+
const { exec } = await import('child_process');
|
|
293
|
+
const { promisify } = await import('util');
|
|
294
|
+
const execAsync = promisify(exec);
|
|
295
|
+
|
|
296
|
+
await execAsync(
|
|
297
|
+
`cd ${this.projectRoot} && node scripts/sync-linear-graphql.js`
|
|
298
|
+
);
|
|
299
|
+
this.log('Linear sync triggered by webhook');
|
|
300
|
+
} catch (error) {
|
|
301
|
+
this.log(`Webhook sync error: ${error.message}`, 'ERROR');
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* 6. Quality Gates Monitor
|
|
307
|
+
* Runs after task completion
|
|
308
|
+
*/
|
|
309
|
+
startQualityGates() {
|
|
310
|
+
this.log('Starting Quality Gates Monitor...');
|
|
311
|
+
|
|
312
|
+
// Watch for task completion patterns
|
|
313
|
+
const taskWatcher = chokidar.watch(
|
|
314
|
+
path.join(this.projectRoot, '.stackmemory', 'tasks.jsonl'),
|
|
315
|
+
{ persistent: true }
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
taskWatcher.on('change', async () => {
|
|
319
|
+
// Check last task status
|
|
320
|
+
try {
|
|
321
|
+
const tasksFile = path.join(this.projectRoot, '.stackmemory', 'tasks.jsonl');
|
|
322
|
+
const lines = fs.readFileSync(tasksFile, 'utf8').split('\n').filter(Boolean);
|
|
323
|
+
const lastTask = JSON.parse(lines[lines.length - 1]);
|
|
324
|
+
|
|
325
|
+
if (lastTask.status === 'completed' &&
|
|
326
|
+
lastTask.timestamp > Date.now() - 60000) { // Within last minute
|
|
327
|
+
await this.runQualityChecks();
|
|
328
|
+
}
|
|
329
|
+
} catch (error) {
|
|
330
|
+
// Silent fail
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
this.watchers.set('quality-gates', taskWatcher);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async runQualityChecks() {
|
|
338
|
+
this.log('Running quality checks...');
|
|
339
|
+
|
|
340
|
+
const checks = [
|
|
341
|
+
{ name: 'Lint', cmd: 'npm run lint' },
|
|
342
|
+
{ name: 'Tests', cmd: 'npm test' },
|
|
343
|
+
{ name: 'Build', cmd: 'npm run build' }
|
|
344
|
+
];
|
|
345
|
+
|
|
346
|
+
for (const check of checks) {
|
|
347
|
+
try {
|
|
348
|
+
const { exec } = await import('child_process');
|
|
349
|
+
const { promisify } = await import('util');
|
|
350
|
+
const execAsync = promisify(exec);
|
|
351
|
+
|
|
352
|
+
await execAsync(`cd ${this.projectRoot} && ${check.cmd}`);
|
|
353
|
+
this.log(`ā
${check.name} passed`);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
this.log(`ā ${check.name} failed: ${error.message}`, 'ERROR');
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* 7. Auto-handoff Daemon
|
|
362
|
+
* Prepares handoff when session ends
|
|
363
|
+
*/
|
|
364
|
+
startAutoHandoff() {
|
|
365
|
+
this.log('Starting Auto-handoff Monitor...');
|
|
366
|
+
|
|
367
|
+
// Monitor for session end signals
|
|
368
|
+
process.on('SIGINT', () => this.prepareHandoff('interrupt'));
|
|
369
|
+
process.on('SIGTERM', () => this.prepareHandoff('terminate'));
|
|
370
|
+
|
|
371
|
+
// Also monitor for idle time
|
|
372
|
+
let lastActivity = Date.now();
|
|
373
|
+
|
|
374
|
+
const idleChecker = setInterval(() => {
|
|
375
|
+
const idleTime = Date.now() - lastActivity;
|
|
376
|
+
if (idleTime > 30 * 60 * 1000) { // 30 minutes idle
|
|
377
|
+
this.prepareHandoff('idle');
|
|
378
|
+
}
|
|
379
|
+
}, 5 * 60 * 1000); // Check every 5 minutes
|
|
380
|
+
|
|
381
|
+
// Update activity on any file change
|
|
382
|
+
this.watchers.get('file-watcher')?.on('all', () => {
|
|
383
|
+
lastActivity = Date.now();
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
this.daemons.set('auto-handoff', idleChecker);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async prepareHandoff(reason) {
|
|
390
|
+
this.log(`Preparing handoff (${reason})...`);
|
|
391
|
+
|
|
392
|
+
try {
|
|
393
|
+
const { exec } = await import('child_process');
|
|
394
|
+
const { promisify } = await import('util');
|
|
395
|
+
const execAsync = promisify(exec);
|
|
396
|
+
|
|
397
|
+
// Generate handoff
|
|
398
|
+
await execAsync(
|
|
399
|
+
`cd ${this.projectRoot} && ~/.stackmemory/bin/stackmemory handoff generate`
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
this.log('Handoff prepared successfully');
|
|
403
|
+
} catch (error) {
|
|
404
|
+
this.log(`Handoff error: ${error.message}`, 'ERROR');
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Start all daemons
|
|
410
|
+
*/
|
|
411
|
+
async start() {
|
|
412
|
+
this.log('š Claude StackMemory Auto-Start Manager');
|
|
413
|
+
this.log('=========================================\n');
|
|
414
|
+
|
|
415
|
+
// Save PID for management
|
|
416
|
+
fs.writeFileSync(this.pidFile, process.pid.toString());
|
|
417
|
+
|
|
418
|
+
// Start all daemons
|
|
419
|
+
this.startContextMonitor();
|
|
420
|
+
this.startLinearSync();
|
|
421
|
+
this.startFileWatcher();
|
|
422
|
+
this.startErrorMonitor();
|
|
423
|
+
|
|
424
|
+
// Optional daemons (only if configured)
|
|
425
|
+
if (process.env.ENABLE_WEBHOOKS === 'true') {
|
|
426
|
+
this.startWebhookListener();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (process.env.ENABLE_QUALITY_GATES === 'true') {
|
|
430
|
+
this.startQualityGates();
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
this.startAutoHandoff();
|
|
434
|
+
|
|
435
|
+
this.log('\nā
All daemons started successfully');
|
|
436
|
+
this.log('š Active daemons:');
|
|
437
|
+
this.daemons.forEach((daemon, name) => {
|
|
438
|
+
this.log(` - ${name}`);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// Handle shutdown
|
|
442
|
+
process.on('SIGINT', () => this.stop());
|
|
443
|
+
process.on('SIGTERM', () => this.stop());
|
|
444
|
+
|
|
445
|
+
// Keep process alive
|
|
446
|
+
process.stdin.resume();
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Stop all daemons
|
|
451
|
+
*/
|
|
452
|
+
stop() {
|
|
453
|
+
this.log('\nš Stopping all daemons...');
|
|
454
|
+
|
|
455
|
+
// Clear intervals
|
|
456
|
+
this.daemons.forEach((daemon, name) => {
|
|
457
|
+
if (typeof daemon.kill === 'function') {
|
|
458
|
+
daemon.kill();
|
|
459
|
+
} else if (typeof daemon === 'number' || daemon._idleTimeout) {
|
|
460
|
+
clearInterval(daemon);
|
|
461
|
+
} else if (typeof daemon.close === 'function') {
|
|
462
|
+
daemon.close();
|
|
463
|
+
}
|
|
464
|
+
this.log(` - ${name} stopped`);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// Close watchers
|
|
468
|
+
this.watchers.forEach((watcher, name) => {
|
|
469
|
+
watcher.close();
|
|
470
|
+
this.log(` - ${name} closed`);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// Remove PID file
|
|
474
|
+
if (fs.existsSync(this.pidFile)) {
|
|
475
|
+
fs.unlinkSync(this.pidFile);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
this.log('š All daemons stopped');
|
|
479
|
+
process.exit(0);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Check status
|
|
484
|
+
*/
|
|
485
|
+
static status() {
|
|
486
|
+
const pidFile = path.join(process.env.HOME, '.stackmemory', 'claude-daemons.pid');
|
|
487
|
+
|
|
488
|
+
if (fs.existsSync(pidFile)) {
|
|
489
|
+
const pid = fs.readFileSync(pidFile, 'utf8').trim();
|
|
490
|
+
|
|
491
|
+
try {
|
|
492
|
+
// Check if process is running
|
|
493
|
+
process.kill(pid, 0);
|
|
494
|
+
console.log(`ā
Claude daemons running (PID: ${pid})`);
|
|
495
|
+
|
|
496
|
+
// Show recent logs
|
|
497
|
+
const logFile = path.join(process.env.HOME, '.stackmemory', 'logs', 'claude-autostart.log');
|
|
498
|
+
if (fs.existsSync(logFile)) {
|
|
499
|
+
const logs = fs.readFileSync(logFile, 'utf8').split('\n').slice(-10);
|
|
500
|
+
console.log('\nRecent activity:');
|
|
501
|
+
logs.forEach(line => console.log(line));
|
|
502
|
+
}
|
|
503
|
+
return true;
|
|
504
|
+
} catch (error) {
|
|
505
|
+
console.log('ā Claude daemons not running (stale PID file)');
|
|
506
|
+
fs.unlinkSync(pidFile);
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
} else {
|
|
510
|
+
console.log('ā Claude daemons not running');
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Handle CLI commands
|
|
517
|
+
const command = process.argv[2];
|
|
518
|
+
|
|
519
|
+
if (command === 'status') {
|
|
520
|
+
ClaudeAutoStartManager.status();
|
|
521
|
+
} else if (command === 'stop') {
|
|
522
|
+
const pidFile = path.join(process.env.HOME, '.stackmemory', 'claude-daemons.pid');
|
|
523
|
+
if (fs.existsSync(pidFile)) {
|
|
524
|
+
const pid = fs.readFileSync(pidFile, 'utf8').trim();
|
|
525
|
+
process.kill(pid, 'SIGTERM');
|
|
526
|
+
console.log('Stop signal sent');
|
|
527
|
+
}
|
|
528
|
+
} else {
|
|
529
|
+
// Start the manager
|
|
530
|
+
const manager = new ClaudeAutoStartManager();
|
|
531
|
+
manager.start();
|
|
532
|
+
}
|