@moda-ai/cli 1.55.4 → 1.55.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  runPromptsCommand,
9
9
  runSkillsCommand,
10
10
  runStatusCommand
11
- } from "./cli-73v8mqh1.js";
11
+ } from "./cli-grn40kvh.js";
12
12
  import"./cli-mm48br00.js";
13
13
  import {
14
14
  ApiError,
@@ -20,7 +20,6 @@ import {
20
20
  assertPositionals,
21
21
  callControlAPI,
22
22
  callDataAPI,
23
- getApiRequestCount,
24
23
  graphForSync,
25
24
  hashHarnessReport,
26
25
  hashStableJson,
@@ -28,7 +27,6 @@ import {
28
27
  readHarnessReport,
29
28
  readHarnessSyncState,
30
29
  renderMarkdownForTerminal,
31
- resetApiRequestCount,
32
30
  runHarnessCommand,
33
31
  runProblemReopenCommand,
34
32
  runSignalsManagementCommand,
@@ -41,7 +39,7 @@ import {
41
39
  terminalStyles,
42
40
  unknownSignalsSubcommandError,
43
41
  validateHarnessReport
44
- } from "./cli-9v0c1xe1.js";
42
+ } from "./cli-gxmq4wmn.js";
45
43
  import"./cli-ssfq84k5.js";
46
44
  import {
47
45
  authFetch,
@@ -50,7 +48,7 @@ import {
50
48
  loadAuthSession,
51
49
  login,
52
50
  revokeCliSessionToken
53
- } from "./cli-t5v7mhvj.js";
51
+ } from "./cli-tw8jj190.js";
54
52
  import {
55
53
  AGENT_ERROR_CODES,
56
54
  CliAccessError,
@@ -79,11 +77,15 @@ import {
79
77
  ensureSecretFilePermissions,
80
78
  explicitOutputMode,
81
79
  getActiveProfile,
80
+ getApiRequestCount,
82
81
  getModaDir,
83
82
  getModaPaths,
84
83
  isApiLikeError,
85
84
  isNetworkLikeError,
86
85
  loadProfileConfig,
86
+ parseBooleanFlagValue,
87
+ recordApiRequest,
88
+ resetApiRequestCount,
87
89
  resolveApiKey,
88
90
  resolveApiKeyFromActiveProfile,
89
91
  resolveApiTenantId,
@@ -101,7 +103,7 @@ import {
101
103
  validateConfig,
102
104
  wasOutputCancelled,
103
105
  writeSecretJsonFile
104
- } from "./cli-zp7a97sp.js";
106
+ } from "./cli-mx9r1689.js";
105
107
  import {
106
108
  __require
107
109
  } from "./cli-0v6na3yp.js";
@@ -112,9 +114,21 @@ import { z as z2 } from "zod";
112
114
 
113
115
  // src/schemas.ts
114
116
  import { z } from "zod";
115
- var boolFlag = () => z.preprocess((val) => val === "true" ? true : val === "false" ? false : val, z.boolean()).optional();
117
+ var boolFlag = () => z.preprocess((val) => {
118
+ const text = String(val).toLowerCase();
119
+ if (["true", "1", "yes", "on"].includes(text))
120
+ return true;
121
+ if (["false", "0", "no", "off"].includes(text))
122
+ return false;
123
+ return val;
124
+ }, z.boolean()).optional();
125
+ var DAYS_BACK_MESSAGE = "must be a whole number from 1 to 90 (e.g. --days-back=7)";
126
+ var daysBackFlag = (fallback) => {
127
+ const days = z.number({ invalid_type_error: DAYS_BACK_MESSAGE }).int(DAYS_BACK_MESSAGE).min(1, DAYS_BACK_MESSAGE).max(90, DAYS_BACK_MESSAGE);
128
+ return fallback === undefined ? days.optional() : days.default(fallback).optional();
129
+ };
116
130
  var OverviewSchema = z.object({
117
- days_back: z.number().min(1).max(90).default(7).optional()
131
+ days_back: daysBackFlag(7)
118
132
  });
119
133
  var ClustersSchema = z.object({
120
134
  parent_id: z.string().optional(),
@@ -154,25 +168,17 @@ var WorldStateSchema = z.object({
154
168
  format: z.enum(["frames", "fold-inputs"]).optional(),
155
169
  message_count: z.number().min(1).max(1e6).optional()
156
170
  });
157
- var lenientBoolFlag = () => z.preprocess((val) => {
158
- const text = String(val).toLowerCase();
159
- if (["true", "1", "yes", "on"].includes(text))
160
- return true;
161
- if (["false", "0", "no", "off"].includes(text))
162
- return false;
163
- return val;
164
- }, z.boolean()).optional();
165
171
  var ContextSchema = z.object({
166
172
  conversation_id: z.string().min(1),
167
173
  msg_index: z.number().int().min(0).optional(),
168
174
  window: z.number().int().min(0).optional(),
169
- metadata_only: lenientBoolFlag(),
170
- all: lenientBoolFlag(),
175
+ metadata_only: boolFlag(),
176
+ all: boolFlag(),
171
177
  from: z.number().int().min(0).optional(),
172
178
  max_messages: z.number().int().min(1).max(5000).optional()
173
179
  });
174
180
  var FrustrationsSchema = z.object({
175
- days_back: z.number().min(1).max(90).default(7).optional(),
181
+ days_back: daysBackFlag(7),
176
182
  limit: z.number().min(1).max(20).default(10).optional(),
177
183
  offset: z.number().min(0).default(0).optional(),
178
184
  include_window: boolFlag(),
@@ -181,10 +187,10 @@ var FrustrationsSchema = z.object({
181
187
  conversation_id: z.string().min(1).max(256).optional()
182
188
  });
183
189
  var ToolFailuresSchema = z.object({
184
- days_back: z.number().min(1).max(90).default(7).optional()
190
+ days_back: daysBackFlag(7)
185
191
  });
186
192
  var ProblemsSchema = z.object({
187
- days_back: z.number().min(1).max(90).default(30).optional(),
193
+ days_back: daysBackFlag(30),
188
194
  limit: z.number().min(1).max(25).default(25).optional()
189
195
  });
190
196
  var REMAINDER_FAMILIES = [
@@ -197,7 +203,7 @@ var REMAINDER_FAMILIES = [
197
203
  "conversation_reader"
198
204
  ];
199
205
  var ProblemsRemainderSchema = z.object({
200
- days_back: z.number().min(1).max(90).default(30).optional(),
206
+ days_back: daysBackFlag(30),
201
207
  limit: z.number().min(1).max(25).default(25).optional(),
202
208
  family: z.enum(REMAINDER_FAMILIES).optional(),
203
209
  cursor: z.string().min(1).max(4096).optional()
@@ -264,7 +270,7 @@ var ProblemLinearFileSchema = z.object({
264
270
  var SignalSchema = z.object({
265
271
  id: z.string().min(1).max(255),
266
272
  detections: boolFlag(),
267
- days_back: z.number().min(1).max(90).optional(),
273
+ days_back: daysBackFlag(),
268
274
  limit: z.number().min(1).max(20).optional(),
269
275
  offset: z.number().min(0).max(1e4).optional()
270
276
  });
@@ -335,7 +341,7 @@ var EMOTION_FAMILIES = [
335
341
  "positive"
336
342
  ];
337
343
  var EmotionsSchema = z.object({
338
- days_back: z.number().min(1).max(90).default(7).optional(),
344
+ days_back: daysBackFlag(7),
339
345
  limit: z.number().min(1).max(20).default(10).optional(),
340
346
  offset: z.number().min(0).max(1e4).optional(),
341
347
  family: z.enum(EMOTION_FAMILIES).optional(),
@@ -343,7 +349,7 @@ var EmotionsSchema = z.object({
343
349
  conversation_id: z.string().min(1).max(256).optional()
344
350
  });
345
351
  var HallucinationsSchema = z.object({
346
- days_back: z.number().min(1).max(90).default(7).optional(),
352
+ days_back: daysBackFlag(7),
347
353
  limit: z.number().min(1).max(20).default(10).optional(),
348
354
  offset: z.number().min(0).max(1e4).optional(),
349
355
  conversation_id: z.string().max(200).optional(),
@@ -371,7 +377,7 @@ var LAZINESS_FAMILIES = [
371
377
  "data_regression"
372
378
  ];
373
379
  var LazinessSchema = z.object({
374
- days_back: z.number().min(1).max(90).default(7).optional(),
380
+ days_back: daysBackFlag(7),
375
381
  limit: z.number().min(1).max(50).default(10).optional(),
376
382
  offset: z.number().min(0).max(1e4).optional(),
377
383
  conversation_id: z.string().max(200).optional(),
@@ -392,7 +398,7 @@ var TailSchema = z.object({
392
398
  var ToolFailureDetailSchema = z.object({
393
399
  tool_name: z.string(),
394
400
  subtype: z.string().optional(),
395
- days_back: z.number().min(1).max(90).default(7).optional(),
401
+ days_back: daysBackFlag(7),
396
402
  limit: z.number().min(1).max(20).default(5).optional(),
397
403
  offset: z.number().min(0).default(0).optional(),
398
404
  include_window: boolFlag(),
@@ -819,8 +825,12 @@ async function runFixesCommand(context) {
819
825
  throw new CliInputError(`Unknown fixes subcommand '${context.positional}'`, FIX_USAGE);
820
826
  }
821
827
  const query = new URLSearchParams;
822
- if (context.flags.status)
823
- query.set("status", context.flags.status);
828
+ const status = (context.flags.status ?? "").trim().toUpperCase();
829
+ if (context.flags.status !== undefined && !status) {
830
+ throw new CliInputError("--status must name a fix status.", FIX_USAGE);
831
+ }
832
+ if (status)
833
+ query.set("status", status);
824
834
  if (context.flags.limit) {
825
835
  const limit = Number(context.flags.limit);
826
836
  if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
@@ -3465,6 +3475,8 @@ import { randomUUID } from "node:crypto";
3465
3475
  var TELEMETRY_TIMEOUT_MS = 500;
3466
3476
  var INVOCATION_ID = randomUUID();
3467
3477
  var TELEMETRY_FLAG = "no-telemetry";
3478
+ var MAX_TELEMETRY_DURATION_MS = 86400000;
3479
+ var MAX_TELEMETRY_API_REQUEST_COUNT = 1e6;
3468
3480
  var SEARCH_SURFACE_BY_COMMAND = {
3469
3481
  search: "message",
3470
3482
  conversations: "conversation",
@@ -3506,7 +3518,7 @@ function buildSearchAdoptionTelemetry(input) {
3506
3518
  adoption.search_mode_requested = SEARCH_MODES2.includes(requested) ? requested : "hybrid";
3507
3519
  }
3508
3520
  if (command === "context") {
3509
- adoption.used_msg_index = flags["msg-index"] !== undefined ? 1 : 0;
3521
+ adoption.used_msg_index = flags["msg-index"] !== undefined || flags["message-index"] !== undefined ? 1 : 0;
3510
3522
  }
3511
3523
  if (recorded.search_mode_effective) {
3512
3524
  adoption.search_mode_effective = recorded.search_mode_effective;
@@ -3584,7 +3596,7 @@ async function sendCliUsageTelemetry(input) {
3584
3596
  timeout.unref();
3585
3597
  }
3586
3598
  try {
3587
- await fetch(`${resolveModaBaseUrl()}/api/v1/data/cli-usage`, {
3599
+ await fetch(`${resolveModaBaseUrl({ profile: input.profile })}/api/v1/data/cli-usage`, {
3588
3600
  method: "POST",
3589
3601
  headers: {
3590
3602
  ...apiKeyHeaders(input.apiKey),
@@ -3597,8 +3609,8 @@ async function sendCliUsageTelemetry(input) {
3597
3609
  error_type: input.errorType,
3598
3610
  ...validationReason ? { validation_reason: validationReason } : {},
3599
3611
  exit_code: input.exitCode,
3600
- duration_ms: Math.max(0, Math.round(input.durationMs)),
3601
- api_request_count: Math.max(0, Math.round(input.apiRequestCount)),
3612
+ duration_ms: clampInt(input.durationMs, 0, MAX_TELEMETRY_DURATION_MS) ?? 0,
3613
+ api_request_count: clampInt(input.apiRequestCount, 0, MAX_TELEMETRY_API_REQUEST_COUNT) ?? 0,
3602
3614
  cli_version: VERSION,
3603
3615
  node_version: process.versions.node,
3604
3616
  platform: process.platform,
@@ -4818,6 +4830,7 @@ function redactApiKey(key) {
4818
4830
  async function probeApiKey(key, options = {}) {
4819
4831
  let response;
4820
4832
  try {
4833
+ recordApiRequest();
4821
4834
  response = await fetch(`${resolveModaBaseUrl(options)}/api/v1/data/overview`, {
4822
4835
  method: "GET",
4823
4836
  headers: apiKeyHeaders(key, options),
@@ -6161,6 +6174,7 @@ async function streamAsk(options) {
6161
6174
  const fetchImpl = options.fetchImpl ?? fetch;
6162
6175
  let response;
6163
6176
  try {
6177
+ recordApiRequest();
6164
6178
  response = await fetchImpl(url, {
6165
6179
  method: "POST",
6166
6180
  headers: {
@@ -6220,7 +6234,7 @@ var ASK_SCHEMA_VERSION = "production_ask.v0.1";
6220
6234
  var INVESTIGATION_PROBLEM_LIMIT = 3;
6221
6235
  var INVESTIGATION_PROBLEM_SLOTS = INVESTIGATION_PROBLEM_LIMIT;
6222
6236
  async function runProductionCommand(context) {
6223
- const daysBack = daysBackFlag(context.flags);
6237
+ const daysBack = daysBackFlag2(context.flags);
6224
6238
  if (context.command === "overview") {
6225
6239
  const briefing = await loadOverviewBriefing({ daysBack, cwd: context.cwd });
6226
6240
  if (context.flags.raw === "true") {
@@ -7555,7 +7569,7 @@ function numberValue(value) {
7555
7569
  }
7556
7570
  return 0;
7557
7571
  }
7558
- function daysBackFlag(flags, fallback = 7) {
7572
+ function daysBackFlag2(flags, fallback = 7) {
7559
7573
  const raw = flags["days-back"];
7560
7574
  if (raw === undefined)
7561
7575
  return fallback;
@@ -7810,8 +7824,64 @@ function parseArgs(argv) {
7810
7824
  ...bareFlags.length > 0 ? { bareFlags } : {}
7811
7825
  };
7812
7826
  }
7813
- var DRY_RUN_TRUE = new Set(["true", "yes", "1", "on"]);
7814
- var DRY_RUN_FALSE = new Set(["false", "no", "0", "off"]);
7827
+ var RAW_BOOLEAN_FLAGS = new Set([
7828
+ "dry-run",
7829
+ "yes",
7830
+ "live",
7831
+ "local",
7832
+ "watch",
7833
+ "promote",
7834
+ "no-promote",
7835
+ "promote-on-win",
7836
+ "undo",
7837
+ "all",
7838
+ "wait",
7839
+ "no-wait",
7840
+ "wait-for-completion",
7841
+ "summary",
7842
+ "list",
7843
+ "save",
7844
+ "sync",
7845
+ "replay",
7846
+ "no-replay",
7847
+ "remote",
7848
+ "no-remote",
7849
+ "raw",
7850
+ "pr",
7851
+ "packet",
7852
+ "transcript",
7853
+ "gate",
7854
+ "discover",
7855
+ "experimental",
7856
+ "force",
7857
+ "force-reprocess",
7858
+ "reprocess-segments",
7859
+ "online",
7860
+ "offline",
7861
+ "progress",
7862
+ "stream",
7863
+ "no-stream",
7864
+ "quiet",
7865
+ "verbose",
7866
+ "allow-untrack",
7867
+ "github-actions",
7868
+ "no-clustering",
7869
+ "no-analyze",
7870
+ "improve-candidates",
7871
+ "hook-stdin",
7872
+ "auto-generate",
7873
+ "allow-no-agents",
7874
+ "allow-any-model",
7875
+ "json",
7876
+ "human",
7877
+ "prose",
7878
+ "json-schemas",
7879
+ "help",
7880
+ "tui",
7881
+ "no-tui",
7882
+ "no-telemetry",
7883
+ "no-update-check"
7884
+ ]);
7815
7885
  var GLOBAL_BOOLEAN_FLAGS = new Set([
7816
7886
  "agent",
7817
7887
  "json",
@@ -7828,6 +7898,20 @@ var GLOBAL_BOOLEAN_FLAGS = new Set([
7828
7898
  "tui",
7829
7899
  "no-tui"
7830
7900
  ]);
7901
+ var BOOLEAN_WORDS = new Set(["true", "false", "yes", "no", "on", "off"]);
7902
+ var META_SWITCHES = new Set([
7903
+ "json",
7904
+ "human",
7905
+ "prose",
7906
+ "quiet",
7907
+ "verbose",
7908
+ "help",
7909
+ "json-schemas",
7910
+ "tui",
7911
+ "no-tui",
7912
+ "no-telemetry",
7913
+ "no-update-check"
7914
+ ]);
7831
7915
  var COMMAND_BOOLEAN_FLAGS = {
7832
7916
  context: new Set(["all", "metadata-only"])
7833
7917
  };
@@ -7852,7 +7936,15 @@ function parseFlag(args, index, flags, beforeCommand, bareFlags, command = "") {
7852
7936
  set(key, "true", true);
7853
7937
  return index;
7854
7938
  }
7855
- if (!beforeCommand && COMMAND_BOOLEAN_FLAGS[command]?.has(key.replace(/_/g, "-")) && next !== "true" && next !== "false") {
7939
+ const switchName = key.replace(/_/g, "-");
7940
+ if (META_SWITCHES.has(switchName)) {
7941
+ set(key, "true", true);
7942
+ return index;
7943
+ }
7944
+ const idFollows = COMMAND_BOOLEAN_FLAGS[command]?.has(switchName) === true;
7945
+ const isSwitch = RAW_BOOLEAN_FLAGS.has(switchName) || idFollows;
7946
+ const takesNext = BOOLEAN_WORDS.has(next?.toLowerCase() ?? "") || !idFollows && (next === "0" || next === "1");
7947
+ if (!beforeCommand && isSwitch && !takesNext) {
7856
7948
  set(key, "true", true);
7857
7949
  return index;
7858
7950
  }
@@ -8022,11 +8114,11 @@ Options:
8022
8114
  --version Show version number
8023
8115
  --json-schemas Print JSON Schemas for the agent output contract
8024
8116
 
8025
- Common flags:
8026
- --days-back=N Number of days to look back (1-90)
8027
- --limit=N Maximum results to return
8028
- --offset=N Number of results to skip
8029
- --time-range=RANGE Time range filter (all|1h|3d|7d|24h|30d|90d)
8117
+ Common flags (a command rejects any it does not take; see 'moda <command> --help'):
8118
+ --days-back=N Whole days to look back, 1-90 (overview, problems, emotions, laziness, tool-failures, ...)
8119
+ --time-range=RANGE Window for traces, search, clusters instead (all|1h|3d|7d|24h|30d|90d)
8120
+ --limit=N Maximum results to return (users: alias of --page-size)
8121
+ --offset=N Number of results to skip (paged lists)
8030
8122
  --profile=NAME Use a named local Moda profile
8031
8123
  --json Emit machine-readable JSON
8032
8124
  --agent Emit schema-versioned agent JSON
@@ -8313,7 +8405,7 @@ function printCommandHelp(definition) {
8313
8405
  lines.push(` ${example}`);
8314
8406
  }
8315
8407
  }
8316
- lines.push("", "Run 'moda --help' for the full command list and common flags", "(--json, --agent, --profile, --days-back, --limit, --time-range, ...).");
8408
+ lines.push("", "Run 'moda --help' for the full command list and the output flags every command", "takes (--json, --agent, --human, --profile). Window and paging flags (--days-back,", "--time-range, --limit, --offset) differ by command; an unsupported one is rejected.");
8317
8409
  console.log(lines.join(`
8318
8410
  `));
8319
8411
  }
@@ -8756,6 +8848,12 @@ var WINDOW_FLAG_HINTS = {
8756
8848
  days_back: "This command takes --time-range (e.g. --time-range=7d), not --days-back.",
8757
8849
  time_range: "This command takes --days-back=N (1-90), not --time-range."
8758
8850
  };
8851
+ var COMMAND_FLAG_HINTS = {
8852
+ problems: {
8853
+ family: "'moda problems' ranks every family together. To filter by family use 'moda problems remainder --family=<family>' " + "(signals no Problem explains yet) or 'moda problem <problem_id> --traces --family=<family>' (one Problem's traces).",
8854
+ cursor: "'moda problems' has no cursor; 'moda problems remainder --cursor=<next_cursor>' pages the unexplained signals."
8855
+ }
8856
+ };
8759
8857
  function editDistance(a, b) {
8760
8858
  const row = Array.from({ length: b.length + 1 }, (_, index) => index);
8761
8859
  for (let i = 1;i <= a.length; i++) {
@@ -8789,6 +8887,9 @@ function parseCommandFlags(schema, args, command, options = {}) {
8789
8887
  const flagKeys = schemaKeys.filter((key) => key !== options.positionalKey);
8790
8888
  const valid = flagKeys.map((key) => `--${key.replace(/_/g, "-")}`);
8791
8889
  const hints = unknown.map((key) => {
8890
+ const commandHint = COMMAND_FLAG_HINTS[command]?.[key];
8891
+ if (commandHint)
8892
+ return commandHint;
8792
8893
  const windowAlternative = key === "days_back" ? "time_range" : key === "time_range" ? "days_back" : undefined;
8793
8894
  if (windowAlternative && WINDOW_FLAG_HINTS[key] && flagKeys.includes(windowAlternative))
8794
8895
  return WINDOW_FLAG_HINTS[key];
@@ -8816,6 +8917,30 @@ function shellArg(value) {
8816
8917
  return `"${value}"`;
8817
8918
  return `'${value.replace(/'/g, `'\\''`)}'`;
8818
8919
  }
8920
+ function semanticIndexCoverage(coverage) {
8921
+ const ratio = asRecord(asRecord(coverage)?.semantic_index)?.coverage_ratio;
8922
+ if (typeof ratio !== "number" || !Number.isFinite(ratio) || ratio < 0 || ratio > 1)
8923
+ return;
8924
+ return `${Number((ratio * 100).toFixed(1))}%`;
8925
+ }
8926
+ function searchDegradeWarning(reason, requestedMode, coverage) {
8927
+ const lead = `Search degraded to keyword mode (${reason}):`;
8928
+ const notComparable = "keyword scores are not comparable to semantic scores";
8929
+ switch (reason) {
8930
+ case "semantic_unavailable":
8931
+ return `${lead} the requested ${requestedMode} ranking did not run, and ${notComparable}. Retry, or use --mode=keyword with exact identifiers.`;
8932
+ case "semantic_index_empty":
8933
+ return `${lead} the semantic index has no embeddings for this tenant in this time window, so the requested ${requestedMode} ranking cannot run and a retry will not help. ` + `The results are keyword matches (${notComparable}); use --mode=keyword with exact identifiers.`;
8934
+ case "semantic_index_partial": {
8935
+ const percent = semanticIndexCoverage(coverage);
8936
+ return `${lead} the semantic index covers ${percent ? `only ${percent}` : "only part"} of the messages in this time window, so the requested ${requestedMode} ranking did not run and a retry will not help. ` + `The results are keyword matches (${notComparable}); use --mode=keyword with exact identifiers.`;
8937
+ }
8938
+ case "below_relevance_floor":
8939
+ return `${lead} no semantic hit cleared the relevance floor, so the requested ${requestedMode} ranking returned nothing and these are keyword matches (${notComparable}). ` + "Rephrase the query, or use --mode=keyword with exact identifiers.";
8940
+ default:
8941
+ return `${lead} the requested ${requestedMode} ranking did not run, and ${notComparable}. Use --mode=keyword with exact identifiers.`;
8942
+ }
8943
+ }
8819
8944
  function searchCoverageWarnings(coverage, timeRange) {
8820
8945
  const record = asRecord(coverage);
8821
8946
  if (!record)
@@ -9008,6 +9133,24 @@ function assertHandParsedFlags(command, flags, positionals) {
9008
9133
  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);
9009
9134
  }
9010
9135
  }
9136
+ function normalizeBooleanFlags(target) {
9137
+ let error;
9138
+ for (const [key, raw] of Object.entries(target)) {
9139
+ const name = key.replace(/_/g, "-");
9140
+ if (!RAW_BOOLEAN_FLAGS.has(name))
9141
+ continue;
9142
+ const value = parseBooleanFlagValue(raw);
9143
+ if (value === undefined) {
9144
+ error ??= new CliInputError(`--${name}=${raw} is not a yes/no value, so nothing was run.`, `--${name} is a switch: pass --${name}, or --${name}=true|false (also 1|0, yes|no, on|off).`, `bad_boolean:${name}`);
9145
+ continue;
9146
+ }
9147
+ if (name === "dry-run" && !value)
9148
+ delete target[key];
9149
+ else
9150
+ target[key] = value ? "true" : "false";
9151
+ }
9152
+ return error;
9153
+ }
9011
9154
  var PROFILE_MANAGING_COMMANDS = new Set(["profiles", "auth", "init", "provision", "status", "config", "keys", "manifest", "agent", "doctor"]);
9012
9155
  async function runProblemsRemainder(flags, context) {
9013
9156
  const params = parseCommandFlags(ProblemsRemainderSchema, flagsToArgs(flags), "problems remainder");
@@ -9039,15 +9182,17 @@ function processingDisabledWarning(data, family) {
9039
9182
  `${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."
9040
9183
  ];
9041
9184
  }
9042
- var CONTEXT_USAGE = "Usage: moda context <conversation_id> [--msg-index=N] [--window=0-5 | --metadata-only]";
9185
+ var CONTEXT_USAGE = "Usage: moda context <conversation_id> [--msg-index=N (alias --message-index)] [--window=0-5 | --metadata-only]";
9043
9186
  var CONTEXT_ALL_USAGE = "Usage: moda context <conversation_id> --all [--from=N] [--max-messages=N]";
9187
+ var CONTEXT_FLAG_ALIASES = { "message-index": "msg-index" };
9044
9188
  var CONTEXT_MAX_WINDOW = 5;
9045
9189
  var EMPTY_ANCHOR_VALUES = new Set(["", "null", "undefined", "none"]);
9046
9190
  function parseContextRequest(positional, rawFlags) {
9047
9191
  const flags = {};
9048
9192
  const flagIds = [];
9049
9193
  for (const [rawKey, value] of Object.entries(rawFlags)) {
9050
- const key = rawKey.replace(/_/g, "-");
9194
+ const kebab = rawKey.replace(/_/g, "-");
9195
+ const key = CONTEXT_FLAG_ALIASES[kebab] ?? kebab;
9051
9196
  if (key === "conversation-id") {
9052
9197
  if (value !== "true")
9053
9198
  flagIds.push(value);
@@ -9404,7 +9549,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
9404
9549
  const actualMode = asString(searchRecord.search_mode);
9405
9550
  const warnings = [];
9406
9551
  if (degradeReason && degradeReason !== "none") {
9407
- warnings.push(`Search degraded to keyword mode (${degradeReason}): the requested ${params.mode ?? "hybrid"} ranking did not run, and keyword scores are not comparable to semantic scores. Retry, or use --mode=keyword with exact identifiers.`);
9552
+ warnings.push(searchDegradeWarning(degradeReason, params.mode ?? "hybrid", searchRecord.coverage));
9408
9553
  } else if (params.mode && actualMode && actualMode !== params.mode && params.mode !== "hybrid") {
9409
9554
  warnings.push(`Search ran in ${actualMode} mode instead of the requested ${params.mode} mode.`);
9410
9555
  }
@@ -10041,6 +10186,9 @@ async function runCommand(command, positional, flags, positionals = positional ?
10041
10186
  const kinds = { quote: "frustration_quote", tool: "tool_call", laziness: "laziness", hallucination: "hallucination", "custom-signal": "custom_signal" };
10042
10187
  const query2 = new URLSearchParams;
10043
10188
  if (flags.kind !== undefined) {
10189
+ if (flags.kind === "problem" || flags.kind === "problem-detection") {
10190
+ throw new CliInputError(`'moda false-positives' cannot list --kind=${flags.kind}: ${flags.kind === "problem" ? "a dismissed Problem is recorded as Problem feedback" : "a problem-detection false positive is recorded as a detection review on its Problem"}, not in the false-positive log this command reads. Nothing was sent.`, flags.kind === "problem" ? "Read it with 'moda problem <problem_id> --feedback'. --kind for this list takes " + Object.keys(kinds).join("|") + "." : "Read them per trace with 'moda detection-reviews <conversation_id>' or 'moda false-positives <conversation_id>'. --kind for this list takes " + Object.keys(kinds).join("|") + ".", "unsupported_kind");
10191
+ }
10044
10192
  if (!kinds[flags.kind])
10045
10193
  throw new CliInputError(`--kind must be one of ${Object.keys(kinds).join("|")}`);
10046
10194
  query2.set("kind", kinds[flags.kind]);
@@ -10233,7 +10381,11 @@ function initAgentWriteOptions(result) {
10233
10381
  path: file,
10234
10382
  description: "Created or verified during Moda init."
10235
10383
  })),
10236
- warnings: manualActions.filter((action) => action.reason).map((action) => `${action.title}: ${action.reason}`)
10384
+ warnings: [
10385
+ ...manualActions.filter((action) => action.reason).map((action) => `${action.title}: ${action.reason}`),
10386
+ ...result.promptSync?.status === "failed" ? [`Prompt sync failed: ${result.promptSync.error}. Retry with 'moda prompts sync'.`] : [],
10387
+ ...result.skillSync?.status === "failed" ? [`Skill sync failed: ${result.skillSync.error}. Retry with 'moda skills sync'.`] : []
10388
+ ]
10237
10389
  };
10238
10390
  }
10239
10391
  function initChangedFiles(setupPlan) {
@@ -10270,7 +10422,7 @@ var commandRegistry = createCommandRegistry([
10270
10422
  resetApiRequestCountBeforeRun: true,
10271
10423
  telemetry: "result",
10272
10424
  handler: async (context) => {
10273
- const { runInit } = await import("./index-8vvznb36.js");
10425
+ const { runInit } = await import("./index-6wxkhbk5.js");
10274
10426
  if (context.outputMode === "agent-stream") {
10275
10427
  context.output.writeEvent({
10276
10428
  event: "started",
@@ -10311,7 +10463,7 @@ var commandRegistry = createCommandRegistry([
10311
10463
  resetApiRequestCountBeforeRun: true,
10312
10464
  telemetry: "result-and-error",
10313
10465
  handler: async (context) => {
10314
- const { runProvision } = await import("./provision-2z73642d.js");
10466
+ const { runProvision } = await import("./provision-parsn3xg.js");
10315
10467
  return runProvision(context, {
10316
10468
  resumeCommand: buildResumeCommand("provision", context.rawFlags)
10317
10469
  });
@@ -10556,7 +10708,8 @@ var commandRegistry = createCommandRegistry([
10556
10708
  description: "List end users with health, frustration and activity (the dashboard Users page)",
10557
10709
  examples: [
10558
10710
  "moda users --window=30d --sort=health_score --order=asc",
10559
- "moda users --cohort=frustrated --min-conversations=3"
10711
+ "moda users --cohort=frustrated --min-conversations=3",
10712
+ "moda users --limit=50 # --limit is an alias of --page-size"
10560
10713
  ],
10561
10714
  auth: "api-key",
10562
10715
  outputModes: ["human", "json", "agent"],
@@ -10774,7 +10927,7 @@ var commandRegistry = createCommandRegistry([
10774
10927
  description: "Get windowed trace context (messages around one turn), or the whole trace with --all",
10775
10928
  examples: [
10776
10929
  "moda context <conversation_id> --window=3",
10777
- "moda context <conversation_id> --msg-index=40 --window=0",
10930
+ "moda context <conversation_id> --msg-index=40 --window=0 # --message-index is an alias",
10778
10931
  "moda context <conversation_id> --metadata-only # summary + total_messages, no messages",
10779
10932
  "moda context <conversation_id> --all",
10780
10933
  "moda context <conversation_id> --all --from=500 --max-messages=500"
@@ -11195,6 +11348,15 @@ var commandRegistry = createCommandRegistry([
11195
11348
  })
11196
11349
  }
11197
11350
  ]);
11351
+ function apiErrorType(status) {
11352
+ if (typeof status !== "number" || status < 400 || status >= 500)
11353
+ return "api_error";
11354
+ if (status === 404)
11355
+ return "api_not_found";
11356
+ if (status === 409)
11357
+ return "api_conflict";
11358
+ return "api_client_error";
11359
+ }
11198
11360
  function classifyCliError(error) {
11199
11361
  if (error instanceof CliAuthError)
11200
11362
  return "auth_error";
@@ -11208,7 +11370,7 @@ function classifyCliError(error) {
11208
11370
  return "validation_error";
11209
11371
  }
11210
11372
  if (error instanceof ApiError || isApiLikeError(error)) {
11211
- return "api_error";
11373
+ return apiErrorType(error.statusCode);
11212
11374
  }
11213
11375
  if (isNetworkLikeError(error)) {
11214
11376
  return "network_error";
@@ -11249,7 +11411,7 @@ function printCliError(error) {
11249
11411
  return ` ${field}: ${e.message}`;
11250
11412
  });
11251
11413
  safeWriteStderr(`Validation error:
11252
- ${issues.join(`
11414
+ ${[...new Set(issues)].join(`
11253
11415
  `)}
11254
11416
  `);
11255
11417
  } else if (error instanceof ApiError && error.readable) {
@@ -11307,6 +11469,7 @@ async function dispatchParsedCommand(parsed, startedAt = Date.now()) {
11307
11469
  }
11308
11470
  async function dispatchParsedCommandInner(parsed, startedAt) {
11309
11471
  const { command, flags } = parsed;
11472
+ const switchError = normalizeBooleanFlags(flags);
11310
11473
  const commandFlags = stripUpdateCheckFlag(stripTelemetryFlag(flags));
11311
11474
  const telemetryDisabled = isTelemetryDisabled(flags);
11312
11475
  const updateCheckDisabled = isUpdateCheckDisabled(flags);
@@ -11337,11 +11500,38 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
11337
11500
  printHelp();
11338
11501
  return 1;
11339
11502
  }
11340
- const failEarly = (error) => {
11503
+ const failEarly = (error, commandDefinition) => {
11341
11504
  const mode = resolveOutputMode(commandFlags, {
11342
11505
  isTTY: process.stdout.isTTY,
11343
11506
  env: process.env
11344
11507
  });
11508
+ const telemetryOptOut = telemetryDisabled || flags["no-telemetry"] !== undefined && flags["no-telemetry"] !== "false";
11509
+ if (commandDefinition && !telemetryOptOut && commandDefinition.telemetry === "result-and-error") {
11510
+ const telemetryCommand2 = telemetryCommandName(commandDefinition);
11511
+ const flagProfile = commandFlags.profile && commandFlags.profile !== "true" ? commandFlags.profile : undefined;
11512
+ const telemetryProfile = flagProfile && Object.hasOwn(loadProfileConfig({ env: process.env }).profiles, flagProfile) ? flagProfile : undefined;
11513
+ sendCliUsageTelemetry({
11514
+ apiKey: resolveApiKey({ profile: telemetryProfile }),
11515
+ profile: telemetryProfile,
11516
+ command: telemetryCommand2,
11517
+ flags: commandFlags,
11518
+ status: "error",
11519
+ errorType: classifyCliError(error),
11520
+ validationReason: cliValidationReason(error),
11521
+ exitCode: 1,
11522
+ durationMs: Date.now() - startedAt,
11523
+ apiRequestCount: 0,
11524
+ adoption: buildSearchAdoptionTelemetry({
11525
+ command: telemetryCommand2,
11526
+ positional: parsed.positional,
11527
+ flags: commandFlags,
11528
+ outputMode: mode,
11529
+ recorded: {},
11530
+ skillVersion: process.env.MODA_SKILL_VERSION,
11531
+ subcommands: commandDefinition.subcommands?.map((subcommand2) => subcommand2.name)
11532
+ })
11533
+ });
11534
+ }
11345
11535
  if (mode === "agent" || mode === "agent-stream" || mode === "json") {
11346
11536
  printAgentCliError(command, error, mode);
11347
11537
  } else {
@@ -11354,30 +11544,19 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
11354
11544
  return failEarly(new CliInputError(`Unknown command '${command}'`, 'Run "moda --help" for usage information.'));
11355
11545
  }
11356
11546
  if (parsed.shortFlags?.length) {
11357
- return failEarly(new CliInputError(`Short options are not supported (${parsed.shortFlags.join(", ")}), so nothing was run.`, `Use the long form, e.g. --dry-run, --json, --quiet. See 'moda ${command} --help'.`));
11358
- }
11359
- for (const target of [flags, commandFlags]) {
11360
- const raw = target["dry-run"];
11361
- if (raw === undefined)
11362
- continue;
11363
- const value = raw.trim().toLowerCase();
11364
- if (DRY_RUN_TRUE.has(value))
11365
- target["dry-run"] = "true";
11366
- else if (DRY_RUN_FALSE.has(value))
11367
- delete target["dry-run"];
11368
- else {
11369
- return failEarly(new CliInputError(`--dry-run=${raw} is not a yes/no value, so nothing was run.`, "Pass --dry-run (or --dry-run=true) to preview, or omit it to run for real."));
11370
- }
11547
+ return failEarly(new CliInputError(`Short options are not supported (${parsed.shortFlags.join(", ")}), so nothing was run.`, `Use the long form, e.g. --dry-run, --json, --quiet. See 'moda ${command} --help'.`, "short_flag"), definition);
11371
11548
  }
11549
+ if (switchError)
11550
+ return failEarly(switchError, definition);
11372
11551
  if (commandFlags.profile === "true" || commandFlags.profile === "") {
11373
- return failEarly(new CliInputError("--profile needs a value, e.g. --profile=prod.", "List profiles with 'moda profiles list'."));
11552
+ return failEarly(new CliInputError("--profile needs a value, e.g. --profile=prod.", "List profiles with 'moda profiles list'.", "missing_value:profile"), definition);
11374
11553
  }
11375
11554
  const requestedProfile = commandFlags.profile ?? process.env.MODA_PROFILE?.trim();
11376
11555
  if (requestedProfile && !PROFILE_MANAGING_COMMANDS.has(definition.name)) {
11377
11556
  const known = Object.keys(loadProfileConfig({ env: process.env }).profiles);
11378
11557
  if (!known.includes(requestedProfile)) {
11379
11558
  const source = commandFlags.profile ? `--profile=${requestedProfile}` : `MODA_PROFILE=${requestedProfile}`;
11380
- return failEarly(new CliInputError(`Moda profile '${requestedProfile}' does not exist (from ${source}), so nothing was run.`, `Known profiles: ${known.join(", ")}. Create it with 'moda profiles create ${requestedProfile}' or fix the name.`));
11559
+ return failEarly(new CliInputError(`Moda profile '${requestedProfile}' does not exist (from ${source}), so nothing was run.`, `Known profiles: ${known.join(", ")}. Create it with 'moda profiles create ${requestedProfile}' or fix the name.`, "unknown_profile"), definition);
11381
11560
  }
11382
11561
  if (commandFlags.profile)
11383
11562
  process.env.MODA_PROFILE = commandFlags.profile;
@@ -11539,6 +11718,7 @@ if (isMain) {
11539
11718
  export {
11540
11719
  telemetryCommandName,
11541
11720
  stripLoneSurrogates,
11721
+ searchDegradeWarning,
11542
11722
  searchCoverageWarnings,
11543
11723
  runCommand,
11544
11724
  resolveTailSignal,
@@ -11546,6 +11726,7 @@ export {
11546
11726
  parseContextRequest,
11547
11727
  parseCommandFlags,
11548
11728
  parseArgs,
11729
+ normalizeBooleanFlags,
11549
11730
  initFlagsForContext,
11550
11731
  flagsToArgs,
11551
11732
  dispatchParsedCommand,
@@ -11558,8 +11739,10 @@ export {
11558
11739
  buildResumeCommand,
11559
11740
  auditCapStatus,
11560
11741
  TAIL_LIMITS,
11742
+ RAW_BOOLEAN_FLAGS,
11561
11743
  LEGACY_WINDOW_MAX_MESSAGES,
11562
11744
  FULL_TRANSCRIPT_DEFAULT_MAX_MESSAGES,
11563
11745
  CliInputError,
11564
- CONTEXT_MAX_WINDOW
11746
+ CONTEXT_MAX_WINDOW,
11747
+ CONTEXT_FLAG_ALIASES
11565
11748
  };