@moda-ai/cli 1.55.23 → 1.55.25
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/{agent-f8a1kejk.js → agent-t7dxmpdq.js} +8 -8
- package/dist/{cli-9g8vz4sv.js → cli-0wr3483h.js} +1 -1
- package/dist/{cli-4f6jkrnm.js → cli-7xc585hc.js} +5 -5
- package/dist/{cli-wkmdw40s.js → cli-9btx12wp.js} +1 -1
- package/dist/{cli-4yarp5s1.js → cli-9hc5bg2r.js} +1 -1
- package/dist/{cli-cv3jt1ms.js → cli-dn91fb56.js} +10 -10
- package/dist/{cli-t3a6eknn.js → cli-dve50x7p.js} +233 -28
- package/dist/{cli-54zhv6cx.js → cli-m0045npe.js} +17 -14
- package/dist/{cli-s6tw1fje.js → cli-m805msp6.js} +210 -23
- package/dist/{cli-z3g07t65.js → cli-mebf5vtr.js} +5 -5
- package/dist/{cli-2cggp8ww.js → cli-qvg26twp.js} +5 -4
- package/dist/{cli-d2zews6c.js → cli-qycsmxev.js} +3 -3
- package/dist/cli.js +1292 -335
- package/dist/{doctor-snb87jvy.js → doctor-q21mr5ya.js} +8 -8
- package/dist/{fix-fr48gr0w.js → fix-0qdtdz5h.js} +6 -6
- package/dist/{harness-github-actions-cfkx9ty9.js → harness-github-actions-atc5bbkj.js} +5 -5
- package/dist/{harness-m63b6s0k.js → harness-vzy7n7ea.js} +5 -5
- package/dist/{index-nsaph4as.js → index-4b1ybz51.js} +17 -17
- package/dist/{production-intelligence-fwjh572m.js → production-intelligence-c8e0xw3n.js} +11 -11
- package/dist/{profile-commands-548ts402.js → profile-commands-6e2dgv72.js} +3 -3
- package/dist/prompt-source-mcp.js +5 -5
- package/dist/{prompts-p66y7wep.js → prompts-3h5jcjrw.js} +6 -6
- package/dist/{provision-r9fhrvpw.js → provision-q86phh4a.js} +5 -5
- package/dist/{registry-n7dsdxmg.js → registry-s2vdsfw8.js} +3 -3
- package/dist/{skills-c17gg69y.js → skills-vq3njc8a.js} +5 -5
- package/dist/{sync-bne90xvj.js → sync-hq9dpqpr.js} +7 -7
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/skills/moda-cli/SKILL.md +35 -30
- package/skills/moda-cli/references/commands.md +22 -22
- package/skills/moda-cli/references/dashboard-parity.md +4 -4
- package/skills/moda-cli/references/detections.md +10 -10
- package/skills/moda-cli/references/feedback.md +2 -2
- package/skills/moda-cli/references/output-protocol.md +2 -2
- package/skills/moda-cli/references/production-intelligence.md +13 -12
- package/skills/moda-cli/references/prompts.md +6 -2
- package/skills/moda-cli/references/recipes.md +14 -14
- package/skills/moda-cli/references/search-and-traces.md +32 -32
- package/skills/moda-cli/references/signals-and-false-positives.md +29 -29
- package/skills/moda-cli/references/troubleshooting.md +4 -4
package/dist/cli.js
CHANGED
|
@@ -5,22 +5,23 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
createCommandContext,
|
|
7
7
|
parseBooleanFlagValue
|
|
8
|
-
} from "./cli-
|
|
8
|
+
} from "./cli-qvg26twp.js";
|
|
9
9
|
import {
|
|
10
10
|
AuditSchema,
|
|
11
11
|
COMMAND_FLAG_SPECS,
|
|
12
12
|
DETECTION_VERDICTS,
|
|
13
13
|
DetectionReviewSchema,
|
|
14
|
+
EMOTION_FAMILIES,
|
|
14
15
|
FALSE_POSITIVE_KINDS,
|
|
15
16
|
FEEDBACK_USAGE,
|
|
16
17
|
FalsePositiveSchema,
|
|
18
|
+
LAZINESS_FAMILIES,
|
|
17
19
|
PROBLEM_FEEDBACK_ACTIONS,
|
|
18
20
|
ProblemFeedbackSchema,
|
|
19
21
|
ProblemImpactSchema,
|
|
20
22
|
ProblemLinearFileSchema,
|
|
21
23
|
ProblemsRemainderSchema,
|
|
22
24
|
SIGNALS_MANAGEMENT_SUBCOMMANDS,
|
|
23
|
-
SIGNAL_LABELS,
|
|
24
25
|
SignalLabelSchema,
|
|
25
26
|
StepScoresSchema,
|
|
26
27
|
UUID_RE,
|
|
@@ -43,12 +44,12 @@ import {
|
|
|
43
44
|
specFlagNames,
|
|
44
45
|
subcommandHelpFlags,
|
|
45
46
|
unknownSignalsSubcommandError
|
|
46
|
-
} from "./cli-
|
|
47
|
+
} from "./cli-dn91fb56.js";
|
|
47
48
|
import {
|
|
48
49
|
ApiError,
|
|
49
50
|
callControlAPI,
|
|
50
51
|
callDataAPI
|
|
51
|
-
} from "./cli-
|
|
52
|
+
} from "./cli-9btx12wp.js";
|
|
52
53
|
import {
|
|
53
54
|
AGENT_ERROR_CODES,
|
|
54
55
|
BATCH_CONCURRENCY,
|
|
@@ -65,6 +66,7 @@ import {
|
|
|
65
66
|
EXIT_ERROR,
|
|
66
67
|
EXIT_INPUT_REQUIRED,
|
|
67
68
|
EXIT_OK,
|
|
69
|
+
LEGACY_NAMES,
|
|
68
70
|
MODA_BASE_URL,
|
|
69
71
|
VERSION,
|
|
70
72
|
agentEnvelopeFits,
|
|
@@ -75,6 +77,7 @@ import {
|
|
|
75
77
|
asRecord,
|
|
76
78
|
asString,
|
|
77
79
|
attachInputDetails,
|
|
80
|
+
canonicalCommandText,
|
|
78
81
|
collectBatchTargets,
|
|
79
82
|
compactProblemDossier,
|
|
80
83
|
contextTarget,
|
|
@@ -85,6 +88,7 @@ import {
|
|
|
85
88
|
frustrationsFromEmotions,
|
|
86
89
|
getApiRequestCount,
|
|
87
90
|
getModaDir,
|
|
91
|
+
inputDetailsOf,
|
|
88
92
|
isApiLikeError,
|
|
89
93
|
isNetworkLikeError,
|
|
90
94
|
isPlatformApiKey,
|
|
@@ -106,7 +110,7 @@ import {
|
|
|
106
110
|
withFrustrationAnchors,
|
|
107
111
|
withToolFailureAnchors,
|
|
108
112
|
withoutDetectionWindows
|
|
109
|
-
} from "./cli-
|
|
113
|
+
} from "./cli-dve50x7p.js";
|
|
110
114
|
import {
|
|
111
115
|
__require
|
|
112
116
|
} from "./cli-0v6na3yp.js";
|
|
@@ -542,7 +546,10 @@ function buildCliUsageRequest(input) {
|
|
|
542
546
|
node_version: process.versions.node,
|
|
543
547
|
platform: process.platform,
|
|
544
548
|
arch: process.arch,
|
|
545
|
-
flags_json: {
|
|
549
|
+
flags_json: {
|
|
550
|
+
names: sanitizedFlagNames(input.flags),
|
|
551
|
+
...((alias) => alias ? { legacy_alias: alias } : {})(sanitizeReasonCode(input.legacyAlias?.replace(/ /g, ":"), 64))
|
|
552
|
+
},
|
|
546
553
|
...serializeAdoption(input.adoption)
|
|
547
554
|
})
|
|
548
555
|
};
|
|
@@ -654,6 +661,540 @@ function printUpdateNotice(options = {}) {
|
|
|
654
661
|
if (notice)
|
|
655
662
|
console.error(notice);
|
|
656
663
|
}
|
|
664
|
+
// src/command-tree.ts
|
|
665
|
+
var CANONICAL_LEAVES = [
|
|
666
|
+
{
|
|
667
|
+
noun: "traces",
|
|
668
|
+
verb: "list",
|
|
669
|
+
path: "traces list",
|
|
670
|
+
args: "",
|
|
671
|
+
handler: "traces",
|
|
672
|
+
summary: "List and filter traces (agent runs); --search ranks whole traces",
|
|
673
|
+
examples: ['moda traces list --search="error" --limit=5', 'moda traces list --world-state="enterprise" --outcome=positive']
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
noun: "traces",
|
|
677
|
+
verb: "search",
|
|
678
|
+
path: "traces search",
|
|
679
|
+
args: '"<query>"',
|
|
680
|
+
handler: "search",
|
|
681
|
+
summary: "Search trace messages (keyword, semantic, or hybrid)",
|
|
682
|
+
examples: ['moda traces search "billing error"', 'moda traces search "refund flow" --mode=semantic --time-range=7d --limit=10']
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
noun: "traces",
|
|
686
|
+
verb: "get",
|
|
687
|
+
path: "traces get",
|
|
688
|
+
args: "<conversation_id>[@N]...",
|
|
689
|
+
handler: "context",
|
|
690
|
+
summary: "Messages around a turn, or the whole trace (--all); up to 25 ids per call",
|
|
691
|
+
examples: [
|
|
692
|
+
"moda traces get <conversation_id> --msg-index=40 --window=3",
|
|
693
|
+
"moda traces get <conversation_id> --all",
|
|
694
|
+
"moda traces get <id>@12 <id2>@3 --window=2 # batch: up to 25 targets (or --ids-from=-|<file>)"
|
|
695
|
+
]
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
noun: "traces",
|
|
699
|
+
verb: "state",
|
|
700
|
+
path: "traces state",
|
|
701
|
+
args: "<conversation_id>",
|
|
702
|
+
handler: "world-state",
|
|
703
|
+
summary: "A trace's world state (slots, threads, events)",
|
|
704
|
+
examples: ["moda traces state <conversation_id> --summary-only", "moda traces state <conversation_id> --snapshot --msg-index=12"]
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
noun: "traces",
|
|
708
|
+
verb: "spans",
|
|
709
|
+
path: "traces spans",
|
|
710
|
+
args: "<conversation_id|trace_id>...",
|
|
711
|
+
handler: "audit",
|
|
712
|
+
summary: "Raw span audit (hierarchy, orphans, duplicates); up to 25 ids per call",
|
|
713
|
+
examples: ["moda traces spans <conversation_id>", "moda traces spans <trace_id> --kind=trace --include-bodies"]
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
noun: "problems",
|
|
717
|
+
verb: "list",
|
|
718
|
+
path: "problems list",
|
|
719
|
+
args: "",
|
|
720
|
+
handler: "problems",
|
|
721
|
+
summary: "Rank cross-signal Problems by root cause (what to fix first)",
|
|
722
|
+
examples: ["moda problems list", "moda problems list --days-back=7 --limit=10"]
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
noun: "problems",
|
|
726
|
+
verb: "get",
|
|
727
|
+
path: "problems get",
|
|
728
|
+
args: "<problem_id>",
|
|
729
|
+
handler: "problem",
|
|
730
|
+
summary: "One Problem: dossier, or its --evidence/--reports/--traces/--feedback pages",
|
|
731
|
+
examples: ["moda problems get <problem_id>", "moda problems get <problem_id> --evidence --limit=10", "moda problems get <problem_id> --traces --family=tool_failure"]
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
noun: "problems",
|
|
735
|
+
verb: "unexplained",
|
|
736
|
+
path: "problems unexplained",
|
|
737
|
+
args: "",
|
|
738
|
+
handler: "problems",
|
|
739
|
+
handlerSubcommand: "remainder",
|
|
740
|
+
summary: "Signals no Problem explains yet, grouped by family",
|
|
741
|
+
examples: ["moda problems unexplained --family=emotion --days-back=7"]
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
noun: "problems",
|
|
745
|
+
verb: "impact",
|
|
746
|
+
path: "problems impact",
|
|
747
|
+
args: "[<problem_id>]",
|
|
748
|
+
handler: "problem-impact",
|
|
749
|
+
summary: "Impact, denominators, recurrence and evidence for a Problem or use case",
|
|
750
|
+
examples: ["moda problems impact <problem_id> --mode=metrics", "moda problems impact --node-id=node_0 --mode=trend --days-back=30", "moda problems impact --mode=catalog"]
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
noun: "problems",
|
|
754
|
+
verb: "feedback",
|
|
755
|
+
path: "problems feedback",
|
|
756
|
+
args: "<problem_id>",
|
|
757
|
+
handler: "problem-feedback",
|
|
758
|
+
summary: "Mark a Problem fixed, dismiss it, rename it, or flag a bad attribution",
|
|
759
|
+
examples: ["moda problems feedback <problem_id> --action=mark_fixed", 'moda problems feedback <problem_id> --action=rename --new-name="Better title"']
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
noun: "problems",
|
|
763
|
+
verb: "reopen",
|
|
764
|
+
path: "problems reopen",
|
|
765
|
+
args: "<problem_id>",
|
|
766
|
+
handler: "problem-reopen",
|
|
767
|
+
summary: "Reopen a Problem marked fixed",
|
|
768
|
+
examples: ['moda problems reopen <problem_id> --reason="still happening" --dry-run']
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
noun: "problems",
|
|
772
|
+
verb: "linear",
|
|
773
|
+
path: "problems linear",
|
|
774
|
+
args: "<problem_id>",
|
|
775
|
+
handler: "problem-linear",
|
|
776
|
+
summary: "Show the Linear issue linked to a Problem",
|
|
777
|
+
examples: ["moda problems linear <problem_id>"]
|
|
778
|
+
},
|
|
779
|
+
{
|
|
780
|
+
noun: "problems",
|
|
781
|
+
verb: "link",
|
|
782
|
+
path: "problems link",
|
|
783
|
+
args: "<problem_id>",
|
|
784
|
+
handler: "problem-linear-file",
|
|
785
|
+
summary: "File a Problem as a Linear issue, or link an existing one with --issue",
|
|
786
|
+
examples: ["moda problems link <problem_id> --issue=ENG-123", "moda problems link <problem_id> --team-id=<team_id> --priority=2"]
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
noun: "detections",
|
|
790
|
+
verb: "list",
|
|
791
|
+
path: "detections list",
|
|
792
|
+
args: "--family=<family>",
|
|
793
|
+
routes: ["emotions", "frustrations", "laziness", "hallucinations", "tool-failures", "signal", "trace-signals"],
|
|
794
|
+
summary: "Detections of one family: emotion[:<f>] frustration laziness[:<f>] hallucination tool_failure custom_signal",
|
|
795
|
+
examples: [
|
|
796
|
+
"moda detections list --family=frustration --limit=10",
|
|
797
|
+
"moda detections list --family=emotion:confusion --days-back=14",
|
|
798
|
+
"moda detections list --family=hallucination --kind=contradicted",
|
|
799
|
+
"moda detections list --family=custom_signal --signal-id=<signal_id>"
|
|
800
|
+
]
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
noun: "detections",
|
|
804
|
+
verb: "tools",
|
|
805
|
+
path: "detections tools",
|
|
806
|
+
args: "[<tool_name>]",
|
|
807
|
+
routes: ["tool-failures", "tool-failure-detail"],
|
|
808
|
+
summary: "Tool failure leaderboard, or one tool's failure examples",
|
|
809
|
+
examples: ["moda detections tools --days-back=14", "moda detections tools <tool_name> --limit=3"]
|
|
810
|
+
},
|
|
811
|
+
{
|
|
812
|
+
noun: "review",
|
|
813
|
+
verb: "",
|
|
814
|
+
path: "review",
|
|
815
|
+
args: "<kind> <target>",
|
|
816
|
+
routes: ["false-positive", "detection-review", "signal-label"],
|
|
817
|
+
summary: "Review a detection: --verdict=false_positive (default), correct, or clear",
|
|
818
|
+
examples: [
|
|
819
|
+
'moda review tool <conversation_id> --tool=send_invoice --tool-use-id=<id> --reason="retried upstream"',
|
|
820
|
+
"moda review problem-detection <problem_id> --conversation-id=<id> --attribution-id=<uuid> --verdict=correct",
|
|
821
|
+
"moda review custom-signal <signal_id> --conversation-id=<id> --verdict=clear"
|
|
822
|
+
]
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
noun: "reviews",
|
|
826
|
+
verb: "list",
|
|
827
|
+
path: "reviews list",
|
|
828
|
+
args: "[<conversation_id>]",
|
|
829
|
+
routes: ["false-positives", "detection-reviews"],
|
|
830
|
+
summary: "False-positive marks and detection reviews (workspace, or one trace)",
|
|
831
|
+
examples: ["moda reviews list --kind=tool --all", "moda reviews list <conversation_id>", "moda reviews list <conversation_id> --kind=problem-detection"]
|
|
832
|
+
},
|
|
833
|
+
{
|
|
834
|
+
noun: "skills",
|
|
835
|
+
verb: "push",
|
|
836
|
+
path: "skills push",
|
|
837
|
+
args: "",
|
|
838
|
+
handler: "skills",
|
|
839
|
+
handlerSubcommand: "sync",
|
|
840
|
+
summary: "Upload locally authored .claude/skills/**/SKILL.md files to Moda (download: skills pull)",
|
|
841
|
+
examples: ["moda skills push --dry-run", "moda skills push"]
|
|
842
|
+
}
|
|
843
|
+
];
|
|
844
|
+
var CANONICAL_NOUNS = ["traces", "problems", "detections", "review", "reviews", "skills"];
|
|
845
|
+
var NOUN_SUMMARIES = {
|
|
846
|
+
traces: "Traces (agent runs): list, search, read, world state, raw spans",
|
|
847
|
+
problems: "Problems ranked by root cause, their evidence, impact and lifecycle",
|
|
848
|
+
detections: "Detections of every family, and the tool failure leaderboard",
|
|
849
|
+
review: "Mark a detection a false positive, correct, or clear the mark",
|
|
850
|
+
reviews: "List false-positive marks and detection reviews",
|
|
851
|
+
skills: "Generate, pull, push, and install agent skills"
|
|
852
|
+
};
|
|
853
|
+
function leafFor(noun, verb) {
|
|
854
|
+
if (noun === "review")
|
|
855
|
+
return CANONICAL_LEAVES.find((leaf) => leaf.noun === "review");
|
|
856
|
+
return CANONICAL_LEAVES.find((leaf) => leaf.noun === noun && leaf.verb === verb);
|
|
857
|
+
}
|
|
858
|
+
function leavesOf(noun) {
|
|
859
|
+
return CANONICAL_LEAVES.filter((leaf) => leaf.noun === noun);
|
|
860
|
+
}
|
|
861
|
+
function usePath(entry) {
|
|
862
|
+
return entry.use.replace(/^moda /, "").split(" ").filter((word) => /^[a-z][a-z-]*$/.test(word)).slice(0, entry.canonical.command === "review" ? 1 : 2).join(" ");
|
|
863
|
+
}
|
|
864
|
+
function flagOn(value) {
|
|
865
|
+
return value !== undefined && value !== "false";
|
|
866
|
+
}
|
|
867
|
+
function legacyNameFor(typed, subcommand, flags) {
|
|
868
|
+
const byName = (name) => LEGACY_NAMES.find((entry) => entry.legacy === name);
|
|
869
|
+
if (subcommand !== undefined) {
|
|
870
|
+
const sub = byName(`${typed} ${subcommand}`);
|
|
871
|
+
if (sub)
|
|
872
|
+
return sub;
|
|
873
|
+
}
|
|
874
|
+
if (typed === "signal")
|
|
875
|
+
return flagOn(flags.detections) ? byName("signal --detections") : undefined;
|
|
876
|
+
if (typed === "problems" || typed === "skills" || typed === "sync")
|
|
877
|
+
return;
|
|
878
|
+
return byName(typed);
|
|
879
|
+
}
|
|
880
|
+
function visibleLegacyNames() {
|
|
881
|
+
return LEGACY_NAMES.filter((entry) => !entry.silent);
|
|
882
|
+
}
|
|
883
|
+
function legacyNamesForPath(path) {
|
|
884
|
+
return visibleLegacyNames().filter((entry) => usePath(entry) === path).map((entry) => entry.legacy);
|
|
885
|
+
}
|
|
886
|
+
function deprecationMessage(entry) {
|
|
887
|
+
return `'moda ${entry.legacy}' is a legacy name; use '${entry.use}' (the old name still works).${entry.note ? ` ${entry.note}` : ""}`;
|
|
888
|
+
}
|
|
889
|
+
function canonicalTelemetryForDefault(name, subcommand) {
|
|
890
|
+
if ((name === "traces" || name === "problems") && subcommand === undefined)
|
|
891
|
+
return { command: name, subcommand: "list" };
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
var without = (flags, ...keys) => {
|
|
895
|
+
const copy = { ...flags };
|
|
896
|
+
for (const key of keys) {
|
|
897
|
+
delete copy[key];
|
|
898
|
+
delete copy[key.replace(/-/g, "_")];
|
|
899
|
+
}
|
|
900
|
+
return copy;
|
|
901
|
+
};
|
|
902
|
+
function specFor(command) {
|
|
903
|
+
const spec = flagSpecFor(command);
|
|
904
|
+
if (!spec)
|
|
905
|
+
throw new Error(`No flag spec for routed command '${command}'`);
|
|
906
|
+
return spec;
|
|
907
|
+
}
|
|
908
|
+
function flagNamesOf(command, ...drop) {
|
|
909
|
+
return specFlagNames(specFor(command)).filter((name) => !drop.includes(name));
|
|
910
|
+
}
|
|
911
|
+
function inputError(message, usage, reason, details = {}) {
|
|
912
|
+
return new CliInputError(message, usage, reason, details);
|
|
913
|
+
}
|
|
914
|
+
var DETECTION_FAMILIES = ["emotion", "frustration", "laziness", "hallucination", "tool_failure", "custom_signal"];
|
|
915
|
+
var DETECTION_FAMILY_HANDLERS = {
|
|
916
|
+
emotion: "emotions",
|
|
917
|
+
frustration: "frustrations",
|
|
918
|
+
laziness: "laziness",
|
|
919
|
+
hallucination: "hallucinations",
|
|
920
|
+
tool_failure: "tool-failures",
|
|
921
|
+
custom_signal: "signal"
|
|
922
|
+
};
|
|
923
|
+
var DETECTION_SUBFAMILIES = {
|
|
924
|
+
emotion: EMOTION_FAMILIES,
|
|
925
|
+
laziness: LAZINESS_FAMILIES
|
|
926
|
+
};
|
|
927
|
+
var REVIEW_VERDICTS = ["false_positive", "correct", "clear"];
|
|
928
|
+
function detectionFamilyValues() {
|
|
929
|
+
return DETECTION_FAMILIES.flatMap((family) => [family, ...(DETECTION_SUBFAMILIES[family] ?? []).map((sub) => `${family}:${sub}`)]);
|
|
930
|
+
}
|
|
931
|
+
var DETECTIONS_LIST_USAGE = "Usage: moda detections list --family=emotion[:<family>]|frustration|laziness[:<family>]|hallucination|tool_failure|custom_signal [flags]. See 'moda detections list --help'.";
|
|
932
|
+
function routeDetectionsList(leaf, rest, flags) {
|
|
933
|
+
const raw = flags.family;
|
|
934
|
+
if (raw === undefined || raw === "true" || raw === "") {
|
|
935
|
+
throw inputError("--family is required for 'moda detections list'.", DETECTIONS_LIST_USAGE, "missing_value:family", {
|
|
936
|
+
field: "--family",
|
|
937
|
+
allowed: [...DETECTION_FAMILIES],
|
|
938
|
+
suggestedCommands: ["moda detections list --help"]
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
const [base, sub, ...extra] = raw.split(":");
|
|
942
|
+
if (!DETECTION_FAMILIES.includes(base) || extra.length > 0) {
|
|
943
|
+
const didYouMean = closestMatches(raw, detectionFamilyValues(), 2);
|
|
944
|
+
throw inputError(`Unknown detection family '${raw}'.${didYouMean.length ? ` Did you mean ${didYouMean.map((value) => `--family=${value}`).join(" or ")}?` : ""}`, DETECTIONS_LIST_USAGE, "bad_enum:family", { field: "--family", allowed: [...DETECTION_FAMILIES], ...didYouMean.length ? { didYouMean } : {}, suggestedCommands: ["moda detections list --help"] });
|
|
945
|
+
}
|
|
946
|
+
const family = base;
|
|
947
|
+
const subfamilies = DETECTION_SUBFAMILIES[family];
|
|
948
|
+
if (sub !== undefined && (!subfamilies || !subfamilies.includes(sub))) {
|
|
949
|
+
const allowed = subfamilies ? subfamilies.map((value) => `${family}:${value}`) : [family];
|
|
950
|
+
throw inputError(subfamilies ? `Unknown ${family} family '${sub}'.` : `--family=${family} takes no sub-family.`, DETECTIONS_LIST_USAGE, "bad_enum:family", { field: "--family", allowed, didYouMean: closestMatches(raw, allowed, 2), suggestedCommands: [`moda detections list --family=${family} --help`] });
|
|
951
|
+
}
|
|
952
|
+
if (rest.length > 0) {
|
|
953
|
+
throw inputError(`'moda detections list' takes no arguments, but got '${rest.join(" ")}'.`, DETECTIONS_LIST_USAGE, "unexpected_positional");
|
|
954
|
+
}
|
|
955
|
+
const label = `detections list --family=${family}`;
|
|
956
|
+
if (family === "custom_signal") {
|
|
957
|
+
const signalId = flags["signal-id"];
|
|
958
|
+
const traceId = flags["conversation-id"];
|
|
959
|
+
if (signalId === undefined === (traceId === undefined)) {
|
|
960
|
+
throw inputError(signalId === undefined ? "--family=custom_signal needs --signal-id=<signal_id> (one signal's hits) or --conversation-id=<conversation_id> (every signal hit on one trace)." : "Pass --signal-id or --conversation-id with --family=custom_signal, not both.", DETECTIONS_LIST_USAGE, signalId === undefined ? "missing_value:signal-id" : "flag_conflict", { field: "--signal-id", suggestedCommands: ["moda detections list --family=custom_signal --help", "moda signals"] });
|
|
961
|
+
}
|
|
962
|
+
if (signalId !== undefined) {
|
|
963
|
+
return {
|
|
964
|
+
leaf,
|
|
965
|
+
handler: "signal",
|
|
966
|
+
positionals: [signalId],
|
|
967
|
+
label,
|
|
968
|
+
telemetrySubcommand: "list",
|
|
969
|
+
rewriteFlags: (input) => ({ ...without(input, "family", "signal-id"), detections: "true" }),
|
|
970
|
+
allowedFlags: ["family", "signal-id", ...flagNamesOf("signal", "detections")],
|
|
971
|
+
renames: { signal: label }
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
return {
|
|
975
|
+
leaf,
|
|
976
|
+
handler: "trace-signals",
|
|
977
|
+
positionals: [traceId],
|
|
978
|
+
label,
|
|
979
|
+
telemetrySubcommand: "list",
|
|
980
|
+
rewriteFlags: (input) => without(input, "family", "conversation-id"),
|
|
981
|
+
allowedFlags: ["family", "conversation-id"],
|
|
982
|
+
renames: { "trace-signals": label }
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
const handler = DETECTION_FAMILY_HANDLERS[family];
|
|
986
|
+
return {
|
|
987
|
+
leaf,
|
|
988
|
+
handler,
|
|
989
|
+
positionals: [],
|
|
990
|
+
label,
|
|
991
|
+
telemetrySubcommand: "list",
|
|
992
|
+
rewriteFlags: (input) => ({ ...without(input, "family"), ...sub !== undefined ? { family: sub } : {} }),
|
|
993
|
+
allowedFlags: ["family", ...flagNamesOf(handler, "family")],
|
|
994
|
+
...specFor(handler).flagAliases ? { flagAliases: specFor(handler).flagAliases } : {},
|
|
995
|
+
renames: { [handler]: label }
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
var REVIEW_USAGE = `Usage: moda review <${FALSE_POSITIVE_KINDS.join("|")}> <target> [--verdict=${REVIEW_VERDICTS.join("|")}] [--reason=TEXT]. See 'moda review --help'.`;
|
|
999
|
+
var REVIEW_FLAG_ALIASES = { note: "reason" };
|
|
1000
|
+
function routeReview(leaf, positionals, flags) {
|
|
1001
|
+
const [kind, target, ...extra] = positionals;
|
|
1002
|
+
if (!kind || !FALSE_POSITIVE_KINDS.includes(kind)) {
|
|
1003
|
+
const didYouMean = kind ? closestMatches(kind, FALSE_POSITIVE_KINDS, 2) : [];
|
|
1004
|
+
throw inputError(kind ? `Unknown review kind '${kind}'.${didYouMean.length ? ` Did you mean ${didYouMean.map((value) => `'${value}'`).join(" or ")}?` : ""}` : "<kind> and <target> are required", REVIEW_USAGE, kind ? "bad_enum:kind" : "missing_positional", { field: "<kind>", allowed: [...FALSE_POSITIVE_KINDS], ...didYouMean.length ? { didYouMean } : {}, suggestedCommands: ["moda review --help"] });
|
|
1005
|
+
}
|
|
1006
|
+
if (!target)
|
|
1007
|
+
throw inputError("<kind> and <target> are required", REVIEW_USAGE, "missing_positional", { field: "<target>", suggestedCommands: ["moda review --help"] });
|
|
1008
|
+
if (extra.length > 0)
|
|
1009
|
+
throw inputError(`'moda review' takes <kind> <target>, but also got '${extra.join(" ")}'.`, REVIEW_USAGE, "unexpected_positional");
|
|
1010
|
+
const verdict = flags.verdict ?? "false_positive";
|
|
1011
|
+
if (!REVIEW_VERDICTS.includes(verdict)) {
|
|
1012
|
+
throw inputError(`--verdict must be one of ${REVIEW_VERDICTS.join(", ")}.`, REVIEW_USAGE, "bad_enum:verdict", {
|
|
1013
|
+
field: "--verdict",
|
|
1014
|
+
allowed: [...REVIEW_VERDICTS],
|
|
1015
|
+
didYouMean: closestMatches(verdict, REVIEW_VERDICTS, 1),
|
|
1016
|
+
suggestedCommands: ["moda review --help"]
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
const label = `review ${kind}`;
|
|
1020
|
+
const reasonToNote = (input) => {
|
|
1021
|
+
const reason = input.reason ?? input.note;
|
|
1022
|
+
const rest = without(input, "reason", "note", "verdict");
|
|
1023
|
+
return reason !== undefined ? { ...rest, note: reason } : rest;
|
|
1024
|
+
};
|
|
1025
|
+
if (kind === "problem-detection") {
|
|
1026
|
+
return {
|
|
1027
|
+
leaf,
|
|
1028
|
+
handler: "detection-review",
|
|
1029
|
+
positionals: [target],
|
|
1030
|
+
label,
|
|
1031
|
+
telemetrySubcommand: kind,
|
|
1032
|
+
rewriteFlags: (input) => ({ ...reasonToNote(input), verdict }),
|
|
1033
|
+
allowedFlags: ["verdict", ...flagNamesOf("detection-review", "verdict")],
|
|
1034
|
+
flagAliases: { reason: "note" },
|
|
1035
|
+
renames: { "detection-review": label, "false-positive problem-detection": label }
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
if (verdict === "correct") {
|
|
1039
|
+
if (kind !== "custom-signal") {
|
|
1040
|
+
throw inputError(`--verdict=correct applies to problem-detection and custom-signal reviews only, not '${kind}'.`, REVIEW_USAGE, "unsupported_verdict", { field: "--verdict", allowed: ["false_positive", "clear"], suggestedCommands: ["moda review --help"] });
|
|
1041
|
+
}
|
|
1042
|
+
return {
|
|
1043
|
+
leaf,
|
|
1044
|
+
handler: "signal-label",
|
|
1045
|
+
positionals: [target],
|
|
1046
|
+
label,
|
|
1047
|
+
telemetrySubcommand: kind,
|
|
1048
|
+
rewriteFlags: (input) => ({ ...reasonToNote(input), label: "fits" }),
|
|
1049
|
+
allowedFlags: ["verdict", ...flagNamesOf("signal-label", "label")],
|
|
1050
|
+
flagAliases: { reason: "note" },
|
|
1051
|
+
renames: { "signal-label": label }
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
return {
|
|
1055
|
+
leaf,
|
|
1056
|
+
handler: "false-positive",
|
|
1057
|
+
positionals: [kind, target],
|
|
1058
|
+
label,
|
|
1059
|
+
telemetrySubcommand: kind,
|
|
1060
|
+
rewriteFlags: (input) => {
|
|
1061
|
+
const note = input.note;
|
|
1062
|
+
const rest = without(input, "verdict", "note");
|
|
1063
|
+
return {
|
|
1064
|
+
...note !== undefined && rest.reason === undefined ? { ...rest, reason: note } : rest,
|
|
1065
|
+
...verdict === "clear" ? { undo: "true" } : {}
|
|
1066
|
+
};
|
|
1067
|
+
},
|
|
1068
|
+
allowedFlags: ["verdict", ...flagNamesOf("false-positive", "undo")],
|
|
1069
|
+
flagAliases: REVIEW_FLAG_ALIASES,
|
|
1070
|
+
renames: { "false-positive": "review" }
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
var REVIEWS_LIST_USAGE = "Usage: moda reviews list [<conversation_id>] [--kind=quote|tool|laziness|hallucination|custom-signal|problem-detection] [--all] [--limit=N]. See 'moda reviews list --help'.";
|
|
1074
|
+
function routeReviewsList(leaf, rest, flags) {
|
|
1075
|
+
if (rest.length > 1)
|
|
1076
|
+
throw inputError(`'moda reviews list' takes at most one <conversation_id>, but got '${rest.join(" ")}'.`, REVIEWS_LIST_USAGE, "unexpected_positional");
|
|
1077
|
+
if (flags.kind === "problem-detection") {
|
|
1078
|
+
if (!rest[0]) {
|
|
1079
|
+
throw inputError("--kind=problem-detection needs a <conversation_id>: detection reviews are read per trace. Nothing was sent.", REVIEWS_LIST_USAGE, "missing_positional", { field: "<conversation_id>", suggestedCommands: ["moda reviews list --help"] });
|
|
1080
|
+
}
|
|
1081
|
+
return {
|
|
1082
|
+
leaf,
|
|
1083
|
+
handler: "detection-reviews",
|
|
1084
|
+
positionals: [rest[0]],
|
|
1085
|
+
label: "reviews list --kind=problem-detection",
|
|
1086
|
+
telemetrySubcommand: "list",
|
|
1087
|
+
rewriteFlags: (input) => without(input, "kind"),
|
|
1088
|
+
allowedFlags: ["kind", ...flagNamesOf("detection-reviews")],
|
|
1089
|
+
renames: { "detection-reviews": "reviews list --kind=problem-detection" }
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
return {
|
|
1093
|
+
leaf,
|
|
1094
|
+
handler: "false-positives",
|
|
1095
|
+
positionals: rest,
|
|
1096
|
+
label: "reviews list",
|
|
1097
|
+
telemetrySubcommand: "list",
|
|
1098
|
+
rewriteFlags: (input) => input,
|
|
1099
|
+
allowedFlags: flagNamesOf("false-positives"),
|
|
1100
|
+
renames: { "false-positives": "reviews list", "detection-reviews": "reviews list --kind=problem-detection" }
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
function routeDetectionsTools(leaf, rest) {
|
|
1104
|
+
if (rest.length > 1) {
|
|
1105
|
+
throw inputError(`'moda detections tools' takes at most one <tool_name>, but got '${rest.join(" ")}'.`, "Usage: moda detections tools [<tool_name>] [flags]", "unexpected_positional");
|
|
1106
|
+
}
|
|
1107
|
+
const handler = rest[0] === undefined ? "tool-failures" : "tool-failure-detail";
|
|
1108
|
+
return {
|
|
1109
|
+
leaf,
|
|
1110
|
+
handler,
|
|
1111
|
+
positionals: rest,
|
|
1112
|
+
label: "detections tools",
|
|
1113
|
+
telemetrySubcommand: "tools",
|
|
1114
|
+
rewriteFlags: (input) => input,
|
|
1115
|
+
renames: { [handler]: "detections tools" }
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
function routeProblemsImpact(leaf, rest, flags) {
|
|
1119
|
+
if (rest.length > 1) {
|
|
1120
|
+
throw inputError(`'moda problems impact' takes at most one <problem_id>, but got '${rest.join(" ")}'.`, "Usage: moda problems impact [<problem_id>] [flags]", "unexpected_positional");
|
|
1121
|
+
}
|
|
1122
|
+
if (rest[0] !== undefined && flags["problem-id"] !== undefined && flags["problem-id"] !== rest[0]) {
|
|
1123
|
+
throw inputError("Pass the problem id once: as <problem_id> or --problem-id, not both.", "Usage: moda problems impact [<problem_id>] [flags]", "flag_conflict", { field: "--problem-id" });
|
|
1124
|
+
}
|
|
1125
|
+
const problemId = rest[0];
|
|
1126
|
+
return {
|
|
1127
|
+
leaf,
|
|
1128
|
+
handler: "problem-impact",
|
|
1129
|
+
positionals: [],
|
|
1130
|
+
label: "problems impact",
|
|
1131
|
+
telemetrySubcommand: "impact",
|
|
1132
|
+
rewriteFlags: (input) => problemId !== undefined ? { ...input, "problem-id": problemId } : input,
|
|
1133
|
+
renames: { "problem-impact": "problems impact" }
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
function routeCanonical(command, positionals, flags) {
|
|
1137
|
+
if (command === "review")
|
|
1138
|
+
return routeReview(leafFor("review", undefined), positionals, flags);
|
|
1139
|
+
if (!CANONICAL_NOUNS.includes(command))
|
|
1140
|
+
return;
|
|
1141
|
+
const [verb, ...rest] = positionals;
|
|
1142
|
+
const leaf = verb !== undefined ? leafFor(command, verb) : undefined;
|
|
1143
|
+
if (!leaf) {
|
|
1144
|
+
if (command === "traces" || command === "problems" || command === "skills")
|
|
1145
|
+
return;
|
|
1146
|
+
const verbs = leavesOf(command).map((entry) => entry.verb);
|
|
1147
|
+
const didYouMean = verb ? closestMatches(verb, verbs, 2) : [];
|
|
1148
|
+
throw inputError(verb ? `Unknown ${command} subcommand '${verb}'.${didYouMean.length ? ` Did you mean ${didYouMean.map((name) => `'${name}'`).join(" or ")}?` : ""}` : `'moda ${command}' needs a subcommand: ${verbs.join(", ")}.`, `Usage: ${leavesOf(command).map((entry) => `moda ${entry.path}${entry.args ? ` ${entry.args}` : ""}`).join(" | ")}`, verb ? "unknown_subcommand" : "missing_subcommand", { field: "<subcommand>", allowed: verbs, ...didYouMean.length ? { didYouMean } : {}, suggestedCommands: [`moda ${command} --help`] });
|
|
1149
|
+
}
|
|
1150
|
+
switch (leaf.path) {
|
|
1151
|
+
case "detections list":
|
|
1152
|
+
return routeDetectionsList(leaf, rest, flags);
|
|
1153
|
+
case "detections tools":
|
|
1154
|
+
return routeDetectionsTools(leaf, rest);
|
|
1155
|
+
case "reviews list":
|
|
1156
|
+
return routeReviewsList(leaf, rest, flags);
|
|
1157
|
+
case "problems impact":
|
|
1158
|
+
return routeProblemsImpact(leaf, rest, flags);
|
|
1159
|
+
default: {
|
|
1160
|
+
const handler = leaf.handler;
|
|
1161
|
+
const renameKey = leaf.handlerSubcommand ? `${handler} ${leaf.handlerSubcommand}` : handler;
|
|
1162
|
+
return {
|
|
1163
|
+
leaf,
|
|
1164
|
+
handler,
|
|
1165
|
+
positionals: leaf.handlerSubcommand ? [leaf.handlerSubcommand, ...rest] : rest,
|
|
1166
|
+
rewriteFlags: (input) => input,
|
|
1167
|
+
label: leaf.path,
|
|
1168
|
+
telemetrySubcommand: leaf.verb,
|
|
1169
|
+
renames: { [renameKey]: leaf.path }
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
function parseCommandFor(command, verb) {
|
|
1175
|
+
const leaf = verb !== undefined && command !== "review" ? leafFor(command, verb) : undefined;
|
|
1176
|
+
return leaf?.handler ?? command;
|
|
1177
|
+
}
|
|
1178
|
+
function renameCommands(text, renames) {
|
|
1179
|
+
const escape = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1180
|
+
let result = text;
|
|
1181
|
+
for (const [from, to] of Object.entries(renames).sort((left, right) => right[0].length - left[0].length)) {
|
|
1182
|
+
const verbs = leavesOf(from).flatMap((leaf) => leaf.verb ? [escape(leaf.verb)] : []);
|
|
1183
|
+
const canonical = verbs.length > 0 ? `(?! (?:${verbs.join("|")})(?![\\w-]))` : "";
|
|
1184
|
+
result = result.replace(new RegExp(`moda ${escape(from)}(?![\\w-])${canonical}`, "g"), `moda ${to}`);
|
|
1185
|
+
}
|
|
1186
|
+
return result;
|
|
1187
|
+
}
|
|
1188
|
+
function leafHandlers(leaf) {
|
|
1189
|
+
return leaf.routes ? [...leaf.routes] : [leaf.handler];
|
|
1190
|
+
}
|
|
1191
|
+
function leafFlagSpec(leaf) {
|
|
1192
|
+
if (!leaf.handler)
|
|
1193
|
+
return;
|
|
1194
|
+
if (leaf.path === "problems unexplained")
|
|
1195
|
+
return COMMAND_FLAG_SPECS.problems.subcommands.remainder;
|
|
1196
|
+
return flagSpecFor(leaf.handler);
|
|
1197
|
+
}
|
|
657
1198
|
|
|
658
1199
|
// src/command-registry.ts
|
|
659
1200
|
function createCommandRegistry(definitions) {
|
|
@@ -887,11 +1428,11 @@ var SKILLS_SUBCOMMAND_HELP = [
|
|
|
887
1428
|
examples: ["moda skills pull", "moda skills pull --status=all"]
|
|
888
1429
|
},
|
|
889
1430
|
{
|
|
890
|
-
name: "
|
|
891
|
-
description: "Upload locally authored .claude/skills/**/SKILL.md files to Moda",
|
|
892
|
-
usage: "moda skills
|
|
1431
|
+
name: "push",
|
|
1432
|
+
description: "Upload locally authored .claude/skills/**/SKILL.md files to Moda (legacy name: sync; download is pull)",
|
|
1433
|
+
usage: "moda skills push [--dry-run]",
|
|
893
1434
|
flags: [{ name: "--dry-run", description: "Server-side preview; nothing is saved." }],
|
|
894
|
-
examples: ["moda skills
|
|
1435
|
+
examples: ["moda skills push --dry-run", "moda skills push"]
|
|
895
1436
|
},
|
|
896
1437
|
{
|
|
897
1438
|
name: "list",
|
|
@@ -1193,15 +1734,6 @@ var HARNESS_SUBCOMMAND_HELP = [
|
|
|
1193
1734
|
examples: ["moda harness diff my-harness-key --from=hver_a --to=hver_b"]
|
|
1194
1735
|
}
|
|
1195
1736
|
];
|
|
1196
|
-
var PROBLEMS_SUBCOMMAND_HELP = [
|
|
1197
|
-
{
|
|
1198
|
-
name: "remainder",
|
|
1199
|
-
description: "Signals no Problem explains yet, grouped by family (read-only)",
|
|
1200
|
-
usage: "moda problems remainder [--family=<family>] [--days-back=N] [--limit=N] [--cursor=<token>]",
|
|
1201
|
-
flags: subcommandHelpFlags(COMMAND_FLAG_SPECS.problems.subcommands.remainder),
|
|
1202
|
-
examples: ["moda problems remainder", "moda problems remainder --family=emotion --days-back=7"]
|
|
1203
|
-
}
|
|
1204
|
-
];
|
|
1205
1737
|
var FIX_TIMEOUT = { name: "--timeout=<ms>", description: "How long to wait, in milliseconds (max 86400000)." };
|
|
1206
1738
|
var FIX_POLL = { name: "--poll-interval=<ms>", description: "Polling interval while waiting, in milliseconds." };
|
|
1207
1739
|
var FIXES_SUBCOMMAND_HELP = [
|
|
@@ -1767,27 +2299,42 @@ function buildManifest(commands) {
|
|
|
1767
2299
|
"moda.intelligence.v1",
|
|
1768
2300
|
"production_ask.v0.1"
|
|
1769
2301
|
],
|
|
1770
|
-
commands: commands.filter((command) => !command.hidden).map((command) =>
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
2302
|
+
commands: commands.filter((command) => !command.hidden).map((command) => {
|
|
2303
|
+
const legacy = legacyEntry(command.name);
|
|
2304
|
+
if (legacy) {
|
|
2305
|
+
return {
|
|
2306
|
+
name: command.name,
|
|
2307
|
+
aliases: command.aliases ?? [],
|
|
2308
|
+
summary: command.description,
|
|
2309
|
+
deprecated_by: legacy
|
|
2310
|
+
};
|
|
2311
|
+
}
|
|
2312
|
+
const deprecatedForms = deprecatedFormsOf(command.name);
|
|
2313
|
+
return {
|
|
2314
|
+
name: command.name,
|
|
2315
|
+
aliases: command.aliases ?? [],
|
|
2316
|
+
summary: command.description,
|
|
2317
|
+
output_modes: command.outputModes,
|
|
2318
|
+
streaming: command.outputModes.includes("agent-stream") || command.outputModes.includes("agent"),
|
|
2319
|
+
examples: command.examples,
|
|
2320
|
+
auth: command.auth,
|
|
2321
|
+
...command.authNote ? { auth_note: command.authNote } : {},
|
|
2322
|
+
mutability: command.mutability,
|
|
2323
|
+
exit_codes: exitCodesForCommand(command.name),
|
|
2324
|
+
...commandFlagFields(command, commands),
|
|
2325
|
+
...treeFields(command.name, commands),
|
|
2326
|
+
...deprecatedForms.length > 0 ? { deprecated_forms: deprecatedForms } : {}
|
|
2327
|
+
};
|
|
2328
|
+
}),
|
|
1783
2329
|
concepts: [
|
|
1784
2330
|
{ term: "tenant", meaning: "A Moda organization/workspace that owns data, prompts, skills, and API keys." },
|
|
1785
2331
|
{ term: "profile", meaning: "A named local CLI configuration under ~/.moda that selects environment, tenant, and secret references." },
|
|
1786
2332
|
{ term: "harness", meaning: "Moda’s local graph of runtime agents, prompts, tools, identities, and deployments in a codebase." },
|
|
1787
2333
|
{ term: "harness report", meaning: "A cited analyst artifact under .moda/ that justifies the harness graph before sync." },
|
|
1788
2334
|
{ term: "remote analyze run", meaning: "A Moda-hosted harness analysis tracked in .moda/harness-remote-run.json; fetch its status or result with `moda harness pull`." },
|
|
1789
|
-
{ term: "trace", meaning: "The full record of one agent run stored in Moda analytics: every message, tool call, thinking block, and step that shares one `conversation_id` (the trace ID). Formerly called a conversation; `moda conversations` remains a
|
|
1790
|
-
{ term: "span", meaning: "Raw OTLP span evidence inside one trace (or one OTLP trace_id), exposed by `moda
|
|
2335
|
+
{ term: "trace", meaning: "The full record of one agent run stored in Moda analytics: every message, tool call, thinking block, and step that shares one `conversation_id` (the trace ID). Formerly called a conversation; `moda conversations` remains a silent alias of `moda traces list`." },
|
|
2336
|
+
{ term: "span", meaning: "Raw OTLP span evidence inside one trace (or one OTLP trace_id), exposed by `moda traces spans`." },
|
|
2337
|
+
{ term: "legacy name", meaning: "A command name from before the `moda <noun> <verb>` tree (e.g. `moda context`, now `moda traces get`). It still runs; its entry names the canonical form in `deprecated_by`, and agent output carries a warning and `meta.deprecated`." },
|
|
1791
2338
|
{ term: "ingest key", meaning: "A `moda_sk_` API key for SDKs, CI, and Data API calls. Treat it as a secret." },
|
|
1792
2339
|
{ term: "CLI session token", meaning: "A local browser-auth session token used by CLI auth/bootstrap flows, not by application SDKs." },
|
|
1793
2340
|
{ term: "prompt", meaning: "A code-first prompt file tracked by `.moda/prompts.yml` and synced to Moda." },
|
|
@@ -1810,7 +2357,11 @@ function renderManifestHuman(manifest) {
|
|
|
1810
2357
|
].join(`
|
|
1811
2358
|
`);
|
|
1812
2359
|
}
|
|
1813
|
-
function commandFlagFields(command) {
|
|
2360
|
+
function commandFlagFields(command, commands) {
|
|
2361
|
+
if (command.name === "review")
|
|
2362
|
+
return leafManifestFields(leafFor("review", undefined), commands);
|
|
2363
|
+
if (command.name === "detections" || command.name === "reviews")
|
|
2364
|
+
return {};
|
|
1814
2365
|
const spec = flagSpecFor(command.name);
|
|
1815
2366
|
if (spec)
|
|
1816
2367
|
return manifestFlagFields(spec);
|
|
@@ -1843,51 +2394,174 @@ function exitCodesForCommand(command) {
|
|
|
1843
2394
|
return [0, 1, 4, 5, 130];
|
|
1844
2395
|
return [0, 1, 4, 130];
|
|
1845
2396
|
}
|
|
2397
|
+
function legacyEntry(name) {
|
|
2398
|
+
return visibleLegacyNames().find((entry) => entry.legacy === name)?.use;
|
|
2399
|
+
}
|
|
2400
|
+
function deprecatedFormsOf(name) {
|
|
2401
|
+
return visibleLegacyNames().filter((entry) => entry.legacy.startsWith(`${name} `)).map((entry) => ({ form: `moda ${entry.legacy}`, use: entry.use }));
|
|
2402
|
+
}
|
|
2403
|
+
function treeFields(name, commands) {
|
|
2404
|
+
if (!CANONICAL_NOUNS.includes(name) || name === "review")
|
|
2405
|
+
return {};
|
|
2406
|
+
return { subcommands: leavesOf(name).map((leaf) => leafManifestFields(leaf, commands)) };
|
|
2407
|
+
}
|
|
2408
|
+
function leafMutability(leaf, commands) {
|
|
2409
|
+
const handlers = leafHandlers(leaf);
|
|
2410
|
+
return commands.some((command) => handlers.includes(command.name) && command.mutability === "write") ? "write" : "read";
|
|
2411
|
+
}
|
|
2412
|
+
function documentedFlags(flags) {
|
|
2413
|
+
return flags.map(documentedFlag);
|
|
2414
|
+
}
|
|
2415
|
+
function withoutFlags(flags, ...names) {
|
|
2416
|
+
return flags.filter((flag) => !names.includes(flag.name));
|
|
2417
|
+
}
|
|
2418
|
+
function handlerFlags(command, ...drop) {
|
|
2419
|
+
const spec = flagSpecFor(command);
|
|
2420
|
+
return spec ? withoutFlags(describeSpecFlags(spec), ...drop) : [];
|
|
2421
|
+
}
|
|
2422
|
+
function leafManifestFields(leaf, commands) {
|
|
2423
|
+
const base = {
|
|
2424
|
+
...leaf.verb ? { name: leaf.verb } : {},
|
|
2425
|
+
path: `moda ${leaf.path}`,
|
|
2426
|
+
summary: leaf.summary,
|
|
2427
|
+
usage: `moda ${leaf.path}${leaf.args ? ` ${leaf.args}` : ""} [flags]`,
|
|
2428
|
+
aliases: legacyNamesForPath(leaf.path),
|
|
2429
|
+
runs: leafHandlers(leaf),
|
|
2430
|
+
mutability: leafMutability(leaf, commands),
|
|
2431
|
+
examples: leaf.examples
|
|
2432
|
+
};
|
|
2433
|
+
const spec = leafFlagSpec(leaf);
|
|
2434
|
+
if (spec) {
|
|
2435
|
+
const fields = manifestFlagFields({ ...spec, subcommands: undefined });
|
|
2436
|
+
if (leaf.path === "problems impact") {
|
|
2437
|
+
fields.positionals = [{ name: "problem_id", required: false, description: "Same as --problem-id", flag: "--problem-id" }];
|
|
2438
|
+
}
|
|
2439
|
+
return { ...base, ...fields };
|
|
2440
|
+
}
|
|
2441
|
+
switch (leaf.path) {
|
|
2442
|
+
case "skills push":
|
|
2443
|
+
return { ...base, flags: documentedFlags([{ name: "--dry-run", description: "Server-side preview; nothing is saved." }]) };
|
|
2444
|
+
case "detections list":
|
|
2445
|
+
return {
|
|
2446
|
+
...base,
|
|
2447
|
+
flags: [{
|
|
2448
|
+
name: "--family",
|
|
2449
|
+
type: "enum",
|
|
2450
|
+
enum: [...DETECTION_FAMILIES],
|
|
2451
|
+
required: true,
|
|
2452
|
+
description: "Detection family; emotion and laziness take a sub-family after a colon (emotion:confusion, laziness:stub_body)"
|
|
2453
|
+
}],
|
|
2454
|
+
routes: DETECTION_FAMILIES.flatMap((family) => family === "custom_signal" ? [
|
|
2455
|
+
{
|
|
2456
|
+
when: "--family=custom_signal --signal-id=<signal_id>",
|
|
2457
|
+
runs: ["signal"],
|
|
2458
|
+
flags: [
|
|
2459
|
+
{ name: "--signal-id", type: "string", required: true, description: "One signal's recent hits (runs signal <id> --detections)" },
|
|
2460
|
+
...handlerFlags("signal", "--detections")
|
|
2461
|
+
]
|
|
2462
|
+
},
|
|
2463
|
+
{
|
|
2464
|
+
when: "--family=custom_signal --conversation-id=<conversation_id>",
|
|
2465
|
+
runs: ["trace-signals"],
|
|
2466
|
+
flags: [{ name: "--conversation-id", type: "string", required: true, description: "Every custom signal hit on one trace (runs trace-signals)" }]
|
|
2467
|
+
}
|
|
2468
|
+
] : [{
|
|
2469
|
+
when: `--family=${family}`,
|
|
2470
|
+
runs: [DETECTION_FAMILY_HANDLERS[family]],
|
|
2471
|
+
...DETECTION_SUBFAMILIES[family] ? { subfamilies: DETECTION_SUBFAMILIES[family] } : {},
|
|
2472
|
+
flags: handlerFlags(DETECTION_FAMILY_HANDLERS[family], "--family")
|
|
2473
|
+
}])
|
|
2474
|
+
};
|
|
2475
|
+
case "detections tools":
|
|
2476
|
+
return {
|
|
2477
|
+
...base,
|
|
2478
|
+
positionals: [{ name: "tool_name", required: false, description: "One tool: its failure examples (without it: the leaderboard)" }],
|
|
2479
|
+
routes: [
|
|
2480
|
+
{ when: "no <tool_name>", runs: ["tool-failures"], flags: handlerFlags("tool-failures") },
|
|
2481
|
+
{ when: "<tool_name>", runs: ["tool-failure-detail"], flags: handlerFlags("tool-failure-detail") }
|
|
2482
|
+
]
|
|
2483
|
+
};
|
|
2484
|
+
case "review": {
|
|
2485
|
+
const verdict = { name: "--verdict", type: "enum", enum: [...REVIEW_VERDICTS], default: "false_positive", required: false, description: "What the review says" };
|
|
2486
|
+
const alias = (name, of) => ({ name, type: "string", required: false, description: `Alias of ${of}`, alias_of: of });
|
|
2487
|
+
const falsePositiveFlags = [verdict, ...handlerFlags("false-positive", "--undo"), alias("--note", "--reason")];
|
|
2488
|
+
return {
|
|
2489
|
+
...base,
|
|
2490
|
+
positionals: [
|
|
2491
|
+
{ name: "kind", required: true, enum: ["problem", "problem-detection", "quote", "tool", "laziness", "hallucination", "custom-signal"] },
|
|
2492
|
+
{ name: "target", required: true, description: "Problem id (problem, problem-detection), signal id (custom-signal), else the trace ID" }
|
|
2493
|
+
],
|
|
2494
|
+
flags: falsePositiveFlags,
|
|
2495
|
+
routes: [
|
|
2496
|
+
{ when: "problem-detection (any verdict)", runs: ["detection-review"], flags: [verdict, ...handlerFlags("detection-review", "--verdict"), alias("--reason", "--note")] },
|
|
2497
|
+
{ when: "custom-signal --verdict=correct", runs: ["signal-label"], flags: [verdict, ...handlerFlags("signal-label", "--label"), alias("--reason", "--note")] },
|
|
2498
|
+
{ when: "any other kind or verdict (clear = --undo; problem cannot be cleared; correct is rejected)", runs: ["false-positive"], flags: falsePositiveFlags }
|
|
2499
|
+
]
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
case "reviews list":
|
|
2503
|
+
return {
|
|
2504
|
+
...base,
|
|
2505
|
+
positionals: [{ name: "conversation_id", required: false, description: "List the marks on one trace instead of the whole workspace" }],
|
|
2506
|
+
flags: [
|
|
2507
|
+
...handlerFlags("false-positives", "--conversation-id", "--kind"),
|
|
2508
|
+
{ name: "--kind", type: "enum", enum: ["quote", "tool", "laziness", "hallucination", "custom-signal", "problem-detection"], required: false, description: "Only this kind of mark (problem-detection needs <conversation_id>)" },
|
|
2509
|
+
{ name: "--attribution-id", type: "string", required: false, description: "With --kind=problem-detection: only this detection (UUID)" }
|
|
2510
|
+
],
|
|
2511
|
+
routes: [
|
|
2512
|
+
{ when: "--kind=problem-detection <conversation_id>", runs: ["detection-reviews"] },
|
|
2513
|
+
{ when: "otherwise", runs: ["false-positives"] }
|
|
2514
|
+
]
|
|
2515
|
+
};
|
|
2516
|
+
default:
|
|
2517
|
+
return base;
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
1846
2520
|
|
|
1847
2521
|
// src/cli.ts
|
|
1848
2522
|
import { fileURLToPath } from "url";
|
|
1849
2523
|
import { realpathSync } from "fs";
|
|
1850
2524
|
async function runPromptsCommand(...args) {
|
|
1851
|
-
return (await import("./prompts-
|
|
2525
|
+
return (await import("./prompts-3h5jcjrw.js")).runPromptsCommand(...args);
|
|
1852
2526
|
}
|
|
1853
2527
|
async function runSkillsCommand(...args) {
|
|
1854
|
-
return (await import("./skills-
|
|
2528
|
+
return (await import("./skills-vq3njc8a.js")).runSkillsCommand(...args);
|
|
1855
2529
|
}
|
|
1856
2530
|
async function runFixCommand(...args) {
|
|
1857
|
-
return (await import("./fix-
|
|
2531
|
+
return (await import("./fix-0qdtdz5h.js")).runFixCommand(...args);
|
|
1858
2532
|
}
|
|
1859
2533
|
async function runFixesCommand(...args) {
|
|
1860
|
-
return (await import("./fix-
|
|
2534
|
+
return (await import("./fix-0qdtdz5h.js")).runFixesCommand(...args);
|
|
1861
2535
|
}
|
|
1862
2536
|
async function runHarnessCommand(...args) {
|
|
1863
|
-
return (await import("./harness-
|
|
2537
|
+
return (await import("./harness-vzy7n7ea.js")).runHarnessCommand(...args);
|
|
1864
2538
|
}
|
|
1865
2539
|
async function runSyncCommand(...args) {
|
|
1866
|
-
return (await import("./sync-
|
|
2540
|
+
return (await import("./sync-hq9dpqpr.js")).runSyncCommand(...args);
|
|
1867
2541
|
}
|
|
1868
2542
|
async function runRegistryCommand(...args) {
|
|
1869
|
-
return (await import("./registry-
|
|
2543
|
+
return (await import("./registry-s2vdsfw8.js")).runRegistryCommand(...args);
|
|
1870
2544
|
}
|
|
1871
2545
|
async function runDoctorCommand(...args) {
|
|
1872
|
-
return (await import("./doctor-
|
|
2546
|
+
return (await import("./doctor-q21mr5ya.js")).runDoctorCommand(...args);
|
|
1873
2547
|
}
|
|
1874
2548
|
async function runStatusCommand(...args) {
|
|
1875
|
-
return (await import("./doctor-
|
|
2549
|
+
return (await import("./doctor-q21mr5ya.js")).runStatusCommand(...args);
|
|
1876
2550
|
}
|
|
1877
2551
|
async function runAuthCommand(...args) {
|
|
1878
|
-
return (await import("./profile-commands-
|
|
2552
|
+
return (await import("./profile-commands-6e2dgv72.js")).runAuthCommand(...args);
|
|
1879
2553
|
}
|
|
1880
2554
|
async function runConfigCommand(...args) {
|
|
1881
|
-
return (await import("./profile-commands-
|
|
2555
|
+
return (await import("./profile-commands-6e2dgv72.js")).runConfigCommand(...args);
|
|
1882
2556
|
}
|
|
1883
2557
|
async function runProfilesCommand(...args) {
|
|
1884
|
-
return (await import("./profile-commands-
|
|
2558
|
+
return (await import("./profile-commands-6e2dgv72.js")).runProfilesCommand(...args);
|
|
1885
2559
|
}
|
|
1886
2560
|
async function runAgentCommand(...args) {
|
|
1887
|
-
return (await import("./agent-
|
|
2561
|
+
return (await import("./agent-t7dxmpdq.js")).runAgentCommand(...args);
|
|
1888
2562
|
}
|
|
1889
2563
|
async function runProductionCommand(...args) {
|
|
1890
|
-
return (await import("./production-intelligence-
|
|
2564
|
+
return (await import("./production-intelligence-c8e0xw3n.js")).runProductionCommand(...args);
|
|
1891
2565
|
}
|
|
1892
2566
|
function parseArgs(argv) {
|
|
1893
2567
|
const args = argv.slice(2);
|
|
@@ -1904,7 +2578,7 @@ function parseArgs(argv) {
|
|
|
1904
2578
|
if (!endOfOptions && arg === "--" && command !== "") {
|
|
1905
2579
|
endOfOptions = true;
|
|
1906
2580
|
} else if (!endOfOptions && arg.startsWith("--")) {
|
|
1907
|
-
i = parseFlag(args, i, flags, command === "", bareFlags, command);
|
|
2581
|
+
i = parseFlag(args, i, flags, command === "", bareFlags, parseCommandFor(command, positional));
|
|
1908
2582
|
} else if (!endOfOptions && command !== "" && /^-[A-Za-z]/.test(arg)) {
|
|
1909
2583
|
shortFlags.push(arg);
|
|
1910
2584
|
} else if (command === "") {
|
|
@@ -2090,58 +2764,35 @@ function buildResumeCommand(command, flags) {
|
|
|
2090
2764
|
];
|
|
2091
2765
|
}
|
|
2092
2766
|
function printHelp() {
|
|
2093
|
-
console.log(`Usage: moda <
|
|
2767
|
+
console.log(`Usage: moda <noun> <verb> [args] [flags]
|
|
2094
2768
|
|
|
2095
2769
|
Start here (goal -> command):
|
|
2096
|
-
find something in traces moda search "<query>" (whole traces: moda traces --search=TEXT)
|
|
2097
|
-
read one trace moda
|
|
2098
|
-
what is broken moda overview, then moda problems
|
|
2099
|
-
tool errors moda
|
|
2100
|
-
user emotion and frustration moda
|
|
2101
|
-
raw spans for one trace moda
|
|
2102
|
-
|
|
2770
|
+
find something in traces moda traces search "<query>" (whole traces: moda traces list --search=TEXT)
|
|
2771
|
+
read one trace moda traces get <conversation_id> (whole trace: --all)
|
|
2772
|
+
what is broken moda overview, then moda problems list
|
|
2773
|
+
tool errors moda detections tools, then moda detections tools <tool_name>
|
|
2774
|
+
user emotion and frustration moda detections list --family=emotion (or --family=frustration)
|
|
2775
|
+
raw spans for one trace moda traces spans <conversation_id|trace_id>
|
|
2776
|
+
a detection is wrong moda review <kind> <target> --reason=TEXT
|
|
2777
|
+
every flag of a command moda <noun> <verb> --help (all commands and flags: moda manifest --json)
|
|
2103
2778
|
|
|
2104
2779
|
Production data:
|
|
2105
2780
|
overview Harness health briefing with production signals
|
|
2106
2781
|
investigate Rank production issues (tool failures, frustrations, top Problems)
|
|
2107
2782
|
failures Tool failures only, ranked (no frustrations or Problems)
|
|
2108
2783
|
ask "<question>" Ask Moda a production question (one paid agent run)
|
|
2109
|
-
search "<query>" Search trace messages (keyword/semantic/hybrid)
|
|
2110
|
-
traces Search and filter traces (agent runs)
|
|
2111
|
-
context <conversation_id> Messages around one turn, or the whole trace with --all
|
|
2112
|
-
context <id>[@N] <id>[@N] ... Batch: up to 25 traces in one call (or --ids-from=-|<file>)
|
|
2113
|
-
audit <conversation_id|trace_id> Raw span audit (spans, hierarchy, orphans, duplicates)
|
|
2114
|
-
audit <id> <id> ... Batch audit: up to 25 ids in one call (or --ids-from=-|<file>)
|
|
2115
|
-
world-state <conversation_id> A trace's world state (slots, threads, events)
|
|
2116
2784
|
clusters Browse or search topic clusters
|
|
2117
2785
|
cluster-traces <node_id> List traces in a cluster
|
|
2118
2786
|
users / user <user_id> End users with health and activity (the dashboard Users page)
|
|
2119
2787
|
tail Live-tail new traces/detections as NDJSON (one line per item)
|
|
2120
2788
|
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
hallucinations Grounding detections: contradicted/verified outputs
|
|
2125
|
-
laziness Laziness detections per trace (repeated calls, premature handoffs, ...)
|
|
2126
|
-
tool-failures Tool failure overview
|
|
2127
|
-
tool-failure-detail <tool_name> Per-tool failure examples
|
|
2789
|
+
Traces, Problems, detections and review (moda <noun> --help lists the verbs):
|
|
2790
|
+
${canonicalTreeHelpLines().join(`
|
|
2791
|
+
`)}
|
|
2128
2792
|
|
|
2129
|
-
|
|
2130
|
-
problems Rank cross-signal Problems by root cause (what to fix first)
|
|
2131
|
-
problem <problem_id> One Problem: dossier, or its evidence/reports/traces/feedback
|
|
2132
|
-
problem-impact Live problem/use-case impact, trend and evidence
|
|
2133
|
-
problem-feedback <problem_id> Mark a Problem fixed, dismiss, rename, or flag a bad attribution
|
|
2134
|
-
problem-reopen <problem_id> Reopen a Problem marked fixed
|
|
2135
|
-
problem-linear <problem_id> Show the Linear issue linked to a Problem
|
|
2136
|
-
problem-linear-file <problem_id> File a Problem as a Linear issue, or link an existing one
|
|
2137
|
-
detection-reviews <conv_id> Problem detections on a trace with their review state
|
|
2138
|
-
detection-review <problem_id> Mark a problem detection correct, a false positive, or clear it
|
|
2139
|
-
false-positive <kind> <target> Mark any detection as a false positive
|
|
2140
|
-
false-positives [conv_id] Current false-positive marks (workspace or one trace)
|
|
2793
|
+
Custom signals and feedback:
|
|
2141
2794
|
signals List and manage custom signals
|
|
2142
|
-
signal <signal_id> One custom signal
|
|
2143
|
-
trace-signals <conversation_id> Custom signal hits on one trace
|
|
2144
|
-
signal-label <signal_id> Label a trace fits / does_not_fit (false positive) for a signal
|
|
2795
|
+
signal <signal_id> One custom signal (its hits: moda detections list --family=custom_signal)
|
|
2145
2796
|
feedback "<note>" Flag wrong/missing data or CLI quirks to the Moda team
|
|
2146
2797
|
|
|
2147
2798
|
Setup:
|
|
@@ -2159,13 +2810,13 @@ Groups with their own subcommands (run 'moda <group> --help'):
|
|
|
2159
2810
|
harness Analyze, scan, validate, approve, sync and register the agent harness
|
|
2160
2811
|
prompts Code-first prompt versions: init, status, sync, promote, A/B, propose
|
|
2161
2812
|
replays Replay results: sets, runs, playouts and transcripts
|
|
2162
|
-
skills Generate
|
|
2813
|
+
skills Generate skills; pull (download), push (upload), install
|
|
2163
2814
|
sync Sync the harness graph and prompts to Moda Cloud
|
|
2164
2815
|
registry Push prompts, skills, tools and agent wiring from one JSON manifest
|
|
2165
2816
|
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
<conversation_id> is the trace ID.
|
|
2817
|
+
${legacyNamesHelpLine()}
|
|
2818
|
+
Silent aliases: conversations = traces list, cluster-conversations = cluster-traces, --conversations = --traces,
|
|
2819
|
+
--signal=conversations = --signal=traces. <conversation_id> is the trace ID.
|
|
2169
2820
|
|
|
2170
2821
|
Output: piped stdout, CI or an agent env (CLAUDECODE/CURSOR/MODA_AGENT) => agent JSON; a terminal => prose.
|
|
2171
2822
|
--json | --agent [--stream] | --human Force an output mode (or MODA_FORMAT=human|json|agent|agent-stream)
|
|
@@ -2250,6 +2901,187 @@ function printSubcommandHelp(parent, entry) {
|
|
|
2250
2901
|
console.log(lines.join(`
|
|
2251
2902
|
`));
|
|
2252
2903
|
}
|
|
2904
|
+
function helpTable(rows) {
|
|
2905
|
+
if (rows.length === 0)
|
|
2906
|
+
return [" (none besides the global flags below)"];
|
|
2907
|
+
const width = Math.min(34, Math.max(...rows.map((row) => row.name.length)));
|
|
2908
|
+
return rows.map((row) => row.name.length > width ? ` ${row.name}
|
|
2909
|
+
${" ".repeat(width)} ${row.description}` : ` ${row.name.padEnd(width)} ${row.description}`);
|
|
2910
|
+
}
|
|
2911
|
+
function routedFlagRows(command, drop = []) {
|
|
2912
|
+
const spec = flagSpecFor(command);
|
|
2913
|
+
if (!spec)
|
|
2914
|
+
return [];
|
|
2915
|
+
return subcommandHelpFlags(spec).filter((row) => !drop.some((name) => row.name === name || row.name.startsWith(`${name}=`)));
|
|
2916
|
+
}
|
|
2917
|
+
function detectionsListHelp(focus) {
|
|
2918
|
+
const lines = ["Families (--family=):"];
|
|
2919
|
+
lines.push(...helpTable(DETECTION_FAMILIES.map((family) => {
|
|
2920
|
+
const subfamilies = DETECTION_SUBFAMILIES[family];
|
|
2921
|
+
const runs = family === "custom_signal" ? "signal --detections / trace-signals" : DETECTION_FAMILY_HANDLERS[family];
|
|
2922
|
+
return {
|
|
2923
|
+
name: subfamilies ? `${family}[:<family>]` : family,
|
|
2924
|
+
description: `runs ${runs}${subfamilies ? `; <family>: ${subfamilies.join(", ")}` : ""}`
|
|
2925
|
+
};
|
|
2926
|
+
})));
|
|
2927
|
+
const base = focus?.split(":")[0];
|
|
2928
|
+
for (const family of DETECTION_FAMILIES.filter((entry) => !base || entry === base)) {
|
|
2929
|
+
lines.push("", `Flags with --family=${family}:`);
|
|
2930
|
+
if (family === "custom_signal") {
|
|
2931
|
+
lines.push(...helpTable([
|
|
2932
|
+
{ name: "--signal-id=ID", description: "One signal's recent hits (paged; runs signal <id> --detections)" },
|
|
2933
|
+
...routedFlagRows("signal", ["--detections"]),
|
|
2934
|
+
{ name: "--conversation-id=ID", description: "Instead of --signal-id: every signal hit on one trace (runs trace-signals)" }
|
|
2935
|
+
]));
|
|
2936
|
+
} else {
|
|
2937
|
+
lines.push(...helpTable(routedFlagRows(DETECTION_FAMILY_HANDLERS[family], ["--family"])));
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
return lines;
|
|
2941
|
+
}
|
|
2942
|
+
function detectionsToolsHelp() {
|
|
2943
|
+
return [
|
|
2944
|
+
"Without <tool_name> (the leaderboard, runs tool-failures):",
|
|
2945
|
+
...helpTable(routedFlagRows("tool-failures")),
|
|
2946
|
+
"",
|
|
2947
|
+
"With <tool_name> (failure examples, runs tool-failure-detail):",
|
|
2948
|
+
...helpTable(routedFlagRows("tool-failure-detail", ["--tool-name"]))
|
|
2949
|
+
];
|
|
2950
|
+
}
|
|
2951
|
+
function reviewHelp() {
|
|
2952
|
+
return [
|
|
2953
|
+
"Kinds and targets: problem <problem_id>, problem-detection <problem_id>, quote|tool|laziness|hallucination <conversation_id>,",
|
|
2954
|
+
"custom-signal <signal_id>.",
|
|
2955
|
+
"",
|
|
2956
|
+
"Verdicts (--verdict, default false_positive) and what each runs:",
|
|
2957
|
+
" problem false_positive: dismiss the Problem (false-positive problem); clear and correct are rejected",
|
|
2958
|
+
" problem-detection every verdict: the detection review the dashboard thumbs write (detection-review)",
|
|
2959
|
+
" custom-signal false_positive: does_not_fit label; clear: removes the label (false-positive custom-signal);",
|
|
2960
|
+
" correct: fits label (signal-label)",
|
|
2961
|
+
" quote, tool, laziness, hallucination",
|
|
2962
|
+
" false_positive or clear (false-positive <kind> [--undo]); correct is rejected",
|
|
2963
|
+
"",
|
|
2964
|
+
"Flags:",
|
|
2965
|
+
...helpTable([
|
|
2966
|
+
{ name: `--verdict=${REVIEW_VERDICTS.join("|")}`, description: "What the review says [default false_positive]" },
|
|
2967
|
+
...routedFlagRows("false-positive", ["--undo"]),
|
|
2968
|
+
{ name: "--note=TEXT", description: "Alias of --reason" }
|
|
2969
|
+
])
|
|
2970
|
+
];
|
|
2971
|
+
}
|
|
2972
|
+
function reviewsListHelp() {
|
|
2973
|
+
return [
|
|
2974
|
+
"Flags:",
|
|
2975
|
+
...helpTable([
|
|
2976
|
+
...routedFlagRows("false-positives", ["--kind"]),
|
|
2977
|
+
{ name: "--kind=<value>", description: "Only this kind of mark: quote, tool, laziness, hallucination, custom-signal, or problem-detection (needs <conversation_id>; runs detection-reviews)" },
|
|
2978
|
+
{ name: "--attribution-id=ID", description: "With --kind=problem-detection: only this detection (UUID)" }
|
|
2979
|
+
])
|
|
2980
|
+
];
|
|
2981
|
+
}
|
|
2982
|
+
function renderLeafHelp(leaf, options = {}) {
|
|
2983
|
+
const lines = [`moda ${leaf.path} — ${leaf.summary}`];
|
|
2984
|
+
if (options.legacy) {
|
|
2985
|
+
lines.push(`'moda ${options.legacy.legacy}' is a legacy name for '${options.legacy.use}'; it still works.${options.legacy.note ? ` ${options.legacy.note}` : ""}`);
|
|
2986
|
+
}
|
|
2987
|
+
const aliases = legacyNamesForPath(leaf.path);
|
|
2988
|
+
if (aliases.length > 0)
|
|
2989
|
+
lines.push(`Legacy names (still work): ${aliases.join(", ")}`);
|
|
2990
|
+
const handlers = leaf.routes ?? [leaf.handler];
|
|
2991
|
+
if (handlers.some((name) => commandRegistry.find(name)?.mutability === "write"))
|
|
2992
|
+
lines.push("This command can mutate state.");
|
|
2993
|
+
const handlerDefinition = leaf.handler ? commandRegistry.find(leaf.handler) : undefined;
|
|
2994
|
+
if (leaf.handler && !leaf.handlerSubcommand) {
|
|
2995
|
+
for (const note of handlerDefinition?.helpNotes ?? [])
|
|
2996
|
+
lines.push("", renameCommands(note, { [leaf.handler]: leaf.path }));
|
|
2997
|
+
}
|
|
2998
|
+
lines.push("", `Usage: moda ${leaf.path}${leaf.args ? ` ${leaf.args}` : ""} [flags]`);
|
|
2999
|
+
const spec = leafFlagSpec(leaf);
|
|
3000
|
+
let ownFlags = [];
|
|
3001
|
+
if (spec) {
|
|
3002
|
+
const body = renderFlagHelp(leaf.path, { ...spec, subcommands: undefined }).slice(1);
|
|
3003
|
+
lines.push(...body.map((line) => renameCommands(line, { [leaf.handler]: leaf.path })));
|
|
3004
|
+
ownFlags = specFlagKeys(spec);
|
|
3005
|
+
} else if (leaf.path === "skills push") {
|
|
3006
|
+
const entry = SKILLS_SUBCOMMAND_HELP.find((subcommand) => subcommand.name === "push");
|
|
3007
|
+
lines.push("", "Flags:", ...helpTable(entry?.flags ?? []));
|
|
3008
|
+
} else if (leaf.path === "detections list") {
|
|
3009
|
+
lines.push("", ...detectionsListHelp(options.focus));
|
|
3010
|
+
} else if (leaf.path === "detections tools") {
|
|
3011
|
+
lines.push("", ...detectionsToolsHelp());
|
|
3012
|
+
} else if (leaf.path === "review") {
|
|
3013
|
+
lines.push("", ...reviewHelp());
|
|
3014
|
+
} else if (leaf.path === "reviews list") {
|
|
3015
|
+
lines.push("", ...reviewsListHelp());
|
|
3016
|
+
}
|
|
3017
|
+
if (leaf.examples.length > 0)
|
|
3018
|
+
lines.push("", "Examples:", ...leaf.examples.map((example) => ` ${example}`));
|
|
3019
|
+
lines.push("", ...globalFlagHelpLines(ownFlags), "Any other flag is rejected.");
|
|
3020
|
+
return lines.join(`
|
|
3021
|
+
`);
|
|
3022
|
+
}
|
|
3023
|
+
function printTreeHelp(parsed) {
|
|
3024
|
+
const { command, positionals, flags } = parsed;
|
|
3025
|
+
if (command === "review") {
|
|
3026
|
+
console.log(renderLeafHelp(leafFor("review", undefined)));
|
|
3027
|
+
return true;
|
|
3028
|
+
}
|
|
3029
|
+
if (CANONICAL_NOUNS.includes(command) && positionals[0] !== undefined) {
|
|
3030
|
+
const leaf2 = leafFor(command, positionals[0]);
|
|
3031
|
+
if (leaf2) {
|
|
3032
|
+
console.log(renderLeafHelp(leaf2, { focus: flags.family }));
|
|
3033
|
+
return true;
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
const legacy = legacyNameFor(command, positionals[0], flags);
|
|
3037
|
+
if (!legacy || legacy.silent)
|
|
3038
|
+
return false;
|
|
3039
|
+
const [noun, verb] = usePath(legacy).split(" ");
|
|
3040
|
+
const leaf = leafFor(noun, verb);
|
|
3041
|
+
if (!leaf) {
|
|
3042
|
+
const target = commandRegistry.find(noun);
|
|
3043
|
+
const entry = target?.subcommands?.find((subcommand) => subcommand.name === verb);
|
|
3044
|
+
if (!target || !entry)
|
|
3045
|
+
return false;
|
|
3046
|
+
console.log(`'moda ${legacy.legacy}' is a legacy name for '${legacy.use}'; it still works. ${legacy.note ?? ""}`.trim());
|
|
3047
|
+
printSubcommandHelp(target, entry);
|
|
3048
|
+
return true;
|
|
3049
|
+
}
|
|
3050
|
+
const focus = /--family=([a-z_]+)/.exec(legacy.use)?.[1];
|
|
3051
|
+
console.log(renderLeafHelp(leaf, { legacy, ...focus ? { focus } : {} }));
|
|
3052
|
+
return true;
|
|
3053
|
+
}
|
|
3054
|
+
function treeSubcommandHelp(noun) {
|
|
3055
|
+
return leavesOf(noun).map((leaf) => ({
|
|
3056
|
+
name: leaf.verb,
|
|
3057
|
+
description: leaf.summary,
|
|
3058
|
+
usage: `moda ${leaf.path}${leaf.args ? ` ${leaf.args}` : ""} [flags]`,
|
|
3059
|
+
examples: [...leaf.examples]
|
|
3060
|
+
}));
|
|
3061
|
+
}
|
|
3062
|
+
function canonicalTreeHelpLines() {
|
|
3063
|
+
const rows = CANONICAL_LEAVES.filter((leaf) => leaf.noun !== "skills").map((leaf) => {
|
|
3064
|
+
const left = `${leaf.path}${leaf.args ? ` ${leaf.args}` : ""}`;
|
|
3065
|
+
return left.length > 32 ? ` ${left}
|
|
3066
|
+
${" ".repeat(33)}${leaf.summary}` : ` ${left.padEnd(32)} ${leaf.summary}`;
|
|
3067
|
+
});
|
|
3068
|
+
return rows;
|
|
3069
|
+
}
|
|
3070
|
+
function legacyNamesHelpLine() {
|
|
3071
|
+
const words = ["Legacy names (still work; `moda <name> --help` shows the new form):", ...visibleLegacyNames().map((entry, index, all) => `${entry.legacy}${index < all.length - 1 ? "," : "."}`)];
|
|
3072
|
+
const lines = [];
|
|
3073
|
+
let line = "";
|
|
3074
|
+
for (const word of words) {
|
|
3075
|
+
if (line && line.length + word.length + 1 > 110) {
|
|
3076
|
+
lines.push(line);
|
|
3077
|
+
line = " ";
|
|
3078
|
+
}
|
|
3079
|
+
line += line ? ` ${word}` : word;
|
|
3080
|
+
}
|
|
3081
|
+
lines.push(line);
|
|
3082
|
+
return lines.join(`
|
|
3083
|
+
`);
|
|
3084
|
+
}
|
|
2253
3085
|
async function attachWindows(rows, windowSize) {
|
|
2254
3086
|
return Promise.all(rows.map(async (row) => {
|
|
2255
3087
|
const anchor = asRecord(row.anchor);
|
|
@@ -2309,7 +3141,7 @@ function withoutRepeatedFrustrationWindow(data) {
|
|
|
2309
3141
|
return { ...data, frustrations };
|
|
2310
3142
|
}
|
|
2311
3143
|
var NOT_FOUND_HINTS = {
|
|
2312
|
-
trace: 'Find trace ids with: moda search "<text>" or moda traces --search=<text>',
|
|
3144
|
+
trace: 'Find trace ids with: moda traces search "<text>" or moda traces --search=<text>',
|
|
2313
3145
|
cluster: "List cluster node ids with: moda clusters",
|
|
2314
3146
|
problem: "List problem ids with: moda problems"
|
|
2315
3147
|
};
|
|
@@ -2323,7 +3155,7 @@ function noWorldStateWarnings(conversationId, isMissing) {
|
|
|
2323
3155
|
return;
|
|
2324
3156
|
return {
|
|
2325
3157
|
warnings: [
|
|
2326
|
-
`No world state for trace "${conversationId}": the trace may not exist in this tenant, or its world state has not been generated yet. Check the trace exists with: moda
|
|
3158
|
+
`No world state for trace "${conversationId}": the trace may not exist in this tenant, or its world state has not been generated yet. Check the trace exists with: moda traces get ${shellArg(contextTarget(conversationId))} --metadata-only`
|
|
2327
3159
|
]
|
|
2328
3160
|
};
|
|
2329
3161
|
}
|
|
@@ -2363,11 +3195,11 @@ function auditOutput(command, params, data) {
|
|
|
2363
3195
|
return { data: auditRecord ? { ...auditRecord, truncated: false, has_more: false, next_cursor: null } : data, options: {} };
|
|
2364
3196
|
}
|
|
2365
3197
|
const limit = params.limit ?? AUDIT_DEFAULT_LIMIT;
|
|
2366
|
-
const perTrace = "audit one OTLP trace at a time with `moda
|
|
3198
|
+
const perTrace = "audit one OTLP trace at a time with `moda traces spans <trace_id> --kind=trace` (ids in summary.trace_ids)";
|
|
2367
3199
|
const warnings = [];
|
|
2368
3200
|
const nextCommands = [];
|
|
2369
3201
|
const auditFlags = (extra) => [
|
|
2370
|
-
`moda ${command} ${shellArg(params.id)}`,
|
|
3202
|
+
`${canonicalCommandText(`moda ${command}`)} ${shellArg(params.id)}`,
|
|
2371
3203
|
params.kind && params.kind !== "auto" ? `--kind=${params.kind}` : "",
|
|
2372
3204
|
params.include_raw === true ? "--include-raw" : "",
|
|
2373
3205
|
params.include_bodies === true ? "--include-bodies" : "",
|
|
@@ -2554,7 +3386,7 @@ async function runTailCommand(params, context) {
|
|
|
2554
3386
|
total,
|
|
2555
3387
|
coverage_pct: coveragePct,
|
|
2556
3388
|
pages_scanned: pages,
|
|
2557
|
-
detail: `Scanned ${scanned}${total !== null ? ` of ${total}` : ""} detection(s) in the last day${coveragePct !== null ? ` (${coveragePct}%)` : ""}. /emotions is ranked by score with no time ordering or cursor, so this follows the highest-scoring detections and lower-scoring ones may not appear. Use --full-scan for completeness (many more requests, up to the API offset ceiling of 10000), or \`moda
|
|
3389
|
+
detail: `Scanned ${scanned}${total !== null ? ` of ${total}` : ""} detection(s) in the last day${coveragePct !== null ? ` (${coveragePct}%)` : ""}. /emotions is ranked by score with no time ordering or cursor, so this follows the highest-scoring detections and lower-scoring ones may not appear. Use --full-scan for completeness (many more requests, up to the API offset ceiling of 10000), or \`moda detections list --family=emotion --offset=N\` to page manually.`
|
|
2558
3390
|
});
|
|
2559
3391
|
} else if (total !== null) {
|
|
2560
3392
|
emitMeta({
|
|
@@ -2695,8 +3527,8 @@ var WINDOW_FLAG_HINTS = {
|
|
|
2695
3527
|
};
|
|
2696
3528
|
var COMMAND_FLAG_HINTS = {
|
|
2697
3529
|
problems: {
|
|
2698
|
-
family: "'moda problems' ranks every family together. To filter by family use 'moda problems
|
|
2699
|
-
cursor: "'moda problems' has no cursor; 'moda problems
|
|
3530
|
+
family: "'moda problems' ranks every family together. To filter by family use 'moda problems unexplained --family=<family>' (signals no Problem explains yet) or 'moda problems get <problem_id> --traces --family=<family>' (one Problem's traces).",
|
|
3531
|
+
cursor: "'moda problems' has no cursor; 'moda problems unexplained --cursor=<next_cursor>' pages the unexplained signals."
|
|
2700
3532
|
}
|
|
2701
3533
|
};
|
|
2702
3534
|
function parseCommandFlags(schema, args, command, options = {}) {
|
|
@@ -2734,8 +3566,9 @@ function parseCommandFlags(schema, args, command, options = {}) {
|
|
|
2734
3566
|
return close ? `Did you mean ${close}?` : "";
|
|
2735
3567
|
}).filter(Boolean);
|
|
2736
3568
|
const names = unknown.map(kebabFlag).join(", ");
|
|
2737
|
-
const
|
|
2738
|
-
|
|
3569
|
+
const shown = canonicalCommandText(`moda ${command}`);
|
|
3570
|
+
const helpCommand = `${shown} --help`;
|
|
3571
|
+
throw new CliInputError(`Unknown flag${unknown.length > 1 ? "s" : ""} ${names} for '${shown}'.` + (didYouMean.length > 0 ? ` Did you mean ${didYouMean.join(", ")}?` : ""), [...hints, `Valid flags: ${valid.join(", ") || "(none)"}. Run '${helpCommand}'.`].join(" "), `unknown_flag:${unknown[0].replace(/_/g, "-")}`, {
|
|
2739
3572
|
field: kebabFlag(unknown[0]),
|
|
2740
3573
|
allowed: valid,
|
|
2741
3574
|
...didYouMean.length > 0 ? { didYouMean: [...new Set(didYouMean)] } : {},
|
|
@@ -2761,7 +3594,7 @@ function attachFlagDetails(error, schema, command) {
|
|
|
2761
3594
|
field: field.name,
|
|
2762
3595
|
...field.enum ? { allowed: field.enum } : {},
|
|
2763
3596
|
...field.min !== undefined || field.max !== undefined ? { range: { ...field.min !== undefined ? { min: field.min } : {}, ...field.max !== undefined ? { max: field.max } : {} } } : {},
|
|
2764
|
-
suggestedCommands: [`moda ${command} --help`]
|
|
3597
|
+
suggestedCommands: [`${canonicalCommandText(`moda ${command}`)} --help`]
|
|
2765
3598
|
});
|
|
2766
3599
|
}
|
|
2767
3600
|
function parseSpecFlags(name, args, command = name) {
|
|
@@ -2914,7 +3747,7 @@ async function searchFallbackToTraces(params) {
|
|
|
2914
3747
|
const describe = literal ? "trace-level keyword matches (each trace contains the words)" : `trace-level ${traceMode}-ranked related traces (they may not contain the exact words; fallback.total is the candidate pool, not a match count)`;
|
|
2915
3748
|
const nextCommands = [];
|
|
2916
3749
|
if (first) {
|
|
2917
|
-
nextCommands.push({ command: `moda
|
|
3750
|
+
nextCommands.push({ command: `moda traces spans ${first}`, purpose: "Open the top trace to locate the relevant message.", mutability: "read", requires_approval: false });
|
|
2918
3751
|
}
|
|
2919
3752
|
if (hasMore) {
|
|
2920
3753
|
nextCommands.push({ command: `${tracesCommand} --offset=${nextOffset}`, purpose: "Page through further trace-level results.", mutability: "read", requires_approval: false });
|
|
@@ -2936,7 +3769,7 @@ async function searchFallbackToTraces(params) {
|
|
|
2936
3769
|
...hasMore ? { next_offset: nextOffset } : {}
|
|
2937
3770
|
}
|
|
2938
3771
|
},
|
|
2939
|
-
warning: `Message-level search returned 0 results, so these are ${describe}, from \`moda traces --search\`. Each result is a whole trace, not a message, with no score or message index. Open one with \`moda
|
|
3772
|
+
warning: `Message-level search returned 0 results, so these are ${describe}, from \`moda traces --search\`. Each result is a whole trace, not a message, with no score or message index. Open one with \`moda traces spans <conversation_id>\` or \`moda traces get <conversation_id>\` to find the matching turn.` + (hasMore ? " More exist: page with the suggested `moda traces --search ... --offset` command." : ""),
|
|
2940
3773
|
nextCommands
|
|
2941
3774
|
};
|
|
2942
3775
|
}
|
|
@@ -3061,9 +3894,9 @@ function processingDisabledWarning(data, family) {
|
|
|
3061
3894
|
`${family === "emotion" ? "Emotion" : "Frustration"} analysis is disabled for this tenant (${setting} = false), ` + "so zero results do NOT mean users are not frustrated. Enable it in Settings → Account → Signal processing; " + "only traces ingested after enabling are analyzed."
|
|
3062
3895
|
];
|
|
3063
3896
|
}
|
|
3064
|
-
var CONTEXT_USAGE = "Usage: moda
|
|
3065
|
-
var CONTEXT_ALL_USAGE = "Usage: moda
|
|
3066
|
-
var CONTEXT_BATCH_USAGE = `Usage: moda
|
|
3897
|
+
var CONTEXT_USAGE = "Usage: moda traces get <conversation_id> [--msg-index=N (alias --message-index)] [--window=0-5 | --metadata-only]";
|
|
3898
|
+
var CONTEXT_ALL_USAGE = "Usage: moda traces get <conversation_id> --all [--from=N] [--max-messages=N]";
|
|
3899
|
+
var CONTEXT_BATCH_USAGE = `Usage: moda traces get <id>[@msg_index] <id>[@msg_index] ... | --ids-from=-|<file> [--window=0-5 | --metadata-only | --all [--from=N] [--max-messages=N]] (max ${BATCH_MAX_TARGETS} targets)`;
|
|
3067
3900
|
var CONTEXT_FLAG_ALIASES = { "message-index": "msg-index" };
|
|
3068
3901
|
var CONTEXT_MAX_WINDOW = 5;
|
|
3069
3902
|
var EMPTY_ANCHOR_VALUES = new Set(["", "null", "undefined", "none"]);
|
|
@@ -3237,7 +4070,7 @@ async function readFullTranscript(conversationId, opts = {}) {
|
|
|
3237
4070
|
if (next !== null) {
|
|
3238
4071
|
const last = asNumber(returned[returned.length - 1]?.index) ?? next - 1;
|
|
3239
4072
|
const nextMax = firstMissing !== null ? Math.min(pageSize, Math.max(kept * 2, 50)) : pageSize;
|
|
3240
|
-
const continueWith = `moda
|
|
4073
|
+
const continueWith = `moda traces get ${shellArg(contextTarget(conversationId))} --all --from=${next} --max-messages=${nextMax}`;
|
|
3241
4074
|
out.push(`Trace has ${total} messages; returned messages ${start}-${last}. ` + (firstMissing !== null ? `Stopped at ${kept} of the ${messages.length} messages read to stay under the agent output byte limit (--json has no limit). ` : "") + `Continue with: ${continueWith}`);
|
|
3242
4075
|
nextCommands.push({
|
|
3243
4076
|
command: continueWith,
|
|
@@ -3250,7 +4083,7 @@ async function readFullTranscript(conversationId, opts = {}) {
|
|
|
3250
4083
|
out.push(`Message order may not be chronological: ${tied} of ${total} messages share a timestamp with another message (ordered by id, not time)${synthetic > 0 ? ` and ${synthetic} carry a synthetic pre-2022 timestamp` : ""}.`);
|
|
3251
4084
|
}
|
|
3252
4085
|
if (legacy && returned.some(isTruncated)) {
|
|
3253
|
-
out.push(`This Moda server is older and truncates message text, tool-call inputs and tool results longer than ${CONTEXT_MESSAGE_TRUNCATE_CHARS} characters. For full content (prompts, tool arguments/results) use \`moda
|
|
4086
|
+
out.push(`This Moda server is older and truncates message text, tool-call inputs and tool results longer than ${CONTEXT_MESSAGE_TRUNCATE_CHARS} characters. For full content (prompts, tool arguments/results) use \`moda traces spans <conversation_id> --include-bodies\` (or \`--include-raw\` for the verbatim events).`);
|
|
3254
4087
|
}
|
|
3255
4088
|
return {
|
|
3256
4089
|
data: stripLoneSurrogates({
|
|
@@ -3317,7 +4150,7 @@ async function readContextItem(command, params, inputWarnings, fits) {
|
|
|
3317
4150
|
const center = asNumber(asRecord(ctxRecord.context)?.center_index) ?? params.msg_index ?? 0;
|
|
3318
4151
|
const span = Math.min(params.window * 2 + 1, CONTEXT_ALL_MAX_MESSAGES);
|
|
3319
4152
|
const from = Math.max(0, center - Math.floor((span - 1) / 2));
|
|
3320
|
-
const wide = `moda
|
|
4153
|
+
const wide = `moda traces get ${shellArg(contextTarget(params.conversation_id))} --all --from=${from} --max-messages=${span}`;
|
|
3321
4154
|
warnings.push(`--window=${params.window} is above the maximum of ${CONTEXT_MAX_WINDOW}, so this shows ±${CONTEXT_MAX_WINDOW} messages. For a wider slice run: ${wide}`);
|
|
3322
4155
|
nextCommands.push({ command: wide, purpose: `Read ±${params.window} messages around the anchor.`, mutability: "read", requires_approval: false });
|
|
3323
4156
|
}
|
|
@@ -3329,8 +4162,8 @@ async function readContextItem(command, params, inputWarnings, fits) {
|
|
|
3329
4162
|
}
|
|
3330
4163
|
};
|
|
3331
4164
|
}
|
|
3332
|
-
var AUDIT_USAGE = "Usage: moda
|
|
3333
|
-
var AUDIT_BATCH_USAGE = `Usage: moda
|
|
4165
|
+
var AUDIT_USAGE = "Usage: moda traces spans <conversation_id|trace_id> [--kind=auto|trace|conversation] [--include-raw] [--include-bodies] [--limit=N] [--cursor=<next_cursor>]";
|
|
4166
|
+
var AUDIT_BATCH_USAGE = `Usage: moda traces spans <id> <id> ... | --ids-from=-|<file> [--kind=auto|trace|conversation] [--include-raw] [--include-bodies] [--limit=N] (max ${BATCH_MAX_TARGETS} targets)`;
|
|
3334
4167
|
async function readAuditItem(command, params, budget) {
|
|
3335
4168
|
const query2 = new URLSearchParams;
|
|
3336
4169
|
if (params.kind && params.kind !== "auto")
|
|
@@ -3443,7 +4276,7 @@ function writeBatchResult(command, context, targets, results, batchWarnings, fla
|
|
|
3443
4276
|
const nextCommands = [];
|
|
3444
4277
|
if (remaining.length > 0) {
|
|
3445
4278
|
const lines = remaining.map((target) => shellArg(target.raw)).join(" ");
|
|
3446
|
-
const continueWith = [`printf '%s\\n' ${lines} |`, `moda ${command}
|
|
4279
|
+
const continueWith = [`printf '%s\\n' ${lines} |`, canonicalCommandText(`moda ${command}`), "--ids-from=-", ...renderBatchFlags(flags)].join(" ");
|
|
3447
4280
|
warnings.push(`Returned ${kept2} of ${targets.length} targets to stay under the agent output byte limit (--json has no limit). Read the other ${remaining.length} with: ${continueWith}`);
|
|
3448
4281
|
nextCommands.push({
|
|
3449
4282
|
command: continueWith,
|
|
@@ -3652,7 +4485,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3652
4485
|
}
|
|
3653
4486
|
case "search": {
|
|
3654
4487
|
if (!positional || positional.trim() === "") {
|
|
3655
|
-
throw new CliInputError("<query> is required", 'Usage: moda search "<query>" [--mode=keyword|semantic|hybrid] [--user-id=ID] [--conversation-id=ID] [--time-range=RANGE] [--since=DATE] [--until=DATE] [--include-tool-io] [--limit=N]');
|
|
4488
|
+
throw new CliInputError("<query> is required", 'Usage: moda traces search "<query>" [--mode=keyword|semantic|hybrid] [--user-id=ID] [--conversation-id=ID] [--time-range=RANGE] [--since=DATE] [--until=DATE] [--include-tool-io] [--limit=N]');
|
|
3656
4489
|
}
|
|
3657
4490
|
const args = withStringIdFlags(flagsToArgs(flags, "q", positional), flags);
|
|
3658
4491
|
const params = parseSpecFlags("search", args, command);
|
|
@@ -3708,12 +4541,12 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3708
4541
|
}
|
|
3709
4542
|
case "world-state": {
|
|
3710
4543
|
if (!positional) {
|
|
3711
|
-
throw new CliInputError("<conversation_id> is required", "Usage: moda
|
|
4544
|
+
throw new CliInputError("<conversation_id> is required", "Usage: moda traces state <conversation_id> [--summary-only] [--event-limit=N]");
|
|
3712
4545
|
}
|
|
3713
4546
|
const args = flagsToArgs(flags, "conversation_id", positional);
|
|
3714
4547
|
const params = parseSpecFlags("world-state", args, command);
|
|
3715
4548
|
if (params.snapshot === true && params.replay === true) {
|
|
3716
|
-
throw new CliInputError("Choose either --snapshot or --replay, not both.", "Usage: moda
|
|
4549
|
+
throw new CliInputError("Choose either --snapshot or --replay, not both.", "Usage: moda traces state <conversation_id> [--snapshot --msg-index=N [--block-index=M] [--segment-index=K]] | [--replay --message-count=N [--format=frames|fold-inputs]]");
|
|
3717
4550
|
}
|
|
3718
4551
|
if (params.snapshot === true) {
|
|
3719
4552
|
const query3 = new URLSearchParams;
|
|
@@ -3812,7 +4645,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3812
4645
|
}
|
|
3813
4646
|
case "tool-failure-detail": {
|
|
3814
4647
|
if (!positional) {
|
|
3815
|
-
throw new CliInputError("<tool_name> is required", "Usage: moda
|
|
4648
|
+
throw new CliInputError("<tool_name> is required", "Usage: moda detections tools <tool_name> [--subtype=S] [--days-back=N] [--limit=N (1-20, default 5)] [--offset=N] [--compact | --include-window [--window=N]]");
|
|
3816
4649
|
}
|
|
3817
4650
|
const { "no-include-window": noIncludeWindow, ...toolFlags } = flags;
|
|
3818
4651
|
if (noIncludeWindow !== undefined && noIncludeWindow !== "false") {
|
|
@@ -3843,7 +4676,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3843
4676
|
const subtypeCount = Array.isArray(tfRecord.subtypes) ? tfRecord.subtypes.length : 0;
|
|
3844
4677
|
const exampleCount = Array.isArray(tfRecord.examples) ? tfRecord.examples.length : 0;
|
|
3845
4678
|
const warnings = subtypeCount === 0 && exampleCount === 0 ? [
|
|
3846
|
-
`No failures found for tool "${params.tool_name}" in the last ${params.days_back ?? 7} day(s) — the tool may have no failures in this window, or the name may not exist in this tenant. Run \`moda
|
|
4679
|
+
`No failures found for tool "${params.tool_name}" in the last ${params.days_back ?? 7} day(s) — the tool may have no failures in this window, or the name may not exist in this tenant. Run \`moda detections tools\` to list tools with recorded failures, or widen --days-back.`
|
|
3847
4680
|
] : [];
|
|
3848
4681
|
const knownSubtypes = Array.isArray(tfRecord.subtypes) ? tfRecord.subtypes.map((row) => asString(asRecord(row)?.subtype)).filter((value) => Boolean(value)) : [];
|
|
3849
4682
|
if (params.subtype && knownSubtypes.length > 0 && !knownSubtypes.includes(params.subtype)) {
|
|
@@ -3901,7 +4734,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3901
4734
|
break;
|
|
3902
4735
|
}
|
|
3903
4736
|
if (positional) {
|
|
3904
|
-
throw new CliInputError(`Unknown problems subcommand '${positional}'.`, "Usage: moda problems [--days-back=N] [--limit=N] | moda problems
|
|
4737
|
+
throw new CliInputError(`Unknown problems subcommand '${positional}'.`, "Usage: moda problems [--days-back=N] [--limit=N] | moda problems unexplained [--family=F] [--days-back=N] [--limit=N] [--cursor=TOKEN]. For one Problem use 'moda problems get <id>'.");
|
|
3905
4738
|
}
|
|
3906
4739
|
const args = flagsToArgs(flags);
|
|
3907
4740
|
const params = parseSpecFlags("problems", args, command);
|
|
@@ -3928,7 +4761,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3928
4761
|
break;
|
|
3929
4762
|
}
|
|
3930
4763
|
if (!positional) {
|
|
3931
|
-
throw new CliInputError("<problem_id> is required", "Usage: moda
|
|
4764
|
+
throw new CliInputError("<problem_id> is required", "Usage: moda problems get <problem_id> [--full | --evidence|--reports|--traces|--feedback] [--limit=N] [--cursor=TOKEN] [--family=F] [--door=D]");
|
|
3932
4765
|
}
|
|
3933
4766
|
const args = flagsToArgs(flags, "id", positional);
|
|
3934
4767
|
const params = parseSpecFlags("problem", args, command);
|
|
@@ -3936,11 +4769,11 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3936
4769
|
const views = ["evidence", "reports", "conversations", "feedback"].filter((view2) => view2 === "conversations" ? traceView : params[view2] === true);
|
|
3937
4770
|
if (views.length > 1) {
|
|
3938
4771
|
const got = views.map((v) => v === "conversations" ? "--traces" : `--${v}`).join(" ");
|
|
3939
|
-
throw new CliInputError(`Choose at most one of --evidence, --reports, --traces, --feedback (got ${got}).`, "Usage: moda
|
|
4772
|
+
throw new CliInputError(`Choose at most one of --evidence, --reports, --traces, --feedback (got ${got}).`, "Usage: moda problems get <problem_id> [--evidence|--reports|--traces|--feedback]");
|
|
3940
4773
|
}
|
|
3941
4774
|
const view = views[0];
|
|
3942
4775
|
if (view !== undefined && params.full === true) {
|
|
3943
|
-
throw new CliInputError("--full applies to the dossier only; the --evidence/--reports/--traces/--feedback pages are always whole.", "Usage: moda
|
|
4776
|
+
throw new CliInputError("--full applies to the dossier only; the --evidence/--reports/--traces/--feedback pages are always whole.", "Usage: moda problems get <problem_id> [--full] | moda problems get <problem_id> --evidence|--reports|--traces|--feedback");
|
|
3944
4777
|
}
|
|
3945
4778
|
if (view === undefined) {
|
|
3946
4779
|
const data2 = await callDataAPI(`/problems/${encodeURIComponent(params.id)}`);
|
|
@@ -3963,7 +4796,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3963
4796
|
}
|
|
3964
4797
|
if (!UUID_RE.test(params.id)) {
|
|
3965
4798
|
const viewFlag = view === "conversations" ? "--traces" : `--${view}`;
|
|
3966
|
-
throw new CliInputError(`${viewFlag} requires a canonical problem UUID; got "${params.id}". Run \`moda problems\` or \`moda
|
|
4799
|
+
throw new CliInputError(`${viewFlag} requires a canonical problem UUID; got "${params.id}". Run \`moda problems\` or \`moda problems get <id>\` first to resolve the id.`);
|
|
3967
4800
|
}
|
|
3968
4801
|
const query2 = new URLSearchParams;
|
|
3969
4802
|
if (params.limit)
|
|
@@ -3983,7 +4816,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3983
4816
|
}
|
|
3984
4817
|
case "problem-feedback": {
|
|
3985
4818
|
if (!positional) {
|
|
3986
|
-
throw new CliInputError("<problem_id> is required", `Usage: moda
|
|
4819
|
+
throw new CliInputError("<problem_id> is required", `Usage: moda problems feedback <problem_id> --action=${PROBLEM_FEEDBACK_ACTIONS.join("|")} [--reason=TEXT] [--attribution-id=UUID] [--new-name=NAME]`);
|
|
3987
4820
|
}
|
|
3988
4821
|
const params = ProblemFeedbackSchema.parse({
|
|
3989
4822
|
id: positional,
|
|
@@ -4005,7 +4838,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4005
4838
|
}
|
|
4006
4839
|
if (params.action === "flag_attribution") {
|
|
4007
4840
|
if (params.attribution_id === undefined || params.attribution_id.trim() === "") {
|
|
4008
|
-
throw new CliInputError("--attribution-id=<uuid> is required for --action=flag_attribution. Run `moda
|
|
4841
|
+
throw new CliInputError("--attribution-id=<uuid> is required for --action=flag_attribution. Run `moda problems get <problem_id> --evidence` to list attribution ids.");
|
|
4009
4842
|
}
|
|
4010
4843
|
if (!UUID_RE.test(params.attribution_id)) {
|
|
4011
4844
|
throw new CliInputError("--attribution-id must be a UUID.");
|
|
@@ -4027,7 +4860,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4027
4860
|
}
|
|
4028
4861
|
case "problem-linear": {
|
|
4029
4862
|
if (!positional) {
|
|
4030
|
-
throw new CliInputError("<problem_id> is required", "Usage: moda
|
|
4863
|
+
throw new CliInputError("<problem_id> is required", "Usage: moda problems linear <problem_id>");
|
|
4031
4864
|
}
|
|
4032
4865
|
if (!UUID_RE.test(positional)) {
|
|
4033
4866
|
throw new CliInputError(`Problem id must be a canonical UUID; got "${positional}". Run \`moda problems\` to list problems with their canonical ids.`);
|
|
@@ -4037,17 +4870,17 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4037
4870
|
});
|
|
4038
4871
|
const link = asRecord(data)?.link;
|
|
4039
4872
|
const summary = describeLinearLink(link);
|
|
4040
|
-
const nextCommands = link ? [] : [`moda
|
|
4873
|
+
const nextCommands = link ? [] : [`moda problems link ${positional}`];
|
|
4041
4874
|
if (context.outputMode === "human" && !context.quiet) {
|
|
4042
4875
|
safeWriteStderr(link ? `${summary}
|
|
4043
|
-
` : `${summary} File one with: moda
|
|
4876
|
+
` : `${summary} File one with: moda problems link ${positional}
|
|
4044
4877
|
`);
|
|
4045
4878
|
}
|
|
4046
4879
|
context.output.writeData(data, { summary, nextCommands });
|
|
4047
4880
|
break;
|
|
4048
4881
|
}
|
|
4049
4882
|
case "problem-linear-file": {
|
|
4050
|
-
const usage = "Usage: moda
|
|
4883
|
+
const usage = "Usage: moda problems link <problem_id> [--issue=ENG-123|URL] [--team-id=ID] [--title=TEXT] [--priority=0-4]";
|
|
4051
4884
|
if (!positional) {
|
|
4052
4885
|
throw new CliInputError("<problem_id> is required", usage);
|
|
4053
4886
|
}
|
|
@@ -4126,14 +4959,14 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4126
4959
|
}
|
|
4127
4960
|
case "trace-signals": {
|
|
4128
4961
|
if (!positional) {
|
|
4129
|
-
throw new CliInputError("<conversation_id> is required", "Usage: moda
|
|
4962
|
+
throw new CliInputError("<conversation_id> is required", "Usage: moda detections list --family=custom_signal --conversation-id=<conversation_id>");
|
|
4130
4963
|
}
|
|
4131
4964
|
const data = await callDataAPI(`/conversations/${encodeURIComponent(positional)}/custom-signals`);
|
|
4132
4965
|
context.output.writeData(data);
|
|
4133
4966
|
break;
|
|
4134
4967
|
}
|
|
4135
4968
|
case "signal-label": {
|
|
4136
|
-
const usage = `Usage: moda signal
|
|
4969
|
+
const usage = `Usage: moda review custom-signal <signal_id> --conversation-id=ID --verdict=correct|false_positive [--note=TEXT] [--msg-index=N] [--msg-dedup-token=T]`;
|
|
4137
4970
|
if (!positional) {
|
|
4138
4971
|
throw new CliInputError("<signal_id> is required", usage);
|
|
4139
4972
|
}
|
|
@@ -4162,7 +4995,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4162
4995
|
}
|
|
4163
4996
|
case "detection-reviews": {
|
|
4164
4997
|
if (!positional) {
|
|
4165
|
-
throw new CliInputError("<conversation_id> is required", "Usage: moda
|
|
4998
|
+
throw new CliInputError("<conversation_id> is required", "Usage: moda reviews list <conversation_id> --kind=problem-detection [--attribution-id=UUID]");
|
|
4166
4999
|
}
|
|
4167
5000
|
const attributionId = flags["attribution-id"];
|
|
4168
5001
|
if (attributionId !== undefined && !UUID_RE.test(attributionId)) {
|
|
@@ -4174,7 +5007,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4174
5007
|
break;
|
|
4175
5008
|
}
|
|
4176
5009
|
case "detection-review": {
|
|
4177
|
-
const usage = `Usage: moda
|
|
5010
|
+
const usage = `Usage: moda review problem-detection <problem_id> --conversation-id=ID --attribution-id=UUID --verdict=${DETECTION_VERDICTS.join("|")} [--note=TEXT]`;
|
|
4178
5011
|
if (!positional) {
|
|
4179
5012
|
throw new CliInputError("<problem_id> is required", usage);
|
|
4180
5013
|
}
|
|
@@ -4201,7 +5034,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4201
5034
|
break;
|
|
4202
5035
|
}
|
|
4203
5036
|
case "false-positive": {
|
|
4204
|
-
const usage = `Usage: moda
|
|
5037
|
+
const usage = `Usage: moda review <${FALSE_POSITIVE_KINDS.join("|")}> <target> --reason=TEXT [--undo]`;
|
|
4205
5038
|
const [kind, target] = positionals;
|
|
4206
5039
|
if (!kind || !target)
|
|
4207
5040
|
throw new CliInputError("<kind> and <target> are required", usage);
|
|
@@ -4274,7 +5107,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4274
5107
|
const query2 = new URLSearchParams;
|
|
4275
5108
|
if (flags.kind !== undefined) {
|
|
4276
5109
|
if (flags.kind === "problem" || flags.kind === "problem-detection") {
|
|
4277
|
-
throw new CliInputError(`'moda
|
|
5110
|
+
throw new CliInputError(`'moda reviews list' cannot list --kind=${flags.kind}: ${flags.kind === "problem" ? "a dismissed Problem is recorded as Problem feedback" : "a problem-detection false positive is recorded as a detection review on its Problem"}, not in the false-positive log this command reads. Nothing was sent.`, flags.kind === "problem" ? "Read it with 'moda problems get <problem_id> --feedback'. --kind for this list takes " + Object.keys(kinds).join("|") + "." : "Read them per trace with 'moda reviews list <conversation_id> --kind=problem-detection' or 'moda reviews list <conversation_id>'. --kind for this list takes " + Object.keys(kinds).join("|") + ".", "unsupported_kind");
|
|
4278
5111
|
}
|
|
4279
5112
|
if (!kinds[flags.kind])
|
|
4280
5113
|
throw new CliInputError(`--kind must be one of ${Object.keys(kinds).join("|")}`);
|
|
@@ -4483,7 +5316,7 @@ function initAgentWriteOptions(result) {
|
|
|
4483
5316
|
warnings: [
|
|
4484
5317
|
...manualActions.filter((action) => action.reason).map((action) => `${action.title}: ${action.reason}`),
|
|
4485
5318
|
...result.promptSync?.status === "failed" ? [`Prompt sync failed: ${result.promptSync.error}. Retry with 'moda prompts sync'.`] : [],
|
|
4486
|
-
...result.skillSync?.status === "failed" ? [`Skill sync failed: ${result.skillSync.error}. Retry with 'moda skills
|
|
5319
|
+
...result.skillSync?.status === "failed" ? [`Skill sync failed: ${result.skillSync.error}. Retry with 'moda skills push'.`] : [],
|
|
4487
5320
|
...result.skillSync?.warnings ?? [],
|
|
4488
5321
|
...(result.skillInstall?.warnings ?? []).map((warning) => `moda-cli skill install: ${warning}`)
|
|
4489
5322
|
]
|
|
@@ -4501,6 +5334,10 @@ function initArtifactKind(file) {
|
|
|
4501
5334
|
return "ci_workflow";
|
|
4502
5335
|
return "init_artifact";
|
|
4503
5336
|
}
|
|
5337
|
+
async function canonicalNounHandler(context) {
|
|
5338
|
+
const route = routeCanonical(context.command, context.positionals, context.flags);
|
|
5339
|
+
throw new CliInputError(`'moda ${context.command}' was not routed${route ? ` (expected ${route.handler})` : ""}.`, `Run 'moda ${context.command} --help'.`, "unrouted_command");
|
|
5340
|
+
}
|
|
4504
5341
|
var dataApiDefaults = {
|
|
4505
5342
|
auth: "api-key",
|
|
4506
5343
|
outputModes: ["human", "json", "agent"],
|
|
@@ -4545,7 +5382,7 @@ var commandRegistry = createCommandRegistry([
|
|
|
4545
5382
|
resetApiRequestCountBeforeRun: true,
|
|
4546
5383
|
telemetry: "result-and-error",
|
|
4547
5384
|
handler: async (context) => {
|
|
4548
|
-
const { runInit } = await import("./index-
|
|
5385
|
+
const { runInit } = await import("./index-4b1ybz51.js");
|
|
4549
5386
|
if (context.outputMode === "agent-stream") {
|
|
4550
5387
|
context.output.writeEvent({
|
|
4551
5388
|
event: "started",
|
|
@@ -4595,7 +5432,7 @@ var commandRegistry = createCommandRegistry([
|
|
|
4595
5432
|
resetApiRequestCountBeforeRun: true,
|
|
4596
5433
|
telemetry: "result-and-error",
|
|
4597
5434
|
handler: async (context) => {
|
|
4598
|
-
const { runProvision } = await import("./provision-
|
|
5435
|
+
const { runProvision } = await import("./provision-q86phh4a.js");
|
|
4599
5436
|
return runProvision(context, {
|
|
4600
5437
|
resumeCommand: buildResumeCommand("provision", context.rawFlags)
|
|
4601
5438
|
});
|
|
@@ -4879,7 +5716,7 @@ var commandRegistry = createCommandRegistry([
|
|
|
4879
5716
|
{
|
|
4880
5717
|
name: "problem-reopen",
|
|
4881
5718
|
description: "Reopen a problem that was marked fixed (resolved problems return only via re-detection)",
|
|
4882
|
-
examples: ['moda
|
|
5719
|
+
examples: ['moda problems reopen <problem_id> --reason="still happening"', "moda problems reopen <problem_id> --dry-run"],
|
|
4883
5720
|
auth: "api-key",
|
|
4884
5721
|
outputModes: ["human", "json", "agent"],
|
|
4885
5722
|
mutability: "write",
|
|
@@ -5025,21 +5862,23 @@ var commandRegistry = createCommandRegistry([
|
|
|
5025
5862
|
name: "traces",
|
|
5026
5863
|
aliases: ["conversations"],
|
|
5027
5864
|
telemetryCommand: "conversations",
|
|
5028
|
-
description: "
|
|
5865
|
+
description: "Traces (agent runs): list (the bare form; --search is ranked), search, get, state, spans",
|
|
5029
5866
|
examples: [
|
|
5030
|
-
'moda traces --search="error" --limit=5',
|
|
5031
|
-
|
|
5032
|
-
'moda traces --world-state="refund,enterprise" --include-world-state'
|
|
5867
|
+
'moda traces list --search="error" --limit=5',
|
|
5868
|
+
"moda traces get <conversation_id> --msg-index=12 --window=3",
|
|
5869
|
+
'moda traces list --world-state="refund,enterprise" --include-world-state'
|
|
5033
5870
|
],
|
|
5871
|
+
subcommands: treeSubcommandHelp("traces"),
|
|
5872
|
+
defaultSubcommand: "list",
|
|
5034
5873
|
...dataApiDefaults
|
|
5035
5874
|
}),
|
|
5036
5875
|
legacyCommand({
|
|
5037
5876
|
name: "search",
|
|
5038
5877
|
description: "Search trace messages (keyword, semantic, or hybrid)",
|
|
5039
5878
|
examples: [
|
|
5040
|
-
'moda search "billing error"',
|
|
5041
|
-
'moda search "refund flow" --mode=semantic --time-range=7d --limit=10',
|
|
5042
|
-
'moda search "area code" --since=2026-10-07 --until=2026-10-07'
|
|
5879
|
+
'moda traces search "billing error"',
|
|
5880
|
+
'moda traces search "refund flow" --mode=semantic --time-range=7d --limit=10',
|
|
5881
|
+
'moda traces search "area code" --since=2026-10-07 --until=2026-10-07'
|
|
5043
5882
|
],
|
|
5044
5883
|
...dataApiDefaults
|
|
5045
5884
|
}),
|
|
@@ -5047,8 +5886,8 @@ var commandRegistry = createCommandRegistry([
|
|
|
5047
5886
|
name: "world-state",
|
|
5048
5887
|
description: "Get a trace's world state (slots, threads, events)",
|
|
5049
5888
|
examples: [
|
|
5050
|
-
"moda
|
|
5051
|
-
"moda
|
|
5889
|
+
"moda traces state <conversation_id>",
|
|
5890
|
+
"moda traces state <conversation_id> --summary-only"
|
|
5052
5891
|
],
|
|
5053
5892
|
...dataApiDefaults
|
|
5054
5893
|
}),
|
|
@@ -5056,13 +5895,13 @@ var commandRegistry = createCommandRegistry([
|
|
|
5056
5895
|
name: "context",
|
|
5057
5896
|
description: "Get windowed trace context (messages around one turn), or the whole trace with --all",
|
|
5058
5897
|
examples: [
|
|
5059
|
-
"moda
|
|
5060
|
-
"moda
|
|
5061
|
-
"moda
|
|
5062
|
-
"moda
|
|
5063
|
-
"moda
|
|
5064
|
-
"moda
|
|
5065
|
-
"moda
|
|
5898
|
+
"moda traces get <conversation_id> --window=3",
|
|
5899
|
+
"moda traces get <conversation_id> --msg-index=40 --window=0 # --message-index is an alias",
|
|
5900
|
+
"moda traces get <conversation_id> --metadata-only # summary + total_messages, no messages",
|
|
5901
|
+
"moda traces get <conversation_id> --all",
|
|
5902
|
+
"moda traces get <conversation_id> --all --from=500 --max-messages=500",
|
|
5903
|
+
"moda traces get <id>@12 <id2>@3 <id3> --window=2 # batch: up to 25 targets, one envelope; id@N anchors at message N",
|
|
5904
|
+
"moda traces get --ids-from=ids.txt --window=1 # one target per line (id or id@N); --ids-from=- reads stdin"
|
|
5066
5905
|
],
|
|
5067
5906
|
...dataApiDefaults
|
|
5068
5907
|
}),
|
|
@@ -5071,13 +5910,13 @@ var commandRegistry = createCommandRegistry([
|
|
|
5071
5910
|
aliases: ["trace"],
|
|
5072
5911
|
description: "Raw non-deduped span audit for one trace or OTLP trace_id (hierarchy, tool spans, orphans, duplicates)",
|
|
5073
5912
|
examples: [
|
|
5074
|
-
"moda
|
|
5075
|
-
"moda
|
|
5076
|
-
"moda
|
|
5077
|
-
"moda
|
|
5078
|
-
"moda
|
|
5079
|
-
"moda
|
|
5080
|
-
"moda
|
|
5913
|
+
"moda traces spans <conversation_id|trace_id>",
|
|
5914
|
+
"moda traces spans <trace_id> --kind=trace --json",
|
|
5915
|
+
"moda traces spans <conversation_id> --include-raw",
|
|
5916
|
+
"moda traces spans <conversation_id> --include-bodies # keep prompt/response bodies (omitted by default; long strings truncated)",
|
|
5917
|
+
"moda traces spans <conversation_id> --limit=1000 # ingest rows to read (default 200, max 1000); warns when capped",
|
|
5918
|
+
"moda traces spans <conversation_id> --cursor=<next_cursor> # next page of a capped audit",
|
|
5919
|
+
"moda traces spans <id1> <id2> <id3> # batch: up to 25 ids, one envelope (or --ids-from=-|<file>)"
|
|
5081
5920
|
],
|
|
5082
5921
|
...dataApiDefaults
|
|
5083
5922
|
}),
|
|
@@ -5085,32 +5924,32 @@ var commandRegistry = createCommandRegistry([
|
|
|
5085
5924
|
name: "frustrations",
|
|
5086
5925
|
description: "User frustration detections: the frustration family of `emotions` (same data as the dashboard), legacy field names",
|
|
5087
5926
|
examples: [
|
|
5088
|
-
"moda
|
|
5089
|
-
"moda
|
|
5927
|
+
"moda detections list --family=frustration --days-back=14",
|
|
5928
|
+
"moda detections list --family=frustration --days-back=14 --include-window # agent output omits conversation windows unless asked"
|
|
5090
5929
|
],
|
|
5091
5930
|
...dataApiDefaults
|
|
5092
5931
|
}),
|
|
5093
5932
|
legacyCommand({
|
|
5094
5933
|
name: "tool-failures",
|
|
5095
5934
|
description: "Get tool failure overview",
|
|
5096
|
-
examples: ["moda
|
|
5935
|
+
examples: ["moda detections tools --days-back=14"],
|
|
5097
5936
|
...dataApiDefaults
|
|
5098
5937
|
}),
|
|
5099
5938
|
legacyCommand({
|
|
5100
5939
|
name: "tool-failure-detail",
|
|
5101
5940
|
description: "Get per-tool failure detail (each example embeds its anchor ±2 messages)",
|
|
5102
5941
|
examples: [
|
|
5103
|
-
"moda
|
|
5104
|
-
"moda
|
|
5105
|
-
"moda
|
|
5942
|
+
"moda detections tools search",
|
|
5943
|
+
"moda detections tools search --compact # no embedded conversation (same as --include-window=false)",
|
|
5944
|
+
"moda detections tools search --include-window --window=3"
|
|
5106
5945
|
],
|
|
5107
5946
|
...dataApiDefaults
|
|
5108
5947
|
}),
|
|
5109
5948
|
legacyCommand({
|
|
5110
5949
|
name: "problems",
|
|
5111
5950
|
description: "Rank cross-signal Problems by root cause (what to fix first)",
|
|
5112
|
-
examples: ["moda problems", "moda problems --days-back=7 --limit=10", "moda problems
|
|
5113
|
-
subcommands:
|
|
5951
|
+
examples: ["moda problems list", "moda problems list --days-back=7 --limit=10", "moda problems get <problem_id> --evidence", "moda problems unexplained --family=emotion"],
|
|
5952
|
+
subcommands: treeSubcommandHelp("problems"),
|
|
5114
5953
|
defaultSubcommand: "list",
|
|
5115
5954
|
...dataApiDefaults
|
|
5116
5955
|
}),
|
|
@@ -5118,14 +5957,14 @@ var commandRegistry = createCommandRegistry([
|
|
|
5118
5957
|
name: "problem-impact",
|
|
5119
5958
|
description: "Live problem/use-case impact, full analysis denominators, daily recurrence, and paginated evidence",
|
|
5120
5959
|
examples: [
|
|
5121
|
-
"moda
|
|
5122
|
-
"moda
|
|
5123
|
-
"moda
|
|
5124
|
-
"moda
|
|
5125
|
-
"moda
|
|
5126
|
-
"moda
|
|
5127
|
-
"moda
|
|
5128
|
-
`moda
|
|
5960
|
+
"moda problems impact --mode=catalog",
|
|
5961
|
+
"moda problems impact --node-id=node_0 --days-back=all",
|
|
5962
|
+
"moda problems impact --problem-id=<uuid> --mode=metrics",
|
|
5963
|
+
"moda problems impact --node-id=node_0 --problem-id=<uuid> --mode=evidence",
|
|
5964
|
+
"moda problems impact --node-id=node_0 --mode=trend --days-back=30",
|
|
5965
|
+
"moda problems impact --mode=overview --days-back=7",
|
|
5966
|
+
"moda problems impact --problem-id=<uuid> --mode=affected-use-cases",
|
|
5967
|
+
`moda problems impact --problem-id=<uuid> --mode=trace-use-cases --conversation-ids='["trace_id"]'`
|
|
5129
5968
|
],
|
|
5130
5969
|
...dataApiDefaults
|
|
5131
5970
|
}),
|
|
@@ -5133,12 +5972,12 @@ var commandRegistry = createCommandRegistry([
|
|
|
5133
5972
|
name: "problem",
|
|
5134
5973
|
description: "Open one Problem: dossier, or --evidence/--reports/--traces/--feedback pages (--conversations is a legacy alias of --traces)",
|
|
5135
5974
|
examples: [
|
|
5136
|
-
"moda
|
|
5137
|
-
"moda
|
|
5138
|
-
"moda
|
|
5139
|
-
"moda
|
|
5140
|
-
"moda
|
|
5141
|
-
"moda
|
|
5975
|
+
"moda problems get <problem_id>",
|
|
5976
|
+
"moda problems get <problem_id> --full # whole dossier (agent output defaults to a compact one)",
|
|
5977
|
+
"moda problems get <problem_id> --evidence --limit=10",
|
|
5978
|
+
"moda problems get <problem_id> --reports",
|
|
5979
|
+
"moda problems get <problem_id> --traces --family=tool_failure",
|
|
5980
|
+
"moda problems get <problem_id> --feedback"
|
|
5142
5981
|
],
|
|
5143
5982
|
...dataApiDefaults
|
|
5144
5983
|
}),
|
|
@@ -5146,10 +5985,10 @@ var commandRegistry = createCommandRegistry([
|
|
|
5146
5985
|
name: "problem-feedback",
|
|
5147
5986
|
description: "Submit Problem feedback: mark fixed, dismiss, rename, or flag a bad attribution",
|
|
5148
5987
|
examples: [
|
|
5149
|
-
"moda
|
|
5150
|
-
'moda
|
|
5151
|
-
'moda
|
|
5152
|
-
'moda
|
|
5988
|
+
"moda problems feedback <problem_id> --action=mark_fixed",
|
|
5989
|
+
'moda problems feedback <problem_id> --action=dismiss --reason="not actionable"',
|
|
5990
|
+
'moda problems feedback <problem_id> --action=rename --new-name="Better title"',
|
|
5991
|
+
'moda problems feedback <problem_id> --action=flag_attribution --attribution-id=<uuid> --reason="wrong trace"'
|
|
5153
5992
|
],
|
|
5154
5993
|
...dataApiDefaults,
|
|
5155
5994
|
mutability: "write"
|
|
@@ -5157,16 +5996,16 @@ var commandRegistry = createCommandRegistry([
|
|
|
5157
5996
|
legacyCommand({
|
|
5158
5997
|
name: "problem-linear",
|
|
5159
5998
|
description: "Show the Linear issue linked to a Problem (key, state, URL); needs Linear connected (Integrations -> Linear)",
|
|
5160
|
-
examples: ["moda
|
|
5999
|
+
examples: ["moda problems linear <problem_id>"],
|
|
5161
6000
|
...dataApiDefaults
|
|
5162
6001
|
}),
|
|
5163
6002
|
legacyCommand({
|
|
5164
6003
|
name: "problem-linear-file",
|
|
5165
6004
|
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)",
|
|
5166
6005
|
examples: [
|
|
5167
|
-
"moda
|
|
5168
|
-
'moda
|
|
5169
|
-
"moda
|
|
6006
|
+
"moda problems link <problem_id>",
|
|
6007
|
+
'moda problems link <problem_id> --team-id=<team_id> --title="CRM lookups time out" --priority=2',
|
|
6008
|
+
"moda problems link <problem_id> --issue=ENG-123"
|
|
5170
6009
|
],
|
|
5171
6010
|
...dataApiDefaults,
|
|
5172
6011
|
mutability: "write"
|
|
@@ -5175,8 +6014,8 @@ var commandRegistry = createCommandRegistry([
|
|
|
5175
6014
|
name: "detection-reviews",
|
|
5176
6015
|
description: "List problem detections on a trace with their review state (correct / false_positive / clear)",
|
|
5177
6016
|
examples: [
|
|
5178
|
-
"moda
|
|
5179
|
-
"moda
|
|
6017
|
+
"moda reviews list <conversation_id> --kind=problem-detection",
|
|
6018
|
+
"moda reviews list <conversation_id> --kind=problem-detection --attribution-id=<uuid>"
|
|
5180
6019
|
],
|
|
5181
6020
|
...dataApiDefaults
|
|
5182
6021
|
}),
|
|
@@ -5184,9 +6023,9 @@ var commandRegistry = createCommandRegistry([
|
|
|
5184
6023
|
name: "detection-review",
|
|
5185
6024
|
description: "Review a problem detection on a trace: mark it correct, a false positive, or clear the review",
|
|
5186
6025
|
examples: [
|
|
5187
|
-
'moda
|
|
5188
|
-
"moda
|
|
5189
|
-
"moda
|
|
6026
|
+
'moda review problem-detection <problem_id> --conversation-id=<id> --attribution-id=<uuid> --verdict=false_positive --note="expected retry"',
|
|
6027
|
+
"moda review problem-detection <problem_id> --conversation-id=<id> --attribution-id=<uuid> --verdict=correct",
|
|
6028
|
+
"moda review problem-detection <problem_id> --conversation-id=<id> --attribution-id=<uuid> --verdict=clear"
|
|
5190
6029
|
],
|
|
5191
6030
|
...dataApiDefaults,
|
|
5192
6031
|
mutability: "write"
|
|
@@ -5195,17 +6034,17 @@ var commandRegistry = createCommandRegistry([
|
|
|
5195
6034
|
name: "false-positive",
|
|
5196
6035
|
description: "Flag a detection as a false positive with a reason: problem, problem-detection, quote, tool, laziness, hallucination, or custom-signal",
|
|
5197
6036
|
helpNotes: [
|
|
5198
|
-
"Kinds: problem <problem_id> dismisses the Problem (hidden at once, logged for review); problem-detection <problem_id> marks one detection on a trace (needs --conversation-id and --attribution-id); quote <conversation_id> needs --family, --turn and --quote (from moda
|
|
6037
|
+
"Kinds: problem <problem_id> dismisses the Problem (hidden at once, logged for review); problem-detection <problem_id> marks one detection on a trace (needs --conversation-id and --attribution-id); quote <conversation_id> needs --family, --turn and --quote (from moda detections list --family=emotion); tool <conversation_id> needs --tool and --tool-use-id or --msg-index; laziness and hallucination <conversation_id> mark that trace; custom-signal <signal_id> needs --conversation-id and is saved as a does_not_fit label. quote, tool, laziness and hallucination marks are audit-only."
|
|
5199
6038
|
],
|
|
5200
6039
|
examples: [
|
|
5201
|
-
'moda
|
|
5202
|
-
'moda
|
|
5203
|
-
'moda
|
|
5204
|
-
'moda
|
|
5205
|
-
'moda
|
|
5206
|
-
"moda
|
|
5207
|
-
"moda
|
|
5208
|
-
'moda
|
|
6040
|
+
'moda review problem <problem_id> --reason="two unrelated tools"',
|
|
6041
|
+
'moda review problem-detection <problem_id> --conversation-id=<id> --attribution-id=<uuid> --reason="refund was approved"',
|
|
6042
|
+
'moda review quote <conversation_id> --family=frustration --turn=3 --quote="this is useless lol" --reason="joking"',
|
|
6043
|
+
'moda review tool <conversation_id> --tool=send_invoice --tool-use-id=<id> --reason="retried upstream"',
|
|
6044
|
+
'moda review laziness <conversation_id> --reason="reload after context reset is expected"',
|
|
6045
|
+
"moda review hallucination <conversation_id> --verdict=clear",
|
|
6046
|
+
"moda review custom-signal <signal_id> --conversation-id=<id> --verdict=clear",
|
|
6047
|
+
'moda review custom-signal <signal_id> --conversation-id=<id> --reason="refund was approved"'
|
|
5209
6048
|
],
|
|
5210
6049
|
...dataApiDefaults,
|
|
5211
6050
|
mutability: "write"
|
|
@@ -5214,9 +6053,9 @@ var commandRegistry = createCommandRegistry([
|
|
|
5214
6053
|
name: "false-positives",
|
|
5215
6054
|
description: "List current false-positive marks across the workspace, or on one trace (with problem detection reviews)",
|
|
5216
6055
|
examples: [
|
|
5217
|
-
"moda
|
|
5218
|
-
"moda
|
|
5219
|
-
"moda
|
|
6056
|
+
"moda reviews list",
|
|
6057
|
+
"moda reviews list --kind=tool --all",
|
|
6058
|
+
"moda reviews list <conversation_id>"
|
|
5220
6059
|
],
|
|
5221
6060
|
...dataApiDefaults
|
|
5222
6061
|
}),
|
|
@@ -5239,22 +6078,22 @@ var commandRegistry = createCommandRegistry([
|
|
|
5239
6078
|
description: "Open one custom signal, or --detections for its recent hits (paged)",
|
|
5240
6079
|
examples: [
|
|
5241
6080
|
"moda signal <signal_id>",
|
|
5242
|
-
"moda signal
|
|
6081
|
+
"moda detections list --family=custom_signal --signal-id=<signal_id> --days-back=30 --limit=20"
|
|
5243
6082
|
],
|
|
5244
6083
|
...dataApiDefaults
|
|
5245
6084
|
}),
|
|
5246
6085
|
legacyCommand({
|
|
5247
6086
|
name: "trace-signals",
|
|
5248
6087
|
description: "Custom signal hits on one trace",
|
|
5249
|
-
examples: ["moda
|
|
6088
|
+
examples: ["moda detections list --family=custom_signal --conversation-id=<conversation_id>"],
|
|
5250
6089
|
...dataApiDefaults
|
|
5251
6090
|
}),
|
|
5252
6091
|
legacyCommand({
|
|
5253
6092
|
name: "signal-label",
|
|
5254
6093
|
description: "Label a trace for a custom signal: fits, or does_not_fit to mark a false positive",
|
|
5255
6094
|
examples: [
|
|
5256
|
-
'moda signal
|
|
5257
|
-
"moda signal
|
|
6095
|
+
'moda review custom-signal <signal_id> --conversation-id=<id> --verdict=false_positive --note="refund was approved"',
|
|
6096
|
+
"moda review custom-signal <signal_id> --conversation-id=<id> --verdict=correct --msg-index=12"
|
|
5258
6097
|
],
|
|
5259
6098
|
...dataApiDefaults,
|
|
5260
6099
|
mutability: "write"
|
|
@@ -5263,9 +6102,9 @@ var commandRegistry = createCommandRegistry([
|
|
|
5263
6102
|
name: "emotions",
|
|
5264
6103
|
description: "Multi-family emotion detections (frustration, disapproval, sadness, confusion, anxiety, trust, positive)",
|
|
5265
6104
|
examples: [
|
|
5266
|
-
"moda
|
|
5267
|
-
"moda
|
|
5268
|
-
"moda
|
|
6105
|
+
"moda detections list --family=emotion --days-back=7",
|
|
6106
|
+
"moda detections list --family=emotion:confusion --limit=20",
|
|
6107
|
+
"moda detections list --family=emotion --include-window # keep conversation windows in agent output"
|
|
5269
6108
|
],
|
|
5270
6109
|
...dataApiDefaults
|
|
5271
6110
|
}),
|
|
@@ -5273,8 +6112,8 @@ var commandRegistry = createCommandRegistry([
|
|
|
5273
6112
|
name: "laziness",
|
|
5274
6113
|
description: "Laziness detections per trace: repeated tool calls, premature handoffs, and other lazy patterns",
|
|
5275
6114
|
examples: [
|
|
5276
|
-
"moda laziness --days-back=30",
|
|
5277
|
-
"moda laziness --conversation-id=<id>"
|
|
6115
|
+
"moda detections list --family=laziness --days-back=30",
|
|
6116
|
+
"moda detections list --family=laziness --conversation-id=<id>"
|
|
5278
6117
|
],
|
|
5279
6118
|
...dataApiDefaults
|
|
5280
6119
|
}),
|
|
@@ -5282,9 +6121,9 @@ var commandRegistry = createCommandRegistry([
|
|
|
5282
6121
|
name: "hallucinations",
|
|
5283
6122
|
description: "Grounding detections: contradicted/verified outputs with rule breakdown",
|
|
5284
6123
|
examples: [
|
|
5285
|
-
"moda
|
|
5286
|
-
"moda
|
|
5287
|
-
"moda
|
|
6124
|
+
"moda detections list --family=hallucination --days-back=30",
|
|
6125
|
+
"moda detections list --family=hallucination --kind=contradicted --limit=20",
|
|
6126
|
+
"moda detections list --family=hallucination --conversation-id=<id>"
|
|
5288
6127
|
],
|
|
5289
6128
|
...dataApiDefaults
|
|
5290
6129
|
}),
|
|
@@ -5306,6 +6145,44 @@ var commandRegistry = createCommandRegistry([
|
|
|
5306
6145
|
],
|
|
5307
6146
|
...dataApiDefaults
|
|
5308
6147
|
}),
|
|
6148
|
+
{
|
|
6149
|
+
name: "detections",
|
|
6150
|
+
description: NOUN_SUMMARIES.detections,
|
|
6151
|
+
examples: [...leavesOf("detections").flatMap((leaf) => leaf.examples.slice(0, 2))],
|
|
6152
|
+
subcommands: treeSubcommandHelp("detections"),
|
|
6153
|
+
auth: "api-key",
|
|
6154
|
+
outputModes: ["human", "json", "agent"],
|
|
6155
|
+
mutability: "read",
|
|
6156
|
+
validateConfigBeforeRun: false,
|
|
6157
|
+
resetApiRequestCountBeforeRun: false,
|
|
6158
|
+
telemetry: "result-and-error",
|
|
6159
|
+
handler: canonicalNounHandler
|
|
6160
|
+
},
|
|
6161
|
+
{
|
|
6162
|
+
name: "review",
|
|
6163
|
+
description: NOUN_SUMMARIES.review,
|
|
6164
|
+
examples: [...leafFor("review", undefined)?.examples ?? []],
|
|
6165
|
+
auth: "api-key",
|
|
6166
|
+
outputModes: ["human", "json", "agent"],
|
|
6167
|
+
mutability: "write",
|
|
6168
|
+
validateConfigBeforeRun: false,
|
|
6169
|
+
resetApiRequestCountBeforeRun: false,
|
|
6170
|
+
telemetry: "result-and-error",
|
|
6171
|
+
handler: canonicalNounHandler
|
|
6172
|
+
},
|
|
6173
|
+
{
|
|
6174
|
+
name: "reviews",
|
|
6175
|
+
description: NOUN_SUMMARIES.reviews,
|
|
6176
|
+
examples: [...leavesOf("reviews").flatMap((leaf) => leaf.examples)],
|
|
6177
|
+
subcommands: treeSubcommandHelp("reviews"),
|
|
6178
|
+
auth: "api-key",
|
|
6179
|
+
outputModes: ["human", "json", "agent"],
|
|
6180
|
+
mutability: "read",
|
|
6181
|
+
validateConfigBeforeRun: false,
|
|
6182
|
+
resetApiRequestCountBeforeRun: false,
|
|
6183
|
+
telemetry: "result-and-error",
|
|
6184
|
+
handler: canonicalNounHandler
|
|
6185
|
+
},
|
|
5309
6186
|
legacyCommand({
|
|
5310
6187
|
name: "feedback",
|
|
5311
6188
|
description: "Flag wrong/missing data or CLI quirks to the Moda team",
|
|
@@ -5382,11 +6259,13 @@ var commandRegistry = createCommandRegistry([
|
|
|
5382
6259
|
{
|
|
5383
6260
|
name: "ab",
|
|
5384
6261
|
description: "Run a judged prompt A/B replay experiment",
|
|
5385
|
-
usage: "moda prompts ab --baseline=<path> --candidate=<path> [--traces=<ids>|--set-id=<id>|--auto-generate] [--yes]",
|
|
6262
|
+
usage: "moda prompts ab --baseline=<path> --candidate=<path> [--traces=<ids>|--problem=<problem_id> [--max-cases=<n>]|--set-id=<id>|--auto-generate] [--yes]",
|
|
5386
6263
|
flags: [
|
|
5387
6264
|
{ name: "--baseline=<path>", description: "Prompt file to treat as the control." },
|
|
5388
6265
|
{ name: "--candidate=<path>", description: "Prompt file to treat as the variant." },
|
|
5389
6266
|
{ name: "--traces=<ids>", description: "Comma-separated trace ids (conversation_id values) to replay, at most 100. Each case uses Moda's distilled scenario and success criteria for the trace; a trace that cannot be distilled falls back to its opening user message with generic criteria, and the run warns. Legacy alias: --conversations=<ids>." },
|
|
6267
|
+
{ name: "--problem=<problem_id>", description: "Replay the Problem's failing traces: one case per affected trace, starting at its evidence msg_index (like --traces=<id>@<n>), read from `moda problems get <id> --evidence`, then `--traces` if needed. Prints the traces it chose. Not combinable with --traces, --set-id, --auto-generate, or --cases." },
|
|
6268
|
+
{ name: "--max-cases=<n>", description: "With --problem: replay at most this many traces (default 10, max 100)." },
|
|
5390
6269
|
{ name: "--cases=<n>", description: "Cases to auto-generate (default 5, max 100)." },
|
|
5391
6270
|
{ name: "--seeds=<n>", description: "Repeats per case per arm (default 3, max 10)." },
|
|
5392
6271
|
{ name: "--model=<slug>", description: "Assistant model for both arms; must be in the replay allowlist unless --allow-any-model." },
|
|
@@ -5394,7 +6273,8 @@ var commandRegistry = createCommandRegistry([
|
|
|
5394
6273
|
{ name: "--promote", description: "Make this run's replay set the tenant's primary replay set. Off by default, so an ad-hoc A/B never replaces it." }
|
|
5395
6274
|
],
|
|
5396
6275
|
examples: [
|
|
5397
|
-
"moda prompts ab --baseline=prompts/agent.prompt.md --candidate=prompts/agent-v2.prompt.md --traces=conv_1,conv_2"
|
|
6276
|
+
"moda prompts ab --baseline=prompts/agent.prompt.md --candidate=prompts/agent-v2.prompt.md --traces=conv_1,conv_2",
|
|
6277
|
+
"moda prompts ab --baseline=prompts/agent.prompt.md --candidate=prompts/agent-v2.prompt.md --problem=<problem_id>"
|
|
5398
6278
|
]
|
|
5399
6279
|
},
|
|
5400
6280
|
{
|
|
@@ -5427,18 +6307,18 @@ var commandRegistry = createCommandRegistry([
|
|
|
5427
6307
|
}),
|
|
5428
6308
|
{
|
|
5429
6309
|
name: "skills",
|
|
5430
|
-
description: "Generate, pull, and
|
|
6310
|
+
description: "Generate, pull (download), push (upload), and install Moda skills",
|
|
5431
6311
|
examples: [
|
|
5432
6312
|
"moda skills gen",
|
|
5433
6313
|
"moda skills status",
|
|
5434
6314
|
"moda skills pull",
|
|
5435
|
-
"moda skills
|
|
6315
|
+
"moda skills push",
|
|
5436
6316
|
"moda skills install --list",
|
|
5437
6317
|
"moda skills install integration-node-openai"
|
|
5438
6318
|
],
|
|
5439
6319
|
subcommands: SKILLS_SUBCOMMAND_HELP,
|
|
5440
6320
|
auth: "api-key",
|
|
5441
|
-
authNote: "install is local; gen, status, pull,
|
|
6321
|
+
authNote: "install is local; gen, status, pull, push, list, show, policy, and proposals need the API key.",
|
|
5442
6322
|
outputModes: ["human", "json", "agent"],
|
|
5443
6323
|
mutability: "write",
|
|
5444
6324
|
validateConfigBeforeRun: false,
|
|
@@ -5543,44 +6423,45 @@ function cliValidationReason(error) {
|
|
|
5543
6423
|
return;
|
|
5544
6424
|
}
|
|
5545
6425
|
function printCliError(error) {
|
|
6426
|
+
const safeWriteStderr2 = (text) => safeWriteStderr(canonicalCommandText(text));
|
|
5546
6427
|
if (error instanceof CliInputError) {
|
|
5547
|
-
|
|
6428
|
+
safeWriteStderr2(`Error: ${error.message}
|
|
5548
6429
|
`);
|
|
5549
6430
|
if (error.usage)
|
|
5550
|
-
|
|
6431
|
+
safeWriteStderr2(`${error.usage}
|
|
5551
6432
|
`);
|
|
5552
6433
|
} else if (error instanceof CliAccessError) {
|
|
5553
|
-
|
|
6434
|
+
safeWriteStderr2(`Error: ${error.message}
|
|
5554
6435
|
`);
|
|
5555
6436
|
if (error.guidance)
|
|
5556
|
-
|
|
6437
|
+
safeWriteStderr2(`${error.guidance}
|
|
5557
6438
|
`);
|
|
5558
6439
|
} else if (error instanceof z.ZodError) {
|
|
5559
6440
|
const issues = error.errors.map((e) => {
|
|
5560
6441
|
const field = e.path.length ? e.path.join(".") : "input";
|
|
5561
6442
|
return ` ${field}: ${e.message}`;
|
|
5562
6443
|
});
|
|
5563
|
-
|
|
6444
|
+
safeWriteStderr2(`Validation error:
|
|
5564
6445
|
${[...new Set(issues)].join(`
|
|
5565
6446
|
`)}
|
|
5566
6447
|
`);
|
|
5567
6448
|
} else if (error instanceof ApiError && error.readable) {
|
|
5568
|
-
|
|
6449
|
+
safeWriteStderr2(`${error.message}
|
|
5569
6450
|
`);
|
|
5570
6451
|
if (error.suggestions?.length) {
|
|
5571
|
-
|
|
6452
|
+
safeWriteStderr2(`Try:
|
|
5572
6453
|
${error.suggestions.map((s) => ` ${s}`).join(`
|
|
5573
6454
|
`)}
|
|
5574
6455
|
`);
|
|
5575
6456
|
}
|
|
5576
6457
|
} else if (error instanceof ApiError) {
|
|
5577
|
-
|
|
6458
|
+
safeWriteStderr2(`API error (HTTP ${error.statusCode}): ${error.responseBody || error.message}
|
|
5578
6459
|
`);
|
|
5579
6460
|
} else if (error instanceof Error) {
|
|
5580
|
-
|
|
6461
|
+
safeWriteStderr2(`Error: ${error.message}
|
|
5581
6462
|
`);
|
|
5582
6463
|
} else {
|
|
5583
|
-
|
|
6464
|
+
safeWriteStderr2(`Error: ${String(error)}
|
|
5584
6465
|
`);
|
|
5585
6466
|
}
|
|
5586
6467
|
}
|
|
@@ -5602,14 +6483,18 @@ ${colorOk ? `\x1B[2m${text}\x1B[0m` : text}
|
|
|
5602
6483
|
function unknownCommandError(command) {
|
|
5603
6484
|
const visible = commandRegistry.all().filter((entry) => !entry.hidden);
|
|
5604
6485
|
const names = visible.flatMap((entry) => [entry.name, ...entry.aliases ?? []]);
|
|
5605
|
-
const
|
|
6486
|
+
const canonical = (name) => {
|
|
6487
|
+
const legacy = legacyNameFor(name, undefined, {});
|
|
6488
|
+
return legacy ? usePath(legacy) : name;
|
|
6489
|
+
};
|
|
6490
|
+
const matches = [...new Set(closestMatches(command, names).map(canonical))];
|
|
5606
6491
|
const suggestion = matches.length > 0 ? ` Did you mean ${matches.map((name) => `'${name}'`).join(" or ")}?` : "";
|
|
5607
6492
|
return new CliInputError(`Unknown command '${command}'.${suggestion}`, [
|
|
5608
6493
|
...matches.map((name) => `\`moda ${name} --help\``),
|
|
5609
6494
|
"Run `moda --help` for the command list."
|
|
5610
6495
|
].join(" "), "unknown_command", {
|
|
5611
6496
|
field: "<command>",
|
|
5612
|
-
allowed: visible.map((entry) => entry.name),
|
|
6497
|
+
allowed: visible.map((entry) => entry.name).filter((name) => !legacyNameFor(name, undefined, {})),
|
|
5613
6498
|
...matches.length > 0 ? { didYouMean: matches } : {},
|
|
5614
6499
|
suggestedCommands: [...matches.map((name) => `moda ${name} --help`), "moda --help"]
|
|
5615
6500
|
});
|
|
@@ -5638,10 +6523,50 @@ async function dispatchParsedCommand(parsed, startedAt = Date.now()) {
|
|
|
5638
6523
|
process.env.MODA_PROFILE = previousProfileEnv;
|
|
5639
6524
|
}
|
|
5640
6525
|
}
|
|
5641
|
-
|
|
5642
|
-
|
|
6526
|
+
function usageIdentityFor(parsed, route, legacy, definition) {
|
|
6527
|
+
if (route)
|
|
6528
|
+
return { command: route.leaf.noun, subcommand: route.telemetrySubcommand };
|
|
6529
|
+
if (legacy) {
|
|
6530
|
+
const kind = legacy.legacy === "false-positive" && FALSE_POSITIVE_KINDS.includes(parsed.positional ?? "") ? parsed.positional : undefined;
|
|
6531
|
+
return { ...legacy.canonical, ...kind ? { subcommand: kind } : {}, legacyAlias: legacy.legacy };
|
|
6532
|
+
}
|
|
6533
|
+
return definition ? canonicalTelemetryForDefault(definition.name, parsed.positional) : undefined;
|
|
6534
|
+
}
|
|
6535
|
+
function rejectedRouteIdentity(parsed) {
|
|
6536
|
+
if (parsed.command === "review") {
|
|
6537
|
+
const kind = FALSE_POSITIVE_KINDS.includes(parsed.positional ?? "") ? parsed.positional : undefined;
|
|
6538
|
+
return { command: "review", ...kind ? { subcommand: kind } : {} };
|
|
6539
|
+
}
|
|
6540
|
+
const leaf = leafFor(parsed.command, parsed.positional);
|
|
6541
|
+
return { command: parsed.command, ...leaf ? { subcommand: leaf.verb } : {} };
|
|
6542
|
+
}
|
|
6543
|
+
function assertRoutedFlags(route, flags) {
|
|
6544
|
+
if (!route.allowedFlags)
|
|
6545
|
+
return;
|
|
6546
|
+
const shape = z.object(Object.fromEntries(route.allowedFlags.map((flag) => [flag.replace(/-/g, "_"), z.unknown()])));
|
|
6547
|
+
const aliases = new Set(Object.keys(route.flagAliases ?? {}));
|
|
6548
|
+
const args = Object.fromEntries(Object.keys(flags).filter((flag) => !aliases.has(flag.replace(/_/g, "-"))).map((flag) => [flag.replace(/-/g, "_"), true]));
|
|
6549
|
+
parseCommandFlags(shape.passthrough(), args, route.label, route.flagAliases ? { flagAliases: route.flagAliases } : {});
|
|
6550
|
+
}
|
|
6551
|
+
function renameErrorCommands(error, renames) {
|
|
6552
|
+
if (error instanceof CliInputError) {
|
|
6553
|
+
const details2 = error.details;
|
|
6554
|
+
const renamed = new CliInputError(renameCommands(error.message, renames), error.usage === undefined ? undefined : renameCommands(error.usage, renames), error.reason, details2 ? { ...details2, ...details2.suggestedCommands ? { suggestedCommands: details2.suggestedCommands.map((command) => renameCommands(command, renames)) } : {} } : undefined);
|
|
6555
|
+
const warnings = error.warnings;
|
|
6556
|
+
if (warnings !== undefined)
|
|
6557
|
+
Object.assign(renamed, { warnings });
|
|
6558
|
+
return renamed;
|
|
6559
|
+
}
|
|
6560
|
+
const details = inputDetailsOf(error);
|
|
6561
|
+
if (details?.suggestedCommands && error !== null && typeof error === "object") {
|
|
6562
|
+
attachInputDetails(error, { ...details, suggestedCommands: details.suggestedCommands.map((command) => renameCommands(command, renames)) });
|
|
6563
|
+
}
|
|
6564
|
+
return error;
|
|
6565
|
+
}
|
|
6566
|
+
async function dispatchParsedCommandInner(typedParsed, startedAt) {
|
|
6567
|
+
const { command, flags } = typedParsed;
|
|
5643
6568
|
const switchError = normalizeBooleanFlags(flags);
|
|
5644
|
-
const
|
|
6569
|
+
const typedFlags = stripUpdateCheckFlag(stripTelemetryFlag(flags));
|
|
5645
6570
|
const telemetryDisabled = isTelemetryDisabled(flags);
|
|
5646
6571
|
const updateCheckDisabled = isUpdateCheckDisabled(flags);
|
|
5647
6572
|
if (command === "--version" || command === "-V" || flags.version === "true") {
|
|
@@ -5649,9 +6574,11 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
|
|
|
5649
6574
|
return 0;
|
|
5650
6575
|
}
|
|
5651
6576
|
if (command === "--help" || command === "-h" || flags.help === "true") {
|
|
6577
|
+
if (command && printTreeHelp(typedParsed))
|
|
6578
|
+
return 0;
|
|
5652
6579
|
const helpTarget = command && command !== "--help" && command !== "-h" ? commandRegistry.find(command) : undefined;
|
|
5653
6580
|
if (helpTarget) {
|
|
5654
|
-
const subcommandName =
|
|
6581
|
+
const subcommandName = typedParsed.positional;
|
|
5655
6582
|
const subcommandEntry = subcommandName ? helpTarget.subcommands?.find((entry) => entry.name === subcommandName) : undefined;
|
|
5656
6583
|
if (subcommandEntry) {
|
|
5657
6584
|
printSubcommandHelp(helpTarget, subcommandEntry);
|
|
@@ -5671,62 +6598,117 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
|
|
|
5671
6598
|
printHelp();
|
|
5672
6599
|
return 1;
|
|
5673
6600
|
}
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
6601
|
+
let route;
|
|
6602
|
+
let routeError;
|
|
6603
|
+
try {
|
|
6604
|
+
route = routeCanonical(command, typedParsed.positionals, typedFlags);
|
|
6605
|
+
} catch (error) {
|
|
6606
|
+
routeError = error;
|
|
6607
|
+
}
|
|
6608
|
+
const legacy = route || routeError ? undefined : legacyNameFor(command, typedParsed.positional, typedFlags);
|
|
6609
|
+
const parsed = route ? {
|
|
6610
|
+
...typedParsed,
|
|
6611
|
+
command: route.handler,
|
|
6612
|
+
positional: route.positionals[0],
|
|
6613
|
+
positionals: route.positionals,
|
|
6614
|
+
flags: route.rewriteFlags(flags)
|
|
6615
|
+
} : typedParsed;
|
|
6616
|
+
const commandFlags = route ? route.rewriteFlags(typedFlags) : typedFlags;
|
|
6617
|
+
const outputModeFor = () => resolveOutputMode(typedFlags, {
|
|
6618
|
+
isTTY: process.stdout.isTTY,
|
|
6619
|
+
env: process.env
|
|
6620
|
+
});
|
|
6621
|
+
const reportUsage = (input) => {
|
|
6622
|
+
const { definition: definition2, identity: identity2 } = input;
|
|
6623
|
+
const handlerTelemetry = definition2 ? telemetryCommandName(definition2) : identity2?.command;
|
|
6624
|
+
if (!handlerTelemetry)
|
|
6625
|
+
return;
|
|
6626
|
+
const telemetryProfile = existingFlagProfile(typedFlags);
|
|
6627
|
+
const adoption = buildSearchAdoptionTelemetry({
|
|
6628
|
+
command: handlerTelemetry,
|
|
6629
|
+
positional: parsed.positional,
|
|
6630
|
+
flags: commandFlags,
|
|
6631
|
+
outputMode: input.outputMode,
|
|
6632
|
+
recorded: input.recorded,
|
|
6633
|
+
skillVersion: process.env.MODA_SKILL_VERSION,
|
|
6634
|
+
subcommands: definition2 ? telemetrySubcommandNames(definition2) : [],
|
|
6635
|
+
defaultSubcommand: definition2?.defaultSubcommand
|
|
6636
|
+
});
|
|
6637
|
+
if (identity2 && adoption) {
|
|
6638
|
+
if (identity2.subcommand)
|
|
6639
|
+
adoption.subcommand = identity2.subcommand;
|
|
6640
|
+
else
|
|
6641
|
+
delete adoption.subcommand;
|
|
6642
|
+
}
|
|
6643
|
+
sendCliUsageTelemetry({
|
|
6644
|
+
apiKey: input.apiKey ?? resolveApiKey({ profile: telemetryProfile }),
|
|
6645
|
+
profile: telemetryProfile,
|
|
6646
|
+
command: identity2?.command ?? handlerTelemetry,
|
|
6647
|
+
flags: typedFlags,
|
|
6648
|
+
status: input.status,
|
|
6649
|
+
...input.error !== undefined ? { errorType: classifyCliError(input.error), validationReason: cliValidationReason(input.error) } : {},
|
|
6650
|
+
exitCode: input.exitCode,
|
|
6651
|
+
durationMs: Date.now() - startedAt,
|
|
6652
|
+
apiRequestCount: input.apiRequestCount,
|
|
6653
|
+
adoption,
|
|
6654
|
+
...identity2?.legacyAlias ? { legacyAlias: identity2.legacyAlias } : {}
|
|
5678
6655
|
});
|
|
6656
|
+
};
|
|
6657
|
+
const failEarly = (error, commandDefinition, identity2) => {
|
|
6658
|
+
const mode = outputModeFor();
|
|
5679
6659
|
const telemetryOptOut = telemetryDisabled || flags["no-telemetry"] !== undefined && flags["no-telemetry"] !== "false";
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
profile: telemetryProfile,
|
|
5686
|
-
command: telemetryCommand2,
|
|
5687
|
-
flags: commandFlags,
|
|
6660
|
+
const reports = commandDefinition ? commandDefinition.telemetry === "result-and-error" : identity2 !== undefined;
|
|
6661
|
+
if (reports && !telemetryOptOut) {
|
|
6662
|
+
reportUsage({
|
|
6663
|
+
definition: commandDefinition,
|
|
6664
|
+
identity: identity2,
|
|
5688
6665
|
status: "error",
|
|
5689
|
-
|
|
5690
|
-
validationReason: cliValidationReason(error),
|
|
6666
|
+
error,
|
|
5691
6667
|
exitCode: 1,
|
|
5692
|
-
durationMs: Date.now() - startedAt,
|
|
5693
6668
|
apiRequestCount: 0,
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
positional: parsed.positional,
|
|
5697
|
-
flags: commandFlags,
|
|
5698
|
-
outputMode: mode,
|
|
5699
|
-
recorded: {},
|
|
5700
|
-
skillVersion: process.env.MODA_SKILL_VERSION,
|
|
5701
|
-
subcommands: telemetrySubcommandNames(commandDefinition),
|
|
5702
|
-
defaultSubcommand: commandDefinition.defaultSubcommand
|
|
5703
|
-
})
|
|
6669
|
+
outputMode: mode,
|
|
6670
|
+
recorded: {}
|
|
5704
6671
|
});
|
|
5705
6672
|
}
|
|
5706
6673
|
if (mode === "agent" || mode === "agent-stream" || mode === "json") {
|
|
5707
|
-
printAgentCliError(command, error, mode);
|
|
6674
|
+
printAgentCliError(route?.leaf.path ?? command, error, mode);
|
|
5708
6675
|
} else {
|
|
5709
6676
|
printCliError(error);
|
|
5710
6677
|
}
|
|
5711
6678
|
return 1;
|
|
5712
6679
|
};
|
|
5713
|
-
|
|
6680
|
+
if (routeError) {
|
|
6681
|
+
if (!(routeError instanceof CliInputError))
|
|
6682
|
+
throw routeError;
|
|
6683
|
+
return failEarly(routeError, undefined, rejectedRouteIdentity(typedParsed));
|
|
6684
|
+
}
|
|
6685
|
+
const definition = commandRegistry.find(parsed.command);
|
|
5714
6686
|
if (!definition)
|
|
5715
6687
|
return failEarly(unknownCommandError(command));
|
|
6688
|
+
const identity = usageIdentityFor(typedParsed, route, legacy, definition);
|
|
5716
6689
|
if (parsed.shortFlags?.length) {
|
|
5717
|
-
return failEarly(new CliInputError(`Short options are not supported (${parsed.shortFlags.join(", ")}), so nothing was run.`, `Use the long form, e.g. --dry-run, --json, --quiet. See 'moda ${command} --help'.`, "short_flag"), definition);
|
|
6690
|
+
return failEarly(new CliInputError(`Short options are not supported (${parsed.shortFlags.join(", ")}), so nothing was run.`, `Use the long form, e.g. --dry-run, --json, --quiet. See 'moda ${route?.leaf.path ?? command} --help'.`, "short_flag"), definition, identity);
|
|
5718
6691
|
}
|
|
5719
6692
|
if (switchError)
|
|
5720
|
-
return failEarly(switchError, definition);
|
|
6693
|
+
return failEarly(switchError, definition, identity);
|
|
6694
|
+
if (route) {
|
|
6695
|
+
try {
|
|
6696
|
+
assertRoutedFlags(route, typedFlags);
|
|
6697
|
+
} catch (error) {
|
|
6698
|
+
if (!(error instanceof CliInputError))
|
|
6699
|
+
throw error;
|
|
6700
|
+
return failEarly(error, definition, identity);
|
|
6701
|
+
}
|
|
6702
|
+
}
|
|
5721
6703
|
if (commandFlags.profile === "true" || commandFlags.profile === "") {
|
|
5722
|
-
return failEarly(new CliInputError("--profile needs a value, e.g. --profile=prod.", "List profiles with 'moda profiles list'.", "missing_value:profile"), definition);
|
|
6704
|
+
return failEarly(new CliInputError("--profile needs a value, e.g. --profile=prod.", "List profiles with 'moda profiles list'.", "missing_value:profile"), definition, identity);
|
|
5723
6705
|
}
|
|
5724
6706
|
const requestedProfile = commandFlags.profile ?? process.env.MODA_PROFILE?.trim();
|
|
5725
6707
|
if (requestedProfile && !PROFILE_MANAGING_COMMANDS.has(definition.name)) {
|
|
5726
6708
|
const known = Object.keys(loadProfileConfig({ env: process.env }).profiles);
|
|
5727
6709
|
if (!known.includes(requestedProfile)) {
|
|
5728
6710
|
const source = commandFlags.profile ? `--profile=${requestedProfile}` : `MODA_PROFILE=${requestedProfile}`;
|
|
5729
|
-
return failEarly(new CliInputError(`Moda profile '${requestedProfile}' does not exist (from ${source}), so nothing was run.`, `Known profiles: ${known.join(", ")}. Create it with 'moda profiles create ${requestedProfile}' or fix the name.`, "unknown_profile"), definition);
|
|
6711
|
+
return failEarly(new CliInputError(`Moda profile '${requestedProfile}' does not exist (from ${source}), so nothing was run.`, `Known profiles: ${known.join(", ")}. Create it with 'moda profiles create ${requestedProfile}' or fix the name.`, "unknown_profile"), definition, identity);
|
|
5730
6712
|
}
|
|
5731
6713
|
if (commandFlags.profile)
|
|
5732
6714
|
process.env.MODA_PROFILE = commandFlags.profile;
|
|
@@ -5741,47 +6723,45 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
|
|
|
5741
6723
|
return latest ? `moda-cli ${VERSION} is out of date (latest ${latest}); commands and flags may differ from the docs. Update: npm install -g @moda-ai/cli@latest` : undefined;
|
|
5742
6724
|
});
|
|
5743
6725
|
}
|
|
6726
|
+
const deprecation = legacy && !legacy.silent ? { legacy: `moda ${legacy.legacy}`, use: legacy.use, message: deprecationMessage(legacy) } : undefined;
|
|
5744
6727
|
const context = createCommandContext(parsed, {
|
|
5745
6728
|
commandName: definition.name,
|
|
5746
6729
|
commandFlags,
|
|
5747
|
-
rawFlags: flags,
|
|
6730
|
+
rawFlags: parsed.flags,
|
|
5748
6731
|
startedAt,
|
|
5749
|
-
outputMode:
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
6732
|
+
outputMode: outputModeFor(),
|
|
6733
|
+
...route ? {
|
|
6734
|
+
outputCommand: route.leaf.path,
|
|
6735
|
+
invocation: {
|
|
6736
|
+
command: route.leaf.path,
|
|
6737
|
+
positionals: route.leaf.verb ? typedParsed.positionals.slice(1) : typedParsed.positionals,
|
|
6738
|
+
flags,
|
|
6739
|
+
...typedParsed.bareFlags ? { bareFlags: typedParsed.bareFlags } : {},
|
|
6740
|
+
routedAs: { command: route.handler, positionals: route.positionals }
|
|
6741
|
+
}
|
|
6742
|
+
} : {},
|
|
6743
|
+
...deprecation ? { deprecation } : {}
|
|
5753
6744
|
});
|
|
5754
6745
|
setActiveContext(context);
|
|
5755
|
-
|
|
5756
|
-
|
|
6746
|
+
if (deprecation && !context.quiet && (context.outputMode === "human" || context.outputMode === "json")) {
|
|
6747
|
+
safeWriteStderr(`Deprecated: ${deprecation.message}
|
|
6748
|
+
`);
|
|
6749
|
+
}
|
|
5757
6750
|
const needsConfig = typeof definition.validateConfigBeforeRun === "function" ? definition.validateConfigBeforeRun(parsed) : definition.validateConfigBeforeRun;
|
|
5758
6751
|
if (needsConfig) {
|
|
5759
6752
|
try {
|
|
5760
6753
|
validateConfig();
|
|
5761
6754
|
} catch (error) {
|
|
5762
6755
|
if (!telemetryDisabled && definition.telemetry === "result-and-error") {
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
profile: telemetryProfile,
|
|
5767
|
-
command: telemetryCommand,
|
|
5768
|
-
flags: commandFlags,
|
|
6756
|
+
reportUsage({
|
|
6757
|
+
definition,
|
|
6758
|
+
identity,
|
|
5769
6759
|
status: "error",
|
|
5770
|
-
|
|
5771
|
-
validationReason: cliValidationReason(error),
|
|
6760
|
+
error,
|
|
5772
6761
|
exitCode: exitCodeForError(error),
|
|
5773
|
-
durationMs: Date.now() - startedAt,
|
|
5774
6762
|
apiRequestCount: getApiRequestCount(),
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
positional: parsed.positional,
|
|
5778
|
-
flags: commandFlags,
|
|
5779
|
-
outputMode: context.outputMode,
|
|
5780
|
-
recorded: context.getTelemetryMeta(),
|
|
5781
|
-
skillVersion: process.env.MODA_SKILL_VERSION,
|
|
5782
|
-
subcommands: telemetrySubcommands,
|
|
5783
|
-
defaultSubcommand: definition.defaultSubcommand
|
|
5784
|
-
})
|
|
6763
|
+
outputMode: context.outputMode,
|
|
6764
|
+
recorded: context.getTelemetryMeta()
|
|
5785
6765
|
});
|
|
5786
6766
|
}
|
|
5787
6767
|
if (context.outputMode === "agent" || context.outputMode === "agent-stream" || context.outputMode === "json") {
|
|
@@ -5798,26 +6778,15 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
|
|
|
5798
6778
|
try {
|
|
5799
6779
|
const result = await definition.handler(context);
|
|
5800
6780
|
if (!telemetryDisabled && definition.telemetry !== "none") {
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
profile: telemetryProfile,
|
|
5805
|
-
command: telemetryCommand,
|
|
5806
|
-
flags: commandFlags,
|
|
6781
|
+
reportUsage({
|
|
6782
|
+
definition,
|
|
6783
|
+
identity,
|
|
5807
6784
|
status: result.exitCode === 0 ? "success" : "error",
|
|
5808
6785
|
exitCode: result.exitCode,
|
|
5809
|
-
|
|
6786
|
+
apiKey: result.apiKey,
|
|
5810
6787
|
apiRequestCount: getApiRequestCount(),
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
positional: parsed.positional,
|
|
5814
|
-
flags: commandFlags,
|
|
5815
|
-
outputMode: context.outputMode,
|
|
5816
|
-
recorded: context.getTelemetryMeta(),
|
|
5817
|
-
skillVersion: process.env.MODA_SKILL_VERSION,
|
|
5818
|
-
subcommands: telemetrySubcommands,
|
|
5819
|
-
defaultSubcommand: definition.defaultSubcommand
|
|
5820
|
-
})
|
|
6788
|
+
outputMode: context.outputMode,
|
|
6789
|
+
recorded: context.getTelemetryMeta()
|
|
5821
6790
|
});
|
|
5822
6791
|
}
|
|
5823
6792
|
if (!updateCheckDisabled) {
|
|
@@ -5825,30 +6794,18 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
|
|
|
5825
6794
|
}
|
|
5826
6795
|
printFeedbackTip(definition.name, context);
|
|
5827
6796
|
return finishRun(context, { kind: "completed", exitCode: result.exitCode });
|
|
5828
|
-
} catch (
|
|
6797
|
+
} catch (thrown) {
|
|
6798
|
+
const error = route ? renameErrorCommands(thrown, route.renames) : thrown;
|
|
5829
6799
|
if (!telemetryDisabled && definition.telemetry === "result-and-error") {
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
profile: telemetryProfile,
|
|
5834
|
-
command: telemetryCommand,
|
|
5835
|
-
flags: commandFlags,
|
|
6800
|
+
reportUsage({
|
|
6801
|
+
definition,
|
|
6802
|
+
identity,
|
|
5836
6803
|
status: "error",
|
|
5837
|
-
|
|
5838
|
-
validationReason: cliValidationReason(error),
|
|
6804
|
+
error,
|
|
5839
6805
|
exitCode: exitCodeForError(error),
|
|
5840
|
-
durationMs: Date.now() - startedAt,
|
|
5841
6806
|
apiRequestCount: getApiRequestCount(),
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
positional: parsed.positional,
|
|
5845
|
-
flags: commandFlags,
|
|
5846
|
-
outputMode: context.outputMode,
|
|
5847
|
-
recorded: context.getTelemetryMeta(),
|
|
5848
|
-
skillVersion: process.env.MODA_SKILL_VERSION,
|
|
5849
|
-
subcommands: telemetrySubcommands,
|
|
5850
|
-
defaultSubcommand: definition.defaultSubcommand
|
|
5851
|
-
})
|
|
6807
|
+
outputMode: context.outputMode,
|
|
6808
|
+
recorded: context.getTelemetryMeta()
|
|
5852
6809
|
});
|
|
5853
6810
|
}
|
|
5854
6811
|
if (context.outputMode === "agent" || context.outputMode === "agent-stream" || context.outputMode === "json") {
|