@delorenj/pjangler 1.5.50 → 1.5.52

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.js CHANGED
@@ -1435,6 +1435,11 @@ function resolveTicketProviderAdapter(provider, env2 = process.env) {
1435
1435
  candidates.push(join18(homedir7(), "code", "33GOD", relativeRoot, file));
1436
1436
  return candidates.find((candidate) => existsSync11(candidate));
1437
1437
  }
1438
+ function providerBoardName(provider, name) {
1439
+ if (provider !== "plane") return name;
1440
+ const cleaned = name.replace(/[^\p{L}\p{N}_\s]+/gu, " ").replace(/\s+/g, " ").trim();
1441
+ return cleaned || name;
1442
+ }
1438
1443
  function provisionTicketProviderBoard(action, env2 = process.env) {
1439
1444
  const provider = action.provider;
1440
1445
  const groups = ticketProviderKeyGroups(provider, action.workspace);
@@ -1775,11 +1780,11 @@ function planProjectInit(input) {
1775
1780
  workspace: project.ticket_provider.workspace ?? "33god",
1776
1781
  identifier,
1777
1782
  repoPath: targetDir,
1778
- boardName: project.name,
1783
+ boardName: providerBoardName(project.ticket_provider.type, project.name),
1779
1784
  description: project.description || `Ticket board for ${project.slug}`,
1780
1785
  boardId: project.ticket_provider.board_id ?? "",
1781
1786
  state: project.ticket_provider.board_id ? "linked" : "planned",
1782
- reason: skipPlane ? "ticket-provider action disabled by skipPlane=true (--skip-board)" : project.ticket_provider.board_id ? "board already linked; no provider call" : !provisionTicketBoard ? "ticket-provider action requires explicit provisionTicketBoard=true" : `create or link the ${project.ticket_provider.type} board "${project.name}" (${identifier}) via the ticket-provider adapter`
1787
+ reason: skipPlane ? "ticket-provider action disabled by skipPlane=true (--skip-board)" : project.ticket_provider.board_id ? "board already linked; no provider call" : !provisionTicketBoard ? "ticket-provider action requires explicit provisionTicketBoard=true" : `create or link the ${project.ticket_provider.type} board "${providerBoardName(project.ticket_provider.type, project.name)}" (${identifier}) via the ticket-provider adapter`
1783
1788
  }
1784
1789
  );
1785
1790
  return {
@@ -6128,6 +6133,15 @@ function isHostScoped(finding2) {
6128
6133
  function gatesProject(finding2) {
6129
6134
  return finding2.status === "fail" && !isHostScoped(finding2);
6130
6135
  }
6136
+ var FINDING_DETAIL_LIMIT = 20;
6137
+ function describeFinding(finding2) {
6138
+ const details = (finding2.details ?? []).map((line) => line.trimEnd()).filter((line) => line.trim().length > 0);
6139
+ if (details.length === 0) return `${finding2.id}: ${finding2.summary}`;
6140
+ const shown = details.slice(0, FINDING_DETAIL_LIMIT).map((line) => ` - ${line.replace(/\r?\n/g, "\n ")}`);
6141
+ const omitted = details.length - shown.length;
6142
+ if (omitted > 0) shown.push(` - (+${omitted} more; run pj audit --rules ${finding2.id} for the full list)`);
6143
+ return [`${finding2.id}: ${finding2.summary}`, ...shown].join("\n");
6144
+ }
6131
6145
 
6132
6146
  // src/recipes/Recipe.ts
6133
6147
  function commandStatus(result2, dryRun) {
@@ -6186,7 +6200,7 @@ var Recipe = class {
6186
6200
  }
6187
6201
  if (!finding2.fixable) {
6188
6202
  phases.push({ id: check.id, status: "failed", changedFiles: [], message: finding2.summary });
6189
- errors.push(`${check.id}: ${finding2.summary}`);
6203
+ errors.push(describeFinding({ id: check.id, summary: finding2.summary, details: finding2.details }));
6190
6204
  break;
6191
6205
  }
6192
6206
  const migrated = await check.migrate(ctx, finding2);
@@ -6196,7 +6210,7 @@ var Recipe = class {
6196
6210
  logs.push(`${check.id}: ${migrated.summary}`);
6197
6211
  changedFiles.push(...actualChanges);
6198
6212
  if (status === "failed") {
6199
- errors.push(`${check.id}: ${migrated.summary}`);
6213
+ errors.push(describeFinding({ id: check.id, summary: migrated.summary, details: migrated.details }));
6200
6214
  break;
6201
6215
  }
6202
6216
  if (!ctx.dryRun) {
@@ -7636,7 +7650,11 @@ function attestBmadInstallerFiles(inventory, base, files, label) {
7636
7650
 
7637
7651
  // src/parity/skill-roots.ts
7638
7652
  var CANONICAL_CLI_SKILLS_ALIAS = "../.agents/skills";
7639
- var SUPPORTED_SKILLS_ALIASES = SUPPORTED_CLIS.map((cli) => cli.skillsRoot);
7653
+ var SKILLEX_ONLY_ALIASES_AFTER = { ".kimi-code/skills": ".kimi/skills" };
7654
+ var SUPPORTED_SKILLS_ALIASES = SUPPORTED_CLIS.flatMap((cli) => {
7655
+ const follower = SKILLEX_ONLY_ALIASES_AFTER[cli.skillsRoot];
7656
+ return follower ? [cli.skillsRoot, follower] : [cli.skillsRoot];
7657
+ });
7640
7658
  var SKILL_ROOTS_QUARANTINE = ".pjangler-quarantine";
7641
7659
  function lstatOrUndefined(path) {
7642
7660
  try {
@@ -8812,7 +8830,7 @@ function readHandoffRecord(ctx) {
8812
8830
  }
8813
8831
  function writeHandoffRecord(ctx, names) {
8814
8832
  const path = handoffRecordPath(ctx);
8815
- mkdirSync5(dirname7(path), { recursive: true });
8833
+ mkdirSync5(dirname7(path), { recursive: true, mode: 448 });
8816
8834
  const temporary = `${path}.${process.pid}.tmp`;
8817
8835
  writeFileSync5(temporary, `${JSON.stringify({ project: realpathOf(ctx.repoRoot) ?? resolve5(ctx.repoRoot), excluded: [...new Set(names)].sort() }, null, 2)}
8818
8836
  `);
@@ -11343,6 +11361,15 @@ function lstatIfPresent(path) {
11343
11361
  throw error;
11344
11362
  }
11345
11363
  }
11364
+ var MOBILE_DEPLOY_HUB_CONTRACT = join12("tools", "ci", "mobile-deploy.json");
11365
+ function mobileDeployHubContract(repoRoot) {
11366
+ const path = join12(repoRoot, MOBILE_DEPLOY_HUB_CONTRACT);
11367
+ if (!existsSync8(path)) return void 0;
11368
+ const schema = record2(tryParseJson(safeReadText2(path))).schema;
11369
+ return {
11370
+ summary: `mobile-deploy-hub app (${MOBILE_DEPLOY_HUB_CONTRACT}${typeof schema === "number" ? `, schema ${schema}` : ""}): the hub is the version authority, so pjangler authors no version tasks, versioning script or version-files manifest here`
11371
+ };
11372
+ }
11346
11373
  function templateVersionFilesConf(ctx, repoRoot) {
11347
11374
  if (isObsidianPluginRepo(ctx, repoRoot)) return OBSIDIAN_VERSION_FILES_CONF;
11348
11375
  const packageJson = join12(repoRoot, "package.json");
@@ -13069,6 +13096,8 @@ function createMiseChecks() {
13069
13096
  id: "mise.versioning",
13070
13097
  title: "managed mise versioning block",
13071
13098
  audit: (ctx) => {
13099
+ const hub = mobileDeployHubContract(ctx.repoRoot);
13100
+ if (hub) return { id: "mise.versioning", title: "managed mise versioning block", status: "skip", summary: hub.summary, details: [], fixable: false };
13072
13101
  const details = [];
13073
13102
  const misePath = join12(ctx.repoRoot, "mise.toml");
13074
13103
  const versioningPath = join12(ctx.repoRoot, ".mise", "scripts", "versioning.sh");
@@ -13096,6 +13125,8 @@ function createMiseChecks() {
13096
13125
  };
13097
13126
  },
13098
13127
  migrate: (ctx, finding2) => {
13128
+ const hub = mobileDeployHubContract(ctx.repoRoot);
13129
+ if (hub) return { id: finding2.id, title: finding2.title, status: "skipped", summary: hub.summary, changedFiles: [], details: [] };
13099
13130
  const changedFiles = [];
13100
13131
  const details = [];
13101
13132
  const misePath = join12(ctx.repoRoot, "mise.toml");
@@ -17357,12 +17388,12 @@ var ProjectRecipe = class extends Recipe {
17357
17388
  changedFiles: result2.status === "applied" || result2.status === "partial" ? result2.changedFiles : [],
17358
17389
  message: result2.summary
17359
17390
  })));
17360
- errors.push(...migrationReport.results.filter((result2) => result2.status === "blocked" || result2.status === "partial").map((result2) => `${result2.id}: ${result2.summary}`));
17391
+ errors.push(...migrationReport.results.filter((result2) => result2.status === "blocked" || result2.status === "partial").map((result2) => describeFinding(result2)));
17361
17392
  }
17362
17393
  const eligibilityAudit = errors.length === 0 ? publicAudit(await this.registry.auditRecipes({ ...transactionContext, dryRun: true })) : void 0;
17363
17394
  audit = eligibilityAudit;
17364
17395
  if (eligibilityAudit && !eligibilityAudit.ok) {
17365
- errors.push(...eligibilityAudit.rules.filter((finding2) => gatesProject(finding2)).map((finding2) => `${finding2.id}: ${finding2.summary}`));
17396
+ errors.push(...eligibilityAudit.rules.filter((finding2) => gatesProject(finding2)).map((finding2) => describeFinding(finding2)));
17366
17397
  }
17367
17398
  phases.push({
17368
17399
  id: "project.audit:eligibility",
@@ -17453,7 +17484,7 @@ var ProjectRecipe = class extends Recipe {
17453
17484
  transactionContext.notebookManifestBinding = projectedNotebook?.binding ? Object.freeze({ ...projectedNotebook.binding }) : null;
17454
17485
  transactionContext.notebookObservation = Object.freeze(await notebookRecipe.observeExternal(plan, notebookPlan));
17455
17486
  const candidateAudit = publicAudit(await this.registry.auditRecipes(transactionContext, ["notebook"]));
17456
- if (!candidateAudit.ok) errors.push(...candidateAudit.rules.filter((item) => item.status === "fail" || item.status === "warn").map((item) => `${item.id}: ${item.summary}`));
17487
+ if (!candidateAudit.ok) errors.push(...candidateAudit.rules.filter((item) => item.status === "fail" || item.status === "warn").map((item) => describeFinding(item)));
17457
17488
  }
17458
17489
  phases.push({
17459
17490
  id: "project.external:notebook",
@@ -17509,7 +17540,7 @@ var ProjectRecipe = class extends Recipe {
17509
17540
  }
17510
17541
  audit = errors.length === 0 ? publicAudit(await this.registry.auditRecipes({ ...transactionContext, dryRun: true })) : audit;
17511
17542
  if (errors.length === 0 && audit && !audit.ok) {
17512
- errors.push(...audit.rules.filter((finding2) => gatesProject(finding2)).map((finding2) => `${finding2.id}: ${finding2.summary}`));
17543
+ errors.push(...audit.rules.filter((finding2) => gatesProject(finding2)).map((finding2) => describeFinding(finding2)));
17513
17544
  }
17514
17545
  phases.push({
17515
17546
  id: "project.audit",
@@ -23120,11 +23151,19 @@ async function promptTextValue(message2, initialValue) {
23120
23151
  }
23121
23152
  return value.trim();
23122
23153
  }
23123
- function reportBoardDelivery(board, slug, skipRequested) {
23154
+ function reportBoardDelivery(board, slug, skipRequested, reached = true) {
23124
23155
  if (board.confirmed) {
23125
23156
  console.log(` ${green(glyph.pass)} ${bold("Board linked")} ${dim(glyph.dot)} ${cyan(`${board.provider}/${board.workspace}/${board.identifier}`)} ${dim(board.boardId)}`);
23126
23157
  return true;
23127
23158
  }
23159
+ if (!reached && !skipRequested) {
23160
+ console.error("");
23161
+ console.error(` ${xmark2} ${bold("Board step not reached")} ${dim(glyph.dot)} ${cyan(slug)}`);
23162
+ console.error(` ${dim("project init failed before the ticket-provider phase; no board was created or looked up")}`);
23163
+ console.error(` ${dim("fix the error(s) above, then re-run the same command. The provider credential is not the cause.")}`);
23164
+ console.error("");
23165
+ return false;
23166
+ }
23128
23167
  const mark = skipRequested ? `${yellow(glyph.warn)}` : xmark2;
23129
23168
  const title = skipRequested ? "No ticket board (--skip-board)" : "No ticket board";
23130
23169
  const log = skipRequested ? console.log : console.error;
@@ -23516,8 +23555,9 @@ async function runProjectInit(name, options) {
23516
23555
  const board = boardDelivery({ ...result2.plan, actions: plan.actions });
23517
23556
  const skipRequested = Boolean(options.skipBoard);
23518
23557
  const boardDelivered = board.confirmed || skipRequested;
23558
+ const boardStepReached = result2.phases.some((phase) => phase.id === "project.external:ticket-provider");
23519
23559
  if (options.json) {
23520
- console.log(JSON.stringify({ ...result2, ok: result2.ok && boardDelivered, board }, null, 2));
23560
+ console.log(JSON.stringify({ ...result2, ok: result2.ok && boardDelivered, board: { ...board, reached: boardStepReached } }, null, 2));
23521
23561
  } else {
23522
23562
  console.log(formatProjectInitPlan(selectedPlan));
23523
23563
  for (const line of result2.logs) console.log(line);
@@ -23525,7 +23565,7 @@ async function runProjectInit(name, options) {
23525
23565
  if (result2.migrationReport) console.log(formatMigrationReport(result2.migrationReport));
23526
23566
  if (result2.ok && result2.changedFiles.length) console.log(` ${green(glyph.pass)} ${bold("Project synchronized")} ${dim(glyph.dot)} ${cyan(plan.project.slug)}`);
23527
23567
  if (result2.ok && result2.changedFiles.length === 0) console.log(` ${green(glyph.pass)} ${dim("Already in parity")} ${dim(glyph.dot)} ${cyan(plan.project.slug)}`);
23528
- reportBoardDelivery(board, plan.project.slug, skipRequested);
23568
+ reportBoardDelivery(board, plan.project.slug, skipRequested, boardStepReached || result2.ok);
23529
23569
  const rendered = selectedPlan.actions.find((action) => action.kind === "copier.copy.commonproject");
23530
23570
  if (result2.ok && rendered?.kind === "copier.copy.commonproject" && rendered.data.project_type === "obsidian-plugin") {
23531
23571
  console.log(` ${bold("Next")} ${dim(`cd ${target.targetDir}`)}`);
@@ -1295,6 +1295,11 @@ function resolveTicketProviderAdapter(provider, env2 = process.env) {
1295
1295
  candidates.push(join17(homedir5(), "code", "33GOD", relativeRoot, file));
1296
1296
  return candidates.find((candidate) => existsSync11(candidate));
1297
1297
  }
1298
+ function providerBoardName(provider, name) {
1299
+ if (provider !== "plane") return name;
1300
+ const cleaned = name.replace(/[^\p{L}\p{N}_\s]+/gu, " ").replace(/\s+/g, " ").trim();
1301
+ return cleaned || name;
1302
+ }
1298
1303
  function provisionTicketProviderBoard(action, env2 = process.env) {
1299
1304
  const provider = action.provider;
1300
1305
  const groups = ticketProviderKeyGroups(provider, action.workspace);
@@ -1646,11 +1651,11 @@ function planProjectInit(input) {
1646
1651
  workspace: project.ticket_provider.workspace ?? "33god",
1647
1652
  identifier,
1648
1653
  repoPath: targetDir,
1649
- boardName: project.name,
1654
+ boardName: providerBoardName(project.ticket_provider.type, project.name),
1650
1655
  description: project.description || `Ticket board for ${project.slug}`,
1651
1656
  boardId: project.ticket_provider.board_id ?? "",
1652
1657
  state: project.ticket_provider.board_id ? "linked" : "planned",
1653
- reason: skipPlane ? "ticket-provider action disabled by skipPlane=true (--skip-board)" : project.ticket_provider.board_id ? "board already linked; no provider call" : !provisionTicketBoard ? "ticket-provider action requires explicit provisionTicketBoard=true" : `create or link the ${project.ticket_provider.type} board "${project.name}" (${identifier}) via the ticket-provider adapter`
1658
+ reason: skipPlane ? "ticket-provider action disabled by skipPlane=true (--skip-board)" : project.ticket_provider.board_id ? "board already linked; no provider call" : !provisionTicketBoard ? "ticket-provider action requires explicit provisionTicketBoard=true" : `create or link the ${project.ticket_provider.type} board "${providerBoardName(project.ticket_provider.type, project.name)}" (${identifier}) via the ticket-provider adapter`
1654
1659
  }
1655
1660
  );
1656
1661
  return {
@@ -5360,6 +5365,15 @@ function isHostScoped(finding2) {
5360
5365
  function gatesProject(finding2) {
5361
5366
  return finding2.status === "fail" && !isHostScoped(finding2);
5362
5367
  }
5368
+ var FINDING_DETAIL_LIMIT = 20;
5369
+ function describeFinding(finding2) {
5370
+ const details = (finding2.details ?? []).map((line) => line.trimEnd()).filter((line) => line.trim().length > 0);
5371
+ if (details.length === 0) return `${finding2.id}: ${finding2.summary}`;
5372
+ const shown = details.slice(0, FINDING_DETAIL_LIMIT).map((line) => ` - ${line.replace(/\r?\n/g, "\n ")}`);
5373
+ const omitted = details.length - shown.length;
5374
+ if (omitted > 0) shown.push(` - (+${omitted} more; run pj audit --rules ${finding2.id} for the full list)`);
5375
+ return [`${finding2.id}: ${finding2.summary}`, ...shown].join("\n");
5376
+ }
5363
5377
 
5364
5378
  // src/recipes/Recipe.ts
5365
5379
  function commandStatus(result2, dryRun) {
@@ -5418,7 +5432,7 @@ var Recipe = class {
5418
5432
  }
5419
5433
  if (!finding2.fixable) {
5420
5434
  phases.push({ id: check.id, status: "failed", changedFiles: [], message: finding2.summary });
5421
- errors.push(`${check.id}: ${finding2.summary}`);
5435
+ errors.push(describeFinding({ id: check.id, summary: finding2.summary, details: finding2.details }));
5422
5436
  break;
5423
5437
  }
5424
5438
  const migrated = await check.migrate(ctx, finding2);
@@ -5428,7 +5442,7 @@ var Recipe = class {
5428
5442
  logs.push(`${check.id}: ${migrated.summary}`);
5429
5443
  changedFiles.push(...actualChanges);
5430
5444
  if (status === "failed") {
5431
- errors.push(`${check.id}: ${migrated.summary}`);
5445
+ errors.push(describeFinding({ id: check.id, summary: migrated.summary, details: migrated.details }));
5432
5446
  break;
5433
5447
  }
5434
5448
  if (!ctx.dryRun) {
@@ -6868,7 +6882,11 @@ function attestBmadInstallerFiles(inventory, base, files, label) {
6868
6882
 
6869
6883
  // src/parity/skill-roots.ts
6870
6884
  var CANONICAL_CLI_SKILLS_ALIAS = "../.agents/skills";
6871
- var SUPPORTED_SKILLS_ALIASES = SUPPORTED_CLIS.map((cli) => cli.skillsRoot);
6885
+ var SKILLEX_ONLY_ALIASES_AFTER = { ".kimi-code/skills": ".kimi/skills" };
6886
+ var SUPPORTED_SKILLS_ALIASES = SUPPORTED_CLIS.flatMap((cli) => {
6887
+ const follower = SKILLEX_ONLY_ALIASES_AFTER[cli.skillsRoot];
6888
+ return follower ? [cli.skillsRoot, follower] : [cli.skillsRoot];
6889
+ });
6872
6890
  var SKILL_ROOTS_QUARANTINE = ".pjangler-quarantine";
6873
6891
  function lstatOrUndefined(path) {
6874
6892
  try {
@@ -7975,7 +7993,7 @@ function readHandoffRecord(ctx) {
7975
7993
  }
7976
7994
  function writeHandoffRecord(ctx, names) {
7977
7995
  const path = handoffRecordPath(ctx);
7978
- mkdirSync5(dirname6(path), { recursive: true });
7996
+ mkdirSync5(dirname6(path), { recursive: true, mode: 448 });
7979
7997
  const temporary = `${path}.${process.pid}.tmp`;
7980
7998
  writeFileSync5(temporary, `${JSON.stringify({ project: realpathOf(ctx.repoRoot) ?? resolve4(ctx.repoRoot), excluded: [...new Set(names)].sort() }, null, 2)}
7981
7999
  `);
@@ -10506,6 +10524,15 @@ function lstatIfPresent(path) {
10506
10524
  throw error;
10507
10525
  }
10508
10526
  }
10527
+ var MOBILE_DEPLOY_HUB_CONTRACT = join12("tools", "ci", "mobile-deploy.json");
10528
+ function mobileDeployHubContract(repoRoot) {
10529
+ const path = join12(repoRoot, MOBILE_DEPLOY_HUB_CONTRACT);
10530
+ if (!existsSync8(path)) return void 0;
10531
+ const schema = record2(tryParseJson(safeReadText2(path))).schema;
10532
+ return {
10533
+ summary: `mobile-deploy-hub app (${MOBILE_DEPLOY_HUB_CONTRACT}${typeof schema === "number" ? `, schema ${schema}` : ""}): the hub is the version authority, so pjangler authors no version tasks, versioning script or version-files manifest here`
10534
+ };
10535
+ }
10509
10536
  function templateVersionFilesConf(ctx, repoRoot) {
10510
10537
  if (isObsidianPluginRepo(ctx, repoRoot)) return OBSIDIAN_VERSION_FILES_CONF;
10511
10538
  const packageJson = join12(repoRoot, "package.json");
@@ -11880,6 +11907,8 @@ function createMiseChecks() {
11880
11907
  id: "mise.versioning",
11881
11908
  title: "managed mise versioning block",
11882
11909
  audit: (ctx) => {
11910
+ const hub = mobileDeployHubContract(ctx.repoRoot);
11911
+ if (hub) return { id: "mise.versioning", title: "managed mise versioning block", status: "skip", summary: hub.summary, details: [], fixable: false };
11883
11912
  const details = [];
11884
11913
  const misePath = join12(ctx.repoRoot, "mise.toml");
11885
11914
  const versioningPath = join12(ctx.repoRoot, ".mise", "scripts", "versioning.sh");
@@ -11907,6 +11936,8 @@ function createMiseChecks() {
11907
11936
  };
11908
11937
  },
11909
11938
  migrate: (ctx, finding2) => {
11939
+ const hub = mobileDeployHubContract(ctx.repoRoot);
11940
+ if (hub) return { id: finding2.id, title: finding2.title, status: "skipped", summary: hub.summary, changedFiles: [], details: [] };
11910
11941
  const changedFiles = [];
11911
11942
  const details = [];
11912
11943
  const misePath = join12(ctx.repoRoot, "mise.toml");
@@ -15851,12 +15882,12 @@ var ProjectRecipe = class extends Recipe {
15851
15882
  changedFiles: result2.status === "applied" || result2.status === "partial" ? result2.changedFiles : [],
15852
15883
  message: result2.summary
15853
15884
  })));
15854
- errors.push(...migrationReport.results.filter((result2) => result2.status === "blocked" || result2.status === "partial").map((result2) => `${result2.id}: ${result2.summary}`));
15885
+ errors.push(...migrationReport.results.filter((result2) => result2.status === "blocked" || result2.status === "partial").map((result2) => describeFinding(result2)));
15855
15886
  }
15856
15887
  const eligibilityAudit = errors.length === 0 ? publicAudit(await this.registry.auditRecipes({ ...transactionContext, dryRun: true })) : void 0;
15857
15888
  audit = eligibilityAudit;
15858
15889
  if (eligibilityAudit && !eligibilityAudit.ok) {
15859
- errors.push(...eligibilityAudit.rules.filter((finding2) => gatesProject(finding2)).map((finding2) => `${finding2.id}: ${finding2.summary}`));
15890
+ errors.push(...eligibilityAudit.rules.filter((finding2) => gatesProject(finding2)).map((finding2) => describeFinding(finding2)));
15860
15891
  }
15861
15892
  phases.push({
15862
15893
  id: "project.audit:eligibility",
@@ -15947,7 +15978,7 @@ var ProjectRecipe = class extends Recipe {
15947
15978
  transactionContext.notebookManifestBinding = projectedNotebook?.binding ? Object.freeze({ ...projectedNotebook.binding }) : null;
15948
15979
  transactionContext.notebookObservation = Object.freeze(await notebookRecipe.observeExternal(plan, notebookPlan));
15949
15980
  const candidateAudit = publicAudit(await this.registry.auditRecipes(transactionContext, ["notebook"]));
15950
- if (!candidateAudit.ok) errors.push(...candidateAudit.rules.filter((item) => item.status === "fail" || item.status === "warn").map((item) => `${item.id}: ${item.summary}`));
15981
+ if (!candidateAudit.ok) errors.push(...candidateAudit.rules.filter((item) => item.status === "fail" || item.status === "warn").map((item) => describeFinding(item)));
15951
15982
  }
15952
15983
  phases.push({
15953
15984
  id: "project.external:notebook",
@@ -16003,7 +16034,7 @@ var ProjectRecipe = class extends Recipe {
16003
16034
  }
16004
16035
  audit = errors.length === 0 ? publicAudit(await this.registry.auditRecipes({ ...transactionContext, dryRun: true })) : audit;
16005
16036
  if (errors.length === 0 && audit && !audit.ok) {
16006
- errors.push(...audit.rules.filter((finding2) => gatesProject(finding2)).map((finding2) => `${finding2.id}: ${finding2.summary}`));
16037
+ errors.push(...audit.rules.filter((finding2) => gatesProject(finding2)).map((finding2) => describeFinding(finding2)));
16007
16038
  }
16008
16039
  phases.push({
16009
16040
  id: "project.audit",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delorenj/pjangler",
3
- "version": "1.5.50",
3
+ "version": "1.5.52",
4
4
  "description": "Project subsystem bootstrapper CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -61,7 +61,7 @@
61
61
  "license": "MIT",
62
62
  "dependencies": {
63
63
  "@clack/prompts": "^1.4.0",
64
- "@delorenj/skillex": "0.3.0",
64
+ "@delorenj/skillex": "0.4.4",
65
65
  "@modelcontextprotocol/sdk": "^1.29.0",
66
66
  "commander": "^14.0.2",
67
67
  "ink": "^8.0.0",