@stackmemoryai/stackmemory 0.3.18 → 0.3.19
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/claude-sm.js +51 -5
- package/dist/cli/claude-sm.js.map +2 -2
- package/dist/cli/codex-sm.js +52 -19
- package/dist/cli/codex-sm.js.map +2 -2
- package/dist/cli/commands/db.js +143 -0
- package/dist/cli/commands/db.js.map +7 -0
- package/dist/cli/commands/login.js +50 -0
- package/dist/cli/commands/login.js.map +7 -0
- package/dist/cli/commands/migrate.js +178 -0
- package/dist/cli/commands/migrate.js.map +7 -0
- package/dist/cli/commands/onboard.js +158 -2
- package/dist/cli/commands/onboard.js.map +2 -2
- package/dist/cli/index.js +5 -0
- package/dist/cli/index.js.map +2 -2
- package/dist/core/context/frame-database.js +1 -0
- package/dist/core/context/frame-database.js.map +2 -2
- package/dist/core/context/frame-manager.js +56 -2
- package/dist/core/context/frame-manager.js.map +2 -2
- package/dist/core/database/database-adapter.js +6 -1
- package/dist/core/database/database-adapter.js.map +2 -2
- package/dist/core/database/sqlite-adapter.js +60 -2
- package/dist/core/database/sqlite-adapter.js.map +2 -2
- package/dist/servers/railway/index.js +843 -82
- package/dist/servers/railway/index.js.map +3 -3
- package/package.json +8 -4
- package/scripts/setup-railway-deployment.sh +37 -0
- package/scripts/verify-railway-schema.ts +35 -0
package/dist/cli/claude-sm.js
CHANGED
|
@@ -78,6 +78,19 @@ class ClaudeSM {
|
|
|
78
78
|
return false;
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
+
resolveClaudeBin() {
|
|
82
|
+
if (this.config.claudeBin && this.config.claudeBin.trim()) {
|
|
83
|
+
return this.config.claudeBin.trim();
|
|
84
|
+
}
|
|
85
|
+
const envBin = process.env["CLAUDE_BIN"];
|
|
86
|
+
if (envBin && envBin.trim()) return envBin.trim();
|
|
87
|
+
try {
|
|
88
|
+
execSync("which claude", { stdio: "ignore" });
|
|
89
|
+
return "claude";
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
81
94
|
setupWorktree() {
|
|
82
95
|
if (!this.config.useWorktree || !this.isGitRepo()) {
|
|
83
96
|
return null;
|
|
@@ -229,6 +242,11 @@ class ClaudeSM {
|
|
|
229
242
|
i++;
|
|
230
243
|
this.config.task = args[i];
|
|
231
244
|
break;
|
|
245
|
+
case "--claude-bin":
|
|
246
|
+
i++;
|
|
247
|
+
this.config.claudeBin = args[i];
|
|
248
|
+
process.env["CLAUDE_BIN"] = this.config.claudeBin;
|
|
249
|
+
break;
|
|
232
250
|
case "--auto":
|
|
233
251
|
case "-a":
|
|
234
252
|
if (this.isGitRepo()) {
|
|
@@ -317,10 +335,40 @@ class ClaudeSM {
|
|
|
317
335
|
console.log();
|
|
318
336
|
console.log(chalk.gray("Starting Claude..."));
|
|
319
337
|
console.log(chalk.gray("\u2500".repeat(42)));
|
|
320
|
-
const
|
|
338
|
+
const claudeBin = this.resolveClaudeBin();
|
|
339
|
+
if (!claudeBin) {
|
|
340
|
+
console.error(chalk.red("\u274C Claude CLI not found."));
|
|
341
|
+
console.log(
|
|
342
|
+
chalk.gray(
|
|
343
|
+
" Install Claude CLI or set an override:\n export CLAUDE_BIN=/path/to/claude\n claude-sm --help\n\n Ensure PATH includes npm global bin (npm bin -g)."
|
|
344
|
+
)
|
|
345
|
+
);
|
|
346
|
+
process.exit(1);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const claude = spawn(claudeBin, claudeArgs, {
|
|
321
350
|
stdio: "inherit",
|
|
322
351
|
env: process.env
|
|
323
352
|
});
|
|
353
|
+
claude.on("error", (err) => {
|
|
354
|
+
console.error(chalk.red("\u274C Failed to launch Claude CLI."));
|
|
355
|
+
if (err.code === "ENOENT") {
|
|
356
|
+
console.error(
|
|
357
|
+
chalk.gray(
|
|
358
|
+
" Not found. Set CLAUDE_BIN or install claude on PATH."
|
|
359
|
+
)
|
|
360
|
+
);
|
|
361
|
+
} else if (err.code === "EPERM" || err.code === "EACCES") {
|
|
362
|
+
console.error(
|
|
363
|
+
chalk.gray(
|
|
364
|
+
" Permission/sandbox issue. Try outside a sandbox or set CLAUDE_BIN."
|
|
365
|
+
)
|
|
366
|
+
);
|
|
367
|
+
} else {
|
|
368
|
+
console.error(chalk.gray(` ${err.message}`));
|
|
369
|
+
}
|
|
370
|
+
process.exit(1);
|
|
371
|
+
});
|
|
324
372
|
claude.on("exit", (code) => {
|
|
325
373
|
this.saveContext("Claude session ended", {
|
|
326
374
|
action: "session_end",
|
|
@@ -358,12 +406,10 @@ class ClaudeSM {
|
|
|
358
406
|
});
|
|
359
407
|
}
|
|
360
408
|
}
|
|
361
|
-
program.name("claude-sm").description("Claude with StackMemory context and worktree isolation").version("1.0.0").option("-w, --worktree", "Create isolated worktree for this instance").option("-s, --sandbox", "Enable sandbox mode (file/network restrictions)").option("-c, --chrome", "Enable Chrome automation").option("-a, --auto", "Automatically detect and apply best settings").option("-b, --branch <name>", "Specify branch name for worktree").option("-t, --task <desc>", "Task description for context").option("--no-context", "Disable StackMemory context integration").option("--no-trace", "Disable debug tracing (enabled by default)").option("--verbose-trace", "Enable verbose debug tracing with full details").helpOption("-h, --help", "Display help").allowUnknownOption(true).action(async (_options) => {
|
|
409
|
+
program.name("claude-sm").description("Claude with StackMemory context and worktree isolation").version("1.0.0").option("-w, --worktree", "Create isolated worktree for this instance").option("-s, --sandbox", "Enable sandbox mode (file/network restrictions)").option("-c, --chrome", "Enable Chrome automation").option("-a, --auto", "Automatically detect and apply best settings").option("-b, --branch <name>", "Specify branch name for worktree").option("-t, --task <desc>", "Task description for context").option("--claude-bin <path>", "Path to claude CLI (or use CLAUDE_BIN)").option("--no-context", "Disable StackMemory context integration").option("--no-trace", "Disable debug tracing (enabled by default)").option("--verbose-trace", "Enable verbose debug tracing with full details").helpOption("-h, --help", "Display help").allowUnknownOption(true).action(async (_options) => {
|
|
362
410
|
const claudeSM = new ClaudeSM();
|
|
363
411
|
const args = process.argv.slice(2);
|
|
364
412
|
await claudeSM.run(args);
|
|
365
413
|
});
|
|
366
|
-
|
|
367
|
-
program.parse(process.argv);
|
|
368
|
-
}
|
|
414
|
+
program.parse(process.argv);
|
|
369
415
|
//# sourceMappingURL=claude-sm.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/cli/claude-sm.ts"],
|
|
4
|
-
"sourcesContent": ["#!/usr/bin/env node\n\n/**\n * claude-sm: Claude wrapper with StackMemory and worktree integration\n * Automatically manages context persistence and instance isolation\n */\n\nimport { spawn, execSync, execFileSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport { program } from 'commander';\nimport { v4 as uuidv4 } from 'uuid';\nimport chalk from 'chalk';\nimport { initializeTracing, trace } from '../core/trace/index.js';\n\ninterface ClaudeConfig {\n instanceId: string;\n worktreePath?: string;\n useSandbox: boolean;\n useChrome: boolean;\n useWorktree: boolean;\n contextEnabled: boolean;\n branch?: string;\n task?: string;\n tracingEnabled: boolean;\n verboseTracing: boolean;\n}\n\nclass ClaudeSM {\n private config: ClaudeConfig;\n private stackmemoryPath: string;\n private worktreeScriptPath: string;\n private claudeConfigDir: string;\n\n constructor() {\n this.config = {\n instanceId: this.generateInstanceId(),\n useSandbox: false,\n useChrome: false,\n useWorktree: false,\n contextEnabled: true,\n tracingEnabled: true, // Enable tracing by default for claude-sm\n verboseTracing: false,\n };\n\n this.stackmemoryPath = this.findStackMemory();\n this.worktreeScriptPath = path.join(\n __dirname,\n '../../scripts/claude-worktree-manager.sh'\n );\n this.claudeConfigDir = path.join(os.homedir(), '.claude');\n\n // Ensure config directory exists\n if (!fs.existsSync(this.claudeConfigDir)) {\n fs.mkdirSync(this.claudeConfigDir, { recursive: true });\n }\n }\n\n private generateInstanceId(): string {\n return uuidv4().substring(0, 8);\n }\n\n private findStackMemory(): string {\n // Check multiple possible locations\n const possiblePaths = [\n path.join(os.homedir(), '.stackmemory', 'bin', 'stackmemory'),\n '/usr/local/bin/stackmemory',\n '/opt/homebrew/bin/stackmemory',\n 'stackmemory', // Rely on PATH\n ];\n\n for (const smPath of possiblePaths) {\n try {\n execFileSync('which', [smPath], { stdio: 'ignore' });\n return smPath;\n } catch {\n // Continue searching\n }\n }\n\n return 'stackmemory'; // Fallback to PATH\n }\n\n private isGitRepo(): boolean {\n try {\n execSync('git rev-parse --git-dir', { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n }\n\n private getCurrentBranch(): string {\n try {\n return execSync('git rev-parse --abbrev-ref HEAD', {\n encoding: 'utf8',\n }).trim();\n } catch {\n return 'main';\n }\n }\n\n private hasUncommittedChanges(): boolean {\n try {\n const status = execSync('git status --porcelain', { encoding: 'utf8' });\n return status.length > 0;\n } catch {\n return false;\n }\n }\n\n private setupWorktree(): string | null {\n if (!this.config.useWorktree || !this.isGitRepo()) {\n return null;\n }\n\n console.log(chalk.blue('\uD83C\uDF33 Setting up isolated worktree...'));\n\n const timestamp = new Date()\n .toISOString()\n .replace(/[:.]/g, '-')\n .substring(0, 19);\n const branch =\n this.config.branch ||\n `claude-${this.config.task || 'work'}-${timestamp}-${this.config.instanceId}`;\n const repoName = path.basename(process.cwd());\n const worktreePath = path.join(\n path.dirname(process.cwd()),\n `${repoName}--${branch}`\n );\n\n try {\n // Create worktree\n const flags = [];\n if (this.config.useSandbox) flags.push('--sandbox');\n if (this.config.useChrome) flags.push('--chrome');\n\n const cmd = `git worktree add -b \"${branch}\" \"${worktreePath}\"`;\n execSync(cmd, { stdio: 'inherit' });\n\n console.log(chalk.green(`\u2705 Worktree created: ${worktreePath}`));\n console.log(chalk.gray(` Branch: ${branch}`));\n\n // Save worktree config\n const configPath = path.join(worktreePath, '.claude-instance.json');\n const configData = {\n instanceId: this.config.instanceId,\n worktreePath,\n branch,\n task: this.config.task,\n sandboxEnabled: this.config.useSandbox,\n chromeEnabled: this.config.useChrome,\n created: new Date().toISOString(),\n parentRepo: process.cwd(),\n };\n fs.writeFileSync(configPath, JSON.stringify(configData, null, 2));\n\n // Copy environment files\n const envFiles = ['.env', '.env.local', '.mise.toml', '.tool-versions'];\n for (const file of envFiles) {\n const srcPath = path.join(process.cwd(), file);\n if (fs.existsSync(srcPath)) {\n fs.copyFileSync(srcPath, path.join(worktreePath, file));\n }\n }\n\n return worktreePath;\n } catch (err: unknown) {\n console.error(chalk.red('\u274C Failed to create worktree:'), err);\n return null;\n }\n }\n\n private saveContext(\n message: string,\n metadata: Record<string, unknown> = {}\n ): void {\n if (!this.config.contextEnabled) return;\n\n try {\n const contextData = {\n message,\n metadata: {\n ...metadata,\n instanceId: this.config.instanceId,\n worktree: this.config.worktreePath,\n timestamp: new Date().toISOString(),\n },\n };\n\n const cmd = `${this.stackmemoryPath} context save --json '${JSON.stringify(contextData)}'`;\n execSync(cmd, { stdio: 'ignore' });\n } catch {\n // Silently fail - don't interrupt Claude\n }\n }\n\n private loadContext(): void {\n if (!this.config.contextEnabled) return;\n\n try {\n console.log(chalk.blue('\uD83D\uDCDA Loading previous context...'));\n\n const cmd = `${this.stackmemoryPath} context list --limit 5 --format json`;\n const output = execSync(cmd, { encoding: 'utf8' });\n const contexts = JSON.parse(output);\n\n if (contexts.length > 0) {\n console.log(chalk.gray('Recent context loaded:'));\n contexts.forEach(\n (ctx: { message: string; metadata?: { timestamp?: string } }) => {\n console.log(\n chalk.gray(` - ${ctx.message} (${ctx.metadata?.timestamp})`)\n );\n }\n );\n }\n } catch {\n // Silently continue\n }\n }\n\n private detectMultipleInstances(): boolean {\n try {\n const lockDir = path.join(process.cwd(), '.claude-worktree-locks');\n if (!fs.existsSync(lockDir)) return false;\n\n const locks = fs.readdirSync(lockDir).filter((f) => f.endsWith('.lock'));\n const activeLocks = locks.filter((lockFile) => {\n const lockPath = path.join(lockDir, lockFile);\n const lockData = JSON.parse(fs.readFileSync(lockPath, 'utf8'));\n const lockAge = Date.now() - new Date(lockData.created).getTime();\n return lockAge < 24 * 60 * 60 * 1000; // Less than 24 hours old\n });\n\n return activeLocks.length > 0;\n } catch {\n return false;\n }\n }\n\n private suggestWorktreeMode(): void {\n if (this.hasUncommittedChanges()) {\n console.log(chalk.yellow('\u26A0\uFE0F Uncommitted changes detected'));\n console.log(\n chalk.gray(' Consider using --worktree to work in isolation')\n );\n }\n\n if (this.detectMultipleInstances()) {\n console.log(chalk.yellow('\u26A0\uFE0F Other Claude instances detected'));\n console.log(\n chalk.gray(' Using --worktree is recommended to avoid conflicts')\n );\n }\n }\n\n public async run(args: string[]): Promise<void> {\n // Parse arguments\n const claudeArgs: string[] = [];\n let i = 0;\n\n while (i < args.length) {\n const arg = args[i];\n\n switch (arg) {\n case '--worktree':\n case '-w':\n this.config.useWorktree = true;\n break;\n case '--sandbox':\n case '-s':\n this.config.useSandbox = true;\n claudeArgs.push('--sandbox');\n break;\n case '--chrome':\n case '-c':\n this.config.useChrome = true;\n claudeArgs.push('--chrome');\n break;\n case '--no-context':\n this.config.contextEnabled = false;\n break;\n case '--no-trace':\n this.config.tracingEnabled = false;\n break;\n case '--verbose-trace':\n this.config.verboseTracing = true;\n break;\n case '--branch':\n case '-b':\n i++;\n this.config.branch = args[i];\n break;\n case '--task':\n case '-t':\n i++;\n this.config.task = args[i];\n break;\n case '--auto':\n case '-a':\n // Auto mode: detect and apply best settings\n if (this.isGitRepo()) {\n this.config.useWorktree =\n this.hasUncommittedChanges() || this.detectMultipleInstances();\n }\n break;\n default:\n claudeArgs.push(arg);\n }\n i++;\n }\n\n // Initialize tracing system if enabled\n if (this.config.tracingEnabled) {\n // Set up environment for tracing\n process.env['DEBUG_TRACE'] = 'true';\n process.env['STACKMEMORY_DEBUG'] = 'true';\n process.env['TRACE_OUTPUT'] = 'file'; // Write to file to not clutter Claude output\n process.env['TRACE_MASK_SENSITIVE'] = 'true'; // Always mask sensitive data\n\n if (this.config.verboseTracing) {\n process.env['TRACE_VERBOSITY'] = 'full';\n process.env['TRACE_PARAMS'] = 'true';\n process.env['TRACE_RESULTS'] = 'true';\n process.env['TRACE_MEMORY'] = 'true';\n } else {\n process.env['TRACE_VERBOSITY'] = 'summary';\n process.env['TRACE_PARAMS'] = 'true';\n process.env['TRACE_RESULTS'] = 'false';\n }\n\n // Initialize the tracing system\n initializeTracing();\n\n // Start tracing this Claude session\n trace.command(\n 'claude-sm',\n {\n instanceId: this.config.instanceId,\n worktree: this.config.useWorktree,\n sandbox: this.config.useSandbox,\n task: this.config.task,\n },\n async () => {\n // Session tracing will wrap the entire Claude execution\n }\n );\n }\n\n // Show header\n console.log(chalk.blue('\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557'));\n console.log(chalk.blue('\u2551 Claude + StackMemory + Worktree \u2551'));\n console.log(chalk.blue('\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D'));\n console.log();\n\n // Check Git repo status\n if (this.isGitRepo()) {\n const branch = this.getCurrentBranch();\n console.log(chalk.gray(`\uD83D\uDCCD Current branch: ${branch}`));\n\n if (!this.config.useWorktree) {\n this.suggestWorktreeMode();\n }\n }\n\n // Setup worktree if requested\n if (this.config.useWorktree) {\n const worktreePath = this.setupWorktree();\n if (worktreePath) {\n this.config.worktreePath = worktreePath;\n process.chdir(worktreePath);\n\n // Save context about worktree creation\n this.saveContext('Created worktree for Claude instance', {\n action: 'worktree_created',\n path: worktreePath,\n branch: this.config.branch,\n });\n }\n }\n\n // Load previous context\n this.loadContext();\n\n // Setup environment\n process.env['CLAUDE_INSTANCE_ID'] = this.config.instanceId;\n if (this.config.worktreePath) {\n process.env['CLAUDE_WORKTREE_PATH'] = this.config.worktreePath;\n }\n\n console.log(chalk.gray(`\uD83E\uDD16 Instance ID: ${this.config.instanceId}`));\n console.log(chalk.gray(`\uD83D\uDCC1 Working in: ${process.cwd()}`));\n\n if (this.config.useSandbox) {\n console.log(chalk.yellow('\uD83D\uDD12 Sandbox mode enabled'));\n }\n if (this.config.useChrome) {\n console.log(chalk.yellow('\uD83C\uDF10 Chrome automation enabled'));\n }\n if (this.config.tracingEnabled) {\n console.log(\n chalk.gray(`\uD83D\uDD0D Debug tracing enabled (logs to ~/.stackmemory/traces/)`)\n );\n if (this.config.verboseTracing) {\n console.log(\n chalk.gray(` Verbose mode: capturing all execution details`)\n );\n }\n }\n\n console.log();\n console.log(chalk.gray('Starting Claude...'));\n console.log(chalk.gray('\u2500'.repeat(42)));\n\n // Launch Claude\n const claude = spawn('claude', claudeArgs, {\n stdio: 'inherit',\n env: process.env,\n });\n\n // Handle exit\n claude.on('exit', (code) => {\n // Save final context\n this.saveContext('Claude session ended', {\n action: 'session_end',\n exitCode: code,\n });\n\n // End tracing and show summary if enabled\n if (this.config.tracingEnabled) {\n const summary = trace.getExecutionSummary();\n console.log();\n console.log(chalk.gray('\u2500'.repeat(42)));\n console.log(chalk.blue('Debug Trace Summary:'));\n console.log(chalk.gray(summary));\n }\n\n // Offer to clean up worktree\n if (this.config.worktreePath) {\n console.log();\n console.log(chalk.gray('\u2500'.repeat(42)));\n console.log(chalk.blue('Session ended in worktree:'));\n console.log(chalk.gray(` ${this.config.worktreePath}`));\n console.log();\n console.log(chalk.gray('To remove worktree: gd_claude'));\n console.log(chalk.gray('To merge to main: cwm'));\n }\n\n process.exit(code || 0);\n });\n\n // Handle signals\n process.on('SIGINT', () => {\n this.saveContext('Claude session interrupted', {\n action: 'session_interrupt',\n });\n claude.kill('SIGINT');\n });\n\n process.on('SIGTERM', () => {\n this.saveContext('Claude session terminated', {\n action: 'session_terminate',\n });\n claude.kill('SIGTERM');\n });\n }\n}\n\n// CLI interface\nprogram\n .name('claude-sm')\n .description('Claude with StackMemory context and worktree isolation')\n .version('1.0.0')\n .option('-w, --worktree', 'Create isolated worktree for this instance')\n .option('-s, --sandbox', 'Enable sandbox mode (file/network restrictions)')\n .option('-c, --chrome', 'Enable Chrome automation')\n .option('-a, --auto', 'Automatically detect and apply best settings')\n .option('-b, --branch <name>', 'Specify branch name for worktree')\n .option('-t, --task <desc>', 'Task description for context')\n .option('--no-context', 'Disable StackMemory context integration')\n .option('--no-trace', 'Disable debug tracing (enabled by default)')\n .option('--verbose-trace', 'Enable verbose debug tracing with full details')\n .helpOption('-h, --help', 'Display help')\n .allowUnknownOption(true)\n .action(async (_options) => {\n const claudeSM = new ClaudeSM();\n const args = process.argv.slice(2);\n await claudeSM.run(args);\n });\n\n// Handle direct execution\nif (require.main === module) {\n program.parse(process.argv);\n}\n"],
|
|
5
|
-
"mappings": ";AAOA,SAAS,OAAO,UAAU,oBAAoB;AAC9C,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,YAAY,QAAQ;AACpB,SAAS,eAAe;AACxB,SAAS,MAAM,cAAc;AAC7B,OAAO,WAAW;AAClB,SAAS,mBAAmB,aAAa;
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n\n/**\n * claude-sm: Claude wrapper with StackMemory and worktree integration\n * Automatically manages context persistence and instance isolation\n */\n\nimport { spawn, execSync, execFileSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport { program } from 'commander';\nimport { v4 as uuidv4 } from 'uuid';\nimport chalk from 'chalk';\nimport { initializeTracing, trace } from '../core/trace/index.js';\n\ninterface ClaudeConfig {\n instanceId: string;\n worktreePath?: string;\n useSandbox: boolean;\n useChrome: boolean;\n useWorktree: boolean;\n contextEnabled: boolean;\n branch?: string;\n task?: string;\n tracingEnabled: boolean;\n verboseTracing: boolean;\n claudeBin?: string;\n}\n\nclass ClaudeSM {\n private config: ClaudeConfig;\n private stackmemoryPath: string;\n private worktreeScriptPath: string;\n private claudeConfigDir: string;\n\n constructor() {\n this.config = {\n instanceId: this.generateInstanceId(),\n useSandbox: false,\n useChrome: false,\n useWorktree: false,\n contextEnabled: true,\n tracingEnabled: true, // Enable tracing by default for claude-sm\n verboseTracing: false,\n };\n\n this.stackmemoryPath = this.findStackMemory();\n this.worktreeScriptPath = path.join(\n __dirname,\n '../../scripts/claude-worktree-manager.sh'\n );\n this.claudeConfigDir = path.join(os.homedir(), '.claude');\n\n // Ensure config directory exists\n if (!fs.existsSync(this.claudeConfigDir)) {\n fs.mkdirSync(this.claudeConfigDir, { recursive: true });\n }\n }\n\n private generateInstanceId(): string {\n return uuidv4().substring(0, 8);\n }\n\n private findStackMemory(): string {\n // Check multiple possible locations\n const possiblePaths = [\n path.join(os.homedir(), '.stackmemory', 'bin', 'stackmemory'),\n '/usr/local/bin/stackmemory',\n '/opt/homebrew/bin/stackmemory',\n 'stackmemory', // Rely on PATH\n ];\n\n for (const smPath of possiblePaths) {\n try {\n execFileSync('which', [smPath], { stdio: 'ignore' });\n return smPath;\n } catch {\n // Continue searching\n }\n }\n\n return 'stackmemory'; // Fallback to PATH\n }\n\n private isGitRepo(): boolean {\n try {\n execSync('git rev-parse --git-dir', { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n }\n\n private getCurrentBranch(): string {\n try {\n return execSync('git rev-parse --abbrev-ref HEAD', {\n encoding: 'utf8',\n }).trim();\n } catch {\n return 'main';\n }\n }\n\n private hasUncommittedChanges(): boolean {\n try {\n const status = execSync('git status --porcelain', { encoding: 'utf8' });\n return status.length > 0;\n } catch {\n return false;\n }\n }\n\n private resolveClaudeBin(): string | null {\n // 1) CLI-specified\n if (this.config.claudeBin && this.config.claudeBin.trim()) {\n return this.config.claudeBin.trim();\n }\n // 2) Env override\n const envBin = process.env['CLAUDE_BIN'];\n if (envBin && envBin.trim()) return envBin.trim();\n // 3) PATH detection\n try {\n execSync('which claude', { stdio: 'ignore' });\n return 'claude';\n } catch {}\n return null;\n }\n\n private setupWorktree(): string | null {\n if (!this.config.useWorktree || !this.isGitRepo()) {\n return null;\n }\n\n console.log(chalk.blue('\uD83C\uDF33 Setting up isolated worktree...'));\n\n const timestamp = new Date()\n .toISOString()\n .replace(/[:.]/g, '-')\n .substring(0, 19);\n const branch =\n this.config.branch ||\n `claude-${this.config.task || 'work'}-${timestamp}-${this.config.instanceId}`;\n const repoName = path.basename(process.cwd());\n const worktreePath = path.join(\n path.dirname(process.cwd()),\n `${repoName}--${branch}`\n );\n\n try {\n // Create worktree\n const flags = [];\n if (this.config.useSandbox) flags.push('--sandbox');\n if (this.config.useChrome) flags.push('--chrome');\n\n const cmd = `git worktree add -b \"${branch}\" \"${worktreePath}\"`;\n execSync(cmd, { stdio: 'inherit' });\n\n console.log(chalk.green(`\u2705 Worktree created: ${worktreePath}`));\n console.log(chalk.gray(` Branch: ${branch}`));\n\n // Save worktree config\n const configPath = path.join(worktreePath, '.claude-instance.json');\n const configData = {\n instanceId: this.config.instanceId,\n worktreePath,\n branch,\n task: this.config.task,\n sandboxEnabled: this.config.useSandbox,\n chromeEnabled: this.config.useChrome,\n created: new Date().toISOString(),\n parentRepo: process.cwd(),\n };\n fs.writeFileSync(configPath, JSON.stringify(configData, null, 2));\n\n // Copy environment files\n const envFiles = ['.env', '.env.local', '.mise.toml', '.tool-versions'];\n for (const file of envFiles) {\n const srcPath = path.join(process.cwd(), file);\n if (fs.existsSync(srcPath)) {\n fs.copyFileSync(srcPath, path.join(worktreePath, file));\n }\n }\n\n return worktreePath;\n } catch (err: unknown) {\n console.error(chalk.red('\u274C Failed to create worktree:'), err);\n return null;\n }\n }\n\n private saveContext(\n message: string,\n metadata: Record<string, unknown> = {}\n ): void {\n if (!this.config.contextEnabled) return;\n\n try {\n const contextData = {\n message,\n metadata: {\n ...metadata,\n instanceId: this.config.instanceId,\n worktree: this.config.worktreePath,\n timestamp: new Date().toISOString(),\n },\n };\n\n const cmd = `${this.stackmemoryPath} context save --json '${JSON.stringify(contextData)}'`;\n execSync(cmd, { stdio: 'ignore' });\n } catch {\n // Silently fail - don't interrupt Claude\n }\n }\n\n private loadContext(): void {\n if (!this.config.contextEnabled) return;\n\n try {\n console.log(chalk.blue('\uD83D\uDCDA Loading previous context...'));\n\n const cmd = `${this.stackmemoryPath} context list --limit 5 --format json`;\n const output = execSync(cmd, { encoding: 'utf8' });\n const contexts = JSON.parse(output);\n\n if (contexts.length > 0) {\n console.log(chalk.gray('Recent context loaded:'));\n contexts.forEach(\n (ctx: { message: string; metadata?: { timestamp?: string } }) => {\n console.log(\n chalk.gray(` - ${ctx.message} (${ctx.metadata?.timestamp})`)\n );\n }\n );\n }\n } catch {\n // Silently continue\n }\n }\n\n private detectMultipleInstances(): boolean {\n try {\n const lockDir = path.join(process.cwd(), '.claude-worktree-locks');\n if (!fs.existsSync(lockDir)) return false;\n\n const locks = fs.readdirSync(lockDir).filter((f) => f.endsWith('.lock'));\n const activeLocks = locks.filter((lockFile) => {\n const lockPath = path.join(lockDir, lockFile);\n const lockData = JSON.parse(fs.readFileSync(lockPath, 'utf8'));\n const lockAge = Date.now() - new Date(lockData.created).getTime();\n return lockAge < 24 * 60 * 60 * 1000; // Less than 24 hours old\n });\n\n return activeLocks.length > 0;\n } catch {\n return false;\n }\n }\n\n private suggestWorktreeMode(): void {\n if (this.hasUncommittedChanges()) {\n console.log(chalk.yellow('\u26A0\uFE0F Uncommitted changes detected'));\n console.log(\n chalk.gray(' Consider using --worktree to work in isolation')\n );\n }\n\n if (this.detectMultipleInstances()) {\n console.log(chalk.yellow('\u26A0\uFE0F Other Claude instances detected'));\n console.log(\n chalk.gray(' Using --worktree is recommended to avoid conflicts')\n );\n }\n }\n\n public async run(args: string[]): Promise<void> {\n // Parse arguments\n const claudeArgs: string[] = [];\n let i = 0;\n\n while (i < args.length) {\n const arg = args[i];\n\n switch (arg) {\n case '--worktree':\n case '-w':\n this.config.useWorktree = true;\n break;\n case '--sandbox':\n case '-s':\n this.config.useSandbox = true;\n claudeArgs.push('--sandbox');\n break;\n case '--chrome':\n case '-c':\n this.config.useChrome = true;\n claudeArgs.push('--chrome');\n break;\n case '--no-context':\n this.config.contextEnabled = false;\n break;\n case '--no-trace':\n this.config.tracingEnabled = false;\n break;\n case '--verbose-trace':\n this.config.verboseTracing = true;\n break;\n case '--branch':\n case '-b':\n i++;\n this.config.branch = args[i];\n break;\n case '--task':\n case '-t':\n i++;\n this.config.task = args[i];\n break;\n case '--claude-bin':\n i++;\n this.config.claudeBin = args[i];\n process.env['CLAUDE_BIN'] = this.config.claudeBin;\n break;\n case '--auto':\n case '-a':\n // Auto mode: detect and apply best settings\n if (this.isGitRepo()) {\n this.config.useWorktree =\n this.hasUncommittedChanges() || this.detectMultipleInstances();\n }\n break;\n default:\n claudeArgs.push(arg);\n }\n i++;\n }\n\n // Initialize tracing system if enabled\n if (this.config.tracingEnabled) {\n // Set up environment for tracing\n process.env['DEBUG_TRACE'] = 'true';\n process.env['STACKMEMORY_DEBUG'] = 'true';\n process.env['TRACE_OUTPUT'] = 'file'; // Write to file to not clutter Claude output\n process.env['TRACE_MASK_SENSITIVE'] = 'true'; // Always mask sensitive data\n\n if (this.config.verboseTracing) {\n process.env['TRACE_VERBOSITY'] = 'full';\n process.env['TRACE_PARAMS'] = 'true';\n process.env['TRACE_RESULTS'] = 'true';\n process.env['TRACE_MEMORY'] = 'true';\n } else {\n process.env['TRACE_VERBOSITY'] = 'summary';\n process.env['TRACE_PARAMS'] = 'true';\n process.env['TRACE_RESULTS'] = 'false';\n }\n\n // Initialize the tracing system\n initializeTracing();\n\n // Start tracing this Claude session\n trace.command(\n 'claude-sm',\n {\n instanceId: this.config.instanceId,\n worktree: this.config.useWorktree,\n sandbox: this.config.useSandbox,\n task: this.config.task,\n },\n async () => {\n // Session tracing will wrap the entire Claude execution\n }\n );\n }\n\n // Show header\n console.log(chalk.blue('\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557'));\n console.log(chalk.blue('\u2551 Claude + StackMemory + Worktree \u2551'));\n console.log(chalk.blue('\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D'));\n console.log();\n\n // Check Git repo status\n if (this.isGitRepo()) {\n const branch = this.getCurrentBranch();\n console.log(chalk.gray(`\uD83D\uDCCD Current branch: ${branch}`));\n\n if (!this.config.useWorktree) {\n this.suggestWorktreeMode();\n }\n }\n\n // Setup worktree if requested\n if (this.config.useWorktree) {\n const worktreePath = this.setupWorktree();\n if (worktreePath) {\n this.config.worktreePath = worktreePath;\n process.chdir(worktreePath);\n\n // Save context about worktree creation\n this.saveContext('Created worktree for Claude instance', {\n action: 'worktree_created',\n path: worktreePath,\n branch: this.config.branch,\n });\n }\n }\n\n // Load previous context\n this.loadContext();\n\n // Setup environment\n process.env['CLAUDE_INSTANCE_ID'] = this.config.instanceId;\n if (this.config.worktreePath) {\n process.env['CLAUDE_WORKTREE_PATH'] = this.config.worktreePath;\n }\n\n console.log(chalk.gray(`\uD83E\uDD16 Instance ID: ${this.config.instanceId}`));\n console.log(chalk.gray(`\uD83D\uDCC1 Working in: ${process.cwd()}`));\n\n if (this.config.useSandbox) {\n console.log(chalk.yellow('\uD83D\uDD12 Sandbox mode enabled'));\n }\n if (this.config.useChrome) {\n console.log(chalk.yellow('\uD83C\uDF10 Chrome automation enabled'));\n }\n if (this.config.tracingEnabled) {\n console.log(\n chalk.gray(`\uD83D\uDD0D Debug tracing enabled (logs to ~/.stackmemory/traces/)`)\n );\n if (this.config.verboseTracing) {\n console.log(\n chalk.gray(` Verbose mode: capturing all execution details`)\n );\n }\n }\n\n console.log();\n console.log(chalk.gray('Starting Claude...'));\n console.log(chalk.gray('\u2500'.repeat(42)));\n\n const claudeBin = this.resolveClaudeBin();\n if (!claudeBin) {\n console.error(chalk.red('\u274C Claude CLI not found.'));\n console.log(\n chalk.gray(\n ' Install Claude CLI or set an override:\\n' +\n ' export CLAUDE_BIN=/path/to/claude\\n' +\n ' claude-sm --help\\n\\n' +\n ' Ensure PATH includes npm global bin (npm bin -g).'\n )\n );\n process.exit(1);\n return;\n }\n\n // Launch Claude\n const claude = spawn(claudeBin, claudeArgs, {\n stdio: 'inherit',\n env: process.env,\n });\n\n claude.on('error', (err: NodeJS.ErrnoException) => {\n console.error(chalk.red('\u274C Failed to launch Claude CLI.'));\n if (err.code === 'ENOENT') {\n console.error(\n chalk.gray(\n ' Not found. Set CLAUDE_BIN or install claude on PATH.'\n )\n );\n } else if (err.code === 'EPERM' || err.code === 'EACCES') {\n console.error(\n chalk.gray(\n ' Permission/sandbox issue. Try outside a sandbox or set CLAUDE_BIN.'\n )\n );\n } else {\n console.error(chalk.gray(` ${err.message}`));\n }\n process.exit(1);\n });\n\n // Handle exit\n claude.on('exit', (code) => {\n // Save final context\n this.saveContext('Claude session ended', {\n action: 'session_end',\n exitCode: code,\n });\n\n // End tracing and show summary if enabled\n if (this.config.tracingEnabled) {\n const summary = trace.getExecutionSummary();\n console.log();\n console.log(chalk.gray('\u2500'.repeat(42)));\n console.log(chalk.blue('Debug Trace Summary:'));\n console.log(chalk.gray(summary));\n }\n\n // Offer to clean up worktree\n if (this.config.worktreePath) {\n console.log();\n console.log(chalk.gray('\u2500'.repeat(42)));\n console.log(chalk.blue('Session ended in worktree:'));\n console.log(chalk.gray(` ${this.config.worktreePath}`));\n console.log();\n console.log(chalk.gray('To remove worktree: gd_claude'));\n console.log(chalk.gray('To merge to main: cwm'));\n }\n\n process.exit(code || 0);\n });\n\n // Handle signals\n process.on('SIGINT', () => {\n this.saveContext('Claude session interrupted', {\n action: 'session_interrupt',\n });\n claude.kill('SIGINT');\n });\n\n process.on('SIGTERM', () => {\n this.saveContext('Claude session terminated', {\n action: 'session_terminate',\n });\n claude.kill('SIGTERM');\n });\n }\n}\n\n// CLI interface\nprogram\n .name('claude-sm')\n .description('Claude with StackMemory context and worktree isolation')\n .version('1.0.0')\n .option('-w, --worktree', 'Create isolated worktree for this instance')\n .option('-s, --sandbox', 'Enable sandbox mode (file/network restrictions)')\n .option('-c, --chrome', 'Enable Chrome automation')\n .option('-a, --auto', 'Automatically detect and apply best settings')\n .option('-b, --branch <name>', 'Specify branch name for worktree')\n .option('-t, --task <desc>', 'Task description for context')\n .option('--claude-bin <path>', 'Path to claude CLI (or use CLAUDE_BIN)')\n .option('--no-context', 'Disable StackMemory context integration')\n .option('--no-trace', 'Disable debug tracing (enabled by default)')\n .option('--verbose-trace', 'Enable verbose debug tracing with full details')\n .helpOption('-h, --help', 'Display help')\n .allowUnknownOption(true)\n .action(async (_options) => {\n const claudeSM = new ClaudeSM();\n const args = process.argv.slice(2);\n await claudeSM.run(args);\n });\n\n// Handle direct execution\n// ESM-safe CLI entry\nprogram.parse(process.argv);\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/cli/codex-sm.js
CHANGED
|
@@ -64,6 +64,26 @@ class CodexSM {
|
|
|
64
64
|
return false;
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
+
resolveCodexBin() {
|
|
68
|
+
if (this.config.codexBin && this.config.codexBin.trim()) {
|
|
69
|
+
return this.config.codexBin.trim();
|
|
70
|
+
}
|
|
71
|
+
const envBin = process.env["CODEX_BIN"];
|
|
72
|
+
if (envBin && envBin.trim()) {
|
|
73
|
+
return envBin.trim();
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
execSync("which codex", { stdio: "ignore" });
|
|
77
|
+
return "codex";
|
|
78
|
+
} catch {
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
execSync("which codex-cli", { stdio: "ignore" });
|
|
82
|
+
return "codex-cli";
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
67
87
|
setupWorktree() {
|
|
68
88
|
if (!this.config.useWorktree || !this.isGitRepo()) return null;
|
|
69
89
|
console.log(chalk.blue("\u{1F333} Setting up isolated worktree..."));
|
|
@@ -175,6 +195,11 @@ class CodexSM {
|
|
|
175
195
|
i++;
|
|
176
196
|
this.config.task = args[i];
|
|
177
197
|
break;
|
|
198
|
+
case "--codex-bin":
|
|
199
|
+
i++;
|
|
200
|
+
this.config.codexBin = args[i];
|
|
201
|
+
process.env["CODEX_BIN"] = this.config.codexBin;
|
|
202
|
+
break;
|
|
178
203
|
case "--auto":
|
|
179
204
|
case "-a":
|
|
180
205
|
if (this.isGitRepo()) {
|
|
@@ -243,22 +268,13 @@ class CodexSM {
|
|
|
243
268
|
console.log();
|
|
244
269
|
console.log(chalk.gray("Starting Codex..."));
|
|
245
270
|
console.log(chalk.gray("\u2500".repeat(42)));
|
|
246
|
-
const codexBin = (
|
|
247
|
-
try {
|
|
248
|
-
execSync("which codex", { stdio: "ignore" });
|
|
249
|
-
return "codex";
|
|
250
|
-
} catch {
|
|
251
|
-
}
|
|
252
|
-
try {
|
|
253
|
-
execSync("which codex-cli", { stdio: "ignore" });
|
|
254
|
-
return "codex-cli";
|
|
255
|
-
} catch {
|
|
256
|
-
}
|
|
257
|
-
return null;
|
|
258
|
-
})();
|
|
271
|
+
const codexBin = this.resolveCodexBin();
|
|
259
272
|
if (!codexBin) {
|
|
260
|
-
console.error(
|
|
261
|
-
|
|
273
|
+
console.error(chalk.red("\u274C Codex CLI not found."));
|
|
274
|
+
console.log(
|
|
275
|
+
chalk.gray(
|
|
276
|
+
" Install codex/codex-cli or set an override:\n export CODEX_BIN=/path/to/codex\n codex-sm --help\n\n Ensure PATH includes npm global bin (npm bin -g)."
|
|
277
|
+
)
|
|
262
278
|
);
|
|
263
279
|
process.exit(1);
|
|
264
280
|
return;
|
|
@@ -267,6 +283,25 @@ class CodexSM {
|
|
|
267
283
|
stdio: "inherit",
|
|
268
284
|
env: process.env
|
|
269
285
|
});
|
|
286
|
+
child.on("error", (err) => {
|
|
287
|
+
console.error(chalk.red("\u274C Failed to launch Codex CLI."));
|
|
288
|
+
if (err.code === "ENOENT") {
|
|
289
|
+
console.error(
|
|
290
|
+
chalk.gray(
|
|
291
|
+
" Not found. Set CODEX_BIN or install codex/codex-cli on PATH."
|
|
292
|
+
)
|
|
293
|
+
);
|
|
294
|
+
} else if (err.code === "EPERM" || err.code === "EACCES") {
|
|
295
|
+
console.error(
|
|
296
|
+
chalk.gray(
|
|
297
|
+
" Permission/sandbox issue. Try running outside a sandbox or set CODEX_BIN."
|
|
298
|
+
)
|
|
299
|
+
);
|
|
300
|
+
} else {
|
|
301
|
+
console.error(chalk.gray(` ${err.message}`));
|
|
302
|
+
}
|
|
303
|
+
process.exit(1);
|
|
304
|
+
});
|
|
270
305
|
child.on("exit", (code) => {
|
|
271
306
|
this.saveContext("Codex session ended", {
|
|
272
307
|
action: "session_end",
|
|
@@ -301,12 +336,10 @@ class CodexSM {
|
|
|
301
336
|
});
|
|
302
337
|
}
|
|
303
338
|
}
|
|
304
|
-
program.name("codex-sm").description("Codex with StackMemory context and optional worktree isolation").version("1.0.0").option("-w, --worktree", "Create isolated worktree for this instance").option("-a, --auto", "Automatically detect and apply best settings").option("-b, --branch <name>", "Specify branch name for worktree").option("-t, --task <desc>", "Task description for context").option("--no-context", "Disable StackMemory context integration").option("--no-trace", "Disable debug tracing (enabled by default)").option("--verbose-trace", "Enable verbose debug tracing with full details").helpOption("-h, --help", "Display help").allowUnknownOption(true).action(async (_options) => {
|
|
339
|
+
program.name("codex-sm").description("Codex with StackMemory context and optional worktree isolation").version("1.0.0").option("-w, --worktree", "Create isolated worktree for this instance").option("-a, --auto", "Automatically detect and apply best settings").option("-b, --branch <name>", "Specify branch name for worktree").option("-t, --task <desc>", "Task description for context").option("--codex-bin <path>", "Path to codex/codex-cli (or use CODEX_BIN)").option("--no-context", "Disable StackMemory context integration").option("--no-trace", "Disable debug tracing (enabled by default)").option("--verbose-trace", "Enable verbose debug tracing with full details").helpOption("-h, --help", "Display help").allowUnknownOption(true).action(async (_options) => {
|
|
305
340
|
const codexSM = new CodexSM();
|
|
306
341
|
const args = process.argv.slice(2);
|
|
307
342
|
await codexSM.run(args);
|
|
308
343
|
});
|
|
309
|
-
|
|
310
|
-
program.parse(process.argv);
|
|
311
|
-
}
|
|
344
|
+
program.parse(process.argv);
|
|
312
345
|
//# sourceMappingURL=codex-sm.js.map
|
package/dist/cli/codex-sm.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/cli/codex-sm.ts"],
|
|
4
|
-
"sourcesContent": ["#!/usr/bin/env node\n\n/**\n * codex-sm: Codex wrapper with StackMemory and worktree integration\n * Automatically manages context persistence, optional worktree isolation, and tracing\n */\n\nimport { spawn, execSync, execFileSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport { program } from 'commander';\nimport { v4 as uuidv4 } from 'uuid';\nimport chalk from 'chalk';\nimport { initializeTracing, trace } from '../core/trace/index.js';\n\ninterface CodexConfig {\n instanceId: string;\n worktreePath?: string;\n useWorktree: boolean;\n contextEnabled: boolean;\n branch?: string;\n task?: string;\n tracingEnabled: boolean;\n verboseTracing: boolean;\n}\n\nclass CodexSM {\n private config: CodexConfig;\n private stackmemoryPath: string;\n\n constructor() {\n this.config = {\n instanceId: this.generateInstanceId(),\n useWorktree: false,\n contextEnabled: true,\n tracingEnabled: true,\n verboseTracing: false,\n };\n\n this.stackmemoryPath = this.findStackMemory();\n }\n\n private generateInstanceId(): string {\n return uuidv4().substring(0, 8);\n }\n\n private findStackMemory(): string {\n const possiblePaths = [\n path.join(os.homedir(), '.stackmemory', 'bin', 'stackmemory'),\n '/usr/local/bin/stackmemory',\n '/opt/homebrew/bin/stackmemory',\n 'stackmemory',\n ];\n for (const smPath of possiblePaths) {\n try {\n execFileSync('which', [smPath], { stdio: 'ignore' });\n return smPath;\n } catch {\n // continue\n }\n }\n return 'stackmemory';\n }\n\n private isGitRepo(): boolean {\n try {\n execSync('git rev-parse --git-dir', { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n }\n\n private getCurrentBranch(): string {\n try {\n return execSync('git rev-parse --abbrev-ref HEAD', {\n encoding: 'utf8',\n }).trim();\n } catch {\n return 'main';\n }\n }\n\n private hasUncommittedChanges(): boolean {\n try {\n const status = execSync('git status --porcelain', { encoding: 'utf8' });\n return status.length > 0;\n } catch {\n return false;\n }\n }\n\n private setupWorktree(): string | null {\n if (!this.config.useWorktree || !this.isGitRepo()) return null;\n\n console.log(chalk.blue('\uD83C\uDF33 Setting up isolated worktree...'));\n\n const timestamp = new Date()\n .toISOString()\n .replace(/[:.]/g, '-')\n .substring(0, 19);\n const branch =\n this.config.branch ||\n `codex-${this.config.task || 'work'}-${timestamp}-${this.config.instanceId}`;\n const repoName = path.basename(process.cwd());\n const worktreePath = path.join(\n path.dirname(process.cwd()),\n `${repoName}--${branch}`\n );\n\n try {\n const cmd = `git worktree add -b \"${branch}\" \"${worktreePath}\"`;\n execSync(cmd, { stdio: 'inherit' });\n\n console.log(chalk.green(`Worktree created: ${worktreePath}`));\n console.log(chalk.gray(` Branch: ${branch}`));\n\n const configPath = path.join(worktreePath, '.codex-instance.json');\n const configData = {\n instanceId: this.config.instanceId,\n worktreePath,\n branch,\n task: this.config.task,\n created: new Date().toISOString(),\n parentRepo: process.cwd(),\n };\n fs.writeFileSync(configPath, JSON.stringify(configData, null, 2));\n\n const envFiles = ['.env', '.env.local', '.mise.toml', '.tool-versions'];\n for (const file of envFiles) {\n const srcPath = path.join(process.cwd(), file);\n if (fs.existsSync(srcPath))\n fs.copyFileSync(srcPath, path.join(worktreePath, file));\n }\n\n return worktreePath;\n } catch (err: unknown) {\n console.error(chalk.red('Failed to create worktree:'), err);\n return null;\n }\n }\n\n private saveContext(\n message: string,\n metadata: Record<string, unknown> = {}\n ): void {\n if (!this.config.contextEnabled) return;\n try {\n const contextData = {\n message,\n metadata: {\n ...metadata,\n instanceId: this.config.instanceId,\n worktree: this.config.worktreePath,\n timestamp: new Date().toISOString(),\n },\n };\n const cmd = `${this.stackmemoryPath} context save --json '${JSON.stringify(contextData)}'`;\n execSync(cmd, { stdio: 'ignore' });\n } catch {\n // ignore\n }\n }\n\n private loadContext(): void {\n if (!this.config.contextEnabled) return;\n try {\n console.log(chalk.blue('\uD83D\uDCDA Loading previous context...'));\n const cmd = `${this.stackmemoryPath} context list --limit 5 --format json`;\n const output = execSync(cmd, { encoding: 'utf8' });\n const contexts = JSON.parse(output);\n if (Array.isArray(contexts) && contexts.length > 0) {\n console.log(chalk.gray('Recent context loaded:'));\n contexts.forEach(\n (ctx: { message: string; metadata?: { timestamp?: string } }) => {\n console.log(\n chalk.gray(` - ${ctx.message} (${ctx.metadata?.timestamp})`)\n );\n }\n );\n }\n } catch {\n // ignore\n }\n }\n\n private suggestWorktreeMode(): void {\n if (this.hasUncommittedChanges()) {\n console.log(chalk.yellow('WARNING: Uncommitted changes detected'));\n console.log(\n chalk.gray(' Consider using --worktree to work in isolation')\n );\n }\n }\n\n public async run(args: string[]): Promise<void> {\n const codexArgs: string[] = [];\n let i = 0;\n while (i < args.length) {\n const arg = args[i];\n switch (arg) {\n case '--worktree':\n case '-w':\n this.config.useWorktree = true;\n break;\n case '--no-context':\n this.config.contextEnabled = false;\n break;\n case '--no-trace':\n this.config.tracingEnabled = false;\n break;\n case '--verbose-trace':\n this.config.verboseTracing = true;\n break;\n case '--branch':\n case '-b':\n i++;\n this.config.branch = args[i];\n break;\n case '--task':\n case '-t':\n i++;\n this.config.task = args[i];\n break;\n case '--auto':\n case '-a':\n if (this.isGitRepo()) {\n this.config.useWorktree = this.hasUncommittedChanges();\n }\n break;\n default:\n codexArgs.push(arg);\n }\n i++;\n }\n\n if (this.config.tracingEnabled) {\n process.env['DEBUG_TRACE'] = 'true';\n process.env['STACKMEMORY_DEBUG'] = 'true';\n process.env['TRACE_OUTPUT'] = 'file';\n process.env['TRACE_MASK_SENSITIVE'] = 'true';\n if (this.config.verboseTracing) {\n process.env['TRACE_VERBOSITY'] = 'full';\n process.env['TRACE_PARAMS'] = 'true';\n process.env['TRACE_RESULTS'] = 'true';\n process.env['TRACE_MEMORY'] = 'true';\n } else {\n process.env['TRACE_VERBOSITY'] = 'summary';\n process.env['TRACE_PARAMS'] = 'true';\n process.env['TRACE_RESULTS'] = 'false';\n }\n initializeTracing();\n trace.command(\n 'codex-sm',\n {\n instanceId: this.config.instanceId,\n worktree: this.config.useWorktree,\n task: this.config.task,\n },\n async () => {}\n );\n }\n\n console.log(chalk.blue('\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557'));\n console.log(chalk.blue('\u2551 Codex + StackMemory + Worktree \u2551'));\n console.log(chalk.blue('\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D'));\n console.log();\n\n if (this.isGitRepo()) {\n const branch = this.getCurrentBranch();\n console.log(chalk.gray(`\uD83D\uDCCD Current branch: ${branch}`));\n if (!this.config.useWorktree) this.suggestWorktreeMode();\n }\n\n if (this.config.useWorktree) {\n const worktreePath = this.setupWorktree();\n if (worktreePath) {\n this.config.worktreePath = worktreePath;\n process.chdir(worktreePath);\n this.saveContext('Created worktree for Codex instance', {\n action: 'worktree_created',\n path: worktreePath,\n branch: this.config.branch,\n });\n }\n }\n\n this.loadContext();\n\n process.env['CODEX_INSTANCE_ID'] = this.config.instanceId;\n if (this.config.worktreePath)\n process.env['CODEX_WORKTREE_PATH'] = this.config.worktreePath;\n\n console.log(chalk.gray(`\uD83E\uDD16 Instance ID: ${this.config.instanceId}`));\n console.log(chalk.gray(`\uD83D\uDCC1 Working in: ${process.cwd()}`));\n\n console.log();\n console.log(chalk.gray('Starting Codex...'));\n console.log(chalk.gray('\u2500'.repeat(42)));\n\n const codexBin = (() => {\n try {\n execSync('which codex', { stdio: 'ignore' });\n return 'codex';\n } catch {}\n try {\n execSync('which codex-cli', { stdio: 'ignore' });\n return 'codex-cli';\n } catch {}\n return null;\n })();\n\n if (!codexBin) {\n console.error(\n chalk.red('\u274C Codex CLI not found in PATH (codex or codex-cli).')\n );\n process.exit(1);\n return;\n }\n\n const child = spawn(codexBin, codexArgs, {\n stdio: 'inherit',\n env: process.env,\n });\n\n child.on('exit', (code) => {\n this.saveContext('Codex session ended', {\n action: 'session_end',\n exitCode: code,\n });\n if (this.config.tracingEnabled) {\n const summary = trace.getExecutionSummary();\n console.log();\n console.log(chalk.gray('\u2500'.repeat(42)));\n console.log(chalk.blue('Debug Trace Summary:'));\n console.log(chalk.gray(summary));\n }\n if (this.config.worktreePath) {\n console.log();\n console.log(chalk.gray('\u2500'.repeat(42)));\n console.log(chalk.blue('Session ended in worktree:'));\n console.log(chalk.gray(` ${this.config.worktreePath}`));\n }\n process.exit(code || 0);\n });\n\n process.on('SIGINT', () => {\n this.saveContext('Codex session interrupted', {\n action: 'session_interrupt',\n });\n child.kill('SIGINT');\n });\n\n process.on('SIGTERM', () => {\n this.saveContext('Codex session terminated', {\n action: 'session_terminate',\n });\n child.kill('SIGTERM');\n });\n }\n}\n\nprogram\n .name('codex-sm')\n .description('Codex with StackMemory context and optional worktree isolation')\n .version('1.0.0')\n .option('-w, --worktree', 'Create isolated worktree for this instance')\n .option('-a, --auto', 'Automatically detect and apply best settings')\n .option('-b, --branch <name>', 'Specify branch name for worktree')\n .option('-t, --task <desc>', 'Task description for context')\n .option('--no-context', 'Disable StackMemory context integration')\n .option('--no-trace', 'Disable debug tracing (enabled by default)')\n .option('--verbose-trace', 'Enable verbose debug tracing with full details')\n .helpOption('-h, --help', 'Display help')\n .allowUnknownOption(true)\n .action(async (_options) => {\n const codexSM = new CodexSM();\n const args = process.argv.slice(2);\n await codexSM.run(args);\n });\n\nif (require.main === module) {\n program.parse(process.argv);\n}\n"],
|
|
5
|
-
"mappings": ";AAOA,SAAS,OAAO,UAAU,oBAAoB;AAC9C,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,YAAY,QAAQ;AACpB,SAAS,eAAe;AACxB,SAAS,MAAM,cAAc;AAC7B,OAAO,WAAW;AAClB,SAAS,mBAAmB,aAAa;
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n\n/**\n * codex-sm: Codex wrapper with StackMemory and worktree integration\n * Automatically manages context persistence, optional worktree isolation, and tracing\n */\n\nimport { spawn, execSync, execFileSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport { program } from 'commander';\nimport { v4 as uuidv4 } from 'uuid';\nimport chalk from 'chalk';\nimport { initializeTracing, trace } from '../core/trace/index.js';\n\ninterface CodexConfig {\n instanceId: string;\n worktreePath?: string;\n useWorktree: boolean;\n contextEnabled: boolean;\n branch?: string;\n task?: string;\n tracingEnabled: boolean;\n verboseTracing: boolean;\n codexBin?: string;\n}\n\nclass CodexSM {\n private config: CodexConfig;\n private stackmemoryPath: string;\n\n constructor() {\n this.config = {\n instanceId: this.generateInstanceId(),\n useWorktree: false,\n contextEnabled: true,\n tracingEnabled: true,\n verboseTracing: false,\n };\n\n this.stackmemoryPath = this.findStackMemory();\n }\n\n private generateInstanceId(): string {\n return uuidv4().substring(0, 8);\n }\n\n private findStackMemory(): string {\n const possiblePaths = [\n path.join(os.homedir(), '.stackmemory', 'bin', 'stackmemory'),\n '/usr/local/bin/stackmemory',\n '/opt/homebrew/bin/stackmemory',\n 'stackmemory',\n ];\n for (const smPath of possiblePaths) {\n try {\n execFileSync('which', [smPath], { stdio: 'ignore' });\n return smPath;\n } catch {\n // continue\n }\n }\n return 'stackmemory';\n }\n\n private isGitRepo(): boolean {\n try {\n execSync('git rev-parse --git-dir', { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n }\n\n private getCurrentBranch(): string {\n try {\n return execSync('git rev-parse --abbrev-ref HEAD', {\n encoding: 'utf8',\n }).trim();\n } catch {\n return 'main';\n }\n }\n\n private hasUncommittedChanges(): boolean {\n try {\n const status = execSync('git status --porcelain', { encoding: 'utf8' });\n return status.length > 0;\n } catch {\n return false;\n }\n }\n\n private resolveCodexBin(): string | null {\n // 1) CLI option\n if (this.config.codexBin && this.config.codexBin.trim()) {\n return this.config.codexBin.trim();\n }\n // 2) Environment override\n const envBin = process.env['CODEX_BIN'];\n if (envBin && envBin.trim()) {\n return envBin.trim();\n }\n // 3) Detect on PATH\n try {\n execSync('which codex', { stdio: 'ignore' });\n return 'codex';\n } catch {}\n try {\n execSync('which codex-cli', { stdio: 'ignore' });\n return 'codex-cli';\n } catch {}\n return null;\n }\n\n private setupWorktree(): string | null {\n if (!this.config.useWorktree || !this.isGitRepo()) return null;\n\n console.log(chalk.blue('\uD83C\uDF33 Setting up isolated worktree...'));\n\n const timestamp = new Date()\n .toISOString()\n .replace(/[:.]/g, '-')\n .substring(0, 19);\n const branch =\n this.config.branch ||\n `codex-${this.config.task || 'work'}-${timestamp}-${this.config.instanceId}`;\n const repoName = path.basename(process.cwd());\n const worktreePath = path.join(\n path.dirname(process.cwd()),\n `${repoName}--${branch}`\n );\n\n try {\n const cmd = `git worktree add -b \"${branch}\" \"${worktreePath}\"`;\n execSync(cmd, { stdio: 'inherit' });\n\n console.log(chalk.green(`Worktree created: ${worktreePath}`));\n console.log(chalk.gray(` Branch: ${branch}`));\n\n const configPath = path.join(worktreePath, '.codex-instance.json');\n const configData = {\n instanceId: this.config.instanceId,\n worktreePath,\n branch,\n task: this.config.task,\n created: new Date().toISOString(),\n parentRepo: process.cwd(),\n };\n fs.writeFileSync(configPath, JSON.stringify(configData, null, 2));\n\n const envFiles = ['.env', '.env.local', '.mise.toml', '.tool-versions'];\n for (const file of envFiles) {\n const srcPath = path.join(process.cwd(), file);\n if (fs.existsSync(srcPath))\n fs.copyFileSync(srcPath, path.join(worktreePath, file));\n }\n\n return worktreePath;\n } catch (err: unknown) {\n console.error(chalk.red('Failed to create worktree:'), err);\n return null;\n }\n }\n\n private saveContext(\n message: string,\n metadata: Record<string, unknown> = {}\n ): void {\n if (!this.config.contextEnabled) return;\n try {\n const contextData = {\n message,\n metadata: {\n ...metadata,\n instanceId: this.config.instanceId,\n worktree: this.config.worktreePath,\n timestamp: new Date().toISOString(),\n },\n };\n const cmd = `${this.stackmemoryPath} context save --json '${JSON.stringify(contextData)}'`;\n execSync(cmd, { stdio: 'ignore' });\n } catch {\n // ignore\n }\n }\n\n private loadContext(): void {\n if (!this.config.contextEnabled) return;\n try {\n console.log(chalk.blue('\uD83D\uDCDA Loading previous context...'));\n const cmd = `${this.stackmemoryPath} context list --limit 5 --format json`;\n const output = execSync(cmd, { encoding: 'utf8' });\n const contexts = JSON.parse(output);\n if (Array.isArray(contexts) && contexts.length > 0) {\n console.log(chalk.gray('Recent context loaded:'));\n contexts.forEach(\n (ctx: { message: string; metadata?: { timestamp?: string } }) => {\n console.log(\n chalk.gray(` - ${ctx.message} (${ctx.metadata?.timestamp})`)\n );\n }\n );\n }\n } catch {\n // ignore\n }\n }\n\n private suggestWorktreeMode(): void {\n if (this.hasUncommittedChanges()) {\n console.log(chalk.yellow('WARNING: Uncommitted changes detected'));\n console.log(\n chalk.gray(' Consider using --worktree to work in isolation')\n );\n }\n }\n\n public async run(args: string[]): Promise<void> {\n const codexArgs: string[] = [];\n let i = 0;\n while (i < args.length) {\n const arg = args[i];\n switch (arg) {\n case '--worktree':\n case '-w':\n this.config.useWorktree = true;\n break;\n case '--no-context':\n this.config.contextEnabled = false;\n break;\n case '--no-trace':\n this.config.tracingEnabled = false;\n break;\n case '--verbose-trace':\n this.config.verboseTracing = true;\n break;\n case '--branch':\n case '-b':\n i++;\n this.config.branch = args[i];\n break;\n case '--task':\n case '-t':\n i++;\n this.config.task = args[i];\n break;\n case '--codex-bin':\n i++;\n this.config.codexBin = args[i];\n process.env['CODEX_BIN'] = this.config.codexBin;\n break;\n case '--auto':\n case '-a':\n if (this.isGitRepo()) {\n this.config.useWorktree = this.hasUncommittedChanges();\n }\n break;\n default:\n codexArgs.push(arg);\n }\n i++;\n }\n\n if (this.config.tracingEnabled) {\n process.env['DEBUG_TRACE'] = 'true';\n process.env['STACKMEMORY_DEBUG'] = 'true';\n process.env['TRACE_OUTPUT'] = 'file';\n process.env['TRACE_MASK_SENSITIVE'] = 'true';\n if (this.config.verboseTracing) {\n process.env['TRACE_VERBOSITY'] = 'full';\n process.env['TRACE_PARAMS'] = 'true';\n process.env['TRACE_RESULTS'] = 'true';\n process.env['TRACE_MEMORY'] = 'true';\n } else {\n process.env['TRACE_VERBOSITY'] = 'summary';\n process.env['TRACE_PARAMS'] = 'true';\n process.env['TRACE_RESULTS'] = 'false';\n }\n initializeTracing();\n trace.command(\n 'codex-sm',\n {\n instanceId: this.config.instanceId,\n worktree: this.config.useWorktree,\n task: this.config.task,\n },\n async () => {}\n );\n }\n\n console.log(chalk.blue('\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557'));\n console.log(chalk.blue('\u2551 Codex + StackMemory + Worktree \u2551'));\n console.log(chalk.blue('\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D'));\n console.log();\n\n if (this.isGitRepo()) {\n const branch = this.getCurrentBranch();\n console.log(chalk.gray(`\uD83D\uDCCD Current branch: ${branch}`));\n if (!this.config.useWorktree) this.suggestWorktreeMode();\n }\n\n if (this.config.useWorktree) {\n const worktreePath = this.setupWorktree();\n if (worktreePath) {\n this.config.worktreePath = worktreePath;\n process.chdir(worktreePath);\n this.saveContext('Created worktree for Codex instance', {\n action: 'worktree_created',\n path: worktreePath,\n branch: this.config.branch,\n });\n }\n }\n\n this.loadContext();\n\n process.env['CODEX_INSTANCE_ID'] = this.config.instanceId;\n if (this.config.worktreePath)\n process.env['CODEX_WORKTREE_PATH'] = this.config.worktreePath;\n\n console.log(chalk.gray(`\uD83E\uDD16 Instance ID: ${this.config.instanceId}`));\n console.log(chalk.gray(`\uD83D\uDCC1 Working in: ${process.cwd()}`));\n\n console.log();\n console.log(chalk.gray('Starting Codex...'));\n console.log(chalk.gray('\u2500'.repeat(42)));\n\n const codexBin = this.resolveCodexBin();\n \n if (!codexBin) {\n console.error(chalk.red('\u274C Codex CLI not found.'));\n console.log(\n chalk.gray(\n ' Install codex/codex-cli or set an override:\\n' +\n ' export CODEX_BIN=/path/to/codex\\n' +\n ' codex-sm --help\\n\\n' +\n ' Ensure PATH includes npm global bin (npm bin -g).'\n )\n );\n process.exit(1);\n return;\n }\n\n const child = spawn(codexBin, codexArgs, {\n stdio: 'inherit',\n env: process.env,\n });\n\n child.on('error', (err: NodeJS.ErrnoException) => {\n console.error(chalk.red('\u274C Failed to launch Codex CLI.'));\n if (err.code === 'ENOENT') {\n console.error(\n chalk.gray(\n ' Not found. Set CODEX_BIN or install codex/codex-cli on PATH.'\n )\n );\n } else if (err.code === 'EPERM' || err.code === 'EACCES') {\n console.error(\n chalk.gray(\n ' Permission/sandbox issue. Try running outside a sandbox or set CODEX_BIN.'\n )\n );\n } else {\n console.error(chalk.gray(` ${err.message}`));\n }\n process.exit(1);\n });\n\n child.on('exit', (code) => {\n this.saveContext('Codex session ended', {\n action: 'session_end',\n exitCode: code,\n });\n if (this.config.tracingEnabled) {\n const summary = trace.getExecutionSummary();\n console.log();\n console.log(chalk.gray('\u2500'.repeat(42)));\n console.log(chalk.blue('Debug Trace Summary:'));\n console.log(chalk.gray(summary));\n }\n if (this.config.worktreePath) {\n console.log();\n console.log(chalk.gray('\u2500'.repeat(42)));\n console.log(chalk.blue('Session ended in worktree:'));\n console.log(chalk.gray(` ${this.config.worktreePath}`));\n }\n process.exit(code || 0);\n });\n\n process.on('SIGINT', () => {\n this.saveContext('Codex session interrupted', {\n action: 'session_interrupt',\n });\n child.kill('SIGINT');\n });\n\n process.on('SIGTERM', () => {\n this.saveContext('Codex session terminated', {\n action: 'session_terminate',\n });\n child.kill('SIGTERM');\n });\n }\n}\n\nprogram\n .name('codex-sm')\n .description('Codex with StackMemory context and optional worktree isolation')\n .version('1.0.0')\n .option('-w, --worktree', 'Create isolated worktree for this instance')\n .option('-a, --auto', 'Automatically detect and apply best settings')\n .option('-b, --branch <name>', 'Specify branch name for worktree')\n .option('-t, --task <desc>', 'Task description for context')\n .option('--codex-bin <path>', 'Path to codex/codex-cli (or use CODEX_BIN)')\n .option('--no-context', 'Disable StackMemory context integration')\n .option('--no-trace', 'Disable debug tracing (enabled by default)')\n .option('--verbose-trace', 'Enable verbose debug tracing with full details')\n .helpOption('-h, --help', 'Display help')\n .allowUnknownOption(true)\n .action(async (_options) => {\n const codexSM = new CodexSM();\n const args = process.argv.slice(2);\n await codexSM.run(args);\n });\n\n// ESM-safe CLI entry\nprogram.parse(process.argv);\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
6
|
+
import { Pool } from "pg";
|
|
7
|
+
function loadConfig() {
|
|
8
|
+
const cfgDir = join(homedir(), ".stackmemory");
|
|
9
|
+
if (!existsSync(cfgDir)) mkdirSync(cfgDir, { recursive: true });
|
|
10
|
+
const cfgPath = join(cfgDir, "config.json");
|
|
11
|
+
let cfg = {};
|
|
12
|
+
try {
|
|
13
|
+
if (existsSync(cfgPath)) cfg = JSON.parse(readFileSync(cfgPath, "utf-8"));
|
|
14
|
+
} catch {
|
|
15
|
+
}
|
|
16
|
+
return { cfgDir, cfgPath, cfg };
|
|
17
|
+
}
|
|
18
|
+
async function testPostgres(url) {
|
|
19
|
+
try {
|
|
20
|
+
const pool = new Pool({ connectionString: url });
|
|
21
|
+
const r = await pool.query("SELECT 1");
|
|
22
|
+
await pool.end();
|
|
23
|
+
return !!r;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function registerLogoutCommand(program) {
|
|
29
|
+
program.command("logout").description("Log out from hosted database (switch back to local)").action(async () => {
|
|
30
|
+
const { cfgDir, cfgPath, cfg } = loadConfig();
|
|
31
|
+
cfg.database = { mode: "local" };
|
|
32
|
+
writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
|
|
33
|
+
const envPath = join(cfgDir, "railway.env");
|
|
34
|
+
try {
|
|
35
|
+
if (existsSync(envPath)) unlinkSync(envPath);
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
console.log(chalk.green("\u2713 Switched to local storage and cleared hosted credentials."));
|
|
39
|
+
console.log(chalk.gray("Start the server without DATABASE_URL to use local SQLite."));
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function registerDbCommands(program) {
|
|
43
|
+
const db = program.command("db").description("Database operations");
|
|
44
|
+
db.command("switch").description("Switch between local and hosted database").option("--mode <local|hosted>", "Target mode (local or hosted)").action(async (opts) => {
|
|
45
|
+
const { cfgDir, cfgPath, cfg } = loadConfig();
|
|
46
|
+
let mode = opts.mode;
|
|
47
|
+
if (!mode || mode !== "local" && mode !== "hosted") {
|
|
48
|
+
const ans = await inquirer.prompt([
|
|
49
|
+
{
|
|
50
|
+
type: "list",
|
|
51
|
+
name: "mode",
|
|
52
|
+
message: "Select database mode:",
|
|
53
|
+
choices: [
|
|
54
|
+
{ name: "Local (SQLite, free)", value: "local" },
|
|
55
|
+
{ name: "Hosted (Postgres, paid)", value: "hosted" }
|
|
56
|
+
],
|
|
57
|
+
default: cfg.database?.mode || "local"
|
|
58
|
+
}
|
|
59
|
+
]);
|
|
60
|
+
mode = ans.mode;
|
|
61
|
+
}
|
|
62
|
+
if (mode === "local") {
|
|
63
|
+
cfg.database = { mode: "local" };
|
|
64
|
+
writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
|
|
65
|
+
const envPath = join(cfgDir, "railway.env");
|
|
66
|
+
try {
|
|
67
|
+
if (existsSync(envPath)) unlinkSync(envPath);
|
|
68
|
+
} catch {
|
|
69
|
+
}
|
|
70
|
+
console.log(chalk.green("\u2713 Switched to local storage."));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const { openSignup } = await inquirer.prompt([
|
|
74
|
+
{ type: "confirm", name: "openSignup", message: "Open hosted signup/login page?", default: false }
|
|
75
|
+
]);
|
|
76
|
+
if (openSignup) {
|
|
77
|
+
try {
|
|
78
|
+
const mod = await import("open");
|
|
79
|
+
await mod.default("https://stackmemory.ai/hosted");
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const { url } = await inquirer.prompt([
|
|
84
|
+
{
|
|
85
|
+
type: "password",
|
|
86
|
+
name: "url",
|
|
87
|
+
message: "Paste your hosted DATABASE_URL (postgres://...)",
|
|
88
|
+
validate: (input) => input.startsWith("postgres://") || input.startsWith("postgresql://") ? true : "Must start with postgres:// or postgresql://"
|
|
89
|
+
}
|
|
90
|
+
]);
|
|
91
|
+
process.stdout.write("Testing connection... ");
|
|
92
|
+
const ok = await testPostgres(url);
|
|
93
|
+
if (!ok) {
|
|
94
|
+
console.log(chalk.red("failed"));
|
|
95
|
+
console.log(chalk.red("\u2717 Could not connect to Postgres with provided URL."));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
console.log(chalk.green("ok"));
|
|
99
|
+
cfg.database = { mode: "hosted", url };
|
|
100
|
+
writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
|
|
101
|
+
const envFile = join(cfgDir, "railway.env");
|
|
102
|
+
writeFileSync(envFile, `# StackMemory hosted DB
|
|
103
|
+
DATABASE_URL=${url}
|
|
104
|
+
`);
|
|
105
|
+
console.log(chalk.green("\u2713 Switched to hosted database."));
|
|
106
|
+
console.log(chalk.gray("Tip: export DATABASE_URL before starting the server."));
|
|
107
|
+
});
|
|
108
|
+
db.command("status").description("Show current database mode and connection status").action(async () => {
|
|
109
|
+
const { cfgDir, cfg } = loadConfig();
|
|
110
|
+
const mode = cfg.database?.mode || "local";
|
|
111
|
+
console.log(`Mode: ${mode}`);
|
|
112
|
+
if (mode === "hosted") {
|
|
113
|
+
const url = process.env.DATABASE_URL || cfg.database?.url || "";
|
|
114
|
+
if (!url) {
|
|
115
|
+
console.log(chalk.yellow('DATABASE_URL not set and not found in config. Run "stackmemory login".'));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const masked = maskDsn(url);
|
|
119
|
+
process.stdout.write(`Hosted DSN: ${masked} \u2192 testing... `);
|
|
120
|
+
const ok = await testPostgres(url);
|
|
121
|
+
console.log(ok ? chalk.green("ok") : chalk.red("failed"));
|
|
122
|
+
} else {
|
|
123
|
+
const sqlitePath = join(cfgDir, "railway.db");
|
|
124
|
+
const exists = existsSync(sqlitePath);
|
|
125
|
+
console.log(`Local SQLite path: ${sqlitePath} (${exists ? "exists" : "will be created at first run"})`);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function maskDsn(url) {
|
|
130
|
+
try {
|
|
131
|
+
const u = new URL(url);
|
|
132
|
+
if (u.password) u.password = "***";
|
|
133
|
+
if (u.username) u.username = "***";
|
|
134
|
+
return u.toString();
|
|
135
|
+
} catch {
|
|
136
|
+
return url.replace(/:\\?[^@]*@/, ":***@");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
registerDbCommands,
|
|
141
|
+
registerLogoutCommand
|
|
142
|
+
};
|
|
143
|
+
//# sourceMappingURL=db.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/cli/commands/db.ts"],
|
|
4
|
+
"sourcesContent": ["import { Command } from 'commander';\nimport inquirer from 'inquirer';\nimport chalk from 'chalk';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'fs';\nimport { Pool } from 'pg';\n\ninterface ConfigShape {\n version?: string;\n setupCompleted?: string;\n features?: any;\n paths?: any;\n database?: { mode?: 'local' | 'hosted'; url?: string };\n}\n\nfunction loadConfig(): { cfgDir: string; cfgPath: string; cfg: ConfigShape } {\n const cfgDir = join(homedir(), '.stackmemory');\n if (!existsSync(cfgDir)) mkdirSync(cfgDir, { recursive: true });\n const cfgPath = join(cfgDir, 'config.json');\n let cfg: ConfigShape = {};\n try { if (existsSync(cfgPath)) cfg = JSON.parse(readFileSync(cfgPath, 'utf-8')); } catch {}\n return { cfgDir, cfgPath, cfg };\n}\n\nasync function testPostgres(url: string): Promise<boolean> {\n try {\n const pool = new Pool({ connectionString: url });\n const r = await pool.query('SELECT 1');\n await pool.end();\n return !!r;\n } catch {\n return false;\n }\n}\n\nexport function registerLogoutCommand(program: Command): void {\n program\n .command('logout')\n .description('Log out from hosted database (switch back to local)')\n .action(async () => {\n const { cfgDir, cfgPath, cfg } = loadConfig();\n cfg.database = { mode: 'local' };\n writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));\n const envPath = join(cfgDir, 'railway.env');\n try { if (existsSync(envPath)) unlinkSync(envPath); } catch {}\n console.log(chalk.green('\u2713 Switched to local storage and cleared hosted credentials.'));\n console.log(chalk.gray('Start the server without DATABASE_URL to use local SQLite.'));\n });\n}\n\nexport function registerDbCommands(program: Command): void {\n const db = program.command('db').description('Database operations');\n\n db\n .command('switch')\n .description('Switch between local and hosted database')\n .option('--mode <local|hosted>', 'Target mode (local or hosted)')\n .action(async (opts) => {\n const { cfgDir, cfgPath, cfg } = loadConfig();\n let mode: 'local' | 'hosted' = opts.mode;\n if (!mode || (mode !== 'local' && mode !== 'hosted')) {\n const ans = await inquirer.prompt([\n {\n type: 'list',\n name: 'mode',\n message: 'Select database mode:',\n choices: [\n { name: 'Local (SQLite, free)', value: 'local' },\n { name: 'Hosted (Postgres, paid)', value: 'hosted' },\n ],\n default: cfg.database?.mode || 'local',\n },\n ]);\n mode = ans.mode;\n }\n\n if (mode === 'local') {\n cfg.database = { mode: 'local' };\n writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));\n const envPath = join(cfgDir, 'railway.env');\n try { if (existsSync(envPath)) unlinkSync(envPath); } catch {}\n console.log(chalk.green('\u2713 Switched to local storage.'));\n return;\n }\n\n // Hosted flow\n const { openSignup } = await inquirer.prompt([\n { type: 'confirm', name: 'openSignup', message: 'Open hosted signup/login page?', default: false },\n ]);\n if (openSignup) {\n try { const mod = await import('open'); await mod.default('https://stackmemory.ai/hosted'); } catch {}\n }\n const { url } = await inquirer.prompt([\n {\n type: 'password',\n name: 'url',\n message: 'Paste your hosted DATABASE_URL (postgres://...)',\n validate: (input: string) =>\n input.startsWith('postgres://') || input.startsWith('postgresql://')\n ? true\n : 'Must start with postgres:// or postgresql://',\n },\n ]);\n process.stdout.write('Testing connection... ');\n const ok = await testPostgres(url);\n if (!ok) {\n console.log(chalk.red('failed'));\n console.log(chalk.red('\u2717 Could not connect to Postgres with provided URL.'));\n return;\n }\n console.log(chalk.green('ok'));\n\n cfg.database = { mode: 'hosted', url };\n writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));\n const envFile = join(cfgDir, 'railway.env');\n writeFileSync(envFile, `# StackMemory hosted DB\\nDATABASE_URL=${url}\\n`);\n console.log(chalk.green('\u2713 Switched to hosted database.'));\n console.log(chalk.gray('Tip: export DATABASE_URL before starting the server.'));\n });\n\n db\n .command('status')\n .description('Show current database mode and connection status')\n .action(async () => {\n const { cfgDir, cfg } = loadConfig();\n const mode = cfg.database?.mode || 'local';\n console.log(`Mode: ${mode}`);\n\n if (mode === 'hosted') {\n const url = process.env.DATABASE_URL || cfg.database?.url || '';\n if (!url) {\n console.log(chalk.yellow('DATABASE_URL not set and not found in config. Run \"stackmemory login\".'));\n return;\n }\n const masked = maskDsn(url);\n process.stdout.write(`Hosted DSN: ${masked} \u2192 testing... `);\n const ok = await testPostgres(url);\n console.log(ok ? chalk.green('ok') : chalk.red('failed'));\n } else {\n const sqlitePath = join(cfgDir, 'railway.db');\n const exists = existsSync(sqlitePath);\n console.log(`Local SQLite path: ${sqlitePath} (${exists ? 'exists' : 'will be created at first run'})`);\n }\n });\n}\n\nfunction maskDsn(url: string): string {\n try {\n const u = new URL(url);\n if (u.password) u.password = '***';\n if (u.username) u.username = '***';\n return u.toString();\n } catch {\n return url.replace(/:\\\\?[^@]*@/, ':***@');\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|