#!/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);
import { config as loadDotenv } from "dotenv";
loadDotenv({ override: true, debug: false });
import { spawn, execSync, execFileSync } from "child_process";
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
import { program } from "commander";
import { v4 as uuidv4 } from "uuid";
import chalk from "chalk";
import { initializeTracing, trace } from "../core/trace/index.js";
const DEFAULT_SM_CONFIG = {
  defaultWorktree: false,
  defaultTracing: true
};
function getConfigPath() {
  return path.join(os.homedir(), ".stackmemory", "opencode-sm.json");
}
function loadSMConfig() {
  try {
    const configPath = getConfigPath();
    if (fs.existsSync(configPath)) {
      const content = fs.readFileSync(configPath, "utf8");
      return { ...DEFAULT_SM_CONFIG, ...JSON.parse(content) };
    }
  } catch {
  }
  return { ...DEFAULT_SM_CONFIG };
}
function saveSMConfig(config) {
  const configPath = getConfigPath();
  const dir = path.dirname(configPath);
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
}
class OpencodeSM {
  config;
  stackmemoryPath;
  smConfig;
  constructor() {
    this.smConfig = loadSMConfig();
    this.config = {
      instanceId: this.generateInstanceId(),
      useWorktree: this.smConfig.defaultWorktree,
      contextEnabled: true,
      tracingEnabled: this.smConfig.defaultTracing,
      verboseTracing: false,
      sessionStartTime: Date.now()
    };
    this.stackmemoryPath = this.findStackMemory();
  }
  generateInstanceId() {
    return uuidv4().substring(0, 8);
  }
  findStackMemory() {
    const possiblePaths = [
      path.join(os.homedir(), ".stackmemory", "bin", "stackmemory"),
      "/usr/local/bin/stackmemory",
      "/opt/homebrew/bin/stackmemory",
      "stackmemory"
    ];
    for (const smPath of possiblePaths) {
      try {
        execFileSync("which", [smPath], { stdio: "ignore" });
        return smPath;
      } catch {
      }
    }
    return "stackmemory";
  }
  isGitRepo() {
    try {
      execSync("git rev-parse --git-dir", { stdio: "ignore" });
      return true;
    } catch {
      return false;
    }
  }
  getCurrentBranch() {
    try {
      return execSync("git rev-parse --abbrev-ref HEAD", {
        encoding: "utf8"
      }).trim();
    } catch {
      return "main";
    }
  }
  hasUncommittedChanges() {
    try {
      const status = execSync("git status --porcelain", { encoding: "utf8" });
      return status.length > 0;
    } catch {
      return false;
    }
  }
  resolveOpencodeBin() {
    if (this.config.opencodeBin && this.config.opencodeBin.trim()) {
      return this.config.opencodeBin.trim();
    }
    const envBin = process.env["OPENCODE_BIN"];
    if (envBin && envBin.trim()) return envBin.trim();
    const possiblePaths = [
      path.join(os.homedir(), ".opencode", "bin", "opencode"),
      "/usr/local/bin/opencode",
      "/opt/homebrew/bin/opencode"
    ];
    for (const binPath of possiblePaths) {
      if (fs.existsSync(binPath)) {
        return binPath;
      }
    }
    try {
      execSync("which opencode", { stdio: "ignore" });
      return "opencode";
    } catch {
    }
    return null;
  }
  setupWorktree() {
    if (!this.config.useWorktree || !this.isGitRepo()) {
      return null;
    }
    console.log(chalk.blue("Setting up isolated worktree..."));
    const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
    const branch = this.config.branch || `opencode-${this.config.task || "work"}-${timestamp}-${this.config.instanceId}`;
    const repoName = path.basename(process.cwd());
    const worktreePath = path.join(
      path.dirname(process.cwd()),
      `${repoName}--${branch}`
    );
    try {
      const cmd = `git worktree add -b "${branch}" "${worktreePath}"`;
      execSync(cmd, { stdio: "inherit" });
      console.log(chalk.green(`Worktree created: ${worktreePath}`));
      console.log(chalk.gray(`Branch: ${branch}`));
      const configPath = path.join(worktreePath, ".opencode-instance.json");
      const configData = {
        instanceId: this.config.instanceId,
        worktreePath,
        branch,
        task: this.config.task,
        created: (/* @__PURE__ */ new Date()).toISOString(),
        parentRepo: process.cwd()
      };
      fs.writeFileSync(configPath, JSON.stringify(configData, null, 2));
      const envFiles = [".env", ".env.local", ".mise.toml", ".tool-versions"];
      for (const file of envFiles) {
        const srcPath = path.join(process.cwd(), file);
        if (fs.existsSync(srcPath)) {
          fs.copyFileSync(srcPath, path.join(worktreePath, file));
        }
      }
      return worktreePath;
    } catch (err) {
      console.error(chalk.red("Failed to create worktree:"), err);
      return null;
    }
  }
  saveContext(message, metadata = {}) {
    if (!this.config.contextEnabled) return;
    try {
      const contextData = {
        message,
        metadata: {
          ...metadata,
          instanceId: this.config.instanceId,
          worktree: this.config.worktreePath,
          timestamp: (/* @__PURE__ */ new Date()).toISOString(),
          tool: "opencode"
        }
      };
      const cmd = `${this.stackmemoryPath} context save --json '${JSON.stringify(contextData)}'`;
      execSync(cmd, { stdio: "ignore" });
    } catch {
    }
  }
  loadContext() {
    if (!this.config.contextEnabled) return;
    try {
      console.log(chalk.blue("Loading previous context..."));
      const cmd = `${this.stackmemoryPath} context show`;
      const output = execSync(cmd, {
        encoding: "utf8",
        stdio: ["pipe", "pipe", "pipe"]
      });
      const lines = output.trim().split("\n").filter((l) => l.trim());
      if (lines.length > 3) {
        console.log(chalk.gray("Context stack loaded"));
      }
    } catch {
    }
  }
  detectMultipleInstances() {
    try {
      const lockDir = path.join(process.cwd(), ".opencode-worktree-locks");
      if (!fs.existsSync(lockDir)) return false;
      const locks = fs.readdirSync(lockDir).filter((f) => f.endsWith(".lock"));
      const activeLocks = locks.filter((lockFile) => {
        const lockPath = path.join(lockDir, lockFile);
        const lockData = JSON.parse(fs.readFileSync(lockPath, "utf8"));
        const lockAge = Date.now() - new Date(lockData.created).getTime();
        return lockAge < 24 * 60 * 60 * 1e3;
      });
      return activeLocks.length > 0;
    } catch {
      return false;
    }
  }
  suggestWorktreeMode() {
    if (this.hasUncommittedChanges()) {
      console.log(chalk.yellow("Uncommitted changes detected"));
      console.log(chalk.gray("Consider using --worktree to work in isolation"));
    }
    if (this.detectMultipleInstances()) {
      console.log(chalk.yellow("Other OpenCode instances detected"));
      console.log(
        chalk.gray("Using --worktree is recommended to avoid conflicts")
      );
    }
  }
  async run(args) {
    const opencodeArgs = [];
    let i = 0;
    while (i < args.length) {
      const arg = args[i];
      switch (arg) {
        case "--worktree":
        case "-w":
          this.config.useWorktree = true;
          break;
        case "--no-worktree":
        case "-W":
          this.config.useWorktree = false;
          break;
        case "--no-context":
          this.config.contextEnabled = false;
          break;
        case "--no-trace":
          this.config.tracingEnabled = false;
          break;
        case "--verbose-trace":
          this.config.verboseTracing = true;
          break;
        case "--branch":
        case "-b":
          i++;
          this.config.branch = args[i];
          break;
        case "--task":
        case "-t":
          i++;
          this.config.task = args[i];
          break;
        case "--opencode-bin":
          i++;
          this.config.opencodeBin = args[i];
          process.env["OPENCODE_BIN"] = this.config.opencodeBin;
          break;
        case "--auto":
        case "-a":
          if (this.isGitRepo()) {
            this.config.useWorktree = this.hasUncommittedChanges() || this.detectMultipleInstances();
          }
          break;
        default:
          opencodeArgs.push(arg);
      }
      i++;
    }
    if (this.config.tracingEnabled) {
      process.env["DEBUG_TRACE"] = "true";
      process.env["STACKMEMORY_DEBUG"] = "true";
      process.env["TRACE_OUTPUT"] = "file";
      process.env["TRACE_MASK_SENSITIVE"] = "true";
      if (this.config.verboseTracing) {
        process.env["TRACE_VERBOSITY"] = "full";
        process.env["TRACE_PARAMS"] = "true";
        process.env["TRACE_RESULTS"] = "true";
        process.env["TRACE_MEMORY"] = "true";
      } else {
        process.env["TRACE_VERBOSITY"] = "summary";
        process.env["TRACE_PARAMS"] = "true";
        process.env["TRACE_RESULTS"] = "false";
      }
      initializeTracing();
      trace.command(
        "opencode-sm",
        {
          instanceId: this.config.instanceId,
          worktree: this.config.useWorktree,
          task: this.config.task
        },
        async () => {
        }
      );
    }
    console.log(chalk.magenta("OpenCode + StackMemory"));
    console.log();
    if (this.isGitRepo()) {
      const branch = this.getCurrentBranch();
      console.log(chalk.gray(`Branch: ${branch}`));
      if (!this.config.useWorktree) {
        this.suggestWorktreeMode();
      }
    }
    if (this.config.useWorktree) {
      const worktreePath = this.setupWorktree();
      if (worktreePath) {
        this.config.worktreePath = worktreePath;
        process.chdir(worktreePath);
        this.saveContext("Created worktree for OpenCode instance", {
          action: "worktree_created",
          path: worktreePath,
          branch: this.config.branch
        });
      }
    }
    this.loadContext();
    process.env["OPENCODE_INSTANCE_ID"] = this.config.instanceId;
    if (this.config.worktreePath) {
      process.env["OPENCODE_WORKTREE_PATH"] = this.config.worktreePath;
    }
    console.log(chalk.gray(`Instance: ${this.config.instanceId}`));
    console.log(chalk.gray(`Working in: ${process.cwd()}`));
    if (this.config.tracingEnabled) {
      console.log(
        chalk.gray(`Tracing enabled (logs to ~/.stackmemory/traces/)`)
      );
    }
    console.log();
    console.log(chalk.gray("Starting OpenCode..."));
    console.log(chalk.gray("-".repeat(40)));
    const opencodeBin = this.resolveOpencodeBin();
    if (!opencodeBin) {
      console.error(chalk.red("OpenCode CLI not found."));
      console.log(
        chalk.gray(
          "Install OpenCode or set an override:\n  export OPENCODE_BIN=/path/to/opencode\n  opencode-sm --help"
        )
      );
      process.exit(1);
      return;
    }
    const opencode = spawn(opencodeBin, opencodeArgs, {
      stdio: "inherit",
      env: process.env
    });
    opencode.on("error", (err) => {
      console.error(chalk.red("Failed to launch OpenCode CLI."));
      if (err.code === "ENOENT") {
        console.error(
          chalk.gray("Not found. Set OPENCODE_BIN or install opencode.")
        );
      } else {
        console.error(chalk.gray(`${err.message}`));
      }
      process.exit(1);
    });
    opencode.on("exit", async (code) => {
      this.saveContext("OpenCode session ended", {
        action: "session_end",
        exitCode: code
      });
      if (this.config.tracingEnabled) {
        const summary = trace.getExecutionSummary();
        console.log();
        console.log(chalk.gray("-".repeat(40)));
        console.log(chalk.magenta("Trace Summary:"));
        console.log(chalk.gray(summary));
      }
      if (this.config.worktreePath) {
        console.log();
        console.log(chalk.gray("-".repeat(40)));
        console.log(chalk.magenta("Session ended in worktree:"));
        console.log(chalk.gray(`  ${this.config.worktreePath}`));
      }
      process.exit(code || 0);
    });
    process.on("SIGINT", () => {
      this.saveContext("OpenCode session interrupted", {
        action: "session_interrupt"
      });
      opencode.kill("SIGINT");
    });
    process.on("SIGTERM", () => {
      this.saveContext("OpenCode session terminated", {
        action: "session_terminate"
      });
      opencode.kill("SIGTERM");
    });
  }
}
program.name("opencode-sm").description("OpenCode with StackMemory context and worktree isolation").version("1.0.0");
const configCmd = program.command("config").description("Manage opencode-sm defaults");
configCmd.command("show").description("Show current default settings").action(() => {
  const config = loadSMConfig();
  console.log(chalk.magenta("opencode-sm defaults:"));
  console.log(
    `  defaultWorktree: ${config.defaultWorktree ? chalk.green("true") : chalk.gray("false")}`
  );
  console.log(
    `  defaultTracing:  ${config.defaultTracing ? chalk.green("true") : chalk.gray("false")}`
  );
  console.log(chalk.gray(`
Config: ${getConfigPath()}`));
});
configCmd.command("set <key> <value>").description("Set a default (e.g., set worktree true)").action((key, value) => {
  const config = loadSMConfig();
  const boolValue = value === "true" || value === "1" || value === "on";
  const keyMap = {
    worktree: "defaultWorktree",
    tracing: "defaultTracing"
  };
  const configKey = keyMap[key];
  if (!configKey) {
    console.log(chalk.red(`Unknown key: ${key}`));
    console.log(chalk.gray("Valid keys: worktree, tracing"));
    process.exit(1);
  }
  config[configKey] = boolValue;
  saveSMConfig(config);
  console.log(chalk.green(`Set ${key} = ${boolValue}`));
});
configCmd.command("worktree-on").description("Enable worktree mode by default").action(() => {
  const config = loadSMConfig();
  config.defaultWorktree = true;
  saveSMConfig(config);
  console.log(chalk.green("Worktree mode enabled by default"));
});
configCmd.command("worktree-off").description("Disable worktree mode by default").action(() => {
  const config = loadSMConfig();
  config.defaultWorktree = false;
  saveSMConfig(config);
  console.log(chalk.green("Worktree mode disabled by default"));
});
program.option("-w, --worktree", "Create isolated worktree for this instance").option("-W, --no-worktree", "Disable worktree (override default)").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("--opencode-bin <path>", "Path to opencode CLI (or use OPENCODE_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) => {
  const opencodeSM = new OpencodeSM();
  const args = process.argv.slice(2);
  await opencodeSM.run(args);
});
program.parse(process.argv);
//# sourceMappingURL=opencode-sm.js.map
