@moda-ai/cli 1.27.0 → 1.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +193 -3
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/skills/moda-cli/SKILL.md +30 -8
package/dist/cli.js
CHANGED
|
@@ -391,6 +391,7 @@ var FIX_USAGE = [
|
|
|
391
391
|
" moda fix verify <fix_id> [--prompt-file=PATH] [--no-wait] [--timeout=ms]",
|
|
392
392
|
" moda fix checkout <fix_id>",
|
|
393
393
|
" moda fix submit <fix_id> --pr | --local-ref=<branch>",
|
|
394
|
+
" moda fix mark-applied <fix_id> [--note=TEXT]",
|
|
394
395
|
" moda fix dismiss <fix_id> --reason=TEXT"
|
|
395
396
|
].join(`
|
|
396
397
|
`);
|
|
@@ -454,6 +455,8 @@ async function runFixCommand(context) {
|
|
|
454
455
|
return runFixCheckout(context, profileOptions, tenantId);
|
|
455
456
|
case "submit":
|
|
456
457
|
return runFixSubmit(context, profileOptions, tenantId);
|
|
458
|
+
case "mark-applied":
|
|
459
|
+
return runFixMarkApplied(context, profileOptions, tenantId);
|
|
457
460
|
case "dismiss":
|
|
458
461
|
return runFixDismiss(context, profileOptions, tenantId);
|
|
459
462
|
default:
|
|
@@ -645,6 +648,9 @@ async function runFixCheckout(context, profileOptions, tenantId) {
|
|
|
645
648
|
throw new CliInputError("<fix_id> is required", "Usage: moda fix checkout <fix_id>");
|
|
646
649
|
}
|
|
647
650
|
const fix = await getFix(tenantId, fixId, profileOptions);
|
|
651
|
+
if (isArtifactFix(fix)) {
|
|
652
|
+
throw new CliInputError(`Fix MODA-FIX-${fix.shortRef} is a ${fix.fixType} fix — its candidate is applied in your own stack, not checked out. Run \`moda fix ${fixId} --packet\` for the candidate (written as local artifacts), apply it, then confirm with \`moda fix mark-applied ${fixId}\`.`);
|
|
653
|
+
}
|
|
648
654
|
if (!fix.targetSourcePath) {
|
|
649
655
|
throw new CliInputError(`Fix MODA-FIX-${fix.shortRef} has no target source path — ${fix.fixType} fixes are packet-only. Run \`moda fix ${fixId} --packet\` for the diagnosis packet.`);
|
|
650
656
|
}
|
|
@@ -752,6 +758,38 @@ async function runFixSubmit(context, profileOptions, tenantId) {
|
|
|
752
758
|
});
|
|
753
759
|
return EXIT_OK;
|
|
754
760
|
}
|
|
761
|
+
async function runFixMarkApplied(context, profileOptions, tenantId) {
|
|
762
|
+
const usage = "Usage: moda fix mark-applied <fix_id> [--note=TEXT]";
|
|
763
|
+
const fixId = (context.positionals[1] || context.flags["fix-id"] || "").trim();
|
|
764
|
+
if (!fixId) {
|
|
765
|
+
throw new CliInputError("<fix_id> is required", usage);
|
|
766
|
+
}
|
|
767
|
+
const note = (context.flags.note ?? "").trim();
|
|
768
|
+
if (note.length > 600) {
|
|
769
|
+
throw new CliInputError(`--note must be 600 characters or fewer (got ${note.length})`, usage);
|
|
770
|
+
}
|
|
771
|
+
const fix = await callControlAPI(`${fixesBase(tenantId)}/${encodeURIComponent(fixId)}/mark-applied`, { method: "POST", body: JSON.stringify(note ? { note } : {}) }, profileOptions, { timeoutMs: 60000, retries: 0 });
|
|
772
|
+
if (context.outputMode === "human") {
|
|
773
|
+
process.stderr.write(fix.duplicate === true ? `Fix MODA-FIX-${fix.shortRef} was already confirmed (${fix.status}) — duplicate no-op; the mark_fixed feedback was re-posted.
|
|
774
|
+
` : `Fix MODA-FIX-${fix.shortRef} marked applied (${fix.status}) — mark_fixed feedback posted, monitoring for recurrence.
|
|
775
|
+
`);
|
|
776
|
+
}
|
|
777
|
+
context.output.writeData(fix, {
|
|
778
|
+
summary: {
|
|
779
|
+
text: fix.duplicate === true ? `Fix MODA-FIX-${fix.shortRef} was already confirmed (${fix.status}); mark-applied was a duplicate no-op.` : `Fix MODA-FIX-${fix.shortRef} marked applied — status ${fix.status}; the problem enters fixed-monitoring.`,
|
|
780
|
+
confidence: "high"
|
|
781
|
+
},
|
|
782
|
+
nextCommands: [
|
|
783
|
+
{
|
|
784
|
+
command: `moda fix ${fix.id}`,
|
|
785
|
+
purpose: "Track confirmation monitoring (HELD after 14 clean days; REGRESSED on reopen).",
|
|
786
|
+
mutability: "read",
|
|
787
|
+
requires_approval: false
|
|
788
|
+
}
|
|
789
|
+
]
|
|
790
|
+
});
|
|
791
|
+
return EXIT_OK;
|
|
792
|
+
}
|
|
755
793
|
async function runFixDismiss(context, profileOptions, tenantId) {
|
|
756
794
|
const usage = "Usage: moda fix dismiss <fix_id> --reason=TEXT";
|
|
757
795
|
const fixId = (context.positionals[1] || context.flags["fix-id"] || "").trim();
|
|
@@ -778,8 +816,16 @@ async function runFixDismiss(context, profileOptions, tenantId) {
|
|
|
778
816
|
async function runFixDetail(context, profileOptions, tenantId, fixId) {
|
|
779
817
|
if (context.flags.packet === "true") {
|
|
780
818
|
const packet = await callControlAPI(`${fixesBase(tenantId)}/${encodeURIComponent(fixId)}/packet`, { method: "GET" }, profileOptions);
|
|
819
|
+
const extras = materializePacketCandidates(packet, fixId, context.outputMode === "human");
|
|
781
820
|
context.output.writeData(packet, {
|
|
782
|
-
summary: {
|
|
821
|
+
summary: {
|
|
822
|
+
text: `moda.fix_packet.v1 for fix ${fixId}.${extras.summarySuffix}`,
|
|
823
|
+
confidence: "high"
|
|
824
|
+
},
|
|
825
|
+
...extras.findings.length > 0 ? { findings: extras.findings } : {},
|
|
826
|
+
...extras.artifacts.length > 0 ? { artifacts: extras.artifacts } : {},
|
|
827
|
+
...extras.warnings.length > 0 ? { warnings: extras.warnings } : {},
|
|
828
|
+
...extras.nextCommands.length > 0 ? { nextCommands: extras.nextCommands } : {}
|
|
783
829
|
});
|
|
784
830
|
return EXIT_OK;
|
|
785
831
|
}
|
|
@@ -841,6 +887,9 @@ async function driveAdvance(args) {
|
|
|
841
887
|
announce(fix);
|
|
842
888
|
if (!ADVANCING_STATUSES.has(fix.status))
|
|
843
889
|
break;
|
|
890
|
+
if (fix.status === "PROPOSED" && /gate (pending|unavailable)/i.test(fix.statusReason ?? "")) {
|
|
891
|
+
break;
|
|
892
|
+
}
|
|
844
893
|
await sleep(fix.status === "GATING" ? args.pollIntervalMs : Math.min(STEP_DELAY_MS, args.pollIntervalMs));
|
|
845
894
|
}
|
|
846
895
|
return fix;
|
|
@@ -871,6 +920,112 @@ async function fetchPacketFailSoft(tenantId, fixId, profileOptions) {
|
|
|
871
920
|
return null;
|
|
872
921
|
}
|
|
873
922
|
}
|
|
923
|
+
function isArtifactFix(fix) {
|
|
924
|
+
return fix.fixType === "TOOL_SCHEMA" || fix.fixType === "SKILL";
|
|
925
|
+
}
|
|
926
|
+
function materializePacketCandidates(packet, fixId, human) {
|
|
927
|
+
const findings = [];
|
|
928
|
+
const artifacts = [];
|
|
929
|
+
const warnings = [];
|
|
930
|
+
const nextCommands = [];
|
|
931
|
+
const summaryParts = [];
|
|
932
|
+
const tool = packet.candidate_tool;
|
|
933
|
+
const skill = packet.candidate_skill;
|
|
934
|
+
const hasTool = typeof tool?.proposed_description === "string" && tool.proposed_description !== "";
|
|
935
|
+
const hasSkill = typeof skill?.skill_md === "string" && skill.skill_md !== "";
|
|
936
|
+
if (!hasTool && !hasSkill) {
|
|
937
|
+
return { findings, artifacts, warnings, nextCommands, summarySuffix: "" };
|
|
938
|
+
}
|
|
939
|
+
const rawRef = typeof packet.short_ref === "string" && packet.short_ref.trim() ? packet.short_ref : fixId;
|
|
940
|
+
const ref = rawRef.replace(/[^A-Za-z0-9._-]/g, "").replace(/^\.+/, "").slice(0, 64) || "fix";
|
|
941
|
+
const w = (line) => process.stderr.write(`${line}
|
|
942
|
+
`);
|
|
943
|
+
const block = (label, text) => {
|
|
944
|
+
w(` ${label}:`);
|
|
945
|
+
for (const line of (text || "(none)").split(`
|
|
946
|
+
`))
|
|
947
|
+
w(` ${line}`);
|
|
948
|
+
};
|
|
949
|
+
const writeCandidateFile = (name, content, kind, description) => {
|
|
950
|
+
const relPath = safeTargetSourcePath(`.moda/fixes/${ref}/${name}`);
|
|
951
|
+
const absPath = join(process.cwd(), relPath);
|
|
952
|
+
try {
|
|
953
|
+
assertRealpathContained(absPath, relPath);
|
|
954
|
+
writeContainedFile(absPath, relPath, content);
|
|
955
|
+
artifacts.push({ kind, path: relPath, description });
|
|
956
|
+
return relPath;
|
|
957
|
+
} catch (error) {
|
|
958
|
+
warnings.push(`Could not write ${relPath}: ${error instanceof Error ? error.message : String(error)} — the full candidate text is still in the packet payload.`);
|
|
959
|
+
return null;
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
if (hasTool && tool) {
|
|
963
|
+
const toolName = typeof tool.tool_name === "string" && tool.tool_name ? tool.tool_name : "unknown-tool";
|
|
964
|
+
const source = typeof tool.current_description_source === "string" ? tool.current_description_source : "";
|
|
965
|
+
const paramNotes = (Array.isArray(tool.param_notes) ? tool.param_notes : []).filter((note) => typeof note === "string" && note !== "");
|
|
966
|
+
const rationale = typeof tool.rationale === "string" ? tool.rationale : "";
|
|
967
|
+
const currentPath = writeCandidateFile("tool-description.current.md", typeof tool.current_description === "string" ? tool.current_description : "", "fix_tool_description_current", `Current description of tool '${toolName}'${source ? ` (from ${source})` : ""} — the diff base.`);
|
|
968
|
+
const proposedPath = writeCandidateFile("tool-description.proposed.md", tool.proposed_description ?? "", "fix_tool_description_proposed", `Proposed description for tool '${toolName}' — apply it to your tool definition, then \`moda fix mark-applied ${fixId}\`.`);
|
|
969
|
+
findings.push({
|
|
970
|
+
id: "fix:candidate_tool",
|
|
971
|
+
title: `tool '${toolName}' description candidate${source ? ` (current from ${source})` : ""}`,
|
|
972
|
+
status: "proposed",
|
|
973
|
+
severity: "info",
|
|
974
|
+
...rationale || paramNotes.length > 0 ? {
|
|
975
|
+
description: [rationale, ...paramNotes.map((note) => `param note: ${note}`)].filter(Boolean).join(`
|
|
976
|
+
`)
|
|
977
|
+
} : {}
|
|
978
|
+
});
|
|
979
|
+
summaryParts.push(`Includes a tool-description candidate for '${toolName}'.`);
|
|
980
|
+
if (human) {
|
|
981
|
+
w("");
|
|
982
|
+
w(`Tool-description candidate — '${toolName}'${source ? ` (current description from ${source})` : ""}`);
|
|
983
|
+
block("current ", tool.current_description ?? "");
|
|
984
|
+
block("proposed", tool.proposed_description ?? "");
|
|
985
|
+
if (paramNotes.length > 0)
|
|
986
|
+
block("param notes", paramNotes.join(`
|
|
987
|
+
`));
|
|
988
|
+
if (rationale)
|
|
989
|
+
block("rationale", rationale);
|
|
990
|
+
if (currentPath && proposedPath) {
|
|
991
|
+
w(` diff: diff ${currentPath} ${proposedPath}`);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
if (hasSkill && skill) {
|
|
996
|
+
const skillName = typeof skill.skill_name === "string" && skill.skill_name ? skill.skill_name : "moda-fix-skill";
|
|
997
|
+
const skillPath = writeCandidateFile("SKILL.md", skill.skill_md ?? "", "fix_candidate_skill", `Drafted SKILL.md for '${skillName}' — install it (for example .claude/skills/${skillName}/SKILL.md), then \`moda fix mark-applied ${fixId}\`.`);
|
|
998
|
+
findings.push({
|
|
999
|
+
id: "fix:candidate_skill",
|
|
1000
|
+
title: `skill '${skillName}' drafted (SKILL.md, ${(skill.skill_md ?? "").length} chars)`,
|
|
1001
|
+
status: "proposed",
|
|
1002
|
+
severity: "info",
|
|
1003
|
+
description: `Install the skill in your agent, then confirm with \`moda fix mark-applied ${fixId}\`. Gating on the replay holdout stays pending until the skill is registered with the skill harness.`
|
|
1004
|
+
});
|
|
1005
|
+
summaryParts.push(`Includes a drafted SKILL.md candidate ('${skillName}').`);
|
|
1006
|
+
if (human) {
|
|
1007
|
+
w("");
|
|
1008
|
+
w(`Skill candidate — '${skillName}'`);
|
|
1009
|
+
if (skillPath)
|
|
1010
|
+
w(` SKILL.md written to ${skillPath}`);
|
|
1011
|
+
w(` install it (for example .claude/skills/${skillName}/SKILL.md), then confirm:`);
|
|
1012
|
+
w(` moda fix mark-applied ${fixId} --note="installed skill ${skillName}"`);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
nextCommands.push({
|
|
1016
|
+
command: `moda fix mark-applied ${fixId} --note="where/how you applied it"`,
|
|
1017
|
+
purpose: "Confirm you applied the candidate in your own stack (no PR) — flips the fix to SHIPPED and starts recurrence monitoring.",
|
|
1018
|
+
mutability: "write",
|
|
1019
|
+
requires_approval: true
|
|
1020
|
+
});
|
|
1021
|
+
return {
|
|
1022
|
+
findings,
|
|
1023
|
+
artifacts,
|
|
1024
|
+
warnings,
|
|
1025
|
+
nextCommands,
|
|
1026
|
+
summarySuffix: summaryParts.length > 0 ? ` ${summaryParts.join(" ")}` : ""
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
874
1029
|
function gateFindings(fix) {
|
|
875
1030
|
const gateResult = fix.gateResult ?? null;
|
|
876
1031
|
if (!gateResult)
|
|
@@ -918,8 +1073,24 @@ function passLabel(value) {
|
|
|
918
1073
|
return value ? "pass" : "fail";
|
|
919
1074
|
}
|
|
920
1075
|
function nextCommandsFor(fix) {
|
|
1076
|
+
const packetCommand = {
|
|
1077
|
+
command: `moda fix ${fix.id} --packet`,
|
|
1078
|
+
purpose: "Read the packet — the tool/skill candidate is written as local artifacts to apply in your own stack.",
|
|
1079
|
+
mutability: "write",
|
|
1080
|
+
requires_approval: false
|
|
1081
|
+
};
|
|
1082
|
+
const markAppliedCommand = {
|
|
1083
|
+
command: `moda fix mark-applied ${fix.id} --note="where/how you applied it"`,
|
|
1084
|
+
purpose: "Confirm you applied the candidate in your own stack (no PR) — flips the fix to SHIPPED and starts recurrence monitoring.",
|
|
1085
|
+
mutability: "write",
|
|
1086
|
+
requires_approval: true
|
|
1087
|
+
};
|
|
1088
|
+
const skillGatePending = fix.fixType === "SKILL" && fix.candidateRef?.kind === "skill" && !fix.candidateRef?.skillId;
|
|
921
1089
|
switch (fix.status) {
|
|
922
1090
|
case "VERIFIED":
|
|
1091
|
+
if (isArtifactFix(fix)) {
|
|
1092
|
+
return [packetCommand, markAppliedCommand];
|
|
1093
|
+
}
|
|
923
1094
|
return [
|
|
924
1095
|
{
|
|
925
1096
|
command: `moda fix submit ${fix.id} --pr`,
|
|
@@ -935,6 +1106,9 @@ function nextCommandsFor(fix) {
|
|
|
935
1106
|
}
|
|
936
1107
|
];
|
|
937
1108
|
case "PROPOSED":
|
|
1109
|
+
if (skillGatePending) {
|
|
1110
|
+
return [packetCommand, markAppliedCommand];
|
|
1111
|
+
}
|
|
938
1112
|
return [
|
|
939
1113
|
{
|
|
940
1114
|
command: `moda fix verify ${fix.id}`,
|
|
@@ -944,6 +1118,17 @@ function nextCommandsFor(fix) {
|
|
|
944
1118
|
}
|
|
945
1119
|
];
|
|
946
1120
|
case "GATE_FAILED":
|
|
1121
|
+
if (isArtifactFix(fix)) {
|
|
1122
|
+
return [
|
|
1123
|
+
{
|
|
1124
|
+
command: `moda fix verify ${fix.id}`,
|
|
1125
|
+
purpose: "Re-gate the stored candidate on the frozen holdout.",
|
|
1126
|
+
mutability: "write",
|
|
1127
|
+
requires_approval: false
|
|
1128
|
+
},
|
|
1129
|
+
packetCommand
|
|
1130
|
+
];
|
|
1131
|
+
}
|
|
947
1132
|
return [
|
|
948
1133
|
{
|
|
949
1134
|
command: `moda fix checkout ${fix.id}`,
|
|
@@ -4692,7 +4877,8 @@ Commands:
|
|
|
4692
4877
|
|
|
4693
4878
|
Fixes (a fix with proof):
|
|
4694
4879
|
fixes Ranked Fix queue (--status=S --limit=N --cursor=TOKEN)
|
|
4695
|
-
fix <fix_id> One Fix: status + gate result; --packet prints moda.fix_packet.v1
|
|
4880
|
+
fix <fix_id> One Fix: status + gate result; --packet prints moda.fix_packet.v1
|
|
4881
|
+
(tool/skill candidates are also written under .moda/fixes/<ref>/);
|
|
4696
4882
|
--wait drives a still-running pipeline to rest
|
|
4697
4883
|
fix start <problem_id> Draft a Fix from a Problem (--type=auto|prompt); --wait drives
|
|
4698
4884
|
scope -> propose -> gate and prints the packet
|
|
@@ -4700,6 +4886,8 @@ Commands:
|
|
|
4700
4886
|
(exit 0 pass / 1 fail with per-case findings / 3 degraded-inconclusive)
|
|
4701
4887
|
fix checkout <fix_id> Write the candidate at its target source path (never runs git)
|
|
4702
4888
|
fix submit <fix_id> Deliver: --pr opens a draft PR | --local-ref=<branch> records your branch
|
|
4889
|
+
fix mark-applied <fix_id> Confirm a candidate you applied in your own stack (--note=TEXT);
|
|
4890
|
+
no PR needed — flips to SHIPPED and starts monitoring
|
|
4703
4891
|
fix dismiss <fix_id> Reject with --reason=TEXT (feeds problem feedback)
|
|
4704
4892
|
|
|
4705
4893
|
Options:
|
|
@@ -4900,7 +5088,8 @@ Examples:
|
|
|
4900
5088
|
moda skills pull
|
|
4901
5089
|
moda fixes
|
|
4902
5090
|
moda fix start <problem_id> --wait
|
|
4903
|
-
moda fix verify <fix_id> --prompt-file=prompts/agent.prompt.md
|
|
5091
|
+
moda fix verify <fix_id> --prompt-file=prompts/agent.prompt.md
|
|
5092
|
+
moda fix mark-applied <fix_id> --note="tool description updated in our agent config"`);
|
|
4904
5093
|
}
|
|
4905
5094
|
function printCommandHelp(definition) {
|
|
4906
5095
|
const lines = [];
|
|
@@ -6192,6 +6381,7 @@ var commandRegistry = createCommandRegistry([
|
|
|
6192
6381
|
"moda fix verify <fix_id> --prompt-file=prompts/agent.prompt.md",
|
|
6193
6382
|
"moda fix checkout <fix_id>",
|
|
6194
6383
|
"moda fix submit <fix_id> --pr",
|
|
6384
|
+
'moda fix mark-applied <fix_id> --note="tool description updated in our agent config"',
|
|
6195
6385
|
'moda fix dismiss <fix_id> --reason="not actionable"'
|
|
6196
6386
|
],
|
|
6197
6387
|
auth: "none",
|
package/package.json
CHANGED
package/skills/moda-cli/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: moda-cli
|
|
3
|
-
version: 2.
|
|
3
|
+
version: 2.4.0
|
|
4
4
|
description: Query Moda's AI agent conversation analytics and manage code-first prompt versions from the terminal — semantic/keyword/hybrid message search, overview KPIs, topic clusters, message context, user frustration detections, tool failures, and moda prompts status/sync/promote. Use when the user asks about moda, modaflows, conversation analytics, prompt management, user frustrations, agent observability, tool failure debugging, wants to find conversations or tool calls about a topic, or wants to investigate how their AI agent is performing.
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -621,23 +621,35 @@ replay **gate** that proves it on held-out production evidence. Use this loop
|
|
|
621
621
|
whenever you are asked to fix a detected Problem, or before hand-editing
|
|
622
622
|
prompt/skill files in a repo that talks to Moda.
|
|
623
623
|
|
|
624
|
+
Fixes are typed by where the evidence points: `PROMPT` (a managed prompt
|
|
625
|
+
revision — the checkout/verify/submit loop below), `TOOL_SCHEMA` (a proposed
|
|
626
|
+
rewrite of one tool's description), and `SKILL` (a drafted SKILL.md for an
|
|
627
|
+
agent-behavior problem). Tool and skill candidates are not repo files: read
|
|
628
|
+
them from the packet (they are also written locally under
|
|
629
|
+
`.moda/fixes/<SHORTREF>/`), apply them in your own stack, then confirm with
|
|
630
|
+
`moda fix mark-applied` — no GitHub integration required.
|
|
631
|
+
|
|
624
632
|
The fail-to-pass loop:
|
|
625
633
|
|
|
626
634
|
```bash
|
|
627
635
|
moda fixes # ranked queue of fixes with proof
|
|
628
636
|
moda fix <fix_id> --packet # moda.fix_packet.v1: cause, target
|
|
629
637
|
# file, evidence links, verify cmd,
|
|
630
|
-
# handback + magic word
|
|
638
|
+
# handback + magic word; tool/skill
|
|
639
|
+
# candidates land in .moda/fixes/
|
|
631
640
|
moda fix start <problem_id> --wait # draft from a Problem and drive
|
|
632
641
|
# scope -> propose -> gate
|
|
633
|
-
moda fix checkout <fix_id> # write the candidate
|
|
634
|
-
#
|
|
642
|
+
moda fix checkout <fix_id> # PROMPT fixes: write the candidate
|
|
643
|
+
# at its target source path
|
|
644
|
+
# (NEVER runs git)
|
|
635
645
|
# ...edit the target file...
|
|
636
646
|
moda fix verify <fix_id> --prompt-file=<path> # re-gate local content on the
|
|
637
|
-
# frozen holdout
|
|
647
|
+
# frozen holdout (PROMPT only)
|
|
638
648
|
# iterate edit -> verify until exit 0, then hand back:
|
|
639
649
|
moda fix submit <fix_id> --pr # draft PR via the Moda GitHub App
|
|
640
650
|
moda fix submit <fix_id> --local-ref=<branch> # or record your own branch
|
|
651
|
+
moda fix mark-applied <fix_id> --note="…" # tool/skill/no-PR path: confirm a
|
|
652
|
+
# candidate you applied yourself
|
|
641
653
|
moda fix dismiss <fix_id> --reason="why" # reject (feeds problem feedback)
|
|
642
654
|
```
|
|
643
655
|
|
|
@@ -660,6 +672,15 @@ Rules that keep the proof honest:
|
|
|
660
672
|
- Fix PRs end with the magic word `Fixes MODA-FIX-<SHORTREF>` in the body —
|
|
661
673
|
Moda-authored PRs carry it automatically; hand-authored PRs must include it
|
|
662
674
|
so the merge confirms the fix and starts production monitoring.
|
|
675
|
+
- `mark-applied` is the confirmation for changes shipped without a PR (tool
|
|
676
|
+
descriptions, installed skills, hand-applied prompts): it flips the fix to
|
|
677
|
+
`SHIPPED`, records your `--note`, and starts the same monitoring the merge
|
|
678
|
+
webhook would. It is idempotent — repeating it is a duplicate no-op.
|
|
679
|
+
- `TOOL_SCHEMA` fixes gate like prompt fixes (both arms pin the same current
|
|
680
|
+
prompt; the proposed description is the only delta). `SKILL` fixes rest at
|
|
681
|
+
`PROPOSED` until the skill is registered with the Moda skill harness — an
|
|
682
|
+
inline drafted SKILL.md cannot enter the gate, so deliver it via the packet
|
|
683
|
+
and `mark-applied`.
|
|
663
684
|
- The pipeline is advance-on-poll: `--wait` (on `start`, `verify`, or
|
|
664
685
|
`moda fix <fix_id> --wait`) drives it; a fix left `GATING` will not finish
|
|
665
686
|
on its own.
|
|
@@ -842,11 +863,12 @@ npx: `npx -p @moda-ai/cli moda <command>`.
|
|
|
842
863
|
| `moda prompts sync` | Upload changed prompt versions and update lockfile |
|
|
843
864
|
| `moda prompts promote <key>` | Move a remote prompt label |
|
|
844
865
|
| `moda fixes` | Ranked queue of Fixes ("a fix with proof"); `--status=S`, `--limit=N`, `--cursor=TOKEN` |
|
|
845
|
-
| `moda fix <fix_id>` | One Fix: status + gate result; `--packet` prints `moda.fix_packet.v1
|
|
866
|
+
| `moda fix <fix_id>` | One Fix: status + gate result; `--packet` prints `moda.fix_packet.v1` and writes tool/skill candidates under `.moda/fixes/<ref>/`; `--wait` drives a running pipeline to rest |
|
|
846
867
|
| `moda fix start <problem_id>` | Draft a Fix from a Problem (`--type=auto\|prompt`); `--wait` drives scope → propose → gate |
|
|
847
|
-
| `moda fix verify <fix_id>` | Re-gate on the frozen holdout; `--prompt-file=P` gates local content (exit `0` pass / `1` fail with per-case findings / `3` degraded — never a pass) |
|
|
848
|
-
| `moda fix checkout <fix_id>` | Write the candidate at its target source path (never runs git) |
|
|
868
|
+
| `moda fix verify <fix_id>` | Re-gate on the frozen holdout; `--prompt-file=P` gates local content, PROMPT fixes only (exit `0` pass / `1` fail with per-case findings / `3` degraded — never a pass) |
|
|
869
|
+
| `moda fix checkout <fix_id>` | Write the candidate at its target source path (never runs git; PROMPT fixes only) |
|
|
849
870
|
| `moda fix submit <fix_id>` | Deliver: `--pr` opens a draft PR \| `--local-ref=<branch>` records your branch |
|
|
871
|
+
| `moda fix mark-applied <fix_id>` | Confirm a candidate you applied in your own stack (`--note=TEXT`, ≤600 chars); no PR needed — flips to SHIPPED and starts monitoring |
|
|
850
872
|
| `moda fix dismiss <fix_id>` | Reject with `--reason=TEXT` (feeds problem feedback) |
|
|
851
873
|
|
|
852
874
|
`moda search` flags: `--mode=keyword|semantic|hybrid` (default `hybrid`),
|