#!/usr/bin/env node
import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
process.env["STACKMEMORY_CLI"] = "true";
import "dotenv/config";
import { initializeTracing, trace } from "../core/trace/index.js";
initializeTracing();
import { program } from "commander";
import { logger } from "../core/monitoring/logger.js";
import { FrameManager } from "../core/context/frame-manager.js";
import { sessionManager, FrameQueryMode } from "../core/session/index.js";
import { sharedContextLayer } from "../core/context/shared-context-layer.js";
import { UpdateChecker } from "../core/utils/update-checker.js";
import { ProgressTracker } from "../core/monitoring/progress-tracker.js";
import { registerProjectCommands } from "./commands/projects.js";
import { registerLinearCommands } from "./commands/linear.js";
import { createSessionCommands } from "./commands/session.js";
import { registerWorktreeCommands } from "./commands/worktree.js";
import { registerOnboardingCommand } from "./commands/onboard.js";
import { createTaskCommands } from "./commands/tasks.js";
import { createSearchCommand } from "./commands/search.js";
import { createLogCommand } from "./commands/log.js";
import { createContextCommands } from "./commands/context.js";
import { createConfigCommand } from "./commands/config.js";
import { createHandoffCommand } from "./commands/handoff.js";
import {
  createDecisionCommand,
  createMemoryCommand
} from "./commands/decision.js";
import { createSkillsCommand } from "./commands/skills.js";
import { createTestCommand } from "./commands/test.js";
import clearCommand from "./commands/clear.js";
import createWorkflowCommand from "./commands/workflow.js";
import monitorCommand from "./commands/monitor.js";
import qualityCommand from "./commands/quality.js";
import createRalphCommand from "./commands/ralph.js";
import serviceCommand from "./commands/service.js";
import { registerLoginCommand } from "./commands/login.js";
import { registerSignupCommand } from "./commands/signup.js";
import { registerLogoutCommand, registerDbCommands } from "./commands/db.js";
import { createSweepCommand } from "./commands/sweep.js";
import { createHooksCommand } from "./commands/hooks.js";
import { createShellCommand } from "./commands/shell.js";
import { createAPICommand } from "./commands/api.js";
import { createCleanupProcessesCommand } from "./commands/cleanup-processes.js";
import { createAutoBackgroundCommand } from "./commands/auto-background.js";
import { createSMSNotifyCommand } from "./commands/sms-notify.js";
import { createSettingsCommand } from "./commands/settings.js";
import { createRetrievalCommands } from "./commands/retrieval.js";
import { createDiscoveryCommands } from "./commands/discovery.js";
import { ProjectManager } from "../core/projects/project-manager.js";
import Database from "better-sqlite3";
import { join } from "path";
import { existsSync, mkdirSync } from "fs";
import inquirer from "inquirer";
import chalk from "chalk";
import {
  loadStorageConfig,
  enableChromaDB,
  getStorageModeDescription
} from "../core/config/storage-config.js";
import { loadSMSConfig } from "../hooks/sms-notify.js";
import { spawn } from "child_process";
import { homedir } from "os";
import { createRequire } from "module";
const require2 = createRequire(import.meta.url);
const pkg = require2("../../package.json");
const VERSION = pkg.version;
UpdateChecker.checkForUpdates(VERSION, true).catch(() => {
});
async function startNotificationServices() {
  try {
    const config = loadSMSConfig();
    if (!config.enabled) return;
    const WEBHOOK_PORT = 3456;
    let webhookStarted = false;
    let ngrokStarted = false;
    const webhookRunning = await fetch(
      `http://localhost:${WEBHOOK_PORT}/health`
    ).then((r) => r.ok).catch(() => false);
    if (!webhookRunning) {
      const webhookPath = join(__dirname, "../hooks/sms-webhook.js");
      const webhookProcess = spawn("node", [webhookPath], {
        detached: true,
        stdio: "ignore",
        env: { ...process.env, SMS_WEBHOOK_PORT: String(WEBHOOK_PORT) }
      });
      webhookProcess.unref();
      webhookStarted = true;
    }
    const ngrokRunning = await fetch("http://localhost:4040/api/tunnels").then((r) => r.ok).catch(() => false);
    if (!ngrokRunning) {
      const ngrokProcess = spawn("ngrok", ["http", String(WEBHOOK_PORT)], {
        detached: true,
        stdio: "ignore"
      });
      ngrokProcess.unref();
      ngrokStarted = true;
    }
    if (webhookStarted || ngrokStarted) {
      setTimeout(async () => {
        try {
          const tunnels = await fetch("http://localhost:4040/api/tunnels").then(
            (r) => r.json()
          );
          const publicUrl = tunnels?.tunnels?.[0]?.public_url;
          if (publicUrl) {
            const configDir = join(homedir(), ".stackmemory");
            const configPath = join(configDir, "ngrok-url.txt");
            const { writeFileSync, mkdirSync: mkdirSync2, existsSync: existsSync2 } = await import("fs");
            if (!existsSync2(configDir)) {
              mkdirSync2(configDir, { recursive: true });
            }
            writeFileSync(configPath, publicUrl);
            console.log(
              chalk.gray(`[notify] Webhook: ${publicUrl}/sms/incoming`)
            );
          }
        } catch {
        }
      }, 4e3);
    }
  } catch {
  }
}
startNotificationServices();
program.name("stackmemory").description(
  "Lossless memory runtime for AI coding tools - organizes context as a call stack instead of linear chat logs, with team collaboration and infinite retention"
).version(VERSION);
program.command("init").description(
  `Initialize StackMemory in current project

Storage Modes:
  SQLite (default): Local only, fast, no setup required
  ChromaDB (hybrid): Adds semantic search and cloud backup, requires API key`
).option("--sqlite", "Use SQLite-only storage (default, skip prompts)").option(
  "--chromadb",
  "Enable ChromaDB for semantic search (prompts for API key)"
).option("--skip-storage-prompt", "Skip storage configuration prompt").action(async (options) => {
  try {
    const projectRoot = process.cwd();
    const dbDir = join(projectRoot, ".stackmemory");
    if (!existsSync(dbDir)) {
      mkdirSync(dbDir, { recursive: true });
    }
    let storageConfig = loadStorageConfig();
    const isFirstTimeSetup = !storageConfig.chromadb.enabled && storageConfig.mode === "sqlite";
    if (options.sqlite || options.skipStoragePrompt) {
      console.log(chalk.gray("Using SQLite-only storage mode."));
    } else if (options.chromadb) {
      await promptAndEnableChromaDB();
    } else if (isFirstTimeSetup && process.stdin.isTTY) {
      console.log(chalk.cyan("\nStorage Configuration"));
      console.log(chalk.gray("StackMemory supports two storage modes:\n"));
      console.log(chalk.white("  SQLite (default):"));
      console.log(chalk.gray("    - Local storage only"));
      console.log(chalk.gray("    - Fast and simple"));
      console.log(chalk.gray("    - No external dependencies\n"));
      console.log(chalk.white("  ChromaDB (hybrid):"));
      console.log(chalk.gray("    - Semantic search across your context"));
      console.log(chalk.gray("    - Cloud backup capability"));
      console.log(chalk.gray("    - Requires ChromaDB API key\n"));
      const { enableChroma } = await inquirer.prompt([
        {
          type: "confirm",
          name: "enableChroma",
          message: "Enable ChromaDB for semantic search? (requires API key)",
          default: false
        }
      ]);
      if (enableChroma) {
        await promptAndEnableChromaDB();
      } else {
        console.log(chalk.gray("Using SQLite-only storage mode."));
      }
    }
    const dbPath = join(dbDir, "context.db");
    const db = new Database(dbPath);
    new FrameManager(db, "cli-project");
    logger.info("StackMemory initialized successfully", { projectRoot });
    console.log(
      chalk.green("\n[OK] StackMemory initialized in"),
      projectRoot
    );
    storageConfig = loadStorageConfig();
    console.log(chalk.gray(`Storage mode: ${getStorageModeDescription()}`));
    db.close();
  } catch (error) {
    logger.error("Failed to initialize StackMemory", error);
    console.error(
      chalk.red("[ERROR] Initialization failed:"),
      error.message
    );
    process.exit(1);
  }
});
async function promptAndEnableChromaDB() {
  const answers = await inquirer.prompt([
    {
      type: "password",
      name: "apiKey",
      message: "Enter your ChromaDB API key:",
      validate: (input) => {
        if (!input || input.trim().length === 0) {
          return "API key is required for ChromaDB";
        }
        return true;
      }
    },
    {
      type: "input",
      name: "apiUrl",
      message: "ChromaDB API URL (press Enter for default):",
      default: "https://api.trychroma.com"
    }
  ]);
  enableChromaDB({
    apiKey: answers.apiKey,
    apiUrl: answers.apiUrl
  });
  console.log(chalk.green("[OK] ChromaDB enabled for semantic search."));
  console.log(
    chalk.gray("API key saved to ~/.stackmemory/storage-config.json")
  );
}
program.command("status").description("Show current StackMemory status").option("--all", "Show all active frames across sessions").option("--project", "Show all active frames in current project").option("--session <id>", "Show frames for specific session").action(async (options) => {
  return trace.command("stackmemory-status", options, async () => {
    try {
      const projectRoot = process.cwd();
      const dbPath = join(projectRoot, ".stackmemory", "context.db");
      if (!existsSync(dbPath)) {
        console.log(
          '\u274C StackMemory not initialized. Run "stackmemory init" first.'
        );
        return;
      }
      await UpdateChecker.checkForUpdates(VERSION);
      await sessionManager.initialize();
      await sharedContextLayer.initialize();
      const session = await sessionManager.getOrCreateSession({
        projectPath: projectRoot,
        sessionId: options.session
      });
      const contextDiscovery = await sharedContextLayer.autoDiscoverContext();
      if (contextDiscovery.hasSharedContext && contextDiscovery.sessionCount > 1) {
        console.log(`
\u{1F4A1} Shared Context Available:`);
        console.log(
          `   ${contextDiscovery.sessionCount} sessions with shared context`
        );
        if (contextDiscovery.recentPatterns.length > 0) {
          console.log(`   Recent patterns:`);
          contextDiscovery.recentPatterns.slice(0, 3).forEach((p) => {
            console.log(
              `     \u2022 ${p.type}: ${p.pattern.slice(0, 50)} (${p.frequency}x)`
            );
          });
        }
        if (contextDiscovery.lastDecisions.length > 0) {
          console.log(
            `   Last decision: ${contextDiscovery.lastDecisions[0].decision.slice(0, 60)}`
          );
        }
      }
      const db = new Database(dbPath);
      const frameManager = new FrameManager(db, session.projectId);
      if (options.all) {
        frameManager.setQueryMode(FrameQueryMode.ALL_ACTIVE);
      } else if (options.project) {
        frameManager.setQueryMode(FrameQueryMode.PROJECT_ACTIVE);
      }
      const activeFrames = frameManager.getActiveFramePath();
      const stackDepth = frameManager.getStackDepth();
      const totalStats = db.prepare(
        `
        SELECT 
          COUNT(*) as total_frames,
          SUM(CASE WHEN state = 'active' THEN 1 ELSE 0 END) as active_frames,
          SUM(CASE WHEN state = 'closed' THEN 1 ELSE 0 END) as closed_frames,
          COUNT(DISTINCT run_id) as total_sessions
        FROM frames
        WHERE project_id = ?
      `
      ).get(session.projectId);
      const contextCount = db.prepare(
        `
        SELECT COUNT(*) as count FROM contexts
      `
      ).get();
      const eventCount = db.prepare(
        `
        SELECT COUNT(*) as count FROM events e
        JOIN frames f ON e.frame_id = f.frame_id
        WHERE f.project_id = ?
      `
      ).get(session.projectId);
      console.log("\u{1F4CA} StackMemory Status:");
      console.log(
        `   Session: ${session.sessionId.slice(0, 8)} (${session.state}, ${Math.round((Date.now() - session.startedAt) / 1e3 / 60)}min old)`
      );
      console.log(`   Project: ${session.projectId}`);
      if (session.branch) {
        console.log(`   Branch: ${session.branch}`);
      }
      console.log(`
   Database Statistics (this project):`);
      console.log(
        `     Frames: ${totalStats.total_frames || 0} (${totalStats.active_frames || 0} active, ${totalStats.closed_frames || 0} closed)`
      );
      console.log(`     Events: ${eventCount.count || 0}`);
      console.log(`     Sessions: ${totalStats.total_sessions || 0}`);
      console.log(
        `     Cached contexts: ${contextCount.count || 0} (global)`
      );
      const recentFrames = db.prepare(
        `
        SELECT name, type, state, datetime(created_at, 'unixepoch') as created
        FROM frames
        WHERE project_id = ?
        ORDER BY created_at DESC
        LIMIT 3
      `
      ).all(session.projectId);
      if (recentFrames.length > 0) {
        console.log(`
   Recent Activity:`);
        recentFrames.forEach((f) => {
          const stateIcon = f.state === "active" ? "\u{1F7E2}" : "\u26AB";
          console.log(
            `     ${stateIcon} ${f.name} [${f.type}] - ${f.created}`
          );
        });
      }
      console.log(`
   Current Session:`);
      console.log(`     Stack depth: ${stackDepth}`);
      console.log(`     Active frames: ${activeFrames.length}`);
      if (activeFrames.length > 0) {
        activeFrames.forEach((frame, i) => {
          const indent = "     " + "  ".repeat(frame.depth || i);
          const prefix = i === 0 ? "\u2514\u2500" : "  \u2514\u2500";
          console.log(`${indent}${prefix} ${frame.name} [${frame.type}]`);
        });
      }
      if (!options.all && !options.project) {
        const otherSessions = await sessionManager.listSessions({
          projectId: session.projectId,
          state: "active"
        });
        const otherActive = otherSessions.filter(
          (s) => s.sessionId !== session.sessionId
        );
        if (otherActive.length > 0) {
          console.log(`
   Other Active Sessions (same project):`);
          otherActive.forEach((s) => {
            const age = Math.round(
              (Date.now() - s.lastActiveAt) / 1e3 / 60 / 60
            );
            console.log(
              `     - ${s.sessionId.slice(0, 8)}: ${s.branch || "main"}, ${age}h old`
            );
          });
          console.log(`
   Tip: Use --all to see frames across sessions`);
        }
      }
      db.close();
    } catch (error) {
      logger.error("Failed to get status", error);
      console.error("\u274C Status check failed:", error.message);
      process.exit(1);
    }
  });
});
program.command("update-check").description("Check for StackMemory updates").action(async () => {
  try {
    console.log("\u{1F50D} Checking for updates...");
    await UpdateChecker.forceCheck(VERSION);
  } catch (error) {
    logger.error("Update check failed", error);
    console.error("\u274C Update check failed:", error.message);
    process.exit(1);
  }
});
program.command("progress").description("Show current progress and recent changes").action(async () => {
  try {
    const projectRoot = process.cwd();
    const dbPath = join(projectRoot, ".stackmemory", "context.db");
    if (!existsSync(dbPath)) {
      console.log(
        '\u274C StackMemory not initialized. Run "stackmemory init" first.'
      );
      return;
    }
    const progress = new ProgressTracker(projectRoot);
    console.log(progress.getSummary());
  } catch (error) {
    logger.error("Failed to show progress", error);
    console.error("\u274C Failed to show progress:", error.message);
    process.exit(1);
  }
});
program.command("mcp-server").description("Start StackMemory MCP server for Claude Desktop").option("-p, --project <path>", "Project root directory", process.cwd()).action(async (options) => {
  try {
    const { runMCPServer } = await import("../integrations/mcp/server.js");
    process.env["PROJECT_ROOT"] = options.project;
    console.log("\u{1F680} Starting StackMemory MCP Server...");
    console.log(`   Project: ${options.project}`);
    console.log(`   Version: ${VERSION}`);
    UpdateChecker.checkForUpdates(VERSION, true).catch(() => {
    });
    await runMCPServer();
  } catch (error) {
    logger.error("Failed to start MCP server", error);
    console.error("\u274C MCP server failed:", error.message);
    process.exit(1);
  }
});
program.command("context:test").description("Test context persistence by creating sample frames").action(async () => {
  try {
    const projectRoot = process.cwd();
    const dbPath = join(projectRoot, ".stackmemory", "context.db");
    if (!existsSync(dbPath)) {
      console.log(
        '\u274C StackMemory not initialized. Run "stackmemory init" first.'
      );
      return;
    }
    const db = new Database(dbPath);
    const frameManager = new FrameManager(db, "cli-project");
    console.log("\u{1F4DD} Creating test context frames...");
    const rootFrame = frameManager.createFrame({
      type: "task",
      name: "Test Session",
      inputs: { test: true, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
    });
    const taskFrame = frameManager.createFrame({
      type: "subtask",
      name: "Sample Task",
      inputs: { description: "Testing context persistence" },
      parentFrameId: rootFrame
    });
    const commandFrame = frameManager.createFrame({
      type: "tool_scope",
      name: "test-command",
      inputs: { args: ["--test"] },
      parentFrameId: taskFrame
    });
    frameManager.addEvent(
      "observation",
      {
        message: "Test event recorded"
      },
      commandFrame
    );
    console.log("\u2705 Test frames created!");
    console.log(`\u{1F4CA} Stack depth: ${frameManager.getStackDepth()}`);
    console.log(
      `\u{1F504} Active frames: ${frameManager.getActiveFramePath().length}`
    );
    frameManager.closeFrame(commandFrame);
    console.log(
      `\u{1F4CA} After closing command frame: depth = ${frameManager.getStackDepth()}`
    );
    db.close();
  } catch (error) {
    logger.error("Test context failed", error);
    console.error("\u274C Test failed:", error.message);
    process.exit(1);
  }
});
registerOnboardingCommand(program);
registerSignupCommand(program);
registerLoginCommand(program);
registerLogoutCommand(program);
registerDbCommands(program);
registerProjectCommands(program);
registerWorktreeCommands(program);
registerLinearCommands(program);
program.addCommand(createSessionCommands());
program.addCommand(createTaskCommands());
program.addCommand(createSearchCommand());
program.addCommand(createLogCommand());
program.addCommand(createContextCommands());
program.addCommand(createConfigCommand());
program.addCommand(createHandoffCommand());
program.addCommand(createDecisionCommand());
program.addCommand(createMemoryCommand());
program.addCommand(createSkillsCommand());
program.addCommand(createTestCommand());
program.addCommand(clearCommand);
program.addCommand(createWorkflowCommand());
program.addCommand(monitorCommand);
program.addCommand(qualityCommand);
program.addCommand(createRalphCommand());
program.addCommand(serviceCommand);
program.addCommand(createSweepCommand());
program.addCommand(createHooksCommand());
program.addCommand(createShellCommand());
program.addCommand(createAPICommand());
program.addCommand(createCleanupProcessesCommand());
program.addCommand(createAutoBackgroundCommand());
program.addCommand(createSMSNotifyCommand());
program.addCommand(createSettingsCommand());
program.addCommand(createRetrievalCommands());
program.addCommand(createDiscoveryCommands());
program.command("dashboard").description("Display monitoring dashboard in terminal").option("-w, --watch", "Auto-refresh dashboard").option("-i, --interval <seconds>", "Refresh interval in seconds", "5").action(async (options) => {
  const { dashboardCommand } = await import("./commands/dashboard.js");
  await dashboardCommand.handler(options);
});
if (process.argv.length > 2) {
  const manager = ProjectManager.getInstance();
  manager.detectProject().catch(() => {
  });
}
const isMainModule = import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("/stackmemory") || process.argv[1]?.endsWith("index.ts") || process.argv[1]?.includes("tsx");
if (isMainModule) {
  program.parse();
}
export {
  program
};
//# sourceMappingURL=index.js.map
