@moda-ai/cli 1.35.0 → 1.37.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/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  runPromptsCommand,
9
9
  runSkillsCommand,
10
10
  runStatusCommand
11
- } from "./cli-7qkhy41j.js";
11
+ } from "./cli-ert1sbbt.js";
12
12
  import {
13
13
  ApiError,
14
14
  HARNESS_REPORT_APPROVAL_PATH,
@@ -29,7 +29,8 @@ import {
29
29
  summarizeHarness,
30
30
  terminalStyles,
31
31
  validateHarnessReport
32
- } from "./cli-0p6n391h.js";
32
+ } from "./cli-07tv75te.js";
33
+ import"./cli-ssfq84k5.js";
33
34
  import {
34
35
  authFetch,
35
36
  clearAuthSession,
@@ -37,7 +38,7 @@ import {
37
38
  loadAuthSession,
38
39
  login,
39
40
  revokeCliSessionToken
40
- } from "./cli-yz99t7n8.js";
41
+ } from "./cli-strngwn3.js";
41
42
  import {
42
43
  AGENT_ERROR_CODES,
43
44
  CliAccessError,
@@ -55,7 +56,6 @@ import {
55
56
  MODA_BASE_URL,
56
57
  PROD_BASE_URL,
57
58
  VERSION,
58
- __require,
59
59
  assertValidProfileName,
60
60
  clearStoredApiKeyCredentials,
61
61
  createCliOutput,
@@ -84,7 +84,10 @@ import {
84
84
  validateConfig,
85
85
  wasOutputCancelled,
86
86
  writeSecretJsonFile
87
- } from "./cli-drabjmaj.js";
87
+ } from "./cli-q79sq80a.js";
88
+ import {
89
+ __require
90
+ } from "./cli-0v6na3yp.js";
88
91
 
89
92
  // src/cli.ts
90
93
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -210,6 +213,13 @@ var ProblemFeedbackSchema = z.object({
210
213
  new_name: z.string().max(255).optional(),
211
214
  actor: z.string().max(255).optional()
212
215
  });
216
+ var ProblemLinearFileSchema = z.object({
217
+ id: z.string().min(1),
218
+ issue: z.string().max(2048).optional(),
219
+ team_id: z.string().max(255).optional(),
220
+ title: z.string().optional(),
221
+ priority: z.preprocess((v) => typeof v === "string" && /^\d+$/.test(v.trim()) ? Number(v) : v, z.number().int().min(0).max(4).optional())
222
+ });
213
223
  var SignalSchema = z.object({
214
224
  id: z.string().min(1).max(255),
215
225
  detections: boolFlag(),
@@ -234,6 +244,46 @@ var DetectionReviewSchema = z.object({
234
244
  verdict: z.enum(DETECTION_VERDICTS),
235
245
  note: z.string().max(2000).optional()
236
246
  });
247
+ var FALSE_POSITIVE_KINDS = ["problem", "problem-detection", "quote", "tool", "laziness", "hallucination", "custom-signal"];
248
+ var FalsePositiveSchema = z.object({
249
+ kind: z.enum(FALSE_POSITIVE_KINDS),
250
+ target: z.string().min(1).max(255),
251
+ undo: z.boolean(),
252
+ reason: z.string().max(2000).optional(),
253
+ conversation_id: z.string().min(1).max(255).optional(),
254
+ attribution_id: z.string().regex(UUID_RE, "Attribution id must be a UUID").optional(),
255
+ family: z.string().regex(/^[a-z_]{1,64}$/, "Family must be a lowercase emotion family").optional(),
256
+ turn: z.preprocess((v) => typeof v === "string" && /^\d+$/.test(v.trim()) ? Number(v) : v, z.number().int().min(0).optional()),
257
+ quote: z.string().min(1).max(4000).optional(),
258
+ tool: z.string().min(1).max(255).optional(),
259
+ tool_use_id: z.string().min(1).max(255).optional(),
260
+ msg_index: z.preprocess((v) => typeof v === "string" && /^\d+$/.test(v.trim()) ? Number(v) : v, z.number().int().min(0).optional()),
261
+ msg_dedup_token: z.string().min(1).max(200).optional()
262
+ }).superRefine((v, ctx) => {
263
+ const need = (ok, message) => {
264
+ if (!ok)
265
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message });
266
+ };
267
+ if (v.kind === "problem") {
268
+ need(UUID_RE.test(v.target), "Problem id must be a canonical UUID");
269
+ need(!v.undo, "A problem dismissal cannot be undone from the CLI");
270
+ }
271
+ if (v.kind === "problem-detection") {
272
+ need(UUID_RE.test(v.target), "Problem id must be a canonical UUID");
273
+ need(v.conversation_id, "--conversation-id is required for problem-detection");
274
+ need(v.attribution_id, "--attribution-id is required for problem-detection");
275
+ }
276
+ if (v.kind === "quote") {
277
+ need(v.family && v.turn !== undefined && v.quote, "--family, --turn and --quote are required for quote (copy them from moda emotions)");
278
+ }
279
+ if (v.kind === "tool") {
280
+ need(v.tool, "--tool is required for tool (the failing tool name)");
281
+ need(v.tool_use_id || v.msg_index !== undefined, "--tool-use-id or --msg-index is required for tool (from moda tool-failure-detail)");
282
+ }
283
+ if (v.kind === "custom-signal") {
284
+ need(v.conversation_id, "--conversation-id is required for custom-signal");
285
+ }
286
+ });
237
287
  var EMOTION_FAMILIES = [
238
288
  "frustration",
239
289
  "sadness",
@@ -255,6 +305,13 @@ var HallucinationsSchema = z.object({
255
305
  conversation_id: z.string().max(200).optional(),
256
306
  kind: z.enum(["contradicted", "verified"]).optional()
257
307
  });
308
+ var LazinessSchema = z.object({
309
+ days_back: z.number().min(1).max(90).default(7).optional(),
310
+ limit: z.number().min(1).max(50).default(10).optional(),
311
+ offset: z.number().min(0).max(1e4).optional(),
312
+ conversation_id: z.string().max(200).optional(),
313
+ family: z.string().regex(/^[a-z_]{1,64}$/, "Family must be a lowercase laziness family").optional()
314
+ });
258
315
  var StepScoresSchema = z.object({
259
316
  conversation_id: z.string().min(1)
260
317
  });
@@ -2854,11 +2911,26 @@ async function runAuthCommand(context) {
2854
2911
  return { exitCode: EXIT_AUTH_REQUIRED };
2855
2912
  }
2856
2913
  process.stdout.write(`${session.access_token}
2914
+ `);
2915
+ return { exitCode: EXIT_OK };
2916
+ }
2917
+ case "api-key": {
2918
+ const credential = resolveApiKeyCredential({ profile: context.profile, env: context.env });
2919
+ if (!credential) {
2920
+ if (context.flags.json === "true") {
2921
+ process.stdout.write(`${JSON.stringify({ error: "no_api_key" })}
2922
+ `);
2923
+ } else {
2924
+ console.error('No API key saved. Run "moda init" (or "moda auth login --api-key-stdin").');
2925
+ }
2926
+ return { exitCode: EXIT_AUTH_REQUIRED };
2927
+ }
2928
+ process.stdout.write(`${credential.key}
2857
2929
  `);
2858
2930
  return { exitCode: EXIT_OK };
2859
2931
  }
2860
2932
  default:
2861
- throw unknownSubcommandError("auth", subcommand(context), ["login", "logout", "status", "token", "whoami"]);
2933
+ throw unknownSubcommandError("auth", subcommand(context), ["api-key", "login", "logout", "status", "token", "whoami"]);
2862
2934
  }
2863
2935
  }
2864
2936
  async function runProfilesCommand(context) {
@@ -5326,6 +5398,19 @@ function parseFlag(args, index, flags, beforeCommand) {
5326
5398
  flags[key] = "true";
5327
5399
  return index;
5328
5400
  }
5401
+ function initFlagsForContext(flags, outputMode, stdinIsTTY) {
5402
+ const args = flagsToArgs(flags);
5403
+ const explicitMode = args.json === true || args.json === "true" || args.agent === true || args.agent === "true";
5404
+ if (explicitMode)
5405
+ return args;
5406
+ if (outputMode === "json")
5407
+ return { ...args, json: true };
5408
+ if (outputMode !== "human")
5409
+ return { ...args, agent: true };
5410
+ if (!stdinIsTTY)
5411
+ return { ...args, non_interactive: true };
5412
+ return args;
5413
+ }
5329
5414
  function flagsToArgs(flags, positionalKey, positionalValue) {
5330
5415
  const result = {};
5331
5416
  if (positionalKey && positionalValue) {
@@ -5358,6 +5443,7 @@ Commands:
5358
5443
  auth login --api-key Non-interactive: validate MODA_API_KEY (or --api-key=<key>) and save it
5359
5444
  auth status Show auth state for both credentials (exit 4 when neither is usable); --online probes the API
5360
5445
  auth token Print the raw bearer token for $(moda auth token)
5446
+ auth api-key Print the saved API key for $(moda auth api-key)
5361
5447
  auth logout Clear cached Moda auth
5362
5448
  auth whoami Show the authenticated Moda identity (session or API key)
5363
5449
  profiles list List local Moda profiles
@@ -5401,12 +5487,17 @@ Commands:
5401
5487
  frustrations Get user frustration detections (legacy single-family; see emotions)
5402
5488
  emotions Multi-family emotion detections (frustration, sadness, confusion, anxiety, trust, positive)
5403
5489
  hallucinations Grounding detections: contradicted/verified outputs with rule breakdown
5490
+ laziness Laziness detections per trace (repeated calls, premature handoffs, ...)
5404
5491
  tool-failures Get tool failure overview
5405
5492
  tool-failure-detail <tool_name> Get per-tool failure detail
5406
5493
  problems Rank cross-signal Problems by root cause (what to fix first)
5407
5494
  problem-impact Live problem/use-case impact (--node-id, --problem-id, --mode, --days-back)
5408
5495
  problem <problem_id> Open one Problem: dossier, or --evidence/--reports/--traces/--feedback
5409
5496
  problem-feedback <problem_id> Mark a Problem fixed, dismiss, rename, or flag a bad attribution
5497
+ false-positive <kind> <target> Mark any detection as a false positive, optional --reason (problem, problem-detection, quote, tool, laziness, hallucination, custom-signal)
5498
+ false-positives [conv_id] Current false-positive marks: all (--kind, --all, --limit) or one trace
5499
+ problem-linear <problem_id> Show the Linear issue linked to a Problem (key, state, URL)
5500
+ problem-linear-file <problem_id> File a Problem as a Linear issue, or link an existing one (--issue)
5410
5501
  detection-reviews <conv_id> Problem detections on a trace with their review state
5411
5502
  detection-review <problem_id> Mark a problem detection correct, a false positive, or clear the review
5412
5503
  signals List custom signals (name, criteria, status, 7-day stats)
@@ -5435,6 +5526,7 @@ Commands:
5435
5526
  skills gen Kick off tenant-wide skill generation
5436
5527
  skills status [run-id] Show a generation run's status, outcome, and recent events (default: latest run)
5437
5528
  skills pull Pull tenant-generated skills into .claude/skills (and .cursor/rules)
5529
+ skills install <id...> Install bundled integration guides into .claude/skills (--list to browse)
5438
5530
  skills pull --status=proposed Include not-yet-promoted skills (default: approved)
5439
5531
  skills sync Push local .claude/skills/**/SKILL.md up to Moda
5440
5532
  skills sync --dry-run Show what would be pushed without writing
@@ -5516,6 +5608,11 @@ Emotions flags:
5516
5608
  --days-back=N --limit=N Window 1-90 (default 7); page size 1-20 (default 10)
5517
5609
  --offset=N Skip N detections (0-10000)
5518
5610
 
5611
+ Laziness flags:
5612
+ --family=F Narrow to one laziness family
5613
+ --conversation-id=ID One trace's detections
5614
+ --days-back=N --limit=N Window 1-90 (default 7); page size 1-50 (default 10)
5615
+
5519
5616
  Hallucinations flags:
5520
5617
  --kind=contradicted|verified Narrow the detections list
5521
5618
  --conversation-id=ID Scope summary + list to one trace ID
@@ -5529,10 +5626,28 @@ Problem flags (moda problem <id>):
5529
5626
 
5530
5627
  Problem-feedback flags:
5531
5628
  --action=A mark_fixed|dismiss|flag_attribution|rename (required)
5532
- --reason=TEXT Required for dismiss and flag_attribution
5629
+ --reason=TEXT Required for flag_attribution; optional for dismiss
5533
5630
  --attribution-id=UUID Required for flag_attribution (ids come from problem --evidence)
5534
5631
  --new-name=NAME Required for rename
5535
5632
 
5633
+ False-positive flags (moda false-positive <kind> <target>):
5634
+ --reason=TEXT Why it is wrong (optional)
5635
+ --undo Remove an earlier mark (every kind except problem)
5636
+ problem <problem_id> Dismiss the problem: hidden at once, logged for review, folded into reconcile
5637
+ problem-detection <problem_id> One detection on a trace; needs --conversation-id and --attribution-id; teaches the problem
5638
+ quote <conversation_id> A user quote; needs --family, --turn and --quote (from moda emotions); audit only
5639
+ tool <conversation_id> One failing tool call; needs --tool and --tool-use-id (or --msg-index); audit only
5640
+ laziness <conversation_id> Laziness on that trace; audit only
5641
+ hallucination <conversation_id> Hallucinations on that trace; audit only
5642
+ custom-signal <signal_id> A hit on that trace (--conversation-id); saved as does_not_fit so the signal learns
5643
+
5644
+ Problem-linear-file flags (needs Linear connected: Integrations -> Linear):
5645
+ --issue=ENG-123|URL Link an existing Linear issue instead of filing a new one
5646
+ --team-id=ID File into this Linear team (default: the integration's default team)
5647
+ --title=TEXT Issue title (default: the Problem's name)
5648
+ --priority=N 0 none, 1 urgent, 2 high, 3 medium, 4 low
5649
+ --issue excludes --team-id/--title/--priority
5650
+
5536
5651
  Detection-review flags (moda detection-review <problem_id>):
5537
5652
  --conversation-id=ID Trace the detection is on (required)
5538
5653
  --attribution-id=UUID Detection id (from problem --evidence or detection-reviews; required)
@@ -5604,6 +5719,8 @@ Init flags:
5604
5719
  --no-tui Show raw external analyst output
5605
5720
  --sdk-integrate Run your coding agent to install the Moda SDK and instrument LLM calls
5606
5721
  --no-sdk-integrate Skip SDK integration (alias: --no-sdk-guide)
5722
+ --verification-id=ID Tag the SDK integration's test trace with this id
5723
+ (the onb_ id from the Moda onboarding page)
5607
5724
  --ci-cd Create GitHub Actions Moda validation workflow
5608
5725
  --no-ci-cd Skip CI/CD workflow creation
5609
5726
  --autosync Create GitHub Actions workflow to sync prompts + skills to Moda on push to main
@@ -5675,6 +5792,8 @@ Examples:
5675
5792
  moda problems --days-back=7
5676
5793
  moda problem <problem_id> --evidence --limit=10
5677
5794
  moda problem-feedback <problem_id> --action=mark_fixed
5795
+ moda problem-linear <problem_id>
5796
+ moda problem-linear-file <problem_id> --priority=2
5678
5797
  moda detection-reviews <conversation_id>
5679
5798
  moda detection-review <problem_id> --conversation-id=<id> --attribution-id=<uuid> --verdict=false_positive --note="expected retry"
5680
5799
  moda signals
@@ -6032,6 +6151,50 @@ function detectCallerAgent(env) {
6032
6151
  return "agent";
6033
6152
  return "human";
6034
6153
  }
6154
+ var LINEAR_CONNECT_GUIDANCE = "Connect Linear in the Moda dashboard (Integrations -> Linear), then retry.";
6155
+ function describeLinearLink(linkValue) {
6156
+ const link = asRecord(linkValue);
6157
+ if (!link)
6158
+ return "No Linear issue linked to this Problem.";
6159
+ const issue = asRecord(link.issue);
6160
+ if (!issue) {
6161
+ return "A Linear issue is still being created for this Problem; re-run shortly.";
6162
+ }
6163
+ return [asString(issue.identifier), asString(issue.stateName), asString(issue.url)].filter((part) => part !== null && part !== "").join(" · ");
6164
+ }
6165
+ function linearApiError(error) {
6166
+ if (!(error instanceof ApiError))
6167
+ return error;
6168
+ let code;
6169
+ let message;
6170
+ try {
6171
+ const body = asRecord(JSON.parse(error.responseBody));
6172
+ code = asString(body?.error) ?? undefined;
6173
+ message = asString(body?.message) ?? undefined;
6174
+ } catch {
6175
+ code = undefined;
6176
+ }
6177
+ if (error.statusCode === 404 && code === "linear_not_enabled") {
6178
+ return new CliAccessError("The Linear integration is not enabled for this tenant.", {
6179
+ guidance: LINEAR_CONNECT_GUIDANCE
6180
+ });
6181
+ }
6182
+ if (error.statusCode === 404 && code === "problem_not_found") {
6183
+ return new CliInputError(`${message ?? "Problem not found"}.`, "Run `moda problems` to list problems with their canonical ids.");
6184
+ }
6185
+ if (error.statusCode === 409 && code === "not_connected") {
6186
+ return new CliAccessError("Linear is not connected, or a disconnect is still in progress.", {
6187
+ guidance: `If Linear was just disconnected, wait for that to finish. ${LINEAR_CONNECT_GUIDANCE}`
6188
+ });
6189
+ }
6190
+ if (error.statusCode === 409 && code === "needs_reauth") {
6191
+ return new CliAccessError("Linear needs to be reconnected.", { guidance: LINEAR_CONNECT_GUIDANCE });
6192
+ }
6193
+ if (error.statusCode === 400 && code === "no_team") {
6194
+ return new CliInputError("No default Linear team is configured.", "Pass --team-id=ID, or set a default team in Integrations -> Linear.");
6195
+ }
6196
+ return error;
6197
+ }
6035
6198
  async function runCommand(command, positional, flags, positionals = positional ? [positional] : [], context = createCommandContext({ command, positional, positionals, flags })) {
6036
6199
  switch (command) {
6037
6200
  case "prompts": {
@@ -6404,7 +6567,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
6404
6567
  throw new CliInputError(`Problem id must be a canonical UUID; got "${params.id}".`);
6405
6568
  }
6406
6569
  const reason = params.reason?.trim() ?? "";
6407
- if ((params.action === "dismiss" || params.action === "flag_attribution") && reason === "") {
6570
+ if (params.action === "flag_attribution" && reason === "") {
6408
6571
  throw new CliInputError(`--reason is required for --action=${params.action}.`);
6409
6572
  }
6410
6573
  if (params.action === "rename" && (params.new_name?.trim() ?? "") === "") {
@@ -6432,6 +6595,72 @@ async function runCommand(command, positional, flags, positionals = positional ?
6432
6595
  context.output.writeData(data);
6433
6596
  break;
6434
6597
  }
6598
+ case "problem-linear": {
6599
+ if (!positional) {
6600
+ throw new CliInputError("<problem_id> is required", "Usage: moda problem-linear <problem_id>");
6601
+ }
6602
+ if (!UUID_RE.test(positional)) {
6603
+ throw new CliInputError(`Problem id must be a canonical UUID; got "${positional}". Run \`moda problems\` to list problems with their canonical ids.`);
6604
+ }
6605
+ const data = await callDataAPI(`/problems/${positional}/linear`).catch((error) => {
6606
+ throw linearApiError(error);
6607
+ });
6608
+ const link = asRecord(data)?.link;
6609
+ const summary = describeLinearLink(link);
6610
+ const nextCommands = link ? [] : [`moda problem-linear-file ${positional}`];
6611
+ if (context.outputMode === "human" && !context.quiet) {
6612
+ safeWriteStderr(link ? `${summary}
6613
+ ` : `${summary} File one with: moda problem-linear-file ${positional}
6614
+ `);
6615
+ }
6616
+ context.output.writeData(data, { summary, nextCommands });
6617
+ break;
6618
+ }
6619
+ case "problem-linear-file": {
6620
+ const usage = "Usage: moda problem-linear-file <problem_id> [--issue=ENG-123|URL] [--team-id=ID] [--title=TEXT] [--priority=0-4]";
6621
+ if (!positional) {
6622
+ throw new CliInputError("<problem_id> is required", usage);
6623
+ }
6624
+ const params = ProblemLinearFileSchema.parse({
6625
+ id: positional,
6626
+ ...flags.issue !== undefined ? { issue: flags.issue } : {},
6627
+ ...flags["team-id"] !== undefined ? { team_id: flags["team-id"] } : {},
6628
+ ...flags.title !== undefined ? { title: flags.title } : {},
6629
+ ...flags.priority !== undefined ? { priority: flags.priority } : {}
6630
+ });
6631
+ if (!UUID_RE.test(params.id)) {
6632
+ throw new CliInputError(`Problem id must be a canonical UUID; got "${params.id}". Run \`moda problems\` to list problems with their canonical ids.`);
6633
+ }
6634
+ const issue = params.issue?.trim() ?? "";
6635
+ if (params.issue !== undefined && issue === "") {
6636
+ throw new CliInputError("--issue must be a Linear issue key (ENG-123) or URL.", usage);
6637
+ }
6638
+ if (issue !== "" && (params.team_id !== undefined || params.title !== undefined || params.priority !== undefined)) {
6639
+ throw new CliInputError("--issue links an existing issue and cannot be combined with --team-id, --title, or --priority.", usage);
6640
+ }
6641
+ const title = params.title?.trim() ?? "";
6642
+ const teamId = params.team_id?.trim() ?? "";
6643
+ const data = await callDataAPI(`/problems/${params.id}/linear`, {
6644
+ method: "POST",
6645
+ body: JSON.stringify(issue !== "" ? { issue } : {
6646
+ ...teamId !== "" ? { team_id: teamId } : {},
6647
+ ...title !== "" ? { title } : {},
6648
+ ...params.priority !== undefined ? { priority: params.priority } : {}
6649
+ }),
6650
+ headers: { "Content-Type": "application/json" }
6651
+ }, { retries: 0 }).catch((error) => {
6652
+ throw linearApiError(error);
6653
+ });
6654
+ const record = asRecord(data);
6655
+ const verb = issue !== "" ? "Linked" : record?.created === true ? "Filed" : "Already linked";
6656
+ const summary = `${verb}: ${describeLinearLink(record?.link)}`;
6657
+ if (context.outputMode === "human" && !context.quiet) {
6658
+ safeWriteStderr(`${summary}
6659
+ `);
6660
+ }
6661
+ context.output.writeData(data, { summary });
6662
+ break;
6663
+ }
6435
6664
  case "signals": {
6436
6665
  const data = await callDataAPI("/custom-signals");
6437
6666
  context.output.writeData(data);
@@ -6538,6 +6767,99 @@ async function runCommand(command, positional, flags, positionals = positional ?
6538
6767
  context.output.writeData(data);
6539
6768
  break;
6540
6769
  }
6770
+ case "false-positive": {
6771
+ const usage = `Usage: moda false-positive <${FALSE_POSITIVE_KINDS.join("|")}> <target> --reason=TEXT [--undo]`;
6772
+ const [kind, target] = positionals;
6773
+ if (!kind || !target)
6774
+ throw new CliInputError("<kind> and <target> are required", usage);
6775
+ const params = FalsePositiveSchema.parse({
6776
+ kind,
6777
+ target,
6778
+ undo: flags.undo !== undefined && flags.undo !== "false",
6779
+ ...flags.reason !== undefined ? { reason: flags.reason } : {},
6780
+ ...flags["conversation-id"] !== undefined ? { conversation_id: flags["conversation-id"] } : {},
6781
+ ...flags["attribution-id"] !== undefined ? { attribution_id: flags["attribution-id"] } : {},
6782
+ ...flags.family !== undefined ? { family: flags.family } : {},
6783
+ ...flags.turn !== undefined ? { turn: flags.turn } : {},
6784
+ ...flags.quote !== undefined ? { quote: flags.quote } : {},
6785
+ ...flags.tool !== undefined ? { tool: flags.tool } : {},
6786
+ ...flags["tool-use-id"] !== undefined ? { tool_use_id: flags["tool-use-id"] } : {},
6787
+ ...flags["msg-index"] !== undefined ? { msg_index: flags["msg-index"] } : {},
6788
+ ...flags["msg-dedup-token"] !== undefined ? { msg_dedup_token: flags["msg-dedup-token"] } : {}
6789
+ });
6790
+ const reason = params.reason?.trim() ?? "";
6791
+ const post = (path, body, retries) => callDataAPI(path, { method: "POST", body: JSON.stringify(body), headers: { "Content-Type": "application/json" } }, retries === undefined ? undefined : { retries });
6792
+ let data;
6793
+ switch (params.kind) {
6794
+ case "problem":
6795
+ data = await post(`/problems/${params.target}/feedback`, { action: "dismiss", reason, actor: "moda-cli" }, 0);
6796
+ break;
6797
+ case "problem-detection":
6798
+ data = await post(`/problems/${params.target}/detection-reviews`, {
6799
+ conversation_id: params.conversation_id,
6800
+ attribution_id: params.attribution_id,
6801
+ verdict: params.undo ? "clear" : "false_positive",
6802
+ ...params.undo ? {} : { note: reason },
6803
+ request_nonce: randomUUID2()
6804
+ });
6805
+ break;
6806
+ case "custom-signal":
6807
+ if (params.undo) {
6808
+ data = await callDataAPI(`/custom-signals/${encodeURIComponent(params.target)}/labels/${encodeURIComponent(params.conversation_id ?? "")}`, { method: "DELETE" }, { retries: 0 });
6809
+ break;
6810
+ }
6811
+ data = await post(`/custom-signals/${encodeURIComponent(params.target)}/labels`, {
6812
+ conversation_id: params.conversation_id,
6813
+ label: "does_not_fit",
6814
+ note: reason,
6815
+ ...params.msg_index !== undefined ? { msg_index: params.msg_index } : {},
6816
+ ...params.msg_dedup_token ? { msg_dedup_token: params.msg_dedup_token } : {}
6817
+ }, 0);
6818
+ break;
6819
+ default: {
6820
+ const apiKind = { quote: "frustration_quote", tool: "tool_call", laziness: "laziness", hallucination: "hallucination" }[params.kind];
6821
+ data = await post("/false-positives", {
6822
+ kind: apiKind,
6823
+ verdict: params.undo ? "clear" : "false_positive",
6824
+ ...params.undo ? {} : { note: reason },
6825
+ conversation_id: params.target,
6826
+ ...params.kind === "tool" ? {
6827
+ tool_name: params.tool,
6828
+ ...params.tool_use_id ? { tool_use_id: params.tool_use_id } : { msg_index: params.msg_index }
6829
+ } : {},
6830
+ ...params.kind === "quote" ? { family: params.family, turn: params.turn, quote: params.quote } : {},
6831
+ request_nonce: randomUUID2()
6832
+ });
6833
+ }
6834
+ }
6835
+ context.output.writeData(data);
6836
+ break;
6837
+ }
6838
+ case "false-positives": {
6839
+ if (!positional) {
6840
+ const kinds = { quote: "frustration_quote", tool: "tool_call", laziness: "laziness", hallucination: "hallucination", "custom-signal": "custom_signal" };
6841
+ const query = new URLSearchParams;
6842
+ if (flags.kind !== undefined) {
6843
+ if (!kinds[flags.kind])
6844
+ throw new CliInputError(`--kind must be one of ${Object.keys(kinds).join("|")}`);
6845
+ query.set("kind", kinds[flags.kind]);
6846
+ }
6847
+ if (flags.limit !== undefined) {
6848
+ if (!/^\d+$/.test(flags.limit) || Number(flags.limit) < 1 || Number(flags.limit) > 200)
6849
+ throw new CliInputError("--limit must be 1-200");
6850
+ query.set("limit", flags.limit);
6851
+ }
6852
+ if (flags.all !== undefined && flags.all !== "false")
6853
+ query.set("include_cleared", "true");
6854
+ const queryString = query.toString() ? `?${query.toString()}` : "";
6855
+ const data2 = await callDataAPI(`/false-positives${queryString}`);
6856
+ context.output.writeData(data2);
6857
+ break;
6858
+ }
6859
+ const data = await callDataAPI(`/conversations/${encodeURIComponent(positional)}/false-positives`);
6860
+ context.output.writeData(data);
6861
+ break;
6862
+ }
6541
6863
  case "emotions": {
6542
6864
  const args = flagsToArgs(flags);
6543
6865
  const params = EmotionsSchema.parse(args);
@@ -6555,6 +6877,28 @@ async function runCommand(command, positional, flags, positionals = positional ?
6555
6877
  context.output.writeData(data);
6556
6878
  break;
6557
6879
  }
6880
+ case "laziness": {
6881
+ const args = flagsToArgs(flags);
6882
+ if (flags["conversation-id"] !== undefined) {
6883
+ args.conversation_id = flags["conversation-id"];
6884
+ }
6885
+ const params = LazinessSchema.parse(args);
6886
+ const query = new URLSearchParams;
6887
+ if (params.days_back)
6888
+ query.set("days_back", params.days_back.toString());
6889
+ if (params.limit)
6890
+ query.set("limit", params.limit.toString());
6891
+ if (params.offset !== undefined)
6892
+ query.set("offset", params.offset.toString());
6893
+ if (params.conversation_id)
6894
+ query.set("conversation_id", params.conversation_id);
6895
+ if (params.family)
6896
+ query.set("family", params.family);
6897
+ const queryString = query.toString() ? `?${query.toString()}` : "";
6898
+ const data = await callDataAPI(`/laziness${queryString}`);
6899
+ context.output.writeData(data);
6900
+ break;
6901
+ }
6558
6902
  case "hallucinations": {
6559
6903
  const args = flagsToArgs(flags);
6560
6904
  if (flags["conversation-id"] !== undefined) {
@@ -6740,7 +7084,7 @@ var commandRegistry = createCommandRegistry([
6740
7084
  resetApiRequestCountBeforeRun: true,
6741
7085
  telemetry: "result",
6742
7086
  handler: async (context) => {
6743
- const { runInit } = await import("./index-pf2hvadv.js");
7087
+ const { runInit } = await import("./index-vmmfya6b.js");
6744
7088
  if (context.outputMode === "agent-stream") {
6745
7089
  context.output.writeEvent({
6746
7090
  event: "started",
@@ -6749,7 +7093,7 @@ var commandRegistry = createCommandRegistry([
6749
7093
  elapsed_ms: Date.now() - context.startedAt
6750
7094
  });
6751
7095
  }
6752
- const result = await runInit(flagsToArgs(context.flags), {
7096
+ const result = await runInit(initFlagsForContext(context.flags, context.outputMode, process.stdin.isTTY === true), {
6753
7097
  onEvent: (event2) => {
6754
7098
  context.output.writeEvent({
6755
7099
  event: event2.status === "warning" ? "warning" : "progress",
@@ -6781,7 +7125,7 @@ var commandRegistry = createCommandRegistry([
6781
7125
  resetApiRequestCountBeforeRun: true,
6782
7126
  telemetry: "result-and-error",
6783
7127
  handler: async (context) => {
6784
- const { runProvision } = await import("./provision-d242y9qd.js");
7128
+ const { runProvision } = await import("./provision-spwn5t41.js");
6785
7129
  return runProvision(context, {
6786
7130
  resumeCommand: buildResumeCommand("provision", context.rawFlags)
6787
7131
  });
@@ -6820,6 +7164,7 @@ var commandRegistry = createCommandRegistry([
6820
7164
  "moda auth status",
6821
7165
  "moda auth status --online",
6822
7166
  "moda auth token",
7167
+ "moda auth api-key",
6823
7168
  "moda auth whoami",
6824
7169
  "moda auth logout"
6825
7170
  ],
@@ -6881,6 +7226,15 @@ var commandRegistry = createCommandRegistry([
6881
7226
  usage: "moda auth token",
6882
7227
  examples: ["moda auth token", 'curl -H "Authorization: Bearer $(moda auth token)" ...']
6883
7228
  },
7229
+ {
7230
+ name: "api-key",
7231
+ description: "Print the saved API key for $(moda auth api-key); pipe it, never echo it",
7232
+ usage: "moda auth api-key",
7233
+ examples: [
7234
+ 'MODA_API_KEY="$(moda auth api-key)" npm run dev',
7235
+ "moda auth api-key | wrangler secret put MODA_API_KEY"
7236
+ ]
7237
+ },
6884
7238
  {
6885
7239
  name: "logout",
6886
7240
  description: "Revoke and clear the browser session and any stored API key",
@@ -7161,6 +7515,23 @@ var commandRegistry = createCommandRegistry([
7161
7515
  ...dataApiDefaults,
7162
7516
  mutability: "write"
7163
7517
  }),
7518
+ legacyCommand({
7519
+ name: "problem-linear",
7520
+ description: "Show the Linear issue linked to a Problem (key, state, URL); needs Linear connected (Integrations -> Linear)",
7521
+ examples: ["moda problem-linear <problem_id>"],
7522
+ ...dataApiDefaults
7523
+ }),
7524
+ legacyCommand({
7525
+ name: "problem-linear-file",
7526
+ description: "File a Problem as a Linear issue in the team's tracker, or link an existing issue with --issue; needs Linear connected (Integrations -> Linear)",
7527
+ examples: [
7528
+ "moda problem-linear-file <problem_id>",
7529
+ 'moda problem-linear-file <problem_id> --team-id=<team_id> --title="CRM lookups time out" --priority=2',
7530
+ "moda problem-linear-file <problem_id> --issue=ENG-123"
7531
+ ],
7532
+ ...dataApiDefaults,
7533
+ mutability: "write"
7534
+ }),
7164
7535
  legacyCommand({
7165
7536
  name: "detection-reviews",
7166
7537
  description: "List problem detections on a trace with their review state (correct / false_positive / clear)",
@@ -7181,6 +7552,32 @@ var commandRegistry = createCommandRegistry([
7181
7552
  ...dataApiDefaults,
7182
7553
  mutability: "write"
7183
7554
  }),
7555
+ legacyCommand({
7556
+ name: "false-positive",
7557
+ description: "Flag a detection as a false positive with a reason: problem, problem-detection, quote, tool, laziness, hallucination, or custom-signal",
7558
+ examples: [
7559
+ 'moda false-positive problem <problem_id> --reason="two unrelated tools"',
7560
+ 'moda false-positive problem-detection <problem_id> --conversation-id=<id> --attribution-id=<uuid> --reason="refund was approved"',
7561
+ 'moda false-positive quote <conversation_id> --family=frustration --turn=3 --quote="this is useless lol" --reason="joking"',
7562
+ 'moda false-positive tool <conversation_id> --tool=send_invoice --tool-use-id=<id> --reason="retried upstream"',
7563
+ 'moda false-positive laziness <conversation_id> --reason="reload after context reset is expected"',
7564
+ "moda false-positive hallucination <conversation_id> --undo",
7565
+ "moda false-positive custom-signal <signal_id> --conversation-id=<id> --undo",
7566
+ 'moda false-positive custom-signal <signal_id> --conversation-id=<id> --reason="refund was approved"'
7567
+ ],
7568
+ ...dataApiDefaults,
7569
+ mutability: "write"
7570
+ }),
7571
+ legacyCommand({
7572
+ name: "false-positives",
7573
+ description: "List current false-positive marks across the workspace, or on one trace (with problem detection reviews)",
7574
+ examples: [
7575
+ "moda false-positives",
7576
+ "moda false-positives --kind=tool --all",
7577
+ "moda false-positives <conversation_id>"
7578
+ ],
7579
+ ...dataApiDefaults
7580
+ }),
7184
7581
  legacyCommand({
7185
7582
  name: "signals",
7186
7583
  description: "List custom signals with criteria, status, and 7-day judged/matched stats",
@@ -7221,6 +7618,15 @@ var commandRegistry = createCommandRegistry([
7221
7618
  ],
7222
7619
  ...dataApiDefaults
7223
7620
  }),
7621
+ legacyCommand({
7622
+ name: "laziness",
7623
+ description: "Laziness detections per trace: repeated tool calls, premature handoffs, and other lazy patterns",
7624
+ examples: [
7625
+ "moda laziness --days-back=30",
7626
+ "moda laziness --conversation-id=<id>"
7627
+ ],
7628
+ ...dataApiDefaults
7629
+ }),
7224
7630
  legacyCommand({
7225
7631
  name: "hallucinations",
7226
7632
  description: "Grounding detections: contradicted/verified outputs with rule breakdown",
@@ -7341,7 +7747,14 @@ var commandRegistry = createCommandRegistry([
7341
7747
  {
7342
7748
  name: "skills",
7343
7749
  description: "Generate, pull, and sync Moda skills",
7344
- examples: ["moda skills gen", "moda skills status", "moda skills pull", "moda skills sync"],
7750
+ examples: [
7751
+ "moda skills gen",
7752
+ "moda skills status",
7753
+ "moda skills pull",
7754
+ "moda skills sync",
7755
+ "moda skills install --list",
7756
+ "moda skills install integration-node-openai"
7757
+ ],
7345
7758
  auth: "none",
7346
7759
  outputModes: ["human", "json", "agent"],
7347
7760
  mutability: "write",
@@ -7662,9 +8075,11 @@ export {
7662
8075
  resolveTailSignal,
7663
8076
  printCliError,
7664
8077
  parseArgs,
8078
+ initFlagsForContext,
7665
8079
  flagsToArgs,
7666
8080
  dispatchParsedCommand,
7667
8081
  detectCallerAgent,
8082
+ describeLinearLink,
7668
8083
  commandRegistry,
7669
8084
  classifyCliError,
7670
8085
  buildResumeCommand,
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  buildSourceSnapshot
3
- } from "./cli-0p6n391h.js";
4
- import"./cli-drabjmaj.js";
3
+ } from "./cli-07tv75te.js";
4
+ import"./cli-q79sq80a.js";
5
+ import"./cli-0v6na3yp.js";
5
6
 
6
7
  // src/harness-github-actions.ts
7
8
  var DEFAULT_MODA_GITHUB_URL = "https://moda-github.modas.workers.dev";