@mutmutco/cli 4.1.13 → 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 +224 -25
- 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
|
{
|
|
@@ -20665,13 +20694,13 @@ var CONTENT_LABELS_AND_ASSIGNEES = `
|
|
|
20665
20694
|
labels(first: ${ITEM_LABELS_PAGE}) { pageInfo { hasNextPage } nodes { name } }
|
|
20666
20695
|
assignees(first: ${ITEM_ASSIGNEES_PAGE}) { pageInfo { hasNextPage } nodes { login } }`;
|
|
20667
20696
|
var PROJECT_ITEMS_QUERY = `
|
|
20668
|
-
query($owner: String!, $number: Int!, $after: String) {
|
|
20697
|
+
query($owner: String!, $number: Int!, $after: String, $query: String) {
|
|
20669
20698
|
viewer { login }
|
|
20670
20699
|
organization(login: $owner) {
|
|
20671
20700
|
projectV2(number: $number) {
|
|
20672
20701
|
id
|
|
20673
20702
|
title
|
|
20674
|
-
items(first: 100, after: $after) {
|
|
20703
|
+
items(first: 100, after: $after, query: $query) {
|
|
20675
20704
|
pageInfo { hasNextPage endCursor }
|
|
20676
20705
|
nodes {
|
|
20677
20706
|
id
|
|
@@ -20711,6 +20740,23 @@ query($owner: String!, $number: Int!, $after: String) {
|
|
|
20711
20740
|
}
|
|
20712
20741
|
}
|
|
20713
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
|
+
}
|
|
20714
20760
|
var SINGLE_ITEM_CONTENT_FRAGMENT = `
|
|
20715
20761
|
id
|
|
20716
20762
|
number
|
|
@@ -20914,13 +20960,14 @@ function renderBoardReport(report) {
|
|
|
20914
20960
|
}
|
|
20915
20961
|
function shouldSkipInactiveBoardNode(node, cfg) {
|
|
20916
20962
|
const content = node.content;
|
|
20917
|
-
if (content && "state" in content && content.state === "CLOSED") return true;
|
|
20963
|
+
if (content && "state" in content && (content.state === "CLOSED" || content.state === "MERGED")) return true;
|
|
20918
20964
|
const status = parseSingleSelectFields(node.fieldValues?.nodes, cfg).status?.name;
|
|
20919
20965
|
return status === "Done";
|
|
20920
20966
|
}
|
|
20921
20967
|
async function collectBoardItems(cfg, options, deps) {
|
|
20922
20968
|
const client = deps.client ?? defaultGitHubClient();
|
|
20923
20969
|
const currentRepo = await resolveCurrentRepo(options, deps);
|
|
20970
|
+
const itemsQuery = boardItemsSearchFilter(options);
|
|
20924
20971
|
const warnings = [];
|
|
20925
20972
|
const nodes = [];
|
|
20926
20973
|
let viewer = "";
|
|
@@ -20931,7 +20978,7 @@ async function collectBoardItems(cfg, options, deps) {
|
|
|
20931
20978
|
let pageCount = 0;
|
|
20932
20979
|
do {
|
|
20933
20980
|
try {
|
|
20934
|
-
const page = await fetchProjectPage(client, cfg, after);
|
|
20981
|
+
const page = await fetchProjectPage(client, cfg, after, itemsQuery);
|
|
20935
20982
|
viewer ||= page.viewer.login;
|
|
20936
20983
|
const project2 = page.organization?.projectV2;
|
|
20937
20984
|
if (!project2) throw new Error(`project ${cfg.projectOwner}#${cfg.projectNumber} not found`);
|
|
@@ -21159,7 +21206,7 @@ async function removeRepoBoardItems(options, deps = {}) {
|
|
|
21159
21206
|
const cfg = resolveBoardConfig(options.config);
|
|
21160
21207
|
const client = deps.client ?? defaultGitHubClient();
|
|
21161
21208
|
const repo = options.repo;
|
|
21162
|
-
const collected = await collectBoardItems(cfg, { repo,
|
|
21209
|
+
const collected = await collectBoardItems(cfg, { repo, purpose: { kind: "retire", repo } }, deps);
|
|
21163
21210
|
const board = { owner: cfg.projectOwner, number: cfg.projectNumber, id: collected.projectId };
|
|
21164
21211
|
const matches = collected.items.filter((it) => it.repository.toLowerCase() === repo.toLowerCase());
|
|
21165
21212
|
const removed = [];
|
|
@@ -21181,7 +21228,7 @@ async function removeRepoBoardItems(options, deps = {}) {
|
|
|
21181
21228
|
async function archiveClosedDoneBoardItems(options, deps = {}) {
|
|
21182
21229
|
const cfg = resolveBoardConfig(options.config);
|
|
21183
21230
|
const client = deps.client ?? defaultGitHubClient();
|
|
21184
|
-
const collected = await collectBoardItems(cfg, { repo: options.repo,
|
|
21231
|
+
const collected = await collectBoardItems(cfg, { repo: options.repo, purpose: { kind: "archive" } }, deps);
|
|
21185
21232
|
const candidates = collected.items.filter((item) => item.status === "Done" && item.state === "CLOSED");
|
|
21186
21233
|
const result = {
|
|
21187
21234
|
board: { owner: cfg.projectOwner, number: cfg.projectNumber, id: collected.projectId },
|
|
@@ -21552,11 +21599,12 @@ async function setBoardPriorityForSelector(options, deps = {}) {
|
|
|
21552
21599
|
}
|
|
21553
21600
|
return { ref: `${options.selector.repo}#${options.selector.number}`, itemId, priority };
|
|
21554
21601
|
}
|
|
21555
|
-
async function fetchProjectPage(client, cfg, after) {
|
|
21602
|
+
async function fetchProjectPage(client, cfg, after, itemsQuery) {
|
|
21556
21603
|
return client.graphql(PROJECT_ITEMS_QUERY, {
|
|
21557
21604
|
owner: cfg.projectOwner,
|
|
21558
21605
|
number: cfg.projectNumber,
|
|
21559
|
-
...after ? { after } : {}
|
|
21606
|
+
...after ? { after } : {},
|
|
21607
|
+
...itemsQuery ? { query: itemsQuery } : {}
|
|
21560
21608
|
});
|
|
21561
21609
|
}
|
|
21562
21610
|
function isGraphQLNotFound(e) {
|
|
@@ -22039,6 +22087,9 @@ function openPullsFetcher(client) {
|
|
|
22039
22087
|
return pulls;
|
|
22040
22088
|
};
|
|
22041
22089
|
}
|
|
22090
|
+
async function readClaimLiveness(client, repo, number) {
|
|
22091
|
+
return gatherClaimLiveness(client, repo, number, openPullsFetcher(client));
|
|
22092
|
+
}
|
|
22042
22093
|
async function gatherClaimLiveness(client, repo, number, fetchOpenPulls) {
|
|
22043
22094
|
const out = { failed: [] };
|
|
22044
22095
|
try {
|
|
@@ -22134,7 +22185,7 @@ var DOCTOR_CONCURRENCY = 4;
|
|
|
22134
22185
|
async function boardDoctor(options, deps = {}) {
|
|
22135
22186
|
const cfg = resolveBoardConfig(options.config);
|
|
22136
22187
|
const client = deps.client ?? defaultGitHubClient();
|
|
22137
|
-
const collected = await collectBoardItems(cfg, { repo: options.repo,
|
|
22188
|
+
const collected = await collectBoardItems(cfg, { repo: options.repo, purpose: { kind: "doctor" } }, deps);
|
|
22138
22189
|
const concurrency = Math.max(1, options.concurrency ?? DOCTOR_CONCURRENCY);
|
|
22139
22190
|
const findings = [];
|
|
22140
22191
|
const result = { scanned: collected.items.length, findings: 0, fixed: 0, failed: 0, items: [], liveClaims: [] };
|
|
@@ -22429,6 +22480,7 @@ var PRIMARY_GROUPS = [
|
|
|
22429
22480
|
["Coordinate and improve", ["wave", "report", "skill-lesson", "closure-rate"]]
|
|
22430
22481
|
];
|
|
22431
22482
|
var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set(["org", "runtime", "plugin"]);
|
|
22483
|
+
var CAMPAIGN_ORCHESTRATOR_TOP_LEVEL = /* @__PURE__ */ new Set(["pickup"]);
|
|
22432
22484
|
var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "repo-index", "find", "tests", "spawn", "dist", "wave", "report", "skill-lesson", "closure-rate"]);
|
|
22433
22485
|
var TOP_LEVEL_ORDER = /* @__PURE__ */ new Map();
|
|
22434
22486
|
var HELP_GROUP_ORDER = /* @__PURE__ */ new Map();
|
|
@@ -22439,6 +22491,7 @@ for (const [helpGroup, names] of PRIMARY_GROUPS) {
|
|
|
22439
22491
|
}
|
|
22440
22492
|
HELP_GROUP_ORDER.set("Operations", HELP_GROUP_ORDER.size);
|
|
22441
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++);
|
|
22442
22495
|
var PATH_OVERRIDES = {
|
|
22443
22496
|
// #4184: the estate-search door reads as Orient, while rebuild/publish/gc/sync-estate stay
|
|
22444
22497
|
// Setup and support — that split is the point.
|
|
@@ -22487,6 +22540,7 @@ var COMMAND_OWNERSHIP = {
|
|
|
22487
22540
|
report: { module_owner: "cli/src/report.ts", consumer: "campaign-orchestrator" },
|
|
22488
22541
|
"skill-lesson": { module_owner: "cli/src/skill-lesson.ts", consumer: "campaign-orchestrator" },
|
|
22489
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" },
|
|
22490
22544
|
org: { module_owner: "cli/src/command-consolidation.ts", consumer: "org-operator" },
|
|
22491
22545
|
runtime: { module_owner: "cli/src/command-consolidation.ts", consumer: "runtime-operator" },
|
|
22492
22546
|
plugin: { module_owner: "cli/src/plugin-guard-io.ts", consumer: "host-runtime" }
|
|
@@ -22519,6 +22573,14 @@ function primaryMetadata(name) {
|
|
|
22519
22573
|
function topLevelMetadata(name) {
|
|
22520
22574
|
const primary = primaryMetadata(name);
|
|
22521
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
|
+
}
|
|
22522
22584
|
if (!OPERATIONAL_TOP_LEVEL.has(name)) {
|
|
22523
22585
|
throw new Error(`command taxonomy: unclassified top-level command "${name}"`);
|
|
22524
22586
|
}
|
|
@@ -36158,9 +36220,12 @@ function catalogCommandPaths(manifest) {
|
|
|
36158
36220
|
return acc;
|
|
36159
36221
|
}
|
|
36160
36222
|
function nearestExplainTargets(query, paths, limit = 5) {
|
|
36161
|
-
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]);
|
|
36162
36228
|
const leafOf = (p) => p.slice(p.lastIndexOf(" ") + 1);
|
|
36163
|
-
const leaf = leafOf(query);
|
|
36164
36229
|
const exactLeaf = all.filter((p) => p !== query && leafOf(p) === leaf).sort();
|
|
36165
36230
|
if (exactLeaf.length) return exactLeaf.slice(0, limit);
|
|
36166
36231
|
const out = [];
|
|
@@ -36286,13 +36351,15 @@ function registerExplainCommand(program3) {
|
|
|
36286
36351
|
const commandPath3 = commandArgs.join(" ");
|
|
36287
36352
|
const command = findCommandInManifest(manifest, commandPath3);
|
|
36288
36353
|
if (!command) {
|
|
36354
|
+
const parentPath = commandArgs.length > 1 ? commandArgs.slice(0, -1).join(" ") : void 0;
|
|
36355
|
+
const guide = unknownCommandDomainGuide(parentPath, commandArgs.at(-1));
|
|
36289
36356
|
const nearest = nearestExplainTargets(commandPath3, catalogCommandPaths(manifest));
|
|
36290
36357
|
fail(`explain: unknown command "${commandPath3}"`, {
|
|
36291
36358
|
code: ERROR_CODES.ERR_NOT_FOUND,
|
|
36292
36359
|
...nearest.length ? {
|
|
36293
36360
|
expected: nearest,
|
|
36294
|
-
did_you_mean: nearest[0],
|
|
36295
|
-
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`
|
|
36296
36363
|
} : {}
|
|
36297
36364
|
});
|
|
36298
36365
|
return;
|
|
@@ -37033,6 +37100,130 @@ function registerSessionReport(program3) {
|
|
|
37033
37100
|
});
|
|
37034
37101
|
}
|
|
37035
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
|
+
|
|
37036
37227
|
// ../infra/src/fleet-health.ts
|
|
37037
37228
|
function uniqueRepos(repos) {
|
|
37038
37229
|
const unique = /* @__PURE__ */ new Map();
|
|
@@ -40781,7 +40972,14 @@ function unknownCommandCandidates(parent, allPaths) {
|
|
|
40781
40972
|
function resolveParseHint() {
|
|
40782
40973
|
if (lastParseErrorKind === "unknown-command") {
|
|
40783
40974
|
const parent = resolveCommandFromArgv(program2, process.argv.slice(2));
|
|
40784
|
-
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
|
+
);
|
|
40785
40983
|
const path3 = lastUnknownCommand ? suggestCommandPath(lastUnknownCommand, candidates) : void 0;
|
|
40786
40984
|
const canonical = path3 ? canonicalPathFor(path3) ?? path3 : void 0;
|
|
40787
40985
|
if (canonical) return `(did you mean \`mmi-cli ${canonical}\`? ${DISCOVERY_HINT})`;
|
|
@@ -42123,7 +42321,7 @@ function resolveCreateSurface(opts) {
|
|
|
42123
42321
|
function surfaceWaived() {
|
|
42124
42322
|
return rawFlag("--no-surface");
|
|
42125
42323
|
}
|
|
42126
|
-
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`");
|
|
42127
42325
|
withExamples(mutating(
|
|
42128
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"),
|
|
42129
42327
|
// --dry-run/--validate-only plan: resolve the same title source and validate the same type, priority,
|
|
@@ -42284,7 +42482,7 @@ async function readParentField(number, repo) {
|
|
|
42284
42482
|
}
|
|
42285
42483
|
return resolveParentField(payload);
|
|
42286
42484
|
}
|
|
42287
|
-
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) => {
|
|
42288
42486
|
const n = Number(number);
|
|
42289
42487
|
if (!Number.isInteger(n) || n <= 0) return fail("issue view: <number> must be a positive integer");
|
|
42290
42488
|
const repo = await resolveRepo(o.repo);
|
|
@@ -43203,6 +43401,7 @@ registerExplainCommand(program2);
|
|
|
43203
43401
|
registerPrLifecycleCommands(program2);
|
|
43204
43402
|
registerSessionReport(program2);
|
|
43205
43403
|
registerLearningClosureRateCommand(program2);
|
|
43404
|
+
registerLearningPickupCommand(program2);
|
|
43206
43405
|
registerBoardCommands(program2);
|
|
43207
43406
|
registerStageCommands(program2);
|
|
43208
43407
|
var GH_TRAIN_TIMEOUT_MS = 3e4;
|
package/package.json
CHANGED