@mutmutco/cli 4.1.12 → 4.1.14
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/main.cjs +276 -28
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -803,6 +803,8 @@ var init_house_map = __esm({
|
|
|
803
803
|
"skill-lesson": "learning",
|
|
804
804
|
"closure-rate": "learning",
|
|
805
805
|
// closure rate per loop kind, computed at read (#4440)
|
|
806
|
+
pickup: "learning",
|
|
807
|
+
// cloud-agent adoption gate (#5708)
|
|
806
808
|
// --- core — the front door itself ---------------------------------------------------------------
|
|
807
809
|
commands: "core",
|
|
808
810
|
whoami: "core",
|
|
@@ -5978,7 +5980,7 @@ init_clean_exit();
|
|
|
5978
5980
|
init_error_codes();
|
|
5979
5981
|
|
|
5980
5982
|
// src/parse-error-hints.ts
|
|
5981
|
-
var TARGET_SELECTOR_FLAGS = /* @__PURE__ */ new Set(["--repo", "--ref", "--target", "--project", "--issue"]);
|
|
5983
|
+
var TARGET_SELECTOR_FLAGS = /* @__PURE__ */ new Set(["--repo", "--ref", "--target", "--project", "--issue", "--number"]);
|
|
5982
5984
|
function targetFlagValueFromArgv(flag, argv) {
|
|
5983
5985
|
const i = argv.indexOf(flag);
|
|
5984
5986
|
if (i < 0) return void 0;
|
|
@@ -6000,6 +6002,33 @@ function unknownTargetFlagMessage(flag, positional) {
|
|
|
6000
6002
|
return `unknown option '${flag}' \u2014 this command takes its target as a positional: ${positional}`;
|
|
6001
6003
|
}
|
|
6002
6004
|
|
|
6005
|
+
// src/unknown-command-domain-hints.ts
|
|
6006
|
+
var GUIDES = /* @__PURE__ */ new Map([
|
|
6007
|
+
[
|
|
6008
|
+
"oracle issue:search",
|
|
6009
|
+
{
|
|
6010
|
+
hint: "no `oracle issue search` \u2014 friction reports dedup server-side via `mmi-cli learning report`; new issues accept `--idempotency-key` on `oracle issue create`; bounded label/state filters use `oracle issue list`",
|
|
6011
|
+
expected: ["learning report", "oracle issue create", "oracle issue list"],
|
|
6012
|
+
did_you_mean: "learning report"
|
|
6013
|
+
}
|
|
6014
|
+
]
|
|
6015
|
+
]);
|
|
6016
|
+
var BLOCKED_SUGGESTIONS = /* @__PURE__ */ new Map([
|
|
6017
|
+
["oracle issue:search", ["oracle repo-index search"]]
|
|
6018
|
+
]);
|
|
6019
|
+
function unknownCommandDomainGuide(parentPath, token) {
|
|
6020
|
+
if (!parentPath || !token) return void 0;
|
|
6021
|
+
return GUIDES.get(`${parentPath}:${token}`);
|
|
6022
|
+
}
|
|
6023
|
+
function blockedUnknownCommandSuggestions(parentPath, token) {
|
|
6024
|
+
if (!parentPath || !token) return [];
|
|
6025
|
+
return BLOCKED_SUGGESTIONS.get(`${parentPath}:${token}`) ?? [];
|
|
6026
|
+
}
|
|
6027
|
+
function filterUnknownCommandCandidates(parentPath, token, candidates) {
|
|
6028
|
+
const blocked = new Set(blockedUnknownCommandSuggestions(parentPath, token));
|
|
6029
|
+
return blocked.size ? candidates.filter((p) => !blocked.has(p)) : [...candidates];
|
|
6030
|
+
}
|
|
6031
|
+
|
|
6003
6032
|
// src/session-start.ts
|
|
6004
6033
|
var import_node_fs8 = require("node:fs");
|
|
6005
6034
|
var import_node_path6 = require("node:path");
|
|
@@ -6874,7 +6903,7 @@ var COMMAND_LADDER_WRITES = [
|
|
|
6874
6903
|
];
|
|
6875
6904
|
var COMMAND_LADDER_READS = [
|
|
6876
6905
|
// #5483: `show` aliases `view` for board-verb callers; advertise the canonical read plus the alias.
|
|
6877
|
-
{ gh: "gh issue view <N>", replacement: "mmi-cli oracle issue view <N> (
|
|
6906
|
+
{ gh: "gh issue view <N>", replacement: "mmi-cli oracle issue view <N> (aliases: show, get)" },
|
|
6878
6907
|
{ gh: "gh pr view <N>", replacement: "mmi-cli devops pr view <N>" },
|
|
6879
6908
|
{ gh: "gh pr checks <N>", replacement: "mmi-cli devops pr checks-wait <N>" }
|
|
6880
6909
|
];
|
|
@@ -13152,10 +13181,10 @@ var rollout_plan_default = {
|
|
|
13152
13181
|
note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
|
|
13153
13182
|
},
|
|
13154
13183
|
baseline: {
|
|
13155
|
-
version: "4.1.
|
|
13156
|
-
tag: "v4.1.
|
|
13157
|
-
commit: "
|
|
13158
|
-
npm: "@mutmutco/cli@4.1.
|
|
13184
|
+
version: "4.1.14",
|
|
13185
|
+
tag: "v4.1.14",
|
|
13186
|
+
commit: "e91bb87d9ce0",
|
|
13187
|
+
npm: "@mutmutco/cli@4.1.14"
|
|
13159
13188
|
},
|
|
13160
13189
|
exitCriterion: "fleet-n-of-n",
|
|
13161
13190
|
hubOnlyShortcut: "forbidden",
|
|
@@ -13172,14 +13201,14 @@ var rollout_plan_default = {
|
|
|
13172
13201
|
repo: "mutmutco/mmi-hub",
|
|
13173
13202
|
role: "canary",
|
|
13174
13203
|
schedule: "train",
|
|
13175
|
-
v3Target: "v4.1.
|
|
13204
|
+
v3Target: "v4.1.14"
|
|
13176
13205
|
}
|
|
13177
13206
|
],
|
|
13178
13207
|
rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
|
|
13179
13208
|
rollback: {
|
|
13180
13209
|
independent: true,
|
|
13181
|
-
mechanism: "npm dist-tag latest -> 4.1.
|
|
13182
|
-
v3Target: "v4.1.
|
|
13210
|
+
mechanism: "npm dist-tag latest -> 4.1.14 and redeploy the Hub Lambda from tag v4.1.14 (e91bb87d9ce0); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
13211
|
+
v3Target: "v4.1.14 (@mutmutco/cli@4.1.14, tag commit e91bb87d9ce0 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
13183
13212
|
}
|
|
13184
13213
|
},
|
|
13185
13214
|
{
|
|
@@ -15938,9 +15967,13 @@ async function withCheckoutRestoredAfterRelease(deps, result, startBranch) {
|
|
|
15938
15967
|
return { ...result, checkout, ...localSync ? { localSync } : {} };
|
|
15939
15968
|
}
|
|
15940
15969
|
var HUB_REPO3 = "mutmutco/MMI-Hub";
|
|
15970
|
+
var JERV_HUB_REPO = "mutmutco/Jerv-Hub";
|
|
15941
15971
|
function isHubControlRepo(repo) {
|
|
15942
15972
|
return repo.toLowerCase() === HUB_REPO3.toLowerCase();
|
|
15943
15973
|
}
|
|
15974
|
+
function isJervHubRepo(repo) {
|
|
15975
|
+
return repo.toLowerCase() === JERV_HUB_REPO.toLowerCase();
|
|
15976
|
+
}
|
|
15944
15977
|
function supportsReleaseAbort(_repo, meta, _branchHints) {
|
|
15945
15978
|
return DEPLOY_MODELS.includes(meta.deployModel);
|
|
15946
15979
|
}
|
|
@@ -16653,6 +16686,42 @@ function appendPublishDispatch(deploy, publish) {
|
|
|
16653
16686
|
deployStatus: deploy.deployStatus === "failure" || publish.deployStatus === "failure" ? "failure" : deploy.deployStatus === "pending" || publish.deployStatus === "pending" ? "pending" : "success"
|
|
16654
16687
|
};
|
|
16655
16688
|
}
|
|
16689
|
+
async function appendJervGatewayReleaseDeploy(deps, repo, tag, tagSha, dispatch) {
|
|
16690
|
+
if (!isJervHubRepo(repo)) return dispatch;
|
|
16691
|
+
if (dispatch.deployStatus !== "success") {
|
|
16692
|
+
return {
|
|
16693
|
+
...dispatch,
|
|
16694
|
+
note: `${dispatch.note}; Jerv Gateway deploy deferred until package publication is proven green`
|
|
16695
|
+
};
|
|
16696
|
+
}
|
|
16697
|
+
try {
|
|
16698
|
+
const output = await deps.run("bash", ["scripts/jerv-gateway-release-deploy.sh", "--tag", tag]);
|
|
16699
|
+
const receipt = output.trim().split(/\r?\n/).reverse().map((line) => {
|
|
16700
|
+
try {
|
|
16701
|
+
return JSON.parse(line);
|
|
16702
|
+
} catch {
|
|
16703
|
+
return null;
|
|
16704
|
+
}
|
|
16705
|
+
}).find((value) => value?.schema === "jerv-gateway/release-deploy-v1");
|
|
16706
|
+
if (!receipt || receipt.tag !== tag || receipt.commit !== tagSha || receipt.status !== "healthy") {
|
|
16707
|
+
throw new Error("helper receipt did not match the released tag and commit");
|
|
16708
|
+
}
|
|
16709
|
+
return {
|
|
16710
|
+
...dispatch,
|
|
16711
|
+
note: `${dispatch.note}; Jerv Gateway deployed ${tagSha.slice(0, 12)} and passed health checks`,
|
|
16712
|
+
workflowRuns: [...dispatch.workflowRuns ?? [], { workflow: "jerv-gateway", conclusion: "success" }],
|
|
16713
|
+
deployStatus: "success"
|
|
16714
|
+
};
|
|
16715
|
+
} catch (error) {
|
|
16716
|
+
const detail = (error instanceof Error ? error.message : String(error)).split(/\r?\n/, 1)[0].slice(0, 240);
|
|
16717
|
+
return {
|
|
16718
|
+
...dispatch,
|
|
16719
|
+
note: `${dispatch.note}; Jerv Gateway deploy FAILED (${detail})`,
|
|
16720
|
+
workflowRuns: [...dispatch.workflowRuns ?? [], { workflow: "jerv-gateway", conclusion: "failure" }],
|
|
16721
|
+
deployStatus: "failure"
|
|
16722
|
+
};
|
|
16723
|
+
}
|
|
16724
|
+
}
|
|
16656
16725
|
async function watchOwnWorkflowRuns(deps, repo, targets, since, headSha, enumerateSha = false) {
|
|
16657
16726
|
const workflowRuns = [];
|
|
16658
16727
|
for (const target of targets) {
|
|
@@ -17366,6 +17435,7 @@ async function completeMainRelease(deps, ctx, meta, deployModel, watch, options,
|
|
|
17366
17435
|
const deployDispatch = await dispatchDeploy(deps, ctx, "main", "main", deployModel, watch, autoRunSince, releaseSha, "report", meta.publishDir);
|
|
17367
17436
|
const publishDispatch = deployDispatch.deployStatus === "success" ? await dispatchPublishIfRequired(deps, ctx, meta, deployModel, "main", tag, watch, "report") : null;
|
|
17368
17437
|
let dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
|
|
17438
|
+
dispatch = await appendJervGatewayReleaseDeploy(deps, ctx.repo, tag, releaseSha, dispatch);
|
|
17369
17439
|
if (!publishDispatch && deployDispatch.deployStatus !== "success" && meta.publishRequired && (deployModel === "tenant-container" || deployModel === "solo-container")) {
|
|
17370
17440
|
const reason = deployDispatch.deployStatus === "failure" ? "box deploy failed \u2014 redeploy the box before publishing" : "box deploy not confirmed (run with --watch) \u2014 publish after the box deploy lands";
|
|
17371
17441
|
dispatch = {
|
|
@@ -17504,9 +17574,14 @@ async function runReleaseResume(deps, options = {}) {
|
|
|
17504
17574
|
const rcAlignment2 = !directTrack && branchHints.hasRcBranch ? await alignRcForward(deps, ctx, tag) : void 0;
|
|
17505
17575
|
if (rcAlignment2) steps2.push(`rc alignment: ${rcAlignment2.status}`);
|
|
17506
17576
|
const historicalDeploy = await deriveHistoricalDeployStatus(deps, ctx, deployModel, tagSha);
|
|
17577
|
+
const dispatch2 = await appendJervGatewayReleaseDeploy(deps, ctx.repo, tag, tagSha, {
|
|
17578
|
+
note: `historical ${deployModel} deploy/publish status: ${historicalDeploy}`,
|
|
17579
|
+
deployStatus: historicalDeploy
|
|
17580
|
+
});
|
|
17581
|
+
if (isJervHubRepo(ctx.repo)) steps2.push(dispatch2.note);
|
|
17507
17582
|
const state2 = resumeStateOf(deriveTrainFollowUpStatus({
|
|
17508
17583
|
projectInfo: "ok",
|
|
17509
|
-
deploy: deployFollowUpOutcome(
|
|
17584
|
+
deploy: deployFollowUpOutcome(dispatch2.deployStatus),
|
|
17510
17585
|
rcRetirement: "ok",
|
|
17511
17586
|
alignment: alignmentFollowUpOutcome(devRollForward2, rcAlignment2),
|
|
17512
17587
|
foldPort: "ok"
|
|
@@ -17519,6 +17594,7 @@ async function runReleaseResume(deps, options = {}) {
|
|
|
17519
17594
|
steps: steps2,
|
|
17520
17595
|
devRollForward: devRollForward2,
|
|
17521
17596
|
rcAlignment: rcAlignment2,
|
|
17597
|
+
dispatch: dispatch2,
|
|
17522
17598
|
note: state2 === "failed" ? `release ${tag} was already published, but its deploy/publish on the release SHA FAILED \u2014 re-run the failed job (do NOT re-tag); the protected alignment PRs were reused` : state2 === "pending" ? `release ${tag} was already published; reused its protected alignment PRs, but a deploy/publish or alignment follow-up is unresolved \u2014 watch it to completion` : `release ${tag} was already published; alignment is complete and the deploy on the release SHA is proven green`
|
|
17523
17599
|
};
|
|
17524
17600
|
}
|
|
@@ -17551,7 +17627,8 @@ async function runReleaseResume(deps, options = {}) {
|
|
|
17551
17627
|
const autoRunSince = (deps.now ?? Date.now)();
|
|
17552
17628
|
const deployDispatch = await dispatchDeploy(deps, ctx, "main", "main", deployModel, watch, autoRunSince, tagSha, "report", meta.publishDir);
|
|
17553
17629
|
const publishDispatch = deployDispatch.deployStatus === "success" ? await dispatchPublishIfRequired(deps, ctx, meta, deployModel, "main", tag, watch, "report") : null;
|
|
17554
|
-
|
|
17630
|
+
let dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
|
|
17631
|
+
dispatch = await appendJervGatewayReleaseDeploy(deps, ctx.repo, tag, tagSha, dispatch);
|
|
17555
17632
|
steps.push(`dispatched the ${deployModel} deploy path`);
|
|
17556
17633
|
const devRollForward = await rollDevelopmentForward(deps, ctx, tag);
|
|
17557
17634
|
steps.push(`development roll-forward: ${devRollForward.status}`);
|
|
@@ -20617,13 +20694,13 @@ var CONTENT_LABELS_AND_ASSIGNEES = `
|
|
|
20617
20694
|
labels(first: ${ITEM_LABELS_PAGE}) { pageInfo { hasNextPage } nodes { name } }
|
|
20618
20695
|
assignees(first: ${ITEM_ASSIGNEES_PAGE}) { pageInfo { hasNextPage } nodes { login } }`;
|
|
20619
20696
|
var PROJECT_ITEMS_QUERY = `
|
|
20620
|
-
query($owner: String!, $number: Int!, $after: String) {
|
|
20697
|
+
query($owner: String!, $number: Int!, $after: String, $query: String) {
|
|
20621
20698
|
viewer { login }
|
|
20622
20699
|
organization(login: $owner) {
|
|
20623
20700
|
projectV2(number: $number) {
|
|
20624
20701
|
id
|
|
20625
20702
|
title
|
|
20626
|
-
items(first: 100, after: $after) {
|
|
20703
|
+
items(first: 100, after: $after, query: $query) {
|
|
20627
20704
|
pageInfo { hasNextPage endCursor }
|
|
20628
20705
|
nodes {
|
|
20629
20706
|
id
|
|
@@ -20663,6 +20740,23 @@ query($owner: String!, $number: Int!, $after: String) {
|
|
|
20663
20740
|
}
|
|
20664
20741
|
}
|
|
20665
20742
|
}`;
|
|
20743
|
+
var ACTIVE_BOARD_ITEMS_FILTER = "-status:Done -is:closed";
|
|
20744
|
+
var DOCTOR_BOARD_ITEMS_FILTER = "-status:Done";
|
|
20745
|
+
var ARCHIVE_BOARD_ITEMS_FILTER = "status:Done is:closed";
|
|
20746
|
+
var retireBoardItemsFilter = (repo) => `repo:"${repo}"`;
|
|
20747
|
+
function boardItemsSearchFilter(options) {
|
|
20748
|
+
if (options.activeOnly) return ACTIVE_BOARD_ITEMS_FILTER;
|
|
20749
|
+
switch (options.purpose?.kind) {
|
|
20750
|
+
case "doctor":
|
|
20751
|
+
return DOCTOR_BOARD_ITEMS_FILTER;
|
|
20752
|
+
case "archive":
|
|
20753
|
+
return ARCHIVE_BOARD_ITEMS_FILTER;
|
|
20754
|
+
case "retire":
|
|
20755
|
+
return retireBoardItemsFilter(options.purpose.repo);
|
|
20756
|
+
default:
|
|
20757
|
+
return void 0;
|
|
20758
|
+
}
|
|
20759
|
+
}
|
|
20666
20760
|
var SINGLE_ITEM_CONTENT_FRAGMENT = `
|
|
20667
20761
|
id
|
|
20668
20762
|
number
|
|
@@ -20866,13 +20960,14 @@ function renderBoardReport(report) {
|
|
|
20866
20960
|
}
|
|
20867
20961
|
function shouldSkipInactiveBoardNode(node, cfg) {
|
|
20868
20962
|
const content = node.content;
|
|
20869
|
-
if (content && "state" in content && content.state === "CLOSED") return true;
|
|
20963
|
+
if (content && "state" in content && (content.state === "CLOSED" || content.state === "MERGED")) return true;
|
|
20870
20964
|
const status = parseSingleSelectFields(node.fieldValues?.nodes, cfg).status?.name;
|
|
20871
20965
|
return status === "Done";
|
|
20872
20966
|
}
|
|
20873
20967
|
async function collectBoardItems(cfg, options, deps) {
|
|
20874
20968
|
const client = deps.client ?? defaultGitHubClient();
|
|
20875
20969
|
const currentRepo = await resolveCurrentRepo(options, deps);
|
|
20970
|
+
const itemsQuery = boardItemsSearchFilter(options);
|
|
20876
20971
|
const warnings = [];
|
|
20877
20972
|
const nodes = [];
|
|
20878
20973
|
let viewer = "";
|
|
@@ -20883,7 +20978,7 @@ async function collectBoardItems(cfg, options, deps) {
|
|
|
20883
20978
|
let pageCount = 0;
|
|
20884
20979
|
do {
|
|
20885
20980
|
try {
|
|
20886
|
-
const page = await fetchProjectPage(client, cfg, after);
|
|
20981
|
+
const page = await fetchProjectPage(client, cfg, after, itemsQuery);
|
|
20887
20982
|
viewer ||= page.viewer.login;
|
|
20888
20983
|
const project2 = page.organization?.projectV2;
|
|
20889
20984
|
if (!project2) throw new Error(`project ${cfg.projectOwner}#${cfg.projectNumber} not found`);
|
|
@@ -21111,7 +21206,7 @@ async function removeRepoBoardItems(options, deps = {}) {
|
|
|
21111
21206
|
const cfg = resolveBoardConfig(options.config);
|
|
21112
21207
|
const client = deps.client ?? defaultGitHubClient();
|
|
21113
21208
|
const repo = options.repo;
|
|
21114
|
-
const collected = await collectBoardItems(cfg, { repo,
|
|
21209
|
+
const collected = await collectBoardItems(cfg, { repo, purpose: { kind: "retire", repo } }, deps);
|
|
21115
21210
|
const board = { owner: cfg.projectOwner, number: cfg.projectNumber, id: collected.projectId };
|
|
21116
21211
|
const matches = collected.items.filter((it) => it.repository.toLowerCase() === repo.toLowerCase());
|
|
21117
21212
|
const removed = [];
|
|
@@ -21133,7 +21228,7 @@ async function removeRepoBoardItems(options, deps = {}) {
|
|
|
21133
21228
|
async function archiveClosedDoneBoardItems(options, deps = {}) {
|
|
21134
21229
|
const cfg = resolveBoardConfig(options.config);
|
|
21135
21230
|
const client = deps.client ?? defaultGitHubClient();
|
|
21136
|
-
const collected = await collectBoardItems(cfg, { repo: options.repo,
|
|
21231
|
+
const collected = await collectBoardItems(cfg, { repo: options.repo, purpose: { kind: "archive" } }, deps);
|
|
21137
21232
|
const candidates = collected.items.filter((item) => item.status === "Done" && item.state === "CLOSED");
|
|
21138
21233
|
const result = {
|
|
21139
21234
|
board: { owner: cfg.projectOwner, number: cfg.projectNumber, id: collected.projectId },
|
|
@@ -21504,11 +21599,12 @@ async function setBoardPriorityForSelector(options, deps = {}) {
|
|
|
21504
21599
|
}
|
|
21505
21600
|
return { ref: `${options.selector.repo}#${options.selector.number}`, itemId, priority };
|
|
21506
21601
|
}
|
|
21507
|
-
async function fetchProjectPage(client, cfg, after) {
|
|
21602
|
+
async function fetchProjectPage(client, cfg, after, itemsQuery) {
|
|
21508
21603
|
return client.graphql(PROJECT_ITEMS_QUERY, {
|
|
21509
21604
|
owner: cfg.projectOwner,
|
|
21510
21605
|
number: cfg.projectNumber,
|
|
21511
|
-
...after ? { after } : {}
|
|
21606
|
+
...after ? { after } : {},
|
|
21607
|
+
...itemsQuery ? { query: itemsQuery } : {}
|
|
21512
21608
|
});
|
|
21513
21609
|
}
|
|
21514
21610
|
function isGraphQLNotFound(e) {
|
|
@@ -21991,6 +22087,9 @@ function openPullsFetcher(client) {
|
|
|
21991
22087
|
return pulls;
|
|
21992
22088
|
};
|
|
21993
22089
|
}
|
|
22090
|
+
async function readClaimLiveness(client, repo, number) {
|
|
22091
|
+
return gatherClaimLiveness(client, repo, number, openPullsFetcher(client));
|
|
22092
|
+
}
|
|
21994
22093
|
async function gatherClaimLiveness(client, repo, number, fetchOpenPulls) {
|
|
21995
22094
|
const out = { failed: [] };
|
|
21996
22095
|
try {
|
|
@@ -22086,7 +22185,7 @@ var DOCTOR_CONCURRENCY = 4;
|
|
|
22086
22185
|
async function boardDoctor(options, deps = {}) {
|
|
22087
22186
|
const cfg = resolveBoardConfig(options.config);
|
|
22088
22187
|
const client = deps.client ?? defaultGitHubClient();
|
|
22089
|
-
const collected = await collectBoardItems(cfg, { repo: options.repo,
|
|
22188
|
+
const collected = await collectBoardItems(cfg, { repo: options.repo, purpose: { kind: "doctor" } }, deps);
|
|
22090
22189
|
const concurrency = Math.max(1, options.concurrency ?? DOCTOR_CONCURRENCY);
|
|
22091
22190
|
const findings = [];
|
|
22092
22191
|
const result = { scanned: collected.items.length, findings: 0, fixed: 0, failed: 0, items: [], liveClaims: [] };
|
|
@@ -22381,6 +22480,7 @@ var PRIMARY_GROUPS = [
|
|
|
22381
22480
|
["Coordinate and improve", ["wave", "report", "skill-lesson", "closure-rate"]]
|
|
22382
22481
|
];
|
|
22383
22482
|
var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set(["org", "runtime", "plugin"]);
|
|
22483
|
+
var CAMPAIGN_ORCHESTRATOR_TOP_LEVEL = /* @__PURE__ */ new Set(["pickup"]);
|
|
22384
22484
|
var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "repo-index", "find", "tests", "spawn", "dist", "wave", "report", "skill-lesson", "closure-rate"]);
|
|
22385
22485
|
var TOP_LEVEL_ORDER = /* @__PURE__ */ new Map();
|
|
22386
22486
|
var HELP_GROUP_ORDER = /* @__PURE__ */ new Map();
|
|
@@ -22391,6 +22491,7 @@ for (const [helpGroup, names] of PRIMARY_GROUPS) {
|
|
|
22391
22491
|
}
|
|
22392
22492
|
HELP_GROUP_ORDER.set("Operations", HELP_GROUP_ORDER.size);
|
|
22393
22493
|
for (const name of OPERATIONAL_TOP_LEVEL) TOP_LEVEL_ORDER.set(name, topLevelPosition++);
|
|
22494
|
+
for (const name of CAMPAIGN_ORCHESTRATOR_TOP_LEVEL) TOP_LEVEL_ORDER.set(name, topLevelPosition++);
|
|
22394
22495
|
var PATH_OVERRIDES = {
|
|
22395
22496
|
// #4184: the estate-search door reads as Orient, while rebuild/publish/gc/sync-estate stay
|
|
22396
22497
|
// Setup and support — that split is the point.
|
|
@@ -22439,6 +22540,7 @@ var COMMAND_OWNERSHIP = {
|
|
|
22439
22540
|
report: { module_owner: "cli/src/report.ts", consumer: "campaign-orchestrator" },
|
|
22440
22541
|
"skill-lesson": { module_owner: "cli/src/skill-lesson.ts", consumer: "campaign-orchestrator" },
|
|
22441
22542
|
"closure-rate": { module_owner: "cli/src/learning-closure-rate.ts", consumer: "campaign-orchestrator" },
|
|
22543
|
+
pickup: { module_owner: "cli/src/learning-pickup.ts", consumer: "campaign-orchestrator" },
|
|
22442
22544
|
org: { module_owner: "cli/src/command-consolidation.ts", consumer: "org-operator" },
|
|
22443
22545
|
runtime: { module_owner: "cli/src/command-consolidation.ts", consumer: "runtime-operator" },
|
|
22444
22546
|
plugin: { module_owner: "cli/src/plugin-guard-io.ts", consumer: "host-runtime" }
|
|
@@ -22471,6 +22573,14 @@ function primaryMetadata(name) {
|
|
|
22471
22573
|
function topLevelMetadata(name) {
|
|
22472
22574
|
const primary = primaryMetadata(name);
|
|
22473
22575
|
if (primary) return primary;
|
|
22576
|
+
if (CAMPAIGN_ORCHESTRATOR_TOP_LEVEL.has(name)) {
|
|
22577
|
+
return {
|
|
22578
|
+
category: "support",
|
|
22579
|
+
discovery: "all-only",
|
|
22580
|
+
help_group: "Coordinate and improve",
|
|
22581
|
+
...commandOwnership(name)
|
|
22582
|
+
};
|
|
22583
|
+
}
|
|
22474
22584
|
if (!OPERATIONAL_TOP_LEVEL.has(name)) {
|
|
22475
22585
|
throw new Error(`command taxonomy: unclassified top-level command "${name}"`);
|
|
22476
22586
|
}
|
|
@@ -36110,9 +36220,12 @@ function catalogCommandPaths(manifest) {
|
|
|
36110
36220
|
return acc;
|
|
36111
36221
|
}
|
|
36112
36222
|
function nearestExplainTargets(query, paths, limit = 5) {
|
|
36113
|
-
const
|
|
36223
|
+
const parentPath = query.includes(" ") ? query.slice(0, query.lastIndexOf(" ")) : void 0;
|
|
36224
|
+
const leaf = query.includes(" ") ? query.slice(query.lastIndexOf(" ") + 1) : query;
|
|
36225
|
+
const guide = unknownCommandDomainGuide(parentPath, leaf);
|
|
36226
|
+
if (guide) return guide.expected.slice(0, limit);
|
|
36227
|
+
const all = filterUnknownCommandCandidates(parentPath, leaf, [...paths]);
|
|
36114
36228
|
const leafOf = (p) => p.slice(p.lastIndexOf(" ") + 1);
|
|
36115
|
-
const leaf = leafOf(query);
|
|
36116
36229
|
const exactLeaf = all.filter((p) => p !== query && leafOf(p) === leaf).sort();
|
|
36117
36230
|
if (exactLeaf.length) return exactLeaf.slice(0, limit);
|
|
36118
36231
|
const out = [];
|
|
@@ -36238,13 +36351,15 @@ function registerExplainCommand(program3) {
|
|
|
36238
36351
|
const commandPath3 = commandArgs.join(" ");
|
|
36239
36352
|
const command = findCommandInManifest(manifest, commandPath3);
|
|
36240
36353
|
if (!command) {
|
|
36354
|
+
const parentPath = commandArgs.length > 1 ? commandArgs.slice(0, -1).join(" ") : void 0;
|
|
36355
|
+
const guide = unknownCommandDomainGuide(parentPath, commandArgs.at(-1));
|
|
36241
36356
|
const nearest = nearestExplainTargets(commandPath3, catalogCommandPaths(manifest));
|
|
36242
36357
|
fail(`explain: unknown command "${commandPath3}"`, {
|
|
36243
36358
|
code: ERROR_CODES.ERR_NOT_FOUND,
|
|
36244
36359
|
...nearest.length ? {
|
|
36245
36360
|
expected: nearest,
|
|
36246
|
-
did_you_mean: nearest[0],
|
|
36247
|
-
corrected_command: `mmi-cli explain ${nearest[0]} --json`
|
|
36361
|
+
did_you_mean: guide?.did_you_mean ?? nearest[0],
|
|
36362
|
+
corrected_command: `mmi-cli explain ${guide?.did_you_mean ?? nearest[0]} --json`
|
|
36248
36363
|
} : {}
|
|
36249
36364
|
});
|
|
36250
36365
|
return;
|
|
@@ -36985,6 +37100,130 @@ function registerSessionReport(program3) {
|
|
|
36985
37100
|
});
|
|
36986
37101
|
}
|
|
36987
37102
|
|
|
37103
|
+
// src/learning-pickup.ts
|
|
37104
|
+
init_cli_shared();
|
|
37105
|
+
init_github_client();
|
|
37106
|
+
var LEARNING_PICKUP_CMD = ["mmi-cli", "learning", "pickup"].join(" ");
|
|
37107
|
+
var LEARNING_CLAIM_MARKER_LIVE_MS = 24 * 60 * 6e4;
|
|
37108
|
+
function isLearningChannelItem(labels) {
|
|
37109
|
+
return labels.includes(LEARNING_LABEL) || loopKindOf(labels) !== void 0;
|
|
37110
|
+
}
|
|
37111
|
+
function claimEvidenceBlocksLearningAdoption(evidence) {
|
|
37112
|
+
if (evidence.failed.includes("comments")) return true;
|
|
37113
|
+
if (!evidence.marker) return false;
|
|
37114
|
+
if (evidence.sessionState === "live") return true;
|
|
37115
|
+
if (evidence.openPr || evidence.branch) return true;
|
|
37116
|
+
if (evidence.failed.length > 0) return true;
|
|
37117
|
+
if (evidence.sessionState !== "dead" && evidence.markerAgeMs !== void 0 && evidence.markerAgeMs < LEARNING_CLAIM_MARKER_LIVE_MS) {
|
|
37118
|
+
return true;
|
|
37119
|
+
}
|
|
37120
|
+
return false;
|
|
37121
|
+
}
|
|
37122
|
+
function evaluateLearningAdoption(input) {
|
|
37123
|
+
if (!isLearningChannelItem(input.labels)) {
|
|
37124
|
+
return { adoptable: false, learning: false, reason: "issue is not in the learning channel" };
|
|
37125
|
+
}
|
|
37126
|
+
if (String(input.state).toUpperCase() === "CLOSED") {
|
|
37127
|
+
return { adoptable: false, learning: true, reason: "issue is closed" };
|
|
37128
|
+
}
|
|
37129
|
+
if (input.status === "In Progress" || input.status === "In Review") {
|
|
37130
|
+
const owners = input.assignees.length ? [...input.assignees] : void 0;
|
|
37131
|
+
const ownerPhrase = owners?.length ? `@${owners.join(", @")}` : "an active board owner";
|
|
37132
|
+
return {
|
|
37133
|
+
adoptable: false,
|
|
37134
|
+
learning: true,
|
|
37135
|
+
reason: `${input.status} \u2014 held by ${ownerPhrase}`,
|
|
37136
|
+
...owners ? { owners } : {}
|
|
37137
|
+
};
|
|
37138
|
+
}
|
|
37139
|
+
if (input.assignees.length > 0) {
|
|
37140
|
+
const owners = [...input.assignees];
|
|
37141
|
+
return {
|
|
37142
|
+
adoptable: false,
|
|
37143
|
+
learning: true,
|
|
37144
|
+
reason: `assigned to @${owners.join(", @")}`,
|
|
37145
|
+
owners
|
|
37146
|
+
};
|
|
37147
|
+
}
|
|
37148
|
+
if (input.claim && claimEvidenceBlocksLearningAdoption(input.claim)) {
|
|
37149
|
+
const claimHolder = input.claim.marker ? describeClaimMarker(input.claim.marker) : void 0;
|
|
37150
|
+
return {
|
|
37151
|
+
adoptable: false,
|
|
37152
|
+
learning: true,
|
|
37153
|
+
reason: claimHolder ? `live claim marker from ${claimHolder}` : "claim marker evidence is unreadable \u2014 deferring adoption",
|
|
37154
|
+
...claimHolder ? { claimHolder } : {},
|
|
37155
|
+
...input.claim.marker ? { claimMarker: input.claim.marker } : {}
|
|
37156
|
+
};
|
|
37157
|
+
}
|
|
37158
|
+
return { adoptable: true, learning: true };
|
|
37159
|
+
}
|
|
37160
|
+
async function probeLearningAdoption(deps, selector) {
|
|
37161
|
+
const cfg = await deps.loadBoardConfig();
|
|
37162
|
+
if (!cfg?.sagaApiUrl) {
|
|
37163
|
+
throw new Error("learning pickup: Hub API URL not configured \u2014 board read required for adoption probe");
|
|
37164
|
+
}
|
|
37165
|
+
const item = await showBoardItem(
|
|
37166
|
+
{ config: cfg, selector: String(selector.number), repo: selector.repo },
|
|
37167
|
+
{ client: deps.client }
|
|
37168
|
+
);
|
|
37169
|
+
const claim = await readClaimLiveness(deps.client, item.repository, item.number);
|
|
37170
|
+
const verdict = evaluateLearningAdoption({
|
|
37171
|
+
labels: item.labels,
|
|
37172
|
+
state: item.state,
|
|
37173
|
+
status: item.status,
|
|
37174
|
+
assignees: item.assignees,
|
|
37175
|
+
claim
|
|
37176
|
+
});
|
|
37177
|
+
return { ...verdict, repo: item.repository, number: item.number, ref: item.ref };
|
|
37178
|
+
}
|
|
37179
|
+
function registerLearningPickupCommand(program3) {
|
|
37180
|
+
withExamples(
|
|
37181
|
+
program3.command("pickup").description(
|
|
37182
|
+
"probe whether a learning-channel issue is free for cloud-agent adoption \u2014 refuses when a live claim marker or assigned In Progress owner already holds it (#5708)"
|
|
37183
|
+
).option("--repo <owner/repo>", "repo holding the issue").option("--number <n>", "issue number").argument("[ref]", "issue ref (#N or owner/repo#N)").option("--json", "print the structured adoption verdict JSON").action(async (ref, o) => {
|
|
37184
|
+
const defaultRepo = await resolveRepo(o.repo);
|
|
37185
|
+
if (!defaultRepo) return fail("learning pickup: could not resolve repo \u2014 pass --repo owner/repo");
|
|
37186
|
+
let selector;
|
|
37187
|
+
try {
|
|
37188
|
+
if (ref) {
|
|
37189
|
+
selector = parseIssueSelector(ref, defaultRepo);
|
|
37190
|
+
} else if (o.number) {
|
|
37191
|
+
selector = { repo: defaultRepo, number: Number.parseInt(o.number, 10) };
|
|
37192
|
+
if (!Number.isFinite(selector.number)) throw new Error("invalid --number");
|
|
37193
|
+
} else {
|
|
37194
|
+
throw new Error("pass an issue ref or --number");
|
|
37195
|
+
}
|
|
37196
|
+
} catch (e) {
|
|
37197
|
+
return fail(`learning pickup: ${e.message}`);
|
|
37198
|
+
}
|
|
37199
|
+
try {
|
|
37200
|
+
const verdict = await probeLearningAdoption(
|
|
37201
|
+
{
|
|
37202
|
+
client: defaultGitHubClient(),
|
|
37203
|
+
loadBoardConfig: async () => loadConfigOrDiscover()
|
|
37204
|
+
},
|
|
37205
|
+
selector
|
|
37206
|
+
);
|
|
37207
|
+
if (o.json) console.log(JSON.stringify(verdict, null, 2));
|
|
37208
|
+
else if (verdict.adoptable) {
|
|
37209
|
+
console.log(`${verdict.ref} is adoptable for learning automation`);
|
|
37210
|
+
} else {
|
|
37211
|
+
const owner = "owners" in verdict && verdict.owners?.length ? ` (owner: @${verdict.owners.join(", @")})` : "claimHolder" in verdict && verdict.claimHolder ? ` (claim: ${verdict.claimHolder})` : "";
|
|
37212
|
+
console.log(`${verdict.ref} is not adoptable \u2014 ${verdict.reason}${owner}`);
|
|
37213
|
+
}
|
|
37214
|
+
if (!verdict.adoptable) process.exitCode = 2;
|
|
37215
|
+
} catch (e) {
|
|
37216
|
+
return fail(`learning pickup: ${e.message}`);
|
|
37217
|
+
}
|
|
37218
|
+
}),
|
|
37219
|
+
[
|
|
37220
|
+
LEARNING_PICKUP_CMD + " mutmutco/Jerv-JervCode#2395 --json",
|
|
37221
|
+
LEARNING_PICKUP_CMD + " --repo mutmutco/MMI-Hub --number 42"
|
|
37222
|
+
],
|
|
37223
|
+
"exit code 2 means defer or refuse \u2014 the learning lane must not implement the issue"
|
|
37224
|
+
);
|
|
37225
|
+
}
|
|
37226
|
+
|
|
36988
37227
|
// ../infra/src/fleet-health.ts
|
|
36989
37228
|
function uniqueRepos(repos) {
|
|
36990
37229
|
const unique = /* @__PURE__ */ new Map();
|
|
@@ -40733,7 +40972,14 @@ function unknownCommandCandidates(parent, allPaths) {
|
|
|
40733
40972
|
function resolveParseHint() {
|
|
40734
40973
|
if (lastParseErrorKind === "unknown-command") {
|
|
40735
40974
|
const parent = resolveCommandFromArgv(program2, process.argv.slice(2));
|
|
40736
|
-
const
|
|
40975
|
+
const parentPath = parent ? canonicalPathFor(commandPath2(parent)) ?? commandPath2(parent) : void 0;
|
|
40976
|
+
const guide = unknownCommandDomainGuide(parentPath, lastUnknownCommand);
|
|
40977
|
+
if (guide) return `(${guide.hint}. ${DISCOVERY_HINT})`;
|
|
40978
|
+
const candidates = filterUnknownCommandCandidates(
|
|
40979
|
+
parentPath,
|
|
40980
|
+
lastUnknownCommand,
|
|
40981
|
+
unknownCommandCandidates(parent, allCommandPaths())
|
|
40982
|
+
);
|
|
40737
40983
|
const path3 = lastUnknownCommand ? suggestCommandPath(lastUnknownCommand, candidates) : void 0;
|
|
40738
40984
|
const canonical = path3 ? canonicalPathFor(path3) ?? path3 : void 0;
|
|
40739
40985
|
if (canonical) return `(did you mean \`mmi-cli ${canonical}\`? ${DISCOVERY_HINT})`;
|
|
@@ -42075,7 +42321,7 @@ function resolveCreateSurface(opts) {
|
|
|
42075
42321
|
function surfaceWaived() {
|
|
42076
42322
|
return rawFlag("--no-surface");
|
|
42077
42323
|
}
|
|
42078
|
-
var issue = program2.command("issue").description("issues \u2014 create and view with structured JSON (view; show
|
|
42324
|
+
var issue = program2.command("issue").description("issues \u2014 create and view with structured JSON (view; show and get alias view for board-verb/gh-shaped callers). Claims are board mutations: use `oracle board claim`, never `oracle issue claim`");
|
|
42079
42325
|
withExamples(mutating(
|
|
42080
42326
|
issue.command("create").description("create an issue (type \u2014 label) and print {number,url,label} JSON").addOption(new Option("--type <type>", "bug | feature | task (sets the matching label; required unless --batch)").choices([...ISSUE_TYPES])).option("--title <title>", "issue title").option("--title-file <path|->", "read the issue title from a UTF-8 file, or from stdin with -").option("--body <body>", "issue body (markdown)").option("--body-file <path|->", "read issue body from a UTF-8 file. `-` (stdin) needs a heredoc, which the agent inline-body guard denies (#1473/#2125) \u2014 prefer a real path; a title with backticks needs --title-file for the same reason (#3381)").option("--priority <priority>", "urgent | high | medium | low (defaults to medium; sets the board Priority field only \u2014 never a priority:* label, #416)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--label <label...>", "extra label(s) to attach (repeatable; auto-created if missing)").option("--surface <surface>", "issue surface, with or without the surface: prefix (#3789). Required when the target repo runs the one-surface-label board rule; any value satisfies it, so this is not a closed enum").option("--no-surface", "file without a surface label on a repo that requires one \u2014 for a genuinely exempt filing (e.g. a coop proof issue that spans every surface)").option("--parent <ref>", "file as a native sub-issue of this parent (#123, owner/repo#123, or URL)").option("--no-related", "skip the auto related-issues comment"),
|
|
42081
42327
|
// --dry-run/--validate-only plan: resolve the same title source and validate the same type, priority,
|
|
@@ -42236,7 +42482,7 @@ async function readParentField(number, repo) {
|
|
|
42236
42482
|
}
|
|
42237
42483
|
return resolveParentField(payload);
|
|
42238
42484
|
}
|
|
42239
|
-
issue.command("view <number>").
|
|
42485
|
+
issue.command("view <number>").aliases(["show", "get"]).description('read an issue as structured JSON \u2014 the mmi-cli path for non-board issue reads (#2347). --comments folds in every comment; --context also adds linkedPrs + children (the one-shot "load the whole item" read, #2894). `show` and `get` alias view (#5483/#5706)').option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json [fields...]", 'gh --json field list (overrides the default field set). Accepts commas, spaces, or repeated --json flags \u2014 in PowerShell an unquoted comma list is an array literal, so QUOTE it: --json "number,title,body,url"').option("--comments", 'include every comment (body + comments in one call) \u2014 the agentic "read the whole item before working it" read (#2894)').option("--context", "full working context in one call: implies --comments and also adds linkedPrs and, for an epic, a children summary (#2894)").action(async (number, o) => {
|
|
42240
42486
|
const n = Number(number);
|
|
42241
42487
|
if (!Number.isInteger(n) || n <= 0) return fail("issue view: <number> must be a positive integer");
|
|
42242
42488
|
const repo = await resolveRepo(o.repo);
|
|
@@ -43155,6 +43401,7 @@ registerExplainCommand(program2);
|
|
|
43155
43401
|
registerPrLifecycleCommands(program2);
|
|
43156
43402
|
registerSessionReport(program2);
|
|
43157
43403
|
registerLearningClosureRateCommand(program2);
|
|
43404
|
+
registerLearningPickupCommand(program2);
|
|
43158
43405
|
registerBoardCommands(program2);
|
|
43159
43406
|
registerStageCommands(program2);
|
|
43160
43407
|
var GH_TRAIN_TIMEOUT_MS = 3e4;
|
|
@@ -43162,10 +43409,11 @@ var GH_RUN_WATCH_TIMEOUT_MS = 20 * 6e4;
|
|
|
43162
43409
|
var NODE_PREPARE_TIMEOUT_MS = 10 * 6e4;
|
|
43163
43410
|
var NODE_VERIFY_TIMEOUT_MS = 5 * 6e4;
|
|
43164
43411
|
var NPM_TRAIN_TIMEOUT_MS = 6e4;
|
|
43412
|
+
var JERV_GATEWAY_DEPLOY_TIMEOUT_MS = 20 * 6e4;
|
|
43165
43413
|
function trainApplyDeps() {
|
|
43166
43414
|
return {
|
|
43167
43415
|
run: async (file, args) => {
|
|
43168
|
-
const timeout = file === "node" && args[1] === "prepare" ? NODE_PREPARE_TIMEOUT_MS : file === "node" && args[1] === "verify" ? NODE_VERIFY_TIMEOUT_MS : file === "npm" ? NPM_TRAIN_TIMEOUT_MS : file !== "gh" ? GIT_TIMEOUT_MS : args[0] === "run" && args[1] === "watch" ? GH_RUN_WATCH_TIMEOUT_MS : GH_TRAIN_TIMEOUT_MS;
|
|
43416
|
+
const timeout = file === "node" && args[1] === "prepare" ? NODE_PREPARE_TIMEOUT_MS : file === "node" && args[1] === "verify" ? NODE_VERIFY_TIMEOUT_MS : file === "npm" ? NPM_TRAIN_TIMEOUT_MS : file === "bash" && args[0] === "scripts/jerv-gateway-release-deploy.sh" ? JERV_GATEWAY_DEPLOY_TIMEOUT_MS : file !== "gh" ? GIT_TIMEOUT_MS : args[0] === "run" && args[1] === "watch" ? GH_RUN_WATCH_TIMEOUT_MS : GH_TRAIN_TIMEOUT_MS;
|
|
43169
43417
|
try {
|
|
43170
43418
|
return isWin2 && file === "npm" ? (await execFileP2("cmd.exe", ["/c", "npm", ...args], { timeout })).stdout : (await execFileP2(file, args, { timeout })).stdout;
|
|
43171
43419
|
} catch (e) {
|
package/package.json
CHANGED