Skip to content

Commit fa802b3

Browse files
feat(onboarding): add 5 UX improvements for new users
1. Zero-config init: `stackmemory init` now works without any prompts - Uses SQLite by default (no questions asked) - Add --interactive flag for configuration prompts - Shows helpful next steps after initialization 2. New `stackmemory setup-mcp` command - Auto-configures Claude Code MCP integration - Creates ~/.claude/stackmemory-mcp.json - Updates Claude config.json - Validates configuration after setup 3. Interactive postinstall hook - Asks for consent before modifying ~/.claude - Shows what will be installed - Skips silently in CI/non-TTY environments - Respects STACKMEMORY_AUTO_HOOKS=true for automation 4. New `stackmemory doctor` diagnostic command - Checks project initialization - Validates database integrity - Verifies MCP configuration - Checks Claude hooks installation - Suggests fixes for each issue found 5. Better error messages with reason + fix + next step - Init failure now shows reason and fix command - Points users to `stackmemory doctor`
1 parent cc59e4d commit fa802b3

File tree

3 files changed

+530
-68
lines changed

3 files changed

+530
-68
lines changed

scripts/install-claude-hooks-auto.js

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
/**
44
* Auto-install Claude hooks during npm install
55
* This runs as a postinstall script to set up tracing hooks and daemon
6+
*
7+
* INTERACTIVE: Asks for user consent before modifying ~/.claude
68
*/
79

810
import {
@@ -14,6 +16,7 @@ import {
1416
} from 'fs';
1517
import { join } from 'path';
1618
import { homedir } from 'os';
19+
import { createInterface } from 'readline';
1720

1821
import { fileURLToPath } from 'url';
1922
import { dirname } from 'path';
@@ -27,12 +30,63 @@ const templatesDir = join(__dirname, '..', 'templates', 'claude-hooks');
2730
const stackmemoryBinDir = join(homedir(), '.stackmemory', 'bin');
2831
const distDir = join(__dirname, '..', 'dist');
2932

33+
/**
34+
* Ask user for confirmation before installing hooks
35+
* Returns true if user consents, false otherwise
36+
*/
37+
async function askForConsent() {
38+
// Skip prompt if:
39+
// 1. Not a TTY (CI/CD, piped input)
40+
// 2. STACKMEMORY_AUTO_HOOKS=true is set
41+
// 3. Running in CI environment
42+
if (
43+
!process.stdin.isTTY ||
44+
process.env.STACKMEMORY_AUTO_HOOKS === 'true' ||
45+
process.env.CI === 'true'
46+
) {
47+
// In non-interactive mode, skip hook installation silently
48+
console.log(
49+
'StackMemory installed. Run "stackmemory setup-mcp" to configure Claude Code integration.'
50+
);
51+
return false;
52+
}
53+
54+
console.log('\n📦 StackMemory Post-Install\n');
55+
console.log(
56+
'StackMemory can integrate with Claude Code by installing hooks that:'
57+
);
58+
console.log(' - Track tool usage for better context');
59+
console.log(' - Enable session persistence across restarts');
60+
console.log(' - Sync context with Linear (optional)');
61+
console.log('\nThis will modify files in ~/.claude/\n');
62+
63+
return new Promise((resolve) => {
64+
const rl = createInterface({
65+
input: process.stdin,
66+
output: process.stdout,
67+
});
68+
69+
rl.question('Install Claude Code hooks? [y/N] ', (answer) => {
70+
rl.close();
71+
const normalized = answer.toLowerCase().trim();
72+
resolve(normalized === 'y' || normalized === 'yes');
73+
});
74+
75+
// Timeout after 30 seconds - default to no
76+
setTimeout(() => {
77+
console.log('\n(Timed out, skipping hook installation)');
78+
rl.close();
79+
resolve(false);
80+
}, 30000);
81+
});
82+
}
83+
3084
async function installClaudeHooks() {
3185
try {
3286
// Create Claude hooks directory if it doesn't exist
3387
if (!existsSync(claudeHooksDir)) {
3488
mkdirSync(claudeHooksDir, { recursive: true });
35-
console.log('📁 Created Claude hooks directory');
89+
console.log('Created ~/.claude/hooks directory');
3690
}
3791

3892
// Copy hook files
@@ -48,7 +102,7 @@ async function installClaudeHooks() {
48102
if (existsSync(destPath)) {
49103
const backupPath = `${destPath}.backup-${Date.now()}`;
50104
copyFileSync(destPath, backupPath);
51-
console.log(`📋 Backed up existing hook: ${hookFile}`);
105+
console.log(` Backed up: ${hookFile}`);
52106
}
53107

54108
copyFileSync(srcPath, destPath);
@@ -62,7 +116,7 @@ async function installClaudeHooks() {
62116
}
63117

64118
installed++;
65-
console.log(`✅ Installed hook: ${hookFile}`);
119+
console.log(` Installed: ${hookFile}`);
66120
}
67121
}
68122

@@ -71,9 +125,8 @@ async function installClaudeHooks() {
71125
if (existsSync(claudeConfigFile)) {
72126
try {
73127
hooksConfig = JSON.parse(readFileSync(claudeConfigFile, 'utf8'));
74-
console.log('📋 Loaded existing hooks.json');
75128
} catch {
76-
console.log('⚠️ Could not parse existing hooks.json, creating new');
129+
// Start fresh if parse fails
77130
}
78131
}
79132

@@ -86,32 +139,20 @@ async function installClaudeHooks() {
86139
};
87140

88141
writeFileSync(claudeConfigFile, JSON.stringify(newHooksConfig, null, 2));
89-
console.log('🔧 Updated hooks.json configuration');
90142

91143
if (installed > 0) {
92-
console.log(
93-
`\nSuccessfully installed ${installed} Claude hooks for StackMemory tracing!`
94-
);
95-
console.log(
96-
'Tool usage and session data will now be automatically logged'
97-
);
98-
console.log(
99-
`Traces saved to: ${join(homedir(), '.stackmemory', 'traces')}`
100-
);
101-
console.log(
102-
'\nTo disable tracing, set DEBUG_TRACE=false in your .env file'
103-
);
144+
console.log(`\n[OK] Installed ${installed} Claude hooks`);
145+
console.log(` Traces: ~/.stackmemory/traces/`);
146+
console.log(' To disable: set DEBUG_TRACE=false in .env');
104147
}
105148

106149
// Install session daemon binary
107150
await installSessionDaemon();
108151

109152
return true;
110153
} catch (error) {
111-
console.error('Failed to install Claude hooks:', error.message);
112-
console.error(
113-
' This is not critical - StackMemory will still work without hooks'
114-
);
154+
console.error('Hook installation failed:', error.message);
155+
console.error('(Non-critical - StackMemory works without hooks)');
115156
return false;
116157
}
117158
}
@@ -154,8 +195,17 @@ async function installSessionDaemon() {
154195

155196
// Only run if called directly (not imported)
156197
if (import.meta.url === `file://${process.argv[1]}`) {
157-
console.log('🔧 Installing StackMemory Claude Code integration hooks...\n');
158-
await installClaudeHooks();
198+
const consent = await askForConsent();
199+
if (consent) {
200+
await installClaudeHooks();
201+
console.log(
202+
'\nNext: Run "stackmemory setup-mcp" to complete Claude Code integration.'
203+
);
204+
} else {
205+
console.log(
206+
'Skipped hook installation. Run "stackmemory hooks install" later if needed.'
207+
);
208+
}
159209
}
160210

161211
export { installClaudeHooks };

0 commit comments

Comments
 (0)