@moda-ai/cli 1.55.24 → 1.55.26

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.
Files changed (34) hide show
  1. package/dist/{agent-b4tdzy9q.js → agent-nqmha8ck.js} +8 -8
  2. package/dist/{cli-9hc5bg2r.js → cli-1mbb0ct0.js} +1 -1
  3. package/dist/{cli-vg88mg20.js → cli-5g1tv80h.js} +5 -5
  4. package/dist/{cli-rt26sbz7.js → cli-8tfk8qeb.js} +10 -6
  5. package/dist/{cli-y2ty9vtv.js → cli-b4740tnv.js} +210 -23
  6. package/dist/{cli-qvg26twp.js → cli-cdrvjva5.js} +11 -8
  7. package/dist/{cli-7xc585hc.js → cli-d577kq6h.js} +5 -5
  8. package/dist/{cli-dn91fb56.js → cli-jgfm7w1r.js} +89 -47
  9. package/dist/{cli-9btx12wp.js → cli-jtyvbd8d.js} +1 -1
  10. package/dist/{cli-qycsmxev.js → cli-k6q8dbpk.js} +3 -3
  11. package/dist/{cli-0wr3483h.js → cli-q6f6vrrr.js} +1 -1
  12. package/dist/{cli-dve50x7p.js → cli-sqsjd08w.js} +784 -725
  13. package/dist/cli.js +713 -234
  14. package/dist/{doctor-3r19ry92.js → doctor-w913fj4g.js} +8 -8
  15. package/dist/{fix-exvxzvtz.js → fix-crkwdkt4.js} +6 -6
  16. package/dist/{harness-github-actions-atc5bbkj.js → harness-github-actions-fjhvhgpj.js} +5 -5
  17. package/dist/{harness-vzy7n7ea.js → harness-x3e7d68b.js} +5 -5
  18. package/dist/{index-e1pf1qz2.js → index-2kf3fnnb.js} +11 -11
  19. package/dist/{production-intelligence-c8e0xw3n.js → production-intelligence-7n83c701.js} +75 -15
  20. package/dist/{profile-commands-6e2dgv72.js → profile-commands-ct2t0bfr.js} +3 -3
  21. package/dist/prompt-source-mcp.js +5 -5
  22. package/dist/{prompts-cvzt994j.js → prompts-92aqqb2k.js} +6 -6
  23. package/dist/{provision-q86phh4a.js → provision-gn8raqpt.js} +5 -5
  24. package/dist/{registry-s2vdsfw8.js → registry-ex162j5y.js} +3 -3
  25. package/dist/{skills-tadh1jss.js → skills-2mbsj9bt.js} +5 -5
  26. package/dist/{sync-g8wtecz1.js → sync-kghmmmk5.js} +7 -7
  27. package/package.json +1 -1
  28. package/skills/integration/index.json +2 -2
  29. package/skills/moda-cli/SKILL.md +8 -2
  30. package/skills/moda-cli/references/dashboard-parity.md +1 -1
  31. package/skills/moda-cli/references/output-protocol.md +35 -10
  32. package/skills/moda-cli/references/production-intelligence.md +3 -2
  33. package/skills/moda-cli/references/prompts.md +6 -2
  34. package/skills/moda-cli/references/search-and-traces.md +20 -2
package/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  createCommandContext,
7
7
  parseBooleanFlagValue
8
- } from "./cli-qvg26twp.js";
8
+ } from "./cli-cdrvjva5.js";
9
9
  import {
10
10
  AuditSchema,
11
11
  COMMAND_FLAG_SPECS,
@@ -29,6 +29,7 @@ import {
29
29
  assertPositionals,
30
30
  buildFeedbackRequest,
31
31
  closestMatches,
32
+ describeField,
32
33
  describeSpecFlags,
33
34
  flagSpecFor,
34
35
  isFeedbackRefFlag,
@@ -43,13 +44,15 @@ import {
43
44
  specFlagKeys,
44
45
  specFlagNames,
45
46
  subcommandHelpFlags,
46
- unknownSignalsSubcommandError
47
- } from "./cli-dn91fb56.js";
47
+ unknownSignalsSubcommandError,
48
+ usageLine,
49
+ writeDryRun
50
+ } from "./cli-jgfm7w1r.js";
48
51
  import {
49
52
  ApiError,
50
53
  callControlAPI,
51
54
  callDataAPI
52
- } from "./cli-9btx12wp.js";
55
+ } from "./cli-jtyvbd8d.js";
53
56
  import {
54
57
  AGENT_ERROR_CODES,
55
58
  BATCH_CONCURRENCY,
@@ -110,7 +113,7 @@ import {
110
113
  withFrustrationAnchors,
111
114
  withToolFailureAnchors,
112
115
  withoutDetectionWindows
113
- } from "./cli-dve50x7p.js";
116
+ } from "./cli-sqsjd08w.js";
114
117
  import {
115
118
  __require
116
119
  } from "./cli-0v6na3yp.js";
@@ -2249,6 +2252,7 @@ function buildManifest(commands) {
2249
2252
  "--agent",
2250
2253
  "--stream",
2251
2254
  "--json",
2255
+ "--pretty",
2252
2256
  "--human",
2253
2257
  "--profile <name>",
2254
2258
  "--quiet",
@@ -2275,7 +2279,7 @@ function buildManifest(commands) {
2275
2279
  exit_codes: [
2276
2280
  { code: 0, name: "ok", meaning: "Command completed successfully." },
2277
2281
  { code: 1, name: "error", meaning: "Command, input, API, network, or unexpected failure." },
2278
- { code: 3, name: "degraded", meaning: "Degraded result: `moda ask` local fallback." },
2282
+ { code: 3, name: "degraded", meaning: "Degraded result: `moda ask` local fallback, or `investigate`/`failures` with some sources unavailable (`data.failedSources`)." },
2279
2283
  { code: 4, name: "auth_required", meaning: "Run login_command, then resume." },
2280
2284
  { code: 5, name: "input_required", meaning: "Choose from input_request choices, then resume." },
2281
2285
  { code: 130, name: "cancelled", meaning: "Interrupted or output consumer closed." }
@@ -2390,6 +2394,8 @@ function event(name, when, terminal, deprecated = false) {
2390
2394
  function exitCodesForCommand(command) {
2391
2395
  if (command === "ask")
2392
2396
  return [0, 1, 3, 4, 5, 130];
2397
+ if (command === "investigate" || command === "failures")
2398
+ return [0, 1, 3, 4, 130];
2393
2399
  if (command === "init" || command === "harness")
2394
2400
  return [0, 1, 4, 5, 130];
2395
2401
  return [0, 1, 4, 130];
@@ -2522,46 +2528,46 @@ function leafManifestFields(leaf, commands) {
2522
2528
  import { fileURLToPath } from "url";
2523
2529
  import { realpathSync } from "fs";
2524
2530
  async function runPromptsCommand(...args) {
2525
- return (await import("./prompts-cvzt994j.js")).runPromptsCommand(...args);
2531
+ return (await import("./prompts-92aqqb2k.js")).runPromptsCommand(...args);
2526
2532
  }
2527
2533
  async function runSkillsCommand(...args) {
2528
- return (await import("./skills-tadh1jss.js")).runSkillsCommand(...args);
2534
+ return (await import("./skills-2mbsj9bt.js")).runSkillsCommand(...args);
2529
2535
  }
2530
2536
  async function runFixCommand(...args) {
2531
- return (await import("./fix-exvxzvtz.js")).runFixCommand(...args);
2537
+ return (await import("./fix-crkwdkt4.js")).runFixCommand(...args);
2532
2538
  }
2533
2539
  async function runFixesCommand(...args) {
2534
- return (await import("./fix-exvxzvtz.js")).runFixesCommand(...args);
2540
+ return (await import("./fix-crkwdkt4.js")).runFixesCommand(...args);
2535
2541
  }
2536
2542
  async function runHarnessCommand(...args) {
2537
- return (await import("./harness-vzy7n7ea.js")).runHarnessCommand(...args);
2543
+ return (await import("./harness-x3e7d68b.js")).runHarnessCommand(...args);
2538
2544
  }
2539
2545
  async function runSyncCommand(...args) {
2540
- return (await import("./sync-g8wtecz1.js")).runSyncCommand(...args);
2546
+ return (await import("./sync-kghmmmk5.js")).runSyncCommand(...args);
2541
2547
  }
2542
2548
  async function runRegistryCommand(...args) {
2543
- return (await import("./registry-s2vdsfw8.js")).runRegistryCommand(...args);
2549
+ return (await import("./registry-ex162j5y.js")).runRegistryCommand(...args);
2544
2550
  }
2545
2551
  async function runDoctorCommand(...args) {
2546
- return (await import("./doctor-3r19ry92.js")).runDoctorCommand(...args);
2552
+ return (await import("./doctor-w913fj4g.js")).runDoctorCommand(...args);
2547
2553
  }
2548
2554
  async function runStatusCommand(...args) {
2549
- return (await import("./doctor-3r19ry92.js")).runStatusCommand(...args);
2555
+ return (await import("./doctor-w913fj4g.js")).runStatusCommand(...args);
2550
2556
  }
2551
2557
  async function runAuthCommand(...args) {
2552
- return (await import("./profile-commands-6e2dgv72.js")).runAuthCommand(...args);
2558
+ return (await import("./profile-commands-ct2t0bfr.js")).runAuthCommand(...args);
2553
2559
  }
2554
2560
  async function runConfigCommand(...args) {
2555
- return (await import("./profile-commands-6e2dgv72.js")).runConfigCommand(...args);
2561
+ return (await import("./profile-commands-ct2t0bfr.js")).runConfigCommand(...args);
2556
2562
  }
2557
2563
  async function runProfilesCommand(...args) {
2558
- return (await import("./profile-commands-6e2dgv72.js")).runProfilesCommand(...args);
2564
+ return (await import("./profile-commands-ct2t0bfr.js")).runProfilesCommand(...args);
2559
2565
  }
2560
2566
  async function runAgentCommand(...args) {
2561
- return (await import("./agent-b4tdzy9q.js")).runAgentCommand(...args);
2567
+ return (await import("./agent-nqmha8ck.js")).runAgentCommand(...args);
2562
2568
  }
2563
2569
  async function runProductionCommand(...args) {
2564
- return (await import("./production-intelligence-c8e0xw3n.js")).runProductionCommand(...args);
2570
+ return (await import("./production-intelligence-7n83c701.js")).runProductionCommand(...args);
2565
2571
  }
2566
2572
  function parseArgs(argv) {
2567
2573
  const args = argv.slice(2);
@@ -2575,10 +2581,10 @@ function parseArgs(argv) {
2575
2581
  let endOfOptions = false;
2576
2582
  while (i < args.length) {
2577
2583
  const arg = args[i];
2578
- if (!endOfOptions && arg === "--" && command !== "") {
2584
+ if (!endOfOptions && arg === "--" && command === "") {} else if (!endOfOptions && arg === "--") {
2579
2585
  endOfOptions = true;
2580
2586
  } else if (!endOfOptions && arg.startsWith("--")) {
2581
- i = parseFlag(args, i, flags, command === "", bareFlags, parseCommandFor(command, positional));
2587
+ i = parseFlag(args, i, flags, command === "", bareFlags, command, positional);
2582
2588
  } else if (!endOfOptions && command !== "" && /^-[A-Za-z]/.test(arg)) {
2583
2589
  shortFlags.push(arg);
2584
2590
  } else if (command === "") {
@@ -2655,7 +2661,8 @@ var RAW_BOOLEAN_FLAGS = new Set([
2655
2661
  "tui",
2656
2662
  "no-tui",
2657
2663
  "no-telemetry",
2658
- "no-update-check"
2664
+ "no-update-check",
2665
+ "pretty"
2659
2666
  ]);
2660
2667
  var GLOBAL_BOOLEAN_FLAGS = new Set([
2661
2668
  "agent",
@@ -2671,7 +2678,8 @@ var GLOBAL_BOOLEAN_FLAGS = new Set([
2671
2678
  "dry-run",
2672
2679
  "stream",
2673
2680
  "tui",
2674
- "no-tui"
2681
+ "no-tui",
2682
+ "pretty"
2675
2683
  ]);
2676
2684
  var BOOLEAN_WORDS = new Set(["true", "false", "yes", "no", "on", "off"]);
2677
2685
  var META_SWITCHES = new Set([
@@ -2685,14 +2693,64 @@ var META_SWITCHES = new Set([
2685
2693
  "tui",
2686
2694
  "no-tui",
2687
2695
  "no-telemetry",
2688
- "no-update-check"
2696
+ "no-update-check",
2697
+ "pretty"
2689
2698
  ]);
2690
2699
  var AGENT_VALUE_COMMANDS = new Set(["harness", "sync"]);
2691
- var COMMAND_BOOLEAN_FLAGS = {
2692
- context: new Set(["all", "metadata-only"])
2693
- };
2694
- function parseFlag(args, index, flags, beforeCommand, bareFlags, command = "") {
2695
- const set = (key2, value, bare) => {
2700
+ function canonicalFlagKey(key) {
2701
+ return key.replace(/_/g, "-");
2702
+ }
2703
+ function canonicalizeFlagKeys(flags, bareFlags) {
2704
+ const entries = Object.entries(flags);
2705
+ for (const [key] of entries)
2706
+ delete flags[key];
2707
+ for (const [key, value] of entries)
2708
+ flags[canonicalFlagKey(key)] = value;
2709
+ if (bareFlags) {
2710
+ const canonical = [...new Set(bareFlags.map(canonicalFlagKey))];
2711
+ bareFlags.splice(0, bareFlags.length, ...canonical);
2712
+ }
2713
+ }
2714
+ function parseHandlersFor(command, verb) {
2715
+ if (command === "review")
2716
+ return leafHandlers(leafFor("review", undefined));
2717
+ if (CANONICAL_NOUNS.includes(command) && verb !== undefined) {
2718
+ const leaf = leafFor(command, verb);
2719
+ if (leaf)
2720
+ return leaf.path === "problems unexplained" ? ["problems"] : leafHandlers(leaf);
2721
+ }
2722
+ return [commandRegistry.find(command)?.name ?? command];
2723
+ }
2724
+ function specsOf(name) {
2725
+ const spec = flagSpecFor(name);
2726
+ return spec ? [spec, ...Object.values(spec.subcommands ?? {})] : [];
2727
+ }
2728
+ var switchFlagCache = new Map;
2729
+ function commandSwitchFlags(command, verb) {
2730
+ const handlers = parseHandlersFor(command, verb);
2731
+ const cacheKey = handlers.join(",");
2732
+ const cached = switchFlagCache.get(cacheKey);
2733
+ if (cached)
2734
+ return cached;
2735
+ const booleans = new Set;
2736
+ const valued = new Set;
2737
+ for (const handler of handlers) {
2738
+ for (const spec of specsOf(handler)) {
2739
+ for (const flag of describeSpecFlags(spec)) {
2740
+ (flag.type === "boolean" ? booleans : valued).add(flag.name.slice(2));
2741
+ }
2742
+ }
2743
+ }
2744
+ const switches = new Set([...booleans].filter((name) => !valued.has(name)));
2745
+ switchFlagCache.set(cacheKey, switches);
2746
+ return switches;
2747
+ }
2748
+ function takesIdPositional(command, verb) {
2749
+ return parseHandlersFor(command, verb).some((handler) => specsOf(handler).some((spec) => (spec.positionals ?? []).some((positional) => !positional.variadic)));
2750
+ }
2751
+ function parseFlag(args, index, flags, beforeCommand, bareFlags, command = "", verb) {
2752
+ const set = (rawKey, value, bare) => {
2753
+ const key2 = canonicalFlagKey(rawKey);
2696
2754
  flags[key2] = value;
2697
2755
  const at = bareFlags.indexOf(key2);
2698
2756
  if (at !== -1)
@@ -2708,17 +2766,19 @@ function parseFlag(args, index, flags, beforeCommand, bareFlags, command = "") {
2708
2766
  }
2709
2767
  const key = arg.slice(2);
2710
2768
  const next = args[index + 1];
2711
- if (beforeCommand && GLOBAL_BOOLEAN_FLAGS.has(key)) {
2769
+ if (beforeCommand && GLOBAL_BOOLEAN_FLAGS.has(canonicalFlagKey(key))) {
2712
2770
  set(key, "true", true);
2713
2771
  return index;
2714
2772
  }
2715
- const switchName = key.replace(/_/g, "-");
2716
- if (META_SWITCHES.has(switchName) || switchName === "agent" && !AGENT_VALUE_COMMANDS.has(command)) {
2773
+ const switchName = canonicalFlagKey(key);
2774
+ const parseCommand = parseCommandFor(command, verb);
2775
+ if (META_SWITCHES.has(switchName) || switchName === "agent" && !AGENT_VALUE_COMMANDS.has(parseCommand)) {
2717
2776
  set(key, "true", true);
2718
2777
  return index;
2719
2778
  }
2720
- const idFollows = COMMAND_BOOLEAN_FLAGS[command]?.has(switchName) === true;
2721
- const isSwitch = RAW_BOOLEAN_FLAGS.has(switchName) || idFollows;
2779
+ const schemaSwitch = !beforeCommand && commandSwitchFlags(command, verb).has(switchName);
2780
+ const isSwitch = RAW_BOOLEAN_FLAGS.has(switchName) || schemaSwitch;
2781
+ const idFollows = schemaSwitch && takesIdPositional(command, verb);
2722
2782
  const takesNext = BOOLEAN_WORDS.has(next?.toLowerCase() ?? "") || !idFollows && (next === "0" || next === "1");
2723
2783
  if (!beforeCommand && isSwitch && !takesNext) {
2724
2784
  set(key, "true", true);
@@ -2750,9 +2810,32 @@ function flagsToArgs(flags, positionalKey, positionalValue) {
2750
2810
  result[positionalKey] = positionalValue;
2751
2811
  }
2752
2812
  for (const [key, value] of Object.entries(flags)) {
2753
- const snakeKey = key.replace(/-/g, "_");
2754
- const num = Number(value);
2755
- result[snakeKey] = isNaN(num) || value === "true" ? value : num;
2813
+ result[key.replace(/-/g, "_")] = value;
2814
+ }
2815
+ return result;
2816
+ }
2817
+ var DECIMAL_TEXT = /^[+-]?(?:\d+\.?\d*|\.\d+)$/;
2818
+ function coerceNumericFlags(schema, args, command) {
2819
+ const shape = schemaShape(schema);
2820
+ const result = { ...args };
2821
+ for (const [key, value] of Object.entries(args)) {
2822
+ if (typeof value !== "string" || !Object.hasOwn(shape, key))
2823
+ continue;
2824
+ const info = describeField(shape[key]);
2825
+ if (info.type !== "integer" && info.type !== "number")
2826
+ continue;
2827
+ const text = value.trim();
2828
+ if (!DECIMAL_TEXT.test(text)) {
2829
+ const flag = kebabFlag(key);
2830
+ const kind = info.type === "integer" ? "a whole number" : "a number";
2831
+ const range = info.min !== undefined && info.max !== undefined ? ` from ${info.min} to ${info.max}` : "";
2832
+ throw new CliInputError(`${flag} must be ${kind}${range} (got '${value}'), so nothing was sent.`, `Pass digits only, e.g. ${flag}=${info.min ?? 1}. See '${canonicalCommandText(`moda ${command}`)} --help'.`, `bad_number:${flag.slice(2)}`, {
2833
+ field: flag,
2834
+ ...info.min !== undefined || info.max !== undefined ? { range: { ...info.min !== undefined ? { min: info.min } : {}, ...info.max !== undefined ? { max: info.max } : {} } } : {},
2835
+ suggestedCommands: [`${canonicalCommandText(`moda ${command}`)} --help`]
2836
+ });
2837
+ }
2838
+ result[key] = Number(text);
2756
2839
  }
2757
2840
  return result;
2758
2841
  }
@@ -2820,6 +2903,7 @@ Silent aliases: conversations = traces list, cluster-conversations = cluster-tra
2820
2903
 
2821
2904
  Output: piped stdout, CI or an agent env (CLAUDECODE/CURSOR/MODA_AGENT) => agent JSON; a terminal => prose.
2822
2905
  --json | --agent [--stream] | --human Force an output mode (or MODA_FORMAT=human|json|agent|agent-stream)
2906
+ --json is compact when piped; --pretty indents it (indented by default on a terminal)
2823
2907
  --profile=NAME --quiet --no-update-check --version --json-schemas
2824
2908
  Exit codes: 0 ok, 1 error (errors[0].code says which), 3 degraded, 4 auth required, 5 input required, 130 cancelled.
2825
2909
  A command rejects any flag it does not take; the error lists errors[0].allowed and errors[0].did_you_mean.`);
@@ -3082,8 +3166,27 @@ function legacyNamesHelpLine() {
3082
3166
  return lines.join(`
3083
3167
  `);
3084
3168
  }
3085
- async function attachWindows(rows, windowSize) {
3086
- return Promise.all(rows.map(async (row) => {
3169
+ var ENRICH_CONCURRENCY = BATCH_CONCURRENCY;
3170
+ function enrichedRowBytes(row) {
3171
+ const window = asRecord(row.window);
3172
+ if (window && !("error" in window)) {
3173
+ const { conversation: _conversation, ...rest } = row;
3174
+ return jsonByteLength(rest);
3175
+ }
3176
+ return jsonByteLength(row);
3177
+ }
3178
+ async function enrichWithinBudget(rows, enrich, budgetBytes, skipKey) {
3179
+ let spent = 0;
3180
+ const enriched = await mapWithConcurrency(rows, ENRICH_CONCURRENCY, async (row) => {
3181
+ const out = await enrich(row);
3182
+ if (budgetBytes !== undefined)
3183
+ spent += enrichedRowBytes(out);
3184
+ return out;
3185
+ }, budgetBytes === undefined ? {} : { shouldStart: () => spent <= budgetBytes });
3186
+ return [...enriched, ...rows.slice(enriched.length).map((row) => ({ ...row, [skipKey]: "agent_output_budget" }))];
3187
+ }
3188
+ async function attachWindows(rows, windowSize, budgetBytes) {
3189
+ return enrichWithinBudget(rows, async (row) => {
3087
3190
  const anchor = asRecord(row.anchor);
3088
3191
  if (!anchor || anchor.no_anchor === true) {
3089
3192
  return { ...row, window: null };
@@ -3112,7 +3215,7 @@ async function attachWindows(rows, windowSize) {
3112
3215
  const message = err instanceof Error ? err.message : String(err);
3113
3216
  return { ...row, window: { error: message } };
3114
3217
  }
3115
- }));
3218
+ }, budgetBytes, "window_skipped");
3116
3219
  }
3117
3220
  var TOOL_FAILURE_DETAIL_DEFAULT_LIMIT = 5;
3118
3221
  var TOOL_FAILURE_AGENT_SUBTYPES = 20;
@@ -3121,7 +3224,8 @@ async function printWithOptionalWindows(enriched, listKey, includeWindow, window
3121
3224
  let data = enriched;
3122
3225
  if (includeWindow) {
3123
3226
  const list = Array.isArray(enriched[listKey]) ? enriched[listKey] : [];
3124
- data = { ...enriched, [listKey]: await attachWindows(list, windowSize) };
3227
+ const agentMode = output.mode === "agent" || output.mode === "agent-stream";
3228
+ data = { ...enriched, [listKey]: await attachWindows(list, windowSize, agentMode ? agentMaxOutputBytes() : undefined) };
3125
3229
  }
3126
3230
  if (finalize)
3127
3231
  data = finalize(data, allWarnings);
@@ -3179,8 +3283,10 @@ function auditCapStatus(data, limit) {
3179
3283
  const nextCursor = typeof cursorValue === "string" && cursorValue ? cursorValue : null;
3180
3284
  return { rowsCapped, spansCapped, hasMoreKnown: hasMore, rows, spans, reason, nextCursor };
3181
3285
  }
3182
- var AUDIT_MIN_PAGE_BYTES = 32768;
3183
3286
  var AUDIT_AGENT_RESERVE_BYTES = 16384;
3287
+ function auditMaxBytes(budgetBytes) {
3288
+ return Math.max(1, budgetBytes - Math.min(AUDIT_AGENT_RESERVE_BYTES, Math.floor(budgetBytes / 4)));
3289
+ }
3184
3290
  async function fetchAudit(id, query2, maxBytes) {
3185
3291
  const pageQuery = new URLSearchParams(query2);
3186
3292
  if (maxBytes !== undefined)
@@ -3188,16 +3294,22 @@ async function fetchAudit(id, query2, maxBytes) {
3188
3294
  const queryString = pageQuery.toString() ? `?${pageQuery.toString()}` : "";
3189
3295
  return callDataAPI(`/audit/${encodeURIComponent(id)}${queryString}`);
3190
3296
  }
3191
- function auditOutput(command, params, data) {
3192
- const capped = auditCapStatus(data, params.limit ?? AUDIT_DEFAULT_LIMIT);
3297
+ function auditOutput(command, params, data, keep) {
3193
3298
  const auditRecord = asRecord(data);
3194
- if (!capped || !auditRecord) {
3195
- return { data: auditRecord ? { ...auditRecord, truncated: false, has_more: false, next_cursor: null } : data, options: {} };
3196
- }
3299
+ const pageSpans = Array.isArray(auditRecord?.spans) ? auditRecord.spans : [];
3300
+ const offset = params.span_offset ?? 0;
3301
+ const end = Math.max(offset, keep === undefined ? pageSpans.length : Math.min(pageSpans.length, offset + keep));
3302
+ const sliced = offset > 0 || end < pageSpans.length;
3303
+ const shown = Math.max(0, Math.min(end, pageSpans.length) - offset);
3304
+ const partial = end < pageSpans.length;
3305
+ const record = auditRecord && sliced ? {
3306
+ ...auditRecord,
3307
+ spans: pageSpans.slice(offset, end),
3308
+ span_offset: offset,
3309
+ page_spans: pageSpans.length,
3310
+ ...partial ? { next_span_offset: end } : {}
3311
+ } : auditRecord;
3197
3312
  const limit = params.limit ?? AUDIT_DEFAULT_LIMIT;
3198
- const perTrace = "audit one OTLP trace at a time with `moda traces spans <trace_id> --kind=trace` (ids in summary.trace_ids)";
3199
- const warnings = [];
3200
- const nextCommands = [];
3201
3313
  const auditFlags = (extra) => [
3202
3314
  `${canonicalCommandText(`moda ${command}`)} ${shellArg(params.id)}`,
3203
3315
  params.kind && params.kind !== "auto" ? `--kind=${params.kind}` : "",
@@ -3205,6 +3317,52 @@ function auditOutput(command, params, data) {
3205
3317
  params.include_bodies === true ? "--include-bodies" : "",
3206
3318
  ...extra
3207
3319
  ].filter(Boolean).join(" ");
3320
+ const sliceWarnings = [];
3321
+ if (offset > 0 && offset >= pageSpans.length) {
3322
+ sliceWarnings.push(`--span-offset=${offset} is past the ${pageSpans.length} span(s) of this page, so no spans are shown.`);
3323
+ } else if (sliced && !partial) {
3324
+ sliceWarnings.push(`Showing spans ${offset}-${end - 1}, the last part of the ${pageSpans.length} span(s) in this page.`);
3325
+ }
3326
+ if (partial && record) {
3327
+ const continueWith = auditFlags([
3328
+ params.limit ? `--limit=${params.limit}` : "",
3329
+ params.cursor ? `--cursor=${params.cursor}` : "",
3330
+ params.page_bytes ? `--page-bytes=${params.page_bytes}` : "",
3331
+ `--span-offset=${end}`
3332
+ ]);
3333
+ const summary = asRecord(record.summary);
3334
+ return {
3335
+ data: {
3336
+ ...record,
3337
+ truncated: true,
3338
+ has_more: true,
3339
+ next_cursor: null,
3340
+ ...summary ? { summary: { ...summary, has_more: true, next_cursor: null } } : {}
3341
+ },
3342
+ options: {
3343
+ warnings: [
3344
+ `Showing spans ${offset}-${end - 1} of the ${pageSpans.length} in this page to stay under the agent output byte limit (--json has no limit). Read the next ${pageSpans.length - end} with: ${continueWith}. next_cursor is withheld until the last spans of this page are read. Orphan/duplicate analysis covers the whole page.`
3345
+ ],
3346
+ nextCommands: [{
3347
+ command: continueWith,
3348
+ purpose: `Read spans ${end}-${pageSpans.length - 1} of this page (left out to fit the agent byte limit).`,
3349
+ mutability: "read",
3350
+ requires_approval: false
3351
+ }]
3352
+ }
3353
+ };
3354
+ }
3355
+ const capped = auditCapStatus(data, limit);
3356
+ if (!capped || !record) {
3357
+ return {
3358
+ data: record ? { ...record, truncated: false, has_more: false, next_cursor: null } : data,
3359
+ options: sliceWarnings.length > 0 ? { warnings: sliceWarnings } : {}
3360
+ };
3361
+ }
3362
+ const perTrace = "audit one OTLP trace at a time with `moda traces spans <trace_id> --kind=trace` (ids in summary.trace_ids)";
3363
+ const shownText = sliced ? `${shown} shown here (${pageSpans.length} in this page)` : `${capped.spans} shown`;
3364
+ const warnings = [...sliceWarnings];
3365
+ const nextCommands = [];
3208
3366
  if (capped.nextCursor) {
3209
3367
  nextCommands.push({
3210
3368
  command: auditFlags([params.limit ? `--limit=${params.limit}` : "", `--cursor=${capped.nextCursor}`]),
@@ -3214,20 +3372,20 @@ function auditOutput(command, params, data) {
3214
3372
  });
3215
3373
  }
3216
3374
  if (capped.reason === "byte_limit") {
3217
- warnings.push(`Spans were capped: the response reached the server's size budget after ${capped.rows} ingest row(s), so this trace has more spans than the ${capped.spans} shown. Continue with the next_cursor page${params.include_bodies === true ? ", or drop --include-bodies to fit more rows per page" : ""}. Orphan/duplicate analysis covers this page only.`);
3375
+ warnings.push(`Spans were capped: the response reached the server's size budget after ${capped.rows} ingest row(s), so this trace has more spans than the ${shownText}. Continue with the next_cursor page${params.include_bodies === true ? ", or drop --include-bodies to fit more rows per page" : ""}. Orphan/duplicate analysis covers this page only.`);
3218
3376
  } else if (capped.reason === "span_limit" && capped.nextCursor) {
3219
- warnings.push(`Spans were capped: the page reached the server's per-page span cap after ${capped.rows} ingest row(s), so this trace has more spans than the ${capped.spans} shown. Continue with the next_cursor page. Orphan/duplicate analysis covers this page only.`);
3377
+ warnings.push(`Spans were capped: the page reached the server's per-page span cap after ${capped.rows} ingest row(s), so this trace has more spans than the ${shownText}. Continue with the next_cursor page. Orphan/duplicate analysis covers this page only.`);
3220
3378
  } else if (capped.rowsCapped) {
3221
3379
  const bigger = Math.min(AUDIT_MAX_LIMIT, limit * 5);
3222
- warnings.push(`Spans were capped: read ${capped.rows} ingest row(s), the --limit=${limit} maximum, so this trace ${capped.hasMoreKnown ? "has" : "may have"} more spans than the ${capped.spans} shown. ` + (capped.nextCursor ? "Page on with the next_cursor command, or " : "") + (limit < AUDIT_MAX_LIMIT ? `${capped.nextCursor ? "rerun" : "Rerun"} with --limit=${bigger} (max ${AUDIT_MAX_LIMIT}), or ${perTrace}.` : capped.nextCursor ? `${perTrace}.` : `${perTrace[0].toUpperCase()}${perTrace.slice(1)}.`));
3223
- if (limit < AUDIT_MAX_LIMIT && !params.cursor) {
3380
+ warnings.push(`Spans were capped: read ${capped.rows} ingest row(s), the --limit=${limit} maximum, so this trace ${capped.hasMoreKnown ? "has" : "may have"} more spans than the ${shownText}. ` + (capped.nextCursor ? "Page on with the next_cursor command, or " : "") + (limit < AUDIT_MAX_LIMIT ? `${capped.nextCursor ? "rerun" : "Rerun"} with --limit=${bigger} (max ${AUDIT_MAX_LIMIT}), or ${perTrace}.` : capped.nextCursor ? `${perTrace}.` : `${perTrace[0].toUpperCase()}${perTrace.slice(1)}.`));
3381
+ if (limit < AUDIT_MAX_LIMIT && !params.cursor && !sliced) {
3224
3382
  nextCommands.push({ command: auditFlags([`--limit=${bigger}`]), purpose: "Read more ingest rows of this trace.", mutability: "read", requires_approval: false });
3225
3383
  }
3226
3384
  } else {
3227
3385
  warnings.push(`Spans were capped: the server returned ${capped.spans} span(s) and marked the response truncated. ${perTrace[0].toUpperCase()}${perTrace.slice(1)}.`);
3228
3386
  }
3229
3387
  return {
3230
- data: { ...auditRecord, truncated: true, has_more: capped.rowsCapped, next_cursor: capped.nextCursor },
3388
+ data: { ...record, truncated: true, has_more: capped.rowsCapped, next_cursor: capped.nextCursor },
3231
3389
  options: { warnings, ...nextCommands.length > 0 ? { nextCommands } : {} }
3232
3390
  };
3233
3391
  }
@@ -3416,8 +3574,8 @@ async function runTailCommand(params, context) {
3416
3574
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
3417
3575
  }
3418
3576
  }
3419
- async function attachWorldState(rows) {
3420
- return Promise.all(rows.map(async (row) => {
3577
+ async function attachWorldState(rows, budgetBytes) {
3578
+ return enrichWithinBudget(rows, async (row) => {
3421
3579
  const conv_id = asString(row.conversation_id) ?? "";
3422
3580
  if (!conv_id) {
3423
3581
  return { ...row, world_state: null };
@@ -3429,7 +3587,7 @@ async function attachWorldState(rows) {
3429
3587
  const message = err instanceof Error ? err.message : String(err);
3430
3588
  return { ...row, world_state: { error: message } };
3431
3589
  }
3432
- }));
3590
+ }, budgetBytes, "world_state_skipped");
3433
3591
  }
3434
3592
  function recordSearchTelemetry(context, data) {
3435
3593
  const record = asRecord(data);
@@ -3539,7 +3697,7 @@ function parseCommandFlags(schema, args, command, options = {}) {
3539
3697
  ]);
3540
3698
  const unknown = Object.keys(args).filter((key) => !known.has(key));
3541
3699
  if (unknown.includes("dry_run")) {
3542
- throw new CliInputError(`'moda ${command}' has no dry-run mode, so nothing was sent.`, `Remove --dry-run to run it for real. Commands with a real dry-run: sync, registry push, harness sync/delete/register, prompts sync/decide, skills sync/policy, signals <write subcommands>, problem-reopen.`, "unsupported_dry_run");
3700
+ throw new CliInputError(`'moda ${command}' has no dry-run mode (it does not write), so nothing was sent.`, `Remove --dry-run. Every write takes --dry-run: problems feedback|reopen|link, review, feedback, signals <write subcommands>, sync, registry push, harness sync/delete/register, prompts sync/decide, skills sync/policy.`, "unsupported_dry_run");
3543
3701
  }
3544
3702
  if (unknown.length > 0) {
3545
3703
  const flagKeys = [
@@ -3575,8 +3733,9 @@ function parseCommandFlags(schema, args, command, options = {}) {
3575
3733
  suggestedCommands: [helpCommand]
3576
3734
  });
3577
3735
  }
3736
+ const typed = coerceNumericFlags(schema, args, command);
3578
3737
  try {
3579
- return schema.parse(args);
3738
+ return schema.parse(typed);
3580
3739
  } catch (error) {
3581
3740
  if (error instanceof z.ZodError)
3582
3741
  attachFlagDetails(error, schema, command);
@@ -3608,13 +3767,6 @@ function specParseOptions(spec) {
3608
3767
  ...spec.flagAliases ? { flagAliases: spec.flagAliases } : {}
3609
3768
  };
3610
3769
  }
3611
- function withStringIdFlags(args, flags) {
3612
- for (const flag of ["user-id", "conversation-id"]) {
3613
- if (flags[flag] !== undefined)
3614
- args[flag.replace(/-/g, "_")] = flags[flag];
3615
- }
3616
- return args;
3617
- }
3618
3770
  function shellArg(value) {
3619
3771
  if (/^[A-Za-z0-9_.,:@%+\/=-]+$/.test(value))
3620
3772
  return value;
@@ -3722,8 +3874,7 @@ async function searchFallbackToTraces(params) {
3722
3874
  return;
3723
3875
  const pagination = asRecord(traces.pagination);
3724
3876
  const total = asNumber(pagination?.total) ?? rows.length;
3725
- const nextOffset = offset + rows.length;
3726
- const hasMore = pagination?.has_more === true || (asNumber(pagination?.total) ?? 0) > nextOffset;
3877
+ const serverHasMore = pagination?.has_more === true;
3727
3878
  const traceMode = asString(traces.search_mode) ?? "keyword";
3728
3879
  const literal = traceMode === "keyword";
3729
3880
  if (params.mode === "keyword" && !literal) {
@@ -3745,33 +3896,46 @@ async function searchFallbackToTraces(params) {
3745
3896
  }));
3746
3897
  const first = asString(rows[0].conversation_id);
3747
3898
  const describe = literal ? "trace-level keyword matches (each trace contains the words)" : `trace-level ${traceMode}-ranked related traces (they may not contain the exact words; fallback.total is the candidate pool, not a match count)`;
3748
- const nextCommands = [];
3749
- if (first) {
3750
- nextCommands.push({ command: `moda traces spans ${first}`, purpose: "Open the top trace to locate the relevant message.", mutability: "read", requires_approval: false });
3751
- }
3752
- if (hasMore) {
3753
- nextCommands.push({ command: `${tracesCommand} --offset=${nextOffset}`, purpose: "Page through further trace-level results.", mutability: "read", requires_approval: false });
3754
- }
3755
- return {
3756
- kind: "results",
3757
- hasMore,
3758
- data: {
3759
- results,
3760
- fallback: {
3761
- source: "traces_search",
3762
- reason: "message_index_empty",
3763
- grain: "trace",
3764
- search_mode: traceMode,
3765
- total,
3766
- total_kind: literal ? "matches" : "candidate_pool",
3767
- has_more: hasMore,
3768
- ...offset > 0 ? { offset } : {},
3769
- ...hasMore ? { next_offset: nextOffset } : {}
3770
- }
3771
- },
3772
- warning: `Message-level search returned 0 results, so these are ${describe}, from \`moda traces --search\`. Each result is a whole trace, not a message, with no score or message index. Open one with \`moda traces spans <conversation_id>\` or \`moda traces get <conversation_id>\` to find the matching turn.` + (hasMore ? " More exist: page with the suggested `moda traces --search ... --offset` command." : ""),
3773
- nextCommands
3899
+ const page = (kept) => {
3900
+ const omitted = rows.length - kept;
3901
+ const nextOffset = offset + kept;
3902
+ const hasMore = omitted > 0 || serverHasMore || total > nextOffset;
3903
+ const nextCommands = [];
3904
+ if (first) {
3905
+ nextCommands.push({ command: `moda traces spans ${shellArg(first)}`, purpose: "Open the top trace to locate the relevant message.", mutability: "read", requires_approval: false });
3906
+ }
3907
+ if (hasMore) {
3908
+ nextCommands.push({
3909
+ command: `${tracesCommand} --offset=${nextOffset}`,
3910
+ purpose: omitted > 0 ? `Read the ${omitted} trace-level result(s) left out to fit the agent byte limit, and the ones after them.` : "Page through further trace-level results.",
3911
+ mutability: "read",
3912
+ requires_approval: false
3913
+ });
3914
+ }
3915
+ return {
3916
+ returned: kept,
3917
+ offset,
3918
+ hasMore,
3919
+ data: {
3920
+ results: results.slice(0, kept),
3921
+ fallback: {
3922
+ source: "traces_search",
3923
+ reason: "message_index_empty",
3924
+ grain: "trace",
3925
+ search_mode: traceMode,
3926
+ total,
3927
+ total_kind: literal ? "matches" : "candidate_pool",
3928
+ has_more: hasMore,
3929
+ ...offset > 0 ? { offset } : {},
3930
+ ...hasMore ? { next_offset: nextOffset } : {},
3931
+ ...omitted > 0 ? { omitted_for_byte_limit: omitted } : {}
3932
+ }
3933
+ },
3934
+ warning: `Message-level search returned 0 results, so these are ${describe}, from \`moda traces --search\`. Each result is a whole trace, not a message, with no score or message index. Open one with \`moda traces spans <conversation_id>\` or \`moda traces get <conversation_id>\` to find the matching turn.` + (omitted > 0 ? ` ${kept} of the ${rows.length} trace-level results read are shown, to stay under the agent output byte limit (--json has no limit); the rest start at --offset=${nextOffset}.` : "") + (hasMore ? " More exist: page with the suggested `moda traces --search ... --offset` command." : ""),
3935
+ nextCommands
3936
+ };
3774
3937
  };
3938
+ return { kind: "results", rows: rows.length, page };
3775
3939
  }
3776
3940
  function capProblemsBlocks(data, limit) {
3777
3941
  const truncated = {};
@@ -3822,6 +3986,31 @@ var NO_POSITIONAL_COMMANDS = new Set([
3822
3986
  "tail",
3823
3987
  "problem-impact"
3824
3988
  ]);
3989
+ function rejectInapplicableFlags(command, params, keys, why) {
3990
+ const given = keys.filter((key) => params[key] !== undefined);
3991
+ if (given.length === 0)
3992
+ return;
3993
+ const names = given.map(kebabFlag);
3994
+ const shown = canonicalCommandText(`moda ${command}`);
3995
+ throw new CliInputError(`${names.join(", ")} ${given.length > 1 ? "do" : "does"} not apply ${why}, so nothing was sent.`, `Drop ${names.join(", ")}, or pick the mode that uses ${given.length > 1 ? "them" : "it"}. See '${shown} --help'.`, `inapplicable_flag:${names[0].slice(2)}`, { field: names[0], suggestedCommands: [`${shown} --help`] });
3996
+ }
3997
+ var BATCH_POSITIONAL_COMMANDS = new Set(["context", "audit", "trace"]);
3998
+ function assertPositionalArity(command, positionals) {
3999
+ const spec = flagSpecFor(command);
4000
+ const expected = spec?.positionals ?? [];
4001
+ if (expected.length === 0 || expected.some((entry) => entry.variadic))
4002
+ return;
4003
+ if (BATCH_POSITIONAL_COMMANDS.has(command))
4004
+ return;
4005
+ if (command === "problem" && positionals[0] === "remainder")
4006
+ return;
4007
+ if (positionals.length <= expected.length)
4008
+ return;
4009
+ const extra = positionals.slice(expected.length);
4010
+ const shown = canonicalCommandText(`moda ${command}`);
4011
+ const takes = expected.map((entry) => `<${entry.name}>`).join(" ");
4012
+ throw new CliInputError(`Unexpected argument${extra.length > 1 ? "s" : ""} '${extra.join(" ")}' for '${shown}', which takes ${takes}, so nothing was sent.`, `Quote a value that has spaces, and pass free text as a flag value (e.g. --reason="..."). Usage: ${canonicalCommandText(usageLine(command, spec))}`, "unexpected_positional", { field: `<${expected[expected.length - 1].name}>`, suggestedCommands: [`${shown} --help`] });
4013
+ }
3825
4014
  function assertHandParsedFlags(command, flags, positionals) {
3826
4015
  if (command === "signals" && positionals[0]) {
3827
4016
  if (SIGNALS_MANAGEMENT_SUBCOMMANDS.has(positionals[0]))
@@ -3831,6 +4020,7 @@ function assertHandParsedFlags(command, flags, positionals) {
3831
4020
  if (NO_POSITIONAL_COMMANDS.has(command) && positionals.length > 0) {
3832
4021
  throw new CliInputError(`'moda ${command}' takes no arguments, but got '${positionals.join(" ")}'.`, `Nothing was run. See 'moda ${command} --help' for its flags.`);
3833
4022
  }
4023
+ assertPositionalArity(command, positionals);
3834
4024
  const hand = handParsedFlags(command);
3835
4025
  if (!hand)
3836
4026
  return;
@@ -3959,6 +4149,10 @@ async function readContextMetadataOnly(contextPath, query2) {
3959
4149
  }
3960
4150
  var FULL_TRANSCRIPT_DEFAULT_MAX_MESSAGES = 500;
3961
4151
  var CONTEXT_RANGE_PAGE_SIZE = 200;
4152
+ var CONTEXT_BUDGET_BYTES_PER_FIRST_PAGE_MESSAGE = 480;
4153
+ function jsonByteLength(value) {
4154
+ return Buffer.byteLength(JSON.stringify(value) ?? "", "utf8");
4155
+ }
3962
4156
  var LEGACY_WINDOW_MAX_MESSAGES = 100;
3963
4157
  var CONTEXT_MESSAGE_TRUNCATE_CHARS = 500;
3964
4158
  var LONE_SURROGATE = /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/g;
@@ -3996,8 +4190,15 @@ async function readFullTranscript(conversationId, opts = {}) {
3996
4190
  let nextFrom = null;
3997
4191
  let legacy = false;
3998
4192
  let cursor = start;
4193
+ let readBytes = 0;
4194
+ const byteBudget = opts.fits ? opts.maxBytes : undefined;
3999
4195
  for (;; ) {
4000
- const count = Math.min(CONTEXT_RANGE_PAGE_SIZE, requested - messages.length);
4196
+ let count = Math.min(CONTEXT_RANGE_PAGE_SIZE, requested - messages.length);
4197
+ if (byteBudget !== undefined) {
4198
+ const average = messages.length > 0 ? readBytes / messages.length : 0;
4199
+ const estimate = average > 0 ? Math.ceil(Math.max(0, byteBudget - readBytes) / average) + 1 : Math.max(1, Math.floor(byteBudget / CONTEXT_BUDGET_BYTES_PER_FIRST_PAGE_MESSAGE));
4200
+ count = Math.max(1, Math.min(count, estimate));
4201
+ }
4001
4202
  let page;
4002
4203
  try {
4003
4204
  page = asRecord(await callDataAPI(`${path}?from=${cursor}&count=${count}`)) ?? {};
@@ -4015,10 +4216,14 @@ async function readFullTranscript(conversationId, opts = {}) {
4015
4216
  }
4016
4217
  const batch = pageMessages(page);
4017
4218
  messages.push(...batch);
4219
+ if (byteBudget !== undefined)
4220
+ readBytes += batch.reduce((sum, message) => sum + jsonByteLength(message) + 1, 0);
4018
4221
  const next = asNumber(page.next_from);
4019
4222
  nextFrom = page.has_more === true && next !== null && next !== undefined && next > cursor ? next : null;
4020
4223
  if (nextFrom === null || batch.length === 0 || messages.length >= requested)
4021
4224
  break;
4225
+ if (byteBudget !== undefined && readBytes > byteBudget)
4226
+ break;
4022
4227
  cursor = nextFrom;
4023
4228
  }
4024
4229
  if (legacy) {
@@ -4115,12 +4320,14 @@ async function readFullTranscript(conversationId, opts = {}) {
4115
4320
  }
4116
4321
  return build(low);
4117
4322
  }
4118
- async function readContextItem(command, params, inputWarnings, fits) {
4323
+ async function readContextItem(command, params, inputWarnings, budget) {
4324
+ const fits = budget?.fits;
4119
4325
  if (params.all === true) {
4120
4326
  const full = await readFullTranscript(params.conversation_id, {
4121
4327
  from: params.from,
4122
4328
  maxMessages: params.max_messages,
4123
- fits: fits ? (page) => fits(page.data, { warnings: page.warnings, nextCommands: page.nextCommands }) : undefined
4329
+ fits: fits ? (page) => fits(page.data, { warnings: page.warnings, nextCommands: page.nextCommands }) : undefined,
4330
+ maxBytes: budget?.maxBytes
4124
4331
  });
4125
4332
  const fullWarnings = [...inputWarnings, ...full.warnings];
4126
4333
  return {
@@ -4154,13 +4361,85 @@ async function readContextItem(command, params, inputWarnings, fits) {
4154
4361
  warnings.push(`--window=${params.window} is above the maximum of ${CONTEXT_MAX_WINDOW}, so this shows ±${CONTEXT_MAX_WINDOW} messages. For a wider slice run: ${wide}`);
4155
4362
  nextCommands.push({ command: wide, purpose: `Read ±${params.window} messages around the anchor.`, mutability: "read", requires_approval: false });
4156
4363
  }
4157
- return {
4158
- data,
4159
- options: {
4160
- ...warnings.length > 0 ? { warnings } : {},
4161
- ...nextCommands.length > 0 ? { nextCommands } : {}
4364
+ const options = (extraWarnings = [], extraNext = []) => {
4365
+ const allWarnings = [...warnings, ...extraWarnings];
4366
+ const allNext = [...nextCommands, ...extraNext];
4367
+ return {
4368
+ ...allWarnings.length > 0 ? { warnings: allWarnings } : {},
4369
+ ...allNext.length > 0 ? { nextCommands: allNext } : {}
4370
+ };
4371
+ };
4372
+ if (fits && params.metadata_only !== true && !fits(data, options())) {
4373
+ const trimmed = trimContextWindow(params.conversation_id, ctxRecord, params.msg_index, (page, extraWarnings, extraNext) => fits(page, options(extraWarnings, extraNext)));
4374
+ if (trimmed)
4375
+ return { data: trimmed.data, options: options(trimmed.warnings, trimmed.nextCommands) };
4376
+ }
4377
+ return { data, options: options() };
4378
+ }
4379
+ function trimContextWindow(conversationId, record, requestedAnchor, fits) {
4380
+ const ctx = asRecord(record.context);
4381
+ const messages = Array.isArray(ctx?.messages) ? ctx.messages : [];
4382
+ if (!ctx || messages.length <= 1)
4383
+ return;
4384
+ const firstIndex = asNumber(ctx.from_index) ?? asNumber(asRecord(messages[0])?.index) ?? 0;
4385
+ const indexed = messages.map((message, position) => ({
4386
+ message,
4387
+ index: asNumber(asRecord(message)?.index) ?? firstIndex + position
4388
+ }));
4389
+ const center = asNumber(ctx.center_index) ?? requestedAnchor ?? indexed[Math.floor(indexed.length / 2)].index;
4390
+ const requestedFrom = Math.min(asNumber(ctx.from_index) ?? indexed[0].index, indexed[0].index);
4391
+ const requestedTo = Math.max(asNumber(ctx.to_index) ?? indexed[indexed.length - 1].index, indexed[indexed.length - 1].index);
4392
+ const order = [...indexed].sort((left, right) => Math.abs(left.index - center) - Math.abs(right.index - center) || right.index - left.index);
4393
+ const target = shellArg(contextTarget(conversationId));
4394
+ const build = (kept) => {
4395
+ const returned = order.slice(0, kept).sort((left, right) => left.index - right.index);
4396
+ const from = returned[0].index;
4397
+ const to = returned[returned.length - 1].index;
4398
+ const before = from - requestedFrom;
4399
+ const after = requestedTo - to;
4400
+ const nextCommands = [];
4401
+ if (before > 0) {
4402
+ nextCommands.push({
4403
+ command: `moda traces get ${target} --all --from=${requestedFrom} --max-messages=${before}`,
4404
+ purpose: `Read the ${before} message(s) before ${from} that were left out to fit the agent byte limit.`,
4405
+ mutability: "read",
4406
+ requires_approval: false
4407
+ });
4408
+ }
4409
+ if (after > 0) {
4410
+ nextCommands.push({
4411
+ command: `moda traces get ${target} --all --from=${to + 1} --max-messages=${after}`,
4412
+ purpose: `Read the ${after} message(s) after ${to} that were left out to fit the agent byte limit.`,
4413
+ mutability: "read",
4414
+ requires_approval: false
4415
+ });
4162
4416
  }
4417
+ const warnings = kept < order.length ? [
4418
+ `Window cut to messages ${from}-${to} around anchor ${center} to stay under the agent output byte limit (--json has no limit); requested ${requestedFrom}-${requestedTo}. Read the rest with: ${nextCommands.map((next) => next.command).join(" and ")}.`
4419
+ ] : [];
4420
+ const data = {
4421
+ ...record,
4422
+ context: {
4423
+ ...ctx,
4424
+ from_index: from,
4425
+ to_index: to,
4426
+ messages: returned.map((entry) => entry.message),
4427
+ ...kept < order.length ? { requested_from_index: requestedFrom, requested_to_index: requestedTo, omitted_for_byte_limit: order.length - kept } : {}
4428
+ }
4429
+ };
4430
+ return { data, warnings, nextCommands };
4163
4431
  };
4432
+ let low = 1;
4433
+ let high = order.length;
4434
+ while (low < high) {
4435
+ const mid = Math.ceil((low + high) / 2);
4436
+ const page = build(mid);
4437
+ if (fits(page.data, page.warnings, page.nextCommands))
4438
+ low = mid;
4439
+ else
4440
+ high = mid - 1;
4441
+ }
4442
+ return build(low);
4164
4443
  }
4165
4444
  var AUDIT_USAGE = "Usage: moda traces spans <conversation_id|trace_id> [--kind=auto|trace|conversation] [--include-raw] [--include-bodies] [--limit=N] [--cursor=<next_cursor>]";
4166
4445
  var AUDIT_BATCH_USAGE = `Usage: moda traces spans <id> <id> ... | --ids-from=-|<file> [--kind=auto|trace|conversation] [--include-raw] [--include-bodies] [--limit=N] (max ${BATCH_MAX_TARGETS} targets)`;
@@ -4176,16 +4455,31 @@ async function readAuditItem(command, params, budget) {
4176
4455
  query2.set("limit", params.limit.toString());
4177
4456
  if (params.cursor)
4178
4457
  query2.set("cursor", params.cursor);
4179
- let maxBytes = budget?.maxBytes;
4180
- let page = auditOutput(command, params, await fetchAudit(params.id, query2, maxBytes));
4181
- while (budget && maxBytes !== undefined && maxBytes > AUDIT_MIN_PAGE_BYTES && !budget.fits(page.data, page.options)) {
4182
- maxBytes = Math.max(AUDIT_MIN_PAGE_BYTES, Math.floor(maxBytes / 2));
4183
- page = auditOutput(command, params, await fetchAudit(params.id, query2, maxBytes));
4458
+ const pageBytes = params.page_bytes ?? (budget ? auditMaxBytes(budget.maxBytes) : undefined);
4459
+ const raw = await fetchAudit(params.id, query2, pageBytes);
4460
+ params = { ...params, page_bytes: pageBytes };
4461
+ const whole = auditOutput(command, params, raw);
4462
+ if (!budget || budget.fits(whole.data, whole.options))
4463
+ return whole;
4464
+ const spans = asRecord(raw)?.spans;
4465
+ const available = (Array.isArray(spans) ? spans.length : 0) - (params.span_offset ?? 0);
4466
+ if (available <= 0)
4467
+ return whole;
4468
+ let low = 1;
4469
+ let high = available;
4470
+ while (low < high) {
4471
+ const mid = Math.ceil((low + high) / 2);
4472
+ const page2 = auditOutput(command, params, raw, mid);
4473
+ if (budget.fits(page2.data, page2.options))
4474
+ low = mid;
4475
+ else
4476
+ high = mid - 1;
4184
4477
  }
4185
- if (budget && !budget.fits(page.data, page.options)) {
4478
+ const page = auditOutput(command, params, raw, low);
4479
+ if (low === 1 && !budget.fits(page.data, page.options)) {
4186
4480
  page.options.warnings = [
4187
4481
  ...page.options.warnings ?? [],
4188
- "This page is larger than the agent output budget even at the smallest page the server returns, so some of its spans are cut from this output. Rerun this command with --json (no byte limit) or a higher MODA_AGENT_MAX_OUTPUT_BYTES to see the whole row before following next_cursor."
4482
+ "One span of this page is larger than the agent output budget, so agent output shortens it. Rerun this command with --json (no byte limit) or a higher MODA_AGENT_MAX_OUTPUT_BYTES to see it whole."
4189
4483
  ];
4190
4484
  }
4191
4485
  return page;
@@ -4202,6 +4496,16 @@ function singleContextTarget(positional, flags) {
4202
4496
  }
4203
4497
  return [target.id, { ...flags, "msg-index": String(target.msgIndex) }];
4204
4498
  }
4499
+ async function withAnchorHint(target, read) {
4500
+ try {
4501
+ return await read();
4502
+ } catch (error) {
4503
+ if (!(error instanceof CliNotFoundError) || target.msgIndex === undefined || target.raw.startsWith("{"))
4504
+ throw error;
4505
+ const verbatim = JSON.stringify({ id: target.raw });
4506
+ throw new CliNotFoundError(error.entity, error.id, `${error.message} '${target.raw}' was read as trace "${target.id}" anchored at message ${target.msgIndex} (the @N suffix is a message index). If "${target.raw}" is the whole trace id, pass it as ${shellArg(verbatim)} or --conversation-id=${shellArg(target.raw)}.`, `moda traces get ${shellArg(verbatim)}`);
4507
+ }
4508
+ }
4205
4509
  function isBatchRead(positionals, flags) {
4206
4510
  return positionals.length > 1 || IDS_FROM_KEYS.some((key) => flags[key] !== undefined);
4207
4511
  }
@@ -4216,14 +4520,41 @@ function splitIdsFrom(flags) {
4216
4520
  }
4217
4521
  return { idsFrom, rest };
4218
4522
  }
4219
- function auditItemByteShare(count) {
4220
- return Math.max(AUDIT_MIN_PAGE_BYTES, Math.floor((agentMaxOutputBytes() - AUDIT_AGENT_RESERVE_BYTES) / count));
4221
- }
4222
4523
  var BATCH_ENVELOPE_RESERVE_BYTES = 4096;
4223
- var BATCH_ENVELOPE_RESERVE_PER_TARGET_BYTES = 256;
4224
- function contextItemByteShare(count) {
4225
- const reserve = BATCH_ENVELOPE_RESERVE_BYTES + BATCH_ENVELOPE_RESERVE_PER_TARGET_BYTES * count;
4226
- return Math.max(1, Math.floor((agentMaxOutputBytes() - reserve) / count));
4524
+ function batchTargetReserveBytes(target) {
4525
+ return 3 * (jsonByteLength(target.raw) + 4) + 16;
4526
+ }
4527
+ var BATCH_MIN_ITEM_SHARE_BYTES = 4096;
4528
+ async function readBatchTargets(targets, agentMode, read) {
4529
+ if (!agentMode) {
4530
+ return mapWithConcurrency(targets, BATCH_CONCURRENCY, (target) => readBatchItem(target, () => read(target, undefined)));
4531
+ }
4532
+ const reserve = targets.reduce((sum, target) => sum + batchTargetReserveBytes(target), BATCH_ENVELOPE_RESERVE_BYTES);
4533
+ const available = Math.max(1, agentMaxOutputBytes() - reserve);
4534
+ const fair = Math.max(BATCH_MIN_ITEM_SHARE_BYTES, Math.floor(available / targets.length));
4535
+ let used = 0;
4536
+ let reserved = 0;
4537
+ const shares = new Map;
4538
+ return mapWithConcurrency(targets, BATCH_CONCURRENCY, async (target, index) => {
4539
+ const share = shares.get(index) ?? fair;
4540
+ try {
4541
+ const result = await readBatchItem(target, () => read(target, { fits: fitsShare(share), maxBytes: share }));
4542
+ used += jsonByteLength(result) + 1;
4543
+ return result;
4544
+ } finally {
4545
+ reserved -= share;
4546
+ }
4547
+ }, {
4548
+ shouldStart: (index) => {
4549
+ const free = available - used - reserved;
4550
+ if (index > 0 && free < BATCH_MIN_ITEM_SHARE_BYTES)
4551
+ return false;
4552
+ const share = Math.max(1, Math.min(fair, free));
4553
+ shares.set(index, share);
4554
+ reserved += share;
4555
+ return true;
4556
+ }
4557
+ });
4227
4558
  }
4228
4559
  function fitsShare(share) {
4229
4560
  return (data, options) => Buffer.byteLength(JSON.stringify({ data, ...options }) ?? "", "utf8") <= share;
@@ -4335,27 +4666,25 @@ async function runContextBatch(command, positionals, flags, context) {
4335
4666
  throw new CliInputError(`${base.all === true ? "--all reads whole traces" : "--metadata-only reads no messages"}, so targets cannot carry an @msg_index anchor; drop the flag or the anchors.`, CONTEXT_BATCH_USAGE, "flag_conflict");
4336
4667
  }
4337
4668
  const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
4338
- const share = agentMode ? contextItemByteShare(targets.length) : undefined;
4339
- const results = await mapWithConcurrency(targets, BATCH_CONCURRENCY, (target) => readBatchItem(target, () => {
4669
+ const results = await readBatchTargets(targets, agentMode, (target, budget) => {
4340
4670
  const params = {
4341
4671
  ...base,
4342
4672
  conversation_id: target.id,
4343
4673
  ...target.msgIndex !== undefined ? { msg_index: target.msgIndex } : {}
4344
4674
  };
4345
- return readContextItem(command, params, [], share !== undefined ? fitsShare(share) : undefined);
4346
- }));
4675
+ return withAnchorHint(target, () => readContextItem(command, params, [], budget));
4676
+ });
4347
4677
  return writeBatchResult(command, context, targets, results, inputWarnings, rest);
4348
4678
  }
4349
4679
  async function runAuditBatch(command, positionals, flags, context) {
4350
4680
  const { idsFrom, rest } = splitIdsFrom(flags);
4351
- if (rest.cursor !== undefined) {
4352
- throw new CliInputError("--cursor continues one trace, so it cannot be used with several ids; follow each item's next_commands instead.", AUDIT_BATCH_USAGE, "flag_conflict");
4681
+ if (rest.cursor !== undefined || Object.keys(rest).some((key) => key.replace(/_/g, "-") === "span-offset")) {
4682
+ throw new CliInputError(`${rest.cursor !== undefined ? "--cursor" : "--span-offset"} continues one trace, so it cannot be used with several ids; follow each item's next_commands instead.`, AUDIT_BATCH_USAGE, "flag_conflict");
4353
4683
  }
4354
4684
  const targets = await collectBatchTargets(positionals, idsFrom, { anchors: false, usage: AUDIT_BATCH_USAGE, command });
4355
4685
  const base = parseCommandFlags(AuditSchema, flagsToArgs(rest, "id", targets[0].id), command, { positionalKey: "id" });
4356
4686
  const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
4357
- const share = agentMode ? auditItemByteShare(targets.length) : undefined;
4358
- const results = await mapWithConcurrency(targets, BATCH_CONCURRENCY, (target) => readBatchItem(target, () => readAuditItem(command, { ...base, id: target.id }, share !== undefined ? { maxBytes: Math.max(AUDIT_MIN_PAGE_BYTES, share), fits: fitsShare(share) } : undefined)));
4687
+ const results = await readBatchTargets(targets, agentMode, (target, budget) => readAuditItem(command, { ...base, id: target.id }, budget));
4359
4688
  return writeBatchResult(command, context, targets, results, [], rest);
4360
4689
  }
4361
4690
  async function runCommand(command, positional, flags, positionals = positional ? [positional] : [], context = createCommandContext({ command, positional, positionals, flags })) {
@@ -4378,6 +4707,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
4378
4707
  const args = flagsToArgs(flags);
4379
4708
  const params = parseSpecFlags("clusters", args, command);
4380
4709
  if (params.search !== undefined || params.node_id !== undefined) {
4710
+ rejectInapplicableFlags(command, params, ["parent_id", "time_range"], "to a cluster search (--search/--node-id); they page the cluster hierarchy");
4381
4711
  const query3 = new URLSearchParams;
4382
4712
  if (params.search !== undefined)
4383
4713
  query3.set("q", params.search);
@@ -4394,6 +4724,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
4394
4724
  context.output.writeData(data2, warnings.length > 0 ? { warnings } : undefined);
4395
4725
  break;
4396
4726
  }
4727
+ rejectInapplicableFlags(command, params, ["mode", "limit"], "without --search or --node-id; they shape a cluster search");
4397
4728
  const query2 = new URLSearchParams;
4398
4729
  if (params.parent_id)
4399
4730
  query2.set("parent_id", params.parent_id);
@@ -4425,7 +4756,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
4425
4756
  }
4426
4757
  case "traces":
4427
4758
  case "conversations": {
4428
- const args = withStringIdFlags(flagsToArgs(flags), flags);
4759
+ const args = flagsToArgs(flags);
4429
4760
  const params = parseSpecFlags("traces", args, command);
4430
4761
  const query2 = new URLSearchParams;
4431
4762
  if (params.search)
@@ -4476,7 +4807,8 @@ async function runCommand(command, positional, flags, positionals = positional ?
4476
4807
  if (params.include_world_state === true) {
4477
4808
  const enriched = asRecord(data) ?? {};
4478
4809
  const list = Array.isArray(enriched.conversations) ? enriched.conversations : [];
4479
- const withState = await attachWorldState(list);
4810
+ const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
4811
+ const withState = await attachWorldState(list, agentMode ? agentMaxOutputBytes() : undefined);
4480
4812
  context.output.writeData({ ...enriched, conversations: withState }, writeOptions);
4481
4813
  } else {
4482
4814
  context.output.writeData(data, writeOptions);
@@ -4484,10 +4816,11 @@ async function runCommand(command, positional, flags, positionals = positional ?
4484
4816
  break;
4485
4817
  }
4486
4818
  case "search": {
4487
- if (!positional || positional.trim() === "") {
4819
+ const queryText = positionals.join(" ");
4820
+ if (queryText.trim() === "") {
4488
4821
  throw new CliInputError("<query> is required", 'Usage: moda traces search "<query>" [--mode=keyword|semantic|hybrid] [--user-id=ID] [--conversation-id=ID] [--time-range=RANGE] [--since=DATE] [--until=DATE] [--include-tool-io] [--limit=N]');
4489
4822
  }
4490
- const args = withStringIdFlags(flagsToArgs(flags, "q", positional), flags);
4823
+ const args = flagsToArgs(flags, "q", queryText);
4491
4824
  const params = parseSpecFlags("search", args, command);
4492
4825
  const query2 = new URLSearchParams;
4493
4826
  query2.set("q", params.q);
@@ -4524,15 +4857,42 @@ async function runCommand(command, positional, flags, positionals = positional ?
4524
4857
  break;
4525
4858
  }
4526
4859
  if (fallback?.kind === "results") {
4527
- warnings.push(fallback.warning);
4528
- const fallbackResults = fallback.data.results;
4529
- const merged = {
4530
- ...searchRecord,
4531
- ...fallback.data,
4532
- pagination: { ...asRecord(searchRecord.pagination) ?? {}, returned: fallbackResults.length, has_more: fallback.hasMore }
4860
+ const write = (kept2) => {
4861
+ const page2 = fallback.page(kept2);
4862
+ const merged = {
4863
+ ...searchRecord,
4864
+ ...page2.data,
4865
+ pagination: {
4866
+ ...asRecord(searchRecord.pagination) ?? {},
4867
+ returned: page2.returned,
4868
+ has_more: page2.hasMore,
4869
+ offset: page2.offset,
4870
+ ...page2.returned < fallback.rows ? { omitted_for_byte_limit: fallback.rows - page2.returned } : {}
4871
+ }
4872
+ };
4873
+ return { data: merged, options: { warnings: [...warnings, page2.warning], nextCommands: page2.nextCommands } };
4874
+ };
4875
+ const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
4876
+ let kept = fallback.rows;
4877
+ const fitsAgent = (count) => {
4878
+ const page2 = write(count);
4879
+ return agentEnvelopeFits(command, page2.data, page2.options);
4533
4880
  };
4534
- recordSearchTelemetry(context, merged);
4535
- context.output.writeData(merged, { warnings, nextCommands: fallback.nextCommands });
4881
+ if (agentMode && kept > 1 && !fitsAgent(kept)) {
4882
+ let low = 1;
4883
+ let high = kept - 1;
4884
+ while (low < high) {
4885
+ const mid = Math.ceil((low + high) / 2);
4886
+ if (fitsAgent(mid))
4887
+ low = mid;
4888
+ else
4889
+ high = mid - 1;
4890
+ }
4891
+ kept = low;
4892
+ }
4893
+ const page = write(kept);
4894
+ recordSearchTelemetry(context, page.data);
4895
+ context.output.writeData(page.data, page.options);
4536
4896
  break;
4537
4897
  }
4538
4898
  }
@@ -4590,7 +4950,8 @@ async function runCommand(command, positional, flags, positionals = positional ?
4590
4950
  return runContextBatch(command, positionals, flags, context);
4591
4951
  const { params, warnings: inputWarnings } = parseContextRequest(...singleContextTarget(positional, flags));
4592
4952
  const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
4593
- const item = await readContextItem(command, params, inputWarnings, agentMode ? (data, options) => agentEnvelopeFits(command, data, options) : undefined);
4953
+ const read = () => readContextItem(command, params, inputWarnings, agentMode ? { fits: (data, options) => agentEnvelopeFits(command, data, options), maxBytes: agentMaxOutputBytes() } : undefined);
4954
+ const item = positional === undefined ? await read() : await withAnchorHint(parseBatchTarget(positional, true, CONTEXT_USAGE), read);
4594
4955
  context.output.writeData(item.data, item.options);
4595
4956
  break;
4596
4957
  }
@@ -4604,15 +4965,12 @@ async function runCommand(command, positional, flags, positionals = positional ?
4604
4965
  const args = flagsToArgs(flags, "id", positional);
4605
4966
  const params = parseSpecFlags("audit", args, command);
4606
4967
  const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
4607
- const page = await readAuditItem(command, params, agentMode ? {
4608
- maxBytes: Math.max(AUDIT_MIN_PAGE_BYTES, agentMaxOutputBytes() - AUDIT_AGENT_RESERVE_BYTES),
4609
- fits: (data, options) => agentEnvelopeFits(command, data, options)
4610
- } : undefined);
4968
+ const page = await readAuditItem(command, params, agentMode ? { maxBytes: agentMaxOutputBytes(), fits: (data, options) => agentEnvelopeFits(command, data, options) } : undefined);
4611
4969
  context.output.writeData(page.data, page.options);
4612
4970
  break;
4613
4971
  }
4614
4972
  case "frustrations": {
4615
- const args = withStringIdFlags(flagsToArgs(flags), flags);
4973
+ const args = flagsToArgs(flags);
4616
4974
  const params = parseSpecFlags("frustrations", args, command);
4617
4975
  const query2 = new URLSearchParams;
4618
4976
  if (params.days_back)
@@ -4775,6 +5133,11 @@ async function runCommand(command, positional, flags, positionals = positional ?
4775
5133
  if (view !== undefined && params.full === true) {
4776
5134
  throw new CliInputError("--full applies to the dossier only; the --evidence/--reports/--traces/--feedback pages are always whole.", "Usage: moda problems get <problem_id> [--full] | moda problems get <problem_id> --evidence|--reports|--traces|--feedback");
4777
5135
  }
5136
+ if (view === undefined) {
5137
+ rejectInapplicableFlags(command, params, ["limit", "cursor", "family", "door"], "to the dossier; they page --evidence/--reports/--traces/--feedback");
5138
+ } else if (view !== "conversations") {
5139
+ rejectInapplicableFlags(command, params, ["family", "door"], `to --${view}; they filter --traces`);
5140
+ }
4778
5141
  if (view === undefined) {
4779
5142
  const data2 = await callDataAPI(`/problems/${encodeURIComponent(params.id)}`);
4780
5143
  const record = asRecord(data2) ?? {};
@@ -4844,15 +5207,21 @@ async function runCommand(command, positional, flags, positionals = positional ?
4844
5207
  throw new CliInputError("--attribution-id must be a UUID.");
4845
5208
  }
4846
5209
  }
4847
- const data = await callDataAPI(`/problems/${params.id}/feedback`, {
5210
+ const feedbackPath = `/problems/${params.id}/feedback`;
5211
+ const feedbackBody = {
5212
+ action: params.action,
5213
+ ...reason !== "" ? { reason } : {},
5214
+ ...params.attribution_id ? { attribution_id: params.attribution_id } : {},
5215
+ ...params.new_name ? { new_display_name: params.new_name } : {},
5216
+ actor: params.actor ?? "moda-cli"
5217
+ };
5218
+ if (context.dryRun) {
5219
+ writeDryRun(context, { plane: "data", method: "POST", path: feedbackPath, body: feedbackBody });
5220
+ break;
5221
+ }
5222
+ const data = await callDataAPI(feedbackPath, {
4848
5223
  method: "POST",
4849
- body: JSON.stringify({
4850
- action: params.action,
4851
- ...reason !== "" ? { reason } : {},
4852
- ...params.attribution_id ? { attribution_id: params.attribution_id } : {},
4853
- ...params.new_name ? { new_display_name: params.new_name } : {},
4854
- actor: params.actor ?? "moda-cli"
4855
- }),
5224
+ body: JSON.stringify(feedbackBody),
4856
5225
  headers: { "Content-Type": "application/json" }
4857
5226
  }, { retries: 0 });
4858
5227
  context.output.writeData(data);
@@ -4903,13 +5272,19 @@ async function runCommand(command, positional, flags, positionals = positional ?
4903
5272
  }
4904
5273
  const title = params.title?.trim() ?? "";
4905
5274
  const teamId = params.team_id?.trim() ?? "";
4906
- const data = await callDataAPI(`/problems/${params.id}/linear`, {
5275
+ const linearPath = `/problems/${params.id}/linear`;
5276
+ const linearBody = issue !== "" ? { issue } : {
5277
+ ...teamId !== "" ? { team_id: teamId } : {},
5278
+ ...title !== "" ? { title } : {},
5279
+ ...params.priority !== undefined ? { priority: params.priority } : {}
5280
+ };
5281
+ if (context.dryRun) {
5282
+ writeDryRun(context, { plane: "data", method: "POST", path: linearPath, body: linearBody });
5283
+ break;
5284
+ }
5285
+ const data = await callDataAPI(linearPath, {
4907
5286
  method: "POST",
4908
- body: JSON.stringify(issue !== "" ? { issue } : {
4909
- ...teamId !== "" ? { team_id: teamId } : {},
4910
- ...title !== "" ? { title } : {},
4911
- ...params.priority !== undefined ? { priority: params.priority } : {}
4912
- }),
5287
+ body: JSON.stringify(linearBody),
4913
5288
  headers: { "Content-Type": "application/json" }
4914
5289
  }, { retries: 0 }).catch((error) => {
4915
5290
  throw linearApiError(error);
@@ -4941,6 +5316,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
4941
5316
  const params = parseSpecFlags("signal", args, command);
4942
5317
  const id = encodeURIComponent(params.id);
4943
5318
  if (params.detections !== true) {
5319
+ rejectInapplicableFlags(command, params, ["days_back", "limit", "offset"], "to the signal summary; they page its hits (--detections)");
4944
5320
  const data2 = await callDataAPI(`/custom-signals/${id}`);
4945
5321
  context.output.writeData(data2);
4946
5322
  break;
@@ -4979,15 +5355,21 @@ async function runCommand(command, positional, flags, positionals = positional ?
4979
5355
  ...flags["msg-dedup-token"] !== undefined ? { msg_dedup_token: flags["msg-dedup-token"] } : {}
4980
5356
  });
4981
5357
  const note = params.note?.trim() ?? "";
4982
- const data = await callDataAPI(`/custom-signals/${encodeURIComponent(params.id)}/labels`, {
5358
+ const labelPath = `/custom-signals/${encodeURIComponent(params.id)}/labels`;
5359
+ const labelBody = {
5360
+ conversation_id: params.conversation_id,
5361
+ label: params.label,
5362
+ ...note !== "" ? { note } : {},
5363
+ ...params.msg_index !== undefined ? { msg_index: params.msg_index } : {},
5364
+ ...params.msg_dedup_token ? { msg_dedup_token: params.msg_dedup_token } : {}
5365
+ };
5366
+ if (context.dryRun) {
5367
+ writeDryRun(context, { plane: "data", method: "POST", path: labelPath, body: labelBody });
5368
+ break;
5369
+ }
5370
+ const data = await callDataAPI(labelPath, {
4983
5371
  method: "POST",
4984
- body: JSON.stringify({
4985
- conversation_id: params.conversation_id,
4986
- label: params.label,
4987
- ...note !== "" ? { note } : {},
4988
- ...params.msg_index !== undefined ? { msg_index: params.msg_index } : {},
4989
- ...params.msg_dedup_token ? { msg_dedup_token: params.msg_dedup_token } : {}
4990
- }),
5372
+ body: JSON.stringify(labelBody),
4991
5373
  headers: { "Content-Type": "application/json" }
4992
5374
  }, { retries: 0 });
4993
5375
  context.output.writeData(data);
@@ -5019,15 +5401,20 @@ async function runCommand(command, positional, flags, positionals = positional ?
5019
5401
  ...flags.note !== undefined ? { note: flags.note } : {}
5020
5402
  });
5021
5403
  const note = params.verdict === "clear" ? "" : params.note?.trim() ?? "";
5022
- const data = await callDataAPI(`/problems/${params.id}/detection-reviews`, {
5404
+ const reviewPath = `/problems/${params.id}/detection-reviews`;
5405
+ const reviewBody = {
5406
+ conversation_id: params.conversation_id,
5407
+ attribution_id: params.attribution_id,
5408
+ verdict: params.verdict,
5409
+ ...note !== "" ? { note } : {}
5410
+ };
5411
+ if (context.dryRun) {
5412
+ writeDryRun(context, { plane: "data", method: "POST", path: reviewPath, body: reviewBody, generatedFields: ["request_nonce"] });
5413
+ break;
5414
+ }
5415
+ const data = await callDataAPI(reviewPath, {
5023
5416
  method: "POST",
5024
- body: JSON.stringify({
5025
- conversation_id: params.conversation_id,
5026
- attribution_id: params.attribution_id,
5027
- verdict: params.verdict,
5028
- ...note !== "" ? { note } : {},
5029
- request_nonce: randomUUID2()
5030
- }),
5417
+ body: JSON.stringify({ ...reviewBody, request_nonce: randomUUID2() }),
5031
5418
  headers: { "Content-Type": "application/json" }
5032
5419
  });
5033
5420
  context.output.writeData(data);
@@ -5054,50 +5441,77 @@ async function runCommand(command, positional, flags, positionals = positional ?
5054
5441
  ...flags["msg-dedup-token"] !== undefined ? { msg_dedup_token: flags["msg-dedup-token"] } : {}
5055
5442
  });
5056
5443
  const reason = params.reason?.trim() ?? "";
5057
- const post = (path, body, retries) => callDataAPI(path, { method: "POST", body: JSON.stringify(body), headers: { "Content-Type": "application/json" } }, retries === undefined ? undefined : { retries });
5058
- let data;
5444
+ let request;
5059
5445
  switch (params.kind) {
5060
5446
  case "problem":
5061
- data = await post(`/problems/${params.target}/feedback`, { action: "dismiss", reason, actor: "moda-cli" }, 0);
5447
+ request = { method: "POST", path: `/problems/${params.target}/feedback`, body: { action: "dismiss", reason, actor: "moda-cli" }, retries: 0 };
5062
5448
  break;
5063
5449
  case "problem-detection":
5064
- data = await post(`/problems/${params.target}/detection-reviews`, {
5065
- conversation_id: params.conversation_id,
5066
- attribution_id: params.attribution_id,
5067
- verdict: params.undo ? "clear" : "false_positive",
5068
- ...params.undo ? {} : { note: reason },
5069
- request_nonce: randomUUID2()
5070
- });
5450
+ request = {
5451
+ method: "POST",
5452
+ path: `/problems/${params.target}/detection-reviews`,
5453
+ body: {
5454
+ conversation_id: params.conversation_id,
5455
+ attribution_id: params.attribution_id,
5456
+ verdict: params.undo ? "clear" : "false_positive",
5457
+ ...params.undo ? {} : { note: reason }
5458
+ },
5459
+ nonce: true
5460
+ };
5071
5461
  break;
5072
5462
  case "custom-signal":
5073
- if (params.undo) {
5074
- data = await callDataAPI(`/custom-signals/${encodeURIComponent(params.target)}/labels/${encodeURIComponent(params.conversation_id ?? "")}`, { method: "DELETE" }, { retries: 0 });
5075
- break;
5076
- }
5077
- data = await post(`/custom-signals/${encodeURIComponent(params.target)}/labels`, {
5078
- conversation_id: params.conversation_id,
5079
- label: "does_not_fit",
5080
- note: reason,
5081
- ...params.msg_index !== undefined ? { msg_index: params.msg_index } : {},
5082
- ...params.msg_dedup_token ? { msg_dedup_token: params.msg_dedup_token } : {}
5083
- }, 0);
5463
+ request = params.undo ? {
5464
+ method: "DELETE",
5465
+ path: `/custom-signals/${encodeURIComponent(params.target)}/labels/${encodeURIComponent(params.conversation_id ?? "")}`,
5466
+ retries: 0
5467
+ } : {
5468
+ method: "POST",
5469
+ path: `/custom-signals/${encodeURIComponent(params.target)}/labels`,
5470
+ body: {
5471
+ conversation_id: params.conversation_id,
5472
+ label: "does_not_fit",
5473
+ note: reason,
5474
+ ...params.msg_index !== undefined ? { msg_index: params.msg_index } : {},
5475
+ ...params.msg_dedup_token ? { msg_dedup_token: params.msg_dedup_token } : {}
5476
+ },
5477
+ retries: 0
5478
+ };
5084
5479
  break;
5085
5480
  default: {
5086
5481
  const apiKind = { quote: "frustration_quote", tool: "tool_call", laziness: "laziness", hallucination: "hallucination" }[params.kind];
5087
- data = await post("/false-positives", {
5088
- kind: apiKind,
5089
- verdict: params.undo ? "clear" : "false_positive",
5090
- ...params.undo ? {} : { note: reason },
5091
- conversation_id: params.target,
5092
- ...params.kind === "tool" ? {
5093
- tool_name: params.tool,
5094
- ...params.tool_use_id ? { tool_use_id: params.tool_use_id } : { msg_index: params.msg_index }
5095
- } : {},
5096
- ...params.kind === "quote" ? { family: params.family, turn: params.turn, quote: params.quote } : {},
5097
- request_nonce: randomUUID2()
5098
- });
5482
+ request = {
5483
+ method: "POST",
5484
+ path: "/false-positives",
5485
+ body: {
5486
+ kind: apiKind,
5487
+ verdict: params.undo ? "clear" : "false_positive",
5488
+ ...params.undo ? {} : { note: reason },
5489
+ conversation_id: params.target,
5490
+ ...params.kind === "tool" ? {
5491
+ tool_name: params.tool,
5492
+ ...params.tool_use_id ? { tool_use_id: params.tool_use_id } : { msg_index: params.msg_index }
5493
+ } : {},
5494
+ ...params.kind === "quote" ? { family: params.family, turn: params.turn, quote: params.quote } : {}
5495
+ },
5496
+ nonce: true
5497
+ };
5099
5498
  }
5100
5499
  }
5500
+ if (context.dryRun) {
5501
+ writeDryRun(context, {
5502
+ plane: "data",
5503
+ method: request.method,
5504
+ path: request.path,
5505
+ ...request.body ? { body: request.body } : {},
5506
+ ...request.nonce ? { generatedFields: ["request_nonce"] } : {}
5507
+ });
5508
+ break;
5509
+ }
5510
+ const sentBody = request.body && request.nonce ? { ...request.body, request_nonce: randomUUID2() } : request.body;
5511
+ const data = await callDataAPI(request.path, {
5512
+ method: request.method,
5513
+ ...sentBody ? { body: JSON.stringify(sentBody), headers: { "Content-Type": "application/json" } } : {}
5514
+ }, request.retries === undefined ? undefined : { retries: request.retries });
5101
5515
  context.output.writeData(data);
5102
5516
  break;
5103
5517
  }
@@ -5125,12 +5539,13 @@ async function runCommand(command, positional, flags, positionals = positional ?
5125
5539
  context.output.writeData(data2);
5126
5540
  break;
5127
5541
  }
5542
+ rejectInapplicableFlags(command, { kind: flags.kind, limit: flags.limit, all: flags.all === "false" ? undefined : flags.all }, ["kind", "limit", "all"], "to one trace's marks (every mark on the trace is listed); they filter the workspace list");
5128
5543
  const data = await callDataAPI(`/conversations/${encodeURIComponent(positional)}/false-positives`);
5129
5544
  context.output.writeData(data);
5130
5545
  break;
5131
5546
  }
5132
5547
  case "emotions": {
5133
- const args = withStringIdFlags(flagsToArgs(flags), flags);
5548
+ const args = flagsToArgs(flags);
5134
5549
  const params = parseSpecFlags("emotions", args, command);
5135
5550
  const query2 = new URLSearchParams;
5136
5551
  if (params.days_back)
@@ -5158,9 +5573,6 @@ async function runCommand(command, positional, flags, positionals = positional ?
5158
5573
  }
5159
5574
  case "laziness": {
5160
5575
  const args = flagsToArgs(flags);
5161
- if (flags["conversation-id"] !== undefined) {
5162
- args.conversation_id = flags["conversation-id"];
5163
- }
5164
5576
  const params = parseSpecFlags("laziness", args, command);
5165
5577
  const query2 = new URLSearchParams;
5166
5578
  if (params.days_back)
@@ -5180,9 +5592,6 @@ async function runCommand(command, positional, flags, positionals = positional ?
5180
5592
  }
5181
5593
  case "hallucinations": {
5182
5594
  const args = flagsToArgs(flags);
5183
- if (flags["conversation-id"] !== undefined) {
5184
- args.conversation_id = flags["conversation-id"];
5185
- }
5186
5595
  const params = parseSpecFlags("hallucinations", args, command);
5187
5596
  const query2 = new URLSearchParams;
5188
5597
  if (params.days_back)
@@ -5223,17 +5632,22 @@ async function runCommand(command, positional, flags, positionals = positional ?
5223
5632
  }
5224
5633
  case "feedback": {
5225
5634
  const { category, severity, note, refs, warnings } = buildFeedbackRequest(positionals, flags, context.bareFlags);
5635
+ const feedbackBody = {
5636
+ category,
5637
+ severity,
5638
+ source: "cli",
5639
+ agent: detectCallerAgent(process.env),
5640
+ cli_version: VERSION,
5641
+ note,
5642
+ ...Object.keys(refs).length > 0 ? { refs } : {}
5643
+ };
5644
+ if (context.dryRun) {
5645
+ writeDryRun(context, { plane: "data", method: "POST", path: "/feedback", body: feedbackBody });
5646
+ break;
5647
+ }
5226
5648
  const data = await callDataAPI("/feedback", {
5227
5649
  method: "POST",
5228
- body: JSON.stringify({
5229
- category,
5230
- severity,
5231
- source: "cli",
5232
- agent: detectCallerAgent(process.env),
5233
- cli_version: VERSION,
5234
- note,
5235
- ...Object.keys(refs).length > 0 ? { refs } : {}
5236
- })
5650
+ body: JSON.stringify(feedbackBody)
5237
5651
  }, { retries: 0 });
5238
5652
  context.output.writeData({ submitted: true, category, severity, ...asRecord(data) ?? {} }, {
5239
5653
  summary: "Feedback submitted. Thank you for helping us improve!",
@@ -5382,7 +5796,7 @@ var commandRegistry = createCommandRegistry([
5382
5796
  resetApiRequestCountBeforeRun: true,
5383
5797
  telemetry: "result-and-error",
5384
5798
  handler: async (context) => {
5385
- const { runInit } = await import("./index-e1pf1qz2.js");
5799
+ const { runInit } = await import("./index-2kf3fnnb.js");
5386
5800
  if (context.outputMode === "agent-stream") {
5387
5801
  context.output.writeEvent({
5388
5802
  event: "started",
@@ -5432,7 +5846,7 @@ var commandRegistry = createCommandRegistry([
5432
5846
  resetApiRequestCountBeforeRun: true,
5433
5847
  telemetry: "result-and-error",
5434
5848
  handler: async (context) => {
5435
- const { runProvision } = await import("./provision-q86phh4a.js");
5849
+ const { runProvision } = await import("./provision-gn8raqpt.js");
5436
5850
  return runProvision(context, {
5437
5851
  resumeCommand: buildResumeCommand("provision", context.rawFlags)
5438
5852
  });
@@ -6259,11 +6673,13 @@ var commandRegistry = createCommandRegistry([
6259
6673
  {
6260
6674
  name: "ab",
6261
6675
  description: "Run a judged prompt A/B replay experiment",
6262
- usage: "moda prompts ab --baseline=<path> --candidate=<path> [--traces=<ids>|--set-id=<id>|--auto-generate] [--yes]",
6676
+ usage: "moda prompts ab --baseline=<path> --candidate=<path> [--traces=<ids>|--problem=<problem_id> [--max-cases=<n>]|--set-id=<id>|--auto-generate] [--yes]",
6263
6677
  flags: [
6264
6678
  { name: "--baseline=<path>", description: "Prompt file to treat as the control." },
6265
6679
  { name: "--candidate=<path>", description: "Prompt file to treat as the variant." },
6266
6680
  { name: "--traces=<ids>", description: "Comma-separated trace ids (conversation_id values) to replay, at most 100. Each case uses Moda's distilled scenario and success criteria for the trace; a trace that cannot be distilled falls back to its opening user message with generic criteria, and the run warns. Legacy alias: --conversations=<ids>." },
6681
+ { name: "--problem=<problem_id>", description: "Replay the Problem's failing traces: one case per affected trace, starting at its evidence msg_index (like --traces=<id>@<n>), read from `moda problems get <id> --evidence`, then `--traces` if needed. Prints the traces it chose. Not combinable with --traces, --set-id, --auto-generate, or --cases." },
6682
+ { name: "--max-cases=<n>", description: "With --problem: replay at most this many traces (default 10, max 100)." },
6267
6683
  { name: "--cases=<n>", description: "Cases to auto-generate (default 5, max 100)." },
6268
6684
  { name: "--seeds=<n>", description: "Repeats per case per arm (default 3, max 10)." },
6269
6685
  { name: "--model=<slug>", description: "Assistant model for both arms; must be in the replay allowlist unless --allow-any-model." },
@@ -6271,7 +6687,8 @@ var commandRegistry = createCommandRegistry([
6271
6687
  { name: "--promote", description: "Make this run's replay set the tenant's primary replay set. Off by default, so an ad-hoc A/B never replaces it." }
6272
6688
  ],
6273
6689
  examples: [
6274
- "moda prompts ab --baseline=prompts/agent.prompt.md --candidate=prompts/agent-v2.prompt.md --traces=conv_1,conv_2"
6690
+ "moda prompts ab --baseline=prompts/agent.prompt.md --candidate=prompts/agent-v2.prompt.md --traces=conv_1,conv_2",
6691
+ "moda prompts ab --baseline=prompts/agent.prompt.md --candidate=prompts/agent-v2.prompt.md --problem=<problem_id>"
6275
6692
  ]
6276
6693
  },
6277
6694
  {
@@ -6451,6 +6868,9 @@ ${error.suggestions.map((s) => ` ${s}`).join(`
6451
6868
  `)}
6452
6869
  `);
6453
6870
  }
6871
+ } else if (error instanceof ApiError && error.statusCode === 404) {
6872
+ safeWriteStderr2(`${errorToAgentError(error).message}
6873
+ `);
6454
6874
  } else if (error instanceof ApiError) {
6455
6875
  safeWriteStderr2(`API error (HTTP ${error.statusCode}): ${error.responseBody || error.message}
6456
6876
  `);
@@ -6545,6 +6965,50 @@ function assertRoutedFlags(route, flags) {
6545
6965
  const args = Object.fromEntries(Object.keys(flags).filter((flag) => !aliases.has(flag.replace(/_/g, "-"))).map((flag) => [flag.replace(/-/g, "_"), true]));
6546
6966
  parseCommandFlags(shape.passthrough(), args, route.label, route.flagAliases ? { flagAliases: route.flagAliases } : {});
6547
6967
  }
6968
+ var EMPTY_VALUE_ALLOWED = {
6969
+ context: new Set(["msg-index", "message-index"])
6970
+ };
6971
+ var EXAMPLE_CLEARABLE_FLAGS = new Set(["note", "blurb"]);
6972
+ function assertFlagValues(handler, positionals, typedFlags, bareFlags, route) {
6973
+ const name = route?.handler ?? handler;
6974
+ const spec = flagSpecFor(name);
6975
+ if (spec?.idRefFlags)
6976
+ return;
6977
+ const bare = new Set(bareFlags ?? []);
6978
+ const empty = new Set(Object.keys(typedFlags).filter((key) => String(typedFlags[key]).trim() === ""));
6979
+ if (bare.size === 0 && empty.size === 0)
6980
+ return;
6981
+ const handlerPositionals = route?.positionals ?? positionals;
6982
+ const managementSubcommand = name === "signals" && SIGNALS_MANAGEMENT_SUBCOMMANDS.has(handlerPositionals[0] ?? "");
6983
+ const examplesUpdate = name === "signals" && handlerPositionals[0] === "examples" && handlerPositionals.includes("update");
6984
+ const valued = new Set;
6985
+ const switches = new Set(RAW_BOOLEAN_FLAGS);
6986
+ for (const entry of specsOf(name)) {
6987
+ for (const flag of describeSpecFlags(entry))
6988
+ (flag.type === "boolean" ? switches : valued).add(flag.name.slice(2));
6989
+ for (const positional of entry.positionals ?? [])
6990
+ if (positional.key)
6991
+ valued.add(positional.key.replace(/_/g, "-"));
6992
+ }
6993
+ for (const flag of route?.allowedFlags ?? [])
6994
+ if (!switches.has(flag))
6995
+ valued.add(flag);
6996
+ for (const [key, value] of Object.entries(typedFlags)) {
6997
+ const isBare = bare.has(key) && value === "true";
6998
+ if (!isBare && !empty.has(key))
6999
+ continue;
7000
+ if (!isBare && EMPTY_VALUE_ALLOWED[name]?.has(key))
7001
+ continue;
7002
+ if (!isBare && examplesUpdate && EXAMPLE_CLEARABLE_FLAGS.has(key))
7003
+ continue;
7004
+ const target = route?.flagAliases?.[key] ?? key;
7005
+ const takesValue = valued.has(key) || valued.has(target) || managementSubcommand && !switches.has(key) && !GLOBAL_FLAG_NAMES.has(key);
7006
+ if (!takesValue || switches.has(key))
7007
+ continue;
7008
+ const shown = canonicalCommandText(`moda ${route?.leaf.path ?? handler}`);
7009
+ throw new CliInputError(`--${key} needs a value, so nothing was sent.`, `Pass it as --${key}=VALUE (an empty shell variable leaves the flag without one). See '${shown} --help'.`, `missing_value:${key}`, { field: `--${key}`, suggestedCommands: [`${shown} --help`] });
7010
+ }
7011
+ }
6548
7012
  function renameErrorCommands(error, renames) {
6549
7013
  if (error instanceof CliInputError) {
6550
7014
  const details2 = error.details;
@@ -6562,6 +7026,7 @@ function renameErrorCommands(error, renames) {
6562
7026
  }
6563
7027
  async function dispatchParsedCommandInner(typedParsed, startedAt) {
6564
7028
  const { command, flags } = typedParsed;
7029
+ canonicalizeFlagKeys(flags, typedParsed.bareFlags);
6565
7030
  const switchError = normalizeBooleanFlags(flags);
6566
7031
  const typedFlags = stripUpdateCheckFlag(stripTelemetryFlag(flags));
6567
7032
  const telemetryDisabled = isTelemetryDisabled(flags);
@@ -6574,6 +7039,15 @@ async function dispatchParsedCommandInner(typedParsed, startedAt) {
6574
7039
  if (command && printTreeHelp(typedParsed))
6575
7040
  return 0;
6576
7041
  const helpTarget = command && command !== "--help" && command !== "-h" ? commandRegistry.find(command) : undefined;
7042
+ if (!helpTarget && command && command !== "--help" && command !== "-h") {
7043
+ const mode = resolveOutputMode(typedFlags, { isTTY: process.stdout.isTTY, env: process.env });
7044
+ const error = unknownCommandError(command);
7045
+ if (mode === "agent" || mode === "agent-stream" || mode === "json")
7046
+ printAgentCliError(command, error, mode);
7047
+ else
7048
+ printCliError(error);
7049
+ return 1;
7050
+ }
6577
7051
  if (helpTarget) {
6578
7052
  const subcommandName = typedParsed.positional;
6579
7053
  const subcommandEntry = subcommandName ? helpTarget.subcommands?.find((entry) => entry.name === subcommandName) : undefined;
@@ -6688,14 +7162,14 @@ async function dispatchParsedCommandInner(typedParsed, startedAt) {
6688
7162
  }
6689
7163
  if (switchError)
6690
7164
  return failEarly(switchError, definition, identity);
6691
- if (route) {
6692
- try {
7165
+ try {
7166
+ if (route)
6693
7167
  assertRoutedFlags(route, typedFlags);
6694
- } catch (error) {
6695
- if (!(error instanceof CliInputError))
6696
- throw error;
6697
- return failEarly(error, definition, identity);
6698
- }
7168
+ assertFlagValues(definition.name, parsed.positionals, typedFlags, typedParsed.bareFlags, route);
7169
+ } catch (error) {
7170
+ if (!(error instanceof CliInputError))
7171
+ throw error;
7172
+ return failEarly(error, definition, identity);
6699
7173
  }
6700
7174
  if (commandFlags.profile === "true" || commandFlags.profile === "") {
6701
7175
  return failEarly(new CliInputError("--profile needs a value, e.g. --profile=prod.", "List profiles with 'moda profiles list'.", "missing_value:profile"), definition, identity);
@@ -6854,6 +7328,7 @@ if (isMain) {
6854
7328
  }
6855
7329
  export {
6856
7330
  unknownCommandError,
7331
+ trimContextWindow,
6857
7332
  telemetrySubcommandNames,
6858
7333
  telemetryCommandName,
6859
7334
  stripLoneSurrogates,
@@ -6873,10 +7348,14 @@ export {
6873
7348
  dispatchParsedCommand,
6874
7349
  detectCallerAgent,
6875
7350
  describeLinearLink,
7351
+ commandSwitchFlags,
6876
7352
  commandRegistry,
7353
+ coerceNumericFlags,
6877
7354
  cliValidationReason,
6878
7355
  classifyCliError,
6879
7356
  capProblemsBlocks,
7357
+ canonicalizeFlagKeys,
7358
+ canonicalFlagKey,
6880
7359
  buildResumeCommand,
6881
7360
  auditCapStatus,
6882
7361
  TELEMETRY_EXEMPT_COMMANDS,