@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.
@@ -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
- Storage Modes:
137
- SQLite (default): Local only, fast, no setup required
138
- ChromaDB (hybrid): Adds semantic search and cloud backup, requires API key`
139
- ).option("--sqlite", "Use SQLite-only storage (default, skip prompts)").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
- ).option("--skip-storage-prompt", "Skip storage configuration prompt").action(async (options) => {
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
- let storageConfig = loadStorageConfig();
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 (isFirstTimeSetup && process.stdin.isTTY) {
154
+ } else if (options.interactive && process.stdin.isTTY) {
156
155
  console.log(chalk.cyan("\nStorage Configuration"));
157
- console.log(chalk.gray("StackMemory supports two storage modes:\n"));
158
- console.log(chalk.white(" SQLite (default):"));
159
- console.log(chalk.gray(" - Local storage only"));
160
- console.log(chalk.gray(" - Fast and simple"));
161
- console.log(chalk.gray(" - No external dependencies\n"));
162
- console.log(chalk.white(" ChromaDB (hybrid):"));
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? (requires API key)",
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.green("\n[OK] StackMemory initialized in"),
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.red("[ERROR] Initialization failed:"),
195
- error.message
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);