#!/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 { config as loadDotenv } from "dotenv";
loadDotenv({ quiet: true });
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/index.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 { createSessionCommands } from "./commands/session.js";
import { isFeatureEnabled, isLocalOnly } from "../core/config/feature-flags.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 {
  createCaptureCommand,
  createRestoreCommand,
  createAutoCaptureCommand
} from "./commands/handoff.js";
import {
  createDecisionCommand,
  createMemoryCommand
} from "./commands/decision.js";
import clearCommand from "./commands/clear.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 { createHooksCommand } from "./commands/hooks.js";
import { createDaemonCommand } from "./commands/daemon.js";
import { createSweepCommand } from "./commands/sweep.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 { createSettingsCommand } from "./commands/settings.js";
import { createRetrievalCommands } from "./commands/retrieval.js";
import { createDiscoveryCommands } from "./commands/discovery.js";
import { createModelCommand } from "./commands/model.js";
import { registerSetupCommands } from "./commands/setup.js";
import { createPingCommand } from "./commands/ping.js";
import chalk from "chalk";
import * as fs from "fs";
import * as path from "path";
import { filterPending } from "../integrations/mcp/pending-utils.js";
import { ProjectManager } from "../core/projects/project-manager.js";
import { join } from "path";
import { existsSync, mkdirSync } from "fs";
import inquirer from "inquirer";
import { enableChromaDB } from "../core/config/storage-config.js";
import { spawn } from "child_process";
import { homedir } from "os";
import { createRequire } from "module";
import { fileURLToPath } from "url";
import * as pathModule from "path";
const localRequire = createRequire(import.meta.url);
const currentFilePath = fileURLToPath(import.meta.url);
const currentDirPath = pathModule.dirname(currentFilePath);
function findPackageJson() {
  let dir = currentDirPath;
  for (let i = 0; i < 5; i++) {
    const pkgPath = pathModule.join(dir, "package.json");
    try {
      return localRequire(pkgPath);
    } catch {
      dir = pathModule.dirname(dir);
    }
  }
  return { version: "0.0.0" };
}
const VERSION = findPackageJson().version;
async function openDatabase(dbPath) {
  const { default: Database } = await import("better-sqlite3");
  return new Database(dbPath);
}
function isTestEnv() {
  return process.env["VITEST"] === "true" || process.env["NODE_ENV"] === "test" || process.env["STACKMEMORY_TEST_SKIP_DB"] === "1";
}
UpdateChecker.checkForUpdates(VERSION, true).catch(() => {
});
async function startNotificationServices() {
  if (isLocalOnly() || !isFeatureEnabled("whatsapp")) return;
  try {
    const { loadSMSConfig } = await import("../hooks/sms-notify.js");
    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 (zero-config by default)"
).option("-i, --interactive", "Interactive mode with configuration prompts").option(
  "--chromadb",
  "Enable ChromaDB for semantic search (prompts for API key)"
).option("--daemon", "Start the background daemon after initialization").action(async (options) => {
  try {
    const projectRoot = process.cwd();
    const dbDir = join(projectRoot, ".stackmemory");
    const alreadyInit = existsSync(join(dbDir, "context.db"));
    if (alreadyInit && !options.interactive) {
      console.log(chalk.yellow("StackMemory already initialized."));
      console.log(chalk.gray("Run with --interactive to reconfigure."));
      return;
    }
    if (!existsSync(dbDir)) {
      mkdirSync(dbDir, { recursive: true });
    }
    if (options.chromadb) {
      await promptAndEnableChromaDB();
    } else if (options.interactive && process.stdin.isTTY) {
      console.log(chalk.cyan("\nStorage Configuration"));
      console.log(
        chalk.gray("SQLite (default) is fast and requires no setup.")
      );
      console.log(
        chalk.gray("ChromaDB adds semantic search but requires an API key.\n")
      );
      const { enableChroma } = await inquirer.prompt([
        {
          type: "confirm",
          name: "enableChroma",
          message: "Enable ChromaDB for semantic search?",
          default: false
        }
      ]);
      if (enableChroma) {
        await promptAndEnableChromaDB();
      }
    }
    const dbPath = join(dbDir, "context.db");
    if (!isTestEnv()) {
      const db = await openDatabase(dbPath);
      new FrameManager(db, "cli-project");
      db.close();
    }
    logger.info("StackMemory initialized successfully", { projectRoot });
    console.log(chalk.green("\n[OK] StackMemory initialized"));
    console.log(chalk.gray(`    Project: ${projectRoot}`));
    console.log(chalk.gray(`    Storage: SQLite (local)`));
    if (options.daemon) {
      console.log(chalk.cyan("\nInstalling background service..."));
      try {
        const { installServiceSilent } = await import("./commands/service.js");
        const success = await installServiceSilent();
        if (success) {
          console.log(chalk.green("[OK] Guardian service installed"));
          console.log(chalk.gray("    Auto-starts on login"));
          console.log(
            chalk.gray("    Check status: stackmemory service status")
          );
        } else {
          console.log(chalk.yellow("[WARN] Could not install service"));
          console.log(chalk.gray("  Run: stackmemory service install"));
        }
      } catch {
        console.log(chalk.yellow("[WARN] Could not install service"));
        console.log(chalk.gray("  Run: stackmemory service install"));
      }
    }
    console.log(chalk.cyan("\nNext steps:"));
    console.log(
      chalk.white("  1. stackmemory setup-mcp") + chalk.gray("  # Configure Claude Code integration")
    );
    console.log(
      chalk.white("  2. stackmemory status") + chalk.gray("     # Check status")
    );
    console.log(
      chalk.white("  3. stackmemory doctor") + chalk.gray("     # Diagnose issues")
    );
  } catch (error) {
    logger.error("Failed to initialize StackMemory", error);
    console.error(chalk.red("\n[ERROR] Initialization failed"));
    console.error(chalk.gray(`  Reason: ${error.message}`));
    console.error(
      chalk.gray(
        "  Fix: Ensure you have write permissions to the current directory"
      )
    );
    console.error(chalk.gray("  Run: stackmemory doctor"));
    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;
      }
      if (isTestEnv()) {
        console.log("\u{1F4CA} StackMemory Status (test mode):");
        console.log("   Frames: n/a");
        console.log("   Events: n/a");
        console.log("   Sessions: n/a");
        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 = await openDatabase(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("mcp-remote").description(
  "Start StackMemory Remote MCP server (HTTP/SSE) for Claude.ai web"
).option("-p, --port <number>", "Port to listen on", "3847").option("-d, --project <path>", "Project root directory", process.cwd()).action(async (options) => {
  try {
    const { runRemoteMCPServer } = await import("../integrations/mcp/remote-server.js");
    const port = parseInt(options.port, 10);
    console.log("Starting StackMemory Remote MCP Server...");
    console.log(`   Project: ${options.project}`);
    console.log(`   Version: ${VERSION}`);
    console.log("");
    await runRemoteMCPServer(port, options.project);
    console.log("");
    console.log("For Claude.ai web connector:");
    console.log(`  URL: http://localhost:${port}/sse`);
    console.log("");
    console.log("For external access (ngrok):");
    console.log(`  ngrok http ${port}`);
    console.log("  Then use the ngrok URL + /sse in Claude.ai");
  } catch (error) {
    logger.error("Failed to start remote MCP server", error);
    console.error("Remote 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;
    }
    if (isTestEnv()) {
      console.log("\u{1F4DD} [test] Skipping DB write in context:test");
      return;
    }
    const db = await openDatabase(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);
if (isFeatureEnabled("linear")) {
  import("./commands/linear.js").then(({ registerLinearCommands }) => registerLinearCommands(program)).catch(() => {
  });
}
program.addCommand(createSessionCommands());
program.addCommand(createTaskCommands());
program.addCommand(createSearchCommand());
program.addCommand(createLogCommand());
program.addCommand(createContextCommands());
program.addCommand(createConfigCommand());
program.addCommand(createCaptureCommand());
program.addCommand(createRestoreCommand());
program.addCommand(createAutoCaptureCommand());
program.addCommand(createDecisionCommand());
program.addCommand(createMemoryCommand());
program.addCommand(clearCommand);
program.addCommand(serviceCommand);
program.addCommand(createHooksCommand());
const lazyCommands = [];
if (isFeatureEnabled("skills")) {
  lazyCommands.push(
    import("./commands/skills.js").then(
      ({ createSkillsCommand }) => program.addCommand(createSkillsCommand())
    ).catch(() => {
    })
  );
}
if (isFeatureEnabled("ralph")) {
  lazyCommands.push(
    import("./commands/ralph.js").then(
      ({ default: createRalphCommand }) => program.addCommand(createRalphCommand())
    ).catch(() => {
    })
  );
}
program.addCommand(createDaemonCommand());
program.addCommand(createSweepCommand());
program.addCommand(createShellCommand());
program.addCommand(createAPICommand());
program.addCommand(createCleanupProcessesCommand());
program.addCommand(createAutoBackgroundCommand());
program.addCommand(createSettingsCommand());
program.addCommand(createPingCommand());
if (isFeatureEnabled("whatsapp")) {
  lazyCommands.push(
    import("./commands/sms-notify.js").then(
      ({ createSMSNotifyCommand }) => program.addCommand(createSMSNotifyCommand())
    ).catch(() => {
    })
  );
}
program.addCommand(createRetrievalCommands());
program.addCommand(createDiscoveryCommands());
program.addCommand(createModelCommand());
registerSetupCommands(program);
program.command("mm-spike").description(
  "Run multi-agent planning/implementation spike (planner/implementer/critic)"
).option(
  "-t, --task <desc>",
  "Task description",
  "Add multi-agent spike harness"
).option(
  "--planner-model <name>",
  "Claude model for planning",
  "claude-sonnet-4-20250514"
).option(
  "--reviewer-model <name>",
  "Claude model for review",
  "claude-sonnet-4-20250514"
).option(
  "--execute",
  "Execute implementer (codex-sm) instead of dry-run",
  false
).option("--implementer <name>", "codex|claude", "codex").option("--max-iters <n>", "Retry loop iterations", "2").option("--audit-dir <path>", "Persist spike results to directory").option("--record-frame", "Record as real frame with anchors", false).option("--record", "Record plan & critique into StackMemory context", false).option("--json", "Emit single JSON result (UI-friendly)", false).option("--quiet", "Minimal output (default)", true).option("--verbose", "Verbose sectioned output", false).option(
  "--log",
  "Pretty print interaction log (planner/implementer/critic)",
  false
).action(async (opts) => {
  try {
    const { runSpike } = await import("../orchestrators/multimodal/harness.js");
    const result = await runSpike(
      {
        task: opts.task,
        repoPath: process.cwd()
      },
      {
        plannerModel: opts.plannerModel,
        reviewerModel: opts.reviewerModel,
        implementer: opts.implementer,
        maxIters: parseInt(opts.maxIters),
        dryRun: !opts.execute,
        auditDir: opts.auditDir,
        recordFrame: Boolean(opts.recordFrame),
        record: Boolean(opts.record)
      }
    );
    if (opts.log) {
      printInteractionLog(
        {
          task: opts.task,
          plannerModel: opts.plannerModel,
          reviewerModel: opts.reviewerModel,
          implementer: opts.implementer,
          execute: Boolean(opts.execute)
        },
        result
      );
      return;
    }
    if (opts.json) {
      console.log(JSON.stringify(result));
      return;
    }
    if (opts.verbose) {
      console.log("\n=== Plan ===");
      console.log(JSON.stringify(result.plan, null, 2));
      console.log("\n=== Iterations ===");
      (result.iterations || []).forEach((it, i) => {
        console.log(`
[Attempt ${i + 1}] ${it.command}`);
        console.log("OK:", it.ok);
        console.log("Critique:", JSON.stringify(it.critique));
      });
      console.log("\n=== Implementation ===");
      console.log(JSON.stringify(result.implementation, null, 2));
      console.log("\n=== Critique ===");
      console.log(JSON.stringify(result.critique, null, 2));
    } else if (!opts.quiet) {
      console.log(
        `Plan steps: ${result.plan.steps.length}, Approved: ${result.critique.approved}`
      );
    }
  } catch (error) {
    console.error("mm-spike failed:", error.message);
    process.exit(1);
  }
});
program.command("build").description(
  "Plan + code: planner (Claude), implementer (Codex/Claude), critic (Claude) with optional log/json output"
).argument("[task]", "Task description (positional)").option(
  "-t, --task <desc>",
  "Task description (required if no positional arg)"
).option(
  "--planner-model <name>",
  "Claude model for planning",
  "claude-sonnet-4-20250514"
).option(
  "--reviewer-model <name>",
  "Claude model for review",
  "claude-sonnet-4-20250514"
).option("--execute", "Execute implementer (default: true)", true).option("--dry-run", "Skip execution, show commands only").option("--implementer <name>", "codex|claude", "codex").option("--max-iters <n>", "Retry loop iterations", "2").option("--audit-dir <path>", "Persist spike results to directory").option("--record-frame", "Record as real frame with anchors").option("--record", "Record plan & critique into StackMemory context").option("--json", "Emit single JSON result (UI-friendly)").option("--quiet", "Minimal output").option("--verbose", "Verbose sectioned output").option("--log", "Pretty print interaction log (default: true)", true).option("-C, --cwd <path>", "Working directory for implementation").action(async (taskArg, opts) => {
  try {
    const task = typeof taskArg === "string" && taskArg.length > 0 ? taskArg : opts.task;
    if (!task) {
      console.error(
        chalk.red(
          "Error: Task description required. Provide as argument or --task option."
        )
      );
      console.error(
        chalk.gray('  Example: stackmemory build "Add user authentication"')
      );
      process.exit(1);
    }
    const { runSpike } = await import("../orchestrators/multimodal/harness.js");
    const dryRun = opts.dryRun === true || opts.execute === false;
    const findGitRoot = (startDir) => {
      let dir = startDir;
      while (dir !== "/") {
        if (existsSync(join(dir, ".git"))) {
          return dir;
        }
        dir = path.dirname(dir);
      }
      return startDir;
    };
    const repoPath = opts.cwd ? path.resolve(opts.cwd) : findGitRoot(process.cwd());
    const result = await runSpike(
      { task, repoPath },
      {
        plannerModel: opts.plannerModel,
        reviewerModel: opts.reviewerModel,
        implementer: opts.implementer,
        maxIters: parseInt(opts.maxIters),
        dryRun,
        auditDir: opts.auditDir,
        recordFrame: Boolean(opts.recordFrame),
        record: Boolean(opts.record)
      }
    );
    if (opts.log) {
      printInteractionLog(
        {
          task,
          plannerModel: opts.plannerModel,
          reviewerModel: opts.reviewerModel,
          implementer: opts.implementer,
          execute: !dryRun
        },
        result
      );
      return;
    }
    if (opts.json) {
      console.log(JSON.stringify(result));
      return;
    }
    if (opts.verbose) {
      console.log("\n=== Plan ===");
      console.log(JSON.stringify(result.plan, null, 2));
      console.log("\n=== Iterations ===");
      (result.iterations || []).forEach((it, i) => {
        console.log(`
[Attempt ${i + 1}] ${it.command}`);
        console.log("OK:", it.ok);
        console.log("Critique:", JSON.stringify(it.critique));
      });
      console.log("\n=== Implementation ===");
      console.log(JSON.stringify(result.implementation, null, 2));
      console.log("\n=== Critique ===");
      console.log(JSON.stringify(result.critique, null, 2));
    } else if (!opts.quiet) {
      console.log(
        `Plan steps: ${result.plan.steps.length}, Approved: ${result.critique.approved}`
      );
    }
  } catch (error) {
    console.error("build failed:", error.message);
    process.exit(1);
  }
});
function printInteractionLog(meta, result) {
  const divider = chalk.gray(
    "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
  );
  console.log(chalk.cyan.bold("\nPlan & Code Session"));
  console.log(`${chalk.gray("Task:")} ${meta.task}`);
  console.log(`${chalk.gray("Planner:")} ${meta.plannerModel}`);
  console.log(`${chalk.gray("Reviewer:")} ${meta.reviewerModel}`);
  console.log(
    `${chalk.gray("Implementer:")} ${meta.implementer} ${meta.execute ? chalk.green("(execute)") : chalk.yellow("(dry-run)")}`
  );
  console.log(divider);
  if (result.plan) {
    console.log(
      chalk.bold("Plan Summary: "),
      result.plan.summary || "(no summary)"
    );
    const steps = result.plan.steps.slice(0, 6);
    if (steps.length) {
      console.log(chalk.bold("\nSteps:"));
      steps.forEach((s, idx) => {
        console.log(`${chalk.gray(String(idx + 1) + ".")} ${s.title || s.id}`);
        const ac = s.acceptanceCriteria || [];
        if (ac.length) {
          ac.slice(0, 3).forEach((c) => console.log(chalk.gray(`   - ${c}`)));
          if (ac.length > 3) console.log(chalk.gray("   - ..."));
        }
      });
    }
    if (result.plan.risks?.length) {
      console.log(chalk.bold("\nRisks:"));
      result.plan.risks.slice(0, 5).forEach((r) => console.log(chalk.gray(` - ${r}`)));
    }
  }
  console.log(`
${divider}`);
  const iters = result.iterations || [];
  if (iters.length) {
    iters.forEach((it, i) => {
      console.log(chalk.magenta(`Attempt ${i + 1}`));
      console.log(`${chalk.gray("Command:")} ${it.command}`);
      console.log(
        `${chalk.gray("OK:")} ${it.ok ? chalk.green("true") : chalk.red("false")}`
      );
      const issues = it.critique?.issues || [];
      const sugg = it.critique?.suggestions || [];
      if (issues.length) {
        console.log(chalk.bold("Issues:"));
        issues.slice(0, 5).forEach((x) => console.log(chalk.red(` - ${x}`)));
      }
      if (sugg.length) {
        console.log(chalk.bold("Suggestions:"));
        sugg.slice(0, 5).forEach((x) => console.log(chalk.yellow(` - ${x}`)));
      }
      console.log(divider);
    });
  }
  const approved = result.critique?.approved ?? false;
  console.log(
    `${chalk.bold("Final:")} ${approved ? chalk.green("Approved") : chalk.yellow("Needs changes")}`
  );
  console.log("");
}
program.command("pending:list").description(
  "List pending approval-gated plans (from .stackmemory/build/pending.json)"
).option("--task-contains <substr>", "Filter tasks containing this substring").option("--older-than-ms <number>", "Only items older than this age (ms)").option("--newer-than-ms <number>", "Only items newer than this age (ms)").option("--sort <asc|desc>", "Sort by createdAt", "desc").option("--limit <number>", "Max items to return", "20").option("--pretty", "Pretty-print JSON output", false).action(async (opts) => {
  try {
    const storePath = path.join(
      process.cwd(),
      ".stackmemory",
      "build",
      "pending.json"
    );
    let pending = {};
    if (fs.existsSync(storePath)) {
      try {
        pending = JSON.parse(fs.readFileSync(storePath, "utf-8"));
      } catch {
      }
    }
    const items = Object.entries(pending).map(([approvalId, data]) => ({
      approvalId,
      task: data?.task ?? "",
      createdAt: Number(data?.createdAt || 0) || null
    }));
    const filters = {
      taskContains: opts.taskContains,
      olderThanMs: opts.olderThanMs ? Number(opts.olderThanMs) : void 0,
      newerThanMs: opts.newerThanMs ? Number(opts.newerThanMs) : void 0,
      sort: opts.sort || void 0,
      limit: opts.limit ? Number(opts.limit) : void 0
    };
    const out = filterPending(items, filters);
    console.log(
      JSON.stringify({ ok: true, pending: out }, null, opts.pretty ? 2 : 0)
    );
  } catch (error) {
    console.error(
      JSON.stringify({ ok: false, error: error.message })
    );
    process.exit(1);
  }
});
program.command("plan").description("Generate an implementation plan (no code execution)").option("-t, --task <desc>", "Task description", "Plan a small change").option(
  "--planner-model <name>",
  "Claude model for planning",
  "claude-sonnet-4-20250514"
).option("--json", "Emit JSON (default)", true).option("--pretty", "Pretty-print JSON", false).option(
  "--compact",
  "Compact output (summary + step titles + criteria)",
  false
).action(async (opts) => {
  try {
    const { runPlanOnly } = await import("../orchestrators/multimodal/harness.js");
    const plan = await runPlanOnly(
      { task: opts.task, repoPath: process.cwd() },
      { plannerModel: opts.plannerModel }
    );
    const typedPlan = plan;
    const compacted = opts.compact ? {
      summary: typedPlan?.summary,
      steps: Array.isArray(typedPlan?.steps) ? typedPlan.steps.map((s) => ({
        id: s.id,
        title: s.title,
        acceptanceCriteria: s.acceptanceCriteria
      })) : [],
      risks: typedPlan?.risks
    } : plan;
    const payload = JSON.stringify(compacted, null, opts.pretty ? 2 : 0);
    console.log(payload);
  } catch (error) {
    console.error("plan failed:", error.message);
    process.exit(1);
  }
});
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) {
  try {
    const manager = ProjectManager.getInstance();
    manager.detectProject().catch(() => {
    });
  } 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) {
  Promise.all(lazyCommands).then(() => program.parse());
}
export {
  program
};
//# sourceMappingURL=index.js.map
