/**
 * P5 CLI 入口:workvault run --vault <dir> --profile <file|name> --data <json> [--dry-run]
 * 不依赖 Obsidian(任意 Markdown 目录,Obsidian 可选),Agent/服务端/脚本/CI 可直接调用
 */
import * as fs from "fs";
import * as path from "path";
import type { Profile } from "./core/types";
import { renderSource } from "./core/engine";
import { planIncremental } from "./core/incremental";
import { FsWriter } from "./core/fsWriter";
import { applyNotes } from "./core/writer";
import { createProvider, autoProviderKind } from "./core/provider";
/** 统一 LLM 配置:CLI 参数 > config.provider > 环境变量 WORK_VAULT_API_* */
function llmOpts(config: any, a: Record<string, any> = {}) {
  const env = process.env.WORK_VAULT_API_KEY
    ? {
        apiBase: process.env.WORK_VAULT_API_BASE || "https://api.deepseek.com",
        apiKey: process.env.WORK_VAULT_API_KEY,
        model: process.env.WORK_VAULT_MODEL || "deepseek-chat",
      }
    : {};
  return {
    apiBase: a["api-base"] ?? config.provider?.apiBase ?? env.apiBase,
    apiKey: a["api-key"] ?? config.provider?.apiKey ?? env.apiKey,
    model: a["model"] ?? config.provider?.model ?? env.model,
    maxTokens: a["max-tokens"] ? Number(a["max-tokens"]) : config.provider?.maxTokens,
  };
}

import { compileRaw } from "./core/compiler";
import { lintWiki } from "./core/lint";
import { queryWiki } from "./core/query";
import { loadConfig } from "./core/config";
import { writeClaims } from "./core/ledger";
import { runSetup } from "./setup";
import { runChat } from "./chat";
import {
  writeMemory,
  searchMemory,
  listMemory,
  captureMemory,
  confirmPending,
  listPendingMemory,
  sessionStart,
  sessionEnd,
} from "./core/memory";
import type { MemoryType } from "./core/memory";

const config = loadConfig([__dirname]);

/**
 * vault 解析优先级:--vault 参数 > WORK_VAULT_VAULT 环境变量(Agent/平台注入,legacy
 * WORK_OBSIDIAN_VAULT 兼容)> 配置文件。环境变量通道让 Agent 指令模板无需硬编码路径:
 * 每台机器在环境变量里设一次即可。
 */
/**
 * 默认工作区:当前目录下的 .workvault 文件夹(零配置兜底)。
 * · 行为确定:知识库就在运行命令的目录里,一眼可见
 * · 点开头不污染目录、不与克隆自带的 workvault 目录撞名
 * · 本地/容器挂载同一工作目录时,两边指向同一位置
 * · 注意:随当前目录变化(每个目录一个知识库);要固定一处请用 WORK_VAULT_VAULT/--vault/配置文件
 */
function defaultWorkspaceDir(): string {
  return path.join(process.cwd(), ".workvault");
}

/**
 * vault 解析优先级:--vault 参数 > WORK_VAULT_VAULT 环境变量(legacy WORK_OBSIDIAN_VAULT 兼容)
 * > 配置文件 > 默认工作区 .workvault(自动创建并提示)。
 */
function vaultFor(a: Record<string, string>): string | undefined {
  const envVault =
    process.env.WORK_VAULT_VAULT || process.env.WORK_OBSIDIAN_VAULT || undefined;
  const v = a["vault"] ?? envVault ?? config.vault;
  if (v) return v;
  const fallback = defaultWorkspaceDir();
  try {
    fs.mkdirSync(fallback, { recursive: true });
  } catch {
    /* 极端情况(目录只读等)交给后续命令自行报错 */
  }
  console.error(
    `⚠ 未指定知识库目录:默认工作区 = 当前目录下的 .workvault\n` +
      `  已就绪:${fallback}\n` +
      `  提示:默认工作区跟随当前目录(每个目录一个知识库);要固定一处,` +
      `请 set WORK_VAULT_VAULT=<目录> 或用 --vault / workvault.config.json`,
  );
  return fallback;
}

const TEXT_EXTS = [".md", ".mdx", ".txt", ".json", ".yaml", ".yml", ".csv", ".html", ".py", ".js", ".ts"];

function parseArgs(argv: string[]): Record<string, string> {
  const out: Record<string, string> = {};
  for (let i = 0; i < argv.length; i++) {
    const a = argv[i];
    if (a.startsWith("--")) {
      const key = a.slice(2);
      const val = argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[i + 1] : "true";
      out[key] = val;
      if (val !== "true") i++;
    }
  }
  return out;
}

function loadProfile(ref: string): Profile {
  // 1) 直接路径
  if (fs.existsSync(ref)) {
    return JSON.parse(fs.readFileSync(ref, "utf-8")) as Profile;
  }
  // 2) 通用内置 profile(templates/profiles/*.json)
  const builtin = path.join(__dirname, "templates", "profiles", `${ref}.json`);
  if (fs.existsSync(builtin)) {
    return JSON.parse(fs.readFileSync(builtin, "utf-8")) as Profile;
  }
  // 3) 官方示例包(examples/<pkg>/profiles/*.json,如 eduswarm)
  const examplesDir = path.join(__dirname, "examples");
  if (fs.existsSync(examplesDir)) {
    for (const pkg of fs.readdirSync(examplesDir)) {
      const p = path.join(examplesDir, pkg, "profiles", `${ref}.json`);
      if (fs.existsSync(p)) {
        return JSON.parse(fs.readFileSync(p, "utf-8")) as Profile;
      }
    }
  }
  throw new Error(`profile 不存在: ${ref}(支持:路径 / 通用内置名 / 示例包名)`);
}

export async function cliMain(argv: string[]): Promise<number> {
  // chat / 无参数 → 对话模式(自然语言驱动)
  if (argv.length === 0 || argv[0] === "chat") {
    return chatMain();
  }
  // compile 子命令:raw 资料 + schema → LLM 编译 wiki(自然语言指令)
  if (argv[0] === "compile") {
    return compileMain(argv.slice(1));
  }
  // query 子命令:基于 wiki 回答(来源纪律 + 渐进式披露,可选归档)
  if (argv[0] === "query") {
    return queryMain(argv.slice(1));
  }
  // setup 子命令:交互式配置向导(vault / API Key / 模型)
  if (argv[0] === "setup") {
    return setupMain();
  }
  // lint 子命令:知识库健康检查
  if (argv[0] === "lint") {
    return lintMain(argv.slice(1));
  }
  // session 子命令:会话记忆生命周期(start 注入上下文 / end 写日志+提炼)
  if (argv[0] === "session") {
    return sessionMain(argv.slice(1));
  }
  // memory 子命令:记忆写入 / 检索 / 列出 / 提炼 / 挂起确认
  if (argv[0] === "memory") {
    return memoryMain(argv.slice(1));
  }
  const args = parseArgs(argv);
  const vault = vaultFor(args);
  const profileRef = args["profile"];
  const dataPath = args["data"];
  const dryRun = args["dry-run"] === "true" || args["dry-run"] === "";

  if (!vault || !profileRef || !dataPath) {
    console.error(
      "用法: workvault run --vault <dir> --profile <file|name> --data <json> [--dry-run]",
    );
    return 2;
  }

  const profile = loadProfile(profileRef);
  const data = fs.readFileSync(dataPath, "utf-8");
  const source = { kind: "json" as const, content: data };

  const { notes, issues, warnings } = renderSource(source, profile);
  for (const w of warnings) console.warn("[warn]", w);
  for (const i of issues) console.error("[issue]", i.path, i.issue);
  if (issues.length > 0) {
    console.error("存在校验问题,中止写入(可用 --dry-run 查看)");
    return 1;
  }

  const writer = new FsWriter(vault);
  const existing = await writer.listExistingPaths();

  // 增量模式:按指纹只更新变化的笔记(不覆盖用户手写/未变化文件)
  let notesToWrite = notes;
  let changedPaths: string[] = [];
  if (profile.incremental) {
    const plan = await planIncremental(notes, writer);
    notesToWrite = plan.notes;
    changedPaths = plan.changed;
    console.log(`[incremental] 变化 ${plan.changed.length} 篇,跳过 ${plan.unchanged.length} 篇`);
  }

  const result = await applyNotes(notesToWrite, writer, {
    dryRun,
    conflictStrategy: profile.conflictStrategy ?? "overwrite",
    existingPaths: existing,
  });

  if (dryRun) {
    console.log(`[dry-run] 将写入 ${result.written.length} 篇,跳过 ${result.skipped.length} 篇`);
    for (const p of result.written) console.log("  +", p);
    return 0;
  }
  console.log(`已写入 ${result.written.length} 篇,跳过 ${result.skipped.length} 篇,错误 ${result.errors.length}`);
  for (const e of result.errors) console.error("  !", e);
  return result.errors.length > 0 ? 1 : 0;
}

// ===========================================================================
// lint 子命令:知识库健康检查(断链 / 孤立页 / index 一致性)
//   node cli.js lint --vault <dir> [--wiki-dir wiki]
// ===========================================================================

function lintMain(args: string[]): number {
  const a = parseArgs(args);
  const vault = vaultFor(a);
  if (!vault) {
    console.error("用法: workvault lint --vault <dir> [--wiki-dir wiki]");
    return 2;
  }
  const wikiDir = a["wiki-dir"] ?? "wiki";
  const r = lintWiki(vault, wikiDir);
  console.log(`[lint] wiki 页面 ${r.totalPages} 篇`);
  if (r.indexMissing) console.warn("  ! index.md 缺失(建议 compile 生成索引)");
  for (const b of r.brokenLinks) console.warn(`  ! 断链 ${b.from} → [[${b.link}]]`);
  for (const l of r.indexBrokenLinks) console.warn(`  ! index 断链 → [[${l}]]`);
  for (const p of r.orphanPages) console.warn(`  ! 孤立页 ${p}`);
  for (const c of r.conflicts) {
    console.warn(`  ! 矛盾线索 ${c.page} ↔ ${c.conflict_with}(${c.source} ${c.updated}):${c.claim.slice(0, 60)}`);
  }
  if (r.ok) {
    console.log(
      r.indexMissing
        ? "[lint] 健康 ✓(无断链、无孤立页;index 尚未生成,可按需 compile 生成索引)"
        : "[lint] 健康 ✓(无断链、无孤立页、index 一致)",
    );
    if (r.conflicts.length > 0) {
      console.log(`[lint] ⚠ 另有 ${r.conflicts.length} 条矛盾线索(claims 台账标注),建议人工核实后处理`);
    }
    return 0;
  }
  console.log(
    `[lint] 共 ${r.brokenLinks.length + r.indexBrokenLinks.length} 处断链 / ${r.orphanPages.length} 个孤立页 / ${r.conflicts.length} 条矛盾线索`,
  );
  return 1;
}

// ===========================================================================
// query 子命令:基于 wiki 回答(来源纪律 + 渐进式披露,可选归档)
//   node cli.js query --vault <dir> --question "问题" --schema <file>
//        [--provider openai|echo] [--api-base <url>] [--api-key <key>] [--model <m>]
//        [--wiki-dir wiki] [--archive] [--dry-run]
// ===========================================================================

async function queryMain(args: string[]): Promise<number> {
  const a = parseArgs(args);
  const vault = vaultFor(a);
  const question = a["question"];
  const schemaPath = a["schema"] ?? path.join(__dirname, "templates", "schemas", "query.AGENTS.md");
  const providerKind = a["provider"] ?? (autoProviderKind(config));
  const archive = a["archive"] === "true" || a["archive"] === "";

  if (!vault || !question) {
    console.error(
      "用法: workvault query --vault <dir> --question \"问题\" [--schema query.AGENTS.md] " +
        "[--provider openai|echo] [--api-base <url>] [--api-key <key>] [--model <m>] [--wiki-dir wiki] [--archive] [--dry-run]",
    );
    return 2;
  }
  if (!fs.existsSync(schemaPath)) {
    console.error(`schema 不存在: ${schemaPath}`);
    return 2;
  }

  let provider;
  try {
    provider = createProvider(providerKind, llmOpts(config, a));
  } catch (e) {
    console.error((e as Error).message);
    return 2;
  }

  console.log(`[query] ${question}`);
  const result = await queryWiki({
    provider,
    vault,
    question,
    schema: fs.readFileSync(schemaPath, "utf-8"),
    wikiDir: a["wiki-dir"] ?? "wiki",
    archive,
    dryRun: a["dry-run"] === "true" || a["dry-run"] === "",
  });

  for (const e of result.errors) console.error("  !", e);
  if (result.answer) {
    console.log("---- 回答 ----");
    console.log(result.answer);
  }
  if (result.sources.length > 0) console.log(`\n[query] 来源: ${result.sources.join(", ")}`);
  if (result.gaps.length > 0) console.log(`[query] 知识缺口: ${result.gaps.join("; ")}`);
  if (archive) {
    if (result.archived.length > 0) {
      console.log(`[query] 已归档 ${result.archived.length} 页: ${result.archived.join(", ")}`);
    } else {
      console.log("[query] 无新认知归档");
    }
  }
  return result.ok ? 0 : 1;
}

// ===========================================================================
// compile 子命令:raw 资料 + schema → LLM 编译 wiki(自然语言驱动入口)
//   node cli.js compile --vault <dir> --schema <file> --input <dir|file>
//        [--provider openai|echo] [--api-base <url>] [--api-key <key>] [--model <m>]
//        [--instruction "自然语言指令"] [--wiki-dir wiki] [--dry-run]
// ===========================================================================
function readInput(ref: string): Array<{ name: string; content: string }> {
  const stat = fs.statSync(ref);
  if (stat.isDirectory()) {
    const out: Array<{ name: string; content: string }> = [];
    const walk = (dir: string, prefix: string) => {
      for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
        if (entry.name.startsWith(".")) continue;
        const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
        const full = path.join(dir, entry.name);
        if (entry.isDirectory()) walk(full, rel);
        else if (entry.isFile() && TEXT_EXTS.includes(path.extname(entry.name).toLowerCase())) {
          out.push({ name: rel, content: fs.readFileSync(full, "utf-8") });
        }
      }
    };
    walk(ref, "");
    return out;
  }
  return [{ name: path.basename(ref), content: fs.readFileSync(ref, "utf-8") }];
}

async function compileMain(args: string[]): Promise<number> {
  const a = parseArgs(args);
  const vault = vaultFor(a);
  const schemaPath = a["schema"] ?? path.join(__dirname, "templates", "schemas", "wiki.AGENTS.md");
  const inputRef = a["input"];
  const providerKind = a["provider"] ?? (autoProviderKind(config));
  const instruction = a["instruction"];

  if (!vault || !schemaPath || !inputRef) {
    console.error(
      "用法: workvault compile --vault <dir> --schema <file> --input <dir|file> " +
        "[--provider openai|echo] [--api-base <url>] [--api-key <key>] [--model <m>] " +
        "[--instruction \"自然语言指令\"] [--wiki-dir wiki] [--dry-run]",
    );
    return 2;
  }
  if (!fs.existsSync(schemaPath)) {
    console.error(`schema 文件不存在: ${schemaPath}`);
    return 2;
  }
  if (!fs.existsSync(inputRef)) {
    console.error(`input 不存在: ${inputRef}`);
    return 2;
  }

  let provider;
  try {
    provider = createProvider(providerKind, llmOpts(config, a));
  } catch (e) {
    console.error((e as Error).message);
    return 2;
  }

  const schema = fs.readFileSync(schemaPath, "utf-8");
  const inputs = readInput(inputRef);
  if (inputs.length === 0) {
    console.error("input 中没有可编译的文本文件");
    return 2;
  }
  console.log(`[compile] 资料 ${inputs.length} 份 · provider ${providerKind} · schema ${path.basename(schemaPath)}`);
  if (instruction) console.log(`[compile] 指令: ${instruction}`);

  const result = await compileRaw({
    provider,
    schema,
    inputs,
    instruction,
    vault,
    wikiDir: a["wiki-dir"] ?? "wiki",
    dryRun: a["dry-run"] === "true" || a["dry-run"] === "",
  });

  // 来源台账:写入 <vault>/<wikiDir>/.ledgers/claims.json(合并去重)
  if (!(a["dry-run"] === "true" || a["dry-run"] === "") && result.claims.length > 0) {
    writeClaims(vault, a["wiki-dir"] ?? "wiki", result.claims);
  }

  if (result.log) console.log(`[compile] ${result.log}`);
  console.log(`[compile] claims ${result.claims.length} 条`);
  for (const e of result.errors) console.error("  !", e);
  if (a["dry-run"] === "true" || a["dry-run"] === "") {
    console.log(`[compile] 将写入 ${result.written.length} 篇,跳过 ${result.skipped.length} 篇(dry-run)`);
    for (const p of result.written) console.log("  +", p);
  } else {
    console.log(
      `[compile] 已写入 ${result.written.length} 篇,跳过 ${result.skipped.length} 篇,未变 ${result.unchanged.length} 篇,错误 ${result.errors.length}`,
    );
  }
  return result.ok ? 0 : 1;
}

/** 合并写入来源台账(按 page+claim 去重)——实现见 src/core/ledger.ts */


// ===========================================================================
// setup 子命令:交互式配置向导(无需敲命令行参数)
//   node cli.js setup
// ===========================================================================
async function setupMain(): Promise<number> {
  const cfg = await runSetup();
  if (cfg === null) {
    // 用户选择跳过 → 直接进入对话
    return chatMain();
  }
  const target = path.join(process.cwd(), "workvault.config.json");
  console.log("\n✅ 已生成 " + target);
  console.log("   vault: " + cfg.vault);
  console.log("   LLM: " + (cfg.provider.apiBase || "?") + " | " + (cfg.provider.model || "?"));
  if (cfg.provider.apiKey) {
    console.log("   API Key: 已配置(" + String(cfg.provider.apiKey).slice(0, 6) + "…)");
  } else {
    console.log("   ⚠️  API Key 未配置,可重跑 setup 补上");
  }
  console.log("");
  // 配置完成 → 直接进入对话模式
  return chatMain();
}

// ===========================================================================
// chat 子命令:对话式 CLI(自然语言驱动 run/compile/query/lint)
//   node cli.js chat   (或直接 node cli.js)
// ===========================================================================
async function chatMain(): Promise<number> {
  const vault = vaultFor({});
  if (!vault) {
    console.error("未配置 vault。请先运行: node cli.js setup");
    return 2;
  }
  let provider;
  try {
    provider = createProvider(autoProviderKind(config), llmOpts(config));
  } catch (e) {
    console.error((e as Error).message);
    return 2;
  }
  const readSchema = (name: string): string => {
    const p = path.join(__dirname, "templates", "schemas", name);
    return fs.existsSync(p) ? fs.readFileSync(p, "utf-8") : "";
  };
  return runChat({
    provider,
    vault,
    schemaWiki: readSchema("wiki.AGENTS.md"),
    schemaQuery: readSchema("query.AGENTS.md"),
    cwd: process.cwd(),
  });
}

// ===========================================================================
// session 子命令:会话记忆生命周期
//   node cli.js session start --vault <dir> [--focus "主题"] [--project p] [--memory-dir memory]
//   node cli.js session end   --vault <dir> --summary "摘要" [--project p] [--agent 名称]
//        [--auto-capture] [--provider openai|echo] [--api-base <url>] [--api-key <key>] [--model <m>]
//        [--auto-commit] [--memory-dir memory]
// ===========================================================================
async function sessionMain(args: string[]): Promise<number> {
  const cmd = args[0];
  const a = parseArgs(args.slice(1));
  const vault = vaultFor(a);
  if (!vault) {
    console.error("用法: workvault session <start|end> --vault <dir> ...");
    return 2;
  }
  const memoryDir = a["memory-dir"];
  if (cmd === "start") {
    const context = sessionStart(vault, { project: a["project"], focus: a["focus"], memoryDir });
    console.log(context);
    return 0;
  }
  if (cmd === "end") {
    const summary = a["summary"];
    if (!summary) {
      console.error("session end 需要 --summary \"会话摘要\"");
      return 2;
    }
    const autoCapture = a["auto-capture"] === "true" || a["auto-capture"] === "";
    let provider;
    if (autoCapture) {
      try {
        provider = createProvider(a["provider"] ?? autoProviderKind(config), llmOpts(config, a));
      } catch (e) {
        console.error((e as Error).message);
        return 2;
      }
    }
    const schemaPath = path.join(__dirname, "templates", "schemas", "memory.AGENTS.md");
    const r = await sessionEnd(vault, {
      summary,
      project: a["project"],
      agent: a["agent"],
      memoryDir,
      provider,
      autoCapture,
      schema: fs.existsSync(schemaPath) ? fs.readFileSync(schemaPath, "utf-8") : undefined,
      autoCommit: a["auto-commit"] === "true" || a["auto-commit"] === "",
    });
    if (!r.ok) {
      console.error("! " + r.error);
      return 1;
    }
    console.log(`[session] 日志 → ${r.journal}`);
    if (r.pending && r.pending.length > 0) {
      console.log(`[session] 提炼 ${r.pending.length} 条候选记忆到 .pending/(确认: workvault memory pending --confirm <名称>)`);
    }
    if (r.commit) console.log("[session] " + r.commit);
    if (r.captureError) console.error("! 提炼失败: " + r.captureError);
    return 0;
  }
  console.error("用法: workvault session <start|end> --vault <dir> ...");
  return 2;
}

// ===========================================================================
// memory 子命令:记忆写入 / 检索 / 列出 / 提炼 / 挂起确认
//   write:    workvault memory write --vault <dir> --type fact --title "t" --description "d" [--content "c"] [--project p] [--tags a,b] [--dry-run]
//             或 --json '{"type":"fact","title":"t","description":"d","content":"c"}'
//   search:   workvault memory search --vault <dir> --query "关键词" [--type fact] [--project p] [--limit 10]
//   list:     workvault memory list --vault <dir> [--type fact] [--project p]
//   capture:  workvault memory capture --vault <dir> --text "会话内容" [--preview] [--provider ...]
//   pending:  workvault memory pending --vault <dir>               (列出挂起)
//             workvault memory pending --vault <dir> --confirm <名称> (确认入库)
// ===========================================================================
async function memoryMain(args: string[]): Promise<number> {
  const cmd = args[0];
  const a = parseArgs(args.slice(1));
  const vault = vaultFor(a);
  if (!vault) {
    console.error("用法: workvault memory <write|search|list|capture|pending> --vault <dir> ...");
    return 2;
  }
  const memoryDir = a["memory-dir"];

  if (cmd === "write") {
    let entry: Record<string, unknown>;
    if (a["json"]) {
      try {
        entry = JSON.parse(a["json"]);
      } catch (e) {
        console.error("--json 不是合法 JSON: " + (e as Error).message);
        return 2;
      }
    } else {
      entry = {
        type: a["type"],
        title: a["title"],
        description: a["description"],
        content: a["content"] ?? "",
        project: a["project"] || undefined,
        agent: a["agent"] || undefined,
        source: a["source"] || undefined,
        tags: a["tags"] ? a["tags"].split(",").map((s) => s.trim()).filter(Boolean) : undefined,
      };
      if (!entry.type || !entry.title || !entry.description) {
        console.error("memory write 需要 --type --title --description(或用 --json 传整条)");
        return 2;
      }
    }
    const r = writeMemory(
      vault,
      {
        type: String(entry.type) as MemoryType,
        title: String(entry.title),
        description: String(entry.description),
        content: String(entry.content ?? ""),
        project: entry.project ? String(entry.project) : undefined,
        agent: entry.agent ? String(entry.agent) : undefined,
        source: entry.source ? String(entry.source) : undefined,
        tags: Array.isArray(entry.tags) ? entry.tags.map(String) : undefined,
      },
      { memoryDir, dryRun: a["dry-run"] === "true" || a["dry-run"] === "" },
    );
    if (!r.ok) {
      console.error("! " + r.reason);
      return 1;
    }
    console.log(`✓ memory/${r.path}` + (a["dry-run"] === "true" || a["dry-run"] === "" ? "(dry-run)" : ""));
    return 0;
  }

  if (cmd === "search") {
    const hits = searchMemory(
      vault,
      { query: a["query"], type: a["type"], project: a["project"], limit: a["limit"] ? Number(a["limit"]) : undefined },
      memoryDir,
    );
    if (hits.length === 0) {
      console.log("无匹配记忆");
      return 0;
    }
    for (const h of hits) {
      console.log(`- [${h.type}] ${h.title}(${h.updated ?? h.created ?? ""})${h.description ? ":" + h.description : ""}`);
    }
    console.log(`共 ${hits.length} 条`);
    return 0;
  }

  if (cmd === "list") {
    const entries = listMemory(vault, { type: a["type"], project: a["project"] }, memoryDir);
    if (entries.length === 0) {
      console.log("暂无记忆");
      return 0;
    }
    for (const e of entries) {
      console.log(`- [${e.type}] ${e.title}(${e.updated ?? e.created ?? ""})${e.description ? ":" + e.description : ""}`);
    }
    console.log(`共 ${entries.length} 条`);
    return 0;
  }

  if (cmd === "capture") {
    const text = a["text"];
    if (!text) {
      console.error("memory capture 需要 --text \"会话内容\"");
      return 2;
    }
    let provider;
    try {
      provider = createProvider(a["provider"] ?? autoProviderKind(config), llmOpts(config, a));
    } catch (e) {
      console.error((e as Error).message);
      return 2;
    }
    const schemaPath = path.join(__dirname, "templates", "schemas", "memory.AGENTS.md");
    const cap = await captureMemory(vault, {
      provider,
      text,
      project: a["project"],
      agent: a["agent"],
      memoryDir,
      schema: fs.existsSync(schemaPath) ? fs.readFileSync(schemaPath, "utf-8") : undefined,
      writePending: a["preview"] !== "true",
    });
    if (cap.error) {
      console.error("! " + cap.error);
      return 1;
    }
    if (cap.drafts.length === 0) {
      console.log("没有提炼出值得记忆的内容");
      return 0;
    }
    for (const d of cap.drafts) {
      console.log(`- [${d.type}] ${d.title}:${d.description}`);
    }
    if (cap.written.length > 0) {
      console.log(`已写入 ${cap.written.length} 条到 .pending/(确认: workvault memory pending --confirm <名称>)`);
    } else {
      console.log("(preview 模式,未写入)");
    }
    return 0;
  }

  if (cmd === "pending") {
    if (a["confirm"]) {
      const r = confirmPending(vault, a["confirm"], { memoryDir });
      if (!r.ok) {
        console.error("! " + r.reason);
        return 1;
      }
      console.log(`✓ 已确认入库: memory/${r.path}`);
      return 0;
    }
    const pending = listPendingMemory(vault, memoryDir);
    if (pending.length === 0) {
      console.log("无挂起记忆");
      return 0;
    }
    for (const p of pending) {
      console.log(`- ${p.path} [${p.type}] ${p.description}`);
    }
    console.log(`共 ${pending.length} 条(确认: workvault memory pending --confirm <名称>)`);
    return 0;
  }

  console.error("用法: workvault memory <write|search|list|capture|pending> --vault <dir> ...");
  return 2;
}

// 直接运行时执行(node cli.js run / compile / query ...)
if (require.main === module) {
  cliMain(process.argv.slice(2)).then((code) => process.exit(code));
}