@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.
- package/README.md +48 -2
- 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,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);
|