@mutmutco/cli 2.5.1 → 2.6.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/index.cjs +221 -16
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3309,6 +3309,9 @@ function buildHealth(i) {
|
|
|
3309
3309
|
if (!i.sagaApiUrl) problems.push("sagaApiUrl not configured in .mmi/config.json");
|
|
3310
3310
|
if (!i.identity) problems.push("no GitHub identity (gh auth token / GH_TOKEN)");
|
|
3311
3311
|
if (!i.reachable) problems.push("saga backend unreachable");
|
|
3312
|
+
if (i.reachable && i.livenessStatus === 403 && i.livenessMessage === "Forbidden") {
|
|
3313
|
+
problems.push("saga API route-level 403 from GitHubAuthorizer cache/policy");
|
|
3314
|
+
}
|
|
3312
3315
|
if (i.reachable && i.authorized === false) problems.push("saga backend rejected authenticated state access");
|
|
3313
3316
|
if (!i.key.sessionId || i.key.sessionId === "-") problems.push("unsafe session id");
|
|
3314
3317
|
const safeToWrite = problems.length === 0;
|
|
@@ -3317,6 +3320,8 @@ function buildHealth(i) {
|
|
|
3317
3320
|
safeToWrite,
|
|
3318
3321
|
identity: i.identity,
|
|
3319
3322
|
reachable: i.reachable,
|
|
3323
|
+
livenessStatus: i.livenessStatus,
|
|
3324
|
+
livenessMessage: i.livenessMessage,
|
|
3320
3325
|
authorized: i.authorized,
|
|
3321
3326
|
sagaApiUrl: i.sagaApiUrl,
|
|
3322
3327
|
key: i.key,
|
|
@@ -3598,6 +3603,36 @@ query($owner: String!, $number: Int!, $after: String) {
|
|
|
3598
3603
|
}
|
|
3599
3604
|
}
|
|
3600
3605
|
}`;
|
|
3606
|
+
var ISSUE_PROJECT_ITEM_QUERY = `
|
|
3607
|
+
query($repoOwner: String!, $repoName: String!, $number: Int!) {
|
|
3608
|
+
repository(owner: $repoOwner, name: $repoName) {
|
|
3609
|
+
issue(number: $number) {
|
|
3610
|
+
id
|
|
3611
|
+
number
|
|
3612
|
+
title
|
|
3613
|
+
url
|
|
3614
|
+
state
|
|
3615
|
+
repository { nameWithOwner }
|
|
3616
|
+
labels(first: 10) { nodes { name } }
|
|
3617
|
+
assignees(first: 10) { nodes { login } }
|
|
3618
|
+
projectItems(first: 20) {
|
|
3619
|
+
nodes {
|
|
3620
|
+
id
|
|
3621
|
+
project { id title }
|
|
3622
|
+
fieldValues(first: 8) {
|
|
3623
|
+
nodes {
|
|
3624
|
+
... on ProjectV2ItemFieldSingleSelectValue {
|
|
3625
|
+
name
|
|
3626
|
+
optionId
|
|
3627
|
+
field { ... on ProjectV2SingleSelectField { name } }
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
}`;
|
|
3601
3636
|
function resolveBoardConfig(cfg) {
|
|
3602
3637
|
const missing = [];
|
|
3603
3638
|
if (!cfg.projectOwner) missing.push("projectOwner");
|
|
@@ -3871,6 +3906,14 @@ async function claimBoardIssue(options, deps = {}) {
|
|
|
3871
3906
|
const cfg = resolveBoardConfig(options.config);
|
|
3872
3907
|
const gh = deps.gh ?? defaultGh;
|
|
3873
3908
|
const collected = await collectBoardItems(cfg, { repo: options.repo, allowPartial: options.allowPartial }, deps);
|
|
3909
|
+
const selector = parseIssueSelector(options.selector, collected.repo);
|
|
3910
|
+
try {
|
|
3911
|
+
findBoardItem(collected.items, selector);
|
|
3912
|
+
} catch (e) {
|
|
3913
|
+
const fallback = await fetchIssueProjectItem(gh, cfg, selector);
|
|
3914
|
+
if (!fallback) throw e;
|
|
3915
|
+
collected.items.push(fallback);
|
|
3916
|
+
}
|
|
3874
3917
|
const writable = await resolveWritableReposForClaimables(collected.items, gh, options.allowPartial ?? false);
|
|
3875
3918
|
collected.warnings.push(...writable.warnings);
|
|
3876
3919
|
collected.partial = collected.partial || writable.partial;
|
|
@@ -3882,7 +3925,6 @@ async function claimBoardIssue(options, deps = {}) {
|
|
|
3882
3925
|
warnings: collected.warnings,
|
|
3883
3926
|
partial: collected.partial
|
|
3884
3927
|
};
|
|
3885
|
-
const selector = parseIssueSelector(options.selector, collected.repo);
|
|
3886
3928
|
const flatItem = findBoardItem(collected.items, selector);
|
|
3887
3929
|
if (flatItem.status === "Todo" && flatItem.assignees.length === 0 && !writable.repos.has(flatItem.repository.toLowerCase())) {
|
|
3888
3930
|
throw new Error(`${flatItem.ref} is not claimable: viewer does not have write access to ${flatItem.repository}`);
|
|
@@ -4039,6 +4081,42 @@ async function fetchProjectPage(gh, cfg, after) {
|
|
|
4039
4081
|
if (!parsed.data) throw new Error("gh GraphQL response did not include data");
|
|
4040
4082
|
return parsed.data;
|
|
4041
4083
|
}
|
|
4084
|
+
async function fetchIssueProjectItem(gh, cfg, selector) {
|
|
4085
|
+
const [repoOwner, repoName] = selector.repo.split("/");
|
|
4086
|
+
if (!repoOwner || !repoName) return void 0;
|
|
4087
|
+
const { stdout } = await gh([
|
|
4088
|
+
"api",
|
|
4089
|
+
"graphql",
|
|
4090
|
+
"-f",
|
|
4091
|
+
`query=${ISSUE_PROJECT_ITEM_QUERY}`,
|
|
4092
|
+
"-f",
|
|
4093
|
+
`repoOwner=${repoOwner}`,
|
|
4094
|
+
"-f",
|
|
4095
|
+
`repoName=${repoName}`,
|
|
4096
|
+
"-F",
|
|
4097
|
+
`number=${selector.number}`
|
|
4098
|
+
]);
|
|
4099
|
+
const parsed = JSON.parse(stdout);
|
|
4100
|
+
const issue2 = parsed.data?.repository?.issue;
|
|
4101
|
+
if (!issue2) return void 0;
|
|
4102
|
+
const projectItem = (issue2.projectItems?.nodes ?? []).find((item) => item.project?.id === cfg.projectId);
|
|
4103
|
+
if (!projectItem) return void 0;
|
|
4104
|
+
return nodeToItem({
|
|
4105
|
+
id: projectItem.id,
|
|
4106
|
+
fieldValues: projectItem.fieldValues,
|
|
4107
|
+
content: {
|
|
4108
|
+
__typename: "Issue",
|
|
4109
|
+
id: issue2.id,
|
|
4110
|
+
number: issue2.number,
|
|
4111
|
+
title: issue2.title,
|
|
4112
|
+
url: issue2.url,
|
|
4113
|
+
state: issue2.state,
|
|
4114
|
+
repository: issue2.repository,
|
|
4115
|
+
labels: issue2.labels,
|
|
4116
|
+
assignees: issue2.assignees
|
|
4117
|
+
}
|
|
4118
|
+
});
|
|
4119
|
+
}
|
|
4042
4120
|
function nodesToItems(nodes, warnings) {
|
|
4043
4121
|
const items = [];
|
|
4044
4122
|
for (const node of nodes) {
|
|
@@ -4505,8 +4583,13 @@ var PLUGIN_DRIFT_LABEL = "plugin config drift (mmi@mmi duplicate rows / stale gi
|
|
|
4505
4583
|
function recordFreshness(r) {
|
|
4506
4584
|
return r.lastUpdated ?? r.installedAt ?? "";
|
|
4507
4585
|
}
|
|
4508
|
-
function
|
|
4509
|
-
return records.reduce((best, r) =>
|
|
4586
|
+
function bestRecord(records) {
|
|
4587
|
+
return records.reduce((best, r) => {
|
|
4588
|
+
const byVersion = compareVersions(r.version ?? "0", best.version ?? "0");
|
|
4589
|
+
if (byVersion > 0) return r;
|
|
4590
|
+
if (byVersion < 0) return best;
|
|
4591
|
+
return recordFreshness(r) > recordFreshness(best) ? r : best;
|
|
4592
|
+
});
|
|
4510
4593
|
}
|
|
4511
4594
|
function pluginConfigDriftFix(pluginId) {
|
|
4512
4595
|
return `\`${pluginId}\` registered as N duplicate project rows / a stale gitCommitSha in ~/.claude/plugins/installed_plugins.json \u2014 run \`mmi-cli doctor\` to collapse them to one \`scope: user\` entry at the highest version (a .bak backup is written first), or in \`/plugin\` uninstall the extra rows and reinstall once at user scope`;
|
|
@@ -4523,19 +4606,21 @@ function buildPluginConfigDriftCheck(input) {
|
|
|
4523
4606
|
const records = input.installed?.plugins?.[pluginId];
|
|
4524
4607
|
if (!Array.isArray(records) || records.length === 0) return base;
|
|
4525
4608
|
const projectRows = records.filter((r) => r.scope === "project");
|
|
4526
|
-
const
|
|
4527
|
-
const
|
|
4528
|
-
const staleShaRows =
|
|
4609
|
+
const best = bestRecord(records);
|
|
4610
|
+
const bestSha = best.gitCommitSha;
|
|
4611
|
+
const staleShaRows = bestSha ? records.filter((r) => r.gitCommitSha !== void 0 && r.gitCommitSha !== bestSha).length : 0;
|
|
4612
|
+
const duplicateRows = records.length > 1 ? records.length : 0;
|
|
4529
4613
|
const duplicateProjectRows = projectRows.length > 1 ? projectRows.length : 0;
|
|
4530
|
-
if (
|
|
4531
|
-
return { ...base, duplicateProjectRows: 0, staleShaRows: 0 };
|
|
4614
|
+
if (duplicateRows === 0 && staleShaRows === 0) {
|
|
4615
|
+
return { ...base, duplicateRows: 0, duplicateProjectRows: 0, staleShaRows: 0 };
|
|
4532
4616
|
}
|
|
4533
|
-
const { projectPath: _drop, ...rest } =
|
|
4617
|
+
const { projectPath: _drop, ...rest } = best;
|
|
4534
4618
|
const collapsed = { ...rest, scope: "user" };
|
|
4535
4619
|
return {
|
|
4536
4620
|
...base,
|
|
4537
4621
|
ok: false,
|
|
4538
4622
|
recordsToWrite: [collapsed],
|
|
4623
|
+
duplicateRows,
|
|
4539
4624
|
duplicateProjectRows,
|
|
4540
4625
|
staleShaRows
|
|
4541
4626
|
};
|
|
@@ -4549,6 +4634,58 @@ function buildGitignoreManagedBlockCheck(input) {
|
|
|
4549
4634
|
if (!changed) return base;
|
|
4550
4635
|
return { ...base, ok: false, contentToWrite: content };
|
|
4551
4636
|
}
|
|
4637
|
+
function detectSurface(env) {
|
|
4638
|
+
const has = (k) => Boolean(env[k]?.trim());
|
|
4639
|
+
if (env.MMI_AGENT_SURFACE === "codex" || has("CODEX_HOME") || (env.CLAUDE_PLUGIN_ROOT ?? "").includes(".codex")) {
|
|
4640
|
+
return "codex";
|
|
4641
|
+
}
|
|
4642
|
+
const isClaude = has("CLAUDECODE") || has("CLAUDE_CODE_ENTRYPOINT") || has("CLAUDE_PLUGIN_ROOT") || env.MMI_AGENT_SURFACE === "claude";
|
|
4643
|
+
const isVscode = env.TERM_PROGRAM === "vscode" || has("VSCODE_PID") || has("VSCODE_GIT_ASKPASS_NODE");
|
|
4644
|
+
if (isClaude && isVscode) return "claude-vscode";
|
|
4645
|
+
if (isClaude) return "claude-cli";
|
|
4646
|
+
return "shell";
|
|
4647
|
+
}
|
|
4648
|
+
function pluginRecoveryFix(surface) {
|
|
4649
|
+
const claude = "claude plugin marketplace update mmi && claude plugin update mmi@mmi && claude plugin enable mmi@mmi";
|
|
4650
|
+
switch (surface) {
|
|
4651
|
+
case "claude-vscode":
|
|
4652
|
+
return `${claude} # then reopen the VS Code workspace to reload MMI commands`;
|
|
4653
|
+
case "claude-cli":
|
|
4654
|
+
return `${claude} # then restart Claude Code, or run /reload-plugins`;
|
|
4655
|
+
case "codex":
|
|
4656
|
+
return "codex plugin marketplace upgrade mmi && codex plugin add mmi@mmi # then restart Codex";
|
|
4657
|
+
case "shell":
|
|
4658
|
+
default:
|
|
4659
|
+
return "npm install -g @mutmutco/cli@latest";
|
|
4660
|
+
}
|
|
4661
|
+
}
|
|
4662
|
+
var INSTALLED_PLUGIN_VERSION_LABEL = "installed MMI plugin version (vs latest release)";
|
|
4663
|
+
function isSemverVersion(v) {
|
|
4664
|
+
return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
|
|
4665
|
+
}
|
|
4666
|
+
function buildInstalledPluginVersionCheck(input) {
|
|
4667
|
+
const pluginId = input.pluginId ?? MMI_PLUGIN_ID;
|
|
4668
|
+
const base = {
|
|
4669
|
+
ok: true,
|
|
4670
|
+
label: INSTALLED_PLUGIN_VERSION_LABEL,
|
|
4671
|
+
fix: pluginRecoveryFix(input.surface),
|
|
4672
|
+
pluginId
|
|
4673
|
+
};
|
|
4674
|
+
if (!input.isOrgRepo) return base;
|
|
4675
|
+
const records = input.installed?.plugins?.[pluginId];
|
|
4676
|
+
if (!Array.isArray(records) || records.length === 0) return base;
|
|
4677
|
+
const installedVersion = bestRecord(records).version;
|
|
4678
|
+
if (!isSemverVersion(installedVersion) || !isSemverVersion(input.releasedVersion)) return base;
|
|
4679
|
+
if (compareVersions(installedVersion, input.releasedVersion) >= 0) {
|
|
4680
|
+
return { ...base, installedVersion, releasedVersion: input.releasedVersion };
|
|
4681
|
+
}
|
|
4682
|
+
return {
|
|
4683
|
+
...base,
|
|
4684
|
+
ok: false,
|
|
4685
|
+
installedVersion,
|
|
4686
|
+
releasedVersion: input.releasedVersion
|
|
4687
|
+
};
|
|
4688
|
+
}
|
|
4552
4689
|
|
|
4553
4690
|
// src/stage-runner.ts
|
|
4554
4691
|
var import_node_child_process3 = require("node:child_process");
|
|
@@ -6413,6 +6550,33 @@ async function applyVersionAutoUpdate(report, log) {
|
|
|
6413
6550
|
return report;
|
|
6414
6551
|
}
|
|
6415
6552
|
}
|
|
6553
|
+
var CLAUDE_PLUGIN_TIMEOUT_MS = 6e4;
|
|
6554
|
+
async function runClaudePlugin(args) {
|
|
6555
|
+
const candidates = isWin ? ["claude.cmd", "claude.exe", "claude"] : ["claude"];
|
|
6556
|
+
for (const bin of candidates) {
|
|
6557
|
+
try {
|
|
6558
|
+
await execFileP3(bin, args, { timeout: CLAUDE_PLUGIN_TIMEOUT_MS });
|
|
6559
|
+
return true;
|
|
6560
|
+
} catch (err) {
|
|
6561
|
+
if (err.code === "ENOENT") continue;
|
|
6562
|
+
return false;
|
|
6563
|
+
}
|
|
6564
|
+
}
|
|
6565
|
+
return false;
|
|
6566
|
+
}
|
|
6567
|
+
async function applyClaudePluginHeal(surface, log) {
|
|
6568
|
+
if (surface !== "claude-cli" && surface !== "claude-vscode") return false;
|
|
6569
|
+
log(" \u21BB updating the MMI plugin via `claude plugin` (marketplace \u2192 update \u2192 enable)\u2026");
|
|
6570
|
+
const steps = [
|
|
6571
|
+
["plugin", "marketplace", "update", "mmi"],
|
|
6572
|
+
["plugin", "update", "mmi@mmi"],
|
|
6573
|
+
["plugin", "enable", "mmi@mmi"]
|
|
6574
|
+
];
|
|
6575
|
+
for (const step of steps) {
|
|
6576
|
+
if (!await runClaudePlugin(step)) return false;
|
|
6577
|
+
}
|
|
6578
|
+
return true;
|
|
6579
|
+
}
|
|
6416
6580
|
var program2 = new Command();
|
|
6417
6581
|
program2.name("mmi-cli").description("MMI Future CLI \u2014 org rules delivery, saga, KB. The engine the plugin SessionStart hook drives.").version(resolveVersion());
|
|
6418
6582
|
var rules = program2.command("rules").description("org rules delivery");
|
|
@@ -6554,9 +6718,15 @@ saga.command("key").option("--json", "machine-readable output").description("pri
|
|
|
6554
6718
|
async function probeBackend(url) {
|
|
6555
6719
|
try {
|
|
6556
6720
|
const res = await fetch(`${url}/saga/head`, { headers: await sagaHeaders(), signal: AbortSignal.timeout(8e3) });
|
|
6557
|
-
|
|
6721
|
+
let message = "";
|
|
6722
|
+
try {
|
|
6723
|
+
const body = await res.clone().json();
|
|
6724
|
+
message = typeof body.message === "string" ? body.message : "";
|
|
6725
|
+
} catch {
|
|
6726
|
+
}
|
|
6727
|
+
return { reachable: res.ok || res.status === 403, status: res.status, message };
|
|
6558
6728
|
} catch {
|
|
6559
|
-
return false;
|
|
6729
|
+
return { reachable: false };
|
|
6560
6730
|
}
|
|
6561
6731
|
}
|
|
6562
6732
|
async function probeSagaAccess(url, key) {
|
|
@@ -6574,9 +6744,18 @@ saga.command("health").option("--json", "machine-readable output").option("--ban
|
|
|
6574
6744
|
const key = await sagaKey(cfg, session);
|
|
6575
6745
|
const source = session.source;
|
|
6576
6746
|
const identity = await githubLogin();
|
|
6577
|
-
const
|
|
6578
|
-
const authorized = cfg.sagaApiUrl && reachable ? await probeSagaAccess(cfg.sagaApiUrl, key) : void 0;
|
|
6579
|
-
const report = buildHealth({
|
|
6747
|
+
const liveness = cfg.sagaApiUrl ? await probeBackend(cfg.sagaApiUrl) : { reachable: false };
|
|
6748
|
+
const authorized = cfg.sagaApiUrl && liveness.reachable ? await probeSagaAccess(cfg.sagaApiUrl, key) : void 0;
|
|
6749
|
+
const report = buildHealth({
|
|
6750
|
+
key,
|
|
6751
|
+
source,
|
|
6752
|
+
identity,
|
|
6753
|
+
reachable: liveness.reachable,
|
|
6754
|
+
livenessStatus: liveness.status,
|
|
6755
|
+
livenessMessage: liveness.message,
|
|
6756
|
+
authorized,
|
|
6757
|
+
sagaApiUrl: cfg.sagaApiUrl
|
|
6758
|
+
});
|
|
6580
6759
|
if (o.json) return console.log(JSON.stringify(report));
|
|
6581
6760
|
if (o.banner) {
|
|
6582
6761
|
const banner = healthBanner(report);
|
|
@@ -7489,7 +7668,7 @@ function writeGitignore(content) {
|
|
|
7489
7668
|
return false;
|
|
7490
7669
|
}
|
|
7491
7670
|
}
|
|
7492
|
-
program2.command("doctor").description("check onboarding gates (GitHub auth, mmi-cli on PATH, repo config, plugin git clone, plugin install record, .gitignore managed block, plugin config drift) and print fixes").option("--banner", "one-line resume summary; silent when all gates pass").option("--guide", "print the MMI Agentic Onboarding guide URL").option("--json", "machine-readable output").action(async (opts) => {
|
|
7671
|
+
program2.command("doctor").description("check onboarding gates (GitHub auth, mmi-cli on PATH, repo config, plugin git clone, plugin install record, .gitignore managed block, plugin config drift, installed plugin version) and print fixes").option("--banner", "one-line resume summary; silent when all gates pass").option("--guide", "print the MMI Agentic Onboarding guide URL").option("--json", "machine-readable output").action(async (opts) => {
|
|
7493
7672
|
if (opts.guide) {
|
|
7494
7673
|
if (opts.json) console.log(JSON.stringify({ resources: [MMI_AGENTIC_ONBOARDING_GUIDE] }, null, 2));
|
|
7495
7674
|
else console.log(MMI_AGENTIC_ONBOARDING_GUIDE.url);
|
|
@@ -7517,12 +7696,15 @@ program2.command("doctor").description("check onboarding gates (GitHub auth, mmi
|
|
|
7517
7696
|
if (root && (0, import_node_fs4.existsSync)(`${root}/bin/mmi-cli${isWin ? ".cmd" : ""}`)) onPath = true;
|
|
7518
7697
|
}
|
|
7519
7698
|
checks.push({ ok: onPath, label: "mmi-cli on PATH", fix: "auto-provisioned at session start \u2014 reopen the session, or install the MMI plugin" });
|
|
7699
|
+
const surface = detectSurface(process.env);
|
|
7700
|
+
const releasedVersion = await fetchReleasedVersion();
|
|
7520
7701
|
let versionReport = buildVersionLagReport({
|
|
7521
7702
|
currentVersion: resolveVersion(),
|
|
7522
7703
|
repoVersion: readRepoVersion(),
|
|
7523
|
-
releasedVersion
|
|
7704
|
+
releasedVersion
|
|
7524
7705
|
});
|
|
7525
7706
|
if (!opts.json) versionReport = await applyVersionAutoUpdate(versionReport, (m) => console.error(m));
|
|
7707
|
+
if (!versionReport.ok) versionReport = { ...versionReport, fix: pluginRecoveryFix(surface) };
|
|
7526
7708
|
checks.push(versionReport);
|
|
7527
7709
|
const cfg = await loadConfig();
|
|
7528
7710
|
checks.push({ ok: Boolean(cfg.sagaApiUrl), label: "repo config (.mmi/config.json)", fix: "ask a master-admin to run /bootstrap on this repo" });
|
|
@@ -7574,7 +7756,30 @@ program2.command("doctor").description("check onboarding gates (GitHub auth, mmi
|
|
|
7574
7756
|
if (!opts.banner) console.error(" \u21BB repaired: collapsed mmi@mmi to one user-scope entry (backup at installed_plugins.json.bak) \u2014 run /reload-plugins");
|
|
7575
7757
|
}
|
|
7576
7758
|
}
|
|
7759
|
+
if (!driftCheck.ok) driftCheck = { ...driftCheck, fix: pluginRecoveryFix(surface) };
|
|
7577
7760
|
checks.push(driftCheck);
|
|
7761
|
+
let installedVersionCheck = buildInstalledPluginVersionCheck({
|
|
7762
|
+
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
7763
|
+
installed,
|
|
7764
|
+
releasedVersion,
|
|
7765
|
+
surface
|
|
7766
|
+
});
|
|
7767
|
+
if (!installedVersionCheck.ok && !opts.json) {
|
|
7768
|
+
if (await applyClaudePluginHeal(surface, (m) => console.error(m))) {
|
|
7769
|
+
const healed = buildInstalledPluginVersionCheck({
|
|
7770
|
+
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
7771
|
+
installed: readInstalledPlugins(),
|
|
7772
|
+
releasedVersion,
|
|
7773
|
+
surface
|
|
7774
|
+
});
|
|
7775
|
+
installedVersionCheck = healed;
|
|
7776
|
+
if (healed.ok && !opts.banner) {
|
|
7777
|
+
const reload = surface === "claude-vscode" ? "reopen the VS Code workspace" : "restart Claude Code (or run /reload-plugins)";
|
|
7778
|
+
console.error(` \u21BB updated MMI plugin \u2192 ${releasedVersion ?? "latest"} via claude plugin \u2014 ${reload} to load the new commands`);
|
|
7779
|
+
}
|
|
7780
|
+
}
|
|
7781
|
+
}
|
|
7782
|
+
checks.push(installedVersionCheck);
|
|
7578
7783
|
const gaps = checks.filter((c) => !c.ok);
|
|
7579
7784
|
const resources = doctorResourcesForGaps(gaps);
|
|
7580
7785
|
if (opts.json) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "MMI Future CLI — delivers the org rules (whole-file), plus saga and KB access. The cross-IDE engine the plugin's SessionStart hook drives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|