zelari-code 2.42.0 → 2.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19304,22 +19304,54 @@ var init_walk = __esm({
19304
19304
  // packages/core/dist/core/tools/builtin/search.js
19305
19305
  import { promises as fs7 } from "node:fs";
19306
19306
  import path10 from "node:path";
19307
+ function cleanGlobFragment(g) {
19308
+ let out = g.trim();
19309
+ if (out.startsWith("[") && out.charAt(1) === '"')
19310
+ out = out.slice(1);
19311
+ if (out.endsWith("]") && out.charAt(out.length - 2) === '"')
19312
+ out = out.slice(0, -1);
19313
+ if (out.length >= 2 && (out.charAt(0) === '"' && out.charAt(out.length - 1) === '"' || out.charAt(0) === "'" && out.charAt(out.length - 1) === "'")) {
19314
+ out = out.slice(1, -1).trim();
19315
+ }
19316
+ return out;
19317
+ }
19318
+ function isJsonArrayString(value) {
19319
+ if (typeof value !== "string")
19320
+ return false;
19321
+ const s = value.trim();
19322
+ if (!s.startsWith("[") || !s.endsWith("]"))
19323
+ return false;
19324
+ try {
19325
+ return Array.isArray(JSON.parse(s));
19326
+ } catch {
19327
+ return false;
19328
+ }
19329
+ }
19307
19330
  function coerceStringList(value, fallback) {
19308
19331
  if (value === void 0 || value === null)
19309
19332
  return fallback;
19310
19333
  if (Array.isArray(value)) {
19311
- const cleaned = value.filter((x) => typeof x === "string" && x.trim().length > 0);
19334
+ const cleaned = value.map((x) => typeof x === "string" ? cleanGlobFragment(x) : x).filter((x) => typeof x === "string" && x.trim().length > 0);
19312
19335
  return cleaned.length > 0 ? cleaned : fallback;
19313
19336
  }
19314
19337
  if (typeof value === "string") {
19315
19338
  const s = value.trim();
19316
19339
  if (!s)
19317
19340
  return fallback;
19341
+ if (s.startsWith("[") && s.endsWith("]")) {
19342
+ try {
19343
+ const parsed = JSON.parse(s);
19344
+ if (Array.isArray(parsed))
19345
+ return coerceStringList(parsed, fallback);
19346
+ } catch {
19347
+ }
19348
+ }
19318
19349
  if (s.includes(",") && !s.includes("{")) {
19319
- const parts = s.split(",").map((x) => x.trim()).filter(Boolean);
19350
+ const parts = s.split(",").map((x) => cleanGlobFragment(x)).filter(Boolean);
19320
19351
  return parts.length > 0 ? parts : fallback;
19321
19352
  }
19322
- return [s];
19353
+ const single = cleanGlobFragment(s);
19354
+ return single ? [single] : fallback;
19323
19355
  }
19324
19356
  return fallback;
19325
19357
  }
@@ -19425,7 +19457,7 @@ var init_search = __esm({
19425
19457
  });
19426
19458
  grepContentTool = {
19427
19459
  name: "grep_content",
19428
- description: `Regex search for content in a file OR recursively in a directory. When path is a directory, include/exclude globs filter which files are searched (default: all files, excluding node_modules/dist/.git/etc.). Glob semantics (grep --include style): a glob without '/' (e.g. "*.md") matches the file basename at ANY depth; a glob with '/' (e.g. "src/*.ts") matches the relative path at exactly that level; "**" is explicit recursion ("**/*.ts" matches at any depth, same as the bare form). include/exclude accept a single glob string (e.g. "*.ts") OR an array of globs. Returns matches with line numbers and context, plus filesWalked/filesInTree counts and a warning when the include globs matched suspiciously few files.`,
19460
+ description: `Regex search for content in a file OR recursively in a directory. When path is a directory, include/exclude globs filter which files are searched (default: all files, excluding node_modules/dist/.git/etc.). Glob semantics (grep --include style): a glob without '/' (e.g. "*.md") matches the file basename at ANY depth; a glob with '/' (e.g. "src/*.ts") matches the relative path at exactly that level; "**" is explicit recursion ("**/*.ts" matches at any depth, same as the bare form). include/exclude accept a single glob string (e.g. "*.ts") OR an array of globs. Stringified-array ('["*.ts"]') or quote-wrapped forms are auto-repaired, with a warning. Returns matches with line numbers and context, plus filesWalked/filesInTree counts and a warning when the include globs matched suspiciously few files.`,
19429
19461
  permissions: ["read"],
19430
19462
  sideEffect: "none",
19431
19463
  timeoutMs: 3e4,
@@ -19440,9 +19472,14 @@ var init_search = __esm({
19440
19472
  const warnings = [];
19441
19473
  if (Array.isArray(rawInclude) && rawInclude.length === 0) {
19442
19474
  warnings.push('DEPRECATED_INPUT: empty include array \u2014 omit the field instead; this will become INVALID_ARGUMENT (planned v1.47). Fell back to ["*"]');
19475
+ } else if (isJsonArrayString(rawInclude)) {
19476
+ warnings.push(`include repaired from stringified-array form to ${JSON.stringify(include)}`);
19443
19477
  } else if (typeof rawInclude === "string") {
19444
19478
  warnings.push(`include coerced from bare string to ${JSON.stringify(include)}`);
19445
19479
  }
19480
+ if (isJsonArrayString(args.exclude)) {
19481
+ warnings.push(`exclude repaired from stringified-array form to ${JSON.stringify(exclude)}`);
19482
+ }
19446
19483
  if (!await isDirectory(absRoot)) {
19447
19484
  let exists = true;
19448
19485
  try {
@@ -29044,22 +29081,32 @@ function parseMinimaxStyleToolCalls(text) {
29044
29081
  }
29045
29082
  function parseLooseArgs(body) {
29046
29083
  const args = {};
29047
- const paramTag = /(?:parameter\s+name=|<\s*)["']?([a-zA-Z_][\w]*)["']?\s*(?:>|=\s*["']?)([^<\]\n]+)/gi;
29084
+ const paramFull = /<parameter\s+name=["']([a-zA-Z_][\w]*)["']\s*>([\s\S]*?)<\/parameter>/gi;
29048
29085
  let m;
29049
- while ((m = paramTag.exec(body)) !== null) {
29086
+ while ((m = paramFull.exec(body)) !== null) {
29087
+ const key = m[1];
29088
+ if (["invoke", "minimax", "tool_call", "parameter"].includes(key))
29089
+ continue;
29090
+ args[key] = coerceJsonishValue(m[2]);
29091
+ }
29092
+ const rest = body.replace(/<parameter\s+name=["'][a-zA-Z_][\w]*["']\s*>[\s\S]*?<\/parameter>/gi, " ");
29093
+ const paramTag = /(?:parameter\s+name=|<\s*)["']?([a-zA-Z_][\w]*)["']?\s*(?:>|=\s*["']?)([^<\]\n]+)/gi;
29094
+ while ((m = paramTag.exec(rest)) !== null) {
29050
29095
  const key = m[1];
29051
29096
  if (key === "invoke" || key === "minimax" || key === "tool_call")
29052
29097
  continue;
29053
- args[key] = m[2].replace(/^["']|["']$/g, "").trim();
29098
+ if (args[key] !== void 0)
29099
+ continue;
29100
+ args[key] = coerceJsonishValue(m[2]);
29054
29101
  }
29055
29102
  const lineRe = /(?:^|[\s\[>])([a-zA-Z_][\w]*)\s*[>:=]\s*([^\n<\]]+)/g;
29056
- while ((m = lineRe.exec(body)) !== null) {
29103
+ while ((m = lineRe.exec(rest)) !== null) {
29057
29104
  const key = m[1];
29058
29105
  if (args[key] !== void 0)
29059
29106
  continue;
29060
29107
  if (["invoke", "name", "minimax", "tool_call", "parameter"].includes(key))
29061
29108
  continue;
29062
- args[key] = m[2].replace(/^["']|["']$/g, "").trim();
29109
+ args[key] = coerceJsonishValue(m[2]);
29063
29110
  }
29064
29111
  if (Object.keys(args).length === 0) {
29065
29112
  const brace = body.indexOf("{");
@@ -29075,6 +29122,20 @@ function parseLooseArgs(body) {
29075
29122
  }
29076
29123
  return args;
29077
29124
  }
29125
+ function coerceJsonishValue(raw) {
29126
+ const t = raw.trim();
29127
+ if (t.startsWith("[") || t.startsWith("{")) {
29128
+ try {
29129
+ return JSON.parse(t);
29130
+ } catch {
29131
+ }
29132
+ try {
29133
+ return JSON.parse(t + (t.startsWith("[") ? "]" : "}"));
29134
+ } catch {
29135
+ }
29136
+ }
29137
+ return t.replace(/^["']|["']$/g, "").trim();
29138
+ }
29078
29139
  function tryParseToolArray(raw) {
29079
29140
  let parsed;
29080
29141
  try {
@@ -38837,7 +38898,7 @@ var CORE_VERSION;
38837
38898
  var init_version = __esm({
38838
38899
  "packages/core/dist/version.js"() {
38839
38900
  "use strict";
38840
- CORE_VERSION = "2.42.0";
38901
+ CORE_VERSION = "2.43.0";
38841
38902
  }
38842
38903
  });
38843
38904
 
@@ -41728,8 +41789,15 @@ import path36 from "node:path";
41728
41789
  function radioDir(cwd) {
41729
41790
  return path36.join(cwd, ".zelari", "radio");
41730
41791
  }
41792
+ function processFallbackRadioId() {
41793
+ if (fallbackRadioId === null) {
41794
+ fallbackRadioId = `default-${process.pid}-${Date.now().toString(36)}`;
41795
+ }
41796
+ return fallbackRadioId;
41797
+ }
41731
41798
  function radioPath(cwd, sessionId2) {
41732
- const safe = (sessionId2 || "default").replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
41799
+ const id3 = sessionId2.trim() ? sessionId2 : processFallbackRadioId();
41800
+ const safe = id3.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
41733
41801
  return path36.join(radioDir(cwd), `${safe}.jsonl`);
41734
41802
  }
41735
41803
  function appendRadioLine(file2, line) {
@@ -41824,10 +41892,11 @@ function formatKrakenRadioStatus(cwd, sessionId2, limit = 12) {
41824
41892
  });
41825
41893
  return [`Kraken radio (last ${events.length}) session=${sessionId2}:`, ...lines].join("\n");
41826
41894
  }
41827
- var MAX_CACHED_RADIO_FDS, radioFds;
41895
+ var fallbackRadioId, MAX_CACHED_RADIO_FDS, radioFds;
41828
41896
  var init_krakenRadio = __esm({
41829
41897
  "src/cli/tools/krakenRadio.ts"() {
41830
41898
  "use strict";
41899
+ fallbackRadioId = null;
41831
41900
  MAX_CACHED_RADIO_FDS = 32;
41832
41901
  radioFds = /* @__PURE__ */ new Map();
41833
41902
  }
@@ -47862,7 +47931,10 @@ async function runTentacle(opts) {
47862
47931
  emitActivity({
47863
47932
  type: "agent_ended",
47864
47933
  agentId: id3,
47865
- reason: info.detail ?? (info.ok === false ? "failed" : "completed"),
47934
+ // t102: `reason` feeds a failure-heuristic on the Desktop; free text in
47935
+ // it (result excerpt) could flip a SUCCESSFUL tentacle to "failed".
47936
+ // Only genuine failures carry detail; successes are enum-only.
47937
+ reason: info.ok === false ? info.detail ?? "failed" : "completed",
47866
47938
  ok: info.ok !== false,
47867
47939
  durationMs: info.durationMs ?? 0,
47868
47940
  ts: Date.now()
@@ -55329,14 +55401,18 @@ function activePermissionPreset() {
55329
55401
  return parsePermissionPreset(process.env.ZELARI_PERMISSION_PRESET) ?? "standard";
55330
55402
  }
55331
55403
  function defaultPermissionPolicy(overrides) {
55332
- const preset = PERMISSION_PRESETS[activePermissionPreset()];
55404
+ const presetName = activePermissionPreset();
55405
+ const preset = PERMISSION_PRESETS[presetName];
55333
55406
  return {
55334
55407
  read: parseAction(process.env.ZELARI_PERMISSION_READ, preset.read),
55335
55408
  write: parseAction(process.env.ZELARI_PERMISSION_WRITE, preset.write),
55336
55409
  execute: parseAction(process.env.ZELARI_PERMISSION_EXECUTE, preset.execute),
55337
55410
  network: parseAction(process.env.ZELARI_PERMISSION_NETWORK, preset.network),
55338
55411
  ui: "allow",
55339
- auto: isAutoPermissions(),
55412
+ // yolo means "go alone": residual asks auto-approve without a UI handler
55413
+ // (no more "denied timed out" piles on unattended builds). Explicit
55414
+ // ZELARI_AUTO=1 keeps working for every preset; deny is never promoted.
55415
+ auto: isAutoPermissions() || presetName === "yolo",
55340
55416
  ...overrides
55341
55417
  };
55342
55418
  }
@@ -56460,9 +56536,11 @@ function createBuiltinToolRegistry(options = {}) {
56460
56536
  const gauntletParent = options.gauntletParent === true;
56461
56537
  const allowMutators = !readOnly && !verifyMode && !gauntletParent;
56462
56538
  const allowBash = (allowMutators || verifyMode) && !gauntletParent;
56463
- if (isParent && allowMutators && sessionId2) {
56464
- registry4.setToolResultListener(createTaskTouchGuard({ projectRoot: root, sessionId: sessionId2 }));
56465
- void runTaskStalenessCheck({ projectRoot: root, sessionId: sessionId2 });
56539
+ if (isParent && allowMutators && options.sessionId) {
56540
+ registry4.setToolResultListener(
56541
+ createTaskTouchGuard({ projectRoot: root, sessionId: options.sessionId })
56542
+ );
56543
+ void runTaskStalenessCheck({ projectRoot: root, sessionId: options.sessionId });
56466
56544
  }
56467
56545
  const permPolicy = options.permissionPolicy ?? defaultPermissionPolicy();
56468
56546
  const agentPolicySet = (() => {
@@ -87435,7 +87513,7 @@ proposals: npm run evolve:propose \u2014 decisions in npm run evolve:decide (P1:
87435
87513
  }
87436
87514
  if (argv.includes("--help") || argv.includes("-h")) {
87437
87515
  console.log(
87438
- "zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor [--json] Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --inspect [--json] Unified project inspection (config, skills, MCP,\n hooks, plugins, AGENTS.md, trust status)\n --trust [path] Trust the cwd (or path) so project MCP + hooks load\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --fix-budget Set recommended ZELARI_MAX_TOOL_LOOP_HARD=180,\n ZELARI_MAX_TOOL_LOOP_ITERATIONS=60, ZELARI_CONTEXT_LIMIT=400000\n at User scope (prevents the agent stopping mid-task)\n --permission-mcp <socket> MCP stdio server for external agent permission prompts\n (spawned by claude --permission-prompt-tool)\n --memory-mcp Optional MCP stdio server for project memory\n --cwd <path> Trusted project root (requires ZELARI_MEMORY_MCP=1)\n --client-id <id> Stable local owner identity for private memories\n --memory-json <json> Read-only project-memory bridge for Zelari Desktop\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode kraken|council|zelari Dispatch mode (default: kraken; agent=alias)\n zelari/mission mode auto-scopes slices from open tasks in .zelari/plan.json\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n --profile <id> Capability profile (minimal/v1|kraken/v1|council/v1|mission/v1)\n --resume <id> Continue a 2.0 spine session\n --resume-mission Resume .zelari/mission-state.json (not the spine)\n --export-session <path> Write zelari-session-export/1 after the run\n --strict-done Evidence-based BUILD completion gate\n --allow-unverified Exit 0 when strict is ON but nothing could be verified (M1.2)\n --task-file <path> Read the task prompt from a file (Windows argv cap)\n --once Single-cycle run (cron/git-hook triggers, ADR-0014)\n --kraken-graph <goal> Plan + execute a Kraken task graph\n (variant: --kraken-graph-file <path>; kill-switch\n ZELARI_KRAKEN_GRAPH=0)\n --plan-only Serialize the graph plan to .zelari/radio/ and exit 0\n --run-plan <id> Execute a pre-built .zelari/radio/plan-<id>.json\n --gauntlet Host-driven gauntlet loop (builder/critic tentacles)\n --session-export <id> Print a portable 2.0 session export (no LLM)\n serve Companion host for Android/remote clients (Tailscale)\n --bind <ip> Listen address (default: 127.0.0.1; use Tailscale IP)\n --port <n> Port (default: 7421)\n --token <secret> Bearer token (default: ~/.zelari-code/companion.token)\n --project <path> Allowlisted project root (repeatable)\n --save-projects Persist --project list to companion.json\n --serve-harness Long-lived harness kernel for hosts (NDJSON JSON-RPC\n on stdin/stdout; Desktop/companion transport)\n --print-config Print provider/model config as JSON (no secrets)\n --print-settings Print zelari.config.json values + the origin of\n each (default < user < project < env)\n --permissions <p> Permission preset: strict | standard | yolo \u2014 changes\n category DEFAULTS only (env vars and policy files win)\n --evolve-status Evolution ledger stats (read-only; ADR-0036; the\n ledger is written only when ZELARI_EVOLUTION=shadow)\n --plugins-status JSON status of optional plugins (Playwright, eslint, \u2026)\n --plugins-install <id> Install plugin (playwright also fetches Chromium)\n --cwd <path> Workspace for -D installs (default: process.cwd())\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --thinking <spec> Thinking effort (auto|off|low|medium|high|budget:N)\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --login-oauth Start subscription OAuth (grok, chatgpt, anthropic)\n --provider <id> grok | chatgpt | anthropic\n --code <paste> Anthropic magic-link code (CODE#STATE)\n --no-browser Do not open the system browser\n --refresh-oauth Force-refresh an OAuth access token\n --provider <id> grok | chatgpt | anthropic\n --logout-oauth Clear stored OAuth credentials\n --provider <id> grok | chatgpt | anthropic\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n --print-mcp Print MCP server config (user + project)\n --cwd <path> Project root for .zelari/mcp.json\n --set-mcp Add/update an MCP server entry\n --name <id> Server name (required)\n --command <bin> Executable for stdio servers\n --url <endpoint> Streamable HTTP endpoint (e.g. UE 5.8 editor)\n --args <json> JSON array of args (stdio, optional)\n --env <json|K=V> Env for the server: JSON object or KEY=VALUE,\n repeatable (omit to keep the stored env)\n --timeout <ms> Per-server request timeout (http, optional)\n --scope user|project Default: user\n --enabled true|false Default: true\n --cwd <path> Required when scope=project\n --set-mcp-preset Install a named MCP preset (e.g. cua)\n --preset cua Cua Driver desktop computer-use (MCP)\n --preset unreal-mcp Unreal Engine 5.8+ editor (MCP over HTTP)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-mcp Remove an MCP server entry\n --name <id> --scope user|project [--cwd <path>]\n --print-skills Print skills (builtin + user + project)\n --cwd <path> Project root for .zelari/skills\n --set-skill Add/update a SKILL.md skill\n --name <id> Skill id (required)\n --description <t> One-line description (required)\n --body <text> Markdown instructions (required)\n --category <c> plan|refactor|debug|review|test|docs|ops|git|db|maint\n --tools <csv> Comma-separated required tools (optional)\n --cost <l> low|medium|high (default: medium)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-skill Remove a user/project SKILL.md\n --name <id> --scope user|project [--cwd <path>]\n --generate-skill-from-url Fetch URL + draft skill via model (JSON)\n --url <https://...> Required\n --provider <id> Override active provider\n --model <name> Override model for the selected provider\n --print-ssh-targets Print SSH deploy/monitor targets\n --set-ssh-target Upsert target (--json '{...}' or flags)\n --remove-ssh-target --id <id>\n --test-ssh-target --id <id> (BatchMode ssh true)\n --print-ssh-pubkey --path <private-or-.pub> (display public key)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ZELARI_MEMORY_MCP=1 Enable the optional external memory MCP server\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
87516
+ "zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor [--json] Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --inspect [--json] Unified project inspection (config, skills, MCP,\n hooks, plugins, AGENTS.md, trust status)\n --trust [path] Trust the cwd (or path) so project MCP + hooks load\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --fix-budget Set recommended ZELARI_MAX_TOOL_LOOP_HARD=180,\n ZELARI_MAX_TOOL_LOOP_ITERATIONS=60, ZELARI_CONTEXT_LIMIT=400000\n at User scope (prevents the agent stopping mid-task)\n --permission-mcp <socket> MCP stdio server for external agent permission prompts\n (spawned by claude --permission-prompt-tool)\n --memory-mcp Optional MCP stdio server for project memory\n --cwd <path> Trusted project root (requires ZELARI_MEMORY_MCP=1)\n --client-id <id> Stable local owner identity for private memories\n --memory-json <json> Read-only project-memory bridge for Zelari Desktop\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode kraken|council|zelari Dispatch mode (default: kraken; agent=alias)\n zelari/mission mode auto-scopes slices from open tasks in .zelari/plan.json\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n --profile <id> Capability profile (minimal/v1|kraken/v1|council/v1|mission/v1)\n --resume <id> Continue a 2.0 spine session\n --resume-mission Resume .zelari/mission-state.json (not the spine)\n --export-session <path> Write zelari-session-export/1 after the run\n --strict-done Evidence-based BUILD completion gate\n --allow-unverified Exit 0 when strict is ON but nothing could be verified (M1.2)\n --task-file <path> Read the task prompt from a file (Windows argv cap)\n --once Single-cycle run (cron/git-hook triggers, ADR-0014)\n --kraken-graph <goal> Plan + execute a Kraken task graph\n (variant: --kraken-graph-file <path>; kill-switch\n ZELARI_KRAKEN_GRAPH=0)\n --plan-only Serialize the graph plan to .zelari/radio/ and exit 0\n --run-plan <id> Execute a pre-built .zelari/radio/plan-<id>.json\n --gauntlet Host-driven gauntlet loop (builder/critic tentacles)\n --session-export <id> Print a portable 2.0 session export (no LLM)\n serve Companion host for Android/remote clients (Tailscale)\n --bind <ip> Listen address (default: 127.0.0.1; use Tailscale IP)\n --port <n> Port (default: 7421)\n --token <secret> Bearer token (default: ~/.zelari-code/companion.token)\n --project <path> Allowlisted project root (repeatable)\n --save-projects Persist --project list to companion.json\n --serve-harness Long-lived harness kernel for hosts (NDJSON JSON-RPC\n on stdin/stdout; Desktop/companion transport)\n --print-config Print provider/model config as JSON (no secrets)\n --print-settings Print zelari.config.json values + the origin of\n each (default < user < project < env)\n --permissions <p> Permission preset: strict | standard | yolo \u2014 changes\n category DEFAULTS only (env vars and policy files win);\n yolo also auto-approves residual asks (unattended runs)\n --evolve-status Evolution ledger stats (read-only; ADR-0036; the\n ledger is written only when ZELARI_EVOLUTION=shadow)\n --plugins-status JSON status of optional plugins (Playwright, eslint, \u2026)\n --plugins-install <id> Install plugin (playwright also fetches Chromium)\n --cwd <path> Workspace for -D installs (default: process.cwd())\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --thinking <spec> Thinking effort (auto|off|low|medium|high|budget:N)\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --login-oauth Start subscription OAuth (grok, chatgpt, anthropic)\n --provider <id> grok | chatgpt | anthropic\n --code <paste> Anthropic magic-link code (CODE#STATE)\n --no-browser Do not open the system browser\n --refresh-oauth Force-refresh an OAuth access token\n --provider <id> grok | chatgpt | anthropic\n --logout-oauth Clear stored OAuth credentials\n --provider <id> grok | chatgpt | anthropic\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n --print-mcp Print MCP server config (user + project)\n --cwd <path> Project root for .zelari/mcp.json\n --set-mcp Add/update an MCP server entry\n --name <id> Server name (required)\n --command <bin> Executable for stdio servers\n --url <endpoint> Streamable HTTP endpoint (e.g. UE 5.8 editor)\n --args <json> JSON array of args (stdio, optional)\n --env <json|K=V> Env for the server: JSON object or KEY=VALUE,\n repeatable (omit to keep the stored env)\n --timeout <ms> Per-server request timeout (http, optional)\n --scope user|project Default: user\n --enabled true|false Default: true\n --cwd <path> Required when scope=project\n --set-mcp-preset Install a named MCP preset (e.g. cua)\n --preset cua Cua Driver desktop computer-use (MCP)\n --preset unreal-mcp Unreal Engine 5.8+ editor (MCP over HTTP)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-mcp Remove an MCP server entry\n --name <id> --scope user|project [--cwd <path>]\n --print-skills Print skills (builtin + user + project)\n --cwd <path> Project root for .zelari/skills\n --set-skill Add/update a SKILL.md skill\n --name <id> Skill id (required)\n --description <t> One-line description (required)\n --body <text> Markdown instructions (required)\n --category <c> plan|refactor|debug|review|test|docs|ops|git|db|maint\n --tools <csv> Comma-separated required tools (optional)\n --cost <l> low|medium|high (default: medium)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-skill Remove a user/project SKILL.md\n --name <id> --scope user|project [--cwd <path>]\n --generate-skill-from-url Fetch URL + draft skill via model (JSON)\n --url <https://...> Required\n --provider <id> Override active provider\n --model <name> Override model for the selected provider\n --print-ssh-targets Print SSH deploy/monitor targets\n --set-ssh-target Upsert target (--json '{...}' or flags)\n --remove-ssh-target --id <id>\n --test-ssh-target --id <id> (BatchMode ssh true)\n --print-ssh-pubkey --path <private-or-.pub> (display public key)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ZELARI_MEMORY_MCP=1 Enable the optional external memory MCP server\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
87439
87517
  );
87440
87518
  process.exit(0);
87441
87519
  }