@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,115 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Comprehensive ChromaDB integration test
5
+ */
6
+
7
+ import { ChromaDBContextSaver, TRIGGER_EVENTS } from '../.claude/hooks/chromadb-save-hook.js';
8
+ import chalk from 'chalk';
9
+
10
+ async function testAllEventTypes() {
11
+ const saver = new ChromaDBContextSaver();
12
+
13
+ console.log(chalk.cyan('\n🧪 Testing ChromaDB Context Saving\n'));
14
+
15
+ // Test different event types
16
+ const testCases = [
17
+ {
18
+ event: TRIGGER_EVENTS.TASK_COMPLETE,
19
+ data: {
20
+ task: 'Fix TypeScript and lint errors',
21
+ taskId: 'STA-293',
22
+ duration: 1200000,
23
+ filesChanged: ['src/skills/claude-skills.ts', 'src/cli/index.ts']
24
+ }
25
+ },
26
+ {
27
+ event: TRIGGER_EVENTS.CODE_CHANGE,
28
+ data: {
29
+ files: ['src/core/storage/chromadb-simple.ts'],
30
+ linesAdded: 45,
31
+ linesRemoved: 23
32
+ }
33
+ },
34
+ {
35
+ event: TRIGGER_EVENTS.TEST_RUN,
36
+ data: {
37
+ total: 421,
38
+ passed: 421,
39
+ failed: 0,
40
+ coverage: 87.5
41
+ }
42
+ },
43
+ {
44
+ event: TRIGGER_EVENTS.DECISION_MADE,
45
+ data: {
46
+ decision: 'Use ChromaDB Cloud for context persistence',
47
+ category: 'architecture',
48
+ alternatives: ['Local SQLite', 'Redis', 'PostgreSQL'],
49
+ reasoning: 'Cloud-based solution provides better persistence and sharing'
50
+ }
51
+ },
52
+ {
53
+ event: TRIGGER_EVENTS.PERIODIC_SAVE,
54
+ data: {
55
+ interval: '15m',
56
+ activeFiles: ['scripts/test-chromadb-sync.ts']
57
+ }
58
+ }
59
+ ];
60
+
61
+ let successCount = 0;
62
+ let failCount = 0;
63
+
64
+ for (const testCase of testCases) {
65
+ console.log(chalk.yellow(`\nšŸ“ Testing: ${testCase.event}`));
66
+ console.log(chalk.gray(` Data: ${JSON.stringify(testCase.data).substring(0, 100)}...`));
67
+
68
+ try {
69
+ const result = await saver.saveContext(testCase.event, testCase.data);
70
+ if (result && result.success) {
71
+ console.log(chalk.green(` āœ… Saved successfully: ${result.id}`));
72
+ successCount++;
73
+ } else {
74
+ console.log(chalk.red(` āŒ Save failed: ${result?.error || 'Unknown error'}`));
75
+ failCount++;
76
+ }
77
+ } catch (error) {
78
+ console.log(chalk.red(` āŒ Error: ${error.message}`));
79
+ failCount++;
80
+ }
81
+ }
82
+
83
+ // Load recent contexts
84
+ console.log(chalk.cyan('\nšŸ” Loading recent contexts...'));
85
+ const recentContexts = await saver.loadRecentContext(1); // Last hour
86
+
87
+ console.log(chalk.blue(`\nšŸ“Š Summary:`));
88
+ console.log(` Saved: ${successCount}/${testCases.length}`);
89
+ console.log(` Failed: ${failCount}/${testCases.length}`);
90
+ console.log(` Recent contexts found: ${recentContexts.length}`);
91
+
92
+ // Display recent contexts
93
+ if (recentContexts.length > 0) {
94
+ console.log(chalk.cyan('\nšŸ“š Recent Contexts:'));
95
+ recentContexts.slice(0, 5).forEach((ctx, i) => {
96
+ console.log(`\n ${i + 1}. ${ctx.type || 'unknown'} - ${new Date(ctx.timestamp).toLocaleString()}`);
97
+ console.log(` ${ctx.content?.substring(0, 80)}...`);
98
+ });
99
+ }
100
+
101
+ return successCount === testCases.length;
102
+ }
103
+
104
+ // Run tests
105
+ testAllEventTypes().then(success => {
106
+ if (success) {
107
+ console.log(chalk.green('\nāœ… All tests passed! ChromaDB integration is working.'));
108
+ } else {
109
+ console.log(chalk.yellow('\nāš ļø Some tests failed, but ChromaDB is partially working.'));
110
+ }
111
+ process.exit(success ? 0 : 1);
112
+ }).catch(error => {
113
+ console.error(chalk.red('\nāŒ Test failed:'), error);
114
+ process.exit(1);
115
+ });
@@ -0,0 +1,28 @@
1
+ #!/bin/bash
2
+
3
+ echo "🧪 Testing ChromaDB Hooks"
4
+ echo "========================"
5
+ echo ""
6
+
7
+ # Test save hook
8
+ echo "1. Testing save hook..."
9
+ echo "Test content" | $HOME/.claude/hooks/on-save
10
+ sleep 1
11
+
12
+ # Test query hook
13
+ echo "2. Testing query hook..."
14
+ echo "test query" | $HOME/.claude/hooks/on-query
15
+ sleep 1
16
+
17
+ # Test checkpoint hook
18
+ echo "3. Testing checkpoint hook..."
19
+ $HOME/.claude/hooks/on-checkpoint
20
+ sleep 1
21
+
22
+ # Check logs
23
+ echo ""
24
+ echo "šŸ“„ Recent hook activity:"
25
+ tail -10 "$HOME/.stackmemory/logs/chromadb-hook.log" 2>/dev/null || echo "No logs yet"
26
+
27
+ echo ""
28
+ echo "āœ… Hook test complete"
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env tsx
2
+
3
+ /**
4
+ * Test ChromaDB synchronization and upload local contexts
5
+ */
6
+
7
+ import { ChromaClient } from 'chromadb';
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+ import * as os from 'os';
11
+ import * as readline from 'readline';
12
+ import dotenv from 'dotenv';
13
+
14
+ // Load environment variables
15
+ dotenv.config();
16
+
17
+ async function testChromaDBConnection() {
18
+ const apiKey = process.env.CHROMADB_API_KEY;
19
+ const tenant = process.env.CHROMADB_TENANT;
20
+ const database = process.env.CHROMADB_DATABASE || 'stackmemory';
21
+
22
+ if (!apiKey || !tenant) {
23
+ console.error('āŒ Missing ChromaDB credentials in .env');
24
+ console.log(' CHROMADB_API_KEY:', apiKey ? 'āœ“ Set' : 'āœ— Missing');
25
+ console.log(' CHROMADB_TENANT:', tenant ? 'āœ“ Set' : 'āœ— Missing');
26
+ console.log(' CHROMADB_DATABASE:', database);
27
+ return null;
28
+ }
29
+
30
+ console.log('šŸ”„ Connecting to ChromaDB Cloud...');
31
+ console.log(' Tenant:', tenant);
32
+ console.log(' Database:', database);
33
+
34
+ try {
35
+ // Create ChromaDB client for cloud using new API
36
+ const client = new ChromaClient({
37
+ ssl: true,
38
+ host: 'api.trychroma.com',
39
+ port: 443,
40
+ headers: {
41
+ 'X-Chroma-Token': apiKey
42
+ },
43
+ tenant: tenant,
44
+ database: database
45
+ });
46
+
47
+ // Test connection
48
+ const heartbeat = await client.heartbeat();
49
+ console.log('āœ… ChromaDB connection successful:', heartbeat);
50
+
51
+ return client;
52
+ } catch (error: unknown) {
53
+ console.error('āŒ Failed to connect to ChromaDB:', (error as Error).message);
54
+ return null;
55
+ }
56
+ }
57
+
58
+ async function getOrCreateCollection(client: ChromaClient) {
59
+ const collectionName = 'stackmemory_contexts';
60
+
61
+ try {
62
+ // Try to get existing collection
63
+ const collections = await client.listCollections();
64
+ console.log('šŸ“š Existing collections:', collections.map((c: any) => c.name).join(', ') || 'none');
65
+
66
+ // Get or create the collection
67
+ const collection = await client.getOrCreateCollection({
68
+ name: collectionName,
69
+ metadata: {
70
+ description: 'StackMemory context storage',
71
+ created_by: 'test-chromadb-sync',
72
+ version: '2.0.0'
73
+ }
74
+ });
75
+
76
+ console.log(`āœ… Collection '${collectionName}' ready`);
77
+
78
+ // Get collection count
79
+ const count = await collection.count();
80
+ console.log(` Current entries: ${count}`);
81
+
82
+ return collection;
83
+ } catch (error: unknown) {
84
+ console.error('āŒ Failed to create/get collection:', (error as Error).message);
85
+ return null;
86
+ }
87
+ }
88
+
89
+ async function loadLocalContexts() {
90
+ const storageFile = path.join(os.homedir(), '.stackmemory', 'context-storage', 'contexts.jsonl');
91
+
92
+ if (!fs.existsSync(storageFile)) {
93
+ console.log('āš ļø No local contexts found');
94
+ return [];
95
+ }
96
+
97
+ const contexts: any[] = [];
98
+ const fileStream = fs.createReadStream(storageFile);
99
+ const rl = readline.createInterface({
100
+ input: fileStream,
101
+ crlfDelay: Infinity
102
+ });
103
+
104
+ for await (const line of rl) {
105
+ if (line.trim()) {
106
+ try {
107
+ contexts.push(JSON.parse(line));
108
+ } catch (error) {
109
+ console.warn('Failed to parse line:', line.substring(0, 50));
110
+ }
111
+ }
112
+ }
113
+
114
+ console.log(`šŸ“ Loaded ${contexts.length} local contexts`);
115
+ return contexts;
116
+ }
117
+
118
+ async function syncContextsToChromaDB(collection: any, contexts: any[]) {
119
+ console.log(`\nšŸ”„ Syncing ${contexts.length} contexts to ChromaDB...`);
120
+
121
+ let synced = 0;
122
+ let failed = 0;
123
+
124
+ // Batch upload for efficiency
125
+ const batchSize = 10;
126
+ for (let i = 0; i < contexts.length; i += batchSize) {
127
+ const batch = contexts.slice(i, Math.min(i + batchSize, contexts.length));
128
+
129
+ const ids: string[] = [];
130
+ const documents: string[] = [];
131
+ const metadatas: any[] = [];
132
+
133
+ for (const ctx of batch) {
134
+ ids.push(ctx.id || `ctx_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`);
135
+ documents.push(ctx.content || JSON.stringify(ctx));
136
+
137
+ // Prepare metadata
138
+ const metadata: any = {
139
+ timestamp: ctx.timestamp || ctx.stored_at || new Date().toISOString(),
140
+ type: ctx.type || 'context',
141
+ user_id: ctx.user_id || process.env.USER || 'default',
142
+ project: ctx.project || 'stackmemory'
143
+ };
144
+
145
+ // Add additional metadata if present
146
+ if (ctx.session_id) metadata.session_id = ctx.session_id;
147
+ if (ctx.metadata) {
148
+ Object.entries(ctx.metadata).forEach(([key, value]) => {
149
+ if (value !== undefined && value !== null) {
150
+ metadata[key] = String(value);
151
+ }
152
+ });
153
+ }
154
+
155
+ metadatas.push(metadata);
156
+ }
157
+
158
+ try {
159
+ await collection.upsert({
160
+ ids,
161
+ documents,
162
+ metadatas
163
+ });
164
+ synced += batch.length;
165
+ console.log(` āœ“ Batch ${Math.floor(i / batchSize) + 1}: ${batch.length} contexts synced`);
166
+ } catch (error: unknown) {
167
+ failed += batch.length;
168
+ console.error(` āœ— Batch ${Math.floor(i / batchSize) + 1} failed:`, (error as Error).message);
169
+ }
170
+ }
171
+
172
+ console.log(`\nšŸ“Š Sync complete:`);
173
+ console.log(` āœ… Synced: ${synced}`);
174
+ console.log(` āŒ Failed: ${failed}`);
175
+
176
+ return { synced, failed };
177
+ }
178
+
179
+ async function queryRecentContexts(collection: any) {
180
+ console.log('\nšŸ” Querying recent contexts...');
181
+
182
+ try {
183
+ // Query for recent contexts
184
+ const results = await collection.query({
185
+ queryTexts: ['task complete code change'],
186
+ nResults: 5
187
+ });
188
+
189
+ if (results.ids && results.ids[0].length > 0) {
190
+ console.log(`Found ${results.ids[0].length} relevant contexts:`);
191
+
192
+ for (let i = 0; i < results.ids[0].length; i++) {
193
+ const metadata = results.metadatas[0][i];
194
+ const document = results.documents[0][i];
195
+ console.log(`\nšŸ“„ Context ${i + 1}:`);
196
+ console.log(` ID: ${results.ids[0][i]}`);
197
+ console.log(` Type: ${metadata.type}`);
198
+ console.log(` Timestamp: ${metadata.timestamp}`);
199
+ console.log(` Content: ${document.substring(0, 100)}...`);
200
+ }
201
+ } else {
202
+ console.log('No contexts found');
203
+ }
204
+ } catch (error: unknown) {
205
+ console.error('Failed to query contexts:', (error as Error).message);
206
+ }
207
+ }
208
+
209
+ async function main() {
210
+ console.log('šŸš€ ChromaDB Sync Test\n');
211
+
212
+ // Connect to ChromaDB
213
+ const client = await testChromaDBConnection();
214
+ if (!client) {
215
+ console.error('\nāŒ Cannot proceed without ChromaDB connection');
216
+ console.log('\nšŸ“ To fix:');
217
+ console.log('1. Ensure you have a ChromaDB Cloud account');
218
+ console.log('2. Add credentials to .env:');
219
+ console.log(' CHROMADB_API_KEY=your-api-key');
220
+ console.log(' CHROMADB_TENANT=your-tenant-id');
221
+ console.log(' CHROMADB_DATABASE=stackmemory');
222
+ process.exit(1);
223
+ }
224
+
225
+ // Get or create collection
226
+ const collection = await getOrCreateCollection(client);
227
+ if (!collection) {
228
+ process.exit(1);
229
+ }
230
+
231
+ // Load local contexts
232
+ const contexts = await loadLocalContexts();
233
+
234
+ if (contexts.length > 0) {
235
+ // Sync to ChromaDB
236
+ await syncContextsToChromaDB(collection, contexts);
237
+ }
238
+
239
+ // Query recent contexts
240
+ await queryRecentContexts(collection);
241
+
242
+ console.log('\nāœ… Test complete!');
243
+ }
244
+
245
+ main().catch(console.error);
@@ -0,0 +1,293 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Security Testing Script for StackMemory CLI/API
5
+ * Tests input validation and security vulnerabilities
6
+ */
7
+
8
+ import { spawn, execSync } from 'child_process';
9
+ import { existsSync, mkdirSync, rmSync } from 'fs';
10
+ import { join } from 'path';
11
+
12
+ const TEST_DIR = '/tmp/stackmemory-security-test';
13
+ const TESTS_PASSED = [];
14
+ const TESTS_FAILED = [];
15
+
16
+ // Color output helpers
17
+ const red = (str) => `\x1b[31m${str}\x1b[0m`;
18
+ const green = (str) => `\x1b[32m${str}\x1b[0m`;
19
+ const yellow = (str) => `\x1b[33m${str}\x1b[0m`;
20
+ const blue = (str) => `\x1b[34m${str}\x1b[0m`;
21
+
22
+ // Test runner
23
+ async function runTest(name, testFn) {
24
+ process.stdout.write(`Testing ${name}... `);
25
+ try {
26
+ await testFn();
27
+ TESTS_PASSED.push(name);
28
+ console.log(green('āœ“ PASSED'));
29
+ return true;
30
+ } catch (error) {
31
+ TESTS_FAILED.push({ name, error: error.message });
32
+ console.log(red('āœ— FAILED'));
33
+ console.log(` ${yellow(error.message)}`);
34
+ return false;
35
+ }
36
+ }
37
+
38
+ // Execute CLI command safely
39
+ function execCLI(args, options = {}) {
40
+ try {
41
+ const result = execSync(`npx stackmemory ${args}`, {
42
+ cwd: TEST_DIR,
43
+ encoding: 'utf8',
44
+ stdio: 'pipe',
45
+ ...options
46
+ });
47
+ return { success: true, output: result };
48
+ } catch (error) {
49
+ return {
50
+ success: false,
51
+ output: error.stdout || '',
52
+ error: error.stderr || error.message
53
+ };
54
+ }
55
+ }
56
+
57
+ // Setup test environment
58
+ function setupTestEnv() {
59
+ if (existsSync(TEST_DIR)) {
60
+ rmSync(TEST_DIR, { recursive: true, force: true });
61
+ }
62
+ mkdirSync(TEST_DIR, { recursive: true });
63
+ process.chdir(TEST_DIR);
64
+
65
+ // Initialize StackMemory
66
+ execSync('npx stackmemory init', { cwd: TEST_DIR, stdio: 'ignore' });
67
+ }
68
+
69
+ // Security Tests
70
+ const securityTests = {
71
+ // SQL Injection Tests
72
+ async testSQLInjection() {
73
+ const payloads = [
74
+ "'; DROP TABLE frames; --",
75
+ "1' OR '1'='1",
76
+ "1; DELETE FROM tasks WHERE 1=1; --",
77
+ "' UNION SELECT * FROM frames --",
78
+ "1' AND (SELECT * FROM (SELECT(SLEEP(5)))a)--"
79
+ ];
80
+
81
+ for (const payload of payloads) {
82
+ const result = execCLI(`search "${payload}"`);
83
+ if (!result.success && result.error?.includes('DROP TABLE')) {
84
+ throw new Error(`SQL injection vulnerability detected with payload: ${payload}`);
85
+ }
86
+ }
87
+ },
88
+
89
+ // Command Injection Tests
90
+ async testCommandInjection() {
91
+ const payloads = [
92
+ '$(whoami)',
93
+ '`id`',
94
+ '; ls -la',
95
+ '| cat /etc/passwd',
96
+ '&& rm -rf /',
97
+ '\n/bin/sh',
98
+ '"; cat ~/.ssh/id_rsa; echo "'
99
+ ];
100
+
101
+ for (const payload of payloads) {
102
+ const result = execCLI(`linear update "${payload}"`);
103
+ // Check if command was executed
104
+ if (result.output?.includes('root') || result.output?.includes('uid=')) {
105
+ throw new Error(`Command injection vulnerability with payload: ${payload}`);
106
+ }
107
+ }
108
+ },
109
+
110
+ // Path Traversal Tests
111
+ async testPathTraversal() {
112
+ const payloads = [
113
+ '../../../etc/passwd',
114
+ '..\\..\\..\\windows\\system32\\config\\sam',
115
+ 'file:///etc/passwd',
116
+ '....//....//....//etc/passwd',
117
+ '%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd'
118
+ ];
119
+
120
+ for (const payload of payloads) {
121
+ // Test with project commands
122
+ const result = execCLI(`projects add "${payload}"`);
123
+ if (result.output?.includes('root:') || result.output?.includes('Administrator:')) {
124
+ throw new Error(`Path traversal vulnerability with payload: ${payload}`);
125
+ }
126
+ }
127
+ },
128
+
129
+ // Invalid Input Handling
130
+ async testInvalidInputs() {
131
+ const tests = [
132
+ { cmd: 'linear sync --direction invalid', shouldFail: true },
133
+ { cmd: 'linear config --set-interval -1', shouldFail: true },
134
+ { cmd: 'linear config --set-quiet-start 25', shouldFail: true },
135
+ { cmd: 'analytics --port abc', shouldFail: true },
136
+ { cmd: 'search --limit notanumber', shouldFail: true }
137
+ ];
138
+
139
+ for (const test of tests) {
140
+ const result = execCLI(test.cmd);
141
+ if (test.shouldFail && result.success) {
142
+ throw new Error(`Invalid input accepted: ${test.cmd}`);
143
+ }
144
+ }
145
+ },
146
+
147
+ // Buffer Overflow Tests
148
+ async testBufferOverflow() {
149
+ const largeString = 'A'.repeat(1000000); // 1MB string
150
+ const veryLargeString = 'B'.repeat(10000000); // 10MB string
151
+
152
+ // Test with large inputs
153
+ const result1 = execCLI(`search "${largeString}"`);
154
+ const result2 = execCLI(`linear create --title "${veryLargeString}"`);
155
+
156
+ // Should handle gracefully without crashing
157
+ if (result1.error?.includes('Segmentation fault') ||
158
+ result2.error?.includes('Segmentation fault')) {
159
+ throw new Error('Buffer overflow vulnerability detected');
160
+ }
161
+ },
162
+
163
+ // YAML/JSON Injection
164
+ async testYAMLInjection() {
165
+ const payloads = [
166
+ '!!python/object/apply:os.system ["ls"]',
167
+ '{"__proto__": {"isAdmin": true}}',
168
+ '{"constructor": {"prototype": {"isAdmin": true}}}',
169
+ '{ "$gt": "" }',
170
+ '{"$where": "sleep(1000)"}'
171
+ ];
172
+
173
+ for (const payload of payloads) {
174
+ const result = execCLI(`config --import '${payload}'`);
175
+ if (result.output?.includes('isAdmin') ||
176
+ result.output?.includes('bin') ||
177
+ result.output?.includes('etc')) {
178
+ throw new Error(`YAML/JSON injection vulnerability with payload: ${payload}`);
179
+ }
180
+ }
181
+ },
182
+
183
+ // Environment Variable Injection
184
+ async testEnvVarInjection() {
185
+ const payloads = [
186
+ '$HOME',
187
+ '${PATH}',
188
+ '$(printenv)',
189
+ '%USERPROFILE%'
190
+ ];
191
+
192
+ for (const payload of payloads) {
193
+ const result = execCLI(`linear setup --client-id "${payload}"`);
194
+ if (result.output?.includes('/home/') ||
195
+ result.output?.includes('/usr/') ||
196
+ result.output?.includes('Users')) {
197
+ throw new Error(`Environment variable injection with payload: ${payload}`);
198
+ }
199
+ }
200
+ },
201
+
202
+ // Integer Overflow Tests
203
+ async testIntegerOverflow() {
204
+ const tests = [
205
+ { cmd: 'search --limit 999999999999999999999', field: 'limit' },
206
+ { cmd: 'analytics --port 999999999', field: 'port' },
207
+ { cmd: 'linear config --set-interval 2147483648', field: 'interval' }
208
+ ];
209
+
210
+ for (const test of tests) {
211
+ const result = execCLI(test.cmd);
212
+ // Should validate numeric ranges
213
+ if (!result.error && result.success) {
214
+ throw new Error(`Integer overflow not handled for ${test.field}`);
215
+ }
216
+ }
217
+ },
218
+
219
+ // XSS in Terminal Output
220
+ async testXSSInTerminal() {
221
+ const payloads = [
222
+ '\x1b[31mRED\x1b[0m\x1b]0;HIJACKED\x07',
223
+ '<script>alert(1)</script>',
224
+ '\r\nInjected line',
225
+ '\x1b[2J\x1b[H' // Clear screen escape
226
+ ];
227
+
228
+ for (const payload of payloads) {
229
+ const result = execCLI(`search "${payload}"`);
230
+ // Output should be sanitized
231
+ if (result.output?.includes('\x1b]0;') ||
232
+ result.output?.includes('\x1b[2J')) {
233
+ throw new Error('Terminal escape sequences not sanitized');
234
+ }
235
+ }
236
+ },
237
+
238
+ // Authentication Bypass Tests
239
+ async testAuthBypass() {
240
+ // Test accessing Linear commands without auth
241
+ const commands = [
242
+ 'linear sync',
243
+ 'linear list',
244
+ 'linear update TEST-123'
245
+ ];
246
+
247
+ // Unset LINEAR_API_KEY for test
248
+ delete process.env.LINEAR_API_KEY;
249
+
250
+ for (const cmd of commands) {
251
+ const result = execCLI(cmd);
252
+ // Should require authentication
253
+ if (result.success && !result.output?.includes('not configured')) {
254
+ throw new Error(`Authentication bypass for: ${cmd}`);
255
+ }
256
+ }
257
+ }
258
+ };
259
+
260
+ // Main test runner
261
+ async function main() {
262
+ console.log(blue('\nšŸ”’ StackMemory CLI/API Security Testing\n'));
263
+ console.log('Setting up test environment...\n');
264
+
265
+ setupTestEnv();
266
+
267
+ // Run all security tests
268
+ for (const [name, testFn] of Object.entries(securityTests)) {
269
+ await runTest(name, testFn);
270
+ }
271
+
272
+ // Summary
273
+ console.log(blue('\nšŸ“Š Test Summary\n'));
274
+ console.log(green(`āœ“ Passed: ${TESTS_PASSED.length}`));
275
+ console.log(red(`āœ— Failed: ${TESTS_FAILED.length}`));
276
+
277
+ if (TESTS_FAILED.length > 0) {
278
+ console.log(red('\nāŒ Failed Tests:'));
279
+ TESTS_FAILED.forEach(({ name, error }) => {
280
+ console.log(` - ${name}: ${error}`);
281
+ });
282
+ }
283
+
284
+ // Cleanup
285
+ if (existsSync(TEST_DIR)) {
286
+ rmSync(TEST_DIR, { recursive: true, force: true });
287
+ }
288
+
289
+ process.exit(TESTS_FAILED.length > 0 ? 1 : 0);
290
+ }
291
+
292
+ // Run tests
293
+ main().catch(console.error);