@moda-ai/cli 1.45.3 → 1.48.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.
package/README.md CHANGED
@@ -116,6 +116,7 @@ Feedback:
116
116
 
117
117
  ```bash
118
118
  moda feedback "cluster label looks wrong" --category=bad_cluster_label
119
+ moda feedback "trace belongs to another problem" --category=false_positive_attribution --problem-id=<id>
119
120
  ```
120
121
 
121
122
  Output adapts to where it runs: piped/CI/agent environments get schema-versioned
@@ -1692,6 +1692,7 @@ function createCommandContext(input, options = {}) {
1692
1692
  positionals: input.positionals,
1693
1693
  flags,
1694
1694
  rawFlags,
1695
+ ...input.bareFlags ? { bareFlags: input.bareFlags } : {},
1695
1696
  startedAt: options.startedAt ?? Date.now(),
1696
1697
  cwd: options.cwd ?? process.cwd(),
1697
1698
  env: options.env ?? process.env,
@@ -13,7 +13,7 @@ import {
13
13
  resolvePromptSource,
14
14
  safePromptEvidencePath,
15
15
  scanHarness
16
- } from "./cli-3xh2w7hb.js";
16
+ } from "./cli-tznhpmf2.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-dy2aavbe.js";
29
+ } from "./cli-fpt3jkyh.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-bvnw7585.js";
48
+ } from "./cli-233xm35b.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-9qpbj2qj.js");
6714
+ const { analyzeHarnessPrompts } = await import("./harness-vf1e1t2h.js");
6715
6715
  const result = await analyzeHarnessPrompts({
6716
6716
  rootDir: process.cwd(),
6717
6717
  existing: established.flatMap((prompt) => prompt.sourceDefinition ? [prompt.sourceDefinition.source] : []),
@@ -7,7 +7,7 @@ import {
7
7
  saveProfileConfig,
8
8
  secretFilePathFromRef,
9
9
  writeSecretJsonFile
10
- } from "./cli-bvnw7585.js";
10
+ } from "./cli-233xm35b.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-dy2aavbe.js";
4
+ } from "./cli-fpt3jkyh.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-bvnw7585.js";
18
+ } from "./cli-233xm35b.js";
19
19
 
20
20
  // src/init/tenant.ts
21
21
  import { randomUUID } from "node:crypto";
@@ -25,7 +25,7 @@ import {
25
25
  resolveTenantId,
26
26
  shouldInventoryFile,
27
27
  stringOption
28
- } from "./cli-bvnw7585.js";
28
+ } from "./cli-233xm35b.js";
29
29
  import {
30
30
  __commonJS,
31
31
  __require,
@@ -184813,7 +184813,7 @@ async function runHarnessCommand(context) {
184813
184813
  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.");
184814
184814
  }
184815
184815
  if (context.flags["github-actions"] === "true") {
184816
- const { runGithubActionsAnalyze } = await import("./harness-github-actions-ky4ym5n3.js");
184816
+ const { runGithubActionsAnalyze } = await import("./harness-github-actions-379faaqe.js");
184817
184817
  const result = await runGithubActionsAnalyze(rootDir, {
184818
184818
  writeReport: (report2) => {
184819
184819
  const normalized = normalizeHarnessReport(report2);
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  runPromptsCommand,
9
9
  runSkillsCommand,
10
10
  runStatusCommand
11
- } from "./cli-nnwegmzh.js";
11
+ } from "./cli-eaaegm7a.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-3xh2w7hb.js";
40
+ } from "./cli-tznhpmf2.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-dy2aavbe.js";
49
+ } from "./cli-fpt3jkyh.js";
50
50
  import {
51
51
  AGENT_ERROR_CODES,
52
52
  CliAccessError,
@@ -96,7 +96,7 @@ import {
96
96
  validateConfig,
97
97
  wasOutputCancelled,
98
98
  writeSecretJsonFile
99
- } from "./cli-bvnw7585.js";
99
+ } from "./cli-233xm35b.js";
100
100
  import {
101
101
  __require
102
102
  } from "./cli-0v6na3yp.js";
@@ -402,6 +402,8 @@ var SearchSchema = z.object({
402
402
  include_tool_io: boolFlag(),
403
403
  offset: z.number().min(0).max(1e4).optional()
404
404
  });
405
+
406
+ // ../../packages/database/src/agent-feedback.ts
405
407
  var FEEDBACK_CATEGORIES = [
406
408
  "bad_cluster_label",
407
409
  "mismatched_frustration",
@@ -410,18 +412,98 @@ var FEEDBACK_CATEGORIES = [
410
412
  "wrong_tool_failure",
411
413
  "incorrect_loop",
412
414
  "api_quirk",
415
+ "false_positive_attribution",
416
+ "misrouted_fix",
417
+ "harness_bug",
413
418
  "other"
414
419
  ];
415
420
  var FEEDBACK_SEVERITIES = ["info", "low", "medium", "high"];
416
- var FeedbackSchema = z.object({
417
- note: z.string().min(1).max(4000),
418
- category: z.enum(FEEDBACK_CATEGORIES).default("other").optional(),
419
- severity: z.enum(FEEDBACK_SEVERITIES).default("low").optional(),
420
- conversation_id: z.string().min(1).max(256).optional(),
421
- cluster_id: z.string().min(1).max(256).optional(),
422
- tool_name: z.string().min(1).max(256).optional(),
423
- run_id: z.string().min(1).max(256).optional()
424
- });
421
+ var DEFAULT_FEEDBACK_CATEGORY = "other";
422
+ var RAW_FEEDBACK_CATEGORY_MAX = 64;
423
+ function isFeedbackCategory(value) {
424
+ return typeof value === "string" && FEEDBACK_CATEGORIES.includes(value);
425
+ }
426
+ function normalizeFeedbackCategory(value) {
427
+ if (value === undefined || value === null || value === "")
428
+ return { category: DEFAULT_FEEDBACK_CATEGORY };
429
+ if (isFeedbackCategory(value))
430
+ return { category: value };
431
+ const raw = String(value).trim().slice(0, RAW_FEEDBACK_CATEGORY_MAX);
432
+ return raw ? { category: DEFAULT_FEEDBACK_CATEGORY, raw_category: raw } : { category: DEFAULT_FEEDBACK_CATEGORY };
433
+ }
434
+
435
+ // src/feedback.ts
436
+ var FEEDBACK_FLAGS = ["category", "severity", "note", "cluster-id", "conversation-id", "run-id", "tool-name"];
437
+ var FEEDBACK_USAGE = `Usage: moda feedback "<note>" [--category=${FEEDBACK_CATEGORIES.join("|")}] ` + `[--severity=${FEEDBACK_SEVERITIES.join("|")}] [--conversation-id=ID] [--cluster-id=ID] ` + "[--tool-name=NAME] [--run-id=ID] [--<name>-id=ID]";
438
+ var NOTE_MAX = 4000;
439
+ var REF_VALUE_MAX = 256;
440
+ var REF_FLAG_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*-(?:id|ids|index|token)$/;
441
+ var REF_FLAG_WORDS = new Set(["turn", "tool", "family", "kind"]);
442
+ function isFeedbackRefFlag(flag) {
443
+ return REF_FLAG_PATTERN.test(flag) || REF_FLAG_WORDS.has(flag);
444
+ }
445
+ function feedbackCategoryHelp(indent = 35, width = 100) {
446
+ const lines = [];
447
+ let line = "";
448
+ FEEDBACK_CATEGORIES.forEach((category, index) => {
449
+ const piece = index < FEEDBACK_CATEGORIES.length - 1 ? `${category}|` : `${category} (default other)`;
450
+ if (line && indent + line.length + piece.length > width) {
451
+ lines.push(line);
452
+ line = "";
453
+ }
454
+ line += piece;
455
+ });
456
+ lines.push(line);
457
+ return lines.join(`
458
+ ${" ".repeat(indent)}`);
459
+ }
460
+ function buildFeedbackRequest(positionals, flags, bareFlags) {
461
+ const isBare = (flag) => bareFlags ? bareFlags.includes(flag) : flags[flag] === "true";
462
+ const flagNote = flags.note !== undefined && !isBare("note") ? flags.note.trim() : "";
463
+ const note = [positionals.join(" ").trim(), flagNote].filter(Boolean).join(`
464
+ `);
465
+ if (note === "")
466
+ throw new CliInputError("<note> is required", FEEDBACK_USAGE);
467
+ if (note.length > NOTE_MAX) {
468
+ throw new CliInputError(`<note> is ${note.length} characters; the limit is ${NOTE_MAX}.`, FEEDBACK_USAGE);
469
+ }
470
+ const severity = flags.severity ?? "low";
471
+ if (!FEEDBACK_SEVERITIES.includes(severity)) {
472
+ throw new CliInputError(`Invalid --severity '${severity}'. Valid severities: ${FEEDBACK_SEVERITIES.join(", ")}.`, FEEDBACK_USAGE);
473
+ }
474
+ const refs = {};
475
+ for (const [flag, value] of Object.entries(flags)) {
476
+ if (flag === "category" || flag === "severity" || flag === "note")
477
+ continue;
478
+ if (!FEEDBACK_FLAGS.includes(flag) && !isFeedbackRefFlag(flag))
479
+ continue;
480
+ const trimmed = value.trim();
481
+ if (value === "true" || trimmed === "") {
482
+ throw new CliInputError(`--${flag} needs a value, e.g. --${flag}=<id>.`, FEEDBACK_USAGE);
483
+ }
484
+ if (trimmed.length > REF_VALUE_MAX) {
485
+ throw new CliInputError(`--${flag} is ${trimmed.length} characters; the limit is ${REF_VALUE_MAX}.`, FEEDBACK_USAGE);
486
+ }
487
+ refs[flag.replace(/-/g, "_")] = trimmed;
488
+ }
489
+ const warnings = [];
490
+ const { category, raw_category: rawCategory } = normalizeFeedbackCategory(flags.category);
491
+ if (rawCategory !== undefined) {
492
+ refs.raw_category = rawCategory;
493
+ warnings.push(`Unknown category '${rawCategory}': sent as 'other' with refs.raw_category. ` + `Valid categories: ${FEEDBACK_CATEGORIES.join(", ")}.`);
494
+ }
495
+ const hint = dedicatedCommandHint(category, rawCategory);
496
+ if (hint)
497
+ warnings.push(hint);
498
+ return { category, severity, note, refs, warnings };
499
+ }
500
+ var MISROUTE_CATEGORIES = new Set(["false_positive_attribution", "misrouted_fix"]);
501
+ var MISROUTE_WORDS = /attribut|misrout|false[\s_-]?positive|wrong[\s_-]?(?:problem|fix)/i;
502
+ function dedicatedCommandHint(category, rawCategory) {
503
+ if (!MISROUTE_CATEGORIES.has(category) && !(rawCategory && MISROUTE_WORDS.test(rawCategory)))
504
+ return;
505
+ return "Feedback is read by the Moda team but does not change the problem. To act on it directly, use " + "`moda problem-feedback <problem_id> --action=flag_attribution --attribution-id=<uuid> --reason=TEXT` (a trace wrongly " + "attributed to a problem), `moda false-positive <kind> <target> --reason=TEXT` (a detection that should not " + "have fired), or `moda fix dismiss <fix_id> --reason=TEXT` (a fix aimed at the wrong problem).";
506
+ }
425
507
 
426
508
  // src/transforms.ts
427
509
  function asRecord(value) {
@@ -7667,6 +7749,7 @@ function parseArgs(argv) {
7667
7749
  const flags = {};
7668
7750
  const positionals = [];
7669
7751
  const shortFlags = [];
7752
+ const bareFlags = [];
7670
7753
  let positional;
7671
7754
  let i = 0;
7672
7755
  let endOfOptions = false;
@@ -7675,7 +7758,7 @@ function parseArgs(argv) {
7675
7758
  if (!endOfOptions && arg === "--" && command !== "") {
7676
7759
  endOfOptions = true;
7677
7760
  } else if (!endOfOptions && arg.startsWith("--")) {
7678
- i = parseFlag(args, i, flags, command === "");
7761
+ i = parseFlag(args, i, flags, command === "", bareFlags);
7679
7762
  } else if (!endOfOptions && command !== "" && /^-[A-Za-z]/.test(arg)) {
7680
7763
  shortFlags.push(arg);
7681
7764
  } else if (command === "") {
@@ -7688,7 +7771,14 @@ function parseArgs(argv) {
7688
7771
  }
7689
7772
  i++;
7690
7773
  }
7691
- return { command, positional, positionals, flags, ...shortFlags.length > 0 ? { shortFlags } : {} };
7774
+ return {
7775
+ command,
7776
+ positional,
7777
+ positionals,
7778
+ flags,
7779
+ ...shortFlags.length > 0 ? { shortFlags } : {},
7780
+ ...bareFlags.length > 0 ? { bareFlags } : {}
7781
+ };
7692
7782
  }
7693
7783
  var DRY_RUN_TRUE = new Set(["true", "yes", "1", "on"]);
7694
7784
  var DRY_RUN_FALSE = new Set(["false", "no", "0", "off"]);
@@ -7708,24 +7798,32 @@ var GLOBAL_BOOLEAN_FLAGS = new Set([
7708
7798
  "tui",
7709
7799
  "no-tui"
7710
7800
  ]);
7711
- function parseFlag(args, index, flags, beforeCommand) {
7801
+ function parseFlag(args, index, flags, beforeCommand, bareFlags) {
7802
+ const set = (key2, value, bare) => {
7803
+ flags[key2] = value;
7804
+ const at = bareFlags.indexOf(key2);
7805
+ if (at !== -1)
7806
+ bareFlags.splice(at, 1);
7807
+ if (bare)
7808
+ bareFlags.push(key2);
7809
+ };
7712
7810
  const arg = args[index];
7713
7811
  const eqIndex = arg.indexOf("=");
7714
7812
  if (eqIndex !== -1) {
7715
- flags[arg.slice(2, eqIndex)] = arg.slice(eqIndex + 1);
7813
+ set(arg.slice(2, eqIndex), arg.slice(eqIndex + 1), false);
7716
7814
  return index;
7717
7815
  }
7718
7816
  const key = arg.slice(2);
7719
7817
  const next = args[index + 1];
7720
7818
  if (beforeCommand && GLOBAL_BOOLEAN_FLAGS.has(key)) {
7721
- flags[key] = "true";
7819
+ set(key, "true", true);
7722
7820
  return index;
7723
7821
  }
7724
7822
  if (next !== undefined && !next.startsWith("--")) {
7725
- flags[key] = next;
7823
+ set(key, next, false);
7726
7824
  return index + 1;
7727
7825
  }
7728
- flags[key] = "true";
7826
+ set(key, "true", true);
7729
7827
  return index;
7730
7828
  }
7731
7829
  function initFlagsForContext(flags, outputMode, stdinIsTTY) {
@@ -8016,13 +8114,16 @@ Clusters search flags:
8016
8114
  --mode=fuzzy|semantic|hybrid Cluster search mode (default hybrid)
8017
8115
 
8018
8116
  Feedback flags:
8019
- --category=CAT bad_cluster_label|mismatched_frustration|missing_data|noisy_data|
8020
- wrong_tool_failure|incorrect_loop|api_quirk|other (default other)
8117
+ --category=CAT ${feedbackCategoryHelp()}
8118
+ An unknown category is sent as other (with refs.raw_category)
8021
8119
  --severity=info|low|medium|high How bad it is (default low)
8120
+ --note=TEXT The note, if not given as the positional argument
8022
8121
  --conversation-id=ID Attach the trace ID you were looking at
8023
8122
  --cluster-id=ID Attach a cluster node id
8024
8123
  --tool-name=NAME Attach a tool name
8025
8124
  --run-id=ID Attach a run id
8125
+ --<name>-id=ID Any other id (--problem-id, --fix-id, --attribution-id,
8126
+ --trace-id, --span-id, --message-index, ...) goes into refs
8026
8127
  (set MODA_CLI_TIPS=0 to hide the feedback tip line)
8027
8128
 
8028
8129
  Init flags:
@@ -8755,7 +8856,7 @@ var HAND_PARSED_COMMAND_FLAGS = {
8755
8856
  "tool-use-id"
8756
8857
  ],
8757
8858
  "false-positives": ["all", "kind", "limit"],
8758
- feedback: ["category", "severity", "cluster-id", "conversation-id", "run-id", "tool-name"],
8859
+ feedback: FEEDBACK_FLAGS,
8759
8860
  "problem-impact": ["mode", "node-id", "problem-id", "run-id", "to", "cursor", "conversation-ids", "days-back"]
8760
8861
  };
8761
8862
  var NO_POSITIONAL_COMMANDS = new Set([
@@ -8786,9 +8887,16 @@ function assertHandParsedFlags(command, flags, positionals) {
8786
8887
  const allowed = HAND_PARSED_COMMAND_FLAGS[command];
8787
8888
  if (!allowed)
8788
8889
  return;
8890
+ const checked = command === "feedback" ? Object.keys(flags).filter((flag2) => !isFeedbackRefFlag(flag2)) : Object.keys(flags);
8789
8891
  const shape = z2.object(Object.fromEntries(allowed.map((flag2) => [flag2.replace(/-/g, "_"), z2.unknown()])));
8790
- const args = Object.fromEntries(Object.keys(flags).map((flag2) => [flag2.replace(/-/g, "_"), true]));
8791
- parseCommandFlags(shape.passthrough(), args, command);
8892
+ const args = Object.fromEntries(checked.map((flag2) => [flag2.replace(/-/g, "_"), true]));
8893
+ try {
8894
+ parseCommandFlags(shape.passthrough(), args, command);
8895
+ } catch (error) {
8896
+ if (command !== "feedback" || !(error instanceof CliInputError))
8897
+ throw error;
8898
+ 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());
8899
+ }
8792
8900
  }
8793
8901
  var PROFILE_MANAGING_COMMANDS = new Set(["profiles", "auth", "init", "provision", "status", "config", "keys", "manifest", "agent", "doctor"]);
8794
8902
  async function runProblemsRemainder(flags, context) {
@@ -9886,30 +9994,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
9886
9994
  break;
9887
9995
  }
9888
9996
  case "feedback": {
9889
- const note = positionals.join(" ").trim();
9890
- if (note === "") {
9891
- throw new CliInputError("<note> is required", `Usage: moda feedback "<note>" [--category=${FEEDBACK_CATEGORIES.join("|")}] [--severity=${FEEDBACK_SEVERITIES.join("|")}] [--conversation-id=ID] [--cluster-id=ID] [--tool-name=NAME] [--run-id=ID]`);
9892
- }
9893
- const params = FeedbackSchema.parse({
9894
- note,
9895
- ...flags.category !== undefined ? { category: flags.category } : {},
9896
- ...flags.severity !== undefined ? { severity: flags.severity } : {},
9897
- ...flags["conversation-id"] !== undefined ? { conversation_id: flags["conversation-id"] } : {},
9898
- ...flags["cluster-id"] !== undefined ? { cluster_id: flags["cluster-id"] } : {},
9899
- ...flags["tool-name"] !== undefined ? { tool_name: flags["tool-name"] } : {},
9900
- ...flags["run-id"] !== undefined ? { run_id: flags["run-id"] } : {}
9901
- });
9902
- const refs = {};
9903
- if (params.conversation_id)
9904
- refs.conversation_id = params.conversation_id;
9905
- if (params.cluster_id)
9906
- refs.cluster_id = params.cluster_id;
9907
- if (params.tool_name)
9908
- refs.tool_name = params.tool_name;
9909
- if (params.run_id)
9910
- refs.run_id = params.run_id;
9911
- const category = params.category ?? "other";
9912
- const severity = params.severity ?? "low";
9997
+ const { category, severity, note, refs, warnings } = buildFeedbackRequest(positionals, flags, context.bareFlags);
9913
9998
  const data = await callDataAPI("/feedback", {
9914
9999
  method: "POST",
9915
10000
  body: JSON.stringify({
@@ -9918,11 +10003,14 @@ async function runCommand(command, positional, flags, positionals = positional ?
9918
10003
  source: "cli",
9919
10004
  agent: detectCallerAgent(process.env),
9920
10005
  cli_version: VERSION,
9921
- note: params.note,
10006
+ note,
9922
10007
  ...Object.keys(refs).length > 0 ? { refs } : {}
9923
10008
  })
9924
10009
  }, { retries: 0 });
9925
- context.output.writeData({ submitted: true, category, severity, ...asRecord(data) ?? {} }, { summary: "Feedback submitted. Thank you for helping us improve!" });
10010
+ context.output.writeData({ submitted: true, category, severity, ...asRecord(data) ?? {} }, {
10011
+ summary: "Feedback submitted. Thank you for helping us improve!",
10012
+ ...warnings.length > 0 ? { warnings } : {}
10013
+ });
9926
10014
  break;
9927
10015
  }
9928
10016
  default:
@@ -10027,7 +10115,7 @@ var commandRegistry = createCommandRegistry([
10027
10115
  resetApiRequestCountBeforeRun: true,
10028
10116
  telemetry: "result",
10029
10117
  handler: async (context) => {
10030
- const { runInit } = await import("./index-bcn5chmf.js");
10118
+ const { runInit } = await import("./index-p8vab4p4.js");
10031
10119
  if (context.outputMode === "agent-stream") {
10032
10120
  context.output.writeEvent({
10033
10121
  event: "started",
@@ -10068,7 +10156,7 @@ var commandRegistry = createCommandRegistry([
10068
10156
  resetApiRequestCountBeforeRun: true,
10069
10157
  telemetry: "result-and-error",
10070
10158
  handler: async (context) => {
10071
- const { runProvision } = await import("./provision-mqw8s0jb.js");
10159
+ const { runProvision } = await import("./provision-e4cnxpdh.js");
10072
10160
  return runProvision(context, {
10073
10161
  resumeCommand: buildResumeCommand("provision", context.rawFlags)
10074
10162
  });
@@ -10769,7 +10857,8 @@ var commandRegistry = createCommandRegistry([
10769
10857
  description: "Flag wrong/missing data or CLI quirks to the Moda team",
10770
10858
  examples: [
10771
10859
  'moda feedback "cluster label looks wrong" --category=bad_cluster_label --cluster-id=<node_id>',
10772
- 'moda feedback "search finds nothing for a trace I can open" --category=missing_data --conversation-id=<id>'
10860
+ 'moda feedback "search finds nothing for a trace I can open" --category=missing_data --conversation-id=<id>',
10861
+ 'moda feedback "trace is about refunds, not this problem" --category=false_positive_attribution --problem-id=<id> --conversation-id=<id>'
10773
10862
  ],
10774
10863
  ...dataApiDefaults,
10775
10864
  mutability: "write"
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  buildSourceSnapshot
3
- } from "./cli-3xh2w7hb.js";
4
- import"./cli-bvnw7585.js";
3
+ } from "./cli-tznhpmf2.js";
4
+ import"./cli-233xm35b.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-3xh2w7hb.js";
46
- import"./cli-bvnw7585.js";
45
+ } from "./cli-tznhpmf2.js";
46
+ import"./cli-233xm35b.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-nnwegmzh.js";
6
+ } from "./cli-eaaegm7a.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-3xh2w7hb.js";
25
+ } from "./cli-tznhpmf2.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-phgzghwq.js";
37
+ } from "./cli-ss2nere4.js";
38
38
  import {
39
39
  ConnectCodeError,
40
40
  connectWithCode,
41
41
  loadAuthSession,
42
42
  loadToken,
43
43
  login
44
- } from "./cli-dy2aavbe.js";
44
+ } from "./cli-fpt3jkyh.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-bvnw7585.js";
52
+ } from "./cli-233xm35b.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-3xh2w7hb.js";
9
- import"./cli-bvnw7585.js";
8
+ } from "./cli-tznhpmf2.js";
9
+ import"./cli-233xm35b.js";
10
10
  import {
11
11
  __commonJS,
12
12
  __toESM
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  selectTenantAndCreateKey
3
- } from "./cli-phgzghwq.js";
3
+ } from "./cli-ss2nere4.js";
4
4
  import {
5
5
  loadAuthSession
6
- } from "./cli-dy2aavbe.js";
6
+ } from "./cli-fpt3jkyh.js";
7
7
  import {
8
8
  CliAuthError,
9
9
  resolveIngestUrl,
10
10
  resolveModaBaseUrl,
11
11
  stringOption
12
- } from "./cli-bvnw7585.js";
12
+ } from "./cli-233xm35b.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.45.3",
3
+ "version": "1.48.0",
4
4
  "description": "CLI for Moda - AI agent analytics and observability",
5
5
  "type": "module",
6
6
  "bin": {
@@ -56,6 +56,7 @@
56
56
  "@modelcontextprotocol/sdk": "^1.30.0"
57
57
  },
58
58
  "devDependencies": {
59
+ "@moda/database": "*",
59
60
  "@moda/eslint-config": "*",
60
61
  "@moda/harness-snapshot": "*",
61
62
  "@moda/integration-skills": "*",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": "moda.skill_index.v1",
3
- "bundled_at": "2026-10-10T04:37:09.232Z",
4
- "cli_version": "1.45.3",
3
+ "bundled_at": "2026-10-10T06:36:17.459Z",
4
+ "cli_version": "1.48.0",
5
5
  "skills": [
6
6
  {
7
7
  "id": "integration-cloudflare-think",
@@ -1186,15 +1186,30 @@ moda feedback "search finds nothing for a trace I can open" \
1186
1186
  --category=missing_data --conversation-id=<id>
1187
1187
  moda feedback "a cancelled call is counted as a tool failure" \
1188
1188
  --category=wrong_tool_failure --tool-name=<tool>
1189
+ moda feedback "trace is a refund request, not this problem" \
1190
+ --category=false_positive_attribution --problem-id=<id> --conversation-id=<id>
1189
1191
  ```
1190
1192
 
1191
- - `<note>` is required (max 4000 chars). Say what looked wrong and why.
1193
+ - `<note>` is required (max 4000 chars), as the argument or `--note=TEXT`.
1194
+ Say what looked wrong and why.
1192
1195
  - `--category`: `bad_cluster_label | mismatched_frustration | missing_data |
1193
- noisy_data | wrong_tool_failure | incorrect_loop | api_quirk | other`
1194
- (default `other`).
1196
+ noisy_data | wrong_tool_failure | incorrect_loop | api_quirk |
1197
+ false_positive_attribution | misrouted_fix | harness_bug | other`
1198
+ (default `other`). Use `false_positive_attribution` when a trace is
1199
+ attributed to the wrong problem, `misrouted_fix` when a fix targets the
1200
+ wrong problem or file, and `harness_bug` for a bug in the agent harness
1201
+ Moda analysed. An unknown category is still sent, as `other` with
1202
+ `refs.raw_category`, and a warning lists the valid ones.
1195
1203
  - `--severity`: `info | low | medium | high` (default `low`).
1196
1204
  - Attach ids when you have them: `--conversation-id`, `--cluster-id`,
1197
- `--tool-name`, `--run-id`; they let the team reproduce what you saw.
1205
+ `--tool-name`, `--run-id`, or any other `--<name>-id` (`--problem-id`,
1206
+ `--fix-id`, `--attribution-id`, `--trace-id`, `--span-id`,
1207
+ `--message-index`); they go into `refs` and let the team reproduce what
1208
+ you saw.
1209
+ - Feedback does not change the problem itself. To act on a misroute directly,
1210
+ use `moda problem-feedback <problem_id> --action=flag_attribution
1211
+ --attribution-id=<uuid> --reason=TEXT`, `moda false-positive <kind> <target>
1212
+ --reason=TEXT`, or `moda fix dismiss <fix_id> --reason=TEXT`.
1198
1213
  - Never include secrets or full transcripts in the note.
1199
1214
 
1200
1215
  ## Validation — how to know it worked