{"version":3,"file":"cli.mjs","names":["validateApi","checkApi","replayApi","suggestApi"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Agent-perms CLI: convert, validate, check, and sync cross-agent permission policies, and replay\n * them over recorded sessions.\n *\n * All flags, no positionals. Format names resolve to default config file locations. Use \"-\" for\n * stdin/stdout.\n *\n * Exit codes: 0 = success, 1 = error, 2 = validation failure.\n */\n\nimport { parseArgs } from \"node:util\";\nimport { dirname, resolve, join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport {\n  convert,\n  validate as validateApi,\n  check as checkApi,\n  replay as replayApi,\n  suggest as suggestApi,\n  resolveFormat,\n  ConvertError,\n  type CheckContext,\n  type Format,\n} from \"./api.ts\";\nimport { OmpEncoded, agentId } from \"./compat/codecs.ts\";\nimport { isAgentId } from \"./guards.ts\";\nimport { ruleToString, stepSource, type DecisionStep } from \"./evaluate.ts\";\nimport { confine, type OutsideBehaviour } from \"./confine.ts\";\nimport { sync } from \"./sync.ts\";\nimport {\n  AGENT_FILES,\n  defaultOmpAgentDir,\n  editOmpConfig,\n  findDefaultFile,\n  readInput,\n  parseAgentFile,\n  parseJson,\n  stringifyAgentFile,\n  writeJsonFile,\n} from \"./agent-files.ts\";\n\nconst AGENTS = agentId.options;\ntype Agent = (typeof AGENTS)[number];\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction error(message: string): never {\n  process.stderr.write(`error: ${message}\\n`);\n  process.exit(1);\n}\n\nfunction isAgent(value: string): value is Agent {\n  for (const agent of AGENTS) {\n    if (agent === value) return true;\n  }\n  return false;\n}\n\n// ---------------------------------------------------------------------------\n// Resolution helpers\n// ---------------------------------------------------------------------------\n\n/** Whether a spec names a format rather than a file (a file path's format is detected separately). */\nfunction isFormatName(spec: string): spec is Format {\n  return spec === \"canonical\" || isAgentId(spec);\n}\n\n/** Resolve a spec to an input file path. Format name → walk up, file path → direct, \"-\" → stdin. */\nfunction resolveInputSpec(spec: string | undefined): string | undefined {\n  if (spec === undefined || spec === \"-\") return undefined;\n  if (isFormatName(spec)) return findDefaultFile(spec, process.cwd());\n  return resolve(spec);\n}\n\n/** Resolve a spec to an output file path. Format name → cwd, file path → direct, \"-\" → stdout. */\nfunction resolveOutputSpec(spec: string | undefined): string | undefined {\n  if (spec === undefined || spec === \"-\") return undefined;\n  if (isFormatName(spec)) {\n    return resolve(join(process.cwd(), AGENT_FILES[spec].name));\n  }\n  return resolve(spec);\n}\nfunction firstString(\n  ...values: (string | boolean | undefined)[]\n): string | undefined {\n  for (const v of values) {\n    if (typeof v === \"string\") return v;\n  }\n  return undefined;\n}\n\nfunction allStrings(\n  ...values: ((string | boolean | undefined)[] | undefined)[]\n): string[] {\n  const result: string[] = [];\n  for (const arr of values) {\n    if (arr === undefined) continue;\n    for (const v of arr) {\n      if (typeof v === \"string\") result.push(v);\n    }\n  }\n  return result;\n}\n\n// ---------------------------------------------------------------------------\n// convert\n// ---------------------------------------------------------------------------\n\nasync function convertCommand(args: string[]): Promise<void> {\n  const { values } = parseArgs({\n    args,\n    options: {\n      from: { type: \"string\", short: \"f\" },\n      to: { type: \"string\", short: \"t\" },\n      input: { type: \"string\" },\n      in: { type: \"string\" },\n      output: { type: \"string\", short: \"o\" },\n      out: { type: \"string\" },\n      compact: { type: \"boolean\", short: \"c\" },\n      verbose: { type: \"boolean\", short: \"v\" },\n    },\n    strict: true,\n  });\n\n  // Merge aliases: --input/--in → --from\n  const fromSpec = firstString(values.from, values.input, values.in);\n  // --to is always the target format/file\n  const toSpec = values.to;\n  // --output/--out overrides destination (--to might set it too)\n  const outputSpec = firstString(values.output, values.out);\n  if (toSpec === undefined) error(\"--to is required\");\n\n  // Resolve input: format name finds file, file path reads directly, omitted = stdin\n  const inputPath = resolveInputSpec(fromSpec);\n  let fromFormat: Format | undefined;\n  if (fromSpec !== undefined && fromSpec !== \"-\") {\n    fromFormat = resolveFormat(fromSpec);\n    // If not a known format name and not an existing file, it's an unknown format\n    if (\n      fromFormat === undefined &&\n      inputPath !== undefined &&\n      !existsSync(inputPath)\n    ) {\n      error(\n        `unknown --from format: ${fromSpec}. Use an agent name, a config file path, or \"-\" for stdin`,\n      );\n    }\n  }\n\n  // Resolve output: format name → default file, file path → directly, \"-\" = stdout\n  const toFormat = resolveFormat(toSpec);\n  if (!toFormat) {\n    error(\n      `unknown --to format: ${toSpec}. Use an agent name (claude-code, codex, kiro, opencode, crush, omp, canonical), a config file path, or \"-\" for stdout`,\n    );\n  }\n  const outputPath = outputSpec\n    ? resolveOutputSpec(outputSpec)\n    : resolveOutputSpec(toSpec);\n\n  // No need to validate --from — auto-detect handles unknown file paths\n\n  const source = inputPath ?? \"stdin\";\n  const raw = await readInput(inputPath);\n  const parsed = parseAgentFile(fromFormat, raw, source);\n  if (!parsed.ok) error(parsed.error);\n  const json = parsed.value;\n\n  try {\n    const result = convert(fromFormat, toFormat, json);\n\n    const jsonStr = stringifyAgentFile(\n      toFormat,\n      result.output,\n      values.compact === true,\n    );\n\n    if (outputPath) {\n      // An existing OMP config holds much more than permissions, so it is edited in place\n      await writeJsonFile(\n        outputPath,\n        toFormat === \"omp\" && existsSync(outputPath)\n          ? editOmpConfig(\n              await readFile(outputPath, \"utf-8\"),\n              OmpEncoded.parse(result.output),\n            )\n          : jsonStr,\n      );\n      for (const companion of result.companions) {\n        await writeJsonFile(\n          join(dirname(outputPath), companion.path),\n          companion.content,\n        );\n      }\n    } else if (result.companions.length > 0) {\n      error(\n        `${toFormat} also needs ${result.companions.map((c) => c.path).join(\", \")} beside its config, which stdout cannot carry; pass --output`,\n      );\n    } else {\n      process.stdout.write(jsonStr);\n    }\n\n    if (values.verbose) {\n      const dest = outputPath ?? \"stdout\";\n      process.stderr.write(\n        `Decoded ${result.from} → canonical (${String(result.ruleCount)} rules), encoded → ${toFormat}, wrote ${dest}\\n`,\n      );\n    }\n  } catch (e) {\n    if (e instanceof ConvertError) {\n      process.stderr.write(`error: ${e.message}\\n`);\n      for (const err of e.errors) {\n        process.stderr.write(`  ${err.path}: ${err.message}\\n`);\n      }\n      process.exit(2);\n    }\n    const message = e instanceof Error ? e.message : String(e);\n    error(message);\n  }\n}\n\n// ---------------------------------------------------------------------------\n// validate\n// ---------------------------------------------------------------------------\n\nasync function validateCommand(args: string[]): Promise<void> {\n  const { values } = parseArgs({\n    args,\n    options: {\n      input: { type: \"string\", short: \"i\" },\n      in: { type: \"string\" },\n    },\n    strict: true,\n  });\n\n  const inputSpec = firstString(values.input, values.in);\n  const inputPath = resolveInputSpec(inputSpec);\n  const source = inputPath ?? \"stdin\";\n\n  const raw = await readInput(inputPath);\n  const parsed = parseJson(raw, source);\n  if (!parsed.ok) error(parsed.error);\n  const json = parsed.value;\n\n  const result = validateApi(json);\n  if (result.valid) {\n    process.stdout.write(\"valid\\n\");\n    return;\n  }\n\n  process.stderr.write(\"validation errors:\\n\");\n  for (const err of result.errors) {\n    process.stderr.write(`  ${err.path}: ${err.message}\\n`);\n  }\n  process.exit(2);\n}\n\n// ---------------------------------------------------------------------------\n// check\n// ---------------------------------------------------------------------------\n\n/** Read repeated `--env NAME=VALUE` flags into the record the evaluation context takes. */\nfunction parseEnvFlags(flags: readonly string[]): Record<string, string> {\n  const env: Record<string, string> = {};\n  for (const flag of flags) {\n    const equals = flag.indexOf(\"=\");\n    if (equals <= 0) error(`--env expects NAME=VALUE, got \"${flag}\"`);\n    env[flag.slice(0, equals)] = flag.slice(equals + 1);\n  }\n  return env;\n}\n\n/** Read `--depth`, the number of agents between the call and the top-level agent. */\nfunction parseDepthFlag(flag: string): number {\n  const depth = Number(flag);\n  if (!Number.isInteger(depth) || depth < 0) {\n    error(`--depth expects a whole number of 0 or more, got \"${flag}\"`);\n  }\n  return depth;\n}\n\n/** One line describing how a command was judged, for `check --explain`. */\nfunction formatStep(step: DecisionStep): string {\n  const by = stepSource(step);\n  const note =\n    step.reason === \"unsplittable\" ? \" (line not fully parsed, so asks)\" : \"\";\n  const hidden = step.hidden === true ? \" (hidden)\" : \"\";\n  return `${step.decision}\\t${step.command}\\t${by}${hidden}${note}`;\n}\n\nasync function checkCommand(args: string[]): Promise<void> {\n  const { values } = parseArgs({\n    args,\n    options: {\n      tool: { type: \"string\" },\n      input: { type: \"string\" },\n      \"policy-file\": { type: \"string\" },\n      cwd: { type: \"string\" },\n      branch: { type: \"string\" },\n      remote: { type: \"string\" },\n      env: { type: \"string\", multiple: true },\n      depth: { type: \"string\" },\n      actor: { type: \"string\" },\n      role: { type: \"string\", multiple: true },\n      explain: { type: \"boolean\" },\n    },\n    strict: true,\n  });\n\n  if (!values.tool) error(\"--tool is required\");\n  if (values.input === undefined) error(\"--input is required\");\n\n  const inputPath = resolveInputSpec(values[\"policy-file\"]);\n  const source = inputPath ?? \"stdin\";\n\n  const raw = await readInput(inputPath);\n  const parsed = parseJson(raw, source);\n  if (!parsed.ok) error(parsed.error);\n  const json = parsed.value;\n\n  try {\n    const ctx: CheckContext = {};\n    if (values.cwd !== undefined) ctx.cwd = values.cwd;\n    if (values.branch !== undefined) ctx.branch = values.branch;\n    if (values.remote !== undefined) ctx.remote = values.remote;\n    if (values.env !== undefined) ctx.env = parseEnvFlags(values.env);\n    if (values.depth !== undefined) ctx.depth = parseDepthFlag(values.depth);\n    if (values.actor !== undefined) ctx.actor = values.actor;\n    if (values.role !== undefined) ctx.roles = values.role;\n    const result = checkApi(values.tool, values.input, json, ctx);\n    process.stdout.write(`${result.decision}\\n`);\n    if (values.explain) {\n      for (const step of result.steps) {\n        process.stderr.write(`${formatStep(step)}\\n`);\n      }\n    }\n    process.exit(result.decision === \"deny\" ? 1 : 0);\n  } catch (e) {\n    if (e instanceof ConvertError) {\n      process.stderr.write(`error: ${e.message}\\n`);\n      for (const err of e.errors) {\n        process.stderr.write(`  ${err.path}: ${err.message}\\n`);\n      }\n      process.exit(2);\n    }\n    const message = e instanceof Error ? e.message : String(e);\n    error(message);\n  }\n}\n\n// ---------------------------------------------------------------------------\n// replay / suggest\n// ---------------------------------------------------------------------------\n\n/** Report a policy or transcript the command cannot read, and exit. */\nfunction inputError(e: unknown): never {\n  if (e instanceof ConvertError) {\n    process.stderr.write(`error: ${e.message}\\n`);\n    for (const err of e.errors) {\n      process.stderr.write(`  ${err.path}: ${err.message}\\n`);\n    }\n    process.exit(2);\n  }\n  error(e instanceof Error ? e.message : String(e));\n}\n\n/** Read a policy spec as parsed JSON, exiting on unreadable input. */\nasync function readPolicy(spec: string | undefined): Promise<unknown> {\n  const inputPath = resolveInputSpec(spec);\n  const parsed = parseJson(await readInput(inputPath), inputPath ?? \"stdin\");\n  if (!parsed.ok) error(parsed.error);\n  return parsed.value;\n}\n\n/** Read a transcript file, or stdin for \"-\". */\nfunction readTranscript(spec: string): Promise<string> {\n  return readInput(spec === \"-\" ? undefined : resolve(spec));\n}\n\nasync function replayCommand(args: string[]): Promise<void> {\n  const { values } = parseArgs({\n    args,\n    options: {\n      \"policy-file\": { type: \"string\" },\n      transcript: { type: \"string\" },\n    },\n    strict: true,\n  });\n\n  if (values.transcript === undefined) error(\"--transcript is required\");\n  // An omitted --policy-file reads stdin, as it does for check.\n  if (\n    values.transcript === \"-\" &&\n    (values[\"policy-file\"] === undefined || values[\"policy-file\"] === \"-\")\n  ) {\n    error(\"--transcript and --policy-file cannot both be read from stdin\");\n  }\n\n  const policy = await readPolicy(values[\"policy-file\"]);\n  const transcript = await readTranscript(values.transcript);\n  try {\n    const report = replayApi(policy, transcript);\n    let out = \"\";\n    for (const [decision, count] of Object.entries(report.counts)) {\n      out += `${decision}\\t${String(count)}\\n`;\n    }\n    if (report.differing.length > 0) {\n      out += \"\\ndiffers from the transcript:\\n\";\n      for (const { call, decision } of report.differing) {\n        out += `${decision}\\t${call.outcome ?? \"\"}\\t${call.tool}\\t${call.subject ?? \"\"}\\n`;\n      }\n    }\n    process.stdout.write(out);\n  } catch (e) {\n    inputError(e);\n  }\n}\n\nasync function suggestCommand(args: string[]): Promise<void> {\n  const { values } = parseArgs({\n    args,\n    options: {\n      transcript: { type: \"string\" },\n      \"policy-file\": { type: \"string\" },\n      // Approved more than once is what makes a call repeated.\n      \"min-count\": { type: \"string\", default: \"2\" },\n    },\n    strict: true,\n  });\n\n  if (values.transcript === undefined) error(\"--transcript is required\");\n  const minCount = Number(values[\"min-count\"]);\n  if (!Number.isInteger(minCount) || minCount < 1) {\n    error(\"--min-count must be a positive integer\");\n  }\n  if (values.transcript === \"-\" && values[\"policy-file\"] === \"-\") {\n    error(\"--transcript and --policy-file cannot both be read from stdin\");\n  }\n\n  const policy =\n    values[\"policy-file\"] === undefined\n      ? undefined\n      : await readPolicy(values[\"policy-file\"]);\n  const transcript = await readTranscript(values.transcript);\n  try {\n    const suggestions = suggestApi(transcript, {\n      minCount,\n      ...(policy === undefined ? {} : { policy }),\n    });\n    const rules = suggestions.map((s) => s.rule);\n    process.stdout.write(JSON.stringify({ rules }, null, 2) + \"\\n\");\n    for (const { rule, count } of suggestions) {\n      process.stderr.write(`${String(count)}\\t${ruleToString(rule)}\\n`);\n    }\n  } catch (e) {\n    inputError(e);\n  }\n}\n\n// ---------------------------------------------------------------------------\n// sync\n// ---------------------------------------------------------------------------\n\nasync function syncCommand(args: string[]): Promise<void> {\n  const { values } = parseArgs({\n    args,\n    options: {\n      \"working-dir\": { type: \"string\", short: \"d\" },\n      up: { type: \"string\", default: \"all\", short: \"u\" },\n      with: { type: \"string\", multiple: true, short: \"w\" },\n      without: { type: \"string\", multiple: true, short: \"x\" },\n      include: { type: \"string\", multiple: true },\n      exclude: { type: \"string\", multiple: true },\n      yes: { type: \"boolean\", short: \"y\" },\n      \"dry-run\": { type: \"boolean\" },\n      create: { type: \"boolean\", short: \"c\" },\n      verbose: { type: \"boolean\", short: \"v\" },\n      backup: { type: \"boolean\", short: \"b\" },\n      \"omp-global\": { type: \"boolean\" },\n    },\n    strict: true,\n  });\n\n  // Parse --up value\n  let up: number;\n  if (values.up === \"all\") {\n    up = Infinity;\n  } else {\n    up = Number(values.up);\n    if (!Number.isInteger(up) || up < 0) {\n      error(\"--up must be a non-negative integer or 'all'\");\n    }\n  }\n\n  // Merge --with and --include (aliases)\n  const withRaw = allStrings(values.with, values.include);\n  // Merge --without and --exclude (aliases)\n  const withoutRaw = allStrings(values.without, values.exclude);\n\n  if (withRaw.length > 0 && withoutRaw.length > 0) {\n    error(\"--with and --without are mutually exclusive\");\n  }\n\n  // Validate agent names\n  const withAgents: Agent[] = [];\n  for (const w of withRaw) {\n    if (w === \"canonical\") continue;\n    if (!isAgent(w))\n      error(\n        `unknown agent: ${w}. Valid: ${[...AGENTS, \"canonical\"].join(\", \")}`,\n      );\n    withAgents.push(w);\n  }\n\n  const withoutAgents: Agent[] = [];\n  for (const w of withoutRaw) {\n    if (w === \"canonical\") continue;\n    if (!isAgent(w))\n      error(\n        `unknown agent: ${w}. Valid: ${[...AGENTS, \"canonical\"].join(\", \")}`,\n      );\n    withoutAgents.push(w);\n  }\n\n  const cwd = values[\"working-dir\"]\n    ? resolve(values[\"working-dir\"])\n    : process.cwd();\n\n  const result = await sync({\n    cwd,\n    up,\n    with: withAgents,\n    without: withoutAgents,\n    yes: values.yes ?? false,\n    dryRun: values[\"dry-run\"] ?? false,\n    create: values.create ?? false,\n    verbose: values.verbose ?? false,\n    backup: values.backup ?? false,\n    ompAgentDir: defaultOmpAgentDir(),\n    ompGlobal: values[\"omp-global\"] ?? false,\n  });\n  if (result.refused.length > 0) process.exit(1);\n}\n\n// ---------------------------------------------------------------------------\n// confine\n// ---------------------------------------------------------------------------\n\nfunction isOutsideBehaviour(value: string): value is OutsideBehaviour {\n  return value === \"ask\" || value === \"deny\";\n}\n\nasync function confineCommand(args: string[]): Promise<void> {\n  const { values } = parseArgs({\n    args,\n    options: {\n      root: { type: \"string\", short: \"r\", multiple: true },\n      outside: { type: \"string\", default: \"ask\" },\n      output: { type: \"string\", short: \"o\" },\n      out: { type: \"string\" },\n      compact: { type: \"boolean\", short: \"c\" },\n    },\n    strict: true,\n  });\n\n  const roots = allStrings(values.root);\n  if (roots.length === 0) error(\"--root is required\");\n  if (!isOutsideBehaviour(values.outside)) {\n    error(`--outside must be \"ask\" or \"deny\", got: ${values.outside}`);\n  }\n\n  const policy = confine({\n    roots,\n    cwd: process.cwd(),\n    outside: values.outside,\n  });\n  const jsonStr =\n    JSON.stringify(policy, null, values.compact ? undefined : 2) + \"\\n\";\n\n  const outputSpec = firstString(values.output, values.out);\n  const outputPath =\n    outputSpec === undefined ? undefined : resolveOutputSpec(outputSpec);\n  if (outputPath) {\n    await writeJsonFile(outputPath, jsonStr);\n  } else {\n    process.stdout.write(jsonStr);\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Main\n// ---------------------------------------------------------------------------\n\nfunction usage(stream: \"stdout\" | \"stderr\"): void {\n  const target = stream === \"stdout\" ? process.stdout : process.stderr;\n  target.write(`agent-perms — cross-agent permission policy tool\n\nUsage:\n  agent-perms convert [--from <spec>] --to <spec>\n  agent-perms validate [--input <spec>]\n  agent-perms check --tool <name> --input <cmd> [--policy-file <spec>]\n  agent-perms replay --transcript <file> [--policy-file <spec>]\n  agent-perms suggest --transcript <file> [--policy-file <spec>] [--min-count <n>]\n  agent-perms confine --root <dir> [--root <dir>] [--output <file>]\n  agent-perms sync\n\nSpecs: agent name, config file path, or \"-\" for stdin/stdout.\n\n  Format names resolve to default config files:\n    claude-code  →  .claude/settings.json\n    canonical    →  .agents/permissions.json\n    opencode     →  opencode.json\n    kiro         →  .kiro/permissions.json\n    codex        →  codex.toml\n    crush        →  .crush.json\n\nCommands:\n  convert   Convert between agent formats\n  validate  Validate a policy file\n  check     Evaluate a tool call against a policy\n  replay    Count what a policy decides for each call in a session transcript\n  suggest   Propose allow rules for calls a session transcript approved repeatedly\n  confine   Generate rules that confine Read, Edit and Write to directories\n  sync      Detect, merge, and write agent configs (bidirectional)\n  mcp       Serve the MCP sync daemon on stdio\n\nConvert flags:\n  -f, --from, --input, --in <spec>   Source (format, file, or \"-\" for stdin)\n  -t, --to, --output, --out <spec>   Target (format, file, or \"-\" for stdout)\n  -c, --compact                      Output compact JSON\n  -v, --verbose                      Show decode/encode summary on stderr\n\nValidate flags:\n  -i, --input, --in <spec>           Policy file (format, file, or \"-\" for stdin)\n\nCheck flags:\n  --tool <name>                      Tool name (required)\n  --input <cmd>                      Tool input string (required)\n  --policy-file <spec>               Policy file (format, file, or \"-\" for stdin)\n  --cwd, --branch, --remote          Evaluation context\n  --env NAME=VALUE                   Environment variable in the context (repeatable)\n  --depth <n>                        Agents between the call and the top-level agent\n  --actor <name>, --role <name>      Who is making the call and the roles they hold (--role repeats)\n  --explain                          Print how each command was judged, to stderr\n\nReplay flags:\n  --transcript <file>                Session transcript, JSON Lines (or \"-\" for stdin)\n  --policy-file <spec>               Policy file (format, file, or \"-\" for stdin)\n\nSuggest flags:\n  --transcript <file>                Session transcript, JSON Lines (or \"-\" for stdin)\n  --policy-file <spec>               Policy whose rules exclude or already cover calls\n  --min-count <n>                    Approvals a rule must cover (default: 2)\nConfine flags:\n  -r, --root <dir>                   Directory the file tools may reach (repeatable, required)\n  --outside <ask|deny>               Outside the roots: prompt (default) or refuse\n  -o, --output, --out <spec>         Write to a file instead of stdout\n  -c, --compact                      Output compact JSON\n  Confine is a convenience, not a sandbox: only Read, Edit and Write are constrained, and a Bash\n  command can still reach outside the roots. Use the policy's sandbox field to enforce that. The\n  default mode set for --outside applies to every tool with no rule of its own, not only file tools.\n\nSync flags:\n  -d, --working-dir <path>           Starting directory (default: cwd)\n  -u, --up <n|all>                   Ascend n parent directories (default: all)\n  -w, --with <agent>                 Only sync these agents (repeatable)\n  -x, --without <agent>              Sync all except these agents (repeatable)\n  -y, --yes                          Apply without prompting\n  --dry-run                          Show changes only, never write\n  -c, --create                       Create config files that don't exist\n  -v, --verbose                      Show rule provenance\n  -b, --backup                       Write .bak files before overwriting\n  --omp-global                       Also write Oh My Pi's global config (every OMP session reads it)\n\nMcp flags:\n  --permission-prompt                Expose the permission_prompt tool\n\nExamples:\n  agent-perms convert --from claude-code --to canonical\n  agent-perms convert --from .claude/settings.json --to crush\n  agent-perms convert --from claude-code --to -\n  cat settings.json | agent-perms convert --from - --to canonical --output -\n  agent-perms validate --input canonical\n  agent-perms validate --input .agents/permissions.json\n  agent-perms check --tool Bash --input \"git status\" --policy-file canonical\n  agent-perms replay --transcript session.jsonl --policy-file canonical\n  agent-perms suggest --transcript session.jsonl --policy-file canonical\n  agent-perms confine --root . --root ../shared --output .agents/permissions.json\n  agent-perms sync\n  agent-perms sync -y\n  agent-perms sync --dry-run\n  agent-perms sync -w claude-code -w opencode\n  agent-perms sync -x codex\n  agent-perms sync -w claude-code --create\n  agent-perms mcp --permission-prompt\n`);\n}\n\n// ---------------------------------------------------------------------------\n// mcp\n// ---------------------------------------------------------------------------\n\nasync function mcpCommand(args: string[]): Promise<void> {\n  const { values } = parseArgs({\n    args,\n    options: { \"permission-prompt\": { type: \"boolean\" } },\n    strict: true,\n  });\n  // Loaded on demand so the other commands never pull in the MCP SDK.\n  const { startMcpServer } = await import(\"./mcp.ts\");\n  await startMcpServer(\n    values[\"permission-prompt\"] === true ? { permissionPrompt: {} } : {},\n  );\n}\n\nasync function main(): Promise<void> {\n  // If invoked as agent-perms-mcp, route directly to MCP server\n  const binName = process.argv[1]?.split(\"/\").pop() ?? \"\";\n  if (binName === \"agent-perms-mcp\") {\n    await mcpCommand(process.argv.slice(2));\n    return;\n  }\n\n  const args = process.argv.slice(2);\n  const command = args[0];\n\n  switch (command) {\n    case \"convert\":\n      await convertCommand(args.slice(1));\n      break;\n    case \"validate\":\n      await validateCommand(args.slice(1));\n      break;\n    case \"check\":\n      await checkCommand(args.slice(1));\n      break;\n    case \"replay\":\n      await replayCommand(args.slice(1));\n      break;\n    case \"suggest\":\n      await suggestCommand(args.slice(1));\n      break;\n    case \"confine\":\n      await confineCommand(args.slice(1));\n      break;\n    case \"sync\":\n      await syncCommand(args.slice(1));\n      break;\n    case \"mcp\":\n      await mcpCommand(args.slice(1));\n      break;\n    case \"--help\":\n    case \"-h\":\n      // An explicit help request is a successful invocation: usage on stdout, exit 0.\n      usage(\"stdout\");\n      return;\n    default:\n      if (command) {\n        process.stderr.write(`unknown command: ${command}\\n\\n`);\n      }\n      usage(\"stderr\");\n      process.exit(1);\n  }\n}\n\nmain().catch((e: unknown) => {\n  process.stderr.write(\n    `fatal: ${e instanceof Error ? e.message : String(e)}\\n`,\n  );\n  process.exit(1);\n});\n"],"mappings":"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"}