#!/usr/bin/env node
import {
  truncateForPersist
} from "./chunk-4JWMT7XQ.js";
import {
  APP_NAME,
  CONFIG_DIR_NAME,
  DEV_STATE_FILE_NAME,
  MCP_CALL_TIMEOUT,
  MCP_CONNECT_TIMEOUT,
  MCP_PROTOCOL_VERSION,
  MCP_TOOL_PREFIX,
  VERSION
} from "./chunk-2DVGTYZG.js";

// src/mcp/client.ts
import { spawn } from "child_process";
var McpClient = class {
  serverId;
  config;
  process = null;
  nextId = 1;
  // M8: wraps at MAX_SAFE_INTEGER via getNextId()
  connected = false;
  serverInfo = null;
  /** stderr 收集（最多保留最后 2KB，用于错误报告） */
  stderrBuffer = "";
  /** 缓存已发现的工具列表 */
  cachedTools = [];
  /** 错误信息（连接失败时设置） */
  errorMessage = null;
  // ── JSON-RPC 请求/响应匹配 ──────────────────────────────────────
  pendingRequests = /* @__PURE__ */ new Map();
  /** stdout 残余缓冲区（处理不完整的 JSON 行） */
  stdoutBuffer = "";
  constructor(serverId, config) {
    this.serverId = serverId;
    this.config = config;
  }
  get isConnected() {
    return this.connected;
  }
  get serverName() {
    return this.serverInfo?.name ?? this.serverId;
  }
  get tools() {
    return this.cachedTools;
  }
  // ══════════════════════════════════════════════════════════════════
  // 连接与初始化
  // ══════════════════════════════════════════════════════════════════
  async connect() {
    const timeout = this.config.timeout ?? MCP_CONNECT_TIMEOUT;
    try {
      this.process = spawn(this.config.command, this.config.args ?? [], {
        stdio: ["pipe", "pipe", "pipe"],
        env: { ...process.env, ...this.config.env },
        // Windows 上 npx 等是 .cmd 脚本，需要 shell 模式
        shell: process.platform === "win32",
        // 不让子进程阻止父进程退出
        detached: false
      });
      this.process.on("error", (err) => {
        this.errorMessage = err.message;
        this.connected = false;
        this.rejectAllPending(new Error(`MCP server [${this.serverId}] process error: ${err.message}`));
      });
      this.process.on("exit", (code, signal) => {
        this.connected = false;
        const reason = signal ? `signal ${signal}` : `code ${code}`;
        this.rejectAllPending(new Error(`MCP server [${this.serverId}] exited: ${reason}`));
      });
      this.process.stdout.setEncoding("utf-8");
      this.process.stdout.on("data", (chunk) => this.handleStdoutData(chunk));
      this.process.stderr.setEncoding("utf-8");
      this.process.stderr.on("data", (chunk) => {
        this.stderrBuffer += chunk;
        if (this.stderrBuffer.length > 2048) {
          this.stderrBuffer = this.stderrBuffer.slice(-2048);
        }
      });
      const initResult = await this.withTimeout(
        this.sendRequest("initialize", {
          protocolVersion: MCP_PROTOCOL_VERSION,
          capabilities: {},
          clientInfo: { name: APP_NAME, version: VERSION }
        }),
        timeout,
        "initialize handshake"
      );
      this.serverInfo = initResult.serverInfo;
      this.sendNotification("notifications/initialized");
      this.connected = true;
      await this.refreshTools();
    } catch (err) {
      this.errorMessage = err instanceof Error ? err.message : String(err);
      this.connected = false;
      this.killProcess();
      throw err;
    }
  }
  // ══════════════════════════════════════════════════════════════════
  // 工具操作
  // ══════════════════════════════════════════════════════════════════
  /** 刷新工具列表（tools/list） */
  async refreshTools() {
    this.ensureConnected();
    const result = await this.withTimeout(
      this.sendRequest("tools/list", {}),
      MCP_CALL_TIMEOUT,
      "tools/list"
    );
    this.cachedTools = result.tools ?? [];
    return this.cachedTools;
  }
  /** 调用工具（tools/call） */
  async callTool(name, args) {
    this.ensureConnected();
    return this.withTimeout(
      this.sendRequest("tools/call", { name, arguments: args }),
      MCP_CALL_TIMEOUT,
      `tools/call(${name})`
    );
  }
  // ══════════════════════════════════════════════════════════════════
  // 关闭连接
  // ══════════════════════════════════════════════════════════════════
  async close() {
    this.connected = false;
    this.rejectAllPending(new Error("Client closing"));
    this.killProcess();
  }
  /**
   * 断线重连：清理旧状态后重新执行完整的 connect() 流程。
   * 用于 MCP 服务器子进程意外退出后的自动或手动恢复。
   */
  async reconnect() {
    this.connected = false;
    this.rejectAllPending(new Error("Reconnecting"));
    this.killProcess();
    this.errorMessage = null;
    this.stderrBuffer = "";
    this.stdoutBuffer = "";
    this.cachedTools = [];
    await this.connect();
  }
  // ══════════════════════════════════════════════════════════════════
  // 内部方法：JSON-RPC 通信
  // ══════════════════════════════════════════════════════════════════
  sendRequest(method, params) {
    return new Promise((resolve, reject) => {
      if (!this.process?.stdin?.writable) {
        return reject(new Error(`MCP server [${this.serverId}] stdin not writable`));
      }
      const id = this.nextId++;
      if (this.nextId > Number.MAX_SAFE_INTEGER - 1) this.nextId = 1;
      const request = {
        jsonrpc: "2.0",
        id,
        method,
        ...params !== void 0 ? { params } : {}
      };
      let timer;
      const cleanup = () => {
        this.pendingRequests.delete(id);
        clearTimeout(timer);
      };
      timer = setTimeout(() => {
        cleanup();
        reject(new Error(`MCP request [${method}] timed out (internal)`));
      }, MCP_CALL_TIMEOUT * 2);
      this.pendingRequests.set(id, {
        resolve: (result) => {
          cleanup();
          resolve(result);
        },
        reject: (error) => {
          cleanup();
          reject(error);
        },
        timer
      });
      const json = JSON.stringify(request) + "\n";
      this.process.stdin.write(json, (err) => {
        if (err) {
          cleanup();
          reject(new Error(`MCP write error: ${err.message}`));
        }
      });
    });
  }
  sendNotification(method, params) {
    if (!this.process?.stdin?.writable) return;
    const notification = {
      jsonrpc: "2.0",
      method,
      ...params !== void 0 ? { params } : {}
    };
    this.process.stdin.write(JSON.stringify(notification) + "\n");
  }
  /**
   * 处理 stdout 数据：按行分割，每行解析为 JSON-RPC 响应。
   * 处理不完整行：残余数据保留在 stdoutBuffer 中。
   */
  handleStdoutData(chunk) {
    this.stdoutBuffer += chunk;
    if (this.stdoutBuffer.length > 1048576) {
      process.stderr.write(`[mcp] stdout buffer exceeded 1MB \u2014 clearing
`);
      this.stdoutBuffer = "";
      return;
    }
    const lines = this.stdoutBuffer.split("\n");
    this.stdoutBuffer = lines.pop() ?? "";
    for (const line of lines) {
      const trimmed = line.trim();
      if (!trimmed) continue;
      try {
        const msg = JSON.parse(trimmed);
        this.handleMessage(msg);
      } catch {
      }
    }
  }
  /** 处理收到的 JSON-RPC 消息 */
  handleMessage(msg) {
    if ("id" in msg && typeof msg.id === "number") {
      const pending = this.pendingRequests.get(msg.id);
      if (!pending) return;
      const response = msg;
      if (response.error) {
        pending.reject(new Error(
          `MCP error [${response.error.code}]: ${response.error.message}`
        ));
      } else {
        pending.resolve(response.result);
      }
    }
  }
  // ══════════════════════════════════════════════════════════════════
  // 辅助方法
  // ══════════════════════════════════════════════════════════════════
  ensureConnected() {
    if (!this.connected) {
      throw new Error(
        `MCP server [${this.serverId}] is not connected` + (this.errorMessage ? `: ${this.errorMessage}` : "")
      );
    }
  }
  /** Promise 超时包装 */
  withTimeout(promise, ms, label) {
    return new Promise((resolve, reject) => {
      const timer = setTimeout(() => {
        reject(new Error(`MCP [${this.serverId}] ${label} timed out after ${ms}ms`));
      }, ms);
      promise.then((val) => {
        clearTimeout(timer);
        resolve(val);
      }).catch((err) => {
        clearTimeout(timer);
        reject(err);
      });
    });
  }
  /** 拒绝所有挂起的请求。pending.reject() 内部的 cleanup() 已包含 clearTimeout。 */
  rejectAllPending(error) {
    for (const [, pending] of this.pendingRequests) {
      pending.reject(error);
    }
    this.pendingRequests.clear();
  }
  /** 杀掉子进程，并移除所有事件监听器防止僵尸引用 */
  killProcess() {
    if (this.process) {
      this.process.removeAllListeners("error");
      this.process.removeAllListeners("exit");
      this.process.stdout?.removeAllListeners("data");
      this.process.stderr?.removeAllListeners("data");
      try {
        this.process.stdin?.end();
        this.process.kill();
      } catch {
      }
      this.process = null;
    }
  }
};

// src/mcp/manager.ts
var McpManager = class {
  clients = /* @__PURE__ */ new Map();
  /**
   * 连接所有配置的 MCP 服务器（并发连接，单个失败不阻塞其他）。
   * 连接结果通过 getStatus() 查看。
   *
   * @param options.quiet 抑制每个 server 的成功/失败 stderr 输出。REPL 在
   *   后台连接时启用 quiet，避免日志和已绘制的 readline prompt 互相穿插；
   *   连接完成后调用方自己用 getStatus() 渲染摘要（含失败列表）。
   *   server.ts 等同步启动场景保留默认（详细日志）。
   */
  async connectAll(servers, options = {}) {
    const entries = Object.entries(servers);
    if (entries.length === 0) return;
    const promises = entries.map(async ([serverId, config]) => {
      const client = new McpClient(serverId, config);
      this.clients.set(serverId, client);
      try {
        await client.connect();
        if (!options.quiet) {
          process.stderr.write(`[mcp] \u2713 ${serverId}: connected (${client.serverName}, ${client.tools.length} tools)
`);
        }
      } catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        if (!options.quiet) {
          process.stderr.write(`[mcp] \u2717 ${serverId}: ${msg}
`);
        }
      }
    });
    await Promise.allSettled(promises);
  }
  /**
   * 获取所有已连接服务器的工具，转换为 ai-cli 的 Tool 接口。
   * 工具名格式：mcp__<serverId>__<toolName>
   */
  getAllTools() {
    const tools = [];
    for (const [serverId, client] of this.clients) {
      if (!client.isConnected) continue;
      for (const mcpTool of client.tools) {
        tools.push(this.wrapMcpTool(serverId, client, mcpTool));
      }
    }
    return tools;
  }
  /**
   * 获取连接状态摘要。
   */
  getStatus() {
    const statuses = [];
    for (const [serverId, client] of this.clients) {
      statuses.push({
        serverId,
        serverName: client.serverName,
        toolCount: client.tools.length,
        connected: client.isConnected,
        error: client.errorMessage ?? void 0
      });
    }
    return statuses;
  }
  /**
   * 获取已连接服务器数量。
   */
  getConnectedCount() {
    let count = 0;
    for (const client of this.clients.values()) {
      if (client.isConnected) count++;
    }
    return count;
  }
  /**
   * 获取所有 MCP 工具的总数。
   */
  getTotalToolCount() {
    let count = 0;
    for (const client of this.clients.values()) {
      if (client.isConnected) count += client.tools.length;
    }
    return count;
  }
  /**
   * 重连指定 MCP 服务器。
   * @returns 重连是否成功
   */
  async reconnectServer(serverId) {
    const client = this.clients.get(serverId);
    if (!client) return false;
    try {
      await client.reconnect();
      process.stderr.write(`[mcp] \u2713 ${serverId}: reconnected (${client.serverName}, ${client.tools.length} tools)
`);
      return true;
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      process.stderr.write(`[mcp] \u2717 ${serverId}: reconnect failed: ${msg}
`);
      return false;
    }
  }
  /**
   * 重连所有已断开的 MCP 服务器。
   * @returns 成功重连的服务器数量
   */
  async reconnectAll() {
    let reconnected = 0;
    for (const [serverId, client] of this.clients) {
      if (!client.isConnected) {
        const ok = await this.reconnectServer(serverId);
        if (ok) reconnected++;
      }
    }
    return reconnected;
  }
  /**
   * 关闭所有 MCP 服务器连接。
   */
  async closeAll() {
    const promises = [...this.clients.entries()].map(
      ([id, c]) => c.close().catch((err) => {
        process.stderr.write(`[mcp] Failed to close ${id}: ${err instanceof Error ? err.message : err}
`);
      })
    );
    await Promise.allSettled(promises);
    this.clients.clear();
  }
  // ══════════════════════════════════════════════════════════════════
  // 内部方法
  // ══════════════════════════════════════════════════════════════════
  /**
   * 将 MCP 工具包装为 ai-cli 的 Tool 接口。
   * execute() 方法委托给对应 McpClient.callTool()。
   */
  wrapMcpTool(serverId, client, mcpTool) {
    const toolName = `${MCP_TOOL_PREFIX}${serverId}__${mcpTool.name}`;
    const definition = this.convertDefinition(toolName, serverId, mcpTool);
    return {
      definition,
      execute: async (args) => {
        if (!client.isConnected) {
          process.stderr.write(`[mcp] ${serverId}: disconnected, attempting reconnect...
`);
          try {
            await client.reconnect();
            process.stderr.write(`[mcp] \u2713 ${serverId}: reconnected (${client.tools.length} tools)
`);
          } catch {
            throw new Error(
              `MCP server [${serverId}] is disconnected and reconnect failed` + (client.errorMessage ? `: ${client.errorMessage}` : "") + `. Use /mcp reconnect to retry, or restart ai-cli.`
            );
          }
        }
        try {
          const result = await client.callTool(mcpTool.name, args);
          if (result.isError) {
            const errorText = this.extractText(result.content);
            throw new Error(errorText || "MCP tool returned error with no message");
          }
          return this.extractText(result.content) || "(no output)";
        } catch (err) {
          throw new Error(`MCP [${serverId}/${mcpTool.name}]: ${err instanceof Error ? err.message : String(err)}`);
        }
      }
    };
  }
  /**
   * 转换 MCP 工具定义为 ai-cli 的 ToolDefinition。
   * MCP 使用 JSON Schema 的 inputSchema，ai-cli 使用 Record<string, ToolParameterSchema>。
   * M12 修复：递归转换嵌套 object/array schema，保留完整结构供 AI 正确传参。
   */
  convertDefinition(toolName, serverId, mcpTool) {
    const parameters = {};
    const props = mcpTool.inputSchema?.properties ?? {};
    const required = new Set(mcpTool.inputSchema?.required ?? []);
    for (const [key, schema] of Object.entries(props)) {
      parameters[key] = {
        ...this.convertSchema(schema),
        ...required.has(key) ? { required: true } : {}
      };
    }
    return {
      name: toolName,
      description: (mcpTool.description ?? mcpTool.name) + ` [MCP: ${serverId}]`,
      parameters
    };
  }
  /**
   * 递归转换单个 JSON Schema 属性为 ToolParameterSchema。
   * 支持嵌套 object（含 properties）和 array（含 items）。
   * 递归深度受 MCP 工具实际 schema 复杂度约束（通常 2-3 层）。
   */
  convertSchema(schema) {
    const type = this.normalizeType(String(schema["type"] ?? "string"));
    const result = {
      type,
      description: String(schema["description"] ?? ""),
      ...schema["enum"] ? { enum: schema["enum"] } : {}
    };
    if (type === "object" && schema["properties"]) {
      const props = schema["properties"];
      result.properties = {};
      for (const [k, v] of Object.entries(props)) {
        result.properties[k] = this.convertSchema(v);
      }
    }
    if (type === "array" && schema["items"]) {
      const items = schema["items"];
      result.items = this.convertSchema(items);
    }
    return result;
  }
  /**
   * 将 JSON Schema 类型映射到 ai-cli 的 ToolParameterType。
   * integer → number（ai-cli 不区分整数/浮点）
   */
  normalizeType(jsonSchemaType) {
    switch (jsonSchemaType) {
      case "string":
        return "string";
      case "number":
      case "integer":
        return "number";
      case "boolean":
        return "boolean";
      case "array":
        return "array";
      case "object":
        return "object";
      default:
        return "string";
    }
  }
  /**
   * 从 MCP 响应的 content blocks 中提取纯文本。
   * 只处理 type="text" 的块，其他类型（image 等）跳过。
   */
  extractText(content) {
    return content.filter((block) => block.type === "text" && block.text).map((block) => block.text).join("\n");
  }
};

// src/skills/manager.ts
import { existsSync, readdirSync, mkdirSync, statSync } from "fs";
import { join } from "path";

// src/skills/types.ts
import { readFileSync } from "fs";
import { basename } from "path";
function parseSimpleYaml(yaml) {
  const result = {};
  for (const line of yaml.split("\n")) {
    const match = line.replace(/\r$/, "").match(/^(\w+)\s*:\s*(.+)$/);
    if (match) {
      result[match[1]] = match[2].trim().replace(/^['"]|['"]$/g, "");
    }
  }
  return result;
}
function parseYamlArray(value) {
  const bracketMatch = value.match(/^\[(.+)]$/);
  if (bracketMatch) {
    return bracketMatch[1].split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, ""));
  }
  return [value.trim()];
}
function parseSkillFile(filePath) {
  let raw;
  try {
    raw = readFileSync(filePath, "utf-8");
  } catch {
    return null;
  }
  const frontmatterMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
  if (!frontmatterMatch) {
    return {
      meta: {
        name: basename(filePath, ".md"),
        description: ""
      },
      content: raw.trim(),
      filePath
    };
  }
  const [, yaml, content] = frontmatterMatch;
  const parsed = parseSimpleYaml(yaml);
  return {
    meta: {
      name: parsed["name"] ?? basename(filePath, ".md"),
      description: parsed["description"] ?? "",
      tools: parsed["tools"] ? parseYamlArray(parsed["tools"]) : void 0
    },
    content: content.trim(),
    filePath
  };
}

// src/skills/manager.ts
var SKILL_CONTENT_WARN_CHARS_DEFAULT = 1e4;
var SkillManager = class {
  skills = /* @__PURE__ */ new Map();
  activeSkill = null;
  skillsDir;
  /** 超大技能文件警告阈值，由调用方从 config 传入；0 = 静默，undefined = 用默认 10000 */
  warnThreshold;
  constructor(skillsDir, warnThreshold) {
    this.skillsDir = skillsDir;
    this.warnThreshold = warnThreshold ?? SKILL_CONTENT_WARN_CHARS_DEFAULT;
  }
  /** 发现并加载 skillsDir 下所有 .md 文件，返回加载数量 */
  loadSkills() {
    this.skills.clear();
    if (!existsSync(this.skillsDir)) {
      try {
        mkdirSync(this.skillsDir, { recursive: true });
      } catch {
      }
      return 0;
    }
    let entries;
    try {
      entries = readdirSync(this.skillsDir);
    } catch {
      return 0;
    }
    for (const entry of entries) {
      let filePath;
      const fullPath = join(this.skillsDir, entry);
      if (entry.endsWith(".md")) {
        filePath = fullPath;
      } else {
        try {
          if (statSync(fullPath).isDirectory()) {
            const skillMd = join(fullPath, "SKILL.md");
            if (existsSync(skillMd)) {
              filePath = skillMd;
            } else {
              continue;
            }
          } else {
            continue;
          }
        } catch {
          continue;
        }
      }
      const skill = parseSkillFile(filePath);
      if (skill) {
        if (skill.meta.name === "SKILL" && !entry.endsWith(".md")) {
          skill.meta.name = entry;
        }
        this.skills.set(skill.meta.name, skill);
      }
    }
    return this.skills.size;
  }
  /** 列出所有已加载的技能 */
  listSkills() {
    return [...this.skills.values()];
  }
  /** 按 name 激活技能。返回被激活的 Skill，未找到返回 null。 */
  activate(name) {
    const skill = this.skills.get(name);
    if (!skill) return null;
    this.activeSkill = skill;
    if (this.warnThreshold > 0 && skill.content.length > this.warnThreshold) {
      process.stderr.write(
        `\u26A0 Skill '${skill.meta.name}' is ${skill.content.length} chars (>${this.warnThreshold}) \u2014 this will be injected into the system prompt now. Adjust threshold with: /config set ui.skillSizeWarn <n|0>
`
      );
    }
    return skill;
  }
  /** 停用当前技能 */
  deactivate() {
    this.activeSkill = null;
  }
  /** 获取当前激活的技能（null 表示无激活技能） */
  getActive() {
    return this.activeSkill;
  }
  /** 获取激活技能的 prompt 内容（null 表示无激活技能） */
  getActivePromptContent() {
    return this.activeSkill?.content ?? null;
  }
  /** 获取激活技能的工具白名单 Set（null 表示无限制） */
  getActiveToolFilter() {
    if (!this.activeSkill?.meta.tools) return null;
    return new Set(this.activeSkill.meta.tools);
  }
  /** 获取技能目录路径 */
  getSkillsDir() {
    return this.skillsDir;
  }
};

// src/core/proxy.ts
async function setupProxy(configProxy) {
  const proxyUrl = process.env.HTTPS_PROXY ?? process.env.HTTP_PROXY ?? process.env.https_proxy ?? process.env.http_proxy ?? configProxy;
  if (!proxyUrl) return;
  try {
    const { ProxyAgent, setGlobalDispatcher } = await import("undici");
    setGlobalDispatcher(new ProxyAgent({ uri: proxyUrl }));
  } catch {
  }
}

// src/session/tool-history.ts
var SESSION_SIZE_LIMIT = 2 * 1024 * 1024;
function persistToolRound(session, toolCalls, toolResults, opts) {
  session.addMessage({
    role: "assistant",
    content: opts?.assistantContent ?? "",
    toolCalls,
    reasoningContent: opts?.reasoningContent,
    timestamp: /* @__PURE__ */ new Date()
  });
  for (let i = 0; i < toolCalls.length; i++) {
    const tc = toolCalls[i];
    const tr = toolResults[i];
    if (!tr) continue;
    session.addMessage({
      role: "tool",
      content: truncateForPersist(tr.content),
      toolCallId: tr.callId,
      toolName: tc.name,
      isError: tr.isError,
      timestamp: /* @__PURE__ */ new Date()
    });
  }
}
function trimOldToolOutput(messages, keepRecentRounds = 10) {
  const roundStarts = [];
  for (let i = 0; i < messages.length; i++) {
    if (messages[i].role === "assistant" && messages[i].toolCalls?.length) {
      roundStarts.push(i);
    }
  }
  if (roundStarts.length <= keepRecentRounds) return 0;
  const cutoffRoundIdx = roundStarts.length - keepRecentRounds;
  let trimmed = 0;
  for (let r = 0; r < cutoffRoundIdx; r++) {
    const start = roundStarts[r];
    const end = r + 1 < roundStarts.length ? roundStarts[r + 1] : messages.length;
    const assistantMsg = messages[start];
    if (typeof assistantMsg.content === "string" && assistantMsg.content.length > 200) {
      assistantMsg.content = assistantMsg.content.slice(0, 100) + "\u2026 [trimmed for size]";
      trimmed++;
    }
    for (let j = start + 1; j < end; j++) {
      const msg = messages[j];
      if (msg.role === "tool") {
        const status = msg.isError ? "\u2717 error" : "\u2713 ok";
        const name = msg.toolName ?? "unknown";
        const currentContent = typeof msg.content === "string" ? msg.content : "";
        if (currentContent.length > 200) {
          msg.content = `[${name}: ${status}] (output trimmed for size \u2014 ${currentContent.length} chars)`;
          trimmed++;
        }
      }
    }
  }
  return trimmed;
}
function cloneMessages(messages) {
  return messages.map((m) => ({ ...m }));
}
function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
  const originalSize = JSON.stringify(session.toJSON()).length;
  if (originalSize <= sizeLimit) return false;
  const snapshotLen = session.messages.length;
  let working = cloneMessages(session.messages.slice(0, snapshotLen));
  let committedSize = originalSize;
  let anyTrim = false;
  let keepRecent = 10;
  while (keepRecent >= 2) {
    const trimmedCount = trimOldToolOutput(working, keepRecent);
    if (trimmedCount === 0) break;
    anyTrim = true;
    const originalMessages = session.messages;
    session.messages = [...working, ...originalMessages.slice(snapshotLen)];
    const newSize = JSON.stringify(session.toJSON()).length;
    session.messages = originalMessages;
    committedSize = newSize;
    if (newSize <= sizeLimit) break;
    keepRecent = Math.max(2, Math.floor(keepRecent / 2));
    if (keepRecent < 2) break;
  }
  if (!anyTrim) return false;
  const appended = session.messages.slice(snapshotLen);
  session.messages.splice(0, session.messages.length, ...working, ...appended);
  return committedSize < originalSize;
}

// src/repl/dev-state.ts
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, unlinkSync, mkdirSync as mkdirSync2 } from "fs";
import { join as join2 } from "path";
import { homedir } from "os";
var DEV_STATE_MAX_CHARS = 6e3;
var SNAPSHOT_PROMPT = `You are about to be replaced by a different AI model. Please generate a structured development state snapshot so the next model can continue seamlessly.

CRITICAL: Be SPECIFIC and DETAILED. Include exact values, file paths, format requirements, and constraints \u2014 vague summaries are useless to the next model.

Output ONLY the snapshot in the following exact format (no preamble, no explanation):

## Development State Snapshot

### Current Task
[1-2 sentence summary of the primary task/goal the user is working on]

### Key Parameters & Constraints
- [List ALL specific parameters, values, format requirements discovered during this conversation]
- [Include exact numbers, dimensions, scoring rules, naming conventions, etc.]
- [Example: "Exam duration 90 minutes, total score 200, 40 single-choice x2 pts + 20 multi-choice x4 pts + 20 true/false x2 pts"]
- [Example: "File naming format YYYYMMDD-NN-mock-difficulty.md, saved to exam_papers/ directory"]

### Completed Steps
- [List each completed step with specific details (file paths, key outcomes)]

### In-Progress Work
- [List any work that was started but not finished]

### Critical Reference Files
- [List file paths that the next model MUST read before doing any work]
- [Include brief description of each file's purpose]
- [Example: "Exam2025.md \u2014 reference format for official past exams (90min/200pts/question type distribution)"]
- [Example: "style-guide.md \u2014 style guidelines (character setup/scenario building/current events)"]

### Modified/Created Files
- [List any files that were created or modified, with brief notes on content]

### Key Decisions & Context
- [Important decisions, user preferences, or constraints established]

### Next Steps
- [What should be done next to continue this work]
- [Include specific instructions the next model should follow]

### Important Notes
- [Any warnings, caveats, or critical context the next model needs to know]
- [Things that went wrong or should be avoided]

If any section has no content, write "(none)" for that section. Be thorough \u2014 the next model may have access to our conversation messages, but the detailed tool call results (file contents, command outputs) are NOT preserved. This snapshot is the primary source of specific details and context.`;
function sessionHasMeaningfulContent(messages) {
  if (messages.length < 2) return false;
  const hasUser = messages.some((m) => m.role === "user");
  const hasAssistant = messages.some((m) => m.role === "assistant");
  return hasUser && hasAssistant;
}
function getDevStatePath() {
  return join2(homedir(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
}
function saveDevState(content) {
  const configDir = join2(homedir(), CONFIG_DIR_NAME);
  if (!existsSync2(configDir)) {
    mkdirSync2(configDir, { recursive: true });
  }
  let trimmed = content.trim();
  if (trimmed.length > DEV_STATE_MAX_CHARS) {
    trimmed = trimmed.slice(0, DEV_STATE_MAX_CHARS);
    const lastNewline = trimmed.lastIndexOf("\n");
    if (lastNewline > 0) {
      trimmed = trimmed.slice(0, lastNewline);
    }
    trimmed += "\n\n[...truncated]";
  }
  writeFileSync(getDevStatePath(), trimmed, "utf-8");
}
function loadDevState() {
  const path = getDevStatePath();
  if (!existsSync2(path)) return null;
  const content = readFileSync2(path, "utf-8").trim();
  return content || null;
}
function clearDevState() {
  const path = getDevStatePath();
  if (existsSync2(path)) {
    try {
      unlinkSync(path);
    } catch {
    }
  }
}

export {
  parseSimpleYaml,
  persistToolRound,
  autoTrimSessionIfNeeded,
  SNAPSHOT_PROMPT,
  sessionHasMeaningfulContent,
  saveDevState,
  loadDevState,
  clearDevState,
  McpManager,
  SkillManager,
  setupProxy
};
