@stackmemoryai/stackmemory 0.3.16 → 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 (213) hide show
  1. package/README.md +48 -2
  2. package/dist/cli/commands/skills.js +15 -2
  3. package/dist/cli/commands/skills.js.map +2 -2
  4. package/dist/cli/index.js +113 -834
  5. package/dist/cli/index.js.map +3 -3
  6. package/dist/core/context/dual-stack-manager.js +1 -1
  7. package/dist/core/context/dual-stack-manager.js.map +1 -1
  8. package/dist/core/context/frame-manager.js +3 -0
  9. package/dist/core/context/frame-manager.js.map +2 -2
  10. package/dist/integrations/claude-code/subagent-client.js +106 -3
  11. package/dist/integrations/claude-code/subagent-client.js.map +2 -2
  12. package/dist/servers/railway/config.js +51 -0
  13. package/dist/servers/railway/config.js.map +7 -0
  14. package/dist/servers/railway/index-enhanced.js +156 -0
  15. package/dist/servers/railway/index-enhanced.js.map +7 -0
  16. package/dist/servers/railway/minimal.js +48 -3
  17. package/dist/servers/railway/minimal.js.map +2 -2
  18. package/dist/servers/railway/storage-test.js +455 -0
  19. package/dist/servers/railway/storage-test.js.map +7 -0
  20. package/dist/skills/claude-skills.js +13 -12
  21. package/dist/skills/claude-skills.js.map +2 -2
  22. package/dist/skills/recursive-agent-orchestrator.js +27 -18
  23. package/dist/skills/recursive-agent-orchestrator.js.map +2 -2
  24. package/dist/skills/unified-rlm-orchestrator.js.map +2 -2
  25. package/package.json +6 -18
  26. package/scripts/README-TESTING.md +186 -0
  27. package/scripts/analyze-cli-security.js +288 -0
  28. package/scripts/archive/add-phase-tasks-to-linear.js +163 -0
  29. package/scripts/archive/analyze-linear-duplicates.js +214 -0
  30. package/scripts/archive/analyze-remaining-duplicates.js +230 -0
  31. package/scripts/archive/analyze-sta-duplicates.js +292 -0
  32. package/scripts/archive/analyze-sta-graphql.js +399 -0
  33. package/scripts/archive/cancel-duplicate-tasks.ts +246 -0
  34. package/scripts/archive/check-all-duplicates.ts +419 -0
  35. package/scripts/archive/clean-duplicate-tasks.js +114 -0
  36. package/scripts/archive/cleanup-duplicate-tasks.ts +286 -0
  37. package/scripts/archive/create-phase-tasks.js +387 -0
  38. package/scripts/archive/delete-linear-duplicates.js +182 -0
  39. package/scripts/archive/delete-remaining-duplicates.js +158 -0
  40. package/scripts/archive/delete-sta-duplicates.js +201 -0
  41. package/scripts/archive/delete-sta-oauth.js +201 -0
  42. package/scripts/archive/export-sta-tasks.js +62 -0
  43. package/scripts/archive/install-auto-sync.js +266 -0
  44. package/scripts/archive/install-chromadb-hooks.sh +133 -0
  45. package/scripts/archive/install-enhanced-clear-hooks.sh +431 -0
  46. package/scripts/archive/install-post-task-hooks.sh +289 -0
  47. package/scripts/archive/install-stackmemory-hooks.sh +420 -0
  48. package/scripts/archive/merge-linear-duplicates-safe.ts +362 -0
  49. package/scripts/archive/merge-linear-duplicates.ts +180 -0
  50. package/scripts/archive/remove-sta-tasks.js +70 -0
  51. package/scripts/archive/setup-background-sync.sh +168 -0
  52. package/scripts/archive/setup-claude-auto-triggers.sh +181 -0
  53. package/scripts/archive/setup-claude-autostart.sh +305 -0
  54. package/scripts/archive/setup-git-hooks.sh +25 -0
  55. package/scripts/archive/setup-linear-oauth.sh +46 -0
  56. package/scripts/archive/setup-mcp.sh +113 -0
  57. package/scripts/archive/setup-railway-deployment.sh +81 -0
  58. package/scripts/auto-handoff.sh +262 -0
  59. package/scripts/background-sync-manager.js +416 -0
  60. package/scripts/benchmark-performance.ts +57 -0
  61. package/scripts/check-redis.ts +48 -0
  62. package/scripts/chromadb-auto-loader.sh +128 -0
  63. package/scripts/chromadb-context-loader.js +479 -0
  64. package/scripts/claude-chromadb-hook.js +460 -0
  65. package/scripts/claude-code-wrapper.sh +66 -0
  66. package/scripts/claude-linear-skill.js +455 -0
  67. package/scripts/claude-pre-commit.sh +302 -0
  68. package/scripts/claude-sm-autostart.js +532 -0
  69. package/scripts/claude-sm-setup.sh +367 -0
  70. package/scripts/claude-with-chromadb.sh +69 -0
  71. package/scripts/claude-worktree-manager.sh +323 -0
  72. package/scripts/claude-worktree-monitor.sh +371 -0
  73. package/scripts/claude-worktree-setup.sh +327 -0
  74. package/scripts/clean-linear-backlog.js +273 -0
  75. package/scripts/cleanup-old-sessions.sh +57 -0
  76. package/scripts/codex-wrapper.sh +88 -0
  77. package/scripts/create-sandbox.sh +269 -0
  78. package/scripts/debug-linear-update.js +174 -0
  79. package/scripts/delete-linear-tasks.js +167 -0
  80. package/scripts/deploy.sh +89 -0
  81. package/scripts/deployment/railway.sh +352 -0
  82. package/scripts/deployment/test-deployment.js +194 -0
  83. package/scripts/detect-and-rehydrate.js +162 -0
  84. package/scripts/detect-and-rehydrate.mjs +165 -0
  85. package/scripts/development/create-demo-tasks.js +143 -0
  86. package/scripts/development/debug-frame-test.js +16 -0
  87. package/scripts/development/demo-auto-sync.js +128 -0
  88. package/scripts/development/fix-all-imports.js +213 -0
  89. package/scripts/development/fix-imports.js +229 -0
  90. package/scripts/development/fix-lint-loop.cjs +103 -0
  91. package/scripts/development/fix-project-id.ts +161 -0
  92. package/scripts/development/fix-strict-mode-issues.ts +291 -0
  93. package/scripts/development/reorganize-structure.sh +228 -0
  94. package/scripts/development/test-persistence-direct.js +148 -0
  95. package/scripts/development/test-persistence.js +114 -0
  96. package/scripts/development/test-tasks.js +93 -0
  97. package/scripts/development/update-imports.js +212 -0
  98. package/scripts/fetch-linear-status.js +125 -0
  99. package/scripts/git-hooks/README.md +310 -0
  100. package/scripts/git-hooks/branch-context-manager.sh +342 -0
  101. package/scripts/git-hooks/post-checkout-stackmemory.sh +63 -0
  102. package/scripts/git-hooks/post-commit-stackmemory.sh +305 -0
  103. package/scripts/git-hooks/pre-commit-stackmemory.sh +275 -0
  104. package/scripts/hooks/cleanup-shell.sh +130 -0
  105. package/scripts/hooks/task-complete.sh +114 -0
  106. package/scripts/initialize.ts +129 -0
  107. package/scripts/install-claude-hooks-auto.js +104 -0
  108. package/scripts/install-claude-hooks.sh +133 -0
  109. package/scripts/install-global.sh +296 -0
  110. package/scripts/install.sh +235 -0
  111. package/scripts/linear-auto-sync.js +262 -0
  112. package/scripts/linear-auto-sync.sh +161 -0
  113. package/scripts/linear-sync-daemon.js +150 -0
  114. package/scripts/linear-task-review.js +237 -0
  115. package/scripts/list-linear-tasks.ts +178 -0
  116. package/scripts/mcp-proxy.js +66 -0
  117. package/scripts/opencode-wrapper.sh +85 -0
  118. package/scripts/publish-local.js +74 -0
  119. package/scripts/query-chromadb.ts +201 -0
  120. package/scripts/railway-env-setup.sh +39 -0
  121. package/scripts/reconcile-local-tasks.js +170 -0
  122. package/scripts/recreate-frames-db.js +89 -0
  123. package/scripts/setup/claude-integration.js +138 -0
  124. package/scripts/setup/configure-alias.js +125 -0
  125. package/scripts/setup/configure-codex-alias.js +161 -0
  126. package/scripts/setup/configure-opencode-alias.js +175 -0
  127. package/scripts/setup-claude-integration.js +204 -0
  128. package/scripts/setup-claude-integration.sh +183 -0
  129. package/scripts/setup.sh +31 -0
  130. package/scripts/show-linear-summary.ts +172 -0
  131. package/scripts/stackmemory-auto-handoff.sh +231 -0
  132. package/scripts/stackmemory-daemon.sh +40 -0
  133. package/scripts/start-linear-sync-daemon.sh +141 -0
  134. package/scripts/start-temporal-paradox.sh +214 -0
  135. package/scripts/status.ts +159 -0
  136. package/scripts/sync-and-clean-tasks.js +258 -0
  137. package/scripts/sync-frames-from-railway.js +228 -0
  138. package/scripts/sync-linear-graphql.js +303 -0
  139. package/scripts/sync-linear-tasks.js +186 -0
  140. package/scripts/test-auto-triggers.sh +57 -0
  141. package/scripts/test-browser-mcp.js +74 -0
  142. package/scripts/test-chromadb-full.js +115 -0
  143. package/scripts/test-chromadb-hooks.sh +28 -0
  144. package/scripts/test-chromadb-sync.ts +245 -0
  145. package/scripts/test-cli-security.js +293 -0
  146. package/scripts/test-hooks-persistence.sh +220 -0
  147. package/scripts/test-installation-scenarios.sh +359 -0
  148. package/scripts/test-installation.sh +224 -0
  149. package/scripts/test-mcp.js +163 -0
  150. package/scripts/test-pre-publish-quick.sh +75 -0
  151. package/scripts/test-quality-gates.sh +263 -0
  152. package/scripts/test-railway-db.js +222 -0
  153. package/scripts/test-redis-storage.ts +490 -0
  154. package/scripts/test-rlm-basic.sh +122 -0
  155. package/scripts/test-rlm-comprehensive.sh +260 -0
  156. package/scripts/test-rlm-e2e.sh +268 -0
  157. package/scripts/test-rlm-simple.js +90 -0
  158. package/scripts/test-rlm.js +110 -0
  159. package/scripts/test-session-handoff.sh +165 -0
  160. package/scripts/test-shell-integration.sh +275 -0
  161. package/scripts/testing/ab-test-runner.ts +508 -0
  162. package/scripts/testing/collect-metrics.ts +457 -0
  163. package/scripts/testing/quick-effectiveness-demo.js +187 -0
  164. package/scripts/testing/real-performance-test.js +422 -0
  165. package/scripts/testing/run-effectiveness-tests.sh +176 -0
  166. package/scripts/testing/scripts/testing/ab-test-runner.js +363 -0
  167. package/scripts/testing/scripts/testing/collect-metrics.js +292 -0
  168. package/scripts/testing/simple-effectiveness-test.js +310 -0
  169. package/scripts/testing/src/core/context/context-bridge.js +253 -0
  170. package/scripts/testing/src/core/context/frame-manager.js +746 -0
  171. package/scripts/testing/src/core/context/shared-context-layer.js +437 -0
  172. package/scripts/testing/src/core/database/database-adapter.js +54 -0
  173. package/scripts/testing/src/core/errors/index.js +291 -0
  174. package/scripts/testing/src/core/errors/recovery.js +268 -0
  175. package/scripts/testing/src/core/monitoring/logger.js +145 -0
  176. package/scripts/testing/src/core/retrieval/context-retriever.js +516 -0
  177. package/scripts/testing/src/core/session/index.js +1 -0
  178. package/scripts/testing/src/core/session/session-manager.js +323 -0
  179. package/scripts/testing/src/core/trace/cli-trace-wrapper.js +140 -0
  180. package/scripts/testing/src/core/trace/db-trace-wrapper.js +251 -0
  181. package/scripts/testing/src/core/trace/debug-trace.js +398 -0
  182. package/scripts/testing/src/core/trace/index.js +120 -0
  183. package/scripts/testing/src/core/trace/linear-api-wrapper.js +204 -0
  184. package/scripts/update-linear-status.js +268 -0
  185. package/scripts/update-linear-tasks-fixed.js +284 -0
  186. package/templates/claude-hooks/hooks.json +5 -0
  187. package/templates/claude-hooks/on-clear.js +56 -0
  188. package/templates/claude-hooks/on-startup.js +56 -0
  189. package/templates/claude-hooks/tool-use-trace.js +67 -0
  190. package/dist/features/tui/components/analytics-panel.js +0 -157
  191. package/dist/features/tui/components/analytics-panel.js.map +0 -7
  192. package/dist/features/tui/components/frame-visualizer.js +0 -377
  193. package/dist/features/tui/components/frame-visualizer.js.map +0 -7
  194. package/dist/features/tui/components/pr-tracker.js +0 -135
  195. package/dist/features/tui/components/pr-tracker.js.map +0 -7
  196. package/dist/features/tui/components/session-monitor.js +0 -299
  197. package/dist/features/tui/components/session-monitor.js.map +0 -7
  198. package/dist/features/tui/components/subagent-fleet.js +0 -395
  199. package/dist/features/tui/components/subagent-fleet.js.map +0 -7
  200. package/dist/features/tui/components/task-board.js +0 -1139
  201. package/dist/features/tui/components/task-board.js.map +0 -7
  202. package/dist/features/tui/index.js +0 -408
  203. package/dist/features/tui/index.js.map +0 -7
  204. package/dist/features/tui/services/data-service.js +0 -641
  205. package/dist/features/tui/services/data-service.js.map +0 -7
  206. package/dist/features/tui/services/linear-task-reader.js +0 -102
  207. package/dist/features/tui/services/linear-task-reader.js.map +0 -7
  208. package/dist/features/tui/services/websocket-client.js +0 -162
  209. package/dist/features/tui/services/websocket-client.js.map +0 -7
  210. package/dist/features/tui/terminal-compat.js +0 -220
  211. package/dist/features/tui/terminal-compat.js.map +0 -7
  212. package/dist/features/tui/types.js +0 -1
  213. package/dist/features/tui/types.js.map +0 -7
@@ -0,0 +1,460 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ChromaDB Hook for Claude
5
+ * Automatically preserves and retrieves context using ChromaDB vector storage
6
+ *
7
+ * This hook runs automatically during Claude operations to:
8
+ * - Store context on saves/clears
9
+ * - Retrieve relevant context on queries
10
+ * - Maintain semantic search history
11
+ */
12
+
13
+ import { CloudClient } from 'chromadb';
14
+ import fs from 'fs';
15
+ import path from 'path';
16
+ import { fileURLToPath } from 'url';
17
+ import dotenv from 'dotenv';
18
+
19
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
20
+
21
+ // Load environment variables
22
+ dotenv.config({
23
+ path: path.join(__dirname, '..', '.env'),
24
+ override: true,
25
+ silent: true
26
+ });
27
+
28
+ class ChromaDBHook {
29
+ constructor() {
30
+ this.config = {
31
+ apiKey: process.env.CHROMADB_API_KEY,
32
+ tenant: process.env.CHROMADB_TENANT,
33
+ database: process.env.CHROMADB_DATABASE || 'stackmemory',
34
+ };
35
+
36
+ this.userId = process.env.USER || 'claude';
37
+ this.teamId = process.env.CHROMADB_TEAM_ID;
38
+ this.sessionId = process.env.CLAUDE_SESSION_ID || `session_${Date.now()}`;
39
+ this.projectName = path.basename(process.cwd());
40
+
41
+ this.client = null;
42
+ this.collection = null;
43
+ this.initialized = false;
44
+
45
+ // Hook context from Claude
46
+ this.hookType = process.argv[2] || 'unknown';
47
+ this.hookData = process.argv[3] ? JSON.parse(process.argv[3]) : {};
48
+ }
49
+
50
+ async initialize() {
51
+ if (this.initialized) return;
52
+
53
+ try {
54
+ if (!this.config.apiKey || !this.config.tenant) {
55
+ this.log('ChromaDB not configured, skipping hook');
56
+ return false;
57
+ }
58
+
59
+ this.client = new CloudClient({
60
+ apiKey: this.config.apiKey,
61
+ tenant: this.config.tenant,
62
+ database: this.config.database,
63
+ });
64
+
65
+ this.collection = await this.client.getOrCreateCollection({
66
+ name: 'claude_contexts',
67
+ metadata: {
68
+ description: 'Claude Code context storage',
69
+ version: '1.0.0',
70
+ },
71
+ });
72
+
73
+ this.initialized = true;
74
+ return true;
75
+ } catch (error) {
76
+ this.logError('Failed to initialize ChromaDB', error);
77
+ return false;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Store context in ChromaDB
83
+ */
84
+ async storeContext(type, content, metadata = {}) {
85
+ if (!await this.initialize()) return;
86
+
87
+ try {
88
+ const contextId = `${type}_${this.sessionId}_${Date.now()}`;
89
+
90
+ await this.collection.add({
91
+ ids: [contextId],
92
+ documents: [content],
93
+ metadatas: [{
94
+ user_id: this.userId,
95
+ team_id: this.teamId,
96
+ session_id: this.sessionId,
97
+ project_name: this.projectName,
98
+ type: type,
99
+ timestamp: Date.now(),
100
+ hook_type: this.hookType,
101
+ ...metadata,
102
+ }],
103
+ });
104
+
105
+ this.log(`Stored ${type} context`);
106
+ } catch (error) {
107
+ this.logError(`Failed to store ${type}`, error);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Query contexts by semantic similarity
113
+ */
114
+ async queryContexts(query, limit = 5) {
115
+ if (!await this.initialize()) return [];
116
+
117
+ try {
118
+ const results = await this.collection.query({
119
+ queryTexts: [query],
120
+ nResults: limit,
121
+ where: {
122
+ user_id: this.userId,
123
+ project_name: this.projectName,
124
+ },
125
+ include: ['documents', 'metadatas', 'distances'],
126
+ });
127
+
128
+ if (results.documents && results.documents[0]) {
129
+ return results.documents[0].map((doc, i) => ({
130
+ content: doc,
131
+ metadata: results.metadatas[0][i],
132
+ distance: results.distances[0][i],
133
+ }));
134
+ }
135
+
136
+ return [];
137
+ } catch (error) {
138
+ this.logError('Failed to query contexts', error);
139
+ return [];
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Get recent contexts
145
+ */
146
+ async getRecentContexts(limit = 10, type = null) {
147
+ if (!await this.initialize()) return [];
148
+
149
+ try {
150
+ const where = {
151
+ user_id: this.userId,
152
+ project_name: this.projectName,
153
+ };
154
+
155
+ if (type) {
156
+ where.type = type;
157
+ }
158
+
159
+ const results = await this.collection.get({
160
+ where: where,
161
+ include: ['documents', 'metadatas'],
162
+ limit: limit * 2, // Get more to sort by timestamp
163
+ });
164
+
165
+ if (results.documents) {
166
+ const contexts = results.documents.map((doc, i) => ({
167
+ content: doc,
168
+ metadata: results.metadatas[i],
169
+ }));
170
+
171
+ // Sort by timestamp and limit
172
+ return contexts
173
+ .sort((a, b) => (b.metadata.timestamp || 0) - (a.metadata.timestamp || 0))
174
+ .slice(0, limit);
175
+ }
176
+
177
+ return [];
178
+ } catch (error) {
179
+ this.logError('Failed to get recent contexts', error);
180
+ return [];
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Handle different hook types
186
+ */
187
+ async handleHook() {
188
+ switch (this.hookType) {
189
+ case 'on-save':
190
+ case 'on-context-save':
191
+ await this.handleSave();
192
+ break;
193
+
194
+ case 'on-clear':
195
+ case 'on-session-end':
196
+ await this.handleClear();
197
+ break;
198
+
199
+ case 'on-query':
200
+ case 'on-search':
201
+ await this.handleQuery();
202
+ break;
203
+
204
+ case 'on-task-complete':
205
+ await this.handleTaskComplete();
206
+ break;
207
+
208
+ case 'on-decision':
209
+ await this.handleDecision();
210
+ break;
211
+
212
+ case 'on-error':
213
+ await this.handleError();
214
+ break;
215
+
216
+ case 'on-file-change':
217
+ await this.handleFileChange();
218
+ break;
219
+
220
+ case 'periodic':
221
+ case 'on-checkpoint':
222
+ await this.handleCheckpoint();
223
+ break;
224
+
225
+ default:
226
+ this.log(`Unknown hook type: ${this.hookType}`);
227
+ }
228
+ }
229
+
230
+ async handleSave() {
231
+ const content = this.hookData.content || this.getCurrentContext();
232
+ await this.storeContext('save', content, {
233
+ files: this.hookData.files,
234
+ tags: this.hookData.tags,
235
+ });
236
+ }
237
+
238
+ async handleClear() {
239
+ // Save context before clear
240
+ const content = this.hookData.content || this.getCurrentContext();
241
+ await this.storeContext('clear', content, {
242
+ reason: this.hookData.reason || 'manual',
243
+ preserved: true,
244
+ });
245
+
246
+ // Generate summary
247
+ const recentContexts = await this.getRecentContexts(20);
248
+ if (recentContexts.length > 0) {
249
+ const summary = this.generateSummary(recentContexts);
250
+ await this.storeContext('summary', summary, {
251
+ contexts_count: recentContexts.length,
252
+ });
253
+ }
254
+ }
255
+
256
+ async handleQuery() {
257
+ const query = this.hookData.query || this.hookData.search;
258
+ if (!query) return;
259
+
260
+ // Find relevant contexts
261
+ const contexts = await this.queryContexts(query, 5);
262
+
263
+ if (contexts.length > 0) {
264
+ // Store the query and results
265
+ await this.storeContext('query', query, {
266
+ results_count: contexts.length,
267
+ top_distance: contexts[0].distance,
268
+ });
269
+
270
+ // Output relevant contexts for Claude
271
+ this.outputContextsForClaude(contexts);
272
+ }
273
+ }
274
+
275
+ async handleTaskComplete() {
276
+ const task = this.hookData.task || {};
277
+ await this.storeContext('task_complete', JSON.stringify(task), {
278
+ task_id: task.id,
279
+ task_title: task.title,
280
+ duration: task.duration,
281
+ });
282
+ }
283
+
284
+ async handleDecision() {
285
+ const decision = this.hookData.decision || this.hookData.content;
286
+ await this.storeContext('decision', decision, {
287
+ importance: this.hookData.importance || 'normal',
288
+ });
289
+ }
290
+
291
+ async handleError() {
292
+ const error = this.hookData.error || this.hookData.message;
293
+ await this.storeContext('error', error, {
294
+ severity: this.hookData.severity || 'error',
295
+ stack: this.hookData.stack,
296
+ });
297
+ }
298
+
299
+ async handleFileChange() {
300
+ const file = this.hookData.file || this.hookData.path;
301
+ await this.storeContext('file_change', `Modified: ${file}`, {
302
+ file: file,
303
+ change_type: this.hookData.type || 'modify',
304
+ });
305
+ }
306
+
307
+ async handleCheckpoint() {
308
+ // Periodic checkpoint
309
+ const content = this.getCurrentContext();
310
+ await this.storeContext('checkpoint', content, {
311
+ automatic: true,
312
+ interval: this.hookData.interval || 900000, // 15 min default
313
+ });
314
+
315
+ // Clean old contexts
316
+ await this.cleanOldContexts();
317
+ }
318
+
319
+ /**
320
+ * Get current context from StackMemory
321
+ */
322
+ getCurrentContext() {
323
+ try {
324
+ // Try to get from StackMemory
325
+ const contextFile = path.join(
326
+ process.env.HOME,
327
+ '.stackmemory',
328
+ 'shared-context',
329
+ 'projects',
330
+ `${this.projectName}.json`
331
+ );
332
+
333
+ if (fs.existsSync(contextFile)) {
334
+ const data = JSON.parse(fs.readFileSync(contextFile, 'utf8'));
335
+ return JSON.stringify(data.contexts || [], null, 2);
336
+ }
337
+
338
+ // Fallback to hook data
339
+ return this.hookData.content || 'No context available';
340
+ } catch (error) {
341
+ return this.hookData.content || 'Context read error';
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Generate summary of contexts
347
+ */
348
+ generateSummary(contexts) {
349
+ const summary = {
350
+ timestamp: new Date().toISOString(),
351
+ project: this.projectName,
352
+ session: this.sessionId,
353
+ contexts_count: contexts.length,
354
+ types: {},
355
+ key_points: [],
356
+ };
357
+
358
+ for (const ctx of contexts) {
359
+ const type = ctx.metadata.type || 'unknown';
360
+ summary.types[type] = (summary.types[type] || 0) + 1;
361
+
362
+ // Extract key points (first line or first 100 chars)
363
+ const point = ctx.content.split('\n')[0].substring(0, 100);
364
+ if (point && !summary.key_points.includes(point)) {
365
+ summary.key_points.push(point);
366
+ }
367
+ }
368
+
369
+ return JSON.stringify(summary, null, 2);
370
+ }
371
+
372
+ /**
373
+ * Output contexts for Claude to use
374
+ */
375
+ outputContextsForClaude(contexts) {
376
+ console.log('\n=== Relevant Context from ChromaDB ===\n');
377
+
378
+ for (const ctx of contexts) {
379
+ console.log(`Type: ${ctx.metadata.type || 'unknown'}`);
380
+ console.log(`Time: ${new Date(ctx.metadata.timestamp).toLocaleString()}`);
381
+ console.log(`Relevance: ${(1 - ctx.distance).toFixed(2)}`);
382
+ console.log('---');
383
+ console.log(ctx.content.substring(0, 500));
384
+ console.log('\n');
385
+ }
386
+
387
+ console.log('=== End of Context ===\n');
388
+ }
389
+
390
+ /**
391
+ * Clean old contexts (retention policy)
392
+ */
393
+ async cleanOldContexts() {
394
+ if (!await this.initialize()) return;
395
+
396
+ try {
397
+ const cutoffTime = Date.now() - (30 * 24 * 60 * 60 * 1000); // 30 days
398
+
399
+ const results = await this.collection.get({
400
+ where: {
401
+ user_id: this.userId,
402
+ timestamp: { $lt: cutoffTime },
403
+ },
404
+ include: ['ids'],
405
+ });
406
+
407
+ if (results.ids && results.ids.length > 0) {
408
+ await this.collection.delete({
409
+ ids: results.ids,
410
+ });
411
+ this.log(`Cleaned ${results.ids.length} old contexts`);
412
+ }
413
+ } catch (error) {
414
+ this.logError('Failed to clean old contexts', error);
415
+ }
416
+ }
417
+
418
+ log(message) {
419
+ const logFile = path.join(
420
+ process.env.HOME,
421
+ '.stackmemory',
422
+ 'logs',
423
+ 'chromadb-hook.log'
424
+ );
425
+
426
+ const timestamp = new Date().toISOString();
427
+ const logMessage = `[${timestamp}] ${message}\n`;
428
+
429
+ try {
430
+ fs.appendFileSync(logFile, logMessage);
431
+ } catch {
432
+ // Silent fail
433
+ }
434
+ }
435
+
436
+ logError(message, error) {
437
+ this.log(`ERROR: ${message} - ${error.message}`);
438
+ }
439
+ }
440
+
441
+ // Main execution
442
+ async function main() {
443
+ const hook = new ChromaDBHook();
444
+
445
+ try {
446
+ await hook.handleHook();
447
+ } catch (error) {
448
+ hook.logError('Hook execution failed', error);
449
+ // Don't fail the parent process
450
+ process.exit(0);
451
+ }
452
+ }
453
+
454
+ // Run if called directly
455
+ if (import.meta.url === `file://${process.argv[1]}`) {
456
+ main().catch(error => {
457
+ console.error('Fatal error:', error);
458
+ process.exit(0); // Don't fail the parent
459
+ });
460
+ }
@@ -0,0 +1,66 @@
1
+ #!/bin/bash
2
+
3
+ # Claude Code wrapper with StackMemory integration
4
+ # Usage: Add alias to ~/.zshrc: alias claude='~/Dev/stackmemory/scripts/claude-code-wrapper.sh'
5
+
6
+ # Check for auto-sync flag
7
+ AUTO_SYNC=false
8
+ SYNC_INTERVAL=5
9
+ for arg in "$@"; do
10
+ case $arg in
11
+ --auto-sync)
12
+ AUTO_SYNC=true
13
+ shift
14
+ ;;
15
+ --sync-interval=*)
16
+ SYNC_INTERVAL="${arg#*=}"
17
+ shift
18
+ ;;
19
+ esac
20
+ done
21
+
22
+ # Start Linear auto-sync in background if requested
23
+ SYNC_PID=""
24
+ if [ "$AUTO_SYNC" = true ] && [ -n "$LINEAR_API_KEY" ]; then
25
+ echo "🔄 Starting Linear auto-sync (${SYNC_INTERVAL}min intervals)..."
26
+ (
27
+ while true; do
28
+ sleep $((SYNC_INTERVAL * 60))
29
+ if [ -d ".stackmemory" ]; then
30
+ stackmemory linear sync --quiet 2>/dev/null || true
31
+ fi
32
+ done
33
+ ) &
34
+ SYNC_PID=$!
35
+ fi
36
+
37
+ cleanup() {
38
+ echo "📝 Saving StackMemory context..."
39
+
40
+ # Kill auto-sync if running
41
+ if [ -n "$SYNC_PID" ] && kill -0 $SYNC_PID 2>/dev/null; then
42
+ echo "🛑 Stopping auto-sync..."
43
+ kill $SYNC_PID 2>/dev/null || true
44
+ fi
45
+
46
+ # Check if in a git repo with stackmemory
47
+ if [ -d ".stackmemory" ] && [ -f "stackmemory.json" ]; then
48
+ # Save current context (without sync)
49
+ stackmemory status 2>/dev/null || true
50
+ echo "✅ StackMemory context saved"
51
+ fi
52
+ }
53
+
54
+ # Set trap for exit signals
55
+ trap cleanup EXIT INT TERM
56
+
57
+ # Run Claude Code (try multiple possible command names)
58
+ if command -v claude-code &> /dev/null; then
59
+ claude-code "$@"
60
+ elif command -v claude &> /dev/null; then
61
+ claude "$@"
62
+ else
63
+ echo "❌ Claude Code not found. Please install it first."
64
+ echo " Visit: https://github.com/anthropics/claude-code"
65
+ exit 1
66
+ fi