@moda-ai/cli 1.55.22 → 1.55.24
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-mj622k8s.js → agent-b4tdzy9q.js} +8 -9
- package/dist/{cli-9x9adrtj.js → cli-0wr3483h.js} +1 -1
- package/dist/{cli-a5c9te95.js → cli-7xc585hc.js} +5 -5
- package/dist/{cli-rqk6f6n6.js → cli-9btx12wp.js} +24 -5
- package/dist/cli-9hc5bg2r.js +61 -0
- package/dist/{cli-cehskjde.js → cli-dn91fb56.js} +13 -15
- package/dist/{cli-5ke3xe6y.js → cli-dve50x7p.js} +258 -28
- package/dist/{cli-569hj0bj.js → cli-qvg26twp.js} +5 -4
- package/dist/{cli-033n2mpb.js → cli-qycsmxev.js} +6 -9
- package/dist/{cli-0ef72tqx.js → cli-rt26sbz7.js} +14 -14
- package/dist/{cli-pc5cw7mx.js → cli-vg88mg20.js} +5 -5
- package/dist/{cli-wf7gfa1e.js → cli-y2ty9vtv.js} +8 -10
- package/dist/cli.js +1310 -346
- package/dist/{doctor-mx6jf435.js → doctor-3r19ry92.js} +8 -9
- package/dist/{fix-9fmy6p2x.js → fix-exvxzvtz.js} +6 -7
- package/dist/{harness-github-actions-c826bfxs.js → harness-github-actions-atc5bbkj.js} +5 -6
- package/dist/{harness-xvg6ws6x.js → harness-vzy7n7ea.js} +5 -6
- package/dist/{index-1wbjz3jc.js → index-e1pf1qz2.js} +17 -17
- package/dist/{production-intelligence-dbe9dmc5.js → production-intelligence-c8e0xw3n.js} +17 -16
- package/dist/{profile-commands-kmmd9y86.js → profile-commands-6e2dgv72.js} +7 -48
- package/dist/prompt-source-mcp.js +5 -6
- package/dist/{prompts-1tkhrxkq.js → prompts-cvzt994j.js} +6 -7
- package/dist/{provision-wzttap58.js → provision-q86phh4a.js} +5 -4
- package/dist/{registry-atqyb5fa.js → registry-s2vdsfw8.js} +3 -4
- package/dist/{skills-gejdvjak.js → skills-tadh1jss.js} +5 -6
- package/dist/{sync-57ercmh9.js → sync-g8wtecz1.js} +7 -8
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/skills/moda-cli/SKILL.md +31 -29
- 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 +10 -10
- 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-7ea02wca.js +0 -30
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,17 +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
|
-
import {
|
|
53
|
-
apiKeyHeaders,
|
|
54
|
-
isPlatformApiKey,
|
|
55
|
-
setPlatformTenantOverride
|
|
56
|
-
} from "./cli-7ea02wca.js";
|
|
52
|
+
} from "./cli-9btx12wp.js";
|
|
57
53
|
import {
|
|
58
54
|
AGENT_ERROR_CODES,
|
|
59
55
|
BATCH_CONCURRENCY,
|
|
@@ -70,15 +66,18 @@ import {
|
|
|
70
66
|
EXIT_ERROR,
|
|
71
67
|
EXIT_INPUT_REQUIRED,
|
|
72
68
|
EXIT_OK,
|
|
69
|
+
LEGACY_NAMES,
|
|
73
70
|
MODA_BASE_URL,
|
|
74
71
|
VERSION,
|
|
75
72
|
agentEnvelopeFits,
|
|
76
73
|
agentMaxOutputBytes,
|
|
77
74
|
apiErrorStatus,
|
|
75
|
+
apiKeyHeaders,
|
|
78
76
|
asNumber,
|
|
79
77
|
asRecord,
|
|
80
78
|
asString,
|
|
81
79
|
attachInputDetails,
|
|
80
|
+
canonicalCommandText,
|
|
82
81
|
collectBatchTargets,
|
|
83
82
|
compactProblemDossier,
|
|
84
83
|
contextTarget,
|
|
@@ -89,8 +88,10 @@ import {
|
|
|
89
88
|
frustrationsFromEmotions,
|
|
90
89
|
getApiRequestCount,
|
|
91
90
|
getModaDir,
|
|
91
|
+
inputDetailsOf,
|
|
92
92
|
isApiLikeError,
|
|
93
93
|
isNetworkLikeError,
|
|
94
|
+
isPlatformApiKey,
|
|
94
95
|
loadProfileConfig,
|
|
95
96
|
mapWithConcurrency,
|
|
96
97
|
parseBatchTarget,
|
|
@@ -102,13 +103,14 @@ import {
|
|
|
102
103
|
safeWriteStderr,
|
|
103
104
|
safeWriteStdout,
|
|
104
105
|
setAgentUpdateNotice,
|
|
106
|
+
setPlatformTenantOverride,
|
|
105
107
|
trimToolFailureConversations,
|
|
106
108
|
validateConfig,
|
|
107
109
|
wasOutputCancelled,
|
|
108
110
|
withFrustrationAnchors,
|
|
109
111
|
withToolFailureAnchors,
|
|
110
112
|
withoutDetectionWindows
|
|
111
|
-
} from "./cli-
|
|
113
|
+
} from "./cli-dve50x7p.js";
|
|
112
114
|
import {
|
|
113
115
|
__require
|
|
114
116
|
} from "./cli-0v6na3yp.js";
|
|
@@ -544,7 +546,10 @@ function buildCliUsageRequest(input) {
|
|
|
544
546
|
node_version: process.versions.node,
|
|
545
547
|
platform: process.platform,
|
|
546
548
|
arch: process.arch,
|
|
547
|
-
flags_json: {
|
|
549
|
+
flags_json: {
|
|
550
|
+
names: sanitizedFlagNames(input.flags),
|
|
551
|
+
...((alias) => alias ? { legacy_alias: alias } : {})(sanitizeReasonCode(input.legacyAlias?.replace(/ /g, ":"), 64))
|
|
552
|
+
},
|
|
548
553
|
...serializeAdoption(input.adoption)
|
|
549
554
|
})
|
|
550
555
|
};
|
|
@@ -656,6 +661,540 @@ function printUpdateNotice(options = {}) {
|
|
|
656
661
|
if (notice)
|
|
657
662
|
console.error(notice);
|
|
658
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
|
+
}
|
|
659
1198
|
|
|
660
1199
|
// src/command-registry.ts
|
|
661
1200
|
function createCommandRegistry(definitions) {
|
|
@@ -889,11 +1428,11 @@ var SKILLS_SUBCOMMAND_HELP = [
|
|
|
889
1428
|
examples: ["moda skills pull", "moda skills pull --status=all"]
|
|
890
1429
|
},
|
|
891
1430
|
{
|
|
892
|
-
name: "
|
|
893
|
-
description: "Upload locally authored .claude/skills/**/SKILL.md files to Moda",
|
|
894
|
-
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]",
|
|
895
1434
|
flags: [{ name: "--dry-run", description: "Server-side preview; nothing is saved." }],
|
|
896
|
-
examples: ["moda skills
|
|
1435
|
+
examples: ["moda skills push --dry-run", "moda skills push"]
|
|
897
1436
|
},
|
|
898
1437
|
{
|
|
899
1438
|
name: "list",
|
|
@@ -1195,15 +1734,6 @@ var HARNESS_SUBCOMMAND_HELP = [
|
|
|
1195
1734
|
examples: ["moda harness diff my-harness-key --from=hver_a --to=hver_b"]
|
|
1196
1735
|
}
|
|
1197
1736
|
];
|
|
1198
|
-
var PROBLEMS_SUBCOMMAND_HELP = [
|
|
1199
|
-
{
|
|
1200
|
-
name: "remainder",
|
|
1201
|
-
description: "Signals no Problem explains yet, grouped by family (read-only)",
|
|
1202
|
-
usage: "moda problems remainder [--family=<family>] [--days-back=N] [--limit=N] [--cursor=<token>]",
|
|
1203
|
-
flags: subcommandHelpFlags(COMMAND_FLAG_SPECS.problems.subcommands.remainder),
|
|
1204
|
-
examples: ["moda problems remainder", "moda problems remainder --family=emotion --days-back=7"]
|
|
1205
|
-
}
|
|
1206
|
-
];
|
|
1207
1737
|
var FIX_TIMEOUT = { name: "--timeout=<ms>", description: "How long to wait, in milliseconds (max 86400000)." };
|
|
1208
1738
|
var FIX_POLL = { name: "--poll-interval=<ms>", description: "Polling interval while waiting, in milliseconds." };
|
|
1209
1739
|
var FIXES_SUBCOMMAND_HELP = [
|
|
@@ -1769,27 +2299,42 @@ function buildManifest(commands) {
|
|
|
1769
2299
|
"moda.intelligence.v1",
|
|
1770
2300
|
"production_ask.v0.1"
|
|
1771
2301
|
],
|
|
1772
|
-
commands: commands.filter((command) => !command.hidden).map((command) =>
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
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
|
+
}),
|
|
1785
2329
|
concepts: [
|
|
1786
2330
|
{ term: "tenant", meaning: "A Moda organization/workspace that owns data, prompts, skills, and API keys." },
|
|
1787
2331
|
{ term: "profile", meaning: "A named local CLI configuration under ~/.moda that selects environment, tenant, and secret references." },
|
|
1788
2332
|
{ term: "harness", meaning: "Moda’s local graph of runtime agents, prompts, tools, identities, and deployments in a codebase." },
|
|
1789
2333
|
{ term: "harness report", meaning: "A cited analyst artifact under .moda/ that justifies the harness graph before sync." },
|
|
1790
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`." },
|
|
1791
|
-
{ 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
|
|
1792
|
-
{ 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`." },
|
|
1793
2338
|
{ term: "ingest key", meaning: "A `moda_sk_` API key for SDKs, CI, and Data API calls. Treat it as a secret." },
|
|
1794
2339
|
{ term: "CLI session token", meaning: "A local browser-auth session token used by CLI auth/bootstrap flows, not by application SDKs." },
|
|
1795
2340
|
{ term: "prompt", meaning: "A code-first prompt file tracked by `.moda/prompts.yml` and synced to Moda." },
|
|
@@ -1812,7 +2357,11 @@ function renderManifestHuman(manifest) {
|
|
|
1812
2357
|
].join(`
|
|
1813
2358
|
`);
|
|
1814
2359
|
}
|
|
1815
|
-
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 {};
|
|
1816
2365
|
const spec = flagSpecFor(command.name);
|
|
1817
2366
|
if (spec)
|
|
1818
2367
|
return manifestFlagFields(spec);
|
|
@@ -1845,51 +2394,174 @@ function exitCodesForCommand(command) {
|
|
|
1845
2394
|
return [0, 1, 4, 5, 130];
|
|
1846
2395
|
return [0, 1, 4, 130];
|
|
1847
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
|
+
}
|
|
1848
2520
|
|
|
1849
2521
|
// src/cli.ts
|
|
1850
2522
|
import { fileURLToPath } from "url";
|
|
1851
2523
|
import { realpathSync } from "fs";
|
|
1852
2524
|
async function runPromptsCommand(...args) {
|
|
1853
|
-
return (await import("./prompts-
|
|
2525
|
+
return (await import("./prompts-cvzt994j.js")).runPromptsCommand(...args);
|
|
1854
2526
|
}
|
|
1855
2527
|
async function runSkillsCommand(...args) {
|
|
1856
|
-
return (await import("./skills-
|
|
2528
|
+
return (await import("./skills-tadh1jss.js")).runSkillsCommand(...args);
|
|
1857
2529
|
}
|
|
1858
2530
|
async function runFixCommand(...args) {
|
|
1859
|
-
return (await import("./fix-
|
|
2531
|
+
return (await import("./fix-exvxzvtz.js")).runFixCommand(...args);
|
|
1860
2532
|
}
|
|
1861
2533
|
async function runFixesCommand(...args) {
|
|
1862
|
-
return (await import("./fix-
|
|
2534
|
+
return (await import("./fix-exvxzvtz.js")).runFixesCommand(...args);
|
|
1863
2535
|
}
|
|
1864
2536
|
async function runHarnessCommand(...args) {
|
|
1865
|
-
return (await import("./harness-
|
|
2537
|
+
return (await import("./harness-vzy7n7ea.js")).runHarnessCommand(...args);
|
|
1866
2538
|
}
|
|
1867
2539
|
async function runSyncCommand(...args) {
|
|
1868
|
-
return (await import("./sync-
|
|
2540
|
+
return (await import("./sync-g8wtecz1.js")).runSyncCommand(...args);
|
|
1869
2541
|
}
|
|
1870
2542
|
async function runRegistryCommand(...args) {
|
|
1871
|
-
return (await import("./registry-
|
|
2543
|
+
return (await import("./registry-s2vdsfw8.js")).runRegistryCommand(...args);
|
|
1872
2544
|
}
|
|
1873
2545
|
async function runDoctorCommand(...args) {
|
|
1874
|
-
return (await import("./doctor-
|
|
2546
|
+
return (await import("./doctor-3r19ry92.js")).runDoctorCommand(...args);
|
|
1875
2547
|
}
|
|
1876
2548
|
async function runStatusCommand(...args) {
|
|
1877
|
-
return (await import("./doctor-
|
|
2549
|
+
return (await import("./doctor-3r19ry92.js")).runStatusCommand(...args);
|
|
1878
2550
|
}
|
|
1879
2551
|
async function runAuthCommand(...args) {
|
|
1880
|
-
return (await import("./profile-commands-
|
|
2552
|
+
return (await import("./profile-commands-6e2dgv72.js")).runAuthCommand(...args);
|
|
1881
2553
|
}
|
|
1882
2554
|
async function runConfigCommand(...args) {
|
|
1883
|
-
return (await import("./profile-commands-
|
|
2555
|
+
return (await import("./profile-commands-6e2dgv72.js")).runConfigCommand(...args);
|
|
1884
2556
|
}
|
|
1885
2557
|
async function runProfilesCommand(...args) {
|
|
1886
|
-
return (await import("./profile-commands-
|
|
2558
|
+
return (await import("./profile-commands-6e2dgv72.js")).runProfilesCommand(...args);
|
|
1887
2559
|
}
|
|
1888
2560
|
async function runAgentCommand(...args) {
|
|
1889
|
-
return (await import("./agent-
|
|
2561
|
+
return (await import("./agent-b4tdzy9q.js")).runAgentCommand(...args);
|
|
1890
2562
|
}
|
|
1891
2563
|
async function runProductionCommand(...args) {
|
|
1892
|
-
return (await import("./production-intelligence-
|
|
2564
|
+
return (await import("./production-intelligence-c8e0xw3n.js")).runProductionCommand(...args);
|
|
1893
2565
|
}
|
|
1894
2566
|
function parseArgs(argv) {
|
|
1895
2567
|
const args = argv.slice(2);
|
|
@@ -1906,7 +2578,7 @@ function parseArgs(argv) {
|
|
|
1906
2578
|
if (!endOfOptions && arg === "--" && command !== "") {
|
|
1907
2579
|
endOfOptions = true;
|
|
1908
2580
|
} else if (!endOfOptions && arg.startsWith("--")) {
|
|
1909
|
-
i = parseFlag(args, i, flags, command === "", bareFlags, command);
|
|
2581
|
+
i = parseFlag(args, i, flags, command === "", bareFlags, parseCommandFor(command, positional));
|
|
1910
2582
|
} else if (!endOfOptions && command !== "" && /^-[A-Za-z]/.test(arg)) {
|
|
1911
2583
|
shortFlags.push(arg);
|
|
1912
2584
|
} else if (command === "") {
|
|
@@ -2092,58 +2764,35 @@ function buildResumeCommand(command, flags) {
|
|
|
2092
2764
|
];
|
|
2093
2765
|
}
|
|
2094
2766
|
function printHelp() {
|
|
2095
|
-
console.log(`Usage: moda <
|
|
2767
|
+
console.log(`Usage: moda <noun> <verb> [args] [flags]
|
|
2096
2768
|
|
|
2097
2769
|
Start here (goal -> command):
|
|
2098
|
-
find something in traces moda search "<query>" (whole traces: moda traces --search=TEXT)
|
|
2099
|
-
read one trace moda
|
|
2100
|
-
what is broken moda overview, then moda problems
|
|
2101
|
-
tool errors moda
|
|
2102
|
-
user emotion and frustration moda
|
|
2103
|
-
raw spans for one trace moda
|
|
2104
|
-
|
|
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)
|
|
2105
2778
|
|
|
2106
2779
|
Production data:
|
|
2107
2780
|
overview Harness health briefing with production signals
|
|
2108
2781
|
investigate Rank production issues (tool failures, frustrations, top Problems)
|
|
2109
2782
|
failures Tool failures only, ranked (no frustrations or Problems)
|
|
2110
2783
|
ask "<question>" Ask Moda a production question (one paid agent run)
|
|
2111
|
-
search "<query>" Search trace messages (keyword/semantic/hybrid)
|
|
2112
|
-
traces Search and filter traces (agent runs)
|
|
2113
|
-
context <conversation_id> Messages around one turn, or the whole trace with --all
|
|
2114
|
-
context <id>[@N] <id>[@N] ... Batch: up to 25 traces in one call (or --ids-from=-|<file>)
|
|
2115
|
-
audit <conversation_id|trace_id> Raw span audit (spans, hierarchy, orphans, duplicates)
|
|
2116
|
-
audit <id> <id> ... Batch audit: up to 25 ids in one call (or --ids-from=-|<file>)
|
|
2117
|
-
world-state <conversation_id> A trace's world state (slots, threads, events)
|
|
2118
2784
|
clusters Browse or search topic clusters
|
|
2119
2785
|
cluster-traces <node_id> List traces in a cluster
|
|
2120
2786
|
users / user <user_id> End users with health and activity (the dashboard Users page)
|
|
2121
2787
|
tail Live-tail new traces/detections as NDJSON (one line per item)
|
|
2122
2788
|
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
hallucinations Grounding detections: contradicted/verified outputs
|
|
2127
|
-
laziness Laziness detections per trace (repeated calls, premature handoffs, ...)
|
|
2128
|
-
tool-failures Tool failure overview
|
|
2129
|
-
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
|
+
`)}
|
|
2130
2792
|
|
|
2131
|
-
|
|
2132
|
-
problems Rank cross-signal Problems by root cause (what to fix first)
|
|
2133
|
-
problem <problem_id> One Problem: dossier, or its evidence/reports/traces/feedback
|
|
2134
|
-
problem-impact Live problem/use-case impact, trend and evidence
|
|
2135
|
-
problem-feedback <problem_id> Mark a Problem fixed, dismiss, rename, or flag a bad attribution
|
|
2136
|
-
problem-reopen <problem_id> Reopen a Problem marked fixed
|
|
2137
|
-
problem-linear <problem_id> Show the Linear issue linked to a Problem
|
|
2138
|
-
problem-linear-file <problem_id> File a Problem as a Linear issue, or link an existing one
|
|
2139
|
-
detection-reviews <conv_id> Problem detections on a trace with their review state
|
|
2140
|
-
detection-review <problem_id> Mark a problem detection correct, a false positive, or clear it
|
|
2141
|
-
false-positive <kind> <target> Mark any detection as a false positive
|
|
2142
|
-
false-positives [conv_id] Current false-positive marks (workspace or one trace)
|
|
2793
|
+
Custom signals and feedback:
|
|
2143
2794
|
signals List and manage custom signals
|
|
2144
|
-
signal <signal_id> One custom signal
|
|
2145
|
-
trace-signals <conversation_id> Custom signal hits on one trace
|
|
2146
|
-
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)
|
|
2147
2796
|
feedback "<note>" Flag wrong/missing data or CLI quirks to the Moda team
|
|
2148
2797
|
|
|
2149
2798
|
Setup:
|
|
@@ -2161,13 +2810,13 @@ Groups with their own subcommands (run 'moda <group> --help'):
|
|
|
2161
2810
|
harness Analyze, scan, validate, approve, sync and register the agent harness
|
|
2162
2811
|
prompts Code-first prompt versions: init, status, sync, promote, A/B, propose
|
|
2163
2812
|
replays Replay results: sets, runs, playouts and transcripts
|
|
2164
|
-
skills Generate
|
|
2813
|
+
skills Generate skills; pull (download), push (upload), install
|
|
2165
2814
|
sync Sync the harness graph and prompts to Moda Cloud
|
|
2166
2815
|
registry Push prompts, skills, tools and agent wiring from one JSON manifest
|
|
2167
2816
|
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
<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.
|
|
2171
2820
|
|
|
2172
2821
|
Output: piped stdout, CI or an agent env (CLAUDECODE/CURSOR/MODA_AGENT) => agent JSON; a terminal => prose.
|
|
2173
2822
|
--json | --agent [--stream] | --human Force an output mode (or MODA_FORMAT=human|json|agent|agent-stream)
|
|
@@ -2252,6 +2901,187 @@ function printSubcommandHelp(parent, entry) {
|
|
|
2252
2901
|
console.log(lines.join(`
|
|
2253
2902
|
`));
|
|
2254
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
|
+
}
|
|
2255
3085
|
async function attachWindows(rows, windowSize) {
|
|
2256
3086
|
return Promise.all(rows.map(async (row) => {
|
|
2257
3087
|
const anchor = asRecord(row.anchor);
|
|
@@ -2311,7 +3141,7 @@ function withoutRepeatedFrustrationWindow(data) {
|
|
|
2311
3141
|
return { ...data, frustrations };
|
|
2312
3142
|
}
|
|
2313
3143
|
var NOT_FOUND_HINTS = {
|
|
2314
|
-
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>',
|
|
2315
3145
|
cluster: "List cluster node ids with: moda clusters",
|
|
2316
3146
|
problem: "List problem ids with: moda problems"
|
|
2317
3147
|
};
|
|
@@ -2325,7 +3155,7 @@ function noWorldStateWarnings(conversationId, isMissing) {
|
|
|
2325
3155
|
return;
|
|
2326
3156
|
return {
|
|
2327
3157
|
warnings: [
|
|
2328
|
-
`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`
|
|
2329
3159
|
]
|
|
2330
3160
|
};
|
|
2331
3161
|
}
|
|
@@ -2365,11 +3195,11 @@ function auditOutput(command, params, data) {
|
|
|
2365
3195
|
return { data: auditRecord ? { ...auditRecord, truncated: false, has_more: false, next_cursor: null } : data, options: {} };
|
|
2366
3196
|
}
|
|
2367
3197
|
const limit = params.limit ?? AUDIT_DEFAULT_LIMIT;
|
|
2368
|
-
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)";
|
|
2369
3199
|
const warnings = [];
|
|
2370
3200
|
const nextCommands = [];
|
|
2371
3201
|
const auditFlags = (extra) => [
|
|
2372
|
-
`moda ${command} ${shellArg(params.id)}`,
|
|
3202
|
+
`${canonicalCommandText(`moda ${command}`)} ${shellArg(params.id)}`,
|
|
2373
3203
|
params.kind && params.kind !== "auto" ? `--kind=${params.kind}` : "",
|
|
2374
3204
|
params.include_raw === true ? "--include-raw" : "",
|
|
2375
3205
|
params.include_bodies === true ? "--include-bodies" : "",
|
|
@@ -2556,7 +3386,7 @@ async function runTailCommand(params, context) {
|
|
|
2556
3386
|
total,
|
|
2557
3387
|
coverage_pct: coveragePct,
|
|
2558
3388
|
pages_scanned: pages,
|
|
2559
|
-
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.`
|
|
2560
3390
|
});
|
|
2561
3391
|
} else if (total !== null) {
|
|
2562
3392
|
emitMeta({
|
|
@@ -2697,8 +3527,8 @@ var WINDOW_FLAG_HINTS = {
|
|
|
2697
3527
|
};
|
|
2698
3528
|
var COMMAND_FLAG_HINTS = {
|
|
2699
3529
|
problems: {
|
|
2700
|
-
family: "'moda problems' ranks every family together. To filter by family use 'moda problems
|
|
2701
|
-
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."
|
|
2702
3532
|
}
|
|
2703
3533
|
};
|
|
2704
3534
|
function parseCommandFlags(schema, args, command, options = {}) {
|
|
@@ -2736,8 +3566,9 @@ function parseCommandFlags(schema, args, command, options = {}) {
|
|
|
2736
3566
|
return close ? `Did you mean ${close}?` : "";
|
|
2737
3567
|
}).filter(Boolean);
|
|
2738
3568
|
const names = unknown.map(kebabFlag).join(", ");
|
|
2739
|
-
const
|
|
2740
|
-
|
|
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, "-")}`, {
|
|
2741
3572
|
field: kebabFlag(unknown[0]),
|
|
2742
3573
|
allowed: valid,
|
|
2743
3574
|
...didYouMean.length > 0 ? { didYouMean: [...new Set(didYouMean)] } : {},
|
|
@@ -2763,7 +3594,7 @@ function attachFlagDetails(error, schema, command) {
|
|
|
2763
3594
|
field: field.name,
|
|
2764
3595
|
...field.enum ? { allowed: field.enum } : {},
|
|
2765
3596
|
...field.min !== undefined || field.max !== undefined ? { range: { ...field.min !== undefined ? { min: field.min } : {}, ...field.max !== undefined ? { max: field.max } : {} } } : {},
|
|
2766
|
-
suggestedCommands: [`moda ${command} --help`]
|
|
3597
|
+
suggestedCommands: [`${canonicalCommandText(`moda ${command}`)} --help`]
|
|
2767
3598
|
});
|
|
2768
3599
|
}
|
|
2769
3600
|
function parseSpecFlags(name, args, command = name) {
|
|
@@ -2916,7 +3747,7 @@ async function searchFallbackToTraces(params) {
|
|
|
2916
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)`;
|
|
2917
3748
|
const nextCommands = [];
|
|
2918
3749
|
if (first) {
|
|
2919
|
-
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 });
|
|
2920
3751
|
}
|
|
2921
3752
|
if (hasMore) {
|
|
2922
3753
|
nextCommands.push({ command: `${tracesCommand} --offset=${nextOffset}`, purpose: "Page through further trace-level results.", mutability: "read", requires_approval: false });
|
|
@@ -2938,21 +3769,27 @@ async function searchFallbackToTraces(params) {
|
|
|
2938
3769
|
...hasMore ? { next_offset: nextOffset } : {}
|
|
2939
3770
|
}
|
|
2940
3771
|
},
|
|
2941
|
-
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." : ""),
|
|
2942
3773
|
nextCommands
|
|
2943
3774
|
};
|
|
2944
3775
|
}
|
|
2945
3776
|
function capProblemsBlocks(data, limit) {
|
|
2946
3777
|
const truncated = {};
|
|
2947
3778
|
const payload = { ...data };
|
|
2948
|
-
if (Array.isArray(data.emerging)
|
|
2949
|
-
|
|
2950
|
-
|
|
3779
|
+
if (Array.isArray(data.emerging)) {
|
|
3780
|
+
const total = Math.max(asNumber(data.emerging_total) ?? 0, data.emerging.length);
|
|
3781
|
+
if (total > limit)
|
|
3782
|
+
truncated.emerging = total - Math.min(limit, data.emerging.length);
|
|
3783
|
+
if (data.emerging.length > limit)
|
|
3784
|
+
payload.emerging = data.emerging.slice(0, limit);
|
|
2951
3785
|
}
|
|
2952
3786
|
const hierarchy = asRecord(data.hierarchy);
|
|
2953
|
-
if (hierarchy && Array.isArray(hierarchy.macros)
|
|
2954
|
-
|
|
2955
|
-
|
|
3787
|
+
if (hierarchy && Array.isArray(hierarchy.macros)) {
|
|
3788
|
+
const total = Math.max(asNumber(hierarchy.macro_total) ?? 0, hierarchy.macros.length);
|
|
3789
|
+
if (total > limit)
|
|
3790
|
+
truncated.hierarchy_macros = total - Math.min(limit, hierarchy.macros.length);
|
|
3791
|
+
if (hierarchy.macros.length > limit)
|
|
3792
|
+
payload.hierarchy = { ...hierarchy, macros: hierarchy.macros.slice(0, limit) };
|
|
2956
3793
|
}
|
|
2957
3794
|
return Object.keys(truncated).length > 0 ? { payload, truncated } : { payload };
|
|
2958
3795
|
}
|
|
@@ -3057,9 +3894,9 @@ function processingDisabledWarning(data, family) {
|
|
|
3057
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."
|
|
3058
3895
|
];
|
|
3059
3896
|
}
|
|
3060
|
-
var CONTEXT_USAGE = "Usage: moda
|
|
3061
|
-
var CONTEXT_ALL_USAGE = "Usage: moda
|
|
3062
|
-
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)`;
|
|
3063
3900
|
var CONTEXT_FLAG_ALIASES = { "message-index": "msg-index" };
|
|
3064
3901
|
var CONTEXT_MAX_WINDOW = 5;
|
|
3065
3902
|
var EMPTY_ANCHOR_VALUES = new Set(["", "null", "undefined", "none"]);
|
|
@@ -3233,7 +4070,7 @@ async function readFullTranscript(conversationId, opts = {}) {
|
|
|
3233
4070
|
if (next !== null) {
|
|
3234
4071
|
const last = asNumber(returned[returned.length - 1]?.index) ?? next - 1;
|
|
3235
4072
|
const nextMax = firstMissing !== null ? Math.min(pageSize, Math.max(kept * 2, 50)) : pageSize;
|
|
3236
|
-
const continueWith = `moda
|
|
4073
|
+
const continueWith = `moda traces get ${shellArg(contextTarget(conversationId))} --all --from=${next} --max-messages=${nextMax}`;
|
|
3237
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}`);
|
|
3238
4075
|
nextCommands.push({
|
|
3239
4076
|
command: continueWith,
|
|
@@ -3246,7 +4083,7 @@ async function readFullTranscript(conversationId, opts = {}) {
|
|
|
3246
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` : ""}.`);
|
|
3247
4084
|
}
|
|
3248
4085
|
if (legacy && returned.some(isTruncated)) {
|
|
3249
|
-
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).`);
|
|
3250
4087
|
}
|
|
3251
4088
|
return {
|
|
3252
4089
|
data: stripLoneSurrogates({
|
|
@@ -3313,7 +4150,7 @@ async function readContextItem(command, params, inputWarnings, fits) {
|
|
|
3313
4150
|
const center = asNumber(asRecord(ctxRecord.context)?.center_index) ?? params.msg_index ?? 0;
|
|
3314
4151
|
const span = Math.min(params.window * 2 + 1, CONTEXT_ALL_MAX_MESSAGES);
|
|
3315
4152
|
const from = Math.max(0, center - Math.floor((span - 1) / 2));
|
|
3316
|
-
const wide = `moda
|
|
4153
|
+
const wide = `moda traces get ${shellArg(contextTarget(params.conversation_id))} --all --from=${from} --max-messages=${span}`;
|
|
3317
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}`);
|
|
3318
4155
|
nextCommands.push({ command: wide, purpose: `Read ±${params.window} messages around the anchor.`, mutability: "read", requires_approval: false });
|
|
3319
4156
|
}
|
|
@@ -3325,8 +4162,8 @@ async function readContextItem(command, params, inputWarnings, fits) {
|
|
|
3325
4162
|
}
|
|
3326
4163
|
};
|
|
3327
4164
|
}
|
|
3328
|
-
var AUDIT_USAGE = "Usage: moda
|
|
3329
|
-
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)`;
|
|
3330
4167
|
async function readAuditItem(command, params, budget) {
|
|
3331
4168
|
const query2 = new URLSearchParams;
|
|
3332
4169
|
if (params.kind && params.kind !== "auto")
|
|
@@ -3439,7 +4276,7 @@ function writeBatchResult(command, context, targets, results, batchWarnings, fla
|
|
|
3439
4276
|
const nextCommands = [];
|
|
3440
4277
|
if (remaining.length > 0) {
|
|
3441
4278
|
const lines = remaining.map((target) => shellArg(target.raw)).join(" ");
|
|
3442
|
-
const continueWith = [`printf '%s\\n' ${lines} |`, `moda ${command}
|
|
4279
|
+
const continueWith = [`printf '%s\\n' ${lines} |`, canonicalCommandText(`moda ${command}`), "--ids-from=-", ...renderBatchFlags(flags)].join(" ");
|
|
3443
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}`);
|
|
3444
4281
|
nextCommands.push({
|
|
3445
4282
|
command: continueWith,
|
|
@@ -3648,7 +4485,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3648
4485
|
}
|
|
3649
4486
|
case "search": {
|
|
3650
4487
|
if (!positional || positional.trim() === "") {
|
|
3651
|
-
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]');
|
|
3652
4489
|
}
|
|
3653
4490
|
const args = withStringIdFlags(flagsToArgs(flags, "q", positional), flags);
|
|
3654
4491
|
const params = parseSpecFlags("search", args, command);
|
|
@@ -3704,12 +4541,12 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3704
4541
|
}
|
|
3705
4542
|
case "world-state": {
|
|
3706
4543
|
if (!positional) {
|
|
3707
|
-
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]");
|
|
3708
4545
|
}
|
|
3709
4546
|
const args = flagsToArgs(flags, "conversation_id", positional);
|
|
3710
4547
|
const params = parseSpecFlags("world-state", args, command);
|
|
3711
4548
|
if (params.snapshot === true && params.replay === true) {
|
|
3712
|
-
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]]");
|
|
3713
4550
|
}
|
|
3714
4551
|
if (params.snapshot === true) {
|
|
3715
4552
|
const query3 = new URLSearchParams;
|
|
@@ -3789,9 +4626,11 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3789
4626
|
if (params.conversation_id)
|
|
3790
4627
|
query2.set("conversation_id", params.conversation_id);
|
|
3791
4628
|
query2.set("family", "frustration");
|
|
3792
|
-
const data = frustrationsFromEmotions(await callDataAPI(`/emotions?${query2.toString()}`));
|
|
3793
4629
|
const includeWindow = params.include_window === true;
|
|
3794
4630
|
const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
|
|
4631
|
+
if (agentMode && !includeWindow)
|
|
4632
|
+
query2.set("include_context", "0");
|
|
4633
|
+
const data = frustrationsFromEmotions(await callDataAPI(`/emotions?${query2.toString()}`));
|
|
3795
4634
|
const anchored = withFrustrationAnchors(data);
|
|
3796
4635
|
await printWithOptionalWindows(agentMode && !includeWindow ? withoutDetectionWindows(anchored, "frustrations", "conversation") : anchored, "frustrations", includeWindow, params.window ?? 1, context.output, processingDisabledWarning(data, "frustration"), agentMode && includeWindow ? (payload) => withoutRepeatedFrustrationWindow(payload) : undefined);
|
|
3797
4636
|
break;
|
|
@@ -3806,7 +4645,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3806
4645
|
}
|
|
3807
4646
|
case "tool-failure-detail": {
|
|
3808
4647
|
if (!positional) {
|
|
3809
|
-
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]]");
|
|
3810
4649
|
}
|
|
3811
4650
|
const { "no-include-window": noIncludeWindow, ...toolFlags } = flags;
|
|
3812
4651
|
if (noIncludeWindow !== undefined && noIncludeWindow !== "false") {
|
|
@@ -3829,13 +4668,15 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3829
4668
|
query2.set("limit", (params.limit ?? TOOL_FAILURE_DETAIL_DEFAULT_LIMIT).toString());
|
|
3830
4669
|
if (params.offset !== undefined)
|
|
3831
4670
|
query2.set("offset", params.offset.toString());
|
|
4671
|
+
if (dropConversation)
|
|
4672
|
+
query2.set("include_context", "0");
|
|
3832
4673
|
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
3833
4674
|
const data = await callDataAPI(`/tool-failures/${encodeURIComponent(params.tool_name)}${queryString}`);
|
|
3834
4675
|
const tfRecord = asRecord(data) ?? {};
|
|
3835
4676
|
const subtypeCount = Array.isArray(tfRecord.subtypes) ? tfRecord.subtypes.length : 0;
|
|
3836
4677
|
const exampleCount = Array.isArray(tfRecord.examples) ? tfRecord.examples.length : 0;
|
|
3837
4678
|
const warnings = subtypeCount === 0 && exampleCount === 0 ? [
|
|
3838
|
-
`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.`
|
|
3839
4680
|
] : [];
|
|
3840
4681
|
const knownSubtypes = Array.isArray(tfRecord.subtypes) ? tfRecord.subtypes.map((row) => asString(asRecord(row)?.subtype)).filter((value) => Boolean(value)) : [];
|
|
3841
4682
|
if (params.subtype && knownSubtypes.length > 0 && !knownSubtypes.includes(params.subtype)) {
|
|
@@ -3893,7 +4734,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3893
4734
|
break;
|
|
3894
4735
|
}
|
|
3895
4736
|
if (positional) {
|
|
3896
|
-
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>'.");
|
|
3897
4738
|
}
|
|
3898
4739
|
const args = flagsToArgs(flags);
|
|
3899
4740
|
const params = parseSpecFlags("problems", args, command);
|
|
@@ -3920,7 +4761,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3920
4761
|
break;
|
|
3921
4762
|
}
|
|
3922
4763
|
if (!positional) {
|
|
3923
|
-
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]");
|
|
3924
4765
|
}
|
|
3925
4766
|
const args = flagsToArgs(flags, "id", positional);
|
|
3926
4767
|
const params = parseSpecFlags("problem", args, command);
|
|
@@ -3928,11 +4769,11 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3928
4769
|
const views = ["evidence", "reports", "conversations", "feedback"].filter((view2) => view2 === "conversations" ? traceView : params[view2] === true);
|
|
3929
4770
|
if (views.length > 1) {
|
|
3930
4771
|
const got = views.map((v) => v === "conversations" ? "--traces" : `--${v}`).join(" ");
|
|
3931
|
-
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]");
|
|
3932
4773
|
}
|
|
3933
4774
|
const view = views[0];
|
|
3934
4775
|
if (view !== undefined && params.full === true) {
|
|
3935
|
-
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");
|
|
3936
4777
|
}
|
|
3937
4778
|
if (view === undefined) {
|
|
3938
4779
|
const data2 = await callDataAPI(`/problems/${encodeURIComponent(params.id)}`);
|
|
@@ -3955,7 +4796,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3955
4796
|
}
|
|
3956
4797
|
if (!UUID_RE.test(params.id)) {
|
|
3957
4798
|
const viewFlag = view === "conversations" ? "--traces" : `--${view}`;
|
|
3958
|
-
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.`);
|
|
3959
4800
|
}
|
|
3960
4801
|
const query2 = new URLSearchParams;
|
|
3961
4802
|
if (params.limit)
|
|
@@ -3975,7 +4816,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3975
4816
|
}
|
|
3976
4817
|
case "problem-feedback": {
|
|
3977
4818
|
if (!positional) {
|
|
3978
|
-
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]`);
|
|
3979
4820
|
}
|
|
3980
4821
|
const params = ProblemFeedbackSchema.parse({
|
|
3981
4822
|
id: positional,
|
|
@@ -3997,7 +4838,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
3997
4838
|
}
|
|
3998
4839
|
if (params.action === "flag_attribution") {
|
|
3999
4840
|
if (params.attribution_id === undefined || params.attribution_id.trim() === "") {
|
|
4000
|
-
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.");
|
|
4001
4842
|
}
|
|
4002
4843
|
if (!UUID_RE.test(params.attribution_id)) {
|
|
4003
4844
|
throw new CliInputError("--attribution-id must be a UUID.");
|
|
@@ -4019,7 +4860,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4019
4860
|
}
|
|
4020
4861
|
case "problem-linear": {
|
|
4021
4862
|
if (!positional) {
|
|
4022
|
-
throw new CliInputError("<problem_id> is required", "Usage: moda
|
|
4863
|
+
throw new CliInputError("<problem_id> is required", "Usage: moda problems linear <problem_id>");
|
|
4023
4864
|
}
|
|
4024
4865
|
if (!UUID_RE.test(positional)) {
|
|
4025
4866
|
throw new CliInputError(`Problem id must be a canonical UUID; got "${positional}". Run \`moda problems\` to list problems with their canonical ids.`);
|
|
@@ -4029,17 +4870,17 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4029
4870
|
});
|
|
4030
4871
|
const link = asRecord(data)?.link;
|
|
4031
4872
|
const summary = describeLinearLink(link);
|
|
4032
|
-
const nextCommands = link ? [] : [`moda
|
|
4873
|
+
const nextCommands = link ? [] : [`moda problems link ${positional}`];
|
|
4033
4874
|
if (context.outputMode === "human" && !context.quiet) {
|
|
4034
4875
|
safeWriteStderr(link ? `${summary}
|
|
4035
|
-
` : `${summary} File one with: moda
|
|
4876
|
+
` : `${summary} File one with: moda problems link ${positional}
|
|
4036
4877
|
`);
|
|
4037
4878
|
}
|
|
4038
4879
|
context.output.writeData(data, { summary, nextCommands });
|
|
4039
4880
|
break;
|
|
4040
4881
|
}
|
|
4041
4882
|
case "problem-linear-file": {
|
|
4042
|
-
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]";
|
|
4043
4884
|
if (!positional) {
|
|
4044
4885
|
throw new CliInputError("<problem_id> is required", usage);
|
|
4045
4886
|
}
|
|
@@ -4118,14 +4959,14 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4118
4959
|
}
|
|
4119
4960
|
case "trace-signals": {
|
|
4120
4961
|
if (!positional) {
|
|
4121
|
-
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>");
|
|
4122
4963
|
}
|
|
4123
4964
|
const data = await callDataAPI(`/conversations/${encodeURIComponent(positional)}/custom-signals`);
|
|
4124
4965
|
context.output.writeData(data);
|
|
4125
4966
|
break;
|
|
4126
4967
|
}
|
|
4127
4968
|
case "signal-label": {
|
|
4128
|
-
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]`;
|
|
4129
4970
|
if (!positional) {
|
|
4130
4971
|
throw new CliInputError("<signal_id> is required", usage);
|
|
4131
4972
|
}
|
|
@@ -4154,7 +4995,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4154
4995
|
}
|
|
4155
4996
|
case "detection-reviews": {
|
|
4156
4997
|
if (!positional) {
|
|
4157
|
-
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]");
|
|
4158
4999
|
}
|
|
4159
5000
|
const attributionId = flags["attribution-id"];
|
|
4160
5001
|
if (attributionId !== undefined && !UUID_RE.test(attributionId)) {
|
|
@@ -4166,7 +5007,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4166
5007
|
break;
|
|
4167
5008
|
}
|
|
4168
5009
|
case "detection-review": {
|
|
4169
|
-
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]`;
|
|
4170
5011
|
if (!positional) {
|
|
4171
5012
|
throw new CliInputError("<problem_id> is required", usage);
|
|
4172
5013
|
}
|
|
@@ -4193,7 +5034,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4193
5034
|
break;
|
|
4194
5035
|
}
|
|
4195
5036
|
case "false-positive": {
|
|
4196
|
-
const usage = `Usage: moda
|
|
5037
|
+
const usage = `Usage: moda review <${FALSE_POSITIVE_KINDS.join("|")}> <target> --reason=TEXT [--undo]`;
|
|
4197
5038
|
const [kind, target] = positionals;
|
|
4198
5039
|
if (!kind || !target)
|
|
4199
5040
|
throw new CliInputError("<kind> and <target> are required", usage);
|
|
@@ -4266,7 +5107,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4266
5107
|
const query2 = new URLSearchParams;
|
|
4267
5108
|
if (flags.kind !== undefined) {
|
|
4268
5109
|
if (flags.kind === "problem" || flags.kind === "problem-detection") {
|
|
4269
|
-
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");
|
|
4270
5111
|
}
|
|
4271
5112
|
if (!kinds[flags.kind])
|
|
4272
5113
|
throw new CliInputError(`--kind must be one of ${Object.keys(kinds).join("|")}`);
|
|
@@ -4304,10 +5145,12 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
4304
5145
|
query2.set("user_id", params.user_id);
|
|
4305
5146
|
if (params.conversation_id)
|
|
4306
5147
|
query2.set("conversation_id", params.conversation_id);
|
|
5148
|
+
const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
|
|
5149
|
+
if (agentMode && params.include_window !== true)
|
|
5150
|
+
query2.set("include_context", "0");
|
|
4307
5151
|
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
4308
5152
|
const raw = await callDataAPI(`/emotions${queryString}`);
|
|
4309
5153
|
const processingWarnings = processingDisabledWarning(raw, "emotion");
|
|
4310
|
-
const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
|
|
4311
5154
|
const record = asRecord(raw);
|
|
4312
5155
|
const data = agentMode && params.include_window !== true && record ? withoutDetectionWindows(record, "detections", "conversation_context") : raw;
|
|
4313
5156
|
context.output.writeData(data, processingWarnings.length > 0 ? { warnings: processingWarnings } : undefined);
|
|
@@ -4473,7 +5316,7 @@ function initAgentWriteOptions(result) {
|
|
|
4473
5316
|
warnings: [
|
|
4474
5317
|
...manualActions.filter((action) => action.reason).map((action) => `${action.title}: ${action.reason}`),
|
|
4475
5318
|
...result.promptSync?.status === "failed" ? [`Prompt sync failed: ${result.promptSync.error}. Retry with 'moda prompts sync'.`] : [],
|
|
4476
|
-
...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'.`] : [],
|
|
4477
5320
|
...result.skillSync?.warnings ?? [],
|
|
4478
5321
|
...(result.skillInstall?.warnings ?? []).map((warning) => `moda-cli skill install: ${warning}`)
|
|
4479
5322
|
]
|
|
@@ -4491,6 +5334,10 @@ function initArtifactKind(file) {
|
|
|
4491
5334
|
return "ci_workflow";
|
|
4492
5335
|
return "init_artifact";
|
|
4493
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
|
+
}
|
|
4494
5341
|
var dataApiDefaults = {
|
|
4495
5342
|
auth: "api-key",
|
|
4496
5343
|
outputModes: ["human", "json", "agent"],
|
|
@@ -4535,7 +5382,7 @@ var commandRegistry = createCommandRegistry([
|
|
|
4535
5382
|
resetApiRequestCountBeforeRun: true,
|
|
4536
5383
|
telemetry: "result-and-error",
|
|
4537
5384
|
handler: async (context) => {
|
|
4538
|
-
const { runInit } = await import("./index-
|
|
5385
|
+
const { runInit } = await import("./index-e1pf1qz2.js");
|
|
4539
5386
|
if (context.outputMode === "agent-stream") {
|
|
4540
5387
|
context.output.writeEvent({
|
|
4541
5388
|
event: "started",
|
|
@@ -4585,7 +5432,7 @@ var commandRegistry = createCommandRegistry([
|
|
|
4585
5432
|
resetApiRequestCountBeforeRun: true,
|
|
4586
5433
|
telemetry: "result-and-error",
|
|
4587
5434
|
handler: async (context) => {
|
|
4588
|
-
const { runProvision } = await import("./provision-
|
|
5435
|
+
const { runProvision } = await import("./provision-q86phh4a.js");
|
|
4589
5436
|
return runProvision(context, {
|
|
4590
5437
|
resumeCommand: buildResumeCommand("provision", context.rawFlags)
|
|
4591
5438
|
});
|
|
@@ -4869,7 +5716,7 @@ var commandRegistry = createCommandRegistry([
|
|
|
4869
5716
|
{
|
|
4870
5717
|
name: "problem-reopen",
|
|
4871
5718
|
description: "Reopen a problem that was marked fixed (resolved problems return only via re-detection)",
|
|
4872
|
-
examples: ['moda
|
|
5719
|
+
examples: ['moda problems reopen <problem_id> --reason="still happening"', "moda problems reopen <problem_id> --dry-run"],
|
|
4873
5720
|
auth: "api-key",
|
|
4874
5721
|
outputModes: ["human", "json", "agent"],
|
|
4875
5722
|
mutability: "write",
|
|
@@ -5015,21 +5862,23 @@ var commandRegistry = createCommandRegistry([
|
|
|
5015
5862
|
name: "traces",
|
|
5016
5863
|
aliases: ["conversations"],
|
|
5017
5864
|
telemetryCommand: "conversations",
|
|
5018
|
-
description: "
|
|
5865
|
+
description: "Traces (agent runs): list (the bare form; --search is ranked), search, get, state, spans",
|
|
5019
5866
|
examples: [
|
|
5020
|
-
'moda traces --search="error" --limit=5',
|
|
5021
|
-
|
|
5022
|
-
'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'
|
|
5023
5870
|
],
|
|
5871
|
+
subcommands: treeSubcommandHelp("traces"),
|
|
5872
|
+
defaultSubcommand: "list",
|
|
5024
5873
|
...dataApiDefaults
|
|
5025
5874
|
}),
|
|
5026
5875
|
legacyCommand({
|
|
5027
5876
|
name: "search",
|
|
5028
5877
|
description: "Search trace messages (keyword, semantic, or hybrid)",
|
|
5029
5878
|
examples: [
|
|
5030
|
-
'moda search "billing error"',
|
|
5031
|
-
'moda search "refund flow" --mode=semantic --time-range=7d --limit=10',
|
|
5032
|
-
'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'
|
|
5033
5882
|
],
|
|
5034
5883
|
...dataApiDefaults
|
|
5035
5884
|
}),
|
|
@@ -5037,8 +5886,8 @@ var commandRegistry = createCommandRegistry([
|
|
|
5037
5886
|
name: "world-state",
|
|
5038
5887
|
description: "Get a trace's world state (slots, threads, events)",
|
|
5039
5888
|
examples: [
|
|
5040
|
-
"moda
|
|
5041
|
-
"moda
|
|
5889
|
+
"moda traces state <conversation_id>",
|
|
5890
|
+
"moda traces state <conversation_id> --summary-only"
|
|
5042
5891
|
],
|
|
5043
5892
|
...dataApiDefaults
|
|
5044
5893
|
}),
|
|
@@ -5046,13 +5895,13 @@ var commandRegistry = createCommandRegistry([
|
|
|
5046
5895
|
name: "context",
|
|
5047
5896
|
description: "Get windowed trace context (messages around one turn), or the whole trace with --all",
|
|
5048
5897
|
examples: [
|
|
5049
|
-
"moda
|
|
5050
|
-
"moda
|
|
5051
|
-
"moda
|
|
5052
|
-
"moda
|
|
5053
|
-
"moda
|
|
5054
|
-
"moda
|
|
5055
|
-
"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"
|
|
5056
5905
|
],
|
|
5057
5906
|
...dataApiDefaults
|
|
5058
5907
|
}),
|
|
@@ -5061,13 +5910,13 @@ var commandRegistry = createCommandRegistry([
|
|
|
5061
5910
|
aliases: ["trace"],
|
|
5062
5911
|
description: "Raw non-deduped span audit for one trace or OTLP trace_id (hierarchy, tool spans, orphans, duplicates)",
|
|
5063
5912
|
examples: [
|
|
5064
|
-
"moda
|
|
5065
|
-
"moda
|
|
5066
|
-
"moda
|
|
5067
|
-
"moda
|
|
5068
|
-
"moda
|
|
5069
|
-
"moda
|
|
5070
|
-
"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>)"
|
|
5071
5920
|
],
|
|
5072
5921
|
...dataApiDefaults
|
|
5073
5922
|
}),
|
|
@@ -5075,32 +5924,32 @@ var commandRegistry = createCommandRegistry([
|
|
|
5075
5924
|
name: "frustrations",
|
|
5076
5925
|
description: "User frustration detections: the frustration family of `emotions` (same data as the dashboard), legacy field names",
|
|
5077
5926
|
examples: [
|
|
5078
|
-
"moda
|
|
5079
|
-
"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"
|
|
5080
5929
|
],
|
|
5081
5930
|
...dataApiDefaults
|
|
5082
5931
|
}),
|
|
5083
5932
|
legacyCommand({
|
|
5084
5933
|
name: "tool-failures",
|
|
5085
5934
|
description: "Get tool failure overview",
|
|
5086
|
-
examples: ["moda
|
|
5935
|
+
examples: ["moda detections tools --days-back=14"],
|
|
5087
5936
|
...dataApiDefaults
|
|
5088
5937
|
}),
|
|
5089
5938
|
legacyCommand({
|
|
5090
5939
|
name: "tool-failure-detail",
|
|
5091
5940
|
description: "Get per-tool failure detail (each example embeds its anchor ±2 messages)",
|
|
5092
5941
|
examples: [
|
|
5093
|
-
"moda
|
|
5094
|
-
"moda
|
|
5095
|
-
"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"
|
|
5096
5945
|
],
|
|
5097
5946
|
...dataApiDefaults
|
|
5098
5947
|
}),
|
|
5099
5948
|
legacyCommand({
|
|
5100
5949
|
name: "problems",
|
|
5101
5950
|
description: "Rank cross-signal Problems by root cause (what to fix first)",
|
|
5102
|
-
examples: ["moda problems", "moda problems --days-back=7 --limit=10", "moda problems
|
|
5103
|
-
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"),
|
|
5104
5953
|
defaultSubcommand: "list",
|
|
5105
5954
|
...dataApiDefaults
|
|
5106
5955
|
}),
|
|
@@ -5108,14 +5957,14 @@ var commandRegistry = createCommandRegistry([
|
|
|
5108
5957
|
name: "problem-impact",
|
|
5109
5958
|
description: "Live problem/use-case impact, full analysis denominators, daily recurrence, and paginated evidence",
|
|
5110
5959
|
examples: [
|
|
5111
|
-
"moda
|
|
5112
|
-
"moda
|
|
5113
|
-
"moda
|
|
5114
|
-
"moda
|
|
5115
|
-
"moda
|
|
5116
|
-
"moda
|
|
5117
|
-
"moda
|
|
5118
|
-
`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"]'`
|
|
5119
5968
|
],
|
|
5120
5969
|
...dataApiDefaults
|
|
5121
5970
|
}),
|
|
@@ -5123,12 +5972,12 @@ var commandRegistry = createCommandRegistry([
|
|
|
5123
5972
|
name: "problem",
|
|
5124
5973
|
description: "Open one Problem: dossier, or --evidence/--reports/--traces/--feedback pages (--conversations is a legacy alias of --traces)",
|
|
5125
5974
|
examples: [
|
|
5126
|
-
"moda
|
|
5127
|
-
"moda
|
|
5128
|
-
"moda
|
|
5129
|
-
"moda
|
|
5130
|
-
"moda
|
|
5131
|
-
"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"
|
|
5132
5981
|
],
|
|
5133
5982
|
...dataApiDefaults
|
|
5134
5983
|
}),
|
|
@@ -5136,10 +5985,10 @@ var commandRegistry = createCommandRegistry([
|
|
|
5136
5985
|
name: "problem-feedback",
|
|
5137
5986
|
description: "Submit Problem feedback: mark fixed, dismiss, rename, or flag a bad attribution",
|
|
5138
5987
|
examples: [
|
|
5139
|
-
"moda
|
|
5140
|
-
'moda
|
|
5141
|
-
'moda
|
|
5142
|
-
'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"'
|
|
5143
5992
|
],
|
|
5144
5993
|
...dataApiDefaults,
|
|
5145
5994
|
mutability: "write"
|
|
@@ -5147,16 +5996,16 @@ var commandRegistry = createCommandRegistry([
|
|
|
5147
5996
|
legacyCommand({
|
|
5148
5997
|
name: "problem-linear",
|
|
5149
5998
|
description: "Show the Linear issue linked to a Problem (key, state, URL); needs Linear connected (Integrations -> Linear)",
|
|
5150
|
-
examples: ["moda
|
|
5999
|
+
examples: ["moda problems linear <problem_id>"],
|
|
5151
6000
|
...dataApiDefaults
|
|
5152
6001
|
}),
|
|
5153
6002
|
legacyCommand({
|
|
5154
6003
|
name: "problem-linear-file",
|
|
5155
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)",
|
|
5156
6005
|
examples: [
|
|
5157
|
-
"moda
|
|
5158
|
-
'moda
|
|
5159
|
-
"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"
|
|
5160
6009
|
],
|
|
5161
6010
|
...dataApiDefaults,
|
|
5162
6011
|
mutability: "write"
|
|
@@ -5165,8 +6014,8 @@ var commandRegistry = createCommandRegistry([
|
|
|
5165
6014
|
name: "detection-reviews",
|
|
5166
6015
|
description: "List problem detections on a trace with their review state (correct / false_positive / clear)",
|
|
5167
6016
|
examples: [
|
|
5168
|
-
"moda
|
|
5169
|
-
"moda
|
|
6017
|
+
"moda reviews list <conversation_id> --kind=problem-detection",
|
|
6018
|
+
"moda reviews list <conversation_id> --kind=problem-detection --attribution-id=<uuid>"
|
|
5170
6019
|
],
|
|
5171
6020
|
...dataApiDefaults
|
|
5172
6021
|
}),
|
|
@@ -5174,9 +6023,9 @@ var commandRegistry = createCommandRegistry([
|
|
|
5174
6023
|
name: "detection-review",
|
|
5175
6024
|
description: "Review a problem detection on a trace: mark it correct, a false positive, or clear the review",
|
|
5176
6025
|
examples: [
|
|
5177
|
-
'moda
|
|
5178
|
-
"moda
|
|
5179
|
-
"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"
|
|
5180
6029
|
],
|
|
5181
6030
|
...dataApiDefaults,
|
|
5182
6031
|
mutability: "write"
|
|
@@ -5185,17 +6034,17 @@ var commandRegistry = createCommandRegistry([
|
|
|
5185
6034
|
name: "false-positive",
|
|
5186
6035
|
description: "Flag a detection as a false positive with a reason: problem, problem-detection, quote, tool, laziness, hallucination, or custom-signal",
|
|
5187
6036
|
helpNotes: [
|
|
5188
|
-
"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."
|
|
5189
6038
|
],
|
|
5190
6039
|
examples: [
|
|
5191
|
-
'moda
|
|
5192
|
-
'moda
|
|
5193
|
-
'moda
|
|
5194
|
-
'moda
|
|
5195
|
-
'moda
|
|
5196
|
-
"moda
|
|
5197
|
-
"moda
|
|
5198
|
-
'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"'
|
|
5199
6048
|
],
|
|
5200
6049
|
...dataApiDefaults,
|
|
5201
6050
|
mutability: "write"
|
|
@@ -5204,9 +6053,9 @@ var commandRegistry = createCommandRegistry([
|
|
|
5204
6053
|
name: "false-positives",
|
|
5205
6054
|
description: "List current false-positive marks across the workspace, or on one trace (with problem detection reviews)",
|
|
5206
6055
|
examples: [
|
|
5207
|
-
"moda
|
|
5208
|
-
"moda
|
|
5209
|
-
"moda
|
|
6056
|
+
"moda reviews list",
|
|
6057
|
+
"moda reviews list --kind=tool --all",
|
|
6058
|
+
"moda reviews list <conversation_id>"
|
|
5210
6059
|
],
|
|
5211
6060
|
...dataApiDefaults
|
|
5212
6061
|
}),
|
|
@@ -5229,22 +6078,22 @@ var commandRegistry = createCommandRegistry([
|
|
|
5229
6078
|
description: "Open one custom signal, or --detections for its recent hits (paged)",
|
|
5230
6079
|
examples: [
|
|
5231
6080
|
"moda signal <signal_id>",
|
|
5232
|
-
"moda signal
|
|
6081
|
+
"moda detections list --family=custom_signal --signal-id=<signal_id> --days-back=30 --limit=20"
|
|
5233
6082
|
],
|
|
5234
6083
|
...dataApiDefaults
|
|
5235
6084
|
}),
|
|
5236
6085
|
legacyCommand({
|
|
5237
6086
|
name: "trace-signals",
|
|
5238
6087
|
description: "Custom signal hits on one trace",
|
|
5239
|
-
examples: ["moda
|
|
6088
|
+
examples: ["moda detections list --family=custom_signal --conversation-id=<conversation_id>"],
|
|
5240
6089
|
...dataApiDefaults
|
|
5241
6090
|
}),
|
|
5242
6091
|
legacyCommand({
|
|
5243
6092
|
name: "signal-label",
|
|
5244
6093
|
description: "Label a trace for a custom signal: fits, or does_not_fit to mark a false positive",
|
|
5245
6094
|
examples: [
|
|
5246
|
-
'moda signal
|
|
5247
|
-
"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"
|
|
5248
6097
|
],
|
|
5249
6098
|
...dataApiDefaults,
|
|
5250
6099
|
mutability: "write"
|
|
@@ -5253,9 +6102,9 @@ var commandRegistry = createCommandRegistry([
|
|
|
5253
6102
|
name: "emotions",
|
|
5254
6103
|
description: "Multi-family emotion detections (frustration, disapproval, sadness, confusion, anxiety, trust, positive)",
|
|
5255
6104
|
examples: [
|
|
5256
|
-
"moda
|
|
5257
|
-
"moda
|
|
5258
|
-
"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"
|
|
5259
6108
|
],
|
|
5260
6109
|
...dataApiDefaults
|
|
5261
6110
|
}),
|
|
@@ -5263,8 +6112,8 @@ var commandRegistry = createCommandRegistry([
|
|
|
5263
6112
|
name: "laziness",
|
|
5264
6113
|
description: "Laziness detections per trace: repeated tool calls, premature handoffs, and other lazy patterns",
|
|
5265
6114
|
examples: [
|
|
5266
|
-
"moda laziness --days-back=30",
|
|
5267
|
-
"moda laziness --conversation-id=<id>"
|
|
6115
|
+
"moda detections list --family=laziness --days-back=30",
|
|
6116
|
+
"moda detections list --family=laziness --conversation-id=<id>"
|
|
5268
6117
|
],
|
|
5269
6118
|
...dataApiDefaults
|
|
5270
6119
|
}),
|
|
@@ -5272,9 +6121,9 @@ var commandRegistry = createCommandRegistry([
|
|
|
5272
6121
|
name: "hallucinations",
|
|
5273
6122
|
description: "Grounding detections: contradicted/verified outputs with rule breakdown",
|
|
5274
6123
|
examples: [
|
|
5275
|
-
"moda
|
|
5276
|
-
"moda
|
|
5277
|
-
"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>"
|
|
5278
6127
|
],
|
|
5279
6128
|
...dataApiDefaults
|
|
5280
6129
|
}),
|
|
@@ -5296,6 +6145,44 @@ var commandRegistry = createCommandRegistry([
|
|
|
5296
6145
|
],
|
|
5297
6146
|
...dataApiDefaults
|
|
5298
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
|
+
},
|
|
5299
6186
|
legacyCommand({
|
|
5300
6187
|
name: "feedback",
|
|
5301
6188
|
description: "Flag wrong/missing data or CLI quirks to the Moda team",
|
|
@@ -5417,18 +6304,18 @@ var commandRegistry = createCommandRegistry([
|
|
|
5417
6304
|
}),
|
|
5418
6305
|
{
|
|
5419
6306
|
name: "skills",
|
|
5420
|
-
description: "Generate, pull, and
|
|
6307
|
+
description: "Generate, pull (download), push (upload), and install Moda skills",
|
|
5421
6308
|
examples: [
|
|
5422
6309
|
"moda skills gen",
|
|
5423
6310
|
"moda skills status",
|
|
5424
6311
|
"moda skills pull",
|
|
5425
|
-
"moda skills
|
|
6312
|
+
"moda skills push",
|
|
5426
6313
|
"moda skills install --list",
|
|
5427
6314
|
"moda skills install integration-node-openai"
|
|
5428
6315
|
],
|
|
5429
6316
|
subcommands: SKILLS_SUBCOMMAND_HELP,
|
|
5430
6317
|
auth: "api-key",
|
|
5431
|
-
authNote: "install is local; gen, status, pull,
|
|
6318
|
+
authNote: "install is local; gen, status, pull, push, list, show, policy, and proposals need the API key.",
|
|
5432
6319
|
outputModes: ["human", "json", "agent"],
|
|
5433
6320
|
mutability: "write",
|
|
5434
6321
|
validateConfigBeforeRun: false,
|
|
@@ -5533,44 +6420,45 @@ function cliValidationReason(error) {
|
|
|
5533
6420
|
return;
|
|
5534
6421
|
}
|
|
5535
6422
|
function printCliError(error) {
|
|
6423
|
+
const safeWriteStderr2 = (text) => safeWriteStderr(canonicalCommandText(text));
|
|
5536
6424
|
if (error instanceof CliInputError) {
|
|
5537
|
-
|
|
6425
|
+
safeWriteStderr2(`Error: ${error.message}
|
|
5538
6426
|
`);
|
|
5539
6427
|
if (error.usage)
|
|
5540
|
-
|
|
6428
|
+
safeWriteStderr2(`${error.usage}
|
|
5541
6429
|
`);
|
|
5542
6430
|
} else if (error instanceof CliAccessError) {
|
|
5543
|
-
|
|
6431
|
+
safeWriteStderr2(`Error: ${error.message}
|
|
5544
6432
|
`);
|
|
5545
6433
|
if (error.guidance)
|
|
5546
|
-
|
|
6434
|
+
safeWriteStderr2(`${error.guidance}
|
|
5547
6435
|
`);
|
|
5548
6436
|
} else if (error instanceof z.ZodError) {
|
|
5549
6437
|
const issues = error.errors.map((e) => {
|
|
5550
6438
|
const field = e.path.length ? e.path.join(".") : "input";
|
|
5551
6439
|
return ` ${field}: ${e.message}`;
|
|
5552
6440
|
});
|
|
5553
|
-
|
|
6441
|
+
safeWriteStderr2(`Validation error:
|
|
5554
6442
|
${[...new Set(issues)].join(`
|
|
5555
6443
|
`)}
|
|
5556
6444
|
`);
|
|
5557
6445
|
} else if (error instanceof ApiError && error.readable) {
|
|
5558
|
-
|
|
6446
|
+
safeWriteStderr2(`${error.message}
|
|
5559
6447
|
`);
|
|
5560
6448
|
if (error.suggestions?.length) {
|
|
5561
|
-
|
|
6449
|
+
safeWriteStderr2(`Try:
|
|
5562
6450
|
${error.suggestions.map((s) => ` ${s}`).join(`
|
|
5563
6451
|
`)}
|
|
5564
6452
|
`);
|
|
5565
6453
|
}
|
|
5566
6454
|
} else if (error instanceof ApiError) {
|
|
5567
|
-
|
|
6455
|
+
safeWriteStderr2(`API error (HTTP ${error.statusCode}): ${error.responseBody || error.message}
|
|
5568
6456
|
`);
|
|
5569
6457
|
} else if (error instanceof Error) {
|
|
5570
|
-
|
|
6458
|
+
safeWriteStderr2(`Error: ${error.message}
|
|
5571
6459
|
`);
|
|
5572
6460
|
} else {
|
|
5573
|
-
|
|
6461
|
+
safeWriteStderr2(`Error: ${String(error)}
|
|
5574
6462
|
`);
|
|
5575
6463
|
}
|
|
5576
6464
|
}
|
|
@@ -5592,14 +6480,18 @@ ${colorOk ? `\x1B[2m${text}\x1B[0m` : text}
|
|
|
5592
6480
|
function unknownCommandError(command) {
|
|
5593
6481
|
const visible = commandRegistry.all().filter((entry) => !entry.hidden);
|
|
5594
6482
|
const names = visible.flatMap((entry) => [entry.name, ...entry.aliases ?? []]);
|
|
5595
|
-
const
|
|
6483
|
+
const canonical = (name) => {
|
|
6484
|
+
const legacy = legacyNameFor(name, undefined, {});
|
|
6485
|
+
return legacy ? usePath(legacy) : name;
|
|
6486
|
+
};
|
|
6487
|
+
const matches = [...new Set(closestMatches(command, names).map(canonical))];
|
|
5596
6488
|
const suggestion = matches.length > 0 ? ` Did you mean ${matches.map((name) => `'${name}'`).join(" or ")}?` : "";
|
|
5597
6489
|
return new CliInputError(`Unknown command '${command}'.${suggestion}`, [
|
|
5598
6490
|
...matches.map((name) => `\`moda ${name} --help\``),
|
|
5599
6491
|
"Run `moda --help` for the command list."
|
|
5600
6492
|
].join(" "), "unknown_command", {
|
|
5601
6493
|
field: "<command>",
|
|
5602
|
-
allowed: visible.map((entry) => entry.name),
|
|
6494
|
+
allowed: visible.map((entry) => entry.name).filter((name) => !legacyNameFor(name, undefined, {})),
|
|
5603
6495
|
...matches.length > 0 ? { didYouMean: matches } : {},
|
|
5604
6496
|
suggestedCommands: [...matches.map((name) => `moda ${name} --help`), "moda --help"]
|
|
5605
6497
|
});
|
|
@@ -5628,10 +6520,50 @@ async function dispatchParsedCommand(parsed, startedAt = Date.now()) {
|
|
|
5628
6520
|
process.env.MODA_PROFILE = previousProfileEnv;
|
|
5629
6521
|
}
|
|
5630
6522
|
}
|
|
5631
|
-
|
|
5632
|
-
|
|
6523
|
+
function usageIdentityFor(parsed, route, legacy, definition) {
|
|
6524
|
+
if (route)
|
|
6525
|
+
return { command: route.leaf.noun, subcommand: route.telemetrySubcommand };
|
|
6526
|
+
if (legacy) {
|
|
6527
|
+
const kind = legacy.legacy === "false-positive" && FALSE_POSITIVE_KINDS.includes(parsed.positional ?? "") ? parsed.positional : undefined;
|
|
6528
|
+
return { ...legacy.canonical, ...kind ? { subcommand: kind } : {}, legacyAlias: legacy.legacy };
|
|
6529
|
+
}
|
|
6530
|
+
return definition ? canonicalTelemetryForDefault(definition.name, parsed.positional) : undefined;
|
|
6531
|
+
}
|
|
6532
|
+
function rejectedRouteIdentity(parsed) {
|
|
6533
|
+
if (parsed.command === "review") {
|
|
6534
|
+
const kind = FALSE_POSITIVE_KINDS.includes(parsed.positional ?? "") ? parsed.positional : undefined;
|
|
6535
|
+
return { command: "review", ...kind ? { subcommand: kind } : {} };
|
|
6536
|
+
}
|
|
6537
|
+
const leaf = leafFor(parsed.command, parsed.positional);
|
|
6538
|
+
return { command: parsed.command, ...leaf ? { subcommand: leaf.verb } : {} };
|
|
6539
|
+
}
|
|
6540
|
+
function assertRoutedFlags(route, flags) {
|
|
6541
|
+
if (!route.allowedFlags)
|
|
6542
|
+
return;
|
|
6543
|
+
const shape = z.object(Object.fromEntries(route.allowedFlags.map((flag) => [flag.replace(/-/g, "_"), z.unknown()])));
|
|
6544
|
+
const aliases = new Set(Object.keys(route.flagAliases ?? {}));
|
|
6545
|
+
const args = Object.fromEntries(Object.keys(flags).filter((flag) => !aliases.has(flag.replace(/_/g, "-"))).map((flag) => [flag.replace(/-/g, "_"), true]));
|
|
6546
|
+
parseCommandFlags(shape.passthrough(), args, route.label, route.flagAliases ? { flagAliases: route.flagAliases } : {});
|
|
6547
|
+
}
|
|
6548
|
+
function renameErrorCommands(error, renames) {
|
|
6549
|
+
if (error instanceof CliInputError) {
|
|
6550
|
+
const details2 = error.details;
|
|
6551
|
+
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);
|
|
6552
|
+
const warnings = error.warnings;
|
|
6553
|
+
if (warnings !== undefined)
|
|
6554
|
+
Object.assign(renamed, { warnings });
|
|
6555
|
+
return renamed;
|
|
6556
|
+
}
|
|
6557
|
+
const details = inputDetailsOf(error);
|
|
6558
|
+
if (details?.suggestedCommands && error !== null && typeof error === "object") {
|
|
6559
|
+
attachInputDetails(error, { ...details, suggestedCommands: details.suggestedCommands.map((command) => renameCommands(command, renames)) });
|
|
6560
|
+
}
|
|
6561
|
+
return error;
|
|
6562
|
+
}
|
|
6563
|
+
async function dispatchParsedCommandInner(typedParsed, startedAt) {
|
|
6564
|
+
const { command, flags } = typedParsed;
|
|
5633
6565
|
const switchError = normalizeBooleanFlags(flags);
|
|
5634
|
-
const
|
|
6566
|
+
const typedFlags = stripUpdateCheckFlag(stripTelemetryFlag(flags));
|
|
5635
6567
|
const telemetryDisabled = isTelemetryDisabled(flags);
|
|
5636
6568
|
const updateCheckDisabled = isUpdateCheckDisabled(flags);
|
|
5637
6569
|
if (command === "--version" || command === "-V" || flags.version === "true") {
|
|
@@ -5639,9 +6571,11 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
|
|
|
5639
6571
|
return 0;
|
|
5640
6572
|
}
|
|
5641
6573
|
if (command === "--help" || command === "-h" || flags.help === "true") {
|
|
6574
|
+
if (command && printTreeHelp(typedParsed))
|
|
6575
|
+
return 0;
|
|
5642
6576
|
const helpTarget = command && command !== "--help" && command !== "-h" ? commandRegistry.find(command) : undefined;
|
|
5643
6577
|
if (helpTarget) {
|
|
5644
|
-
const subcommandName =
|
|
6578
|
+
const subcommandName = typedParsed.positional;
|
|
5645
6579
|
const subcommandEntry = subcommandName ? helpTarget.subcommands?.find((entry) => entry.name === subcommandName) : undefined;
|
|
5646
6580
|
if (subcommandEntry) {
|
|
5647
6581
|
printSubcommandHelp(helpTarget, subcommandEntry);
|
|
@@ -5661,62 +6595,117 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
|
|
|
5661
6595
|
printHelp();
|
|
5662
6596
|
return 1;
|
|
5663
6597
|
}
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
6598
|
+
let route;
|
|
6599
|
+
let routeError;
|
|
6600
|
+
try {
|
|
6601
|
+
route = routeCanonical(command, typedParsed.positionals, typedFlags);
|
|
6602
|
+
} catch (error) {
|
|
6603
|
+
routeError = error;
|
|
6604
|
+
}
|
|
6605
|
+
const legacy = route || routeError ? undefined : legacyNameFor(command, typedParsed.positional, typedFlags);
|
|
6606
|
+
const parsed = route ? {
|
|
6607
|
+
...typedParsed,
|
|
6608
|
+
command: route.handler,
|
|
6609
|
+
positional: route.positionals[0],
|
|
6610
|
+
positionals: route.positionals,
|
|
6611
|
+
flags: route.rewriteFlags(flags)
|
|
6612
|
+
} : typedParsed;
|
|
6613
|
+
const commandFlags = route ? route.rewriteFlags(typedFlags) : typedFlags;
|
|
6614
|
+
const outputModeFor = () => resolveOutputMode(typedFlags, {
|
|
6615
|
+
isTTY: process.stdout.isTTY,
|
|
6616
|
+
env: process.env
|
|
6617
|
+
});
|
|
6618
|
+
const reportUsage = (input) => {
|
|
6619
|
+
const { definition: definition2, identity: identity2 } = input;
|
|
6620
|
+
const handlerTelemetry = definition2 ? telemetryCommandName(definition2) : identity2?.command;
|
|
6621
|
+
if (!handlerTelemetry)
|
|
6622
|
+
return;
|
|
6623
|
+
const telemetryProfile = existingFlagProfile(typedFlags);
|
|
6624
|
+
const adoption = buildSearchAdoptionTelemetry({
|
|
6625
|
+
command: handlerTelemetry,
|
|
6626
|
+
positional: parsed.positional,
|
|
6627
|
+
flags: commandFlags,
|
|
6628
|
+
outputMode: input.outputMode,
|
|
6629
|
+
recorded: input.recorded,
|
|
6630
|
+
skillVersion: process.env.MODA_SKILL_VERSION,
|
|
6631
|
+
subcommands: definition2 ? telemetrySubcommandNames(definition2) : [],
|
|
6632
|
+
defaultSubcommand: definition2?.defaultSubcommand
|
|
6633
|
+
});
|
|
6634
|
+
if (identity2 && adoption) {
|
|
6635
|
+
if (identity2.subcommand)
|
|
6636
|
+
adoption.subcommand = identity2.subcommand;
|
|
6637
|
+
else
|
|
6638
|
+
delete adoption.subcommand;
|
|
6639
|
+
}
|
|
6640
|
+
sendCliUsageTelemetry({
|
|
6641
|
+
apiKey: input.apiKey ?? resolveApiKey({ profile: telemetryProfile }),
|
|
6642
|
+
profile: telemetryProfile,
|
|
6643
|
+
command: identity2?.command ?? handlerTelemetry,
|
|
6644
|
+
flags: typedFlags,
|
|
6645
|
+
status: input.status,
|
|
6646
|
+
...input.error !== undefined ? { errorType: classifyCliError(input.error), validationReason: cliValidationReason(input.error) } : {},
|
|
6647
|
+
exitCode: input.exitCode,
|
|
6648
|
+
durationMs: Date.now() - startedAt,
|
|
6649
|
+
apiRequestCount: input.apiRequestCount,
|
|
6650
|
+
adoption,
|
|
6651
|
+
...identity2?.legacyAlias ? { legacyAlias: identity2.legacyAlias } : {}
|
|
5668
6652
|
});
|
|
6653
|
+
};
|
|
6654
|
+
const failEarly = (error, commandDefinition, identity2) => {
|
|
6655
|
+
const mode = outputModeFor();
|
|
5669
6656
|
const telemetryOptOut = telemetryDisabled || flags["no-telemetry"] !== undefined && flags["no-telemetry"] !== "false";
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
profile: telemetryProfile,
|
|
5676
|
-
command: telemetryCommand2,
|
|
5677
|
-
flags: commandFlags,
|
|
6657
|
+
const reports = commandDefinition ? commandDefinition.telemetry === "result-and-error" : identity2 !== undefined;
|
|
6658
|
+
if (reports && !telemetryOptOut) {
|
|
6659
|
+
reportUsage({
|
|
6660
|
+
definition: commandDefinition,
|
|
6661
|
+
identity: identity2,
|
|
5678
6662
|
status: "error",
|
|
5679
|
-
|
|
5680
|
-
validationReason: cliValidationReason(error),
|
|
6663
|
+
error,
|
|
5681
6664
|
exitCode: 1,
|
|
5682
|
-
durationMs: Date.now() - startedAt,
|
|
5683
6665
|
apiRequestCount: 0,
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
positional: parsed.positional,
|
|
5687
|
-
flags: commandFlags,
|
|
5688
|
-
outputMode: mode,
|
|
5689
|
-
recorded: {},
|
|
5690
|
-
skillVersion: process.env.MODA_SKILL_VERSION,
|
|
5691
|
-
subcommands: telemetrySubcommandNames(commandDefinition),
|
|
5692
|
-
defaultSubcommand: commandDefinition.defaultSubcommand
|
|
5693
|
-
})
|
|
6666
|
+
outputMode: mode,
|
|
6667
|
+
recorded: {}
|
|
5694
6668
|
});
|
|
5695
6669
|
}
|
|
5696
6670
|
if (mode === "agent" || mode === "agent-stream" || mode === "json") {
|
|
5697
|
-
printAgentCliError(command, error, mode);
|
|
6671
|
+
printAgentCliError(route?.leaf.path ?? command, error, mode);
|
|
5698
6672
|
} else {
|
|
5699
6673
|
printCliError(error);
|
|
5700
6674
|
}
|
|
5701
6675
|
return 1;
|
|
5702
6676
|
};
|
|
5703
|
-
|
|
6677
|
+
if (routeError) {
|
|
6678
|
+
if (!(routeError instanceof CliInputError))
|
|
6679
|
+
throw routeError;
|
|
6680
|
+
return failEarly(routeError, undefined, rejectedRouteIdentity(typedParsed));
|
|
6681
|
+
}
|
|
6682
|
+
const definition = commandRegistry.find(parsed.command);
|
|
5704
6683
|
if (!definition)
|
|
5705
6684
|
return failEarly(unknownCommandError(command));
|
|
6685
|
+
const identity = usageIdentityFor(typedParsed, route, legacy, definition);
|
|
5706
6686
|
if (parsed.shortFlags?.length) {
|
|
5707
|
-
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);
|
|
6687
|
+
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);
|
|
5708
6688
|
}
|
|
5709
6689
|
if (switchError)
|
|
5710
|
-
return failEarly(switchError, definition);
|
|
6690
|
+
return failEarly(switchError, definition, identity);
|
|
6691
|
+
if (route) {
|
|
6692
|
+
try {
|
|
6693
|
+
assertRoutedFlags(route, typedFlags);
|
|
6694
|
+
} catch (error) {
|
|
6695
|
+
if (!(error instanceof CliInputError))
|
|
6696
|
+
throw error;
|
|
6697
|
+
return failEarly(error, definition, identity);
|
|
6698
|
+
}
|
|
6699
|
+
}
|
|
5711
6700
|
if (commandFlags.profile === "true" || commandFlags.profile === "") {
|
|
5712
|
-
return failEarly(new CliInputError("--profile needs a value, e.g. --profile=prod.", "List profiles with 'moda profiles list'.", "missing_value:profile"), definition);
|
|
6701
|
+
return failEarly(new CliInputError("--profile needs a value, e.g. --profile=prod.", "List profiles with 'moda profiles list'.", "missing_value:profile"), definition, identity);
|
|
5713
6702
|
}
|
|
5714
6703
|
const requestedProfile = commandFlags.profile ?? process.env.MODA_PROFILE?.trim();
|
|
5715
6704
|
if (requestedProfile && !PROFILE_MANAGING_COMMANDS.has(definition.name)) {
|
|
5716
6705
|
const known = Object.keys(loadProfileConfig({ env: process.env }).profiles);
|
|
5717
6706
|
if (!known.includes(requestedProfile)) {
|
|
5718
6707
|
const source = commandFlags.profile ? `--profile=${requestedProfile}` : `MODA_PROFILE=${requestedProfile}`;
|
|
5719
|
-
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);
|
|
6708
|
+
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);
|
|
5720
6709
|
}
|
|
5721
6710
|
if (commandFlags.profile)
|
|
5722
6711
|
process.env.MODA_PROFILE = commandFlags.profile;
|
|
@@ -5731,47 +6720,45 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
|
|
|
5731
6720
|
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;
|
|
5732
6721
|
});
|
|
5733
6722
|
}
|
|
6723
|
+
const deprecation = legacy && !legacy.silent ? { legacy: `moda ${legacy.legacy}`, use: legacy.use, message: deprecationMessage(legacy) } : undefined;
|
|
5734
6724
|
const context = createCommandContext(parsed, {
|
|
5735
6725
|
commandName: definition.name,
|
|
5736
6726
|
commandFlags,
|
|
5737
|
-
rawFlags: flags,
|
|
6727
|
+
rawFlags: parsed.flags,
|
|
5738
6728
|
startedAt,
|
|
5739
|
-
outputMode:
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
6729
|
+
outputMode: outputModeFor(),
|
|
6730
|
+
...route ? {
|
|
6731
|
+
outputCommand: route.leaf.path,
|
|
6732
|
+
invocation: {
|
|
6733
|
+
command: route.leaf.path,
|
|
6734
|
+
positionals: route.leaf.verb ? typedParsed.positionals.slice(1) : typedParsed.positionals,
|
|
6735
|
+
flags,
|
|
6736
|
+
...typedParsed.bareFlags ? { bareFlags: typedParsed.bareFlags } : {},
|
|
6737
|
+
routedAs: { command: route.handler, positionals: route.positionals }
|
|
6738
|
+
}
|
|
6739
|
+
} : {},
|
|
6740
|
+
...deprecation ? { deprecation } : {}
|
|
5743
6741
|
});
|
|
5744
6742
|
setActiveContext(context);
|
|
5745
|
-
|
|
5746
|
-
|
|
6743
|
+
if (deprecation && !context.quiet && (context.outputMode === "human" || context.outputMode === "json")) {
|
|
6744
|
+
safeWriteStderr(`Deprecated: ${deprecation.message}
|
|
6745
|
+
`);
|
|
6746
|
+
}
|
|
5747
6747
|
const needsConfig = typeof definition.validateConfigBeforeRun === "function" ? definition.validateConfigBeforeRun(parsed) : definition.validateConfigBeforeRun;
|
|
5748
6748
|
if (needsConfig) {
|
|
5749
6749
|
try {
|
|
5750
6750
|
validateConfig();
|
|
5751
6751
|
} catch (error) {
|
|
5752
6752
|
if (!telemetryDisabled && definition.telemetry === "result-and-error") {
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
profile: telemetryProfile,
|
|
5757
|
-
command: telemetryCommand,
|
|
5758
|
-
flags: commandFlags,
|
|
6753
|
+
reportUsage({
|
|
6754
|
+
definition,
|
|
6755
|
+
identity,
|
|
5759
6756
|
status: "error",
|
|
5760
|
-
|
|
5761
|
-
validationReason: cliValidationReason(error),
|
|
6757
|
+
error,
|
|
5762
6758
|
exitCode: exitCodeForError(error),
|
|
5763
|
-
durationMs: Date.now() - startedAt,
|
|
5764
6759
|
apiRequestCount: getApiRequestCount(),
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
positional: parsed.positional,
|
|
5768
|
-
flags: commandFlags,
|
|
5769
|
-
outputMode: context.outputMode,
|
|
5770
|
-
recorded: context.getTelemetryMeta(),
|
|
5771
|
-
skillVersion: process.env.MODA_SKILL_VERSION,
|
|
5772
|
-
subcommands: telemetrySubcommands,
|
|
5773
|
-
defaultSubcommand: definition.defaultSubcommand
|
|
5774
|
-
})
|
|
6760
|
+
outputMode: context.outputMode,
|
|
6761
|
+
recorded: context.getTelemetryMeta()
|
|
5775
6762
|
});
|
|
5776
6763
|
}
|
|
5777
6764
|
if (context.outputMode === "agent" || context.outputMode === "agent-stream" || context.outputMode === "json") {
|
|
@@ -5788,26 +6775,15 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
|
|
|
5788
6775
|
try {
|
|
5789
6776
|
const result = await definition.handler(context);
|
|
5790
6777
|
if (!telemetryDisabled && definition.telemetry !== "none") {
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
profile: telemetryProfile,
|
|
5795
|
-
command: telemetryCommand,
|
|
5796
|
-
flags: commandFlags,
|
|
6778
|
+
reportUsage({
|
|
6779
|
+
definition,
|
|
6780
|
+
identity,
|
|
5797
6781
|
status: result.exitCode === 0 ? "success" : "error",
|
|
5798
6782
|
exitCode: result.exitCode,
|
|
5799
|
-
|
|
6783
|
+
apiKey: result.apiKey,
|
|
5800
6784
|
apiRequestCount: getApiRequestCount(),
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
positional: parsed.positional,
|
|
5804
|
-
flags: commandFlags,
|
|
5805
|
-
outputMode: context.outputMode,
|
|
5806
|
-
recorded: context.getTelemetryMeta(),
|
|
5807
|
-
skillVersion: process.env.MODA_SKILL_VERSION,
|
|
5808
|
-
subcommands: telemetrySubcommands,
|
|
5809
|
-
defaultSubcommand: definition.defaultSubcommand
|
|
5810
|
-
})
|
|
6785
|
+
outputMode: context.outputMode,
|
|
6786
|
+
recorded: context.getTelemetryMeta()
|
|
5811
6787
|
});
|
|
5812
6788
|
}
|
|
5813
6789
|
if (!updateCheckDisabled) {
|
|
@@ -5815,30 +6791,18 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
|
|
|
5815
6791
|
}
|
|
5816
6792
|
printFeedbackTip(definition.name, context);
|
|
5817
6793
|
return finishRun(context, { kind: "completed", exitCode: result.exitCode });
|
|
5818
|
-
} catch (
|
|
6794
|
+
} catch (thrown) {
|
|
6795
|
+
const error = route ? renameErrorCommands(thrown, route.renames) : thrown;
|
|
5819
6796
|
if (!telemetryDisabled && definition.telemetry === "result-and-error") {
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
profile: telemetryProfile,
|
|
5824
|
-
command: telemetryCommand,
|
|
5825
|
-
flags: commandFlags,
|
|
6797
|
+
reportUsage({
|
|
6798
|
+
definition,
|
|
6799
|
+
identity,
|
|
5826
6800
|
status: "error",
|
|
5827
|
-
|
|
5828
|
-
validationReason: cliValidationReason(error),
|
|
6801
|
+
error,
|
|
5829
6802
|
exitCode: exitCodeForError(error),
|
|
5830
|
-
durationMs: Date.now() - startedAt,
|
|
5831
6803
|
apiRequestCount: getApiRequestCount(),
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
positional: parsed.positional,
|
|
5835
|
-
flags: commandFlags,
|
|
5836
|
-
outputMode: context.outputMode,
|
|
5837
|
-
recorded: context.getTelemetryMeta(),
|
|
5838
|
-
skillVersion: process.env.MODA_SKILL_VERSION,
|
|
5839
|
-
subcommands: telemetrySubcommands,
|
|
5840
|
-
defaultSubcommand: definition.defaultSubcommand
|
|
5841
|
-
})
|
|
6804
|
+
outputMode: context.outputMode,
|
|
6805
|
+
recorded: context.getTelemetryMeta()
|
|
5842
6806
|
});
|
|
5843
6807
|
}
|
|
5844
6808
|
if (context.outputMode === "agent" || context.outputMode === "agent-stream" || context.outputMode === "json") {
|