@noir-ai/cli 1.0.0-beta.1 → 1.2.0-beta.1

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.
package/dist/bin.js CHANGED
@@ -7,15 +7,19 @@ import {
7
7
  handleError,
8
8
  inferExitCode,
9
9
  info,
10
- init,
11
10
  isInteractive,
12
11
  log,
13
12
  serve,
14
- table
15
- } from "./chunk-UXT7YWQY.js";
13
+ table,
14
+ warn
15
+ } from "./chunk-OMUB7XOA.js";
16
+ import {
17
+ init
18
+ } from "./chunk-UGUQQSWH.js";
16
19
 
17
20
  // src/bin.ts
18
21
  import { pathToFileURL } from "url";
22
+ import { SUPPORTED_HOSTS } from "@noir-ai/adapters";
19
23
  import { NOIR_VERSION as NOIR_VERSION3 } from "@noir-ai/core";
20
24
  import { Command, Option } from "commander";
21
25
 
@@ -58,7 +62,7 @@ async function probeDaemon(opts = {}) {
58
62
  return { running: false };
59
63
  }
60
64
  const body = await res.json().catch(() => null);
61
- if (!body || body.ok !== true) {
65
+ if (body?.ok !== true) {
62
66
  if (opts.verbose) process.stderr.write("noir: daemon probe: /health body not ok\n");
63
67
  return { running: false };
64
68
  }
@@ -95,7 +99,7 @@ async function connectClient(client, url, opts) {
95
99
  async function callToolParse(client, opts, name, args) {
96
100
  const result = await client.callTool({ name, arguments: args }).catch((err) => failDaemonDown(opts, err));
97
101
  const block = result.content?.[0];
98
- if (!block || block.type !== "text" || typeof block.text !== "string") {
102
+ if (block?.type !== "text" || typeof block.text !== "string") {
99
103
  failDaemonDown(opts, new Error(`tool '${name}' returned no text content`));
100
104
  }
101
105
  const text = block.text;
@@ -183,10 +187,10 @@ function failTool(label, envelope, opts) {
183
187
  function toHit(raw) {
184
188
  const h = raw ?? {};
185
189
  return {
186
- path: typeof h["path"] === "string" ? h["path"] : "<unknown>",
187
- score: typeof h["score"] === "number" ? h["score"] : 0,
188
- snippet: typeof h["snippet"] === "string" ? h["snippet"] : "",
189
- source: typeof h["source"] === "string" ? h["source"] : ""
190
+ path: typeof h.path === "string" ? h.path : "<unknown>",
191
+ score: typeof h.score === "number" ? h.score : 0,
192
+ snippet: typeof h.snippet === "string" ? h.snippet : "",
193
+ source: typeof h.source === "string" ? h.source : ""
190
194
  };
191
195
  }
192
196
  async function contextSearch(opts) {
@@ -422,7 +426,7 @@ async function daemonStatus(opts) {
422
426
  } catch {
423
427
  health = null;
424
428
  }
425
- if (!health || health.ok !== true) {
429
+ if (health?.ok !== true) {
426
430
  clearDaemonRecord();
427
431
  fail(
428
432
  EXIT.DAEMON_DOWN,
@@ -553,15 +557,15 @@ function toHit2(raw) {
553
557
  const h = raw ?? {};
554
558
  const arr = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
555
559
  return {
556
- id: typeof h["id"] === "string" ? h["id"] : "",
557
- type: typeof h["type"] === "string" ? h["type"] : "",
558
- score: typeof h["score"] === "number" ? h["score"] : 0,
559
- content: typeof h["content"] === "string" ? h["content"] : "",
560
- concepts: arr(h["concepts"]),
561
- files: arr(h["files"]),
562
- ts: typeof h["ts"] === "number" ? h["ts"] : 0,
563
- importance: typeof h["importance"] === "number" ? h["importance"] : 0,
564
- source: typeof h["source"] === "string" ? h["source"] : ""
560
+ id: typeof h.id === "string" ? h.id : "",
561
+ type: typeof h.type === "string" ? h.type : "",
562
+ score: typeof h.score === "number" ? h.score : 0,
563
+ content: typeof h.content === "string" ? h.content : "",
564
+ concepts: arr(h.concepts),
565
+ files: arr(h.files),
566
+ ts: typeof h.ts === "number" ? h.ts : 0,
567
+ importance: typeof h.importance === "number" ? h.importance : 0,
568
+ source: typeof h.source === "string" ? h.source : ""
565
569
  };
566
570
  }
567
571
  function failTool2(label, envelope, opts) {
@@ -612,8 +616,8 @@ async function memorySave(opts) {
612
616
  const content = await resolveContent(opts);
613
617
  const files = splitCsv(opts.files);
614
618
  const args = { content };
615
- if (typeof opts.type === "string" && opts.type.length > 0) args["type"] = opts.type;
616
- if (files !== void 0) args["files"] = files;
619
+ if (typeof opts.type === "string" && opts.type.length > 0) args.type = opts.type;
620
+ if (files !== void 0) args.files = files;
617
621
  const res = await callDaemonTool(opts, "memory_save", args);
618
622
  if (res.ok !== true) failTool2("memory save", res, opts);
619
623
  const id = typeof res.id === "string" ? res.id : "";
@@ -654,7 +658,7 @@ function renderObservation(obs, opts) {
654
658
  if (obs[key] !== void 0) rows.push({ Field: key, Value: obs[key] });
655
659
  }
656
660
  if (rows.length > 0) table(rows, ["Field", "Value"], opts);
657
- const content = obs["content"];
661
+ const content = obs.content;
658
662
  if (typeof content === "string") log(`
659
663
  ${content}`, opts);
660
664
  }
@@ -664,9 +668,9 @@ async function memorySessions(opts) {
664
668
  const sessions = Array.isArray(res.sessions) ? res.sessions.map((raw) => {
665
669
  const s = raw ?? {};
666
670
  return {
667
- id: typeof s["id"] === "string" ? s["id"] : "",
668
- count: typeof s["count"] === "number" ? s["count"] : 0,
669
- lastTs: typeof s["lastTs"] === "number" ? s["lastTs"] : 0
671
+ id: typeof s.id === "string" ? s.id : "",
672
+ count: typeof s.count === "number" ? s.count : 0,
673
+ lastTs: typeof s.lastTs === "number" ? s.lastTs : 0
670
674
  };
671
675
  }) : [];
672
676
  const data = { sessions };
@@ -711,8 +715,8 @@ async function memoryConsolidate(opts) {
711
715
  const types = splitCsv(opts.types);
712
716
  const limit = parseLimit2(opts.limit, "memory consolidate", opts);
713
717
  const args = {};
714
- if (types !== void 0) args["types"] = types;
715
- if (limit !== void 0) args["limit"] = limit;
718
+ if (types !== void 0) args.types = types;
719
+ if (limit !== void 0) args.limit = limit;
716
720
  const res = await callDaemonTool(
717
721
  opts,
718
722
  "memory_consolidate",
@@ -735,15 +739,15 @@ async function memoryConsolidate(opts) {
735
739
  const l = lessons[i];
736
740
  if (l === void 0) continue;
737
741
  const obs = l ?? {};
738
- const id = typeof obs["id"] === "string" ? obs["id"] : `<lesson ${i + 1}>`;
739
- const content = typeof obs["content"] === "string" ? obs["content"] : "";
742
+ const id = typeof obs.id === "string" ? obs.id : `<lesson ${i + 1}>`;
743
+ const content = typeof obs.content === "string" ? obs.content : "";
740
744
  log(`- ${id}: ${content}`, opts);
741
745
  }
742
746
  return;
743
747
  }
744
748
  const env = res;
745
- const reason = (typeof env["reason"] === "string" ? env["reason"] : void 0) ?? (typeof env["error"] === "string" ? env["error"] : void 0) ?? "unknown";
746
- const logged = env["logged"] === true || env["degraded"] === true;
749
+ const reason = (typeof env.reason === "string" ? env.reason : void 0) ?? (typeof env.error === "string" ? env.error : void 0) ?? "unknown";
750
+ const logged = env.logged === true || env.degraded === true;
747
751
  const suffix = logged ? " (logged)" : "";
748
752
  fail(EXIT.ERROR, `memory consolidate did not run: ${reason}${suffix}`, opts);
749
753
  }
@@ -751,7 +755,10 @@ async function memoryConsolidate(opts) {
751
755
  // src/commands/skills.ts
752
756
  import { claudeAdapter } from "@noir-ai/adapters";
753
757
  import { loadProjectInfo as loadProjectInfo4 } from "@noir-ai/core";
754
- import { discoverBuiltin, emitSkillsToDir } from "@noir-ai/skills";
758
+ import {
759
+ discoverAll,
760
+ emitSkillsToDir
761
+ } from "@noir-ai/skills";
755
762
  var CATEGORY = {
756
763
  "noir-brainstorm": "discovery",
757
764
  "noir-intake": "discovery",
@@ -790,9 +797,9 @@ function categoryOf(name) {
790
797
  if (mapped !== void 0) return mapped;
791
798
  return name.replace(/^noir-/, "") || "general";
792
799
  }
793
- function toRow(s) {
800
+ function toRow(s, kind = "builtin") {
794
801
  const description = typeof s.frontmatter.description === "string" ? s.frontmatter.description : "";
795
- return { name: s.name, category: categoryOf(s.name), description };
802
+ return { name: s.name, category: categoryOf(s.name), description, kind };
796
803
  }
797
804
  function truncate(text, max) {
798
805
  if (text.length <= max) return text;
@@ -803,30 +810,39 @@ function truncate(text, max) {
803
810
  }
804
811
  async function skillsList(opts) {
805
812
  let builtins;
813
+ let integrations;
806
814
  try {
807
- builtins = discoverBuiltin();
815
+ const all = discoverAll();
816
+ builtins = all.builtins;
817
+ integrations = all.integrations;
808
818
  } catch (err) {
809
819
  fail(
810
820
  EXIT.ERROR,
811
- `skills list: could not discover builtin skills (${err instanceof Error ? err.message : String(err)})`,
821
+ `skills list: could not discover skills (${err instanceof Error ? err.message : String(err)})`,
812
822
  opts
813
823
  );
814
824
  }
815
- const rows = builtins.map(toRow);
825
+ const rows = [
826
+ ...builtins.map((b) => toRow(b, "builtin")),
827
+ ...integrations.map((i) => toRow(i, "integration"))
828
+ ];
816
829
  const data = { count: rows.length, skills: rows };
817
830
  if (opts.json === true) {
818
831
  process.stdout.write(`${JSON.stringify({ ok: true, data })}
819
832
  `);
820
833
  return;
821
834
  }
822
- log(`noir skills \u2014 ${rows.length} builtin skill${rows.length === 1 ? "" : "s"}`, opts);
835
+ const intCount = integrations.length;
836
+ const banner = intCount > 0 ? `noir skills \u2014 ${rows.length} skill${rows.length === 1 ? "" : "s"} (${builtins.length} builtin, ${intCount} integration${intCount === 1 ? "" : "s"})` : `noir skills \u2014 ${rows.length} builtin skill${rows.length === 1 ? "" : "s"}`;
837
+ log(banner, opts);
823
838
  table(
824
839
  rows.map((r) => ({
825
840
  Skill: r.name,
841
+ Kind: r.kind,
826
842
  Category: r.category,
827
843
  Description: truncate(r.description, 80)
828
844
  })),
829
- ["Skill", "Category", "Description"],
845
+ ["Skill", "Kind", "Category", "Description"],
830
846
  opts
831
847
  );
832
848
  }
@@ -850,10 +866,12 @@ async function skillsSync(opts) {
850
866
  }
851
867
  const dir = claudeAdapter.skillsDir({ root });
852
868
  const summary = await emitSkillsToDir(dir);
869
+ const pruned = summary.pruned ?? [];
853
870
  const data = {
854
871
  emitted: summary.emitted,
855
872
  references: summary.references,
856
- dir
873
+ dir,
874
+ pruned
857
875
  };
858
876
  if (opts.json === true) {
859
877
  process.stdout.write(`${JSON.stringify({ ok: true, data })}
@@ -864,6 +882,12 @@ async function skillsSync(opts) {
864
882
  `Synced ${summary.emitted.length} Noir skill${summary.emitted.length === 1 ? "" : "s"} to ${dir}.`,
865
883
  opts
866
884
  );
885
+ if (pruned.length > 0) {
886
+ warn(
887
+ `Pruned ${pruned.length} stale noir-* skill dir${pruned.length === 1 ? "" : "s"}: ${pruned.join(", ")}`,
888
+ opts
889
+ );
890
+ }
867
891
  }
868
892
 
869
893
  // src/commands/status.ts
@@ -876,7 +900,7 @@ async function tryTool(caller, name) {
876
900
  }
877
901
  }
878
902
  function pickStore(s) {
879
- if (!s || s.ok !== true) return null;
903
+ if (s?.ok !== true) return null;
880
904
  return {
881
905
  docCount: s.docCount,
882
906
  vecCount: s.vecCount,
@@ -890,7 +914,7 @@ function describeEmbedder2(e) {
890
914
  return `${e.kind} \xB7 ${model} (${e.dim}-dim)`;
891
915
  }
892
916
  function pickContext(c) {
893
- if (!c || c.ok !== true) return null;
917
+ if (c?.ok !== true) return null;
894
918
  return {
895
919
  docCount: c.docCount,
896
920
  vecCount: c.vecCount,
@@ -900,7 +924,7 @@ function pickContext(c) {
900
924
  };
901
925
  }
902
926
  function pickWorkflow(w) {
903
- if (!w || w.ok !== true) return null;
927
+ if (w?.ok !== true) return null;
904
928
  return {
905
929
  taskId: w.taskId,
906
930
  phase: w.phase,
@@ -911,7 +935,7 @@ function pickWorkflow(w) {
911
935
  };
912
936
  }
913
937
  function pickMemory(m) {
914
- if (!m || m.ok !== true || !Array.isArray(m.sessions)) return null;
938
+ if (m?.ok !== true || !Array.isArray(m.sessions)) return null;
915
939
  let observations = 0;
916
940
  for (const s of m.sessions) observations += s.count;
917
941
  return { sessions: m.sessions.length, observations };
@@ -1186,15 +1210,20 @@ function toStatusOptions(g) {
1186
1210
  input: g.input !== false
1187
1211
  };
1188
1212
  }
1213
+ function parseHost(raw) {
1214
+ if (raw === void 0) return void 0;
1215
+ return raw;
1216
+ }
1189
1217
  function createProgram() {
1190
1218
  const program2 = new Command();
1191
1219
  program2.name("noir").description("Noir \u2014 discipline, context, and memory layer for agentic CLIs.").version(NOIR_VERSION3, "-v, --version").addOption(new Option("--json", "emit machine-readable JSON to stdout")).addOption(new Option("--no-input", "never prompt; error if input is required")).addOption(new Option("--quiet", "suppress non-essential diagnostics")).addOption(new Option("--verbose", "show additional diagnostic detail")).addOption(new Option("--cwd <path>", "run as if started in <path>")).exitOverride((err) => {
1192
1220
  throw err;
1193
1221
  }).configureOutput({
1194
- // S9 stream discipline: data → stdout, diagnostics stderr. Help and
1195
- // errors are diagnostics, so both go to stderr (keeps stdout pristine for
1196
- // --json payloads). commander's default error formatting is preserved.
1197
- writeOut: (str) => process.stderr.write(str),
1222
+ // Stream discipline: --help/--version → stdout (conventional; users pipe
1223
+ // them), errors stderr. --json data is emitted by command actions via
1224
+ // process.stdout directly, NOT by commander's writeOut, so routing help/
1225
+ // version to stdout does not conflict with --json payloads.
1226
+ writeOut: (str) => process.stdout.write(str),
1198
1227
  writeErr: (str) => process.stderr.write(str)
1199
1228
  });
1200
1229
  program2.hook("preAction", (_thisCmd, actionCmd) => {
@@ -1211,13 +1240,66 @@ function createProgram() {
1211
1240
  }
1212
1241
  }
1213
1242
  });
1214
- program2.command("init").description("scaffold Noir in the current project (.noir/, .mcp.json, CLAUDE.md, skills)").option("--transport <transport>", "stdio | streamable-http (default: stdio)", "stdio").option("--url <url>", "streamable-http daemon URL (localhost only)").action(async (opts) => {
1215
- const transport = opts.transport === "streamable-http" ? "streamable-http" : "stdio";
1216
- await init(process.cwd(), { transport, url: opts.url });
1217
- });
1218
- program2.command("sync").description("re-emit the Noir builtin skills into the host skills dir").action(async () => {
1219
- const { sync } = await import("./sync-7YLRJYMR.js");
1220
- await sync(process.cwd());
1243
+ program2.command("init").description("scaffold Noir in the current project (.noir/, .mcp.json, CLAUDE.md, skills)").option("--transport <transport>", "stdio | streamable-http (default: stdio)", "stdio").option("--url <url>", "streamable-http daemon URL (localhost only)").option(
1244
+ "--upgrade",
1245
+ "run scaffold migrations before re-emitting (re-run on an existing project)"
1246
+ ).addOption(
1247
+ // S10: target host. Defaults to `'claude'` (the regression anchor). The
1248
+ // chosen host is forwarded to scaffold() + skills emission via
1249
+ // resolveAdapter. The choice list mirrors `SUPPORTED_HOSTS` so a new host
1250
+ // lands here automatically; commander rejects anything else as usage=2.
1251
+ new Option(
1252
+ "--host <id>",
1253
+ "target agentic CLI (default: claude) \u2014 drives host-side emission"
1254
+ ).choices(SUPPORTED_HOSTS)
1255
+ ).action(
1256
+ async (opts) => {
1257
+ const transport = opts.transport === "streamable-http" ? "streamable-http" : "stdio";
1258
+ const upgrade = opts.upgrade === true;
1259
+ const host = parseHost(opts.host);
1260
+ await init(process.cwd(), {
1261
+ transport,
1262
+ url: opts.url,
1263
+ ...upgrade ? { upgrade } : {},
1264
+ ...host !== void 0 ? { host } : {}
1265
+ });
1266
+ }
1267
+ );
1268
+ program2.command("create [dir]").description("bootstrap the Noir AI layer in a new or empty directory").option("--transport <transport>", "stdio | streamable-http (default: stdio)", "stdio").option("--url <url>", "streamable-http daemon URL (localhost only)").addOption(
1269
+ new Option(
1270
+ "--host <id>",
1271
+ "target agentic CLI (default: claude) \u2014 drives host-side emission"
1272
+ ).choices(SUPPORTED_HOSTS)
1273
+ ).action(
1274
+ async (dir, opts) => {
1275
+ const transport = opts.transport === "streamable-http" ? "streamable-http" : "stdio";
1276
+ const host = parseHost(opts.host);
1277
+ const { create } = await import("./create-WP5ZKMCW.js");
1278
+ await create(dir, {
1279
+ transport,
1280
+ url: opts.url,
1281
+ ...host !== void 0 ? { host } : {}
1282
+ });
1283
+ }
1284
+ );
1285
+ program2.command("sync").description(
1286
+ "re-emit Noir managed files (.mcp.json, CLAUDE.md blocks, NOIR.md brief, ignores) + skills"
1287
+ ).addOption(
1288
+ // S10: optional `--host` override. When omitted, sync reads host from
1289
+ // `.noir/config.yml` (whatever `noir init --host <id>` persisted). The
1290
+ // override is rarely needed — documented as advanced.
1291
+ new Option(
1292
+ "--host <id>",
1293
+ "override the configured host (advanced; default reads .noir/config.yml)"
1294
+ ).choices(SUPPORTED_HOSTS)
1295
+ ).action(async (opts) => {
1296
+ const { sync } = await import("./sync-IEH2BVXU.js");
1297
+ const host = parseHost(opts.host);
1298
+ if (host === void 0) {
1299
+ await sync(process.cwd());
1300
+ } else {
1301
+ await sync(process.cwd(), { host });
1302
+ }
1221
1303
  });
1222
1304
  const mcpCmd = program2.command("mcp").description("MCP server control");
1223
1305
  mcpCmd.command("serve").description("run the Noir MCP server (stdio, or via the shared daemon)").option("--stdio", "force the stdio transport").action(async (opts) => {
@@ -1230,7 +1312,7 @@ function createProgram() {
1230
1312
  daemonGrp.command("start").description("start the Noir daemon (foreground; backgrounding deferred)").option("--detach", "run in the background (not yet implemented; exits 2)").action(async (...args) => {
1231
1313
  const cmd = trailingCmd(args);
1232
1314
  const g = cmd.optsWithGlobals();
1233
- const detach = g["detach"] === true;
1315
+ const detach = g.detach === true;
1234
1316
  await daemonStart({ ...toCliOptions(g), ...detach ? { detach } : {} });
1235
1317
  });
1236
1318
  daemonGrp.command("stop").description("stop the Noir daemon").action(async (...args) => {
@@ -1256,7 +1338,7 @@ function createProgram() {
1256
1338
  const cmd = trailingCmd(args);
1257
1339
  const g = cmd.optsWithGlobals();
1258
1340
  const query = typeof args[0] === "string" ? args[0] : "";
1259
- const limit = typeof g["limit"] === "string" ? g["limit"] : void 0;
1341
+ const limit = typeof g.limit === "string" ? g.limit : void 0;
1260
1342
  await contextSearch({
1261
1343
  ...toCliOptions(g),
1262
1344
  query,
@@ -1271,8 +1353,8 @@ function createProgram() {
1271
1353
  ).option("--force", "ignore content-hash caching (recognized; not yet honored)").action(async (...args) => {
1272
1354
  const cmd = trailingCmd(args);
1273
1355
  const g = cmd.optsWithGlobals();
1274
- const rawPaths = Array.isArray(g["path"]) ? g["path"].filter((p) => typeof p === "string") : [];
1275
- const force = g["force"] === true;
1356
+ const rawPaths = Array.isArray(g.path) ? g.path.filter((p) => typeof p === "string") : [];
1357
+ const force = g.force === true;
1276
1358
  await contextIndex({
1277
1359
  ...toCliOptions(g),
1278
1360
  ...rawPaths.length === 0 ? {} : { paths: rawPaths },
@@ -1290,7 +1372,7 @@ function createProgram() {
1290
1372
  const cmd = trailingCmd(args);
1291
1373
  const g = cmd.optsWithGlobals();
1292
1374
  const query = typeof args[0] === "string" ? args[0] : "";
1293
- const limit = typeof g["limit"] === "string" ? g["limit"] : void 0;
1375
+ const limit = typeof g.limit === "string" ? g.limit : void 0;
1294
1376
  await memoryRecall({
1295
1377
  ...toCliOptions(g),
1296
1378
  query,
@@ -1303,9 +1385,9 @@ function createProgram() {
1303
1385
  ).option("--files <files>", "comma-separated related file paths").action(async (...args) => {
1304
1386
  const cmd = trailingCmd(args);
1305
1387
  const g = cmd.optsWithGlobals();
1306
- const content = typeof g["content"] === "string" ? g["content"] : void 0;
1307
- const type = typeof g["type"] === "string" ? g["type"] : void 0;
1308
- const files = typeof g["files"] === "string" ? g["files"] : void 0;
1388
+ const content = typeof g.content === "string" ? g.content : void 0;
1389
+ const type = typeof g.type === "string" ? g.type : void 0;
1390
+ const files = typeof g.files === "string" ? g.files : void 0;
1309
1391
  await memorySave({
1310
1392
  ...toCliOptions(g),
1311
1393
  ...content === void 0 ? {} : { content },
@@ -1326,8 +1408,8 @@ function createProgram() {
1326
1408
  memoryGrp.command("consolidate").description("consolidate memories into a derived lesson (provider-explicit)").option("--types <types>", "comma-separated observation types to consolidate").option("--limit <n>", "cap on candidate observations").action(async (...args) => {
1327
1409
  const cmd = trailingCmd(args);
1328
1410
  const g = cmd.optsWithGlobals();
1329
- const types = typeof g["types"] === "string" ? g["types"] : void 0;
1330
- const limit = typeof g["limit"] === "string" ? g["limit"] : void 0;
1411
+ const types = typeof g.types === "string" ? g.types : void 0;
1412
+ const limit = typeof g.limit === "string" ? g.limit : void 0;
1331
1413
  await memoryConsolidate({
1332
1414
  ...toCliOptions(g),
1333
1415
  ...types === void 0 ? {} : { types },
@@ -1354,8 +1436,8 @@ function createProgram() {
1354
1436
  taskGrp.command("new").description("start a new workflow task").requiredOption("--slug <slug>", "task slug").option("--mode <mode>", "full | quick").action(async (...args) => {
1355
1437
  const cmd = trailingCmd(args);
1356
1438
  const g = cmd.optsWithGlobals();
1357
- const slug = typeof g["slug"] === "string" ? g["slug"] : "";
1358
- const mode = typeof g["mode"] === "string" ? g["mode"] : void 0;
1439
+ const slug = typeof g.slug === "string" ? g.slug : "";
1440
+ const mode = typeof g.mode === "string" ? g.mode : void 0;
1359
1441
  await taskNew({
1360
1442
  ...toCliOptions(g),
1361
1443
  slug,
@@ -1375,8 +1457,8 @@ function createProgram() {
1375
1457
  taskGrp.command("advance").description("advance the active task to the next phase").option("--to <phase>", "target phase").option("--force <reason>", "force the gate with a reason").action(async (...args) => {
1376
1458
  const cmd = trailingCmd(args);
1377
1459
  const g = cmd.optsWithGlobals();
1378
- const to = typeof g["to"] === "string" ? g["to"] : void 0;
1379
- const force = typeof g["force"] === "string" ? g["force"] : void 0;
1460
+ const to = typeof g.to === "string" ? g.to : void 0;
1461
+ const force = typeof g.force === "string" ? g.force : void 0;
1380
1462
  await taskAdvance({
1381
1463
  ...toCliOptions(g),
1382
1464
  ...to === void 0 ? {} : { to },