@stackmemoryai/stackmemory 1.2.2 → 1.2.6
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 +10 -6
- package/dist/src/cli/claude-sm.js +33 -4
- package/dist/src/cli/codex-sm-danger.js +4 -1
- package/dist/src/cli/codex-sm.js +6 -8
- package/dist/src/cli/commands/config.js +0 -81
- package/dist/src/cli/commands/context-rehydrate.js +133 -47
- package/dist/src/cli/commands/db.js +35 -8
- package/dist/src/cli/commands/handoff.js +1 -1
- package/dist/src/cli/commands/linear.js +9 -0
- package/dist/src/cli/commands/ralph.js +2 -2
- package/dist/src/cli/commands/setup.js +2 -2
- package/dist/src/cli/commands/signup.js +3 -1
- package/dist/src/cli/commands/skills.js +108 -1
- package/dist/src/cli/commands/storage-tier.js +26 -8
- package/dist/src/cli/index.js +1 -57
- package/dist/src/core/config/feature-flags.js +0 -4
- package/dist/src/core/context/dual-stack-manager.js +10 -3
- package/dist/src/core/context/frame-database.js +32 -0
- package/dist/src/core/context/frame-handoff-manager.js +2 -2
- package/dist/src/core/context/{refactored-frame-manager.js → frame-manager.js} +3 -3
- package/dist/src/core/context/index.js +2 -2
- package/dist/src/core/database/sqlite-adapter.js +161 -1
- package/dist/src/core/digest/frame-digest-integration.js +1 -1
- package/dist/src/core/digest/index.js +1 -1
- package/dist/src/core/execution/parallel-executor.js +5 -1
- package/dist/src/core/projects/project-isolation.js +18 -4
- package/dist/src/core/security/index.js +2 -0
- package/dist/src/core/security/input-sanitizer.js +23 -0
- package/dist/src/core/utils/update-checker.js +10 -6
- package/dist/src/daemon/daemon-config.js +2 -1
- package/dist/src/daemon/services/auto-save-service.js +121 -0
- package/dist/src/daemon/services/maintenance-service.js +76 -1
- package/dist/src/features/sweep/prompt-builder.js +2 -2
- package/dist/src/hooks/daemon.js +0 -8
- package/dist/src/integrations/linear/config.js +3 -1
- package/dist/src/integrations/linear/sync.js +18 -5
- package/dist/src/integrations/linear/webhook.js +0 -16
- package/dist/src/integrations/mcp/handlers/code-execution-handlers.js +33 -7
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +397 -0
- package/dist/src/integrations/mcp/handlers/index.js +55 -9
- package/dist/src/integrations/mcp/handlers/task-handlers.js +55 -12
- package/dist/src/integrations/mcp/handlers/team-handlers.js +211 -0
- package/dist/src/integrations/mcp/handlers/trace-handlers.js +28 -9
- package/dist/src/integrations/mcp/index.js +2 -2
- package/dist/src/integrations/mcp/refactored-server.js +31 -10
- package/dist/src/integrations/mcp/server.js +0 -130
- package/dist/src/integrations/mcp/tool-definitions.js +196 -67
- package/dist/src/integrations/ralph/context/context-budget-manager.js +10 -2
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +54 -22
- package/dist/src/integrations/ralph/learning/pattern-learner.js +59 -24
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +81 -35
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +12 -4
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +32 -9
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +25 -8
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +17 -5
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +73 -22
- package/dist/src/skills/claude-skills.js +37 -103
- package/dist/src/skills/theory-skill.js +191 -0
- package/dist/src/utils/hook-installer.js +16 -0
- package/package.json +6 -6
- package/scripts/install-claude-hooks-auto.js +8 -0
- package/templates/claude-hooks/cord-trace.js +225 -0
- package/templates/claude-hooks/theory-capture.js +100 -0
- package/dist/src/core/config/storage-config.js +0 -114
- package/dist/src/core/storage/chromadb-adapter.js +0 -379
- package/dist/src/hooks/graphiti-hooks.js +0 -253
- package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +0 -458
- package/dist/src/integrations/graphiti/client.js +0 -115
- package/dist/src/integrations/graphiti/config.js +0 -17
- package/dist/src/integrations/graphiti/linear-graphiti-bridge.js +0 -115
- package/dist/src/integrations/graphiti/types.js +0 -4
- package/dist/src/integrations/ralph/coordination/enhanced-coordination.js +0 -409
- package/dist/src/skills/repo-ingestion-skill.js +0 -631
- package/templates/claude-hooks/chromadb-wrapper +0 -21
- /package/dist/src/core/context/{enhanced-rehydration.js → rehydration.js} +0 -0
- /package/dist/src/core/digest/{enhanced-hybrid-digest.js → hybrid-digest.js} +0 -0
- /package/dist/src/core/session/{enhanced-handoff.js → handoff.js} +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cord Trace Hook (PostToolUse)
|
|
5
|
+
*
|
|
6
|
+
* Fires on every cord_* MCP tool call, logging metrics to a JSONL trace file
|
|
7
|
+
* and accumulating session metrics in a per-session state file.
|
|
8
|
+
*
|
|
9
|
+
* Trace dir: ~/.stackmemory/cord-traces/
|
|
10
|
+
* cord-trace-{date}.jsonl — append-only trace log
|
|
11
|
+
* cord-state-{session_id}.json — running session metrics
|
|
12
|
+
*
|
|
13
|
+
* Must complete in <50ms — pure file I/O only.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
const HOME = process.env.HOME || '/tmp';
|
|
20
|
+
const TRACE_DIR = path.join(HOME, '.stackmemory', 'cord-traces');
|
|
21
|
+
const CORD_TOOLS = new Set([
|
|
22
|
+
'cord_spawn',
|
|
23
|
+
'cord_fork',
|
|
24
|
+
'cord_complete',
|
|
25
|
+
'cord_ask',
|
|
26
|
+
'cord_tree',
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
function ensureDir(dir) {
|
|
30
|
+
if (!fs.existsSync(dir)) {
|
|
31
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function safeWriteFile(filePath, content) {
|
|
36
|
+
const tmp = filePath + '.tmp';
|
|
37
|
+
fs.writeFileSync(tmp, content);
|
|
38
|
+
fs.renameSync(tmp, filePath);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Extract the cord tool name from the full MCP tool name.
|
|
43
|
+
* e.g. "mcp__stackmemory-refactored__cord_spawn" -> "cord_spawn"
|
|
44
|
+
*/
|
|
45
|
+
function extractCordTool(toolName) {
|
|
46
|
+
if (!toolName) return null;
|
|
47
|
+
for (const t of CORD_TOOLS) {
|
|
48
|
+
if (toolName.endsWith(t)) return t;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function loadState(sessionId) {
|
|
54
|
+
const stateFile = path.join(TRACE_DIR, `cord-state-${sessionId}.json`);
|
|
55
|
+
try {
|
|
56
|
+
if (fs.existsSync(stateFile)) {
|
|
57
|
+
return JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
// Corrupted state, start fresh
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
session_id: sessionId,
|
|
64
|
+
started: new Date().toISOString(),
|
|
65
|
+
tool_counts: {},
|
|
66
|
+
task_ids: [],
|
|
67
|
+
context_modes: {},
|
|
68
|
+
unblocked_total: 0,
|
|
69
|
+
max_depth: 0,
|
|
70
|
+
completion_rate: 0,
|
|
71
|
+
ask_count: 0,
|
|
72
|
+
completed_count: 0,
|
|
73
|
+
spawn_fork_count: 0,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function saveState(sessionId, state) {
|
|
78
|
+
const stateFile = path.join(TRACE_DIR, `cord-state-${sessionId}.json`);
|
|
79
|
+
safeWriteFile(stateFile, JSON.stringify(state, null, 2));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function appendTrace(entry) {
|
|
83
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
84
|
+
const traceFile = path.join(TRACE_DIR, `cord-trace-${date}.jsonl`);
|
|
85
|
+
fs.appendFileSync(traceFile, JSON.stringify(entry) + '\n');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Safely extract metadata from tool_response.
|
|
90
|
+
* MCP tool responses come as { content: [{ type, text }], metadata: {...} }.
|
|
91
|
+
* The hook receives tool_response which may be the raw MCP result or a string.
|
|
92
|
+
*/
|
|
93
|
+
function extractMetadata(toolResponse) {
|
|
94
|
+
if (!toolResponse) return {};
|
|
95
|
+
if (typeof toolResponse === 'string') {
|
|
96
|
+
try {
|
|
97
|
+
const parsed = JSON.parse(toolResponse);
|
|
98
|
+
return parsed.metadata || parsed;
|
|
99
|
+
} catch {
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (toolResponse.metadata) return toolResponse.metadata;
|
|
104
|
+
return toolResponse;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function updateState(state, cordTool, toolInput, toolResponse) {
|
|
108
|
+
// Increment tool count
|
|
109
|
+
state.tool_counts[cordTool] = (state.tool_counts[cordTool] || 0) + 1;
|
|
110
|
+
|
|
111
|
+
const meta = extractMetadata(toolResponse);
|
|
112
|
+
|
|
113
|
+
switch (cordTool) {
|
|
114
|
+
case 'cord_spawn':
|
|
115
|
+
case 'cord_fork': {
|
|
116
|
+
state.spawn_fork_count++;
|
|
117
|
+
|
|
118
|
+
// Track context mode
|
|
119
|
+
const mode = meta.context_mode || cordTool.replace('cord_', '');
|
|
120
|
+
state.context_modes[mode] = (state.context_modes[mode] || 0) + 1;
|
|
121
|
+
|
|
122
|
+
// Track task ID
|
|
123
|
+
if (meta.task_id && !state.task_ids.includes(meta.task_id)) {
|
|
124
|
+
state.task_ids.push(meta.task_id);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Track max depth
|
|
128
|
+
const depth = meta.depth ?? 0;
|
|
129
|
+
if (depth > state.max_depth) {
|
|
130
|
+
state.max_depth = depth;
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
case 'cord_complete': {
|
|
136
|
+
state.completed_count++;
|
|
137
|
+
|
|
138
|
+
// Count unblocked tasks
|
|
139
|
+
const unblocked = meta.unblocked;
|
|
140
|
+
if (Array.isArray(unblocked)) {
|
|
141
|
+
state.unblocked_total += unblocked.length;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Update completion rate
|
|
145
|
+
if (state.spawn_fork_count > 0) {
|
|
146
|
+
state.completion_rate = +(
|
|
147
|
+
state.completed_count / state.spawn_fork_count
|
|
148
|
+
).toFixed(3);
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
case 'cord_ask': {
|
|
154
|
+
state.ask_count++;
|
|
155
|
+
|
|
156
|
+
// Track task ID
|
|
157
|
+
if (meta.task_id && !state.task_ids.includes(meta.task_id)) {
|
|
158
|
+
state.task_ids.push(meta.task_id);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Track context mode
|
|
162
|
+
state.context_modes['ask'] = (state.context_modes['ask'] || 0) + 1;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
case 'cord_tree':
|
|
167
|
+
// Read-only — no state change
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function buildMetricsSummary(state) {
|
|
173
|
+
return {
|
|
174
|
+
task_count: state.task_ids.length,
|
|
175
|
+
active: state.spawn_fork_count - state.completed_count,
|
|
176
|
+
completed: state.completed_count,
|
|
177
|
+
asks: state.ask_count,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function readInput() {
|
|
182
|
+
let input = '';
|
|
183
|
+
for await (const chunk of process.stdin) {
|
|
184
|
+
input += chunk;
|
|
185
|
+
}
|
|
186
|
+
return JSON.parse(input);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function main() {
|
|
190
|
+
try {
|
|
191
|
+
const input = await readInput();
|
|
192
|
+
const { tool_name, tool_input, tool_response, session_id } = input;
|
|
193
|
+
|
|
194
|
+
const cordTool = extractCordTool(tool_name);
|
|
195
|
+
if (!cordTool) return; // Not a cord tool, shouldn't happen given matcher
|
|
196
|
+
|
|
197
|
+
const sid =
|
|
198
|
+
session_id || process.env.CLAUDE_INSTANCE_ID || String(process.ppid);
|
|
199
|
+
|
|
200
|
+
ensureDir(TRACE_DIR);
|
|
201
|
+
|
|
202
|
+
// Load and update state
|
|
203
|
+
const state = loadState(sid);
|
|
204
|
+
updateState(state, cordTool, tool_input, tool_response);
|
|
205
|
+
|
|
206
|
+
// Build trace entry
|
|
207
|
+
const entry = {
|
|
208
|
+
ts: new Date().toISOString(),
|
|
209
|
+
session_id: sid,
|
|
210
|
+
cwd: input.cwd || process.cwd(),
|
|
211
|
+
tool: cordTool,
|
|
212
|
+
input: tool_input || {},
|
|
213
|
+
response: extractMetadata(tool_response),
|
|
214
|
+
metrics: buildMetricsSummary(state),
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Append trace + save state
|
|
218
|
+
appendTrace(entry);
|
|
219
|
+
saveState(sid, state);
|
|
220
|
+
} catch {
|
|
221
|
+
// Silent fail — never block the agent
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
main();
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Theory Capture Hook (PostToolUse)
|
|
5
|
+
*
|
|
6
|
+
* Detects writes to THEORY.MD and:
|
|
7
|
+
* 1. Caches metadata to .stackmemory/theory-cache.json
|
|
8
|
+
* 2. Fire-and-forget: records a StackMemory frame via CLI
|
|
9
|
+
*
|
|
10
|
+
* Must complete in <50ms -- file I/O only, CLI exec is detached.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const crypto = require('crypto');
|
|
16
|
+
const { spawn } = require('child_process');
|
|
17
|
+
|
|
18
|
+
const WRITE_TOOLS = ['Edit', 'Write', 'MultiEdit'];
|
|
19
|
+
|
|
20
|
+
function ensureDir(dir) {
|
|
21
|
+
if (!fs.existsSync(dir)) {
|
|
22
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function safeWriteFile(filePath, content) {
|
|
27
|
+
const tmp = filePath + '.tmp';
|
|
28
|
+
fs.writeFileSync(tmp, content);
|
|
29
|
+
fs.renameSync(tmp, filePath);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isTheoryPath(filePath) {
|
|
33
|
+
if (!filePath) return false;
|
|
34
|
+
const lower = filePath.toLowerCase();
|
|
35
|
+
return (
|
|
36
|
+
lower.endsWith('/theory.md') ||
|
|
37
|
+
lower.endsWith('\\theory.md') ||
|
|
38
|
+
lower === 'theory.md'
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function readInput() {
|
|
43
|
+
let input = '';
|
|
44
|
+
for await (const chunk of process.stdin) {
|
|
45
|
+
input += chunk;
|
|
46
|
+
}
|
|
47
|
+
return JSON.parse(input);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function main() {
|
|
51
|
+
try {
|
|
52
|
+
const input = await readInput();
|
|
53
|
+
const { tool_name, tool_input } = input;
|
|
54
|
+
|
|
55
|
+
// Only care about file-write tools
|
|
56
|
+
if (!WRITE_TOOLS.includes(tool_name)) return;
|
|
57
|
+
|
|
58
|
+
const fp = tool_input?.file_path || '';
|
|
59
|
+
if (!isTheoryPath(fp)) return;
|
|
60
|
+
|
|
61
|
+
// Read current content
|
|
62
|
+
let content;
|
|
63
|
+
try {
|
|
64
|
+
content = fs.readFileSync(fp, 'utf-8');
|
|
65
|
+
} catch {
|
|
66
|
+
return; // File doesn't exist or unreadable
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Cache metadata for next session start
|
|
70
|
+
const cacheDir = path.join(process.cwd(), '.stackmemory');
|
|
71
|
+
ensureDir(cacheDir);
|
|
72
|
+
|
|
73
|
+
const lineCount = content.split('\n').length;
|
|
74
|
+
safeWriteFile(
|
|
75
|
+
path.join(cacheDir, 'theory-cache.json'),
|
|
76
|
+
JSON.stringify({
|
|
77
|
+
path: fp,
|
|
78
|
+
timestamp: new Date().toISOString(),
|
|
79
|
+
hash: crypto.createHash('md5').update(content).digest('hex'),
|
|
80
|
+
lineCount,
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// Fire-and-forget: record as frame via CLI
|
|
85
|
+
const child = spawn(
|
|
86
|
+
'stackmemory',
|
|
87
|
+
['context', 'add', 'artifact', `THEORY.MD updated (${lineCount} lines)`],
|
|
88
|
+
{
|
|
89
|
+
cwd: process.cwd(),
|
|
90
|
+
stdio: 'ignore',
|
|
91
|
+
detached: true,
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
child.unref();
|
|
95
|
+
} catch {
|
|
96
|
+
// Silent fail -- never block the agent
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
main();
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
-
import { dirname as __pathDirname } from 'path';
|
|
3
|
-
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
-
const __dirname = __pathDirname(__filename);
|
|
5
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
6
|
-
import { join } from "path";
|
|
7
|
-
import { homedir } from "os";
|
|
8
|
-
const DEFAULT_STORAGE_CONFIG = {
|
|
9
|
-
mode: "sqlite",
|
|
10
|
-
chromadb: {
|
|
11
|
-
enabled: false
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
const STACKMEMORY_DIR = join(homedir(), ".stackmemory");
|
|
15
|
-
const CONFIG_FILE = join(STACKMEMORY_DIR, "storage-config.json");
|
|
16
|
-
function loadStorageConfig() {
|
|
17
|
-
try {
|
|
18
|
-
if (existsSync(CONFIG_FILE)) {
|
|
19
|
-
const content = readFileSync(CONFIG_FILE, "utf-8");
|
|
20
|
-
const config = JSON.parse(content);
|
|
21
|
-
return {
|
|
22
|
-
mode: config.mode || DEFAULT_STORAGE_CONFIG.mode,
|
|
23
|
-
chromadb: {
|
|
24
|
-
...DEFAULT_STORAGE_CONFIG.chromadb,
|
|
25
|
-
...config.chromadb
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
} catch (error) {
|
|
30
|
-
console.warn("Failed to load storage config, using defaults:", error);
|
|
31
|
-
}
|
|
32
|
-
return DEFAULT_STORAGE_CONFIG;
|
|
33
|
-
}
|
|
34
|
-
function saveStorageConfig(config) {
|
|
35
|
-
try {
|
|
36
|
-
if (!existsSync(STACKMEMORY_DIR)) {
|
|
37
|
-
mkdirSync(STACKMEMORY_DIR, { recursive: true });
|
|
38
|
-
}
|
|
39
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
|
|
40
|
-
} catch (error) {
|
|
41
|
-
console.error("Failed to save storage config:", error);
|
|
42
|
-
throw error;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function isChromaDBEnabled() {
|
|
46
|
-
const config = loadStorageConfig();
|
|
47
|
-
if (!config.chromadb.enabled) {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
const apiKey = config.chromadb.apiKey || process.env["CHROMADB_API_KEY"];
|
|
51
|
-
if (!apiKey) {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
function getStorageMode() {
|
|
57
|
-
const config = loadStorageConfig();
|
|
58
|
-
if (config.mode === "hybrid" && !isChromaDBEnabled()) {
|
|
59
|
-
return "sqlite";
|
|
60
|
-
}
|
|
61
|
-
return config.mode;
|
|
62
|
-
}
|
|
63
|
-
function getChromaDBConfig() {
|
|
64
|
-
if (!isChromaDBEnabled()) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
const config = loadStorageConfig();
|
|
68
|
-
const apiKey = config.chromadb.apiKey || process.env["CHROMADB_API_KEY"];
|
|
69
|
-
const apiUrl = config.chromadb.apiUrl || process.env["CHROMADB_API_URL"] || "https://api.trychroma.com";
|
|
70
|
-
return {
|
|
71
|
-
enabled: true,
|
|
72
|
-
apiKey,
|
|
73
|
-
apiUrl,
|
|
74
|
-
tenant: config.chromadb.tenant || process.env["CHROMADB_TENANT"] || "default_tenant",
|
|
75
|
-
database: config.chromadb.database || process.env["CHROMADB_DATABASE"] || "default_database"
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
function enableChromaDB(chromaConfig) {
|
|
79
|
-
const config = loadStorageConfig();
|
|
80
|
-
config.mode = "hybrid";
|
|
81
|
-
config.chromadb = {
|
|
82
|
-
enabled: true,
|
|
83
|
-
apiKey: chromaConfig.apiKey,
|
|
84
|
-
apiUrl: chromaConfig.apiUrl || "https://api.trychroma.com",
|
|
85
|
-
tenant: chromaConfig.tenant || "default_tenant",
|
|
86
|
-
database: chromaConfig.database || "default_database"
|
|
87
|
-
};
|
|
88
|
-
saveStorageConfig(config);
|
|
89
|
-
}
|
|
90
|
-
function disableChromaDB() {
|
|
91
|
-
const config = loadStorageConfig();
|
|
92
|
-
config.mode = "sqlite";
|
|
93
|
-
config.chromadb = {
|
|
94
|
-
enabled: false
|
|
95
|
-
};
|
|
96
|
-
saveStorageConfig(config);
|
|
97
|
-
}
|
|
98
|
-
function getStorageModeDescription() {
|
|
99
|
-
const mode = getStorageMode();
|
|
100
|
-
if (mode === "hybrid") {
|
|
101
|
-
return "Hybrid (SQLite + ChromaDB for semantic search and cloud backup)";
|
|
102
|
-
}
|
|
103
|
-
return "SQLite (local storage only, fast, no external dependencies)";
|
|
104
|
-
}
|
|
105
|
-
export {
|
|
106
|
-
disableChromaDB,
|
|
107
|
-
enableChromaDB,
|
|
108
|
-
getChromaDBConfig,
|
|
109
|
-
getStorageMode,
|
|
110
|
-
getStorageModeDescription,
|
|
111
|
-
isChromaDBEnabled,
|
|
112
|
-
loadStorageConfig,
|
|
113
|
-
saveStorageConfig
|
|
114
|
-
};
|