@launchsecure/launch-kit 0.0.31 → 0.0.32

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.
Files changed (69) hide show
  1. package/dist/beacon/beacon.mjs +550 -521
  2. package/dist/beacon/beacon.mjs.map +1 -1
  3. package/dist/beacon/beacon.umd.js +9 -9
  4. package/dist/beacon/beacon.umd.js.map +1 -1
  5. package/dist/beacon/types/internal/pick-mode-overlay.d.ts.map +1 -1
  6. package/dist/beacon/types/internal/picker.d.ts.map +1 -1
  7. package/dist/beacon/types/internal/pin-popover.d.ts.map +1 -1
  8. package/dist/beacon/types/internal/selector.d.ts.map +1 -1
  9. package/dist/deck-client/assets/{_baseUniq-DdHaBFYO.js → _baseUniq-C7GsHvgg.js} +1 -1
  10. package/dist/deck-client/assets/{arc-D98e_18X.js → arc-CSrZRINY.js} +1 -1
  11. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-DNFZzh-4.js → architectureDiagram-Q4EWVU46-zoB-G17J.js} +1 -1
  12. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-DeQvGUdX.js → blockDiagram-DXYQGD6D-BRjjtYH6.js} +1 -1
  13. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-B6ekZf1n.js → c4Diagram-AHTNJAMY-C3D3sd2U.js} +1 -1
  14. package/dist/deck-client/assets/channel-8ReQnQfH.js +1 -0
  15. package/dist/deck-client/assets/{chunk-4BX2VUAB-9aDWymq2.js → chunk-4BX2VUAB-DhpDMOPO.js} +1 -1
  16. package/dist/deck-client/assets/{chunk-4TB4RGXK-DtKQqaI7.js → chunk-4TB4RGXK-BIRgPXRl.js} +1 -1
  17. package/dist/deck-client/assets/{chunk-55IACEB6-COy9hEae.js → chunk-55IACEB6-BF24dwDZ.js} +1 -1
  18. package/dist/deck-client/assets/{chunk-EDXVE4YY-D_f861An.js → chunk-EDXVE4YY-CW75Y61B.js} +1 -1
  19. package/dist/deck-client/assets/{chunk-FMBD7UC4-CmuA5UKn.js → chunk-FMBD7UC4-B5-oyL79.js} +1 -1
  20. package/dist/deck-client/assets/{chunk-OYMX7WX6-vT8z8D-0.js → chunk-OYMX7WX6-BB2bHe_Q.js} +1 -1
  21. package/dist/deck-client/assets/{chunk-QZHKN3VN-CTlwwg-R.js → chunk-QZHKN3VN-D80eZO4B.js} +1 -1
  22. package/dist/deck-client/assets/{chunk-YZCP3GAM-C44yr620.js → chunk-YZCP3GAM-Dz9787p_.js} +1 -1
  23. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-cRxTeGkK.js +1 -0
  24. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-cRxTeGkK.js +1 -0
  25. package/dist/deck-client/assets/clone-LSHZ3K6R.js +1 -0
  26. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-DBB2J2nL.js → cose-bilkent-S5V4N54A-MQjiZLcL.js} +1 -1
  27. package/dist/deck-client/assets/{dagre-KV5264BT-DxDTYbKl.js → dagre-KV5264BT-DG4EcLpJ.js} +1 -1
  28. package/dist/deck-client/assets/{diagram-5BDNPKRD-DByWrWd1.js → diagram-5BDNPKRD-1n7hM3Gc.js} +1 -1
  29. package/dist/deck-client/assets/{diagram-G4DWMVQ6-B8B6ddMq.js → diagram-G4DWMVQ6-CYMarncV.js} +1 -1
  30. package/dist/deck-client/assets/{diagram-MMDJMWI5-BMUZ2PWK.js → diagram-MMDJMWI5-DSisoipe.js} +1 -1
  31. package/dist/deck-client/assets/{diagram-TYMM5635-Bk9e8BB-.js → diagram-TYMM5635-Btnq49OJ.js} +1 -1
  32. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-DcOSwSol.js → erDiagram-SMLLAGMA-Cu2Hb_Tz.js} +1 -1
  33. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-DI-4BR0F.js → flowDiagram-DWJPFMVM-CGJzUzsO.js} +1 -1
  34. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-BeZuXBoU.js → ganttDiagram-T4ZO3ILL-D9sqGUBT.js} +1 -1
  35. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-Bcki__f-.js → gitGraphDiagram-UUTBAWPF-C0QwX2od.js} +1 -1
  36. package/dist/deck-client/assets/{graph-CifKx6G1.js → graph-CcBjOQCl.js} +1 -1
  37. package/dist/deck-client/assets/{index-CB-qlwRT.js → index-0arwoc0z.js} +76 -76
  38. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-CReN1nFN.js → infoDiagram-42DDH7IO-DTimhhhS.js} +1 -1
  39. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-CDF_VLN_.js → ishikawaDiagram-UXIWVN3A-DxOxg_B4.js} +1 -1
  40. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-DwgGrNVB.js → journeyDiagram-VCZTEJTY-Bpq0qa4j.js} +1 -1
  41. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-DB_zohh5.js → kanban-definition-6JOO6SKY-aTIrpcVO.js} +1 -1
  42. package/dist/deck-client/assets/{layout-DFfX1O3z.js → layout-DqglLR2E.js} +1 -1
  43. package/dist/deck-client/assets/{linear-CtKb4EXj.js → linear-D5GxehPc.js} +1 -1
  44. package/dist/deck-client/assets/{min-DCRRwUZv.js → min-DXLfSREq.js} +1 -1
  45. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-D0QBOiFe.js → mindmap-definition-QFDTVHPH-mO5Vys7I.js} +1 -1
  46. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-CD-EV5WB.js → pieDiagram-DEJITSTG-Dm0gzdAr.js} +1 -1
  47. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-B-JXZ8xI.js → quadrantDiagram-34T5L4WZ-Daq7j3qD.js} +1 -1
  48. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-D2_OK5Dp.js → requirementDiagram-MS252O5E-CmwV95um.js} +1 -1
  49. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BbBJqVSC.js → sankeyDiagram-XADWPNL6-BOYl3Nkf.js} +1 -1
  50. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-Db8A-Rkk.js → sequenceDiagram-FGHM5R23-BuUjhIcW.js} +1 -1
  51. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-DGJnanjS.js → stateDiagram-FHFEXIEX-LUZ_uwio.js} +1 -1
  52. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CnnRwE5D.js +1 -0
  53. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-BRkr6T4w.js → timeline-definition-GMOUNBTQ-CDUxCCAW.js} +1 -1
  54. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-d0rsTqFo.js → vennDiagram-DHZGUBPP-BRb24Tf7.js} +1 -1
  55. package/dist/deck-client/assets/wardley-RL74JXVD-B0BYyVBY.js +162 -0
  56. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-DzboAsHh.js → wardleyDiagram-NUSXRM2D-BLGlYrQz.js} +1 -1
  57. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-CgTP9u2V.js → xychartDiagram-5P7HB3ND-De31MSnk.js} +1 -1
  58. package/dist/deck-client/index.html +1 -1
  59. package/dist/server/deck-mcp-entry.js +83 -40
  60. package/dist/server/deck-serve.js +54 -20
  61. package/dist/server/init-entry.js +216 -73
  62. package/package.json +1 -1
  63. package/scaffolds/ls-marketplace/plugins/kit/skills/diagram/SKILL.md +27 -5
  64. package/dist/deck-client/assets/channel-DmR7Tyyt.js +0 -1
  65. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-Bl4ozQWs.js +0 -1
  66. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-Bl4ozQWs.js +0 -1
  67. package/dist/deck-client/assets/clone-BAy58j24.js +0 -1
  68. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CR7riiab.js +0 -1
  69. package/dist/deck-client/assets/wardley-RL74JXVD-2t7cMqdS.js +0 -162
@@ -295,6 +295,50 @@ function recoverCred(targetDir, opts) {
295
295
  init_statusline_install();
296
296
  var DEFAULT_SERVER_URL = "https://launchsecure-v2.vercel.app";
297
297
  var ONBOARD_SCRIPT_NAME = "onboard";
298
+ var TTY = process.stdout.isTTY === true && process.env.NO_COLOR !== "1";
299
+ var c = {
300
+ dim: (s) => TTY ? `\x1B[2m${s}\x1B[0m` : s,
301
+ bold: (s) => TTY ? `\x1B[1m${s}\x1B[0m` : s,
302
+ cyan: (s) => TTY ? `\x1B[36m${s}\x1B[0m` : s,
303
+ green: (s) => TTY ? `\x1B[32m${s}\x1B[0m` : s,
304
+ yellow: (s) => TTY ? `\x1B[33m${s}\x1B[0m` : s,
305
+ red: (s) => TTY ? `\x1B[31m${s}\x1B[0m` : s
306
+ };
307
+ var PHASE_NAME_COL = 16;
308
+ var HEADER_WIDTH = 64;
309
+ function header(title, kv) {
310
+ if (VERBOSE) return;
311
+ const dashes = "\u2500".repeat(Math.max(3, HEADER_WIDTH - title.length - 5));
312
+ console.log("");
313
+ console.log(c.cyan(`\u2500\u2500\u2500 ${c.bold(title)} ${dashes}`));
314
+ if (kv.length > 0) {
315
+ const labelWidth = Math.max(...kv.map(([k]) => k.length));
316
+ for (const [k, v] of kv) console.log(` ${c.dim(k.padEnd(labelWidth))} ${v}`);
317
+ }
318
+ console.log("");
319
+ }
320
+ function phase(name, result) {
321
+ if (VERBOSE) return;
322
+ const mark = result.status === "ok" ? c.green("\u2713") : result.status === "in-sync" ? c.dim("\xB7") : result.status === "skipped" ? c.dim("\u2212") : c.yellow("\u26A0");
323
+ const summary = result.status === "in-sync" || result.status === "skipped" ? c.dim(result.summary) : result.summary;
324
+ const label = name.padEnd(PHASE_NAME_COL);
325
+ console.log(` ${result.status === "warn" ? c.yellow(label) : label} ${mark} ${summary}`);
326
+ }
327
+ function section(title) {
328
+ if (VERBOSE) return;
329
+ console.log("");
330
+ console.log(` ${c.dim("\u2500 " + title + " \u2500")}`);
331
+ }
332
+ function footer(msg, hints = []) {
333
+ if (VERBOSE) return;
334
+ console.log("");
335
+ console.log(` ${c.bold(msg)}`);
336
+ for (const h of hints) console.log(` ${c.dim(h)}`);
337
+ console.log("");
338
+ }
339
+ function warn(msg) {
340
+ console.log(` ${c.yellow("\u26A0")} ${msg}`);
341
+ }
298
342
  var LAUNCH_KIT_PKG = "@launchsecure/launch-kit";
299
343
  var LAUNCH_KIT_TOOLS_GUIDE_STATIC_HEAD = `
300
344
  Wired in Claude Code (.mcp.json):
@@ -369,7 +413,10 @@ var KNOWN_BOOL_FLAGS = /* @__PURE__ */ new Set([
369
413
  "--refresh-scaffolds",
370
414
  "--quiet",
371
415
  "--force",
372
- "--dry-run"
416
+ "--dry-run",
417
+ "--verbose",
418
+ "--guide",
419
+ "--no-guide"
373
420
  ]);
374
421
  var KNOWN_KV_KEYS = /* @__PURE__ */ new Set(["token", "org", "project", "url", "dir", "course"]);
375
422
  function parseArgs(argv) {
@@ -390,6 +437,8 @@ function parseArgs(argv) {
390
437
  quiet: false,
391
438
  force: false,
392
439
  dryRun: false,
440
+ verbose: false,
441
+ guide: null,
393
442
  help: false
394
443
  };
395
444
  const unknown = [];
@@ -440,6 +489,18 @@ function parseArgs(argv) {
440
489
  args.dryRun = true;
441
490
  continue;
442
491
  }
492
+ if (raw === "--verbose") {
493
+ args.verbose = true;
494
+ continue;
495
+ }
496
+ if (raw === "--guide") {
497
+ args.guide = true;
498
+ continue;
499
+ }
500
+ if (raw === "--no-guide") {
501
+ args.guide = false;
502
+ continue;
503
+ }
443
504
  const eq = raw.indexOf("=");
444
505
  if (raw.startsWith("--") && eq > 0) {
445
506
  const key = raw.slice(2, eq);
@@ -511,8 +572,15 @@ Options:
511
572
  --refresh-scaffolds Force-overwrite migrate-safety files (default is to
512
573
  preserve user edits). Use this to pull updates
513
574
  published to @launchsecure/launch-kit.
514
- --quiet Suppress the post-run tools guide.
575
+ --verbose Print the legacy per-line debug log instead of the
576
+ default compact phase summary. Useful when refresh
577
+ reports a warn-status phase and you want detail.
578
+ --guide Print the trailing tools guide (MCP table + every
579
+ /kit:* command). Off by default on refresh.
580
+ --quiet Suppress the tools guide and the next-steps hint.
581
+ Overrides --guide.
515
582
  --dry-run Preview every file write without making changes.
583
+ Implies --verbose.
516
584
  --help Show this help.
517
585
 
518
586
  Tip: prefix the command with @latest (\`launch-kit@latest refresh\`) to force
@@ -570,9 +638,17 @@ Options:
570
638
  user edits; use this to pull updates published to
571
639
  @launchsecure/launch-kit (e.g., a newer
572
640
  migrate-with-backup.sh).
573
- --quiet Suppress the post-run tools guide (the long block
574
- listing /kit:* commands and wired MCPs). Useful for
575
- idempotent re-runs in CI or scripts.
641
+ --verbose Print the legacy per-line debug log instead of the
642
+ default compact phase summary. Useful when a phase
643
+ reports a warn status and you want per-file detail.
644
+ --guide Print the trailing tools guide (MCP table + every
645
+ /kit:* command). On by default for init; pair with
646
+ --no-guide to suppress.
647
+ --no-guide Suppress the trailing tools guide (handy after the
648
+ first init, when you don't need the catalog again).
649
+ --quiet Suppress the post-run tools guide AND the next-steps
650
+ hint. Useful for idempotent re-runs in CI or scripts.
651
+ Overrides --guide.
576
652
  --force Skip the auto-delegate-to-refresh check. By default
577
653
  init detects an existing bootstrap (cred file +
578
654
  launch-secure MCP entry) and runs refresh instead.
@@ -623,11 +699,12 @@ function fail(msg) {
623
699
  console.error(`[launch-kit] \u2717 ${msg}`);
624
700
  process.exit(1);
625
701
  }
702
+ var VERBOSE = false;
626
703
  function info(msg) {
627
- console.log(`[launch-kit] ${msg}`);
704
+ if (VERBOSE) console.log(`[launch-kit] ${msg}`);
628
705
  }
629
706
  function ok(msg) {
630
- console.log(`[launch-kit] \u2713 ${msg}`);
707
+ if (VERBOSE) console.log(`[launch-kit] \u2713 ${msg}`);
631
708
  }
632
709
  var DRY_RUN = false;
633
710
  function dryNote(msg) {
@@ -685,7 +762,7 @@ function attemptProjectInfo(args) {
685
762
  },
686
763
  (res) => {
687
764
  const chunks = [];
688
- res.on("data", (c) => chunks.push(c));
765
+ res.on("data", (c2) => chunks.push(c2));
689
766
  res.on("end", () => {
690
767
  const text = Buffer.concat(chunks).toString("utf-8");
691
768
  if (res.statusCode === 401) {
@@ -936,13 +1013,15 @@ function mergeMcpFile(targetDir, launchKitEntries) {
936
1013
  }
937
1014
  }
938
1015
  if (DRY_RUN) {
939
- const action2 = hadExisting && existingServerCount > 0 ? "would merge into" : "would write";
940
- dryNote(`${action2} .mcp.json \u2014 overwriting [${overwrites.join(", ") || "none"}], adding [${additions.join(", ") || "none"}]`);
941
- return;
1016
+ const action = hadExisting && existingServerCount > 0 ? "would merge into" : "would write";
1017
+ dryNote(`${action} .mcp.json \u2014 overwriting [${overwrites.join(", ") || "none"}], adding [${additions.join(", ") || "none"}]`);
1018
+ return { status: "skipped", summary: "(dry-run)" };
942
1019
  }
943
1020
  fs4.writeFileSync(p, JSON.stringify(merged, null, 2) + "\n", "utf-8");
944
- const action = hadExisting && existingServerCount > 0 ? "merged into" : "wrote";
945
- ok(`${action} .mcp.json (${Object.keys(launchKitEntries).length} launch-kit entries)`);
1021
+ const verb = hadExisting && existingServerCount > 0 ? "merged" : "wrote";
1022
+ ok(`${verb === "merged" ? "merged into" : "wrote"} .mcp.json (${Object.keys(launchKitEntries).length} launch-kit entries)`);
1023
+ const total = Object.keys(launchKitEntries).length;
1024
+ return { status: "ok", summary: `${verb} ${total} entries` };
946
1025
  }
947
1026
  function detectPackageManager(repoDir) {
948
1027
  const pkgPath = path4.join(repoDir, "package.json");
@@ -1093,8 +1172,8 @@ function copyScaffoldDirAlways(srcDir, destDir, labelPrefix) {
1093
1172
  function scaffoldMigrateSafety(targetDir, refreshScaffolds = false) {
1094
1173
  const scaffoldsRoot = path4.resolve(__dirname, "..", "..", "scaffolds", "migrate-safety");
1095
1174
  if (!fs4.existsSync(scaffoldsRoot)) {
1096
- info(`\u26A0 migrate-safety scaffolds not found at ${scaffoldsRoot} \u2014 skipping (this is a packaging bug; main onboarding is unaffected)`);
1097
- return;
1175
+ warn(`migrate-safety scaffolds missing at ${scaffoldsRoot} \u2014 packaging bug; main onboarding unaffected`);
1176
+ return { status: "warn", summary: "scaffolds missing (packaging bug)" };
1098
1177
  }
1099
1178
  const files = [
1100
1179
  {
@@ -1114,9 +1193,33 @@ function scaffoldMigrateSafety(targetDir, refreshScaffolds = false) {
1114
1193
  }
1115
1194
  ];
1116
1195
  info(`scaffolding migrate-safety (pg_dump wrapper + GHA backup workflow + runbook)${refreshScaffolds ? " \u2014 --refresh-scaffolds active" : ""} \u2026`);
1117
- for (const f of files) copyScaffoldDriftAware(f.src, f.dest, f.label, refreshScaffolds);
1196
+ const counts = { wrote: 0, inSync: 0, drifted: 0, refreshed: 0, missing: 0 };
1197
+ for (const f of files) {
1198
+ const r = copyScaffoldDriftAware(f.src, f.dest, f.label, refreshScaffolds);
1199
+ if (r === "wrote") counts.wrote++;
1200
+ else if (r === "in-sync") counts.inSync++;
1201
+ else if (r === "drifted-preserved") counts.drifted++;
1202
+ else if (r === "drifted-refreshed") counts.refreshed++;
1203
+ else counts.missing++;
1204
+ }
1118
1205
  ensureGitignoreLine(targetDir, ".backups/");
1119
1206
  ok("migrate-safety ready \u2014 see docs/migrations-runbook.md for db:migrate wiring + PROD_DATABASE_URL secret setup");
1207
+ return summarizeFileCounts(counts);
1208
+ }
1209
+ function summarizeFileCounts(counts) {
1210
+ const total = counts.wrote + counts.inSync + counts.drifted + counts.refreshed + counts.missing;
1211
+ const parts = [];
1212
+ if (counts.wrote) parts.push(`${counts.wrote} written`);
1213
+ if (counts.refreshed) parts.push(`${counts.refreshed} refreshed`);
1214
+ if (counts.drifted) parts.push(`${counts.drifted} drifted (preserved \u2014 pass --refresh-scaffolds to update)`);
1215
+ if (counts.inSync && (counts.wrote || counts.refreshed || counts.drifted)) parts.push(`${counts.inSync} in sync`);
1216
+ if (counts.missing) parts.push(`${counts.missing} missing scaffold src`);
1217
+ if (parts.length === 0) parts.push(`in sync (${total} file${total === 1 ? "" : "s"})`);
1218
+ else if (!counts.wrote && !counts.refreshed && !counts.drifted && counts.inSync === total) {
1219
+ return { status: "in-sync", summary: `in sync (${total} file${total === 1 ? "" : "s"})` };
1220
+ }
1221
+ const status = counts.drifted || counts.missing ? "warn" : counts.wrote || counts.refreshed ? "ok" : "in-sync";
1222
+ return { status, summary: parts.join(", ") };
1120
1223
  }
1121
1224
  function hashFile(p) {
1122
1225
  try {
@@ -1186,21 +1289,22 @@ function isDogfoodMarketplace(targetDir) {
1186
1289
  function scaffoldLsMarketplace(targetDir) {
1187
1290
  const scaffoldsRoot = path4.resolve(__dirname, "..", "..", "scaffolds", "ls-marketplace");
1188
1291
  if (!fs4.existsSync(scaffoldsRoot)) {
1189
- info(`\u26A0 ls-marketplace scaffolds not found at ${scaffoldsRoot} \u2014 skipping (this is a packaging bug; main onboarding is unaffected)`);
1190
- return;
1292
+ warn(`ls-marketplace scaffolds missing at ${scaffoldsRoot} \u2014 packaging bug`);
1293
+ return { status: "warn", summary: "scaffolds missing (packaging bug)" };
1191
1294
  }
1192
1295
  const dogfood = isDogfoodMarketplace(targetDir);
1193
1296
  if (dogfood.isDogfood) {
1194
1297
  info(`dogfood marketplace pointer detected (${dogfood.existingPath}) \u2014 skipping copy, only refreshing enabledPlugins`);
1195
1298
  wireLsSettings(targetDir);
1196
1299
  ok(`launch-secure marketplace (dogfood) \u2014 Claude Code loads commands from ${dogfood.existingPath}`);
1197
- return;
1300
+ return { status: "ok", summary: `dogfood pointer (${dogfood.existingPath})` };
1198
1301
  }
1199
1302
  const marketplaceRoot = path4.join(targetDir, ".claude", "marketplace");
1200
1303
  info("scaffolding launch-secure marketplace (Claude Code /kit: namespace \u2014 refreshes every /kit:* command found in the scaffold) \u2026");
1201
1304
  copyScaffoldDirAlways(scaffoldsRoot, marketplaceRoot, ".claude/marketplace");
1202
1305
  wireLsSettings(targetDir);
1203
1306
  ok(`launch-secure marketplace ready \u2014 open this repo in Claude Code, approve the "${MARKETPLACE_ID}" marketplace prompt, then try /kit:activate-beacon, /kit:standup, or /kit:show-mcp-status`);
1307
+ return { status: "ok", summary: "marketplace tree + settings refreshed" };
1204
1308
  }
1205
1309
  function wireLsSettings(targetDir) {
1206
1310
  const p = path4.join(targetDir, ".claude", "settings.json");
@@ -1247,13 +1351,14 @@ var RECALL_HOOK_COMMAND = 'bash "${CLAUDE_PROJECT_DIR:-$PWD}/scripts/ensure-reca
1247
1351
  function scaffoldRecallHook(targetDir) {
1248
1352
  const scaffoldsRoot = path4.resolve(__dirname, "..", "..", "scaffolds", "recall-hook");
1249
1353
  if (!fs4.existsSync(scaffoldsRoot)) {
1250
- info(`\u26A0 recall-hook scaffolds not found at ${scaffoldsRoot} \u2014 skipping (this is a packaging bug; main onboarding is unaffected)`);
1251
- return;
1354
+ warn(`recall-hook scaffolds missing at ${scaffoldsRoot} \u2014 packaging bug`);
1355
+ return { status: "warn", summary: "scaffolds missing (packaging bug)" };
1252
1356
  }
1253
1357
  info("scaffolding recall-hook (SessionStart watcher-respawn hook + ensure-recall.sh) \u2026");
1254
1358
  copyScaffoldDirAlways(scaffoldsRoot, targetDir, "");
1255
- wireRecallHook(targetDir);
1359
+ const wired = wireRecallHook(targetDir);
1256
1360
  ok("recall-hook ready \u2014 opens with Claude Code will respawn the launch-recall watcher if it died between sessions");
1361
+ return wired ? { status: "ok", summary: "ensure-recall.sh refreshed + SessionStart hook appended" } : { status: "ok", summary: "ensure-recall.sh refreshed (hook already wired)" };
1257
1362
  }
1258
1363
  function wireRecallHook(targetDir) {
1259
1364
  const p = path4.join(targetDir, ".claude", "settings.json");
@@ -1273,7 +1378,7 @@ function wireRecallHook(targetDir) {
1273
1378
  );
1274
1379
  if (alreadyWired) {
1275
1380
  info(".claude/settings.json SessionStart hook already references ensure-recall.sh \u2014 leaving alone");
1276
- return;
1381
+ return false;
1277
1382
  }
1278
1383
  const newGroup = {
1279
1384
  hooks: [{ type: "command", command: RECALL_HOOK_COMMAND }]
@@ -1287,23 +1392,28 @@ function wireRecallHook(targetDir) {
1287
1392
  };
1288
1393
  if (DRY_RUN) {
1289
1394
  dryNote(`would append SessionStart hook to .claude/settings.json (bash scripts/ensure-recall.sh; preserves every other key + existing hooks)`);
1290
- return;
1395
+ return true;
1291
1396
  }
1292
1397
  fs4.mkdirSync(path4.dirname(p), { recursive: true });
1293
1398
  fs4.writeFileSync(p, JSON.stringify(merged, null, 2) + "\n", "utf-8");
1294
1399
  ok(`appended SessionStart hook to .claude/settings.json (bash scripts/ensure-recall.sh)`);
1400
+ return true;
1295
1401
  }
1296
1402
  function tryActivateStatusline() {
1297
1403
  if (DRY_RUN) {
1298
1404
  dryNote(`would wrap ~/.claude/settings.json statusLine.command with launch-kit's MCP chip wrapper (skips silently if no statusline configured)`);
1299
- return;
1405
+ return { status: "skipped", summary: "(dry-run)" };
1300
1406
  }
1301
1407
  const res = activateStatusline();
1302
1408
  if (res.ok && res.outcome === "activated") {
1303
1409
  ok(`statusline wrapped \u2014 MCP chips will render alongside your existing statusline next session`);
1304
- } else if (res.ok && res.outcome === "refreshed") {
1410
+ return { status: "ok", summary: "wrapped \u2014 MCP chips render next session" };
1411
+ }
1412
+ if (res.ok && res.outcome === "refreshed") {
1305
1413
  info(`statusline already wrapped \u2014 refreshed chip scripts`);
1414
+ return { status: "ok", summary: "chip scripts refreshed" };
1306
1415
  }
1416
+ return null;
1307
1417
  }
1308
1418
  async function main() {
1309
1419
  const subcommand = process.argv[2];
@@ -1347,11 +1457,12 @@ async function main() {
1347
1457
  fail(`Unknown subcommand "${subcommand}". Supported: init, refresh, statusline. Run with --help for usage.`);
1348
1458
  }
1349
1459
  DRY_RUN = args.dryRun;
1460
+ VERBOSE = args.verbose || DRY_RUN;
1350
1461
  if (DRY_RUN) {
1351
- info("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1352
- info("DRY RUN \u2014 no files will be written, no commands will run.");
1353
- info("Lines tagged (dry-run) show what would happen.");
1354
- info("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1462
+ console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1463
+ console.log(c.bold("DRY RUN") + c.dim(" \u2014 no files will be written, no commands will run."));
1464
+ console.log(c.dim("Lines tagged (dry-run) show what would happen."));
1465
+ console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1355
1466
  }
1356
1467
  if (subcommand === "refresh") return mainRefresh(args);
1357
1468
  return mainInit(args);
@@ -1390,25 +1501,34 @@ async function mainRefresh(args) {
1390
1501
  const active = nested.profiles[nested.active];
1391
1502
  if (!active) fail(`${CONFIG_FILENAME} active profile "${nested.active}" is not present in profiles.`);
1392
1503
  info(`refreshing launch-kit in ${targetDir} (course: ${nested.active}, project: ${active.orgSlug}/${active.projectSlug}) \u2026`);
1504
+ header("launch-kit refresh", [
1505
+ ["course", nested.active],
1506
+ ["project", `${active.orgSlug}/${active.projectSlug}`],
1507
+ ["dir", path4.relative(cwd, targetDir) || "."]
1508
+ ]);
1393
1509
  const cfg = { pat: active.pat, orgSlug: active.orgSlug, projectSlug: active.projectSlug, serverUrl: active.serverUrl };
1394
- mergeMcpFile(targetDir, buildLaunchKitMcpEntries(cfg));
1510
+ phase(".mcp.json", mergeMcpFile(targetDir, buildLaunchKitMcpEntries(cfg)));
1395
1511
  ensureGitignoreLine(targetDir, CONFIG_FILENAME);
1396
- if (!args.noMigrateSafety) scaffoldMigrateSafety(targetDir, args.refreshScaffolds);
1397
- if (!args.noLsMarketplace) scaffoldLsMarketplace(targetDir);
1398
- if (!args.noRecallHook) scaffoldRecallHook(targetDir);
1399
- tryActivateStatusline();
1400
- console.log("");
1512
+ if (!args.noMigrateSafety) phase("migrate-safety", scaffoldMigrateSafety(targetDir, args.refreshScaffolds));
1513
+ if (!args.noLsMarketplace) phase("ls-marketplace", scaffoldLsMarketplace(targetDir));
1514
+ if (!args.noRecallHook) phase("recall-hook", scaffoldRecallHook(targetDir));
1515
+ const slR = tryActivateStatusline();
1516
+ if (slR) phase("statusline", slR);
1401
1517
  if (DRY_RUN) {
1402
- info("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1403
- info(`DRY RUN COMPLETE \u2014 refresh would have applied the above; no files modified.`);
1404
- info("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1518
+ console.log("");
1519
+ console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1520
+ console.log(c.bold("DRY RUN COMPLETE") + c.dim(" \u2014 refresh would have applied the above; no files modified."));
1521
+ console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1405
1522
  return;
1406
1523
  }
1407
1524
  ok(`refresh complete \u2014 restart Claude Code to pick up any new /kit:* commands`);
1408
- if (!args.quiet) {
1409
- console.log(`
1410
- Skipped (refresh never runs these): clone, dependency install, onboard script, launch-recall init. Use \`npx @launchsecure/launch-kit init\` for a full bootstrap.
1411
- ${getLaunchKitToolsGuide()}`);
1525
+ const showGuide = args.quiet ? false : args.guide === true;
1526
+ const hints = ["Restart Claude Code to pick up any new /kit:* commands."];
1527
+ if (!showGuide && !args.quiet) hints.push("Run with --guide to print the full MCP + slash-command catalog. --verbose for per-file detail.");
1528
+ footer("Refresh complete.", hints);
1529
+ if (showGuide) {
1530
+ console.log(c.dim(`(refresh never runs these: clone, dependency install, onboard script, launch-recall init \u2014 use \`launch-kit init\` for a full bootstrap)`));
1531
+ console.log(getLaunchKitToolsGuide());
1412
1532
  }
1413
1533
  }
1414
1534
  async function mainInit(args) {
@@ -1454,7 +1574,13 @@ async function mainInit(args) {
1454
1574
  if (!/^ls_pat_/.test(args.token)) fail("Token does not look like a LaunchSecure PAT (expected prefix ls_pat_).");
1455
1575
  if (!args.orgSlug) fail("--org=<orgSlug> is required.");
1456
1576
  if (!args.projectSlug) fail("--project=<projectSlug> is required.");
1577
+ header("launch-kit init", [
1578
+ ["org", args.orgSlug],
1579
+ ["project", args.projectSlug],
1580
+ ["server", args.serverUrl]
1581
+ ]);
1457
1582
  const { hasGh } = preflight();
1583
+ phase("preflight", { status: "ok", summary: `node ${process.versions.node}${hasGh ? " \xB7 git \xB7 gh" : " \xB7 git (gh not found \u2014 will use git for clone)"}` });
1458
1584
  info(`resolving project ${args.orgSlug}/${args.projectSlug} on ${args.serverUrl} \u2026`);
1459
1585
  let resolved;
1460
1586
  try {
@@ -1463,6 +1589,7 @@ async function mainInit(args) {
1463
1589
  fail(err instanceof Error ? err.message : String(err));
1464
1590
  }
1465
1591
  ok(`resolved "${resolved.projectName}"`);
1592
+ phase("project_info", { status: "ok", summary: `"${resolved.projectName}"` });
1466
1593
  if (!resolved.repositoryUrl) {
1467
1594
  fail(
1468
1595
  `Project "${resolved.projectSlug}" has no GitHub repository configured. Connect GitHub at ${args.serverUrl}/${resolved.orgSlug}/projects/${resolved.projectSlug}/settings/integrations, then re-run init.`
@@ -1486,7 +1613,14 @@ async function mainInit(args) {
1486
1613
  fail(`${targetDir} exists and is not empty (and not a matching git repo). Refusing to clone into it. Pass --dir=<other-path>.`);
1487
1614
  }
1488
1615
  }
1489
- if (!skipClone) cloneRepo(repoUrl, targetDir, hasGh);
1616
+ const relTarget = path4.relative(cwd, targetDir) || ".";
1617
+ if (!skipClone) {
1618
+ section(`Cloning ${repoUrl}`);
1619
+ cloneRepo(repoUrl, targetDir, hasGh);
1620
+ phase("clone", { status: "ok", summary: `\u2192 ${relTarget}` });
1621
+ } else {
1622
+ phase("clone", { status: "in-sync", summary: `${relTarget} (already a clone of this repo)` });
1623
+ }
1490
1624
  const cfg = {
1491
1625
  pat: args.token,
1492
1626
  orgSlug: resolved.orgSlug,
@@ -1495,7 +1629,8 @@ async function mainInit(args) {
1495
1629
  };
1496
1630
  const courseName = args.course ?? inferCourseName(cfg.serverUrl);
1497
1631
  writeConfigFile(targetDir, cfg, courseName);
1498
- mergeMcpFile(targetDir, buildLaunchKitMcpEntries(cfg));
1632
+ phase("cred file", { status: "ok", summary: `course=${courseName}, active` });
1633
+ phase(".mcp.json", mergeMcpFile(targetDir, buildLaunchKitMcpEntries(cfg)));
1499
1634
  ensureGitignoreLine(targetDir, CONFIG_FILENAME);
1500
1635
  let installSkippedReason = null;
1501
1636
  const detected = detectPackageManager(targetDir);
@@ -1505,43 +1640,51 @@ async function mainInit(args) {
1505
1640
  } else if (!detected) {
1506
1641
  installSkippedReason = "no package.json found";
1507
1642
  } else {
1643
+ section(`Installing dependencies (${detected.pm.name})`);
1508
1644
  runInstall(targetDir, detected);
1509
- if (!args.noOnboard && hasOnboardScript(targetDir)) runOnboard(targetDir, detected.pm);
1645
+ phase("install", { status: "ok", summary: `${detected.pm.binary} ${detected.pm.installArgs.join(" ")}` });
1646
+ if (!args.noOnboard && hasOnboardScript(targetDir)) {
1647
+ section(`Running ${detected.pm.binary} run ${ONBOARD_SCRIPT_NAME}`);
1648
+ runOnboard(targetDir, detected.pm);
1649
+ phase("onboard", { status: "ok", summary: `${detected.pm.binary} run ${ONBOARD_SCRIPT_NAME}` });
1650
+ }
1510
1651
  }
1511
1652
  const hasOnboard = hasOnboardScript(targetDir);
1512
- if (!args.noRecall) runRecallInit(targetDir);
1513
- if (!args.noMigrateSafety) scaffoldMigrateSafety(targetDir, args.refreshScaffolds);
1514
- if (!args.noLsMarketplace) scaffoldLsMarketplace(targetDir);
1515
- if (!args.noRecallHook) scaffoldRecallHook(targetDir);
1516
- tryActivateStatusline();
1517
- const relTarget = path4.relative(cwd, targetDir) || ".";
1518
- console.log("");
1653
+ if (installSkippedReason) phase("install", { status: "skipped", summary: installSkippedReason });
1654
+ if (!args.noRecall) {
1655
+ section("Initializing launch-recall (shadow git backup)");
1656
+ runRecallInit(targetDir);
1657
+ phase("launch-recall", { status: "ok", summary: "shadow git ready" });
1658
+ }
1659
+ if (!args.noMigrateSafety) phase("migrate-safety", scaffoldMigrateSafety(targetDir, args.refreshScaffolds));
1660
+ if (!args.noLsMarketplace) phase("ls-marketplace", scaffoldLsMarketplace(targetDir));
1661
+ if (!args.noRecallHook) phase("recall-hook", scaffoldRecallHook(targetDir));
1662
+ const slR = tryActivateStatusline();
1663
+ if (slR) phase("statusline", slR);
1519
1664
  if (DRY_RUN) {
1520
- info("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1521
- info(`DRY RUN COMPLETE \u2014 no files were modified, no commands ran.`);
1522
- info(`Target: ${targetDir}`);
1523
- info(`Re-run without --dry-run to apply the changes shown above.`);
1524
- info("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1665
+ console.log("");
1666
+ console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1667
+ console.log(c.bold("DRY RUN COMPLETE") + c.dim(` \u2014 no files were modified, no commands ran.`));
1668
+ console.log(c.dim(`Target: ${targetDir}`));
1669
+ console.log(c.dim(`Re-run without --dry-run to apply the changes shown above.`));
1670
+ console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1525
1671
  return;
1526
1672
  }
1527
1673
  ok(`done \u2014 ${resolved.projectName} is ready at ${targetDir}`);
1674
+ const showGuide = args.quiet ? false : args.guide ?? true;
1675
+ const nextSteps = [`cd ${relTarget}`];
1528
1676
  if (installSkippedReason) {
1529
- const installLine = detected ? ` ${detected.pm.binary} ${detected.pm.installArgs.join(" ")}` : ` npm install # or your package manager of choice`;
1530
- const onboardLine = hasOnboard && detected && !args.noOnboard ? `
1531
- ${detected.pm.binary} run ${ONBOARD_SCRIPT_NAME} # project setup hook` : "";
1532
- console.log(`
1533
- Next steps (install skipped: ${installSkippedReason}):
1534
- cd ${relTarget}
1535
- ${installLine}${onboardLine}
1536
- claude # launch Claude Code (5 MCPs wired)${args.quiet ? "" : `
1537
- ${getLaunchKitToolsGuide()}`}`);
1538
- } else if (!args.quiet) {
1539
- console.log(`
1540
- Next steps:
1541
- cd ${relTarget}
1542
- claude # launch Claude Code (5 MCPs wired)
1543
- ${getLaunchKitToolsGuide()}`);
1677
+ nextSteps.push(detected ? `${detected.pm.binary} ${detected.pm.installArgs.join(" ")} # install skipped: ${installSkippedReason}` : `npm install # install skipped: ${installSkippedReason}`);
1678
+ if (hasOnboard && detected && !args.noOnboard) {
1679
+ nextSteps.push(`${detected.pm.binary} run ${ONBOARD_SCRIPT_NAME} # project setup hook`);
1680
+ }
1544
1681
  }
1682
+ nextSteps.push("claude # launch Claude Code (5 MCPs wired)");
1683
+ footer(`${resolved.projectName} is ready at ${relTarget}.`, [
1684
+ "Next:",
1685
+ ...nextSteps.map((s) => " " + s)
1686
+ ]);
1687
+ if (showGuide) console.log(getLaunchKitToolsGuide());
1545
1688
  }
1546
1689
  main().catch((err) => {
1547
1690
  console.error(`[launch-kit] unexpected error: ${err instanceof Error ? err.stack ?? err.message : String(err)}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@launchsecure/launch-kit",
3
- "version": "0.0.31",
3
+ "version": "0.0.32",
4
4
  "description": "LaunchSecure toolkit — launch-pod (pipeline), launch-chart (project graph MCP), launch-deck (visual playground MCP), launch-kit-beacon (feedback Web Component), launch-recall (file-watcher backup).",
5
5
  "license": "MIT",
6
6
  "author": "LaunchSecure - AutomateWithUs",
@@ -31,7 +31,7 @@ Parse `$ARGUMENTS`:
31
31
  - **--scope=<filter>** — for ERDs, limit to one module or table set (e.g. `--scope=auth` or `--scope=User,Org,Membership`). Default: all tables.
32
32
  - **--session=<id>** — deck session name. Default `diagram-<kind>-<subject-slug>`.
33
33
  - **--dsl=<raw-mermaid>** — bypass auto-generation; push the user's DSL verbatim.
34
- - **--feedback** — push in feedback mode and await response.
34
+ - **--no-feedback** — skip feedback round-trip (rare; only when the caller has already validated the DSL).
35
35
 
36
36
  Examples:
37
37
  - `/kit:diagram flowchart user login flow`
@@ -97,18 +97,38 @@ Rules:
97
97
  - `read_graph(layer: "db")` returns 0 nodes
98
98
  - the chart shows tables but `inspect_node` returns no `columns` (parser misconfiguration)
99
99
 
100
- ## Push to deck
100
+ ## Push to deck — feedback mode is the default
101
+
102
+ **Always use `mode: "feedback"` for diagrams.** Mermaid DSL frequently fails on the first attempt (syntax pitfalls below), and the deck server's auto-error poll is racy (1500ms timeout in `deck-mcp.ts`). Feedback mode gives you two backstops: a synchronous render-error capture *and* explicit user confirmation. Skip only when `--no-feedback` was passed.
101
103
 
102
104
  Call `mcp__launch-deck__deck` with:
103
105
  - `session: <session>`
104
- - `mode: "show"` or `"feedback"` (+ `prompt: "Does this match the schema you expected?"` for ERDs)
106
+ - `mode: "feedback"`
107
+ - `prompt: "Diagram render OK? Reply 'ok' to accept, or paste the error / describe what's wrong."` (ERDs: append "Does this match the schema you expected?")
105
108
  - `blocks: [{ type: "mermaid", label: <kind>-<subject>, content: <mermaid-dsl> }]`
106
109
 
107
- If `--feedback`, call `await_feedback` and surface the response.
110
+ ### Render-error retry loop
111
+
112
+ The deck MCP catches mermaid parse/render errors via the browser's `onMermaidError` → `/api/render-errors` channel and surfaces them on the `deck` tool response. Handle both signal paths:
113
+
114
+ 1. **Synchronous render error** — if the `deck` response is `{ok: false, mermaid_error, failed_source, ...}`, inspect `failed_source` first:
115
+ - If `failed_source` matches the DSL you just pushed → real error. Fix the DSL based on `mermaid_error` and re-push immediately (same session — the tab is already open). Cap at 2 auto-retries before surfacing to the user.
116
+ - If `failed_source` does NOT match what you just pushed → **stale error**. The deck server's `lastRenderError` is a module-level pop register (`consumeRenderError` in `deck-serve.ts`) populated via WebSocket from the browser; if the previous push's render error arrived AFTER its 1500ms MCP poll, it leaks into this push's response. Ignore it and proceed to `await_feedback`.
117
+ 2. **User feedback** — if `deck` returned `ok: true`, call `mcp__launch-deck__await_feedback({ session })`. If the reply is positive ("ok", "looks good", thumbs up), you're done. Otherwise treat the reply as a correction (syntax error pasted, "missing X", "wrong direction") and regenerate. Cap at 2 user-feedback retries before stopping and asking for a clearer instruction.
118
+
119
+ ### Common Mermaid pitfalls (avoid on first attempt)
120
+
121
+ - **No `<br/>` in participant aliases.** `participant Pod as LaunchPod CLI<br/>(agent)` breaks the parser in many Mermaid builds. Use one-line names or a trailing parenthetical.
122
+ - **Multi-line notes are fragile.** `Note over X: line1<br/>line2` is risky inside `rect` blocks. Keep notes one-line, or chain two `Note` statements.
123
+ - **Multiple `else` branches.** `alt … else … else … else … end` works in current Mermaid but failure modes are noisy; prefer 2-branch alts or split into separate alt blocks.
124
+ - **`rect rgb(...)`** uses **space-separated** values, not commas: `rect rgb(230 240 255)` — not `rect rgb(230,240,255)`. Comma form errors silently in some builds.
125
+ - **Special chars in labels** — wrap in double quotes when using `:`, `(`, `,`, or unicode arrows: `A->>B: "step (1): start"`.
126
+ - **Reserved keywords as actor names** — avoid `end`, `note`, `loop`, `alt`, `opt` as participant ids.
127
+ - **ERD column types** must be one word: `string`, `int`, `datetime`, `enum`. No spaces, no parens.
108
128
 
109
129
  ## Open in browser
110
130
 
111
- After the push (and after `await_feedback` if `--feedback`), open the per-session URL so the user lands directly on this diagram:
131
+ After the final successful push (and after `await_feedback` returns positive), open the per-session URL so the user lands directly on this diagram:
112
132
 
113
133
  ```
114
134
  open "<deck-url>/#<session>" # macOS
@@ -150,3 +170,5 @@ Then fall back: render a `flowchart` placeholder explaining "DB layer not availa
150
170
  - **Cap entities at 25.** Past that, Mermaid wraps unreadably; ask the user to narrow scope.
151
171
  - **Mermaid only.** This skill does not draw raster images — Mermaid DSL is the deliverable.
152
172
  - **One block per push.**
173
+ - **Feedback mode by default.** Never push a diagram with `mode: "show"` unless `--no-feedback` was explicitly passed. Diagrams are validated artifacts, not fire-and-forget output.
174
+ - **Retry caps.** 2 auto-retries on synchronous render error + 2 user-feedback retries. Beyond that, hand the broken DSL back to the user with the last error message and stop.
@@ -1 +0,0 @@
1
- import{aq as o,ar as n}from"./index-CB-qlwRT.js";const t=(r,a)=>o.lang.round(n.parse(r)[a]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-4TB4RGXK-DtKQqaI7.js";import{_ as i}from"./index-CB-qlwRT.js";import"./chunk-FMBD7UC4-CmuA5UKn.js";import"./chunk-YZCP3GAM-C44yr620.js";import"./chunk-55IACEB6-COy9hEae.js";import"./chunk-EDXVE4YY-D_f861An.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-4TB4RGXK-DtKQqaI7.js";import{_ as i}from"./index-CB-qlwRT.js";import"./chunk-FMBD7UC4-CmuA5UKn.js";import"./chunk-YZCP3GAM-C44yr620.js";import"./chunk-55IACEB6-COy9hEae.js";import"./chunk-EDXVE4YY-D_f861An.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
@@ -1 +0,0 @@
1
- import{b as r}from"./graph-CifKx6G1.js";var e=4;function a(o){return r(o,e)}export{a as c};
@@ -1 +0,0 @@
1
- import{s as t,b as r,a,S as s}from"./chunk-OYMX7WX6-vT8z8D-0.js";import{_ as i}from"./index-CB-qlwRT.js";import"./chunk-55IACEB6-COy9hEae.js";import"./chunk-EDXVE4YY-D_f861An.js";var l={parser:a,get db(){return new s(2)},renderer:r,styles:t,init:i(e=>{e.state||(e.state={}),e.state.arrowMarkerAbsolute=e.arrowMarkerAbsolute},"init")};export{l as diagram};