@stackmemoryai/stackmemory 0.5.38 → 0.5.40
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/dist/cli/commands/setup.js +327 -0
- package/dist/cli/commands/setup.js.map +7 -0
- package/dist/cli/index.js +41 -35
- package/dist/cli/index.js.map +2 -2
- package/dist/core/utils/async-mutex.js +114 -0
- package/dist/core/utils/async-mutex.js.map +7 -0
- package/dist/hooks/secure-fs.js +24 -4
- package/dist/hooks/secure-fs.js.map +2 -2
- package/dist/hooks/sms-action-runner.js.map +2 -2
- package/dist/hooks/sms-webhook.js +52 -4
- package/dist/hooks/sms-webhook.js.map +2 -2
- package/dist/integrations/linear/sync-manager.js +24 -25
- package/dist/integrations/linear/sync-manager.js.map +2 -2
- package/package.json +1 -1
- package/scripts/install-claude-hooks-auto.js +74 -24
|
@@ -0,0 +1,327 @@
|
|
|
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 { Command } from "commander";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
import { execSync } from "child_process";
|
|
11
|
+
const CLAUDE_DIR = join(homedir(), ".claude");
|
|
12
|
+
const CLAUDE_CONFIG_FILE = join(CLAUDE_DIR, "config.json");
|
|
13
|
+
const MCP_CONFIG_FILE = join(CLAUDE_DIR, "stackmemory-mcp.json");
|
|
14
|
+
const HOOKS_JSON = join(CLAUDE_DIR, "hooks.json");
|
|
15
|
+
function createSetupMCPCommand() {
|
|
16
|
+
return new Command("setup-mcp").description("Auto-configure Claude Code MCP integration").option("--dry-run", "Show what would be configured without making changes").option("--reset", "Reset MCP configuration to defaults").action(async (options) => {
|
|
17
|
+
console.log(chalk.cyan("\nStackMemory MCP Setup\n"));
|
|
18
|
+
if (options.dryRun) {
|
|
19
|
+
console.log(chalk.yellow("[DRY RUN] No changes will be made.\n"));
|
|
20
|
+
}
|
|
21
|
+
if (!existsSync(CLAUDE_DIR)) {
|
|
22
|
+
if (options.dryRun) {
|
|
23
|
+
console.log(chalk.gray(`Would create: ${CLAUDE_DIR}`));
|
|
24
|
+
} else {
|
|
25
|
+
mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
26
|
+
console.log(chalk.green("[OK]") + " Created ~/.claude directory");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const mcpConfig = {
|
|
30
|
+
mcpServers: {
|
|
31
|
+
stackmemory: {
|
|
32
|
+
command: "stackmemory",
|
|
33
|
+
args: ["mcp-server"],
|
|
34
|
+
env: {
|
|
35
|
+
NODE_ENV: "production"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
if (options.dryRun) {
|
|
41
|
+
console.log(
|
|
42
|
+
chalk.gray(`Would write MCP config to: ${MCP_CONFIG_FILE}`)
|
|
43
|
+
);
|
|
44
|
+
console.log(chalk.gray(JSON.stringify(mcpConfig, null, 2)));
|
|
45
|
+
} else {
|
|
46
|
+
writeFileSync(MCP_CONFIG_FILE, JSON.stringify(mcpConfig, null, 2));
|
|
47
|
+
console.log(chalk.green("[OK]") + " Created MCP server configuration");
|
|
48
|
+
}
|
|
49
|
+
let claudeConfig = {};
|
|
50
|
+
if (existsSync(CLAUDE_CONFIG_FILE)) {
|
|
51
|
+
try {
|
|
52
|
+
claudeConfig = JSON.parse(readFileSync(CLAUDE_CONFIG_FILE, "utf8"));
|
|
53
|
+
} catch {
|
|
54
|
+
console.log(
|
|
55
|
+
chalk.yellow("[WARN]") + " Could not parse existing config.json, creating new"
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (!claudeConfig.mcp) {
|
|
60
|
+
claudeConfig.mcp = {};
|
|
61
|
+
}
|
|
62
|
+
const mcp = claudeConfig.mcp;
|
|
63
|
+
if (!mcp.configFiles) {
|
|
64
|
+
mcp.configFiles = [];
|
|
65
|
+
}
|
|
66
|
+
const configFiles = mcp.configFiles;
|
|
67
|
+
if (!configFiles.includes(MCP_CONFIG_FILE)) {
|
|
68
|
+
configFiles.push(MCP_CONFIG_FILE);
|
|
69
|
+
}
|
|
70
|
+
if (options.dryRun) {
|
|
71
|
+
console.log(chalk.gray(`Would update: ${CLAUDE_CONFIG_FILE}`));
|
|
72
|
+
} else {
|
|
73
|
+
writeFileSync(
|
|
74
|
+
CLAUDE_CONFIG_FILE,
|
|
75
|
+
JSON.stringify(claudeConfig, null, 2)
|
|
76
|
+
);
|
|
77
|
+
console.log(chalk.green("[OK]") + " Updated Claude config.json");
|
|
78
|
+
}
|
|
79
|
+
console.log(chalk.cyan("\nValidating configuration..."));
|
|
80
|
+
try {
|
|
81
|
+
execSync("stackmemory --version", { stdio: "pipe" });
|
|
82
|
+
console.log(chalk.green("[OK]") + " stackmemory CLI is installed");
|
|
83
|
+
} catch {
|
|
84
|
+
console.log(chalk.yellow("[WARN]") + " stackmemory CLI not in PATH");
|
|
85
|
+
console.log(chalk.gray(" You may need to restart your terminal"));
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
execSync("claude --version", { stdio: "pipe" });
|
|
89
|
+
console.log(chalk.green("[OK]") + " Claude Code is installed");
|
|
90
|
+
} catch {
|
|
91
|
+
console.log(chalk.yellow("[WARN]") + " Claude Code not found");
|
|
92
|
+
console.log(chalk.gray(" Install from: https://claude.ai/code"));
|
|
93
|
+
}
|
|
94
|
+
if (!options.dryRun) {
|
|
95
|
+
console.log(chalk.green("\nMCP setup complete!"));
|
|
96
|
+
console.log(chalk.cyan("\nNext steps:"));
|
|
97
|
+
console.log(chalk.white(" 1. Restart Claude Code"));
|
|
98
|
+
console.log(
|
|
99
|
+
chalk.white(
|
|
100
|
+
" 2. The StackMemory MCP tools will be available automatically"
|
|
101
|
+
)
|
|
102
|
+
);
|
|
103
|
+
console.log(
|
|
104
|
+
chalk.gray(
|
|
105
|
+
'\nTo verify: Run "stackmemory doctor" to check all integrations'
|
|
106
|
+
)
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function createDoctorCommand() {
|
|
112
|
+
return new Command("doctor").description("Diagnose StackMemory configuration and common issues").option("--fix", "Attempt to automatically fix issues").action(async (options) => {
|
|
113
|
+
console.log(chalk.cyan("\nStackMemory Doctor\n"));
|
|
114
|
+
console.log(chalk.gray("Checking configuration and dependencies...\n"));
|
|
115
|
+
const results = [];
|
|
116
|
+
const projectDir = join(process.cwd(), ".stackmemory");
|
|
117
|
+
const dbPath = join(projectDir, "context.db");
|
|
118
|
+
if (existsSync(dbPath)) {
|
|
119
|
+
results.push({
|
|
120
|
+
name: "Project Initialization",
|
|
121
|
+
status: "ok",
|
|
122
|
+
message: "StackMemory is initialized in this project"
|
|
123
|
+
});
|
|
124
|
+
} else if (existsSync(projectDir)) {
|
|
125
|
+
results.push({
|
|
126
|
+
name: "Project Initialization",
|
|
127
|
+
status: "warn",
|
|
128
|
+
message: ".stackmemory directory exists but database not found",
|
|
129
|
+
fix: "Run: stackmemory init"
|
|
130
|
+
});
|
|
131
|
+
} else {
|
|
132
|
+
results.push({
|
|
133
|
+
name: "Project Initialization",
|
|
134
|
+
status: "error",
|
|
135
|
+
message: "StackMemory not initialized in this project",
|
|
136
|
+
fix: "Run: stackmemory init"
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
if (existsSync(dbPath)) {
|
|
140
|
+
try {
|
|
141
|
+
const Database = (await import("better-sqlite3")).default;
|
|
142
|
+
const db = new Database(dbPath, { readonly: true });
|
|
143
|
+
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
|
|
144
|
+
db.close();
|
|
145
|
+
const hasFrames = tables.some((t) => t.name === "frames");
|
|
146
|
+
if (hasFrames) {
|
|
147
|
+
results.push({
|
|
148
|
+
name: "Database Integrity",
|
|
149
|
+
status: "ok",
|
|
150
|
+
message: `Database has ${tables.length} tables`
|
|
151
|
+
});
|
|
152
|
+
} else {
|
|
153
|
+
results.push({
|
|
154
|
+
name: "Database Integrity",
|
|
155
|
+
status: "warn",
|
|
156
|
+
message: "Database exists but missing expected tables",
|
|
157
|
+
fix: "Run: stackmemory init --interactive"
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
} catch (error) {
|
|
161
|
+
results.push({
|
|
162
|
+
name: "Database Integrity",
|
|
163
|
+
status: "error",
|
|
164
|
+
message: `Database error: ${error.message}`,
|
|
165
|
+
fix: "Remove .stackmemory/context.db and run: stackmemory init"
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (existsSync(MCP_CONFIG_FILE)) {
|
|
170
|
+
try {
|
|
171
|
+
const config = JSON.parse(readFileSync(MCP_CONFIG_FILE, "utf8"));
|
|
172
|
+
if (config.mcpServers?.stackmemory) {
|
|
173
|
+
results.push({
|
|
174
|
+
name: "MCP Configuration",
|
|
175
|
+
status: "ok",
|
|
176
|
+
message: "MCP server configured"
|
|
177
|
+
});
|
|
178
|
+
} else {
|
|
179
|
+
results.push({
|
|
180
|
+
name: "MCP Configuration",
|
|
181
|
+
status: "warn",
|
|
182
|
+
message: "MCP config file exists but stackmemory server not configured",
|
|
183
|
+
fix: "Run: stackmemory setup-mcp"
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
} catch {
|
|
187
|
+
results.push({
|
|
188
|
+
name: "MCP Configuration",
|
|
189
|
+
status: "error",
|
|
190
|
+
message: "Invalid MCP configuration file",
|
|
191
|
+
fix: "Run: stackmemory setup-mcp --reset"
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
results.push({
|
|
196
|
+
name: "MCP Configuration",
|
|
197
|
+
status: "warn",
|
|
198
|
+
message: "MCP not configured for Claude Code",
|
|
199
|
+
fix: "Run: stackmemory setup-mcp"
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (existsSync(HOOKS_JSON)) {
|
|
203
|
+
try {
|
|
204
|
+
const hooks = JSON.parse(readFileSync(HOOKS_JSON, "utf8"));
|
|
205
|
+
const hasTraceHook = !!hooks["tool-use-approval"];
|
|
206
|
+
if (hasTraceHook) {
|
|
207
|
+
results.push({
|
|
208
|
+
name: "Claude Hooks",
|
|
209
|
+
status: "ok",
|
|
210
|
+
message: "Tool tracing hook installed"
|
|
211
|
+
});
|
|
212
|
+
} else {
|
|
213
|
+
results.push({
|
|
214
|
+
name: "Claude Hooks",
|
|
215
|
+
status: "warn",
|
|
216
|
+
message: "Hooks file exists but tracing not configured",
|
|
217
|
+
fix: "Run: stackmemory hooks install"
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
} catch {
|
|
221
|
+
results.push({
|
|
222
|
+
name: "Claude Hooks",
|
|
223
|
+
status: "warn",
|
|
224
|
+
message: "Could not read hooks.json"
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
results.push({
|
|
229
|
+
name: "Claude Hooks",
|
|
230
|
+
status: "warn",
|
|
231
|
+
message: "Claude hooks not installed (optional)",
|
|
232
|
+
fix: "Run: stackmemory hooks install"
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
const envChecks = [
|
|
236
|
+
{ key: "LINEAR_API_KEY", name: "Linear API Key", optional: true },
|
|
237
|
+
{ key: "TWILIO_ACCOUNT_SID", name: "Twilio Account", optional: true }
|
|
238
|
+
];
|
|
239
|
+
for (const check of envChecks) {
|
|
240
|
+
const value = process.env[check.key];
|
|
241
|
+
if (value) {
|
|
242
|
+
results.push({
|
|
243
|
+
name: check.name,
|
|
244
|
+
status: "ok",
|
|
245
|
+
message: "Environment variable set"
|
|
246
|
+
});
|
|
247
|
+
} else if (!check.optional) {
|
|
248
|
+
results.push({
|
|
249
|
+
name: check.name,
|
|
250
|
+
status: "error",
|
|
251
|
+
message: "Required environment variable not set",
|
|
252
|
+
fix: `Set ${check.key} in your .env file`
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const homeStackmemory = join(homedir(), ".stackmemory");
|
|
257
|
+
if (existsSync(homeStackmemory)) {
|
|
258
|
+
try {
|
|
259
|
+
const testFile = join(homeStackmemory, ".write-test");
|
|
260
|
+
writeFileSync(testFile, "test");
|
|
261
|
+
const { unlinkSync } = await import("fs");
|
|
262
|
+
unlinkSync(testFile);
|
|
263
|
+
results.push({
|
|
264
|
+
name: "File Permissions",
|
|
265
|
+
status: "ok",
|
|
266
|
+
message: "~/.stackmemory is writable"
|
|
267
|
+
});
|
|
268
|
+
} catch {
|
|
269
|
+
results.push({
|
|
270
|
+
name: "File Permissions",
|
|
271
|
+
status: "error",
|
|
272
|
+
message: "~/.stackmemory is not writable",
|
|
273
|
+
fix: "Run: chmod 700 ~/.stackmemory"
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
let hasErrors = false;
|
|
278
|
+
let hasWarnings = false;
|
|
279
|
+
for (const result of results) {
|
|
280
|
+
const icon = result.status === "ok" ? chalk.green("[OK]") : result.status === "warn" ? chalk.yellow("[WARN]") : chalk.red("[ERROR]");
|
|
281
|
+
console.log(`${icon} ${result.name}`);
|
|
282
|
+
console.log(chalk.gray(` ${result.message}`));
|
|
283
|
+
if (result.fix) {
|
|
284
|
+
console.log(chalk.cyan(` Fix: ${result.fix}`));
|
|
285
|
+
if (options.fix && result.status !== "ok") {
|
|
286
|
+
if (result.fix.includes("stackmemory setup-mcp")) {
|
|
287
|
+
console.log(chalk.gray(" Attempting auto-fix..."));
|
|
288
|
+
try {
|
|
289
|
+
execSync("stackmemory setup-mcp", { stdio: "inherit" });
|
|
290
|
+
} catch {
|
|
291
|
+
console.log(chalk.red(" Auto-fix failed"));
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (result.status === "error") hasErrors = true;
|
|
297
|
+
if (result.status === "warn") hasWarnings = true;
|
|
298
|
+
}
|
|
299
|
+
console.log("");
|
|
300
|
+
if (hasErrors) {
|
|
301
|
+
console.log(
|
|
302
|
+
chalk.red("Some issues need attention. Run suggested fixes above.")
|
|
303
|
+
);
|
|
304
|
+
process.exit(1);
|
|
305
|
+
} else if (hasWarnings) {
|
|
306
|
+
console.log(
|
|
307
|
+
chalk.yellow(
|
|
308
|
+
"StackMemory is working but some optional features are not configured."
|
|
309
|
+
)
|
|
310
|
+
);
|
|
311
|
+
} else {
|
|
312
|
+
console.log(
|
|
313
|
+
chalk.green("All checks passed! StackMemory is properly configured.")
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
function registerSetupCommands(program) {
|
|
319
|
+
program.addCommand(createSetupMCPCommand());
|
|
320
|
+
program.addCommand(createDoctorCommand());
|
|
321
|
+
}
|
|
322
|
+
export {
|
|
323
|
+
createDoctorCommand,
|
|
324
|
+
createSetupMCPCommand,
|
|
325
|
+
registerSetupCommands
|
|
326
|
+
};
|
|
327
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/cli/commands/setup.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Setup commands for StackMemory onboarding\n * - setup-mcp: Auto-configure Claude Code MCP integration\n * - doctor: Diagnose common issues\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { execSync } from 'child_process';\n\n// Claude config paths\nconst CLAUDE_DIR = join(homedir(), '.claude');\nconst CLAUDE_CONFIG_FILE = join(CLAUDE_DIR, 'config.json');\nconst MCP_CONFIG_FILE = join(CLAUDE_DIR, 'stackmemory-mcp.json');\nconst HOOKS_JSON = join(CLAUDE_DIR, 'hooks.json');\n\ninterface DiagnosticResult {\n name: string;\n status: 'ok' | 'warn' | 'error';\n message: string;\n fix?: string;\n}\n\n/**\n * Create setup-mcp command\n */\nexport function createSetupMCPCommand(): Command {\n return new Command('setup-mcp')\n .description('Auto-configure Claude Code MCP integration')\n .option('--dry-run', 'Show what would be configured without making changes')\n .option('--reset', 'Reset MCP configuration to defaults')\n .action(async (options) => {\n console.log(chalk.cyan('\\nStackMemory MCP Setup\\n'));\n\n if (options.dryRun) {\n console.log(chalk.yellow('[DRY RUN] No changes will be made.\\n'));\n }\n\n // Step 1: Ensure Claude directory exists\n if (!existsSync(CLAUDE_DIR)) {\n if (options.dryRun) {\n console.log(chalk.gray(`Would create: ${CLAUDE_DIR}`));\n } else {\n mkdirSync(CLAUDE_DIR, { recursive: true });\n console.log(chalk.green('[OK]') + ' Created ~/.claude directory');\n }\n }\n\n // Step 2: Create MCP server configuration\n const mcpConfig = {\n mcpServers: {\n stackmemory: {\n command: 'stackmemory',\n args: ['mcp-server'],\n env: {\n NODE_ENV: 'production',\n },\n },\n },\n };\n\n if (options.dryRun) {\n console.log(\n chalk.gray(`Would write MCP config to: ${MCP_CONFIG_FILE}`)\n );\n console.log(chalk.gray(JSON.stringify(mcpConfig, null, 2)));\n } else {\n writeFileSync(MCP_CONFIG_FILE, JSON.stringify(mcpConfig, null, 2));\n console.log(chalk.green('[OK]') + ' Created MCP server configuration');\n }\n\n // Step 3: Update Claude config.json to reference MCP config\n let claudeConfig: Record<string, unknown> = {};\n if (existsSync(CLAUDE_CONFIG_FILE)) {\n try {\n claudeConfig = JSON.parse(readFileSync(CLAUDE_CONFIG_FILE, 'utf8'));\n } catch {\n console.log(\n chalk.yellow('[WARN]') +\n ' Could not parse existing config.json, creating new'\n );\n }\n }\n\n // Ensure mcp.configFiles array includes our config\n if (!claudeConfig.mcp) {\n claudeConfig.mcp = {};\n }\n const mcp = claudeConfig.mcp as Record<string, unknown>;\n if (!mcp.configFiles) {\n mcp.configFiles = [];\n }\n const configFiles = mcp.configFiles as string[];\n if (!configFiles.includes(MCP_CONFIG_FILE)) {\n configFiles.push(MCP_CONFIG_FILE);\n }\n\n if (options.dryRun) {\n console.log(chalk.gray(`Would update: ${CLAUDE_CONFIG_FILE}`));\n } else {\n writeFileSync(\n CLAUDE_CONFIG_FILE,\n JSON.stringify(claudeConfig, null, 2)\n );\n console.log(chalk.green('[OK]') + ' Updated Claude config.json');\n }\n\n // Step 4: Validate configuration\n console.log(chalk.cyan('\\nValidating configuration...'));\n\n // Check stackmemory command is available\n try {\n execSync('stackmemory --version', { stdio: 'pipe' });\n console.log(chalk.green('[OK]') + ' stackmemory CLI is installed');\n } catch {\n console.log(chalk.yellow('[WARN]') + ' stackmemory CLI not in PATH');\n console.log(chalk.gray(' You may need to restart your terminal'));\n }\n\n // Check Claude Code is available\n try {\n execSync('claude --version', { stdio: 'pipe' });\n console.log(chalk.green('[OK]') + ' Claude Code is installed');\n } catch {\n console.log(chalk.yellow('[WARN]') + ' Claude Code not found');\n console.log(chalk.gray(' Install from: https://claude.ai/code'));\n }\n\n // Final instructions\n if (!options.dryRun) {\n console.log(chalk.green('\\nMCP setup complete!'));\n console.log(chalk.cyan('\\nNext steps:'));\n console.log(chalk.white(' 1. Restart Claude Code'));\n console.log(\n chalk.white(\n ' 2. The StackMemory MCP tools will be available automatically'\n )\n );\n console.log(\n chalk.gray(\n '\\nTo verify: Run \"stackmemory doctor\" to check all integrations'\n )\n );\n }\n });\n}\n\n/**\n * Create doctor command for diagnostics\n */\nexport function createDoctorCommand(): Command {\n return new Command('doctor')\n .description('Diagnose StackMemory configuration and common issues')\n .option('--fix', 'Attempt to automatically fix issues')\n .action(async (options) => {\n console.log(chalk.cyan('\\nStackMemory Doctor\\n'));\n console.log(chalk.gray('Checking configuration and dependencies...\\n'));\n\n const results: DiagnosticResult[] = [];\n\n // 1. Check project initialization\n const projectDir = join(process.cwd(), '.stackmemory');\n const dbPath = join(projectDir, 'context.db');\n if (existsSync(dbPath)) {\n results.push({\n name: 'Project Initialization',\n status: 'ok',\n message: 'StackMemory is initialized in this project',\n });\n } else if (existsSync(projectDir)) {\n results.push({\n name: 'Project Initialization',\n status: 'warn',\n message: '.stackmemory directory exists but database not found',\n fix: 'Run: stackmemory init',\n });\n } else {\n results.push({\n name: 'Project Initialization',\n status: 'error',\n message: 'StackMemory not initialized in this project',\n fix: 'Run: stackmemory init',\n });\n }\n\n // 2. Check database integrity\n if (existsSync(dbPath)) {\n try {\n const Database = (await import('better-sqlite3')).default;\n const db = new Database(dbPath, { readonly: true });\n const tables = db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table'\")\n .all() as { name: string }[];\n db.close();\n\n const hasFrames = tables.some((t) => t.name === 'frames');\n if (hasFrames) {\n results.push({\n name: 'Database Integrity',\n status: 'ok',\n message: `Database has ${tables.length} tables`,\n });\n } else {\n results.push({\n name: 'Database Integrity',\n status: 'warn',\n message: 'Database exists but missing expected tables',\n fix: 'Run: stackmemory init --interactive',\n });\n }\n } catch (error) {\n results.push({\n name: 'Database Integrity',\n status: 'error',\n message: `Database error: ${(error as Error).message}`,\n fix: 'Remove .stackmemory/context.db and run: stackmemory init',\n });\n }\n }\n\n // 3. Check MCP configuration\n if (existsSync(MCP_CONFIG_FILE)) {\n try {\n const config = JSON.parse(readFileSync(MCP_CONFIG_FILE, 'utf8'));\n if (config.mcpServers?.stackmemory) {\n results.push({\n name: 'MCP Configuration',\n status: 'ok',\n message: 'MCP server configured',\n });\n } else {\n results.push({\n name: 'MCP Configuration',\n status: 'warn',\n message:\n 'MCP config file exists but stackmemory server not configured',\n fix: 'Run: stackmemory setup-mcp',\n });\n }\n } catch {\n results.push({\n name: 'MCP Configuration',\n status: 'error',\n message: 'Invalid MCP configuration file',\n fix: 'Run: stackmemory setup-mcp --reset',\n });\n }\n } else {\n results.push({\n name: 'MCP Configuration',\n status: 'warn',\n message: 'MCP not configured for Claude Code',\n fix: 'Run: stackmemory setup-mcp',\n });\n }\n\n // 4. Check Claude hooks\n if (existsSync(HOOKS_JSON)) {\n try {\n const hooks = JSON.parse(readFileSync(HOOKS_JSON, 'utf8'));\n const hasTraceHook = !!hooks['tool-use-approval'];\n if (hasTraceHook) {\n results.push({\n name: 'Claude Hooks',\n status: 'ok',\n message: 'Tool tracing hook installed',\n });\n } else {\n results.push({\n name: 'Claude Hooks',\n status: 'warn',\n message: 'Hooks file exists but tracing not configured',\n fix: 'Run: stackmemory hooks install',\n });\n }\n } catch {\n results.push({\n name: 'Claude Hooks',\n status: 'warn',\n message: 'Could not read hooks.json',\n });\n }\n } else {\n results.push({\n name: 'Claude Hooks',\n status: 'warn',\n message: 'Claude hooks not installed (optional)',\n fix: 'Run: stackmemory hooks install',\n });\n }\n\n // 5. Check environment variables\n const envChecks = [\n { key: 'LINEAR_API_KEY', name: 'Linear API Key', optional: true },\n { key: 'TWILIO_ACCOUNT_SID', name: 'Twilio Account', optional: true },\n ];\n\n for (const check of envChecks) {\n const value = process.env[check.key];\n if (value) {\n results.push({\n name: check.name,\n status: 'ok',\n message: 'Environment variable set',\n });\n } else if (!check.optional) {\n results.push({\n name: check.name,\n status: 'error',\n message: 'Required environment variable not set',\n fix: `Set ${check.key} in your .env file`,\n });\n }\n // Skip optional env vars that aren't set\n }\n\n // 6. Check file permissions\n const homeStackmemory = join(homedir(), '.stackmemory');\n if (existsSync(homeStackmemory)) {\n try {\n const testFile = join(homeStackmemory, '.write-test');\n writeFileSync(testFile, 'test');\n const { unlinkSync } = await import('fs');\n unlinkSync(testFile);\n results.push({\n name: 'File Permissions',\n status: 'ok',\n message: '~/.stackmemory is writable',\n });\n } catch {\n results.push({\n name: 'File Permissions',\n status: 'error',\n message: '~/.stackmemory is not writable',\n fix: 'Run: chmod 700 ~/.stackmemory',\n });\n }\n }\n\n // Display results\n let hasErrors = false;\n let hasWarnings = false;\n\n for (const result of results) {\n const icon =\n result.status === 'ok'\n ? chalk.green('[OK]')\n : result.status === 'warn'\n ? chalk.yellow('[WARN]')\n : chalk.red('[ERROR]');\n\n console.log(`${icon} ${result.name}`);\n console.log(chalk.gray(` ${result.message}`));\n\n if (result.fix) {\n console.log(chalk.cyan(` Fix: ${result.fix}`));\n\n if (options.fix && result.status !== 'ok') {\n // Auto-fix logic for specific issues\n if (result.fix.includes('stackmemory setup-mcp')) {\n console.log(chalk.gray(' Attempting auto-fix...'));\n try {\n execSync('stackmemory setup-mcp', { stdio: 'inherit' });\n } catch {\n console.log(chalk.red(' Auto-fix failed'));\n }\n }\n }\n }\n\n if (result.status === 'error') hasErrors = true;\n if (result.status === 'warn') hasWarnings = true;\n }\n\n // Summary\n console.log('');\n if (hasErrors) {\n console.log(\n chalk.red('Some issues need attention. Run suggested fixes above.')\n );\n process.exit(1);\n } else if (hasWarnings) {\n console.log(\n chalk.yellow(\n 'StackMemory is working but some optional features are not configured.'\n )\n );\n } else {\n console.log(\n chalk.green('All checks passed! StackMemory is properly configured.')\n );\n }\n });\n}\n\n/**\n * Register setup commands\n */\nexport function registerSetupCommands(program: Command): void {\n program.addCommand(createSetupMCPCommand());\n program.addCommand(createDoctorCommand());\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/cli/index.js
CHANGED
|
@@ -50,17 +50,14 @@ import { createSettingsCommand } from "./commands/settings.js";
|
|
|
50
50
|
import { createRetrievalCommands } from "./commands/retrieval.js";
|
|
51
51
|
import { createDiscoveryCommands } from "./commands/discovery.js";
|
|
52
52
|
import { createModelCommand } from "./commands/model.js";
|
|
53
|
+
import { registerSetupCommands } from "./commands/setup.js";
|
|
53
54
|
import { ProjectManager } from "../core/projects/project-manager.js";
|
|
54
55
|
import Database from "better-sqlite3";
|
|
55
56
|
import { join } from "path";
|
|
56
57
|
import { existsSync, mkdirSync } from "fs";
|
|
57
58
|
import inquirer from "inquirer";
|
|
58
59
|
import chalk from "chalk";
|
|
59
|
-
import {
|
|
60
|
-
loadStorageConfig,
|
|
61
|
-
enableChromaDB,
|
|
62
|
-
getStorageModeDescription
|
|
63
|
-
} from "../core/config/storage-config.js";
|
|
60
|
+
import { enableChromaDB } from "../core/config/storage-config.js";
|
|
64
61
|
import { spawn } from "child_process";
|
|
65
62
|
import { homedir } from "os";
|
|
66
63
|
import { createRequire } from "module";
|
|
@@ -131,69 +128,77 @@ program.name("stackmemory").description(
|
|
|
131
128
|
"Lossless memory runtime for AI coding tools - organizes context as a call stack instead of linear chat logs, with team collaboration and infinite retention"
|
|
132
129
|
).version(VERSION);
|
|
133
130
|
program.command("init").description(
|
|
134
|
-
`Initialize StackMemory in current project
|
|
131
|
+
`Initialize StackMemory in current project (zero-config by default)
|
|
135
132
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
ChromaDB
|
|
139
|
-
).option("--
|
|
133
|
+
Options:
|
|
134
|
+
--interactive Ask configuration questions
|
|
135
|
+
--chromadb Enable ChromaDB semantic search (prompts for API key)`
|
|
136
|
+
).option("-i, --interactive", "Interactive mode with configuration prompts").option(
|
|
140
137
|
"--chromadb",
|
|
141
138
|
"Enable ChromaDB for semantic search (prompts for API key)"
|
|
142
|
-
).
|
|
139
|
+
).action(async (options) => {
|
|
143
140
|
try {
|
|
144
141
|
const projectRoot = process.cwd();
|
|
145
142
|
const dbDir = join(projectRoot, ".stackmemory");
|
|
143
|
+
const alreadyInit = existsSync(join(dbDir, "context.db"));
|
|
144
|
+
if (alreadyInit && !options.interactive) {
|
|
145
|
+
console.log(chalk.yellow("StackMemory already initialized."));
|
|
146
|
+
console.log(chalk.gray("Run with --interactive to reconfigure."));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
146
149
|
if (!existsSync(dbDir)) {
|
|
147
150
|
mkdirSync(dbDir, { recursive: true });
|
|
148
151
|
}
|
|
149
|
-
|
|
150
|
-
const isFirstTimeSetup = !storageConfig.chromadb.enabled && storageConfig.mode === "sqlite";
|
|
151
|
-
if (options.sqlite || options.skipStoragePrompt) {
|
|
152
|
-
console.log(chalk.gray("Using SQLite-only storage mode."));
|
|
153
|
-
} else if (options.chromadb) {
|
|
152
|
+
if (options.chromadb) {
|
|
154
153
|
await promptAndEnableChromaDB();
|
|
155
|
-
} else if (
|
|
154
|
+
} else if (options.interactive && process.stdin.isTTY) {
|
|
156
155
|
console.log(chalk.cyan("\nStorage Configuration"));
|
|
157
|
-
console.log(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
console.log(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
console.log(chalk.gray(" - Semantic search across your context"));
|
|
164
|
-
console.log(chalk.gray(" - Cloud backup capability"));
|
|
165
|
-
console.log(chalk.gray(" - Requires ChromaDB API key\n"));
|
|
156
|
+
console.log(
|
|
157
|
+
chalk.gray("SQLite (default) is fast and requires no setup.")
|
|
158
|
+
);
|
|
159
|
+
console.log(
|
|
160
|
+
chalk.gray("ChromaDB adds semantic search but requires an API key.\n")
|
|
161
|
+
);
|
|
166
162
|
const { enableChroma } = await inquirer.prompt([
|
|
167
163
|
{
|
|
168
164
|
type: "confirm",
|
|
169
165
|
name: "enableChroma",
|
|
170
|
-
message: "Enable ChromaDB for semantic search?
|
|
166
|
+
message: "Enable ChromaDB for semantic search?",
|
|
171
167
|
default: false
|
|
172
168
|
}
|
|
173
169
|
]);
|
|
174
170
|
if (enableChroma) {
|
|
175
171
|
await promptAndEnableChromaDB();
|
|
176
|
-
} else {
|
|
177
|
-
console.log(chalk.gray("Using SQLite-only storage mode."));
|
|
178
172
|
}
|
|
179
173
|
}
|
|
180
174
|
const dbPath = join(dbDir, "context.db");
|
|
181
175
|
const db = new Database(dbPath);
|
|
182
176
|
new FrameManager(db, "cli-project");
|
|
183
177
|
logger.info("StackMemory initialized successfully", { projectRoot });
|
|
178
|
+
console.log(chalk.green("\n[OK] StackMemory initialized"));
|
|
179
|
+
console.log(chalk.gray(` Project: ${projectRoot}`));
|
|
180
|
+
console.log(chalk.gray(` Storage: SQLite (local)`));
|
|
181
|
+
console.log(chalk.cyan("\nNext steps:"));
|
|
182
|
+
console.log(
|
|
183
|
+
chalk.white(" 1. stackmemory setup-mcp") + chalk.gray(" # Configure Claude Code integration")
|
|
184
|
+
);
|
|
185
|
+
console.log(
|
|
186
|
+
chalk.white(" 2. stackmemory status") + chalk.gray(" # Check status")
|
|
187
|
+
);
|
|
184
188
|
console.log(
|
|
185
|
-
chalk.
|
|
186
|
-
projectRoot
|
|
189
|
+
chalk.white(" 3. stackmemory doctor") + chalk.gray(" # Diagnose issues")
|
|
187
190
|
);
|
|
188
|
-
storageConfig = loadStorageConfig();
|
|
189
|
-
console.log(chalk.gray(`Storage mode: ${getStorageModeDescription()}`));
|
|
190
191
|
db.close();
|
|
191
192
|
} catch (error) {
|
|
192
193
|
logger.error("Failed to initialize StackMemory", error);
|
|
194
|
+
console.error(chalk.red("\n[ERROR] Initialization failed"));
|
|
195
|
+
console.error(chalk.gray(` Reason: ${error.message}`));
|
|
193
196
|
console.error(
|
|
194
|
-
chalk.
|
|
195
|
-
|
|
197
|
+
chalk.gray(
|
|
198
|
+
" Fix: Ensure you have write permissions to the current directory"
|
|
199
|
+
)
|
|
196
200
|
);
|
|
201
|
+
console.error(chalk.gray(" Run: stackmemory doctor"));
|
|
197
202
|
process.exit(1);
|
|
198
203
|
}
|
|
199
204
|
});
|
|
@@ -517,6 +522,7 @@ if (isFeatureEnabled("whatsapp")) {
|
|
|
517
522
|
program.addCommand(createRetrievalCommands());
|
|
518
523
|
program.addCommand(createDiscoveryCommands());
|
|
519
524
|
program.addCommand(createModelCommand());
|
|
525
|
+
registerSetupCommands(program);
|
|
520
526
|
program.command("dashboard").description("Display monitoring dashboard in terminal").option("-w, --watch", "Auto-refresh dashboard").option("-i, --interval <seconds>", "Refresh interval in seconds", "5").action(async (options) => {
|
|
521
527
|
const { dashboardCommand } = await import("./commands/dashboard.js");
|
|
522
528
|
await dashboardCommand.handler(options);
|