@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/README.md +2 -0
- package/dist/{cli-0p6n391h.js → cli-07tv75te.js} +21 -6
- package/dist/cli-0v6na3yp.js +35 -0
- package/dist/cli-c5c6f0yw.js +128 -0
- package/dist/{cli-7qkhy41j.js → cli-ert1sbbt.js} +112 -444
- package/dist/{cli-drabjmaj.js → cli-q79sq80a.js} +1 -35
- package/dist/{cli-scjxgaba.js → cli-qgf2cz2v.js} +2 -2
- package/dist/cli-ssfq84k5.js +393 -0
- package/dist/{cli-yz99t7n8.js → cli-strngwn3.js} +4 -2
- package/dist/cli.js +427 -12
- package/dist/{harness-github-actions-tk17mnge.js → harness-github-actions-qz5d22rz.js} +3 -2
- package/dist/{harness-heasma1f.js → harness-n2xgzz31.js} +3 -2
- package/dist/{index-pf2hvadv.js → index-vmmfya6b.js} +167 -207
- package/dist/prompt-source-mcp.js +3 -2
- package/dist/{provision-d242y9qd.js → provision-spwn5t41.js} +4 -3
- package/dist/skills-bundled-mdmhzfsr.js +14 -0
- package/package.json +1 -1
- package/skills/integration/index.json +3 -3
- package/skills/integration/integration-python-langchain/EXAMPLE.md +1 -1
- package/skills/integration/integration-python-langchain/SKILL.md +44 -11
- package/skills/moda-cli/SKILL.md +47 -3
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
runPromptsCommand,
|
|
9
9
|
runSkillsCommand,
|
|
10
10
|
runStatusCommand
|
|
11
|
-
} from "./cli-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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 (
|
|
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-
|
|
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(
|
|
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-
|
|
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: [
|
|
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-
|
|
4
|
-
import"./cli-
|
|
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";
|