@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.
162 lines (141 loc) • 5.53 kB
JavaScript
import fs from 'fs';
import os from 'os';
import path, { dirname, join } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const ALIAS_NAME = 'codex-sm';
const WRAPPER_SCRIPT = 'codex-wrapper.sh';
function getShellConfigFiles() {
const shell = process.env.SHELL || '';
const home = os.homedir();
const files = [];
if (shell.includes('zsh')) {
files.push(path.join(home, '.zshrc'));
const zprofile = path.join(home, '.zprofile');
if (fs.existsSync(zprofile)) files.push(zprofile);
}
if (shell.includes('bash') || !shell.includes('zsh')) {
const profilePath = path.join(home, '.bash_profile');
const rcPath = path.join(home, '.bashrc');
if (fs.existsSync(profilePath)) files.push(profilePath);
if (fs.existsSync(rcPath)) files.push(rcPath);
}
if (shell.includes('fish')) {
files.push(path.join(home, '.config', 'fish', 'config.fish'));
}
return files.length > 0 ? files : [path.join(home, '.bashrc')];
}
function setupAlias() {
try {
const scriptsDir = dirname(__dirname);
const repoRoot = dirname(scriptsDir);
const distBin = join(repoRoot, 'dist', 'src', 'cli', 'codex-sm.js');
const wrapperPath = join(scriptsDir, WRAPPER_SCRIPT);
// Prefer TypeScript bin (built file); fallback to shell wrapper
const targetCmd = fs.existsSync(distBin)
? `node ${distBin}`
: (fs.existsSync(wrapperPath)
? `${wrapperPath}`
: null);
if (!targetCmd) {
console.log('⚠️ Neither built TS bin nor wrapper script found.');
console.log(' Build first: npm run build');
console.log(` Or ensure ${WRAPPER_SCRIPT} exists in scripts/`);
return;
}
const configFiles = getShellConfigFiles();
const aliasLine = `alias ${ALIAS_NAME}="${targetCmd}"`;
const marker = '# StackMemory Codex alias';
let updatedOrConfigured = false;
const touched = [];
for (const configFile of configFiles) {
let config = '';
if (fs.existsSync(configFile)) config = fs.readFileSync(configFile, 'utf8');
// If already present, replace existing alias line in-place
if (config.includes(`alias ${ALIAS_NAME}=`)) {
const newConfig = config.replace(
new RegExp(`^.*alias\\s+${ALIAS_NAME}=.*$`, 'm'),
aliasLine
);
if (newConfig !== config) {
fs.writeFileSync(configFile, newConfig, 'utf8');
console.log(`✅ Updated ${ALIAS_NAME} alias in ${configFile}`);
updatedOrConfigured = true;
touched.push(configFile);
continue;
}
}
// Otherwise, add once to the first config file available
if (!updatedOrConfigured && configFiles.indexOf(configFile) === 0) {
const aliasBlock = `\n${marker}\n${aliasLine}\n`;
fs.appendFileSync(configFile, aliasBlock);
console.log(`✅ Added ${ALIAS_NAME} alias to ${configFile}`);
updatedOrConfigured = true;
touched.push(configFile);
}
}
if (touched.length > 0) {
console.log(` Run 'source ${touched[0]}' or restart your terminal to use it`);
console.log(` You can then use: ${ALIAS_NAME} [your message]`);
} else {
console.log(`ℹ️ No suitable shell config found to modify.`);
}
console.log(`\n📖 Usage:`);
console.log(` ${ALIAS_NAME} # Start Codex with StackMemory`);
console.log(` ${ALIAS_NAME} --auto-sync # With Linear auto-sync`);
console.log(` ${ALIAS_NAME} --sync-interval=10 # Custom sync interval (minutes)`);
} catch (error) {
console.error('Error setting up alias:', error.message);
console.log('\nManual setup:');
console.log(`Add this line to your shell config file:`);
console.log(`alias ${ALIAS_NAME}="${join(dirname(__dirname), WRAPPER_SCRIPT)}"`);
}
}
if (process.argv.includes('--check')) {
const configFiles = getShellConfigFiles();
let found = false;
for (const configFile of configFiles) {
if (fs.existsSync(configFile)) {
const config = fs.readFileSync(configFile, 'utf8');
if (config.includes(`alias ${ALIAS_NAME}=`)) {
console.log(`✓ ${ALIAS_NAME} alias is configured in ${configFile}`);
found = true;
break;
}
}
}
if (!found) {
console.log(`✗ ${ALIAS_NAME} alias not found`);
process.exit(1);
}
process.exit(0);
}
if (process.argv.includes('--remove')) {
const configFiles = getShellConfigFiles();
let removed = false;
for (const configFile of configFiles) {
if (fs.existsSync(configFile)) {
let config = fs.readFileSync(configFile, 'utf8');
const marker = '# StackMemory Codex alias';
if (config.includes(marker) || config.includes(`alias ${ALIAS_NAME}=`)) {
const lines = config.split('\n');
const newLines = [];
let skipNext = false;
for (const line of lines) {
if (line.includes(marker)) { skipNext = true; continue; }
if (skipNext && line.includes(`alias ${ALIAS_NAME}=`)) { skipNext = false; continue; }
skipNext = false;
newLines.push(line);
}
fs.writeFileSync(configFile, newLines.join('\n'));
console.log(`✅ Removed ${ALIAS_NAME} alias from ${configFile}`);
removed = true;
}
}
}
if (!removed) console.log(`✗ ${ALIAS_NAME} alias not found in any config file`);
process.exit(0);
}
setupAlias();