@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.
Files changed (212) hide show
  1. package/dist/cli/commands/skills.js +15 -2
  2. package/dist/cli/commands/skills.js.map +2 -2
  3. package/dist/cli/index.js +113 -834
  4. package/dist/cli/index.js.map +3 -3
  5. package/dist/core/context/dual-stack-manager.js +1 -1
  6. package/dist/core/context/dual-stack-manager.js.map +1 -1
  7. package/dist/core/context/frame-manager.js +3 -0
  8. package/dist/core/context/frame-manager.js.map +2 -2
  9. package/dist/integrations/claude-code/subagent-client.js +106 -3
  10. package/dist/integrations/claude-code/subagent-client.js.map +2 -2
  11. package/dist/servers/railway/config.js +51 -0
  12. package/dist/servers/railway/config.js.map +7 -0
  13. package/dist/servers/railway/index-enhanced.js +156 -0
  14. package/dist/servers/railway/index-enhanced.js.map +7 -0
  15. package/dist/servers/railway/minimal.js +48 -3
  16. package/dist/servers/railway/minimal.js.map +2 -2
  17. package/dist/servers/railway/storage-test.js +455 -0
  18. package/dist/servers/railway/storage-test.js.map +7 -0
  19. package/dist/skills/claude-skills.js +13 -12
  20. package/dist/skills/claude-skills.js.map +2 -2
  21. package/dist/skills/recursive-agent-orchestrator.js +27 -18
  22. package/dist/skills/recursive-agent-orchestrator.js.map +2 -2
  23. package/dist/skills/unified-rlm-orchestrator.js.map +2 -2
  24. package/package.json +6 -18
  25. package/scripts/README-TESTING.md +186 -0
  26. package/scripts/analyze-cli-security.js +288 -0
  27. package/scripts/archive/add-phase-tasks-to-linear.js +163 -0
  28. package/scripts/archive/analyze-linear-duplicates.js +214 -0
  29. package/scripts/archive/analyze-remaining-duplicates.js +230 -0
  30. package/scripts/archive/analyze-sta-duplicates.js +292 -0
  31. package/scripts/archive/analyze-sta-graphql.js +399 -0
  32. package/scripts/archive/cancel-duplicate-tasks.ts +246 -0
  33. package/scripts/archive/check-all-duplicates.ts +419 -0
  34. package/scripts/archive/clean-duplicate-tasks.js +114 -0
  35. package/scripts/archive/cleanup-duplicate-tasks.ts +286 -0
  36. package/scripts/archive/create-phase-tasks.js +387 -0
  37. package/scripts/archive/delete-linear-duplicates.js +182 -0
  38. package/scripts/archive/delete-remaining-duplicates.js +158 -0
  39. package/scripts/archive/delete-sta-duplicates.js +201 -0
  40. package/scripts/archive/delete-sta-oauth.js +201 -0
  41. package/scripts/archive/export-sta-tasks.js +62 -0
  42. package/scripts/archive/install-auto-sync.js +266 -0
  43. package/scripts/archive/install-chromadb-hooks.sh +133 -0
  44. package/scripts/archive/install-enhanced-clear-hooks.sh +431 -0
  45. package/scripts/archive/install-post-task-hooks.sh +289 -0
  46. package/scripts/archive/install-stackmemory-hooks.sh +420 -0
  47. package/scripts/archive/merge-linear-duplicates-safe.ts +362 -0
  48. package/scripts/archive/merge-linear-duplicates.ts +180 -0
  49. package/scripts/archive/remove-sta-tasks.js +70 -0
  50. package/scripts/archive/setup-background-sync.sh +168 -0
  51. package/scripts/archive/setup-claude-auto-triggers.sh +181 -0
  52. package/scripts/archive/setup-claude-autostart.sh +305 -0
  53. package/scripts/archive/setup-git-hooks.sh +25 -0
  54. package/scripts/archive/setup-linear-oauth.sh +46 -0
  55. package/scripts/archive/setup-mcp.sh +113 -0
  56. package/scripts/archive/setup-railway-deployment.sh +81 -0
  57. package/scripts/auto-handoff.sh +262 -0
  58. package/scripts/background-sync-manager.js +416 -0
  59. package/scripts/benchmark-performance.ts +57 -0
  60. package/scripts/check-redis.ts +48 -0
  61. package/scripts/chromadb-auto-loader.sh +128 -0
  62. package/scripts/chromadb-context-loader.js +479 -0
  63. package/scripts/claude-chromadb-hook.js +460 -0
  64. package/scripts/claude-code-wrapper.sh +66 -0
  65. package/scripts/claude-linear-skill.js +455 -0
  66. package/scripts/claude-pre-commit.sh +302 -0
  67. package/scripts/claude-sm-autostart.js +532 -0
  68. package/scripts/claude-sm-setup.sh +367 -0
  69. package/scripts/claude-with-chromadb.sh +69 -0
  70. package/scripts/claude-worktree-manager.sh +323 -0
  71. package/scripts/claude-worktree-monitor.sh +371 -0
  72. package/scripts/claude-worktree-setup.sh +327 -0
  73. package/scripts/clean-linear-backlog.js +273 -0
  74. package/scripts/cleanup-old-sessions.sh +57 -0
  75. package/scripts/codex-wrapper.sh +88 -0
  76. package/scripts/create-sandbox.sh +269 -0
  77. package/scripts/debug-linear-update.js +174 -0
  78. package/scripts/delete-linear-tasks.js +167 -0
  79. package/scripts/deploy.sh +89 -0
  80. package/scripts/deployment/railway.sh +352 -0
  81. package/scripts/deployment/test-deployment.js +194 -0
  82. package/scripts/detect-and-rehydrate.js +162 -0
  83. package/scripts/detect-and-rehydrate.mjs +165 -0
  84. package/scripts/development/create-demo-tasks.js +143 -0
  85. package/scripts/development/debug-frame-test.js +16 -0
  86. package/scripts/development/demo-auto-sync.js +128 -0
  87. package/scripts/development/fix-all-imports.js +213 -0
  88. package/scripts/development/fix-imports.js +229 -0
  89. package/scripts/development/fix-lint-loop.cjs +103 -0
  90. package/scripts/development/fix-project-id.ts +161 -0
  91. package/scripts/development/fix-strict-mode-issues.ts +291 -0
  92. package/scripts/development/reorganize-structure.sh +228 -0
  93. package/scripts/development/test-persistence-direct.js +148 -0
  94. package/scripts/development/test-persistence.js +114 -0
  95. package/scripts/development/test-tasks.js +93 -0
  96. package/scripts/development/update-imports.js +212 -0
  97. package/scripts/fetch-linear-status.js +125 -0
  98. package/scripts/git-hooks/README.md +310 -0
  99. package/scripts/git-hooks/branch-context-manager.sh +342 -0
  100. package/scripts/git-hooks/post-checkout-stackmemory.sh +63 -0
  101. package/scripts/git-hooks/post-commit-stackmemory.sh +305 -0
  102. package/scripts/git-hooks/pre-commit-stackmemory.sh +275 -0
  103. package/scripts/hooks/cleanup-shell.sh +130 -0
  104. package/scripts/hooks/task-complete.sh +114 -0
  105. package/scripts/initialize.ts +129 -0
  106. package/scripts/install-claude-hooks-auto.js +104 -0
  107. package/scripts/install-claude-hooks.sh +133 -0
  108. package/scripts/install-global.sh +296 -0
  109. package/scripts/install.sh +235 -0
  110. package/scripts/linear-auto-sync.js +262 -0
  111. package/scripts/linear-auto-sync.sh +161 -0
  112. package/scripts/linear-sync-daemon.js +150 -0
  113. package/scripts/linear-task-review.js +237 -0
  114. package/scripts/list-linear-tasks.ts +178 -0
  115. package/scripts/mcp-proxy.js +66 -0
  116. package/scripts/opencode-wrapper.sh +85 -0
  117. package/scripts/publish-local.js +74 -0
  118. package/scripts/query-chromadb.ts +201 -0
  119. package/scripts/railway-env-setup.sh +39 -0
  120. package/scripts/reconcile-local-tasks.js +170 -0
  121. package/scripts/recreate-frames-db.js +89 -0
  122. package/scripts/setup/claude-integration.js +138 -0
  123. package/scripts/setup/configure-alias.js +125 -0
  124. package/scripts/setup/configure-codex-alias.js +161 -0
  125. package/scripts/setup/configure-opencode-alias.js +175 -0
  126. package/scripts/setup-claude-integration.js +204 -0
  127. package/scripts/setup-claude-integration.sh +183 -0
  128. package/scripts/setup.sh +31 -0
  129. package/scripts/show-linear-summary.ts +172 -0
  130. package/scripts/stackmemory-auto-handoff.sh +231 -0
  131. package/scripts/stackmemory-daemon.sh +40 -0
  132. package/scripts/start-linear-sync-daemon.sh +141 -0
  133. package/scripts/start-temporal-paradox.sh +214 -0
  134. package/scripts/status.ts +159 -0
  135. package/scripts/sync-and-clean-tasks.js +258 -0
  136. package/scripts/sync-frames-from-railway.js +228 -0
  137. package/scripts/sync-linear-graphql.js +303 -0
  138. package/scripts/sync-linear-tasks.js +186 -0
  139. package/scripts/test-auto-triggers.sh +57 -0
  140. package/scripts/test-browser-mcp.js +74 -0
  141. package/scripts/test-chromadb-full.js +115 -0
  142. package/scripts/test-chromadb-hooks.sh +28 -0
  143. package/scripts/test-chromadb-sync.ts +245 -0
  144. package/scripts/test-cli-security.js +293 -0
  145. package/scripts/test-hooks-persistence.sh +220 -0
  146. package/scripts/test-installation-scenarios.sh +359 -0
  147. package/scripts/test-installation.sh +224 -0
  148. package/scripts/test-mcp.js +163 -0
  149. package/scripts/test-pre-publish-quick.sh +75 -0
  150. package/scripts/test-quality-gates.sh +263 -0
  151. package/scripts/test-railway-db.js +222 -0
  152. package/scripts/test-redis-storage.ts +490 -0
  153. package/scripts/test-rlm-basic.sh +122 -0
  154. package/scripts/test-rlm-comprehensive.sh +260 -0
  155. package/scripts/test-rlm-e2e.sh +268 -0
  156. package/scripts/test-rlm-simple.js +90 -0
  157. package/scripts/test-rlm.js +110 -0
  158. package/scripts/test-session-handoff.sh +165 -0
  159. package/scripts/test-shell-integration.sh +275 -0
  160. package/scripts/testing/ab-test-runner.ts +508 -0
  161. package/scripts/testing/collect-metrics.ts +457 -0
  162. package/scripts/testing/quick-effectiveness-demo.js +187 -0
  163. package/scripts/testing/real-performance-test.js +422 -0
  164. package/scripts/testing/run-effectiveness-tests.sh +176 -0
  165. package/scripts/testing/scripts/testing/ab-test-runner.js +363 -0
  166. package/scripts/testing/scripts/testing/collect-metrics.js +292 -0
  167. package/scripts/testing/simple-effectiveness-test.js +310 -0
  168. package/scripts/testing/src/core/context/context-bridge.js +253 -0
  169. package/scripts/testing/src/core/context/frame-manager.js +746 -0
  170. package/scripts/testing/src/core/context/shared-context-layer.js +437 -0
  171. package/scripts/testing/src/core/database/database-adapter.js +54 -0
  172. package/scripts/testing/src/core/errors/index.js +291 -0
  173. package/scripts/testing/src/core/errors/recovery.js +268 -0
  174. package/scripts/testing/src/core/monitoring/logger.js +145 -0
  175. package/scripts/testing/src/core/retrieval/context-retriever.js +516 -0
  176. package/scripts/testing/src/core/session/index.js +1 -0
  177. package/scripts/testing/src/core/session/session-manager.js +323 -0
  178. package/scripts/testing/src/core/trace/cli-trace-wrapper.js +140 -0
  179. package/scripts/testing/src/core/trace/db-trace-wrapper.js +251 -0
  180. package/scripts/testing/src/core/trace/debug-trace.js +398 -0
  181. package/scripts/testing/src/core/trace/index.js +120 -0
  182. package/scripts/testing/src/core/trace/linear-api-wrapper.js +204 -0
  183. package/scripts/update-linear-status.js +268 -0
  184. package/scripts/update-linear-tasks-fixed.js +284 -0
  185. package/templates/claude-hooks/hooks.json +5 -0
  186. package/templates/claude-hooks/on-clear.js +56 -0
  187. package/templates/claude-hooks/on-startup.js +56 -0
  188. package/templates/claude-hooks/tool-use-trace.js +67 -0
  189. package/dist/features/tui/components/analytics-panel.js +0 -157
  190. package/dist/features/tui/components/analytics-panel.js.map +0 -7
  191. package/dist/features/tui/components/frame-visualizer.js +0 -377
  192. package/dist/features/tui/components/frame-visualizer.js.map +0 -7
  193. package/dist/features/tui/components/pr-tracker.js +0 -135
  194. package/dist/features/tui/components/pr-tracker.js.map +0 -7
  195. package/dist/features/tui/components/session-monitor.js +0 -299
  196. package/dist/features/tui/components/session-monitor.js.map +0 -7
  197. package/dist/features/tui/components/subagent-fleet.js +0 -395
  198. package/dist/features/tui/components/subagent-fleet.js.map +0 -7
  199. package/dist/features/tui/components/task-board.js +0 -1139
  200. package/dist/features/tui/components/task-board.js.map +0 -7
  201. package/dist/features/tui/index.js +0 -408
  202. package/dist/features/tui/index.js.map +0 -7
  203. package/dist/features/tui/services/data-service.js +0 -641
  204. package/dist/features/tui/services/data-service.js.map +0 -7
  205. package/dist/features/tui/services/linear-task-reader.js +0 -102
  206. package/dist/features/tui/services/linear-task-reader.js.map +0 -7
  207. package/dist/features/tui/services/websocket-client.js +0 -162
  208. package/dist/features/tui/services/websocket-client.js.map +0 -7
  209. package/dist/features/tui/terminal-compat.js +0 -220
  210. package/dist/features/tui/terminal-compat.js.map +0 -7
  211. package/dist/features/tui/types.js +0 -1
  212. 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
+ }