@mutmutco/cli 3.2.0 → 3.3.0
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 +213 -32
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -9219,9 +9219,23 @@ function parseNpmVersion(stdout) {
|
|
|
9219
9219
|
function staleTrainCliMessage(report, commandName) {
|
|
9220
9220
|
return `running mmi-cli ${report.currentVersion} is stale against released ${report.releasedVersion}; this is recoverable \u2014 run \`mmi-cli doctor --apply --no-repo-writes\` to update to ${report.releasedVersion}, then rerun ${commandName} so it uses the current train path`;
|
|
9221
9221
|
}
|
|
9222
|
-
function
|
|
9222
|
+
async function resolveReleasedVersion(runners) {
|
|
9223
|
+
try {
|
|
9224
|
+
const npmVersion = parseNpmVersion(await runners.npm());
|
|
9225
|
+
if (npmVersion) return { version: npmVersion, source: "npm" };
|
|
9226
|
+
} catch {
|
|
9227
|
+
}
|
|
9228
|
+
try {
|
|
9229
|
+
const ghVersion = parseManifestVersion(await runners.gh());
|
|
9230
|
+
if (ghVersion) return { version: ghVersion, source: "gh" };
|
|
9231
|
+
} catch {
|
|
9232
|
+
}
|
|
9233
|
+
return { version: void 0, source: "unavailable" };
|
|
9234
|
+
}
|
|
9235
|
+
function versionAutoUpdateAction(report, hasPluginRoot, releasedSource) {
|
|
9223
9236
|
if (report.ok || report.staleAgainst !== "released") return "none";
|
|
9224
|
-
|
|
9237
|
+
if (hasPluginRoot) return "plugin-pull";
|
|
9238
|
+
return releasedSource === "gh" ? "npm-unreachable" : "npm";
|
|
9225
9239
|
}
|
|
9226
9240
|
|
|
9227
9241
|
// src/issue-related.ts
|
|
@@ -12508,6 +12522,19 @@ async function fetchTrainAuthority(repo, deps) {
|
|
|
12508
12522
|
return { ok: false, error: e.message };
|
|
12509
12523
|
}
|
|
12510
12524
|
}
|
|
12525
|
+
async function mintWikiToken(repo, deps) {
|
|
12526
|
+
const res = await postJson("/wiki-mint", { repo }, deps, "POST", { noRetry: true });
|
|
12527
|
+
if (res.error) return { ok: false, error: res.error };
|
|
12528
|
+
const body = res.body ?? {};
|
|
12529
|
+
if (res.status === 426) return { ok: false, error: upgradeRequiredError({ status: 426 }, res.body) };
|
|
12530
|
+
if (res.status === 403) return { ok: false, error: `master-admin only (HTTP 403)${body.error ? ` \u2014 ${body.error}` : ""}` };
|
|
12531
|
+
if (res.status === 404) {
|
|
12532
|
+
return { ok: false, error: "the Hub API did not recognize /wiki-mint (HTTP 404) \u2014 the deployed Hub predates this command; it answers once a Hub release carrying it is deployed" };
|
|
12533
|
+
}
|
|
12534
|
+
if (!res.ok) return { ok: false, error: `wiki-mint HTTP ${res.status}${body.error ? ` \u2014 ${body.error}` : ""}` };
|
|
12535
|
+
if (!body.token || !body.expiresAt) return { ok: false, error: "malformed wiki-mint response (no token)" };
|
|
12536
|
+
return { ok: true, mint: { token: body.token, expiresAt: body.expiresAt, repository: body.repository ?? repo, permissions: body.permissions ?? {} } };
|
|
12537
|
+
}
|
|
12511
12538
|
async function fetchProjectsList(deps) {
|
|
12512
12539
|
if (!deps.baseUrl) return null;
|
|
12513
12540
|
const token = await deps.token();
|
|
@@ -12645,6 +12672,32 @@ async function tenantDeploy(payload, deps) {
|
|
|
12645
12672
|
return postJson("/tenant-deploy", payload, deps, "POST", { noRetry: true, timeoutMs: TENANT_DEPLOY_TIMEOUT_MS });
|
|
12646
12673
|
}
|
|
12647
12674
|
|
|
12675
|
+
// src/wiki-publish-command.ts
|
|
12676
|
+
async function wikiPublish(deps, opts) {
|
|
12677
|
+
if (!opts.pagesDir) {
|
|
12678
|
+
deps.err("wiki publish: <pages-dir> is required");
|
|
12679
|
+
return false;
|
|
12680
|
+
}
|
|
12681
|
+
const script = deps.scriptPath();
|
|
12682
|
+
if (!deps.scriptExists(script)) {
|
|
12683
|
+
deps.err(`wiki publish: publish script not found at ${script} \u2014 run from the repo checkout root`);
|
|
12684
|
+
return false;
|
|
12685
|
+
}
|
|
12686
|
+
const minted = await deps.mint(opts.repo);
|
|
12687
|
+
if (!minted.ok) {
|
|
12688
|
+
deps.err(`wiki publish failed: could not mint a scoped wiki token for ${opts.repo}: ${minted.error}`);
|
|
12689
|
+
return false;
|
|
12690
|
+
}
|
|
12691
|
+
const env = { ...process.env, WIKI_REPO: opts.repo, WIKI_TOKEN: minted.mint.token };
|
|
12692
|
+
const code = deps.spawn("node", [script, opts.pagesDir], env);
|
|
12693
|
+
if (code !== 0) {
|
|
12694
|
+
deps.err(`wiki publish failed: publish script exited ${code} (token was scoped to ${opts.repo}, contents:write, expires ${minted.mint.expiresAt})`);
|
|
12695
|
+
return false;
|
|
12696
|
+
}
|
|
12697
|
+
deps.log(`wiki published for ${opts.repo} (scoped token, expired/discarded)`);
|
|
12698
|
+
return true;
|
|
12699
|
+
}
|
|
12700
|
+
|
|
12648
12701
|
// src/project-readiness.ts
|
|
12649
12702
|
function stagesForTrack(meta) {
|
|
12650
12703
|
return branchesForTrack(resolveReleaseTrack(meta)).map((b) => b === "development" ? "dev" : b);
|
|
@@ -13788,7 +13841,9 @@ function vaultPointer(slug) {
|
|
|
13788
13841
|
slug,
|
|
13789
13842
|
root,
|
|
13790
13843
|
tiers: {
|
|
13791
|
-
|
|
13844
|
+
// #2482: a stageless catalog entry (stages: []) lives at the slug ROOT — one value per repo —
|
|
13845
|
+
// alongside the staged tree for per-stage entries. Both are the same project-admin wall (#2032).
|
|
13846
|
+
project: `${root}/<KEY> (stageless, one value per repo) + ${root}/{dev,rc,main}/* (staged entries) (project-admin self-serve for this repo)`,
|
|
13792
13847
|
org: [`/mmi-future/{shared,cloudflare,mmi-hub,...}/* (org-infra, master-gated)`]
|
|
13793
13848
|
},
|
|
13794
13849
|
stages: ["dev", "rc", "main"],
|
|
@@ -15293,6 +15348,7 @@ function buildCodexActiveCacheCheck(input) {
|
|
|
15293
15348
|
fix: CODEX_PLUGIN_RECOVERY
|
|
15294
15349
|
};
|
|
15295
15350
|
if (!input.isOrgRepo || !isSemverVersion2(input.releasedVersion)) return base;
|
|
15351
|
+
if (input.codexPresent === false) return base;
|
|
15296
15352
|
if (isSemverVersion2(input.codexActiveVersion)) {
|
|
15297
15353
|
if (compareVersions(input.codexActiveVersion, input.releasedVersion) < 0) {
|
|
15298
15354
|
return {
|
|
@@ -15571,12 +15627,13 @@ function buildPluginUpdateReport(input) {
|
|
|
15571
15627
|
...codexMarketplace ? { codexMarketplace } : {},
|
|
15572
15628
|
...codexActiveCache ? { codexActiveCache } : {},
|
|
15573
15629
|
...opencodePlugin ? { opencodePlugin } : {},
|
|
15574
|
-
...isSemverVersion2(input.releasedVersion) ? { released: input.releasedVersion } : {}
|
|
15630
|
+
...isSemverVersion2(input.releasedVersion) ? { released: input.releasedVersion } : {},
|
|
15631
|
+
...input.releasedVersionSource ? { releasedSource: input.releasedVersionSource } : {}
|
|
15575
15632
|
},
|
|
15576
15633
|
recipes: PLUGIN_UPDATE_RECIPES
|
|
15577
15634
|
};
|
|
15578
15635
|
}
|
|
15579
|
-
function
|
|
15636
|
+
function renderPluginVersionBlock(report) {
|
|
15580
15637
|
const v = report.versions;
|
|
15581
15638
|
const show = (x) => x ?? "unknown";
|
|
15582
15639
|
const versionRows = [
|
|
@@ -15587,16 +15644,10 @@ function renderPluginUpdateReport(report) {
|
|
|
15587
15644
|
["OpenCode plugin", show(v.opencodePlugin)]
|
|
15588
15645
|
];
|
|
15589
15646
|
const pad = Math.max(...versionRows.map(([label]) => label.length));
|
|
15590
|
-
|
|
15647
|
+
return [
|
|
15591
15648
|
`MMI versions (target release: ${show(v.released)})`,
|
|
15592
|
-
...versionRows.map(([label, value]) => ` ${label.padEnd(pad)} ${value}`)
|
|
15593
|
-
"",
|
|
15594
|
-
"Update commands by surface"
|
|
15649
|
+
...versionRows.map(([label, value]) => ` ${label.padEnd(pad)} ${value}`)
|
|
15595
15650
|
];
|
|
15596
|
-
for (const surface of PLUGIN_GUIDE_SURFACES) {
|
|
15597
|
-
lines.push(...renderSurfaceGuide(surface.label, report.recipes[surface.key]));
|
|
15598
|
-
}
|
|
15599
|
-
return lines;
|
|
15600
15651
|
}
|
|
15601
15652
|
function buildDoctorJsonPayload(input) {
|
|
15602
15653
|
return {
|
|
@@ -16230,7 +16281,7 @@ function doctorHumanLines(input) {
|
|
|
16230
16281
|
lines.push("", DOCTOR_VERBOSE_HINT);
|
|
16231
16282
|
}
|
|
16232
16283
|
if (!input.verbose) return lines;
|
|
16233
|
-
lines.push("", ...
|
|
16284
|
+
lines.push("", ...renderPluginVersionBlock(input.updateReport));
|
|
16234
16285
|
lines.push(
|
|
16235
16286
|
"",
|
|
16236
16287
|
doctorSummaryLine({ checks: input.checks, updateReport: input.updateReport, shouldApply: input.shouldApply, healedCount: input.healedCount }),
|
|
@@ -16347,6 +16398,7 @@ function extractTomlServer(content, spec) {
|
|
|
16347
16398
|
}
|
|
16348
16399
|
function detectMcpState(target, spec) {
|
|
16349
16400
|
if (!target.present) return { target, state: "skip", action: "skip", reasons: [] };
|
|
16401
|
+
if (target.format === "claude-cli") return detectClaudeCliMcpState(target, spec);
|
|
16350
16402
|
if (target.content == null) {
|
|
16351
16403
|
if (target.createWhenFileAbsent) return { target, state: "absent", action: "install", reasons: [] };
|
|
16352
16404
|
return { target, state: "skip", action: "skip", reasons: [] };
|
|
@@ -16370,6 +16422,56 @@ function detectMcpState(target, spec) {
|
|
|
16370
16422
|
function planMcpReconcile(targets, spec) {
|
|
16371
16423
|
return targets.map((t) => detectMcpState(t, spec));
|
|
16372
16424
|
}
|
|
16425
|
+
function parseClaudeMcpGetOutput(stdout) {
|
|
16426
|
+
const commandMatch = /^\s*Command:\s*(.+)$/m.exec(stdout);
|
|
16427
|
+
if (commandMatch == null) return null;
|
|
16428
|
+
const argsMatch = /^\s*Args:\s*(.*)$/m.exec(stdout);
|
|
16429
|
+
const env = {};
|
|
16430
|
+
const lines = stdout.replace(/\r\n/g, "\n").split("\n");
|
|
16431
|
+
const headerIdx = lines.findIndex((l) => /^\s*Environment:\s*$/.test(l));
|
|
16432
|
+
if (headerIdx !== -1) {
|
|
16433
|
+
for (const line of lines.slice(headerIdx + 1)) {
|
|
16434
|
+
if (line.trim() === "") continue;
|
|
16435
|
+
if (!/^[ \t]/.test(line)) break;
|
|
16436
|
+
const kv = /^\s*([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(line);
|
|
16437
|
+
if (kv != null) env[kv[1]] = kv[2];
|
|
16438
|
+
}
|
|
16439
|
+
}
|
|
16440
|
+
return { command: commandMatch[1].trim(), args: (argsMatch?.[1] ?? "").trim(), env };
|
|
16441
|
+
}
|
|
16442
|
+
function detectClaudeCliMcpState(target, spec) {
|
|
16443
|
+
if (target.content == null) {
|
|
16444
|
+
return { target, state: "absent", action: "install", reasons: [] };
|
|
16445
|
+
}
|
|
16446
|
+
const matchedName = target.claudeCliMatchedName ?? spec.name;
|
|
16447
|
+
const isLegacy = matchedName !== spec.name;
|
|
16448
|
+
const parsed = parseClaudeMcpGetOutput(target.content);
|
|
16449
|
+
if (parsed == null) {
|
|
16450
|
+
const reasons2 = isLegacy ? ["legacy-name", "unparseable"] : ["unparseable"];
|
|
16451
|
+
return { target, state: "drifted", action: "reconcile", reasons: reasons2, matchedName };
|
|
16452
|
+
}
|
|
16453
|
+
const reasons = [];
|
|
16454
|
+
if (isLegacy) reasons.push("legacy-name");
|
|
16455
|
+
if (textHasPlaywrightVisionCap(parsed.args)) reasons.push("vision-cap");
|
|
16456
|
+
if (!textHasCanonicalPlaywrightOutputDir(parsed.args)) reasons.push("output-dir");
|
|
16457
|
+
if (reasons.length === 0) return { target, state: "healthy", action: "none", reasons: [], matchedName };
|
|
16458
|
+
return { target, state: "drifted", action: "reconcile", reasons, matchedName };
|
|
16459
|
+
}
|
|
16460
|
+
var CLAUDE_MCP_USER_SCOPE = "user";
|
|
16461
|
+
function planClaudeCliMcpHeal(item, spec) {
|
|
16462
|
+
if (item.action !== "install" && item.action !== "reconcile") return [];
|
|
16463
|
+
const commands = [];
|
|
16464
|
+
if (item.action === "reconcile" && item.matchedName != null) {
|
|
16465
|
+
commands.push({ op: "remove", args: ["remove", item.matchedName, "-s", CLAUDE_MCP_USER_SCOPE] });
|
|
16466
|
+
}
|
|
16467
|
+
const parsed = item.target.content != null ? parseClaudeMcpGetOutput(item.target.content) : null;
|
|
16468
|
+
const envFlags = Object.entries(parsed?.env ?? {}).flatMap(([k, v]) => ["-e", `${k}=${v}`]);
|
|
16469
|
+
commands.push({
|
|
16470
|
+
op: "add",
|
|
16471
|
+
args: ["add", spec.name, "-s", CLAUDE_MCP_USER_SCOPE, ...envFlags, "--", spec.command, ...spec.args]
|
|
16472
|
+
});
|
|
16473
|
+
return commands;
|
|
16474
|
+
}
|
|
16373
16475
|
function buildMcpReconcileCheck(plan) {
|
|
16374
16476
|
const base = {
|
|
16375
16477
|
ok: true,
|
|
@@ -16771,20 +16873,22 @@ function readRepoVersion() {
|
|
|
16771
16873
|
}
|
|
16772
16874
|
}
|
|
16773
16875
|
async function fetchReleasedVersion() {
|
|
16774
|
-
|
|
16775
|
-
|
|
16776
|
-
|
|
16777
|
-
}
|
|
16778
|
-
return void 0;
|
|
16779
|
-
}
|
|
16876
|
+
return resolveReleasedVersion({
|
|
16877
|
+
npm: async () => (await runHostBin("npm", npmReleasedVersionArgs(), { timeout: NPM_VIEW_TIMEOUT_MS })).stdout,
|
|
16878
|
+
gh: async () => (await execFileP2("gh", pluginManifestVersionArgs(), { timeout: 5e3 })).stdout
|
|
16879
|
+
});
|
|
16780
16880
|
}
|
|
16781
16881
|
var NPM_UPDATE_TIMEOUT_MS = 12e4;
|
|
16782
16882
|
var NPM_VIEW_TIMEOUT_MS = 15e3;
|
|
16783
16883
|
var PLUGIN_PULL_TIMEOUT_MS = 3e4;
|
|
16784
|
-
async function applyVersionAutoUpdate(report, log) {
|
|
16785
|
-
const action = versionAutoUpdateAction(report, Boolean(process.env.CLAUDE_PLUGIN_ROOT));
|
|
16884
|
+
async function applyVersionAutoUpdate(report, log, releasedSource) {
|
|
16885
|
+
const action = versionAutoUpdateAction(report, Boolean(process.env.CLAUDE_PLUGIN_ROOT), releasedSource);
|
|
16786
16886
|
if (action === "none") return { report, applied: "none" };
|
|
16787
16887
|
const target = report.releasedVersion ?? "latest";
|
|
16888
|
+
if (action === "npm-unreachable") {
|
|
16889
|
+
log(` \u2717 mmi-cli ${report.currentVersion} is stale (released ${target}, resolved via gh) \u2014 npm registry unreachable this pass; when npm is back: npm install -g @mutmutco/cli@latest`);
|
|
16890
|
+
return { report, applied: "none" };
|
|
16891
|
+
}
|
|
16788
16892
|
if (action === "plugin-pull") {
|
|
16789
16893
|
try {
|
|
16790
16894
|
const root = (await execFileP2("git", ["-C", process.env.CLAUDE_PLUGIN_ROOT, "rev-parse", "--show-toplevel"], { timeout: PLUGIN_PULL_TIMEOUT_MS })).stdout.trim();
|
|
@@ -17495,17 +17599,51 @@ function writeMcpConfigFile(path2, content) {
|
|
|
17495
17599
|
return false;
|
|
17496
17600
|
}
|
|
17497
17601
|
}
|
|
17602
|
+
var CLAUDE_CLI_MCP_LABEL = "Claude Code MCP store (user scope)";
|
|
17603
|
+
var CLAUDE_CLI_MCP_PATH = "claude mcp (user scope)";
|
|
17604
|
+
async function claudeCliMcpTarget(spec) {
|
|
17605
|
+
const base = {
|
|
17606
|
+
host: "claude-cli",
|
|
17607
|
+
label: CLAUDE_CLI_MCP_LABEL,
|
|
17608
|
+
path: CLAUDE_CLI_MCP_PATH,
|
|
17609
|
+
format: "claude-cli",
|
|
17610
|
+
isRepoFile: false,
|
|
17611
|
+
createWhenFileAbsent: true
|
|
17612
|
+
};
|
|
17613
|
+
if (!await hostBinAvailable("claude")) return { ...base, present: false, content: null };
|
|
17614
|
+
for (const name of [spec.name, ...spec.legacyNames]) {
|
|
17615
|
+
try {
|
|
17616
|
+
const { stdout } = await runHostBin("claude", ["mcp", "get", name], { timeout: CLAUDE_PLUGIN_TIMEOUT_MS });
|
|
17617
|
+
return { ...base, present: true, content: stdout, claudeCliMatchedName: name };
|
|
17618
|
+
} catch {
|
|
17619
|
+
}
|
|
17620
|
+
}
|
|
17621
|
+
return { ...base, present: true, content: null };
|
|
17622
|
+
}
|
|
17623
|
+
async function runClaudeCliMcpCommands(commands) {
|
|
17624
|
+
for (const cmd of commands) {
|
|
17625
|
+
try {
|
|
17626
|
+
await runHostBin("claude", ["mcp", ...cmd.args], { timeout: CLAUDE_PLUGIN_TIMEOUT_MS });
|
|
17627
|
+
} catch {
|
|
17628
|
+
return false;
|
|
17629
|
+
}
|
|
17630
|
+
}
|
|
17631
|
+
return true;
|
|
17632
|
+
}
|
|
17498
17633
|
async function reconcileMcpRegistrations(input) {
|
|
17499
17634
|
if (!input.isOrgRepo) return buildMcpReconcileCheck([]);
|
|
17500
|
-
const
|
|
17635
|
+
const snapshot = async () => [...mcpConfigTargets(), await claudeCliMcpTarget(PLAYWRIGHT_MCP_SPEC)];
|
|
17636
|
+
const targets = await snapshot();
|
|
17501
17637
|
if (!input.apply) return buildMcpReconcileCheck(planMcpReconcile(targets, PLAYWRIGHT_MCP_SPEC));
|
|
17502
|
-
const
|
|
17638
|
+
const fileTargets = targets.filter((t) => t.format !== "claude-cli");
|
|
17639
|
+
const results = applyMcpReconcile(fileTargets, PLAYWRIGHT_MCP_SPEC, {
|
|
17503
17640
|
repoWritesAllowed: input.repoWritesAllowed,
|
|
17504
17641
|
write: writeMcpConfigFile
|
|
17505
17642
|
});
|
|
17506
17643
|
for (const r of results) {
|
|
17507
17644
|
if (r.outcome === "wrote") {
|
|
17508
17645
|
input.onHealed();
|
|
17646
|
+
input.onReloadRequired();
|
|
17509
17647
|
const verb = r.action === "install" ? "registered" : "reconciled";
|
|
17510
17648
|
input.log(` \u21BB ${verb} Playwright MCP in ${r.target.path} \u2014 restart the host to load it`);
|
|
17511
17649
|
} else if (r.outcome === "unparseable") {
|
|
@@ -17514,7 +17652,22 @@ async function reconcileMcpRegistrations(input) {
|
|
|
17514
17652
|
input.log(` \u26A0 ${r.target.label}: failed to write ${r.target.path}`);
|
|
17515
17653
|
}
|
|
17516
17654
|
}
|
|
17517
|
-
|
|
17655
|
+
const claudeTarget = targets.find((t) => t.format === "claude-cli");
|
|
17656
|
+
if (claudeTarget != null) {
|
|
17657
|
+
const item = detectMcpState(claudeTarget, PLAYWRIGHT_MCP_SPEC);
|
|
17658
|
+
if (item.action === "install" || item.action === "reconcile") {
|
|
17659
|
+
const commands = planClaudeCliMcpHeal(item, PLAYWRIGHT_MCP_SPEC);
|
|
17660
|
+
if (await runClaudeCliMcpCommands(commands)) {
|
|
17661
|
+
input.onHealed();
|
|
17662
|
+
input.onReloadRequired();
|
|
17663
|
+
const verb = item.action === "install" ? "registered" : "reconciled";
|
|
17664
|
+
input.log(` \u21BB ${verb} Playwright MCP via \`claude mcp add\` (${CLAUDE_CLI_MCP_PATH}) \u2014 restart Claude Code to load it`);
|
|
17665
|
+
} else {
|
|
17666
|
+
input.log(` \u26A0 ${CLAUDE_CLI_MCP_LABEL}: \`claude mcp add\` failed for ${PLAYWRIGHT_MCP_SPEC.name} \u2014 run it by hand`);
|
|
17667
|
+
}
|
|
17668
|
+
}
|
|
17669
|
+
}
|
|
17670
|
+
return buildMcpReconcileCheck(planMcpReconcile(await snapshot(), PLAYWRIGHT_MCP_SPEC));
|
|
17518
17671
|
}
|
|
17519
17672
|
function strayBrowserArtifactPaths() {
|
|
17520
17673
|
const cwd = process.cwd();
|
|
@@ -17553,7 +17706,7 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
17553
17706
|
const checks = [];
|
|
17554
17707
|
const REWRITE_KEY = "url.https://github.com/.insteadOf";
|
|
17555
17708
|
const CLONE_FIX = 'run: git config --global url."https://github.com/".insteadOf "git@github.com:"';
|
|
17556
|
-
const [login, pathProbe,
|
|
17709
|
+
const [login, pathProbe, releasedVersionResolution, cfg, callerArn, cloneProbe, isOrgRepo] = await Promise.all([
|
|
17557
17710
|
githubLogin(),
|
|
17558
17711
|
execFileP2(isWin ? "where" : "which", ["mmi-cli"]).then(() => true).catch(() => false),
|
|
17559
17712
|
fetchReleasedVersion(),
|
|
@@ -17565,6 +17718,7 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
17565
17718
|
// not the now-always-defaulted `cfg.sagaApiUrl`. Independent git read — resolved concurrently here.
|
|
17566
17719
|
readOrigin ? isOrgRepoRoot(readOrigin) : isOrgRepoRoot()
|
|
17567
17720
|
]);
|
|
17721
|
+
const releasedVersion = releasedVersionResolution.version;
|
|
17568
17722
|
const surface = detectSurface(process.env);
|
|
17569
17723
|
const versionReportProbe = buildVersionLagReport({
|
|
17570
17724
|
currentVersion: resolveClientVersion(),
|
|
@@ -17630,7 +17784,7 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
17630
17784
|
});
|
|
17631
17785
|
let selfUpdatedCli = false;
|
|
17632
17786
|
if (repairFull && !process.env[DOCTOR_POST_SELF_UPDATE_ENV]) {
|
|
17633
|
-
const updated = await applyVersionAutoUpdate(versionReport, (m) => io.err(m));
|
|
17787
|
+
const updated = await applyVersionAutoUpdate(versionReport, (m) => io.err(m), releasedVersionResolution.source);
|
|
17634
17788
|
versionReport = updated.report;
|
|
17635
17789
|
selfUpdatedCli = updated.applied === "npm";
|
|
17636
17790
|
}
|
|
@@ -17978,15 +18132,16 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
17978
18132
|
};
|
|
17979
18133
|
}
|
|
17980
18134
|
checks.push(cacheCleanupCheck);
|
|
18135
|
+
const canDriveCodex = surfaceToken(surface) === "codex" || await hostBinAvailable("codex");
|
|
17981
18136
|
let codexActiveCacheCheck = buildCodexActiveCacheCheck({
|
|
17982
18137
|
isOrgRepo,
|
|
17983
18138
|
releasedVersion,
|
|
17984
18139
|
codexCacheVersions: codexCacheVersions(),
|
|
17985
18140
|
codexRecordVersion: codexRecordVersion(),
|
|
17986
|
-
codexActiveVersion: await codexActiveVersion()
|
|
18141
|
+
codexActiveVersion: await codexActiveVersion(),
|
|
18142
|
+
codexPresent: canDriveCodex
|
|
17987
18143
|
});
|
|
17988
18144
|
if (!codexActiveCacheCheck.ok && repairFull) {
|
|
17989
|
-
const canDriveCodex = surfaceToken(surface) === "codex" || await hostBinAvailable("codex");
|
|
17990
18145
|
if (canDriveCodex && await applyPluginHeal("codex", surface, (m) => io.err(m), { force: true })) {
|
|
17991
18146
|
markPluginReloadRequired();
|
|
17992
18147
|
markHealed();
|
|
@@ -17996,7 +18151,8 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
17996
18151
|
releasedVersion,
|
|
17997
18152
|
codexCacheVersions: codexCacheVersions(),
|
|
17998
18153
|
codexRecordVersion: codexRecordVersion(),
|
|
17999
|
-
codexActiveVersion: await codexActiveVersion()
|
|
18154
|
+
codexActiveVersion: await codexActiveVersion(),
|
|
18155
|
+
codexPresent: canDriveCodex
|
|
18000
18156
|
});
|
|
18001
18157
|
}
|
|
18002
18158
|
}
|
|
@@ -18130,7 +18286,8 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
18130
18286
|
apply: Boolean(opts.apply),
|
|
18131
18287
|
repoWritesAllowed,
|
|
18132
18288
|
log: (m) => io.err(m),
|
|
18133
|
-
onHealed: markHealed
|
|
18289
|
+
onHealed: markHealed,
|
|
18290
|
+
onReloadRequired: markPluginReloadRequired
|
|
18134
18291
|
})
|
|
18135
18292
|
);
|
|
18136
18293
|
}
|
|
@@ -18204,7 +18361,8 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
18204
18361
|
codexPluginVersions: sourceVersions("codex"),
|
|
18205
18362
|
codexCacheVersions: cacheVersionsFor("codex"),
|
|
18206
18363
|
opencodePluginVersions: opencodePluginVersionsForReport(),
|
|
18207
|
-
releasedVersion
|
|
18364
|
+
releasedVersion,
|
|
18365
|
+
releasedVersionSource: releasedVersionResolution.source
|
|
18208
18366
|
});
|
|
18209
18367
|
}
|
|
18210
18368
|
});
|
|
@@ -20741,6 +20899,29 @@ ${r.repo}: applied=[${r.applied.join("; ")}] skipped=[${r.skipped.join("; ")}]${
|
|
|
20741
20899
|
}
|
|
20742
20900
|
if (!audit.ok) process.exitCode = 1;
|
|
20743
20901
|
});
|
|
20902
|
+
var wiki = program2.command("wiki").description("release wiki lane \u2014 publish generated pages via a short-lived, repo-scoped minted token");
|
|
20903
|
+
wiki.command("publish <pages-dir>").description("mint a 1h repo-scoped contents:write token (master-gated) and publish the generated .md pages to <repo>.wiki.git via scripts/wiki-publish.mjs; the token stays in memory (never printed/committed). Fails loud on the credential gap \u2014 never a silent skip").option("--repo <owner/repo>", "target repo (defaults to the cwd repo)").action(async (pagesDir, o) => {
|
|
20904
|
+
const repo = await resolveRepo(o.repo);
|
|
20905
|
+
if (!repo) return fail("wiki publish: could not determine the target repo (pass --repo owner/name)");
|
|
20906
|
+
const cfg = await loadConfig();
|
|
20907
|
+
const ok = await wikiPublish(
|
|
20908
|
+
{
|
|
20909
|
+
mint: (r) => mintWikiToken(r, registryClientDeps(cfg)),
|
|
20910
|
+
// scripts/wiki-publish.mjs lives at the repo root; Lane B runs there. Resolve against cwd.
|
|
20911
|
+
scriptPath: () => (0, import_node_path18.resolve)(process.cwd(), "scripts", "wiki-publish.mjs"),
|
|
20912
|
+
scriptExists: (p) => (0, import_node_fs18.existsSync)(p),
|
|
20913
|
+
spawn: (command, args, env) => {
|
|
20914
|
+
const r = (0, import_node_child_process11.spawnSync)(command, args, { stdio: "inherit", env });
|
|
20915
|
+
if (r.error) throw r.error;
|
|
20916
|
+
return r.status ?? 1;
|
|
20917
|
+
},
|
|
20918
|
+
log: (msg) => console.log(msg),
|
|
20919
|
+
err: (msg) => console.error(msg)
|
|
20920
|
+
},
|
|
20921
|
+
{ repo, pagesDir }
|
|
20922
|
+
);
|
|
20923
|
+
if (!ok) process.exitCode = 1;
|
|
20924
|
+
});
|
|
20744
20925
|
var bootstrap = program2.command("bootstrap").description("plan repo bootstrap operations; mutations require master-admin approval").option("--repo <owner/repo>", "target repo").option("--class <class>", "deployable | content", "deployable").option("--json", "machine-readable output").option("--apply", "reserved for future bootstrap execution after explicit master-admin approval").action((o) => {
|
|
20745
20926
|
if (!o.repo) return fail("bootstrap: required option --repo <owner/repo> not specified");
|
|
20746
20927
|
if (o.apply) return fail("bootstrap: execution is not implemented yet; use the dry-run plan and the existing /bootstrap skill");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "MMI Future CLI — the org dev toolbox (board, registry, keyless secrets, release train, bootstrap, doctor) and the cross-IDE engine the plugin's session-start hook drives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|