@moda-ai/cli 1.50.0 → 1.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -534,9 +534,11 @@ function writeSecretJsonFile(path, value) {
534
534
  // src/errors.ts
535
535
  class CliInputError extends Error {
536
536
  usage;
537
- constructor(message, usage) {
537
+ reason;
538
+ constructor(message, usage, reason) {
538
539
  super(message);
539
540
  this.usage = usage;
541
+ this.reason = reason;
540
542
  this.name = "CliInputError";
541
543
  }
542
544
  }
@@ -13,7 +13,7 @@ import {
13
13
  resolvePromptSource,
14
14
  safePromptEvidencePath,
15
15
  scanHarness
16
- } from "./cli-x0x0n35w.js";
16
+ } from "./cli-he2wb4hd.js";
17
17
  import {
18
18
  SKILL_SOURCE_HARNESS,
19
19
  SKILL_SOURCE_INTEGRATION_BUNDLED,
@@ -26,7 +26,7 @@ import {
26
26
  import {
27
27
  isAuthSessionValid,
28
28
  loadAuthSession
29
- } from "./cli-7kbzqact.js";
29
+ } from "./cli-kxwm7y62.js";
30
30
  import {
31
31
  CliInputError,
32
32
  EXIT_ERROR,
@@ -45,7 +45,7 @@ import {
45
45
  resolveTenantId,
46
46
  secretFilePathFromRef,
47
47
  validateConfig
48
- } from "./cli-xrs02c0t.js";
48
+ } from "./cli-ac3p2byq.js";
49
49
  import {
50
50
  __commonJS,
51
51
  __require,
@@ -6711,7 +6711,7 @@ async function runPromptSyncExclusive(flags, profileOptions, options) {
6711
6711
  const analyzedDefinitions = new Map;
6712
6712
  if (flags["no-analyze"] !== "true") {
6713
6713
  options.onProgress?.("Analyzing prompt sources before sync.");
6714
- const { analyzeHarnessPrompts } = await import("./harness-7gr0xbtc.js");
6714
+ const { analyzeHarnessPrompts } = await import("./harness-s13wvdxt.js");
6715
6715
  const result = await analyzeHarnessPrompts({
6716
6716
  rootDir: process.cwd(),
6717
6717
  existing: established.flatMap((prompt) => prompt.sourceDefinition ? [prompt.sourceDefinition.source] : []),
@@ -25,7 +25,7 @@ import {
25
25
  resolveTenantId,
26
26
  shouldInventoryFile,
27
27
  stringOption
28
- } from "./cli-xrs02c0t.js";
28
+ } from "./cli-ac3p2byq.js";
29
29
  import {
30
30
  __commonJS,
31
31
  __require,
@@ -184857,7 +184857,7 @@ async function runHarnessCommand(context) {
184857
184857
  throw new CliInputError("Focused remote prompt analysis is not supported yet.", "Use local --analyst=claude|codex|cursor|local-scan, or drop --concern for a full remote report.");
184858
184858
  }
184859
184859
  if (context.flags["github-actions"] === "true") {
184860
- const { runGithubActionsAnalyze } = await import("./harness-github-actions-q9kewzb6.js");
184860
+ const { runGithubActionsAnalyze } = await import("./harness-github-actions-gbty3k66.js");
184861
184861
  const result = await runGithubActionsAnalyze(rootDir, {
184862
184862
  writeReport: (report2) => {
184863
184863
  const normalized = normalizeHarnessReport(report2);
@@ -7,7 +7,7 @@ import {
7
7
  saveProfileConfig,
8
8
  secretFilePathFromRef,
9
9
  writeSecretJsonFile
10
- } from "./cli-xrs02c0t.js";
10
+ } from "./cli-ac3p2byq.js";
11
11
  import {
12
12
  __require
13
13
  } from "./cli-0v6na3yp.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  authFetch,
3
3
  getCachedLoginContext
4
- } from "./cli-7kbzqact.js";
4
+ } from "./cli-kxwm7y62.js";
5
5
  import {
6
6
  CliAccessError,
7
7
  CliInputError,
@@ -15,7 +15,7 @@ import {
15
15
  resolveTenantIdFromActiveProfile,
16
16
  saveCliConfig,
17
17
  writeSecretJsonFile
18
- } from "./cli-xrs02c0t.js";
18
+ } from "./cli-ac3p2byq.js";
19
19
 
20
20
  // src/init/tenant.ts
21
21
  import { randomUUID } from "node:crypto";
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  runPromptsCommand,
9
9
  runSkillsCommand,
10
10
  runStatusCommand
11
- } from "./cli-jqfm8dp4.js";
11
+ } from "./cli-ha40mgy1.js";
12
12
  import {
13
13
  ApiError,
14
14
  HARNESS_REPORT_APPROVAL_PATH,
@@ -37,7 +37,7 @@ import {
37
37
  terminalStyles,
38
38
  unknownSignalsSubcommandError,
39
39
  validateHarnessReport
40
- } from "./cli-x0x0n35w.js";
40
+ } from "./cli-he2wb4hd.js";
41
41
  import"./cli-ssfq84k5.js";
42
42
  import {
43
43
  authFetch,
@@ -46,7 +46,7 @@ import {
46
46
  loadAuthSession,
47
47
  login,
48
48
  revokeCliSessionToken
49
- } from "./cli-7kbzqact.js";
49
+ } from "./cli-kxwm7y62.js";
50
50
  import {
51
51
  AGENT_ERROR_CODES,
52
52
  CliAccessError,
@@ -97,7 +97,7 @@ import {
97
97
  validateConfig,
98
98
  wasOutputCancelled,
99
99
  writeSecretJsonFile
100
- } from "./cli-xrs02c0t.js";
100
+ } from "./cli-ac3p2byq.js";
101
101
  import {
102
102
  __require
103
103
  } from "./cli-0v6na3yp.js";
@@ -150,11 +150,20 @@ var WorldStateSchema = z.object({
150
150
  format: z.enum(["frames", "fold-inputs"]).optional(),
151
151
  message_count: z.number().min(1).max(1e6).optional()
152
152
  });
153
+ var lenientBoolFlag = () => z.preprocess((val) => {
154
+ const text = String(val).toLowerCase();
155
+ if (["true", "1", "yes", "on"].includes(text))
156
+ return true;
157
+ if (["false", "0", "no", "off"].includes(text))
158
+ return false;
159
+ return val;
160
+ }, z.boolean()).optional();
153
161
  var ContextSchema = z.object({
154
- conversation_id: z.string(),
155
- msg_index: z.number().min(0).optional(),
162
+ conversation_id: z.string().min(1),
163
+ msg_index: z.number().int().min(0).optional(),
156
164
  window: z.number().int().min(0).optional(),
157
- all: boolFlag(),
165
+ metadata_only: lenientBoolFlag(),
166
+ all: lenientBoolFlag(),
158
167
  from: z.number().int().min(0).optional(),
159
168
  max_messages: z.number().int().min(1).max(5000).optional()
160
169
  });
@@ -3534,6 +3543,11 @@ function stripTelemetryFlag(flags) {
3534
3543
  function isTelemetryDisabled(flags) {
3535
3544
  return flags[TELEMETRY_FLAG] === "true" || process.env.MODA_CLI_TELEMETRY === "0" || process.env.DO_NOT_TRACK === "1";
3536
3545
  }
3546
+ var VALIDATION_REASON_MAX = 128;
3547
+ function sanitizeValidationReason(reason) {
3548
+ const cleaned = reason?.replace(/[^A-Za-z0-9_.:-]/g, "_").slice(0, VALIDATION_REASON_MAX);
3549
+ return cleaned ? cleaned : undefined;
3550
+ }
3537
3551
  function sanitizedFlagNames(flags) {
3538
3552
  return Object.keys(stripTelemetryFlag(flags)).sort();
3539
3553
  }
@@ -3541,6 +3555,7 @@ async function sendCliUsageTelemetry(input) {
3541
3555
  if (!input.apiKey || isTelemetryDisabled(input.flags)) {
3542
3556
  return;
3543
3557
  }
3558
+ const validationReason = sanitizeValidationReason(input.validationReason);
3544
3559
  const controller = new AbortController;
3545
3560
  const timeout = setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS);
3546
3561
  if (typeof timeout.unref === "function") {
@@ -3558,6 +3573,7 @@ async function sendCliUsageTelemetry(input) {
3558
3573
  command: input.command.slice(0, 64),
3559
3574
  status: input.status,
3560
3575
  error_type: input.errorType,
3576
+ ...validationReason ? { validation_reason: validationReason } : {},
3561
3577
  exit_code: input.exitCode,
3562
3578
  duration_ms: Math.max(0, Math.round(input.durationMs)),
3563
3579
  api_request_count: Math.max(0, Math.round(input.apiRequestCount)),
@@ -6663,7 +6679,7 @@ function frustrationFindings(data, daysBack, evidenceRefs) {
6663
6679
  id: "action_frustration_window",
6664
6680
  title: "Read the frustration window.",
6665
6681
  rationale: "The user quote and nearby turns reveal what confused or blocked the user.",
6666
- command: conversationId ? `moda context ${conversationId}${anchorIndex !== null ? ` --msg-index=${anchorIndex}` : ""} --window=3` : `moda frustrations --days-back=${daysBack} --include-window`,
6682
+ command: conversationId ? `moda context ${shellWord(conversationId)}${anchorIndex !== null ? ` --msg-index=${anchorIndex}` : ""} --window=3` : `moda frustrations --days-back=${daysBack} --include-window`,
6667
6683
  mutability: "read",
6668
6684
  requiresApproval: false
6669
6685
  }]
@@ -7750,7 +7766,7 @@ function parseArgs(argv) {
7750
7766
  if (!endOfOptions && arg === "--" && command !== "") {
7751
7767
  endOfOptions = true;
7752
7768
  } else if (!endOfOptions && arg.startsWith("--")) {
7753
- i = parseFlag(args, i, flags, command === "", bareFlags);
7769
+ i = parseFlag(args, i, flags, command === "", bareFlags, command);
7754
7770
  } else if (!endOfOptions && command !== "" && /^-[A-Za-z]/.test(arg)) {
7755
7771
  shortFlags.push(arg);
7756
7772
  } else if (command === "") {
@@ -7790,7 +7806,10 @@ var GLOBAL_BOOLEAN_FLAGS = new Set([
7790
7806
  "tui",
7791
7807
  "no-tui"
7792
7808
  ]);
7793
- function parseFlag(args, index, flags, beforeCommand, bareFlags) {
7809
+ var COMMAND_BOOLEAN_FLAGS = {
7810
+ context: new Set(["all", "metadata-only"])
7811
+ };
7812
+ function parseFlag(args, index, flags, beforeCommand, bareFlags, command = "") {
7794
7813
  const set = (key2, value, bare) => {
7795
7814
  flags[key2] = value;
7796
7815
  const at = bareFlags.indexOf(key2);
@@ -7811,6 +7830,10 @@ function parseFlag(args, index, flags, beforeCommand, bareFlags) {
7811
7830
  set(key, "true", true);
7812
7831
  return index;
7813
7832
  }
7833
+ if (!beforeCommand && COMMAND_BOOLEAN_FLAGS[command]?.has(key.replace(/_/g, "-")) && next !== "true" && next !== "false") {
7834
+ set(key, "true", true);
7835
+ return index;
7836
+ }
7814
7837
  if (next !== undefined && !next.startsWith("--")) {
7815
7838
  set(key, next, false);
7816
7839
  return index + 1;
@@ -7903,7 +7926,7 @@ Commands:
7903
7926
  traces --sort=longest Sort traces: recent (default), longest, slowest
7904
7927
  search "<query>" Search trace messages (keyword/semantic/hybrid)
7905
7928
  world-state <conversation_id> Get a trace's world state (slots/threads/events)
7906
- context <conversation_id> Get windowed trace context
7929
+ context <conversation_id> Get windowed trace context (--metadata-only: summary + counts only)
7907
7930
  context <conversation_id> --all Whole transcript in order (first 500; --from=N --max-messages=N to continue)
7908
7931
  audit <conversation_id|trace_id> Raw span audit (spans, hierarchy, orphans, duplicates)
7909
7932
  frustrations Frustration detections (same data as emotions --family=frustration)
@@ -8736,7 +8759,7 @@ function parseCommandFlags(schema, args, command, options = {}) {
8736
8759
  ]);
8737
8760
  const unknown = Object.keys(args).filter((key) => !known.has(key));
8738
8761
  if (unknown.includes("dry_run")) {
8739
- 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, prompts sync/decide, skills sync/policy, signals <write subcommands>, problem-reopen.`);
8762
+ 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, prompts sync/decide, skills sync/policy, signals <write subcommands>, problem-reopen.`, "unsupported_dry_run");
8740
8763
  }
8741
8764
  if (unknown.length > 0) {
8742
8765
  const flagKeys = schemaKeys.filter((key) => key !== options.positionalKey);
@@ -8751,7 +8774,7 @@ function parseCommandFlags(schema, args, command, options = {}) {
8751
8774
  return close ? `Did you mean --${close.candidate.replace(/_/g, "-")}?` : "";
8752
8775
  }).filter(Boolean);
8753
8776
  const names = unknown.map((key) => `--${key.replace(/_/g, "-")}`).join(", ");
8754
- throw new CliInputError(`Unknown flag${unknown.length > 1 ? "s" : ""} ${names} for 'moda ${command}'.`, [...hints, `Valid flags: ${valid.join(", ") || "(none)"}. Run 'moda ${command} --help'.`].join(" "));
8777
+ throw new CliInputError(`Unknown flag${unknown.length > 1 ? "s" : ""} ${names} for 'moda ${command}'.`, [...hints, `Valid flags: ${valid.join(", ") || "(none)"}. Run 'moda ${command} --help'.`].join(" "), `unknown_flag:${unknown[0].replace(/_/g, "-")}`);
8755
8778
  }
8756
8779
  return schema.parse(args);
8757
8780
  }
@@ -8941,7 +8964,7 @@ function assertHandParsedFlags(command, flags, positionals) {
8941
8964
  } catch (error) {
8942
8965
  if (command !== "feedback" || !(error instanceof CliInputError))
8943
8966
  throw error;
8944
- throw new CliInputError(/nothing was sent/i.test(error.message) ? error.message : `${error.message} Nothing was sent.`, `${error.usage ?? ""} Id flags (--<name>-id=ID) are attached as refs; put other detail in the note. ${FEEDBACK_USAGE}`.trim());
8967
+ throw new CliInputError(/nothing was sent/i.test(error.message) ? error.message : `${error.message} Nothing was sent.`, `${error.usage ?? ""} Id flags (--<name>-id=ID) are attached as refs; put other detail in the note. ${FEEDBACK_USAGE}`.trim(), error.reason);
8945
8968
  }
8946
8969
  }
8947
8970
  var PROFILE_MANAGING_COMMANDS = new Set(["profiles", "auth", "init", "provision", "status", "config", "keys", "manifest", "agent", "doctor"]);
@@ -8975,6 +8998,66 @@ function processingDisabledWarning(data, family) {
8975
8998
  `${family === "emotion" ? "Emotion" : "Frustration"} analysis is disabled for this tenant (${setting} = false), ` + "so zero results do NOT mean users are not frustrated. Enable it in Settings → Account → Signal processing; " + "only traces ingested after enabling are analyzed."
8976
8999
  ];
8977
9000
  }
9001
+ var CONTEXT_USAGE = "Usage: moda context <conversation_id> [--msg-index=N] [--window=0-5 | --metadata-only]";
9002
+ var CONTEXT_ALL_USAGE = "Usage: moda context <conversation_id> --all [--from=N] [--max-messages=N]";
9003
+ var CONTEXT_MAX_WINDOW = 5;
9004
+ var EMPTY_ANCHOR_VALUES = new Set(["", "null", "undefined", "none"]);
9005
+ function parseContextRequest(positional, rawFlags) {
9006
+ const flags = {};
9007
+ const flagIds = [];
9008
+ for (const [rawKey, value] of Object.entries(rawFlags)) {
9009
+ const key = rawKey.replace(/_/g, "-");
9010
+ if (key === "conversation-id") {
9011
+ if (value !== "true")
9012
+ flagIds.push(value);
9013
+ } else if (flags[key] !== undefined && flags[key] !== value) {
9014
+ throw new CliInputError(`Got two different values for --${key} (both spellings were passed), so nothing was read.`, CONTEXT_USAGE, "flag_conflict");
9015
+ } else {
9016
+ flags[key] = value;
9017
+ }
9018
+ }
9019
+ const flagId = flagIds[0];
9020
+ const warnings = [];
9021
+ if (new Set([...positional !== undefined ? [positional] : [], ...flagIds]).size > 1) {
9022
+ throw new CliInputError("Got two different trace ids (from the positional and --conversation-id), so nothing was read.", CONTEXT_USAGE, "flag_conflict");
9023
+ }
9024
+ const conversationId = positional ?? flagId;
9025
+ if (!conversationId) {
9026
+ throw new CliInputError("<conversation_id> is required", CONTEXT_USAGE, "missing_positional");
9027
+ }
9028
+ const msgIndex = flags["msg-index"];
9029
+ if (msgIndex !== undefined && EMPTY_ANCHOR_VALUES.has(msgIndex.trim().toLowerCase())) {
9030
+ delete flags["msg-index"];
9031
+ warnings.push(`--msg-index had no value (${JSON.stringify(msgIndex)}), so the window is centered on the middle of the trace. ` + "Trace-level search hits have no message_index; pass one only when the hit has it.");
9032
+ }
9033
+ const args = { ...flagsToArgs(flags), conversation_id: conversationId };
9034
+ const params = parseCommandFlags(ContextSchema, args, "context", { positionalKey: "conversation_id" });
9035
+ if (params.metadata_only === true && params.window !== undefined) {
9036
+ throw new CliInputError("--metadata-only reads no messages, so it cannot be combined with --window; drop --window.", CONTEXT_USAGE, "flag_conflict");
9037
+ }
9038
+ if (params.all === true) {
9039
+ if (params.msg_index !== undefined || flags.window !== undefined || params.metadata_only === true) {
9040
+ throw new CliInputError("--all reads the whole trace; drop --msg-index/--window/--metadata-only.", CONTEXT_ALL_USAGE, "flag_conflict");
9041
+ }
9042
+ } else if (params.from !== undefined || params.max_messages !== undefined) {
9043
+ throw new CliInputError("--from and --max-messages page the whole trace and need --all.", CONTEXT_ALL_USAGE, "flag_conflict");
9044
+ }
9045
+ return { params, warnings };
9046
+ }
9047
+ async function readContextMetadataOnly(contextPath, query2) {
9048
+ try {
9049
+ return await callDataAPI(`${contextPath}?${query2.toString()}`);
9050
+ } catch (error) {
9051
+ if (!(error instanceof ApiError) || error.statusCode !== 400 || !/metadata_only/.test(error.message))
9052
+ throw error;
9053
+ const legacy = new URLSearchParams(query2);
9054
+ legacy.delete("metadata_only");
9055
+ legacy.set("window", "0");
9056
+ const data = asRecord(await callDataAPI(`${contextPath}?${legacy.toString()}`)) ?? {};
9057
+ const ctx = asRecord(data.context) ?? {};
9058
+ return { ...data, metadata_only: true, context: { ...ctx, messages: [] } };
9059
+ }
9060
+ }
8978
9061
  var FULL_TRANSCRIPT_DEFAULT_MAX_MESSAGES = 500;
8979
9062
  var CONTEXT_RANGE_PAGE_SIZE = 200;
8980
9063
  var LEGACY_WINDOW_MAX_MESSAGES = 100;
@@ -9001,7 +9084,6 @@ function pageMessages(page) {
9001
9084
  const messages = asRecord(page.context)?.messages ?? page.messages;
9002
9085
  return (Array.isArray(messages) ? messages : []).map((m) => asRecord(m) ?? {});
9003
9086
  }
9004
- var CONTEXT_MAX_WINDOW = 5;
9005
9087
  var CONTEXT_ALL_MAX_MESSAGES = 5000;
9006
9088
  async function readFullTranscript(conversationId, opts = {}) {
9007
9089
  const start = opts.from ?? 0;
@@ -9360,40 +9442,37 @@ async function runCommand(command, positional, flags, positionals = positional ?
9360
9442
  break;
9361
9443
  }
9362
9444
  case "context": {
9363
- if (!positional) {
9364
- throw new CliInputError("<conversation_id> is required", "Usage: moda context <conversation_id> [--msg-index=N] [--window=N]");
9365
- }
9366
- const args = flagsToArgs(flags, "conversation_id", positional);
9367
- const params = parseCommandFlags(ContextSchema, args, command, { positionalKey: "conversation_id" });
9445
+ const { params, warnings: inputWarnings } = parseContextRequest(positional, flags);
9368
9446
  if (params.all === true) {
9369
- if (params.msg_index !== undefined || flags.window !== undefined) {
9370
- throw new CliInputError("--all reads the whole trace; drop --msg-index/--window.", "Usage: moda context <conversation_id> --all [--from=N] [--max-messages=N]");
9371
- }
9372
9447
  const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
9373
9448
  const full = await readFullTranscript(params.conversation_id, {
9374
9449
  from: params.from,
9375
9450
  maxMessages: params.max_messages,
9376
9451
  fits: agentMode ? (page) => agentEnvelopeFits(command, page.data, { warnings: page.warnings, nextCommands: page.nextCommands }) : undefined
9377
9452
  });
9453
+ const fullWarnings = [...inputWarnings, ...full.warnings];
9378
9454
  context.output.writeData(full.data, {
9379
- ...full.warnings.length > 0 ? { warnings: full.warnings } : {},
9455
+ ...fullWarnings.length > 0 ? { warnings: fullWarnings } : {},
9380
9456
  ...full.nextCommands.length > 0 ? { nextCommands: full.nextCommands } : {}
9381
9457
  });
9382
9458
  break;
9383
9459
  }
9384
- if (params.from !== undefined || params.max_messages !== undefined) {
9385
- throw new CliInputError("--from and --max-messages page the whole trace and need --all.", "Usage: moda context <conversation_id> --all [--from=N] [--max-messages=N]");
9386
- }
9387
9460
  const query2 = new URLSearchParams;
9388
9461
  if (params.msg_index !== undefined)
9389
9462
  query2.set("msg_index", params.msg_index.toString());
9463
+ if (params.metadata_only === true)
9464
+ query2.set("metadata_only", "1");
9390
9465
  const window = params.window === undefined ? undefined : Math.min(params.window, CONTEXT_MAX_WINDOW);
9391
9466
  if (window !== undefined)
9392
9467
  query2.set("window", window.toString());
9393
9468
  const queryString = query2.toString() ? `?${query2.toString()}` : "";
9394
- const data = await callDataAPI(`/conversations/${params.conversation_id}/context${queryString}`);
9469
+ const contextPath = `/conversations/${encodeURIComponent(params.conversation_id)}/context`;
9470
+ const data = params.metadata_only === true ? await readContextMetadataOnly(contextPath, query2) : await callDataAPI(`${contextPath}${queryString}`);
9395
9471
  const ctxRecord = asRecord(data) ?? {};
9396
- const warnings = notFoundWarning("trace", params.conversation_id, asNumber(ctxRecord.total_messages) === 0);
9472
+ const warnings = [
9473
+ ...inputWarnings,
9474
+ ...notFoundWarning("trace", params.conversation_id, asNumber(ctxRecord.total_messages) === 0)
9475
+ ];
9397
9476
  const nextCommands = [];
9398
9477
  if (params.window !== undefined && params.window > CONTEXT_MAX_WINDOW) {
9399
9478
  const center = asNumber(asRecord(ctxRecord.context)?.center_index) ?? params.msg_index ?? 0;
@@ -10147,7 +10226,7 @@ var commandRegistry = createCommandRegistry([
10147
10226
  resetApiRequestCountBeforeRun: true,
10148
10227
  telemetry: "result",
10149
10228
  handler: async (context) => {
10150
- const { runInit } = await import("./index-z0jpjcsm.js");
10229
+ const { runInit } = await import("./index-pykaaezb.js");
10151
10230
  if (context.outputMode === "agent-stream") {
10152
10231
  context.output.writeEvent({
10153
10232
  event: "started",
@@ -10188,7 +10267,7 @@ var commandRegistry = createCommandRegistry([
10188
10267
  resetApiRequestCountBeforeRun: true,
10189
10268
  telemetry: "result-and-error",
10190
10269
  handler: async (context) => {
10191
- const { runProvision } = await import("./provision-qkazpt0c.js");
10270
+ const { runProvision } = await import("./provision-d1dfj6as.js");
10192
10271
  return runProvision(context, {
10193
10272
  resumeCommand: buildResumeCommand("provision", context.rawFlags)
10194
10273
  });
@@ -10652,6 +10731,7 @@ var commandRegistry = createCommandRegistry([
10652
10731
  examples: [
10653
10732
  "moda context <conversation_id> --window=3",
10654
10733
  "moda context <conversation_id> --msg-index=40 --window=0",
10734
+ "moda context <conversation_id> --metadata-only # summary + total_messages, no messages",
10655
10735
  "moda context <conversation_id> --all",
10656
10736
  "moda context <conversation_id> --all --from=500 --max-messages=500"
10657
10737
  ],
@@ -11062,7 +11142,15 @@ var commandRegistry = createCommandRegistry([
11062
11142
  }
11063
11143
  ]);
11064
11144
  function classifyCliError(error) {
11065
- if (error instanceof z2.ZodError || error instanceof CliInputError || error instanceof CliInputRequiredError || error instanceof CliAuthError || error instanceof CliAccessError) {
11145
+ if (error instanceof CliAuthError)
11146
+ return "auth_error";
11147
+ if (error instanceof CliAccessError)
11148
+ return "access_error";
11149
+ if (error instanceof ApiError && error.statusCode === 401)
11150
+ return "auth_error";
11151
+ if (error instanceof ApiError && error.statusCode === 403)
11152
+ return "access_error";
11153
+ if (error instanceof z2.ZodError || error instanceof CliInputError || error instanceof CliInputRequiredError) {
11066
11154
  return "validation_error";
11067
11155
  }
11068
11156
  if (error instanceof ApiError || isApiLikeError(error)) {
@@ -11073,6 +11161,21 @@ function classifyCliError(error) {
11073
11161
  }
11074
11162
  return "unknown_error";
11075
11163
  }
11164
+ function cliValidationReason(error) {
11165
+ if (error instanceof z2.ZodError) {
11166
+ const issue = error.issues[0];
11167
+ if (!issue)
11168
+ return "zod";
11169
+ const path = issue.path.join(".") || "_";
11170
+ return issue.code === "invalid_enum_value" ? `bad_enum:${path}` : `zod:${path}:${issue.code}`;
11171
+ }
11172
+ if (error instanceof CliInputRequiredError)
11173
+ return "input_required";
11174
+ if (error instanceof CliInputError) {
11175
+ return error.reason ?? (/^<[^>]+> is required/.test(error.message) ? "missing_positional" : "input_error");
11176
+ }
11177
+ return;
11178
+ }
11076
11179
  function printCliError(error) {
11077
11180
  if (error instanceof CliInputError) {
11078
11181
  safeWriteStderr(`Error: ${error.message}
@@ -11256,6 +11359,7 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
11256
11359
  flags: commandFlags,
11257
11360
  status: "error",
11258
11361
  errorType: classifyCliError(error),
11362
+ validationReason: cliValidationReason(error),
11259
11363
  exitCode: exitCodeForError(error),
11260
11364
  durationMs: Date.now() - startedAt,
11261
11365
  apiRequestCount: getApiRequestCount(),
@@ -11316,6 +11420,7 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
11316
11420
  flags: commandFlags,
11317
11421
  status: "error",
11318
11422
  errorType: classifyCliError(error),
11423
+ validationReason: cliValidationReason(error),
11319
11424
  exitCode: exitCodeForError(error),
11320
11425
  durationMs: Date.now() - startedAt,
11321
11426
  apiRequestCount: getApiRequestCount(),
@@ -11383,6 +11488,7 @@ export {
11383
11488
  runCommand,
11384
11489
  resolveTailSignal,
11385
11490
  printCliError,
11491
+ parseContextRequest,
11386
11492
  parseCommandFlags,
11387
11493
  parseArgs,
11388
11494
  initFlagsForContext,
@@ -11391,6 +11497,7 @@ export {
11391
11497
  detectCallerAgent,
11392
11498
  describeLinearLink,
11393
11499
  commandRegistry,
11500
+ cliValidationReason,
11394
11501
  classifyCliError,
11395
11502
  capProblemsBlocks,
11396
11503
  buildResumeCommand,
@@ -11398,5 +11505,6 @@ export {
11398
11505
  TAIL_LIMITS,
11399
11506
  LEGACY_WINDOW_MAX_MESSAGES,
11400
11507
  FULL_TRANSCRIPT_DEFAULT_MAX_MESSAGES,
11401
- CliInputError
11508
+ CliInputError,
11509
+ CONTEXT_MAX_WINDOW
11402
11510
  };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  buildSourceSnapshot
3
- } from "./cli-x0x0n35w.js";
4
- import"./cli-xrs02c0t.js";
3
+ } from "./cli-he2wb4hd.js";
4
+ import"./cli-ac3p2byq.js";
5
5
  import"./cli-0v6na3yp.js";
6
6
 
7
7
  // src/harness-github-actions.ts
@@ -42,8 +42,8 @@ import {
42
42
  writeHarnessAnalyzeResult,
43
43
  writeHarnessArtifacts,
44
44
  writeHumanProgress
45
- } from "./cli-x0x0n35w.js";
46
- import"./cli-xrs02c0t.js";
45
+ } from "./cli-he2wb4hd.js";
46
+ import"./cli-ac3p2byq.js";
47
47
  import"./cli-0v6na3yp.js";
48
48
  export {
49
49
  writeHumanProgress,
@@ -3,7 +3,7 @@ import {
3
3
  initPrompts,
4
4
  runPromptSync,
5
5
  runSkillSync
6
- } from "./cli-jqfm8dp4.js";
6
+ } from "./cli-ha40mgy1.js";
7
7
  import {
8
8
  codingAgentDisplayName,
9
9
  describeCodingAgentEvent,
@@ -22,7 +22,7 @@ import {
22
22
  runHarnessCommand,
23
23
  startRemoteAnalyze,
24
24
  stripAnsi
25
- } from "./cli-x0x0n35w.js";
25
+ } from "./cli-he2wb4hd.js";
26
26
  import {
27
27
  bundledSkillsDir,
28
28
  installIntegrationSkill,
@@ -34,14 +34,14 @@ import {
34
34
  } from "./cli-ssfq84k5.js";
35
35
  import {
36
36
  selectTenantAndCreateKey
37
- } from "./cli-h4k23fb0.js";
37
+ } from "./cli-sq6g0tk5.js";
38
38
  import {
39
39
  ConnectCodeError,
40
40
  connectWithCode,
41
41
  loadAuthSession,
42
42
  loadToken,
43
43
  login
44
- } from "./cli-7kbzqact.js";
44
+ } from "./cli-kxwm7y62.js";
45
45
  import {
46
46
  CliAuthError,
47
47
  CliInputError,
@@ -49,7 +49,7 @@ import {
49
49
  createCommandContext,
50
50
  resolveIngestUrl,
51
51
  resolveModaBaseUrl
52
- } from "./cli-xrs02c0t.js";
52
+ } from "./cli-ac3p2byq.js";
53
53
  import"./cli-0v6na3yp.js";
54
54
 
55
55
  // src/init/index.ts
@@ -5,8 +5,8 @@ import {
5
5
  inspectCodeSelectors,
6
6
  inspectPythonSelectors,
7
7
  safePromptEvidencePath
8
- } from "./cli-x0x0n35w.js";
9
- import"./cli-xrs02c0t.js";
8
+ } from "./cli-he2wb4hd.js";
9
+ import"./cli-ac3p2byq.js";
10
10
  import {
11
11
  __commonJS,
12
12
  __toESM
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  selectTenantAndCreateKey
3
- } from "./cli-h4k23fb0.js";
3
+ } from "./cli-sq6g0tk5.js";
4
4
  import {
5
5
  loadAuthSession
6
- } from "./cli-7kbzqact.js";
6
+ } from "./cli-kxwm7y62.js";
7
7
  import {
8
8
  CliAuthError,
9
9
  resolveIngestUrl,
10
10
  resolveModaBaseUrl,
11
11
  stringOption
12
- } from "./cli-xrs02c0t.js";
12
+ } from "./cli-ac3p2byq.js";
13
13
  import"./cli-0v6na3yp.js";
14
14
 
15
15
  // src/provision.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moda-ai/cli",
3
- "version": "1.50.0",
3
+ "version": "1.52.0",
4
4
  "description": "CLI for Moda - AI agent analytics and observability",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": "moda.skill_index.v1",
3
- "bundled_at": "2026-10-10T14:07:17.742Z",
4
- "cli_version": "1.50.0",
3
+ "bundled_at": "2026-10-12T06:24:12.441Z",
4
+ "cli_version": "1.52.0",
5
5
  "skills": [
6
6
  {
7
7
  "id": "integration-cloudflare-think",
@@ -458,6 +458,8 @@ fail instead of being clamped.
458
458
  time window. On `context`, `--window=0` returns only the anchor message (plus
459
459
  `total_messages`), and a value above 5 is clamped to 5 with a warning that
460
460
  prints the `--all --from --max-messages` command for a wider slice.
461
+ `--metadata-only` returns only the summary and `total_messages`, with no
462
+ messages.
461
463
 
462
464
  ### Schema introspection
463
465
 
@@ -546,11 +548,13 @@ compare scores across modes.
546
548
  Hand the anchors straight to `moda context`:
547
549
 
548
550
  ```bash
549
- # Find, then read the exact turn
550
- hit=$(moda search "double charge" --limit=1 | jq -r '.results[0]')
551
+ # Find, then read the exact turn. --json prints the bare payload; piped output
552
+ # without it is the agent envelope, where results sit under .data.results.
553
+ hit=$(moda search "double charge" --limit=1 --json | jq -c '.results[0] // empty')
551
554
  cid=$(echo "$hit" | jq -r '.conversation_id')
552
- idx=$(echo "$hit" | jq -r '.message_index')
553
- moda context "$cid" --msg-index="$idx"
555
+ # Trace-level fallback hits (match_grain: "trace") have no message_index.
556
+ idx=$(echo "$hit" | jq -r '.message_index // empty')
557
+ moda context "$cid" ${idx:+--msg-index="$idx"}
554
558
  ```
555
559
 
556
560
  ### 2. Overview (start here for health questions)
@@ -647,6 +651,7 @@ moda context <conversation_id> # default window around middle
647
651
  moda context <conversation_id> --msg-index=5 # center on message 5
648
652
  moda context <conversation_id> --window=3 # 3 messages each side (max 5)
649
653
  moda context <conversation_id> --msg-index=40 --window=0 # just message 40 + total_messages
654
+ moda context <conversation_id> --metadata-only # summary + total_messages, no messages
650
655
  moda context <conversation_id> --all # whole trace in order, first 500 messages
651
656
  moda context <conversation_id> --all --from=500 --max-messages=500 # continue (max 5000)
652
657
  ```
@@ -1128,16 +1133,17 @@ and archiving an active custom signal (pause it, or delete a draft).
1128
1133
 
1129
1134
  ```bash
1130
1135
  # Semantic: find the moment a user expressed an intent, in their own words
1131
- moda search "user wants to cancel subscription" --mode=semantic --limit=5 \
1136
+ moda search "user wants to cancel subscription" --mode=semantic --limit=5 --json \
1132
1137
  | jq -r '.results[] | "\(.conversation_id) @\(.message_index) \(.snippet)"'
1133
1138
 
1134
1139
  # Keyword: locate every tool call/result mentioning an exact identifier
1135
1140
  moda search "stripe.refunds.create" --mode=keyword --limit=10
1136
1141
 
1137
- # Search, then open the top hit at the exact turn
1138
- moda search "rate limit exceeded" --limit=1 \
1139
- | jq -r '.results[0] | "\(.conversation_id) \(.message_index)"' \
1140
- | xargs -n2 sh -c 'moda context "$0" --msg-index="$1"'
1142
+ # Search, then open the top message-level hit at the exact turn (skips
1143
+ # trace-level fallback hits, which have no message_index)
1144
+ moda search "rate limit exceeded" --limit=5 --json \
1145
+ | jq -r 'first(.results[] | select(.message_index != null)) | "\(.conversation_id) \(.message_index)"' \
1146
+ | xargs -r -n2 sh -c 'moda context "$0" --msg-index="$1"'
1141
1147
  ```
1142
1148
 
1143
1149
  ### Daily health check