@stackmemoryai/stackmemory
Version:
Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.
221 lines (197 loc) ⢠6.99 kB
text/typescript
#!/usr/bin/env tsx
/**
* Initialize StackMemory in the current project
*/
import {
existsSync,
mkdirSync,
writeFileSync,
readFileSync,
appendFileSync,
} from 'fs';
import { join } from 'path';
import { execSync } from 'child_process';
import chalk from 'chalk';
// Parse CLI args for --with-diffmem flag
const enableDiffMem = process.argv.includes('--with-diffmem');
// Type-safe environment variable access
function getEnv(key: string, defaultValue?: string): string {
const value = process.env[key];
if (value === undefined) {
if (defaultValue !== undefined) return defaultValue;
throw new Error(`Environment variable ${key} is required`);
}
return value;
}
function getOptionalEnv(key: string): string | undefined {
return process.env[key];
}
const projectRoot = process.cwd();
console.log(chalk.blue.bold('\nš Initializing StackMemory...\n'));
// 1. Create .stackmemory directory
const stackDir = join(projectRoot, '.stackmemory');
if (!existsSync(stackDir)) {
mkdirSync(stackDir, { recursive: true });
console.log(chalk.green('ā') + ' Created .stackmemory directory');
} else {
console.log(chalk.yellow('ā ') + ' .stackmemory directory already exists');
}
// 2. Add to .gitignore
const gitignorePath = join(projectRoot, '.gitignore');
const gitignoreEntry =
'\n# StackMemory\n.stackmemory/*.db\n.stackmemory/*.db-*\n';
if (existsSync(gitignorePath)) {
const gitignore = readFileSync(gitignorePath, 'utf-8');
if (!gitignore.includes('.stackmemory')) {
appendFileSync(gitignorePath, gitignoreEntry);
console.log(chalk.green('ā') + ' Added .stackmemory to .gitignore');
}
} else {
writeFileSync(gitignorePath, gitignoreEntry);
console.log(chalk.green('ā') + ' Created .gitignore with .stackmemory');
}
// 3. Create config file
const configPath = join(stackDir, 'config.json');
if (!existsSync(configPath)) {
const config = {
projectId: projectRoot.split('/').pop(),
userId: process.env['USER'] || 'default',
teamId: 'local',
initialized: new Date().toISOString(),
};
writeFileSync(configPath, JSON.stringify(config, null, 2));
console.log(chalk.green('ā') + ' Created config file');
}
// 4. Create initial JSONL file
const jsonlPath = join(stackDir, 'frames.jsonl');
if (!existsSync(jsonlPath)) {
const initialFrame = {
id: 'init_' + Date.now(),
type: 'system',
content: 'StackMemory initialized',
timestamp: Date.now(),
};
writeFileSync(jsonlPath, JSON.stringify(initialFrame) + '\n');
console.log(chalk.green('ā') + ' Created frames.jsonl');
}
// 5. Create MCP config for Claude Code
const isMacOS = process.platform === 'darwin';
const mcpConfigPath = isMacOS
? join(
process.env['HOME'] || '~',
'Library',
'Application Support',
'Claude',
'claude_desktop_config.json'
)
: join(
process.env['HOME'] || '~',
'.config',
'claude',
'claude_desktop_config.json'
);
console.log(chalk.blue('\nš MCP Configuration for Claude Code:\n'));
const mcpConfig = {
mcpServers: {
stackmemory: {
command: 'node',
args: [join(projectRoot, 'dist', 'integrations', 'mcp', 'server.js')],
env: {
PROJECT_ROOT: projectRoot,
},
},
},
};
console.log(chalk.gray('Add this to your Claude Code MCP configuration:'));
console.log(chalk.gray('(' + mcpConfigPath + ')\n'));
console.log(chalk.cyan(JSON.stringify(mcpConfig, null, 2)));
// 6. Build the project
console.log(chalk.blue('\nš¦ Building TypeScript files...\n'));
try {
execSync('npm run build', { stdio: 'inherit', cwd: projectRoot });
console.log(chalk.green('ā') + ' Build completed');
} catch {
console.log(chalk.yellow('ā ') + ' Build failed - run npm run build manually');
}
// 7. Optional: Initialize DiffMem for long-term user memory
if (enableDiffMem) {
console.log(chalk.blue('\nš§ Setting up DiffMem integration...\n'));
const diffmemDir = join(stackDir, 'diffmem');
const diffmemStorageDir = join(diffmemDir, 'storage');
const diffmemWorktreesDir = join(diffmemDir, 'worktrees');
// Create DiffMem directories
mkdirSync(diffmemStorageDir, { recursive: true });
mkdirSync(diffmemWorktreesDir, { recursive: true });
// Initialize git repo for storage
if (!existsSync(join(diffmemStorageDir, '.git'))) {
try {
execSync('git init', { cwd: diffmemStorageDir, stdio: 'pipe' });
execSync('git commit --allow-empty -m "Initialize DiffMem storage"', {
cwd: diffmemStorageDir,
stdio: 'pipe',
});
console.log(chalk.green('ā') + ' Initialized DiffMem storage repository');
} catch {
console.log(
chalk.yellow('ā ') + ' Failed to initialize git repo for DiffMem'
);
}
}
// Create DiffMem config
const diffmemConfigPath = join(diffmemDir, 'config.json');
const diffmemConfig = {
enabled: true,
endpoint: 'http://localhost:8000',
userId: process.env['USER'] || 'default',
storagePath: diffmemStorageDir,
worktreePath: diffmemWorktreesDir,
};
writeFileSync(diffmemConfigPath, JSON.stringify(diffmemConfig, null, 2));
console.log(chalk.green('ā') + ' Created DiffMem configuration');
// Add DiffMem env vars to .env file
const envPath = join(projectRoot, '.env');
const diffmemEnvVars = `
# DiffMem Configuration (Long-term User Memory)
DIFFMEM_ENABLED=true
DIFFMEM_ENDPOINT=http://localhost:8000
DIFFMEM_USER_ID=${process.env['USER'] || 'default'}
DIFFMEM_STORAGE_PATH=${diffmemStorageDir}
DIFFMEM_WORKTREE_PATH=${diffmemWorktreesDir}
`;
if (existsSync(envPath)) {
const envContent = readFileSync(envPath, 'utf-8');
if (!envContent.includes('DIFFMEM_')) {
appendFileSync(envPath, diffmemEnvVars);
console.log(chalk.green('ā') + ' Added DiffMem configuration to .env');
}
} else {
writeFileSync(envPath, diffmemEnvVars.trim() + '\n');
console.log(chalk.green('ā') + ' Created .env with DiffMem configuration');
}
console.log(chalk.green('ā') + ' DiffMem integration configured');
console.log(
chalk.gray(
' Note: DiffMem server must be running for user memory features'
)
);
}
console.log(chalk.green.bold('\nā
StackMemory initialized successfully!\n'));
console.log(chalk.gray('Next steps:'));
console.log(chalk.gray('1. Add the MCP configuration above to Claude Code'));
console.log(chalk.gray('2. Restart Claude Code'));
console.log(chalk.gray('3. Start using context tracking!'));
if (enableDiffMem) {
console.log(chalk.gray('4. Start DiffMem server: npm run diffmem:start'));
}
console.log(chalk.gray('\nUseful commands:'));
console.log(
chalk.cyan(' npm run mcp:dev') + ' - Start MCP server in dev mode'
);
console.log(chalk.cyan(' npm run status') + ' - Check StackMemory status');
console.log(chalk.cyan(' npm run analyze') + ' - Analyze context usage');
if (enableDiffMem) {
console.log(
chalk.cyan(' npm run diffmem:start') + ' - Start DiffMem server'
);
}
console.log();