@magic-spells/constellation 0.1.0 → 0.1.1

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 (102) hide show
  1. package/README.md +10 -3
  2. package/dist/cli/index.js +13 -3
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/cli/update-check.d.ts +6 -0
  5. package/dist/cli/update-check.js +70 -0
  6. package/dist/cli/update-check.js.map +1 -0
  7. package/dist/core/resolve.js +10 -1
  8. package/dist/core/resolve.js.map +1 -1
  9. package/dist/core/scaffold.d.ts +16 -3
  10. package/dist/core/scaffold.js +30 -7
  11. package/dist/core/scaffold.js.map +1 -1
  12. package/dist/mcp/server.js +124 -13
  13. package/dist/mcp/server.js.map +1 -1
  14. package/docs/002-mcp.md +26 -7
  15. package/examples/constellation/plan.md +1 -1
  16. package/package.json +3 -1
  17. package/skill/SKILL.md +66 -1
  18. package/skill/methodology.md +211 -0
  19. package/viewer/dist/assets/{arc-Kj6pF3JI.js → arc-z-d28NWT.js} +1 -1
  20. package/viewer/dist/assets/architecture-7EHR7CIX-QBvJmJV9.js +1 -0
  21. package/viewer/dist/assets/{architectureDiagram-3BPJPVTR-C5bZdErB.js → architectureDiagram-3BPJPVTR-CjoeulX0.js} +1 -1
  22. package/viewer/dist/assets/{blockDiagram-GPEHLZMM-C1Q6l6fE.js → blockDiagram-GPEHLZMM-BGlWRy-x.js} +1 -1
  23. package/viewer/dist/assets/{c4Diagram-AAUBKEIU-BmM6Tmtq.js → c4Diagram-AAUBKEIU-B09gKF2I.js} +1 -1
  24. package/viewer/dist/assets/channel-COWeTobO.js +1 -0
  25. package/viewer/dist/assets/{chunk-2J33WTMH-z09tLTpZ.js → chunk-2J33WTMH-Cfc4xM5m.js} +1 -1
  26. package/viewer/dist/assets/{chunk-3OPIFGDE-BynpXh1r.js → chunk-3OPIFGDE-CfJIsbrs.js} +1 -1
  27. package/viewer/dist/assets/{chunk-4BX2VUAB-CDOVuPyG.js → chunk-4BX2VUAB-B1J5W5bz.js} +1 -1
  28. package/viewer/dist/assets/{chunk-55IACEB6-nBwigOgn.js → chunk-55IACEB6-Bjg14U7h.js} +1 -1
  29. package/viewer/dist/assets/{chunk-5ZQYHXKU-Bxe5xIy_.js → chunk-5ZQYHXKU-BCvNFwZ5.js} +1 -1
  30. package/viewer/dist/assets/{chunk-727SXJPM-DZmTgL68.js → chunk-727SXJPM-XfiLvG1c.js} +1 -1
  31. package/viewer/dist/assets/{chunk-AQP2D5EJ-B7wr_Owx.js → chunk-AQP2D5EJ-BS-Yzcyy.js} +1 -1
  32. package/viewer/dist/assets/{chunk-BSJP7CBP-DbAKfVCK.js → chunk-BSJP7CBP-DdbVG_ma.js} +1 -1
  33. package/viewer/dist/assets/{chunk-CSCIHK7Q-C0rsBwqP.js → chunk-CSCIHK7Q-C_v3KOxf.js} +1 -1
  34. package/viewer/dist/assets/{chunk-FMBD7UC4-BAtzt0wv.js → chunk-FMBD7UC4-Djdrd8lQ.js} +1 -1
  35. package/viewer/dist/assets/{chunk-KSCS5N6A-CXXwf52I.js → chunk-KSCS5N6A-CLg-rwxg.js} +1 -1
  36. package/viewer/dist/assets/{chunk-L5ZTLDWV-DS4vRI1U.js → chunk-L5ZTLDWV-DCOC9Gbq.js} +1 -1
  37. package/viewer/dist/assets/chunk-LZXEDZCA-D_cw-C3l.js +2 -0
  38. package/viewer/dist/assets/{chunk-ND2GUHAM-CUSnPl8t.js → chunk-ND2GUHAM-7krqLOpO.js} +1 -1
  39. package/viewer/dist/assets/{chunk-NZK2D7GU-Dh986nJk.js → chunk-NZK2D7GU-BHqkidnq.js} +1 -1
  40. package/viewer/dist/assets/{chunk-O5CBEL6O-JAEZ_pS6.js → chunk-O5CBEL6O-D953Jfjo.js} +1 -1
  41. package/viewer/dist/assets/chunk-QZHKN3VN-D7xg9F2p.js +1 -0
  42. package/viewer/dist/assets/chunk-WU5MYG2G-CWG30QUB.js +1 -0
  43. package/viewer/dist/assets/{chunk-XPW4576I-BwMZI0gv.js → chunk-XPW4576I-tr-y3uUk.js} +1 -1
  44. package/viewer/dist/assets/classDiagram-4FO5ZUOK-BsU5cU-N.js +1 -0
  45. package/viewer/dist/assets/classDiagram-v2-Q7XG4LA2-BsU5cU-N.js +1 -0
  46. package/viewer/dist/assets/{cose-bilkent-S5V4N54A-NGC7gYHM.js → cose-bilkent-S5V4N54A-RHqSF1Fo.js} +1 -1
  47. package/viewer/dist/assets/{dagre-BM42HDAG-RD63uyvd.js → dagre-BM42HDAG-CaRlC_3n.js} +1 -1
  48. package/viewer/dist/assets/{diagram-2AECGRRQ-hwnqqCcb.js → diagram-2AECGRRQ-zBc7NQxu.js} +1 -1
  49. package/viewer/dist/assets/{diagram-5GNKFQAL-q8EaoZSG.js → diagram-5GNKFQAL-CdFUltct.js} +1 -1
  50. package/viewer/dist/assets/{diagram-KO2AKTUF-D4_5Qf-l.js → diagram-KO2AKTUF-DKBMUmIp.js} +1 -1
  51. package/viewer/dist/assets/{diagram-LMA3HP47-D8pwekFs.js → diagram-LMA3HP47-aj0REjiQ.js} +1 -1
  52. package/viewer/dist/assets/{diagram-OG6HWLK6-D9KinIWZ.js → diagram-OG6HWLK6-Bqtmu-Nc.js} +1 -1
  53. package/viewer/dist/assets/{dist-CFOOgrqc.js → dist-CkpbBMm1.js} +1 -1
  54. package/viewer/dist/assets/{erDiagram-TEJ5UH35-D0Wfq250.js → erDiagram-TEJ5UH35-_EpsTRRO.js} +1 -1
  55. package/viewer/dist/assets/eventmodeling-FCH6USID-BEUuQhV3.js +1 -0
  56. package/viewer/dist/assets/{flowDiagram-I6XJVG4X-Y2DY-Ze2.js → flowDiagram-I6XJVG4X-ChGqvvgX.js} +1 -1
  57. package/viewer/dist/assets/{ganttDiagram-6RSMTGT7-BnqkeLVw.js → ganttDiagram-6RSMTGT7-BjcMfREp.js} +1 -1
  58. package/viewer/dist/assets/{gitGraph-WXDBUCRP-Cft7usRT.js → gitGraph-WXDBUCRP-DfwN1rQD.js} +1 -1
  59. package/viewer/dist/assets/{gitGraphDiagram-PVQCEYII-D-cYtraK.js → gitGraphDiagram-PVQCEYII-ClI7X9vN.js} +1 -1
  60. package/viewer/dist/assets/index-BRID9hB2.js +98 -0
  61. package/viewer/dist/assets/index-CI5JmpBT.css +2 -0
  62. package/viewer/dist/assets/{info-J43DQDTF-Djc8Bx3F.js → info-J43DQDTF-CofQIoJ2.js} +1 -1
  63. package/viewer/dist/assets/{infoDiagram-5YYISTIA-D-ehtyyJ.js → infoDiagram-5YYISTIA-D2BcKzo_.js} +1 -1
  64. package/viewer/dist/assets/{ishikawaDiagram-YF4QCWOH-Ct3f6bH-.js → ishikawaDiagram-YF4QCWOH-CXw1XdbZ.js} +1 -1
  65. package/viewer/dist/assets/{journeyDiagram-JHISSGLW-DXlULEmi.js → journeyDiagram-JHISSGLW-3Jhqf-KB.js} +1 -1
  66. package/viewer/dist/assets/{kanban-definition-UN3LZRKU-3vE9h-R7.js → kanban-definition-UN3LZRKU-ClSj7gDE.js} +1 -1
  67. package/viewer/dist/assets/{line-B8MygbLB.js → line-DeameHHf.js} +1 -1
  68. package/viewer/dist/assets/{linear-CfMuM0B3.js → linear-BDDnY--V.js} +1 -1
  69. package/viewer/dist/assets/{mermaid-parser.core-DzlZTbbh.js → mermaid-parser.core-Cah87FZ1.js} +2 -2
  70. package/viewer/dist/assets/{mermaid.core-IM-sPiyq.js → mermaid.core-lOcajBDU.js} +3 -3
  71. package/viewer/dist/assets/{mindmap-definition-RKZ34NQL-CMnpAq1T.js → mindmap-definition-RKZ34NQL-CebNj7Vy.js} +1 -1
  72. package/viewer/dist/assets/{packet-YPE3B663-D44AzgHh.js → packet-YPE3B663-BlKTjmdv.js} +1 -1
  73. package/viewer/dist/assets/{pie-LRSECV5Y-DL8AVJH_.js → pie-LRSECV5Y-CHjlSKV3.js} +1 -1
  74. package/viewer/dist/assets/{pieDiagram-4H26LBE5-FvKK5jd7.js → pieDiagram-4H26LBE5-zWcP8Zx3.js} +1 -1
  75. package/viewer/dist/assets/{quadrantDiagram-W4KKPZXB-CmjSkU8c.js → quadrantDiagram-W4KKPZXB-BSBO8usz.js} +1 -1
  76. package/viewer/dist/assets/{radar-GUYGQ44K-BvfZTVyH.js → radar-GUYGQ44K-BDmaTdGw.js} +1 -1
  77. package/viewer/dist/assets/{requirementDiagram-4Y6WPE33-BOjca3VH.js → requirementDiagram-4Y6WPE33-C1wh6GaA.js} +1 -1
  78. package/viewer/dist/assets/{sankeyDiagram-5OEKKPKP-ANcjfNix.js → sankeyDiagram-5OEKKPKP-L3mqPVcH.js} +1 -1
  79. package/viewer/dist/assets/{sequenceDiagram-3UESZ5HK-BLQ9AL7I.js → sequenceDiagram-3UESZ5HK-7vbfvJiz.js} +1 -1
  80. package/viewer/dist/assets/src-CslsSLVV.js +1 -0
  81. package/viewer/dist/assets/{stateDiagram-AJRCARHV-D6CriBS6.js → stateDiagram-AJRCARHV-CfEgJmUy.js} +1 -1
  82. package/viewer/dist/assets/stateDiagram-v2-BHNVJYJU-Dj6oCjNw.js +1 -0
  83. package/viewer/dist/assets/{timeline-definition-PNZ67QCA-BNhWZ_DL.js → timeline-definition-PNZ67QCA-DMhMwUyE.js} +1 -1
  84. package/viewer/dist/assets/{treeView-BLDUP644-CY6Ph5Pu.js → treeView-BLDUP644-aGCNEij1.js} +1 -1
  85. package/viewer/dist/assets/{treemap-LRROVOQU-DChSA_Qx.js → treemap-LRROVOQU-DTgPn5R6.js} +1 -1
  86. package/viewer/dist/assets/{vennDiagram-CIIHVFJN-C01WznAC.js → vennDiagram-CIIHVFJN-BZygSp3k.js} +1 -1
  87. package/viewer/dist/assets/{wardley-L42UT6IY-BJ8uNoJu.js → wardley-L42UT6IY-BSxi2k9r.js} +1 -1
  88. package/viewer/dist/assets/{wardleyDiagram-YWT4CUSO-DwDEzlVm.js → wardleyDiagram-YWT4CUSO-ZBjbz3Mj.js} +1 -1
  89. package/viewer/dist/assets/{xychartDiagram-2RQKCTM6-BCvIDwU0.js → xychartDiagram-2RQKCTM6-VFJI2tv3.js} +1 -1
  90. package/viewer/dist/index.html +3 -3
  91. package/viewer/dist/assets/architecture-7EHR7CIX-CGfWeim3.js +0 -1
  92. package/viewer/dist/assets/channel-19IdUS_c.js +0 -1
  93. package/viewer/dist/assets/chunk-LZXEDZCA-CclT9MXr.js +0 -2
  94. package/viewer/dist/assets/chunk-QZHKN3VN-BKc_Kg2Z.js +0 -1
  95. package/viewer/dist/assets/chunk-WU5MYG2G-9ssTSMzt.js +0 -1
  96. package/viewer/dist/assets/classDiagram-4FO5ZUOK-DXv85WFd.js +0 -1
  97. package/viewer/dist/assets/classDiagram-v2-Q7XG4LA2-DXv85WFd.js +0 -1
  98. package/viewer/dist/assets/eventmodeling-FCH6USID-D3KRSuC1.js +0 -1
  99. package/viewer/dist/assets/index-CDR-riG2.css +0 -2
  100. package/viewer/dist/assets/index-DRPsTWe2.js +0 -98
  101. package/viewer/dist/assets/src-CAMdANUp.js +0 -1
  102. package/viewer/dist/assets/stateDiagram-v2-BHNVJYJU-DcTp66RQ.js +0 -1
package/README.md CHANGED
@@ -100,6 +100,13 @@ claude mcp add --scope project constellation -- npx -y @magic-spells/constellati
100
100
  Manage it with `claude mcp list`, `claude mcp get constellation`, and
101
101
  `claude mcp remove constellation`.
102
102
 
103
+ To build a plan from an existing codebase (or audit one), ask the agent to bootstrap or
104
+ audit — the server ships **`bootstrap_plan`** and **`audit_plan`** prompts (slash commands
105
+ in Claude Code) that walk the code macro→micro: follow the data, follow the user/auth, then
106
+ step back and pressure-test the plan for blind spots (missing unhappy paths, auth gaps,
107
+ forgotten cross-cutting concerns) and recommend. The full method is in
108
+ [`skill/methodology.md`](skill/methodology.md).
109
+
103
110
  ### Other MCP clients
104
111
 
105
112
  Hand-edit the client's config (Claude Desktop, a project `.mcp.json`, etc.):
@@ -123,9 +130,10 @@ may not be your project — in which case tools return `NO_PLAN_FOUND`.
123
130
  ## Viewer
124
131
 
125
132
  `constellation serve` renders the plan as a local website, **editable in place**
126
- (pass `--readonly` to disable writes). Five themes toggle in the header:
133
+ (pass `--readonly` to disable writes). Eight themes toggle in the header:
127
134
  **observatory** (dark, star-field), **claw** (cream paper, serif, coral accents),
128
- **black**, **synthwave**, and **sumi**. Card pages show structured fields, the
135
+ **black**, **synthwave**, **sumi**, **daylight**, **frost**, and **ember**.
136
+ Card pages show structured fields, the
129
137
  markdown body, connection chips in both directions, and a small constellation
130
138
  diagram of the card's neighborhood — its nodes tinted by card type. Mermaid blocks
131
139
  render in-browser, `[[HANDLE]]` links navigate, and the page live-reloads when plan
@@ -144,4 +152,3 @@ calls the `start_viewer` MCP tool, which returns the URL (`stop_viewer` closes i
144
152
  <p align="center">
145
153
  Made by <a href="https://github.com/coryschulz">Cory Schulz</a>
146
154
  </p>
147
-
package/dist/cli/index.js CHANGED
@@ -1,14 +1,22 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
2
3
  import path from 'node:path';
3
4
  import process from 'node:process';
4
5
  import { Command } from 'commander';
5
6
  import pc from 'picocolors';
6
7
  import { lintPlan } from '../core/lint.js';
7
8
  import { resolvePlanDir } from '../core/resolve.js';
9
+ import { notifyUpdate } from './update-check.js';
10
+ const require = createRequire(import.meta.url);
11
+ const { name, version } = require('../../package.json');
8
12
  const program = new Command();
9
13
  program
10
14
  .name('constellation')
11
- .description('Files-first architecture planning for AI-assisted development');
15
+ .description('Files-first architecture planning for AI-assisted development')
16
+ .version(version);
17
+ // Update notice on every human command, but never for `mcp` (its stdout is JSON-RPC).
18
+ if (process.argv[2] !== 'mcp')
19
+ notifyUpdate(name, version);
12
20
  program
13
21
  .command('lint')
14
22
  .argument('[path]', 'plan folder, or a directory containing constellation/ (default: walk up from cwd)')
@@ -54,12 +62,14 @@ program
54
62
  program
55
63
  .command('init')
56
64
  .argument('[path]', 'directory to create the plan in (default: cwd)', '.')
65
+ .option('-n, --name <name>', 'project name shown as the viewer title (default: a title-cased folder name)')
57
66
  .description('Scaffold a constellation/ folder with a starter plan.md')
58
- .action(async (target) => {
67
+ .action(async (target, opts) => {
59
68
  const { initPlan } = await import('../core/scaffold.js');
60
69
  try {
61
- const root = await initPlan(target);
70
+ const { root, name: projectName } = await initPlan(target, { name: opts.name });
62
71
  console.log(pc.green('✓') + ` Created ${path.relative(process.cwd(), root)}/plan.md`);
72
+ console.log(` Project name: ${pc.bold(projectName)} ${pc.dim('— edit the name: field in plan.md to change it')}`);
63
73
  console.log('\nAdd cards as <type>/<HANDLE>.md (e.g. api/API-LIST-USERS.md),\nthen run `constellation lint` to validate.');
64
74
  }
65
75
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAU,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAG5D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,eAAe,CAAC;KACrB,WAAW,CAAC,+DAA+D,CAAC,CAAC;AAEhF,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,QAAQ,CACP,QAAQ,EACR,mFAAmF,CACpF;KACA,WAAW,CAAC,0DAA0D,CAAC;KACvE,MAAM,CAAC,KAAK,EAAE,MAAiC,EAAE,EAAE;IAClD,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;IACvD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CACX,EAAE,CAAC,GAAG,CAAC,iCAAiC,CAAC;YACvC,0CAA0C,CAC7C,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC1C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACxD,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,GAAG,GACP,KAAK,CAAC,QAAQ,KAAK,OAAO;gBACxB,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,SAAS,KAAK,CAAC,IAAI,EAAE,CAAC;gBAC/B,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACvC,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,EAAE,CAAC;IAEnC,MAAM,OAAO,GAAG;QACd,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,QAAQ;QAClC,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,cAAc;QAChD,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;YACtB,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,SAAS,CAAC;YAC1C,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC;QACxB,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;YACxB,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,WAAW,CAAC;YACjD,CAAC,CAAC,YAAY;KACjB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,OAAO,EAAE,CAAC,CAAC;IAEpF,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,QAAQ,CAAC,QAAQ,EAAE,gDAAgD,EAAE,GAAG,CAAC;KACzE,WAAW,CAAC,yDAAyD,CAAC;KACtE,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,EAAE;IAC/B,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACzD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,YAAY,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACtF,OAAO,CAAC,GAAG,CACT,6GAA6G,CAC9G,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,KAAK,CAAC;KACd,WAAW,CAAC,6CAA6C,CAAC;KAC1D,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAC5D,MAAM,cAAc,EAAE,CAAC;AACzB,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,QAAQ,CAAC,QAAQ,EAAE,sDAAsD,CAAC;KAC1E,MAAM,CAAC,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,CAAC;KACxD,MAAM,CAAC,WAAW,EAAE,yBAAyB,CAAC;KAC9C,MAAM,CAAC,YAAY,EAAE,kCAAkC,CAAC;KACxD,WAAW,CAAC,uDAAuD,CAAC;KACpE,MAAM,CAAC,KAAK,EACX,MAAiC,EACjC,IAAyD,EACzD,EAAE;IACF,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;IACvD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC,CAAC;QACzD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAC3D,IAAI,OAAgD,CAAC;IACrD,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,WAAW,CAAC;YAC1B,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK;SACjC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,EAAE,IAAI,CAAC;QAClD,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;YAC1B,OAAO,CAAC,KAAK,CACX,EAAE,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,IAAI,qBAAqB,CAAC;gBAC5C,mDAAmD,CACtD,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,GAAG,GAAG,oBAAoB,OAAO,CAAC,IAAI,EAAE,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,4BAA4B,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC7E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC;IACvC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACrD,MAAM,GAAG,GACP,OAAO,CAAC,QAAQ,KAAK,QAAQ;YAC3B,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;gBAC5B,CAAC,CAAC,OAAO;gBACT,CAAC,CAAC,UAAU,CAAC;QACnB,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;IACjE,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC7C,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Print an "update available" notice on stderr (from cache — never hits the
3
+ * network on this path, so it never blocks), then kick off a detached
4
+ * background refresh if the cache is stale. Do NOT call this for `mcp`.
5
+ */
6
+ export declare function notifyUpdate(name: string, current: string): void;
@@ -0,0 +1,70 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { readFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import process from 'node:process';
6
+ import pc from 'picocolors';
7
+ const CACHE_FILE = join(process.env.XDG_CACHE_HOME || join(homedir(), '.cache'), 'constellation', 'update.json');
8
+ const ONE_DAY = 24 * 60 * 60 * 1000;
9
+ /**
10
+ * Print an "update available" notice on stderr (from cache — never hits the
11
+ * network on this path, so it never blocks), then kick off a detached
12
+ * background refresh if the cache is stale. Do NOT call this for `mcp`.
13
+ */
14
+ export function notifyUpdate(name, current) {
15
+ if (!process.stderr.isTTY)
16
+ return; // not a terminal (pipe / redirect)
17
+ if (process.env.CI || process.env.NO_UPDATE_NOTIFIER)
18
+ return;
19
+ const cache = readCache();
20
+ if (cache?.latest && isNewer(cache.latest, current)) {
21
+ process.stderr.write(`\n ${pc.yellow('▲ Update available')} ${pc.dim(`${current} →`)} ${pc.green(cache.latest)}\n` +
22
+ ` ${pc.dim('Run')} ${pc.cyan(`npm i -g ${name}`)}\n\n`);
23
+ }
24
+ if (!cache || Date.now() - cache.checkedAt > ONE_DAY)
25
+ scheduleRefresh(name);
26
+ }
27
+ function readCache() {
28
+ try {
29
+ return JSON.parse(readFileSync(CACHE_FILE, 'utf8'));
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ /** Detached child fetches the registry, writes the cache, then exits — outlives us. */
36
+ function scheduleRefresh(name) {
37
+ const script = `
38
+ const fs = require('node:fs'), path = require('node:path'), file = process.argv[2];
39
+ fetch('https://registry.npmjs.org/' + process.argv[1] + '/latest')
40
+ .then((r) => r.json())
41
+ .then((j) => {
42
+ if (!j?.version) return;
43
+ fs.mkdirSync(path.dirname(file), { recursive: true });
44
+ fs.writeFileSync(file, JSON.stringify({ latest: j.version, checkedAt: Date.now() }));
45
+ })
46
+ .catch(() => {});
47
+ `;
48
+ try {
49
+ spawn(process.execPath, ['-e', script, name, CACHE_FILE], {
50
+ detached: true,
51
+ stdio: 'ignore',
52
+ }).unref();
53
+ }
54
+ catch {
55
+ /* a failed background check must never affect the CLI */
56
+ }
57
+ }
58
+ /** Is `a` a newer release than `b`? Compares major.minor.patch; ignores prerelease tags. */
59
+ function isNewer(a, b) {
60
+ const pa = a.split('-')[0].split('.').map(Number);
61
+ const pb = b.split('-')[0].split('.').map(Number);
62
+ for (let i = 0; i < 3; i++) {
63
+ const da = pa[i] || 0;
64
+ const db = pb[i] || 0;
65
+ if (da !== db)
66
+ return da > db;
67
+ }
68
+ return false;
69
+ }
70
+ //# sourceMappingURL=update-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-check.js","sourceRoot":"","sources":["../../src/cli/update-check.ts"],"names":[],"mappings":"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"}
@@ -11,7 +11,10 @@ export async function resolvePlanDir(target) {
11
11
  const nested = path.join(abs, 'constellation');
12
12
  if (await isDirectory(nested))
13
13
  return nested;
14
- if (await isDirectory(abs))
14
+ // Only adopt the target itself when it actually looks like a plan root —
15
+ // otherwise an explicit path to an unrelated directory would be linted or
16
+ // served as if its markdown files were cards.
17
+ if ((await isDirectory(abs)) && (await looksLikePlanRoot(abs)))
15
18
  return abs;
16
19
  return null;
17
20
  }
@@ -37,6 +40,12 @@ export async function findPlanUp(startDir) {
37
40
  dir = parent;
38
41
  }
39
42
  }
43
+ /** A directory is a plan root if it's named `constellation` or contains a plan.md. */
44
+ async function looksLikePlanRoot(dir) {
45
+ if (path.basename(dir) === 'constellation')
46
+ return true;
47
+ return exists(path.join(dir, 'plan.md'));
48
+ }
40
49
  export async function exists(p) {
41
50
  try {
42
51
  await access(p);
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/core/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,OAAO,MAAM,cAAc,CAAC;AAEnC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAe;IAClD,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;QAC/C,IAAI,MAAM,WAAW,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC;QAC7C,IAAI,MAAM,WAAW,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjC,SAAS,CAAC;QACR,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;QAClD,IAAI,MAAM,MAAM,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;QAC9C,kEAAkE;QAClE,IAAI,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACtD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAChC,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,CAAS;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,CAAS;IAClC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/core/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,OAAO,MAAM,cAAc,CAAC;AAEnC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAe;IAClD,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;QAC/C,IAAI,MAAM,WAAW,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC;QAC7C,yEAAyE;QACzE,0EAA0E;QAC1E,8CAA8C;QAC9C,IAAI,CAAC,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QAC3E,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjC,SAAS,CAAC;QACR,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;QAClD,IAAI,MAAM,MAAM,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;QAC9C,kEAAkE;QAClE,IAAI,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACtD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAChC,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,KAAK,UAAU,iBAAiB,CAAC,GAAW;IAC1C,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,eAAe;QAAE,OAAO,IAAI,CAAC;IACxD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,CAAS;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,CAAS;IAClC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -1,3 +1,16 @@
1
- export declare const STARTER_PLAN = "---\nname: Project plan\n---\n\n# Project Plan\n\n## Current state\n\n- (what exists, what is in flight)\n\n## Conventions\n\n- (project-wide rules cards should follow)\n\n## Last synced\n\nCode has not been reconciled against this plan yet.\n";
2
- /** Create constellation/ with a starter plan.md. Throws if it already exists. */
3
- export declare function initPlan(targetDir: string): Promise<string>;
1
+ /** Turn a folder slug into a human-readable project name: pyramid-server Pyramid Server. */
2
+ export declare function titleCaseFromSlug(slug: string): string;
3
+ /** The starter plan.md body, parameterized by the project's display name. */
4
+ export declare function starterPlan(name: string): string;
5
+ /**
6
+ * Create constellation/ with a starter plan.md. Throws if it already exists.
7
+ * The project name defaults to a title-cased version of the target folder
8
+ * (so pyramid-server → "Pyramid Server"); pass opts.name to override it. The
9
+ * resolved name is returned so callers can echo / confirm it.
10
+ */
11
+ export declare function initPlan(targetDir: string, opts?: {
12
+ name?: string;
13
+ }): Promise<{
14
+ root: string;
15
+ name: string;
16
+ }>;
@@ -1,7 +1,20 @@
1
1
  import { access, mkdir, writeFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- export const STARTER_PLAN = `---
4
- name: Project plan
3
+ /** Turn a folder slug into a human-readable project name: pyramid-server → Pyramid Server. */
4
+ export function titleCaseFromSlug(slug) {
5
+ return slug
6
+ .replace(/[-_]+/g, ' ') // kebab / snake → spaces
7
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2') // split camelCase
8
+ .trim()
9
+ .split(/\s+/)
10
+ .filter(Boolean)
11
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
12
+ .join(' ');
13
+ }
14
+ /** The starter plan.md body, parameterized by the project's display name. */
15
+ export function starterPlan(name) {
16
+ return `---
17
+ name: ${name}
5
18
  ---
6
19
 
7
20
  # Project Plan
@@ -18,9 +31,19 @@ name: Project plan
18
31
 
19
32
  Code has not been reconciled against this plan yet.
20
33
  `;
21
- /** Create constellation/ with a starter plan.md. Throws if it already exists. */
22
- export async function initPlan(targetDir) {
23
- const root = path.resolve(targetDir, 'constellation');
34
+ }
35
+ /**
36
+ * Create constellation/ with a starter plan.md. Throws if it already exists.
37
+ * The project name defaults to a title-cased version of the target folder
38
+ * (so pyramid-server → "Pyramid Server"); pass opts.name to override it. The
39
+ * resolved name is returned so callers can echo / confirm it.
40
+ */
41
+ export async function initPlan(targetDir, opts = {}) {
42
+ const resolvedTarget = path.resolve(targetDir);
43
+ const root = path.join(resolvedTarget, 'constellation');
44
+ const name = opts.name?.trim() ||
45
+ titleCaseFromSlug(path.basename(resolvedTarget)) ||
46
+ 'Project plan';
24
47
  try {
25
48
  await access(root);
26
49
  throw new Error(`${root} already exists`);
@@ -30,7 +53,7 @@ export async function initPlan(targetDir) {
30
53
  throw err;
31
54
  }
32
55
  await mkdir(root, { recursive: true });
33
- await writeFile(path.join(root, 'plan.md'), STARTER_PLAN, { flag: 'wx' });
34
- return root;
56
+ await writeFile(path.join(root, 'plan.md'), starterPlan(name), { flag: 'wx' });
57
+ return { root, name };
35
58
  }
36
59
  //# sourceMappingURL=scaffold.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/core/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;CAiB3B,CAAC;AAEF,iFAAiF;AACjF,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,SAAiB;IAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IACtD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,iBAAiB,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YAAE,MAAM,GAAG,CAAC;IAChF,CAAC;IACD,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1E,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/core/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,8FAA8F;AAC9F,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,IAAI;SACR,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,yBAAyB;SAChD,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC,kBAAkB;SACzD,IAAI,EAAE;SACN,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAClD,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO;QACD,IAAI;;;;;;;;;;;;;;;;CAgBX,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,SAAiB,EACjB,OAA0B,EAAE;IAE5B,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;IACxD,MAAM,IAAI,GACR,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE;QACjB,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;QAChD,cAAc,CAAC;IACjB,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,iBAAiB,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YAAE,MAAM,GAAG,CAAC;IAChF,CAAC;IACD,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/E,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC"}
@@ -1,4 +1,6 @@
1
- import { rm } from 'node:fs/promises';
1
+ import { readFile, rm } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
2
4
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
5
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
6
  import { z } from 'zod';
@@ -25,10 +27,25 @@ Writes are validated: every write tool lints and returns issues for the file it
25
27
  update_card patch.fields deep-merges (arrays replace, null deletes); body replaces.
26
28
  Body-only updates never reformat frontmatter.
27
29
 
28
- Change tracking is git: diff_plan reports per-card changes since the sync marker (or
29
- HEAD); traverse the changed handles for blast radius; set_sync_point after reconciling
30
- code (commit the plan first — it warns if the plan is uncommitted). Never stamp dirty
31
- flags into cards.
30
+ Change tracking is git: diff_plan reports per-card changes since the sync marker (or HEAD).
31
+ Never stamp dirty flags into cards.
32
+
33
+ "Sync the plan" / "sync the plan to the code" = bring the CODE up to match the plan (the
34
+ plan is the source of truth — behavior changes in the plan FIRST, then in code, never the
35
+ reverse). It is NOT merely stamping the marker. The loop: diff_plan (base = marker) for what
36
+ changed → traverse the changed handles (detail: "full") for blast radius → update the
37
+ application code to match those cards → run the build/tests and bump card status → commit,
38
+ then set_sync_point to advance the marker (commit the plan first — it warns if the plan is
39
+ uncommitted). When the diff is large and the affected areas don't share files, act as the
40
+ ORCHESTRATOR rather than editing it all yourself: partition the blast radius into
41
+ independent, non-overlapping neighborhoods (split on file boundaries so no two agents touch
42
+ the same file, AND assign each plan card to exactly one agent — two agents calling
43
+ update_card on the same card race, and the later write silently clobbers the earlier) and
44
+ fan out a sub-agent per neighborhood in parallel; use one agent when the change is small or
45
+ files overlap. Delegating keeps your context clean and lets you hold the
46
+ macro view. ALWAYS verify the sub-agents' work yourself after they have all finished — re-read
47
+ each change against its cards and run the build/tests; never trust their reports alone — then
48
+ set the sync point once.
32
49
 
33
50
  For migrations or large scaffolds, use create_cards and add_connections (batched, one
34
51
  lint pass) instead of many single calls — connections between cards in the same batch
@@ -40,11 +57,60 @@ The plan folder is found by walking up from the working directory, BOUNDED by th
40
57
  root (it never adopts another repo's plan). If no plan exists in this repo (tools return
41
58
  NO_PLAN_FOUND), call init_plan once — create_card works immediately after.
42
59
 
60
+ When building or auditing a plan, act as a senior engineer and architect advising the user,
61
+ not a scribe taking dictation: don't assume they know everything — bring expertise, name
62
+ trade-offs and risks, propose what's missing, and explain the why so they can decide. Hold
63
+ the bar high and with integrity: do it right, be honest about built-vs-planned and
64
+ verified-vs-assumed, and surface uncertainty rather than papering over it. But don't
65
+ over-engineer — there's elegance in simplicity: calibrate to the project's scope, recommend
66
+ the smallest change that most improves the plan, and don't manufacture gaps to look thorough.
67
+ The aim is a plan the user would be proud to ship. Hold it to one bar above all: if every
68
+ line of code were deleted, the app could be rebuilt from the plan alone — aim for that
69
+ coverage (not volume), and whatever you couldn't rebuild is the gap.
70
+
71
+ Work macro→micro: orient (manifest, routes, folder
72
+ layout) and seed PLAN-PROJECT + a system DIAGRAM — propose a human-readable project name
73
+ and confirm it with the user (it's plan.md's name: and the viewer's title; change it
74
+ anytime via update_card on PLAN-PROJECT); then follow the DATA (DB → DATATYPE →
75
+ API → PAGE, with FLOW/STATE for paths and lifecycles) and the USER (ROLE + auth FLOW
76
+ first, then PAGE/COMPONENT and key journeys) and the EDGES (EXTERNAL/JOB/EVENT); then zoom
77
+ into central or complex areas. Read before you ask — ask the user only for intent,
78
+ priorities, and history the code can't reveal. Then find gaps IN THE PLAN: step back and
79
+ hunt blind spots the user may not have considered — missing unhappy paths and lifecycle
80
+ states, auth/permission gaps, and cross-cutting concerns plans forget (security, privacy,
81
+ observability, rate limits, pagination, migrations, testing). The mechanical checks
82
+ (check_integrity orphans, dangling refs, code-without-cards) are just hygiene. Give a
83
+ short, prioritized list of recommendations and ask about the judgment calls. For the full
84
+ method use the bootstrap_plan or audit_plan prompt. Status is planned → building → built →
85
+ verified; verify only against real code.
86
+
43
87
  To let the user browse the plan visually, start_viewer launches a local web server that
44
88
  renders the plan as an editable site and returns its URL (it scans forward from port 4747
45
89
  for a free port, so always read the actual port from the response). ALWAYS post that URL
46
90
  back to the user as a clickable link, e.g. http://localhost:4747/, and tell them the port.
47
91
  The viewer runs until stop_viewer or until this server process exits.`;
92
+ // The full plan-from-code playbook lives in one file (skill/methodology.md), shared by the
93
+ // skill and the MCP prompts so the two can't drift. Resolve it relative to this module:
94
+ // from dist/mcp/server.js (or src/mcp/server.ts) '../..' is the package/repo root.
95
+ const METHODOLOGY_PATH = path.join(fileURLToPath(new URL('../..', import.meta.url)), 'skill', 'methodology.md');
96
+ let methodologyCache = null;
97
+ async function methodologyText() {
98
+ if (methodologyCache === null) {
99
+ methodologyCache = await readFile(METHODOLOGY_PATH, 'utf8');
100
+ }
101
+ return methodologyCache;
102
+ }
103
+ /** A prompt body = a one-line mode intro followed by the shared methodology. */
104
+ async function planPromptBody(intro) {
105
+ try {
106
+ return `${intro}\n\n${await methodologyText()}`;
107
+ }
108
+ catch {
109
+ return (`${intro}\n\n(Could not read the methodology file; follow the macro→micro summary ` +
110
+ `in the server instructions: orient, follow the data, follow the user/auth, follow ` +
111
+ `the edges, zoom in, ask only what the code can't answer, find gaps, recommend.)`);
112
+ }
113
+ }
48
114
  function ok(data) {
49
115
  return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
50
116
  }
@@ -82,7 +148,35 @@ const detailSchema = z.enum(['none', 'summary', 'full']);
82
148
  const typeSchema = z.enum(TYPE_NAMES);
83
149
  const statusSchema = z.enum(['planned', 'building', 'built', 'verified']);
84
150
  export function buildServer(options = {}) {
85
- const server = new McpServer({ name: 'constellation', version: '0.1.0' }, { instructions: INSTRUCTIONS });
151
+ const server = new McpServer({ name: 'constellation', version: '0.1.1' }, { instructions: INSTRUCTIONS });
152
+ server.registerPrompt('bootstrap_plan', {
153
+ title: 'Bootstrap a plan from the codebase',
154
+ description: 'Analyze this repository macro→micro and build (or extend) the Constellation plan: follow the data and the user/auth journeys, capture flows, diagrams, and state machines, ask the user what the code cannot answer, and flag gaps.',
155
+ }, async () => ({
156
+ messages: [
157
+ {
158
+ role: 'user',
159
+ content: {
160
+ type: 'text',
161
+ text: await planPromptBody('Bootstrap (or extend) the Constellation plan for THIS repository by analyzing its code. If no plan exists, call init_plan first. Work the method below end to end, then report the gaps you found and a short, prioritized list of recommendations.'),
162
+ },
163
+ },
164
+ ],
165
+ }));
166
+ server.registerPrompt('audit_plan', {
167
+ title: 'Audit the plan against the code',
168
+ description: 'Reconcile the existing plan with the codebase: find gaps, orphans, stale or missing cards, and dangling refs; verify card statuses against the real code; and make tasteful recommendations.',
169
+ }, async () => ({
170
+ messages: [
171
+ {
172
+ role: 'user',
173
+ content: {
174
+ type: 'text',
175
+ text: await planPromptBody('Audit the existing Constellation plan for THIS repository against its code. Lean on Steps 7–8 (find gaps, recommend) and on verifying statuses against real code, but use the whole method as a checklist. Report what is missing, stale, or orphaned, then a short, prioritized list of recommendations.'),
176
+ },
177
+ },
178
+ ],
179
+ }));
86
180
  async function planRoot() {
87
181
  return options.planRoot ?? resolvePlanDir();
88
182
  }
@@ -107,20 +201,25 @@ export function buildServer(options = {}) {
107
201
  };
108
202
  }
109
203
  server.registerTool('init_plan', {
110
- description: 'Bootstrap a new plan: create a constellation/ folder with a starter plan.md. Use only when no plan exists yet (other tools return NO_PLAN_FOUND). After this, create_card works immediately.',
204
+ description: 'Bootstrap a new plan: create a constellation/ folder with a starter plan.md. Use only when no plan exists yet (other tools return NO_PLAN_FOUND). Pass name to set the project name (shown as the viewer title); if omitted it defaults to a title-cased folder name (pyramid-server → "Pyramid Server"). Propose a name, confirm it with the user, and change it anytime via update_card on PLAN-PROJECT. After this, create_card works immediately.',
111
205
  inputSchema: {
112
206
  path: z
113
207
  .string()
114
208
  .optional()
115
209
  .describe('directory to create constellation/ in (default: cwd)'),
210
+ name: z
211
+ .string()
212
+ .optional()
213
+ .describe('project name for plan.md (default: a title-cased folder name)'),
116
214
  },
117
- }, async ({ path: target }) => {
215
+ }, async ({ path: target, name }) => {
118
216
  try {
119
217
  const { initPlan } = await import('../core/scaffold.js');
120
- const created = await initPlan(target ?? process.cwd());
218
+ const { root: created, name: projectName } = await initPlan(target ?? process.cwd(), { name });
121
219
  return ok({
122
220
  created,
123
- next: 'Add cards with create_card; update plan.md via update_card on PLAN-PROJECT.',
221
+ name: projectName,
222
+ next: `Plan created with project name "${projectName}" (the viewer title). Confirm the name with the user — change it anytime via update_card on PLAN-PROJECT (patch.name). Then add cards with create_card.`,
124
223
  });
125
224
  }
126
225
  catch (err) {
@@ -406,6 +505,7 @@ export function buildServer(options = {}) {
406
505
  additions.get(f).add(t);
407
506
  }
408
507
  let added = 0;
508
+ const touched = new Set();
409
509
  for (const [src, targets] of additions) {
410
510
  const card = index.cards.get(src);
411
511
  const existingList = Array.isArray(card.frontmatter.connections)
@@ -414,10 +514,11 @@ export function buildServer(options = {}) {
414
514
  const merged = [...new Set([...existingList, ...targets])];
415
515
  const frontmatter = applyCardPatch(card.frontmatter, { connections: merged });
416
516
  await updateCardFile(card.filePath, { frontmatter });
517
+ touched.add(card.relPath);
417
518
  added += targets.size;
418
519
  }
419
520
  const lint = await lintPlan(root);
420
- return ok({ added, failed, errors: lint.errors });
521
+ return ok({ added, failed, issues: lint.issues.filter((i) => touched.has(i.file)) });
421
522
  }));
422
523
  server.registerTool('update_card', {
423
524
  description: 'Update a card. patch.name/kind/status set or delete (null); patch.connections replaces the list; patch.fields deep-merges into type-specific frontmatter (arrays replace, null deletes). body replaces the whole body. Body-only updates never reformat frontmatter.',
@@ -497,7 +598,12 @@ export function buildServer(options = {}) {
497
598
  connections: [...existing, to.handle],
498
599
  });
499
600
  await updateCardFile(from.filePath, { frontmatter });
500
- return ok({ connected: [from.handle, to.handle], declared_on: from.handle });
601
+ const lint = await lintPlan(root);
602
+ return ok({
603
+ connected: [from.handle, to.handle],
604
+ declared_on: from.handle,
605
+ issues: issuesForFile(lint.issues, from.relPath),
606
+ });
501
607
  }));
502
608
  server.registerTool('remove_connection', {
503
609
  description: 'Remove a connection by deleting it from either card’s connections list. Reports if the cards remain connected through other sources (frontmatter fields, body links, mermaid) that must be edited manually.',
@@ -526,7 +632,8 @@ export function buildServer(options = {}) {
526
632
  removedFrom.push(card.handle);
527
633
  }
528
634
  }
529
- const after = await loadPlan(root);
635
+ const lint = await lintPlan(root);
636
+ const after = lint.index;
530
637
  const stillConnected = after.connectedHandles.get(cardA.handle)?.has(cardB.handle) ?? false;
531
638
  const remainingSources = [];
532
639
  if (stillConnected) {
@@ -542,10 +649,14 @@ export function buildServer(options = {}) {
542
649
  remainingSources.push(`mermaid block in ${card.handle}`);
543
650
  }
544
651
  }
652
+ const touched = new Set(removedFrom
653
+ .map((h) => after.cards.get(h)?.relPath)
654
+ .filter((p) => Boolean(p)));
545
655
  return ok({
546
656
  removed_from: removedFrom,
547
657
  still_connected: stillConnected,
548
658
  remaining_sources: remainingSources,
659
+ issues: lint.issues.filter((i) => touched.has(i.file)),
549
660
  });
550
661
  }));
551
662
  server.registerTool('check_integrity', {