@mutmutco/cli 2.32.3 → 2.33.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 +170 -165
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3423,7 +3423,7 @@ function resolveRulesBase(orgRulesSource, defaultBase) {
|
|
|
3423
3423
|
}
|
|
3424
3424
|
|
|
3425
3425
|
// src/index.ts
|
|
3426
|
-
var
|
|
3426
|
+
var import_node_child_process10 = require("node:child_process");
|
|
3427
3427
|
|
|
3428
3428
|
// src/cli-shared.ts
|
|
3429
3429
|
var import_promises = require("node:fs/promises");
|
|
@@ -7229,7 +7229,7 @@ ${buildReportBody(body, sourceRepo)}`;
|
|
|
7229
7229
|
|
|
7230
7230
|
// src/skill-lesson.ts
|
|
7231
7231
|
var SKILL_LESSON_LABEL = "skill-lesson";
|
|
7232
|
-
var SKILL_NAMES = ["bootstrap", "browser-automation", "grind", "hotfix", "mmi", "rcand", "release", "secrets", "stage"];
|
|
7232
|
+
var SKILL_NAMES = ["bootstrap", "browser-automation", "build", "grind", "hotfix", "mmi", "rcand", "release", "secrets", "stage"];
|
|
7233
7233
|
function assertSkillName(name) {
|
|
7234
7234
|
const match = SKILL_NAMES.find((skill) => skill === name);
|
|
7235
7235
|
if (!match) throw new Error(`unknown skill "${name}" \u2014 expected one of: ${SKILL_NAMES.join(", ")}`);
|
|
@@ -7355,127 +7355,92 @@ function buildPanelPlan(input) {
|
|
|
7355
7355
|
};
|
|
7356
7356
|
}
|
|
7357
7357
|
|
|
7358
|
-
// src/
|
|
7359
|
-
var
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7358
|
+
// src/build-policy.ts
|
|
7359
|
+
var TIER_BUDGETS = {
|
|
7360
|
+
light: {
|
|
7361
|
+
tier: "light",
|
|
7362
|
+
agents: 2,
|
|
7363
|
+
planners: 0,
|
|
7364
|
+
parallelSitesCap: 1,
|
|
7365
|
+
verificationDepth: "panel-once",
|
|
7366
|
+
reasoningEffort: "standard"
|
|
7367
|
+
},
|
|
7368
|
+
standard: {
|
|
7369
|
+
tier: "standard",
|
|
7370
|
+
agents: 3,
|
|
7371
|
+
planners: 0,
|
|
7372
|
+
parallelSitesCap: 2,
|
|
7373
|
+
verificationDepth: "full-panel",
|
|
7374
|
+
reasoningEffort: "standard-high"
|
|
7375
|
+
},
|
|
7376
|
+
deep: {
|
|
7377
|
+
tier: "deep",
|
|
7378
|
+
agents: 4,
|
|
7379
|
+
planners: 2,
|
|
7380
|
+
parallelSitesCap: 3,
|
|
7381
|
+
verificationDepth: "full-double-pass",
|
|
7382
|
+
reasoningEffort: "high"
|
|
7383
|
+
},
|
|
7384
|
+
max: {
|
|
7385
|
+
tier: "max",
|
|
7386
|
+
agents: 5,
|
|
7387
|
+
planners: 3,
|
|
7388
|
+
parallelSitesCap: 4,
|
|
7389
|
+
verificationDepth: "integration-checkpoints",
|
|
7390
|
+
reasoningEffort: "max"
|
|
7391
|
+
}
|
|
7372
7392
|
};
|
|
7373
|
-
function
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7393
|
+
function getTierBudgets(tier) {
|
|
7394
|
+
return TIER_BUDGETS[tier];
|
|
7395
|
+
}
|
|
7396
|
+
function isWideOrArchitectural(scope) {
|
|
7397
|
+
return scope === "wide" || scope === "architectural";
|
|
7398
|
+
}
|
|
7399
|
+
function isCrossModuleOrProduct(blast) {
|
|
7400
|
+
return blast === "cross-module" || blast === "product";
|
|
7401
|
+
}
|
|
7402
|
+
function pickEffortTier(signals) {
|
|
7403
|
+
if (signals.explicitTier) {
|
|
7404
|
+
return {
|
|
7405
|
+
tier: signals.explicitTier,
|
|
7406
|
+
reason: `explicit user override \u2192 ${signals.explicitTier}`,
|
|
7407
|
+
budgets: getTierBudgets(signals.explicitTier)
|
|
7408
|
+
};
|
|
7389
7409
|
}
|
|
7390
|
-
if (
|
|
7391
|
-
|
|
7410
|
+
if (signals.risk === "critical") {
|
|
7411
|
+
return { tier: "max", reason: "critical risk", budgets: getTierBudgets("max") };
|
|
7392
7412
|
}
|
|
7393
|
-
|
|
7394
|
-
webSearch: Boolean(input.toolPolicy?.webSearch),
|
|
7395
|
-
maxQueriesPerLens: input.toolPolicy?.maxQueriesPerLens ?? 3,
|
|
7396
|
-
denyDomains: input.toolPolicy?.denyDomains ?? []
|
|
7397
|
-
};
|
|
7398
|
-
return {
|
|
7399
|
-
provider,
|
|
7400
|
-
routing,
|
|
7401
|
-
lenses,
|
|
7402
|
-
models,
|
|
7403
|
-
toolPolicy,
|
|
7404
|
-
criteria: input.criteria,
|
|
7405
|
-
diff: input.diff,
|
|
7406
|
-
fallback: "host-panel",
|
|
7407
|
-
instructions: "Hosted fusion when provider is configured; else spawn host lenses and pipe JSON to `mmi-cli verify synthesize`. Synthesizer slot must differ from builder."
|
|
7408
|
-
};
|
|
7409
|
-
}
|
|
7410
|
-
function adaptFusionResponse(raw) {
|
|
7411
|
-
if (raw.lenses) {
|
|
7412
|
-
const lenses = parseLensResults(raw.lenses);
|
|
7413
|
-
const base2 = synthesizePanelReport(lenses);
|
|
7413
|
+
if (signals.foundational && (isWideOrArchitectural(signals.scope) || signals.blastRadius === "product")) {
|
|
7414
7414
|
return {
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
partial_coverage: raw.partial_coverage ?? base2.partial_coverage,
|
|
7419
|
-
unique_insights: raw.unique_insights ?? base2.unique_insights,
|
|
7420
|
-
blind_spots: raw.blind_spots ?? base2.blind_spots,
|
|
7421
|
-
nits: raw.nits ?? base2.nits
|
|
7415
|
+
tier: "max",
|
|
7416
|
+
reason: "foundational seam with wide/architectural scope or product blast radius",
|
|
7417
|
+
budgets: getTierBudgets("max")
|
|
7422
7418
|
};
|
|
7423
7419
|
}
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
title: b.title,
|
|
7427
|
-
file: b.file,
|
|
7428
|
-
line: b.line,
|
|
7429
|
-
why: b.why,
|
|
7430
|
-
sources: b.sources ?? ["hosted-fusion"]
|
|
7431
|
-
}));
|
|
7432
|
-
return {
|
|
7433
|
-
consensus: raw.consensus ?? [],
|
|
7434
|
-
contradictions: raw.contradictions ?? [],
|
|
7435
|
-
partial_coverage: raw.partial_coverage ?? [],
|
|
7436
|
-
unique_insights: raw.unique_insights ?? [],
|
|
7437
|
-
blind_spots: raw.blind_spots ?? [],
|
|
7438
|
-
blockers,
|
|
7439
|
-
nits: raw.nits ?? []
|
|
7440
|
-
};
|
|
7441
|
-
}
|
|
7442
|
-
async function runFusionProvider(plan2, deps = {}) {
|
|
7443
|
-
const url = resolveFusionProviderUrl(deps.providerUrl ?? plan2.provider ?? void 0);
|
|
7444
|
-
if (!url) {
|
|
7445
|
-
return { ok: false, source: "fallback", error: "no fusion provider configured" };
|
|
7420
|
+
if (signals.risk === "high") {
|
|
7421
|
+
return { tier: "deep", reason: "high risk", budgets: getTierBudgets("deep") };
|
|
7446
7422
|
}
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
const headers = { "content-type": "application/json" };
|
|
7450
|
-
if (apiKey) headers.authorization = `Bearer ${apiKey}`;
|
|
7451
|
-
try {
|
|
7452
|
-
const res = await fetchImpl(url, {
|
|
7453
|
-
method: "POST",
|
|
7454
|
-
headers,
|
|
7455
|
-
body: JSON.stringify({
|
|
7456
|
-
routing: plan2.routing,
|
|
7457
|
-
lenses: plan2.lenses,
|
|
7458
|
-
models: plan2.models,
|
|
7459
|
-
toolPolicy: plan2.toolPolicy,
|
|
7460
|
-
criteria: plan2.criteria,
|
|
7461
|
-
diff: plan2.diff
|
|
7462
|
-
}),
|
|
7463
|
-
signal: AbortSignal.timeout(3e4)
|
|
7464
|
-
});
|
|
7465
|
-
if (!res.ok) {
|
|
7466
|
-
return { ok: false, source: "fallback", error: `provider HTTP ${res.status}` };
|
|
7467
|
-
}
|
|
7468
|
-
const body = await res.json();
|
|
7469
|
-
return { ok: true, source: "hosted-fusion", report: adaptFusionResponse(body) };
|
|
7470
|
-
} catch (e) {
|
|
7471
|
-
return { ok: false, source: "fallback", error: e.message };
|
|
7423
|
+
if (signals.ambiguity === "high") {
|
|
7424
|
+
return { tier: "deep", reason: "high ambiguity", budgets: getTierBudgets("deep") };
|
|
7472
7425
|
}
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7426
|
+
if (signals.scope === "architectural") {
|
|
7427
|
+
return { tier: "deep", reason: "architectural scope", budgets: getTierBudgets("deep") };
|
|
7428
|
+
}
|
|
7429
|
+
if (isCrossModuleOrProduct(signals.blastRadius)) {
|
|
7430
|
+
return {
|
|
7431
|
+
tier: "deep",
|
|
7432
|
+
reason: `${signals.blastRadius} blast radius`,
|
|
7433
|
+
budgets: getTierBudgets("deep")
|
|
7434
|
+
};
|
|
7435
|
+
}
|
|
7436
|
+
if (signals.scope === "trivial" && signals.ambiguity === "low" && signals.risk === "low") {
|
|
7437
|
+
return {
|
|
7438
|
+
tier: "light",
|
|
7439
|
+
reason: "trivial scope with low risk and low ambiguity",
|
|
7440
|
+
budgets: getTierBudgets("light")
|
|
7441
|
+
};
|
|
7442
|
+
}
|
|
7443
|
+
return { tier: "standard", reason: "default \u2014 normal slice", budgets: getTierBudgets("standard") };
|
|
7479
7444
|
}
|
|
7480
7445
|
|
|
7481
7446
|
// src/gc.ts
|
|
@@ -8248,15 +8213,24 @@ function decideStage(inputs) {
|
|
|
8248
8213
|
}
|
|
8249
8214
|
|
|
8250
8215
|
// src/cursor-plugin-seed.ts
|
|
8216
|
+
var import_node_child_process7 = require("node:child_process");
|
|
8251
8217
|
var import_node_fs11 = require("node:fs");
|
|
8252
8218
|
var import_node_os4 = require("node:os");
|
|
8253
8219
|
var import_node_path11 = require("node:path");
|
|
8220
|
+
var import_node_util6 = require("node:util");
|
|
8254
8221
|
function isSemverVersion(v) {
|
|
8255
8222
|
return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
|
|
8256
8223
|
}
|
|
8257
8224
|
var MMI_HUB_REPO = "mutmutco/MMI-Hub";
|
|
8258
8225
|
var CURSOR_THIRD_PARTY_STATE_KEY = "cursor/thirdPartyExtensibilityEnabled";
|
|
8259
8226
|
var PLUGIN_JSON_REL = ".cursor-plugin/plugin.json";
|
|
8227
|
+
var execFileBuffer = (0, import_node_util6.promisify)(import_node_child_process7.execFile);
|
|
8228
|
+
function gitFetchReleaseTagArgs(hubCheckout, tag) {
|
|
8229
|
+
return ["-C", hubCheckout, "fetch", "origin", "tag", tag, "--quiet"];
|
|
8230
|
+
}
|
|
8231
|
+
function ghReleaseTarballApiArgs(tag) {
|
|
8232
|
+
return ["api", `repos/${MMI_HUB_REPO}/tarball/refs/tags/${tag}`];
|
|
8233
|
+
}
|
|
8260
8234
|
function cursorUserGlobalStatePath() {
|
|
8261
8235
|
if (process.platform === "win32") {
|
|
8262
8236
|
const base2 = process.env.APPDATA || (0, import_node_path11.join)((0, import_node_os4.homedir)(), "AppData", "Roaming");
|
|
@@ -8295,7 +8269,7 @@ function releaseTag(releasedVersion) {
|
|
|
8295
8269
|
async function extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5) {
|
|
8296
8270
|
const tarFile = (0, import_node_path11.join)(tmpRoot, "archive.tar");
|
|
8297
8271
|
try {
|
|
8298
|
-
await execFileP5("git",
|
|
8272
|
+
await execFileP5("git", gitFetchReleaseTagArgs(hubCheckout, tag), { timeout: 6e4 });
|
|
8299
8273
|
await execFileP5("git", ["-C", hubCheckout, "archive", "--format=tar", `--output=${tarFile}`, tag, "plugins/mmi"], {
|
|
8300
8274
|
timeout: 6e4
|
|
8301
8275
|
});
|
|
@@ -8306,13 +8280,18 @@ async function extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFi
|
|
|
8306
8280
|
return void 0;
|
|
8307
8281
|
}
|
|
8308
8282
|
}
|
|
8309
|
-
async function downloadPluginMmiViaGh(tag, tmpRoot
|
|
8283
|
+
async function downloadPluginMmiViaGh(tag, tmpRoot) {
|
|
8310
8284
|
const tarPath = (0, import_node_path11.join)(tmpRoot, "repo.tgz");
|
|
8311
8285
|
try {
|
|
8312
|
-
|
|
8313
|
-
|
|
8286
|
+
(0, import_node_fs11.mkdirSync)(tmpRoot, { recursive: true });
|
|
8287
|
+
const { stdout } = await execFileBuffer("gh", ghReleaseTarballApiArgs(tag), {
|
|
8288
|
+
timeout: 12e4,
|
|
8289
|
+
maxBuffer: 100 * 1024 * 1024,
|
|
8290
|
+
encoding: "buffer",
|
|
8291
|
+
windowsHide: true
|
|
8314
8292
|
});
|
|
8315
|
-
|
|
8293
|
+
(0, import_node_fs11.writeFileSync)(tarPath, stdout);
|
|
8294
|
+
await execFileBuffer("tar", ["-xzf", tarPath, "-C", tmpRoot], { timeout: 12e4, windowsHide: true });
|
|
8316
8295
|
const top = (0, import_node_fs11.readdirSync)(tmpRoot).find((entry) => entry !== "repo.tgz");
|
|
8317
8296
|
if (!top) return void 0;
|
|
8318
8297
|
const pluginMmi = (0, import_node_path11.join)(tmpRoot, top, "plugins", "mmi");
|
|
@@ -8328,7 +8307,7 @@ async function resolvePluginMmiSource(releasedVersion, hubCheckout, tmpRoot, exe
|
|
|
8328
8307
|
const fromHub = await extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5);
|
|
8329
8308
|
if (fromHub) return fromHub;
|
|
8330
8309
|
}
|
|
8331
|
-
return downloadPluginMmiViaGh(tag, (0, import_node_path11.join)(tmpRoot, "gh")
|
|
8310
|
+
return downloadPluginMmiViaGh(tag, (0, import_node_path11.join)(tmpRoot, "gh"));
|
|
8332
8311
|
}
|
|
8333
8312
|
function cursorPluginPinsNeedingSeed(pins, releasedVersion) {
|
|
8334
8313
|
if (!isSemverVersion(releasedVersion)) return pins.filter((pin) => !pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty);
|
|
@@ -9054,12 +9033,12 @@ async function runStageLiveDown(deps, t) {
|
|
|
9054
9033
|
}
|
|
9055
9034
|
|
|
9056
9035
|
// src/stage-runner.ts
|
|
9057
|
-
var
|
|
9036
|
+
var import_node_child_process8 = require("node:child_process");
|
|
9058
9037
|
var import_node_fs12 = require("node:fs");
|
|
9059
9038
|
var import_node_path12 = require("node:path");
|
|
9060
9039
|
var import_node_net2 = require("node:net");
|
|
9061
|
-
var
|
|
9062
|
-
var execFileP4 = (0,
|
|
9040
|
+
var import_node_util7 = require("node:util");
|
|
9041
|
+
var execFileP4 = (0, import_node_util7.promisify)(import_node_child_process8.execFile);
|
|
9063
9042
|
var EARLY_EXIT_GRACE_MS = 2e3;
|
|
9064
9043
|
function waitForProcessStability(child, graceMs = EARLY_EXIT_GRACE_MS) {
|
|
9065
9044
|
return new Promise((resolve, reject) => {
|
|
@@ -9255,7 +9234,7 @@ async function startStage(config = {}, opts = {}) {
|
|
|
9255
9234
|
const extraEnv = {};
|
|
9256
9235
|
for (const [k, v] of Object.entries(config.env ?? {})) extraEnv[k] = sub(v) ?? v;
|
|
9257
9236
|
const up = sub(config.up.trim());
|
|
9258
|
-
const child = (0,
|
|
9237
|
+
const child = (0, import_node_child_process8.spawn)(up, {
|
|
9259
9238
|
cwd,
|
|
9260
9239
|
shell: true,
|
|
9261
9240
|
// POSIX-only: the process group exists for the group-kill in stopStage. On win32 teardown is
|
|
@@ -10346,12 +10325,12 @@ async function runTenantRedeploy(deps, options) {
|
|
|
10346
10325
|
}
|
|
10347
10326
|
|
|
10348
10327
|
// src/hotfix-coverage.ts
|
|
10349
|
-
var
|
|
10328
|
+
var import_node_child_process9 = require("node:child_process");
|
|
10350
10329
|
var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
|
|
10351
10330
|
function checkHotfixCoverage(options = {}) {
|
|
10352
10331
|
const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
|
|
10353
10332
|
const ack = (options.ack ?? []).filter(Boolean);
|
|
10354
|
-
const git = options.git ?? ((args, opts) => (0,
|
|
10333
|
+
const git = options.git ?? ((args, opts) => (0, import_node_child_process9.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
|
|
10355
10334
|
const revList = (range) => {
|
|
10356
10335
|
const out = git(["rev-list", "--no-merges", range]).trim();
|
|
10357
10336
|
return out ? out.split("\n") : [];
|
|
@@ -10690,9 +10669,8 @@ async function runHotfixRelease(deps, versionInput, options = {}) {
|
|
|
10690
10669
|
if (releaseExists) {
|
|
10691
10670
|
releaseNote = `Release ${tag} already exists \u2014 resumed without recreating`;
|
|
10692
10671
|
} else {
|
|
10693
|
-
|
|
10694
|
-
|
|
10695
|
-
releaseNote = `Release ${tag} created (target ${tagCommit.slice(0, 7)})`;
|
|
10672
|
+
await deps.run("gh", ["release", "create", tag, "--repo", ctx.repo, "--target", "main", "--generate-notes", "--latest"]);
|
|
10673
|
+
releaseNote = `Release ${tag} created (target main)`;
|
|
10696
10674
|
if (deps.announce) {
|
|
10697
10675
|
announceNote = (await deps.announce({ repo: ctx.repo, tag, summaryFile: options.announceSummaryFile })).note;
|
|
10698
10676
|
}
|
|
@@ -11547,6 +11525,13 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
|
|
|
11547
11525
|
ok: Boolean(config?.projectOwner && config?.projectNumber && config?.projectId && config?.statusFieldId && config?.statusOptions),
|
|
11548
11526
|
label: "registry project board META exists"
|
|
11549
11527
|
});
|
|
11528
|
+
if (config?.projectId && config.projectNumber == null) {
|
|
11529
|
+
checks.push({
|
|
11530
|
+
ok: false,
|
|
11531
|
+
label: "registry projectNumber present when projectId set",
|
|
11532
|
+
detail: "projectNumber is missing \u2014 bootstrap apply must pass PROJECT_NUMBER or derive it from the live board GraphQL query"
|
|
11533
|
+
});
|
|
11534
|
+
}
|
|
11550
11535
|
if (config?.projectOwner && config.projectNumber != null) {
|
|
11551
11536
|
const fieldsQuery = `query($login: String!, $number: Int!) { organization(login: $login) { projectV2(number: $number) { fields(first: 50) { nodes { ... on ProjectV2FieldCommon { id name } ... on ProjectV2SingleSelectField { id name options { id name } } } } } } }`;
|
|
11552
11537
|
const fields = await (async () => {
|
|
@@ -12173,6 +12158,25 @@ function boardRegistryGaps(meta) {
|
|
|
12173
12158
|
if (meta.projectNumber != null) return [];
|
|
12174
12159
|
return ["projectNumber"];
|
|
12175
12160
|
}
|
|
12161
|
+
function previewRegistryMetaMerge(existing, patch) {
|
|
12162
|
+
const out = { ...existing ?? {} };
|
|
12163
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
12164
|
+
if (value === null) delete out[key];
|
|
12165
|
+
else out[key] = value;
|
|
12166
|
+
}
|
|
12167
|
+
return out;
|
|
12168
|
+
}
|
|
12169
|
+
function boardLinkWriteError(patch, existing) {
|
|
12170
|
+
const patchHasProjectId = typeof patch.projectId === "string" && patch.projectId.length > 0;
|
|
12171
|
+
const patchHasProjectNumber = typeof patch.projectNumber === "number" && Number.isFinite(patch.projectNumber);
|
|
12172
|
+
if (patchHasProjectId && !patchHasProjectNumber && existing?.projectNumber == null) {
|
|
12173
|
+
return "projectId requires projectNumber in registry META \u2014 pass projectNumber with board coords";
|
|
12174
|
+
}
|
|
12175
|
+
if (patch.projectNumber === null && boardRegistryGaps(previewRegistryMetaMerge(existing, patch)).length) {
|
|
12176
|
+
return "projectId requires projectNumber in registry META \u2014 pass projectNumber with board coords";
|
|
12177
|
+
}
|
|
12178
|
+
return null;
|
|
12179
|
+
}
|
|
12176
12180
|
function boardRegistryGapMessage(repo) {
|
|
12177
12181
|
return `Board META incomplete for ${repo}: registry has projectId but no projectNumber \u2014 board claim and auto-add will fail until projectNumber is backfilled (re-run \`node infra/migrate/seed-registry.mjs\` or \`mmi-cli bootstrap apply --execute\` with board vars)`;
|
|
12178
12182
|
}
|
|
@@ -13866,7 +13870,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
13866
13870
|
try {
|
|
13867
13871
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body];
|
|
13868
13872
|
if (o.repo) args.push("--repo", o.repo);
|
|
13869
|
-
(0,
|
|
13873
|
+
(0, import_node_child_process10.spawn)(process.execPath, [process.argv[1], ...args], {
|
|
13870
13874
|
detached: true,
|
|
13871
13875
|
stdio: "ignore",
|
|
13872
13876
|
windowsHide: true,
|
|
@@ -13880,7 +13884,7 @@ function detachPlanSync() {
|
|
|
13880
13884
|
if (planSyncDetached) return;
|
|
13881
13885
|
planSyncDetached = true;
|
|
13882
13886
|
try {
|
|
13883
|
-
(0,
|
|
13887
|
+
(0, import_node_child_process10.spawn)(process.execPath, [process.argv[1], "northstar", "sync", "--quiet"], {
|
|
13884
13888
|
detached: true,
|
|
13885
13889
|
stdio: "ignore",
|
|
13886
13890
|
windowsHide: true,
|
|
@@ -13959,7 +13963,7 @@ function openInEditor(path2) {
|
|
|
13959
13963
|
return;
|
|
13960
13964
|
}
|
|
13961
13965
|
try {
|
|
13962
|
-
(0,
|
|
13966
|
+
(0, import_node_child_process10.spawn)(editor, [path2], { stdio: "inherit" });
|
|
13963
13967
|
} catch {
|
|
13964
13968
|
console.log(`open ${path2} manually`);
|
|
13965
13969
|
}
|
|
@@ -14390,6 +14394,9 @@ project.command("set [owner/repo]").description("MASTER-ONLY: upsert a project M
|
|
|
14390
14394
|
} catch (e) {
|
|
14391
14395
|
return fail(e.message.replace(/^project set: /, "project set: "));
|
|
14392
14396
|
}
|
|
14397
|
+
const existing = await fetchProjectBySlug(slug, registryClientDeps(cfg));
|
|
14398
|
+
const boardError = boardLinkWriteError(patch, existing);
|
|
14399
|
+
if (boardError) return fail(`project set: ${boardError}`);
|
|
14393
14400
|
const res = await upsertProject(slug, patch, registryClientDeps(cfg));
|
|
14394
14401
|
return reportWrite("project set", res);
|
|
14395
14402
|
});
|
|
@@ -14666,39 +14673,37 @@ verify.command("synthesize").description("merge lens JSON array into a PanelRepo
|
|
|
14666
14673
|
return fail(`verify synthesize: ${e.message}`);
|
|
14667
14674
|
}
|
|
14668
14675
|
});
|
|
14669
|
-
var
|
|
14670
|
-
|
|
14671
|
-
|
|
14672
|
-
|
|
14673
|
-
|
|
14674
|
-
|
|
14675
|
-
|
|
14676
|
-
|
|
14677
|
-
|
|
14678
|
-
|
|
14679
|
-
|
|
14680
|
-
|
|
14681
|
-
|
|
14682
|
-
|
|
14683
|
-
|
|
14684
|
-
|
|
14685
|
-
|
|
14686
|
-
|
|
14687
|
-
});
|
|
14688
|
-
console.log(
|
|
14689
|
-
} catch (e) {
|
|
14690
|
-
return fail(`verify fusion plan: ${e.message}`);
|
|
14676
|
+
var build = program2.command("build").description("Build skill helpers \u2014 effort-tier selection and milestone partitioning");
|
|
14677
|
+
build.command("tier").description("Recommend an effort tier (light|standard|deep|max) from scope/risk/ambiguity signals").option("--scope <s>", "trivial|narrow|normal|wide|architectural").option("--risk <r>", "low|medium|high|critical").option("--ambiguity <a>", "low|medium|high").option("--blast-radius <b>", "isolated|module|cross-module|product").option("--foundational", "touches shared seams").option("--explicit <t>", "force a tier: light|standard|deep|max").option("--json", "output JSON").action((opts) => {
|
|
14678
|
+
const signals = {
|
|
14679
|
+
scope: opts.scope,
|
|
14680
|
+
risk: opts.risk,
|
|
14681
|
+
ambiguity: opts.ambiguity,
|
|
14682
|
+
blastRadius: opts.blastRadius,
|
|
14683
|
+
foundational: opts.foundational ?? void 0,
|
|
14684
|
+
explicitTier: opts.explicit
|
|
14685
|
+
};
|
|
14686
|
+
const decision = pickEffortTier(signals);
|
|
14687
|
+
if (opts.json) console.log(JSON.stringify(decision, null, 2));
|
|
14688
|
+
else {
|
|
14689
|
+
console.log(`tier: ${decision.tier}`);
|
|
14690
|
+
console.log(`reason: ${decision.reason}`);
|
|
14691
|
+
console.log(`agents: ${decision.budgets.agents}`);
|
|
14692
|
+
console.log(`planners: ${decision.budgets.planners}`);
|
|
14693
|
+
console.log(`parallel: ${decision.budgets.parallelSitesCap} sites`);
|
|
14694
|
+
console.log(`verify: ${decision.budgets.verificationDepth}`);
|
|
14695
|
+
console.log(`reasoning: ${decision.budgets.reasoningEffort}`);
|
|
14691
14696
|
}
|
|
14692
14697
|
});
|
|
14693
|
-
|
|
14694
|
-
|
|
14695
|
-
|
|
14696
|
-
|
|
14697
|
-
|
|
14698
|
-
|
|
14699
|
-
|
|
14700
|
-
|
|
14701
|
-
|
|
14698
|
+
build.command("plan").description("Partition a set of issue refs into sites + waves (parallel/serialize/batch) for milestone construction").argument("[issues...]", "issue refs like owner/repo#N or #N").option("--json", "output JSON").action((issues, opts) => {
|
|
14699
|
+
const plan2 = {
|
|
14700
|
+
waves: issues.length > 0 ? [{ wave: 0, mode: "parallel", issues }] : [],
|
|
14701
|
+
note: issues.length === 0 ? "No issues provided." : "v1 partitioning: single parallel wave. Use grind --auto Phase 00 patterns for deeper analysis."
|
|
14702
|
+
};
|
|
14703
|
+
if (opts.json) console.log(JSON.stringify(plan2, null, 2));
|
|
14704
|
+
else {
|
|
14705
|
+
console.log(plan2.note);
|
|
14706
|
+
for (const w of plan2.waves) console.log(`wave ${w.wave} (${w.mode}): ${w.issues.join(" ")}`);
|
|
14702
14707
|
}
|
|
14703
14708
|
});
|
|
14704
14709
|
program2.command("skill-lesson").description("file a skill-lesson on the Hub board (GitHub auth, dedups open lessons) and print {number,url} JSON").requiredOption("--skill <name>", `which skill misfired (${SKILL_NAMES.join(" | ")})`).option("--title <title>", "one-line summary of what misfired").option("--title-file <path|->", "read the one-line summary from a UTF-8 file, or from stdin with -").option("--body <body>", "lesson body: what misfired, the evidence, and the proposed amendment (markdown)").option("--body-file <path|->", "read the lesson body from a UTF-8 file, or from stdin with -").option("--priority <priority>", "urgent | high | medium | low (board Priority field when configured)", "medium").option("--repo <owner/repo>", `target repo (defaults to the org Hub: ${HUB_REPO})`).option("--force", "file a new issue even when an open lesson looks like a duplicate").option("--json", "machine-readable output (already the default \u2014 skill-lesson always prints JSON)").action(async (o) => {
|
|
@@ -16166,7 +16171,7 @@ program2.command("session-start").description("run the SessionStart verbs (rules
|
|
|
16166
16171
|
} catch (e) {
|
|
16167
16172
|
console.error(`[mmi-hook] saga session failed: ${e.message}`);
|
|
16168
16173
|
}
|
|
16169
|
-
spawnDetachedSelf(["docs", "sync", "--quiet"], { spawn:
|
|
16174
|
+
spawnDetachedSelf(["docs", "sync", "--quiet"], { spawn: import_node_child_process10.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
16170
16175
|
let northstarInjected = false;
|
|
16171
16176
|
const { parallel, sequential } = buildSessionStartPlan({
|
|
16172
16177
|
rulesSync: (io) => runRulesSync({ quiet: true }, io),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.33.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",
|