@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.
- package/README.md +10 -3
- package/dist/cli/index.js +13 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/update-check.d.ts +6 -0
- package/dist/cli/update-check.js +70 -0
- package/dist/cli/update-check.js.map +1 -0
- package/dist/core/resolve.js +10 -1
- package/dist/core/resolve.js.map +1 -1
- package/dist/core/scaffold.d.ts +16 -3
- package/dist/core/scaffold.js +30 -7
- package/dist/core/scaffold.js.map +1 -1
- package/dist/mcp/server.js +124 -13
- package/dist/mcp/server.js.map +1 -1
- package/docs/002-mcp.md +26 -7
- package/examples/constellation/plan.md +1 -1
- package/package.json +3 -1
- package/skill/SKILL.md +66 -1
- package/skill/methodology.md +211 -0
- package/viewer/dist/assets/{arc-Kj6pF3JI.js → arc-z-d28NWT.js} +1 -1
- package/viewer/dist/assets/architecture-7EHR7CIX-QBvJmJV9.js +1 -0
- package/viewer/dist/assets/{architectureDiagram-3BPJPVTR-C5bZdErB.js → architectureDiagram-3BPJPVTR-CjoeulX0.js} +1 -1
- package/viewer/dist/assets/{blockDiagram-GPEHLZMM-C1Q6l6fE.js → blockDiagram-GPEHLZMM-BGlWRy-x.js} +1 -1
- package/viewer/dist/assets/{c4Diagram-AAUBKEIU-BmM6Tmtq.js → c4Diagram-AAUBKEIU-B09gKF2I.js} +1 -1
- package/viewer/dist/assets/channel-COWeTobO.js +1 -0
- package/viewer/dist/assets/{chunk-2J33WTMH-z09tLTpZ.js → chunk-2J33WTMH-Cfc4xM5m.js} +1 -1
- package/viewer/dist/assets/{chunk-3OPIFGDE-BynpXh1r.js → chunk-3OPIFGDE-CfJIsbrs.js} +1 -1
- package/viewer/dist/assets/{chunk-4BX2VUAB-CDOVuPyG.js → chunk-4BX2VUAB-B1J5W5bz.js} +1 -1
- package/viewer/dist/assets/{chunk-55IACEB6-nBwigOgn.js → chunk-55IACEB6-Bjg14U7h.js} +1 -1
- package/viewer/dist/assets/{chunk-5ZQYHXKU-Bxe5xIy_.js → chunk-5ZQYHXKU-BCvNFwZ5.js} +1 -1
- package/viewer/dist/assets/{chunk-727SXJPM-DZmTgL68.js → chunk-727SXJPM-XfiLvG1c.js} +1 -1
- package/viewer/dist/assets/{chunk-AQP2D5EJ-B7wr_Owx.js → chunk-AQP2D5EJ-BS-Yzcyy.js} +1 -1
- package/viewer/dist/assets/{chunk-BSJP7CBP-DbAKfVCK.js → chunk-BSJP7CBP-DdbVG_ma.js} +1 -1
- package/viewer/dist/assets/{chunk-CSCIHK7Q-C0rsBwqP.js → chunk-CSCIHK7Q-C_v3KOxf.js} +1 -1
- package/viewer/dist/assets/{chunk-FMBD7UC4-BAtzt0wv.js → chunk-FMBD7UC4-Djdrd8lQ.js} +1 -1
- package/viewer/dist/assets/{chunk-KSCS5N6A-CXXwf52I.js → chunk-KSCS5N6A-CLg-rwxg.js} +1 -1
- package/viewer/dist/assets/{chunk-L5ZTLDWV-DS4vRI1U.js → chunk-L5ZTLDWV-DCOC9Gbq.js} +1 -1
- package/viewer/dist/assets/chunk-LZXEDZCA-D_cw-C3l.js +2 -0
- package/viewer/dist/assets/{chunk-ND2GUHAM-CUSnPl8t.js → chunk-ND2GUHAM-7krqLOpO.js} +1 -1
- package/viewer/dist/assets/{chunk-NZK2D7GU-Dh986nJk.js → chunk-NZK2D7GU-BHqkidnq.js} +1 -1
- package/viewer/dist/assets/{chunk-O5CBEL6O-JAEZ_pS6.js → chunk-O5CBEL6O-D953Jfjo.js} +1 -1
- package/viewer/dist/assets/chunk-QZHKN3VN-D7xg9F2p.js +1 -0
- package/viewer/dist/assets/chunk-WU5MYG2G-CWG30QUB.js +1 -0
- package/viewer/dist/assets/{chunk-XPW4576I-BwMZI0gv.js → chunk-XPW4576I-tr-y3uUk.js} +1 -1
- package/viewer/dist/assets/classDiagram-4FO5ZUOK-BsU5cU-N.js +1 -0
- package/viewer/dist/assets/classDiagram-v2-Q7XG4LA2-BsU5cU-N.js +1 -0
- package/viewer/dist/assets/{cose-bilkent-S5V4N54A-NGC7gYHM.js → cose-bilkent-S5V4N54A-RHqSF1Fo.js} +1 -1
- package/viewer/dist/assets/{dagre-BM42HDAG-RD63uyvd.js → dagre-BM42HDAG-CaRlC_3n.js} +1 -1
- package/viewer/dist/assets/{diagram-2AECGRRQ-hwnqqCcb.js → diagram-2AECGRRQ-zBc7NQxu.js} +1 -1
- package/viewer/dist/assets/{diagram-5GNKFQAL-q8EaoZSG.js → diagram-5GNKFQAL-CdFUltct.js} +1 -1
- package/viewer/dist/assets/{diagram-KO2AKTUF-D4_5Qf-l.js → diagram-KO2AKTUF-DKBMUmIp.js} +1 -1
- package/viewer/dist/assets/{diagram-LMA3HP47-D8pwekFs.js → diagram-LMA3HP47-aj0REjiQ.js} +1 -1
- package/viewer/dist/assets/{diagram-OG6HWLK6-D9KinIWZ.js → diagram-OG6HWLK6-Bqtmu-Nc.js} +1 -1
- package/viewer/dist/assets/{dist-CFOOgrqc.js → dist-CkpbBMm1.js} +1 -1
- package/viewer/dist/assets/{erDiagram-TEJ5UH35-D0Wfq250.js → erDiagram-TEJ5UH35-_EpsTRRO.js} +1 -1
- package/viewer/dist/assets/eventmodeling-FCH6USID-BEUuQhV3.js +1 -0
- package/viewer/dist/assets/{flowDiagram-I6XJVG4X-Y2DY-Ze2.js → flowDiagram-I6XJVG4X-ChGqvvgX.js} +1 -1
- package/viewer/dist/assets/{ganttDiagram-6RSMTGT7-BnqkeLVw.js → ganttDiagram-6RSMTGT7-BjcMfREp.js} +1 -1
- package/viewer/dist/assets/{gitGraph-WXDBUCRP-Cft7usRT.js → gitGraph-WXDBUCRP-DfwN1rQD.js} +1 -1
- package/viewer/dist/assets/{gitGraphDiagram-PVQCEYII-D-cYtraK.js → gitGraphDiagram-PVQCEYII-ClI7X9vN.js} +1 -1
- package/viewer/dist/assets/index-BRID9hB2.js +98 -0
- package/viewer/dist/assets/index-CI5JmpBT.css +2 -0
- package/viewer/dist/assets/{info-J43DQDTF-Djc8Bx3F.js → info-J43DQDTF-CofQIoJ2.js} +1 -1
- package/viewer/dist/assets/{infoDiagram-5YYISTIA-D-ehtyyJ.js → infoDiagram-5YYISTIA-D2BcKzo_.js} +1 -1
- package/viewer/dist/assets/{ishikawaDiagram-YF4QCWOH-Ct3f6bH-.js → ishikawaDiagram-YF4QCWOH-CXw1XdbZ.js} +1 -1
- package/viewer/dist/assets/{journeyDiagram-JHISSGLW-DXlULEmi.js → journeyDiagram-JHISSGLW-3Jhqf-KB.js} +1 -1
- package/viewer/dist/assets/{kanban-definition-UN3LZRKU-3vE9h-R7.js → kanban-definition-UN3LZRKU-ClSj7gDE.js} +1 -1
- package/viewer/dist/assets/{line-B8MygbLB.js → line-DeameHHf.js} +1 -1
- package/viewer/dist/assets/{linear-CfMuM0B3.js → linear-BDDnY--V.js} +1 -1
- package/viewer/dist/assets/{mermaid-parser.core-DzlZTbbh.js → mermaid-parser.core-Cah87FZ1.js} +2 -2
- package/viewer/dist/assets/{mermaid.core-IM-sPiyq.js → mermaid.core-lOcajBDU.js} +3 -3
- package/viewer/dist/assets/{mindmap-definition-RKZ34NQL-CMnpAq1T.js → mindmap-definition-RKZ34NQL-CebNj7Vy.js} +1 -1
- package/viewer/dist/assets/{packet-YPE3B663-D44AzgHh.js → packet-YPE3B663-BlKTjmdv.js} +1 -1
- package/viewer/dist/assets/{pie-LRSECV5Y-DL8AVJH_.js → pie-LRSECV5Y-CHjlSKV3.js} +1 -1
- package/viewer/dist/assets/{pieDiagram-4H26LBE5-FvKK5jd7.js → pieDiagram-4H26LBE5-zWcP8Zx3.js} +1 -1
- package/viewer/dist/assets/{quadrantDiagram-W4KKPZXB-CmjSkU8c.js → quadrantDiagram-W4KKPZXB-BSBO8usz.js} +1 -1
- package/viewer/dist/assets/{radar-GUYGQ44K-BvfZTVyH.js → radar-GUYGQ44K-BDmaTdGw.js} +1 -1
- package/viewer/dist/assets/{requirementDiagram-4Y6WPE33-BOjca3VH.js → requirementDiagram-4Y6WPE33-C1wh6GaA.js} +1 -1
- package/viewer/dist/assets/{sankeyDiagram-5OEKKPKP-ANcjfNix.js → sankeyDiagram-5OEKKPKP-L3mqPVcH.js} +1 -1
- package/viewer/dist/assets/{sequenceDiagram-3UESZ5HK-BLQ9AL7I.js → sequenceDiagram-3UESZ5HK-7vbfvJiz.js} +1 -1
- package/viewer/dist/assets/src-CslsSLVV.js +1 -0
- package/viewer/dist/assets/{stateDiagram-AJRCARHV-D6CriBS6.js → stateDiagram-AJRCARHV-CfEgJmUy.js} +1 -1
- package/viewer/dist/assets/stateDiagram-v2-BHNVJYJU-Dj6oCjNw.js +1 -0
- package/viewer/dist/assets/{timeline-definition-PNZ67QCA-BNhWZ_DL.js → timeline-definition-PNZ67QCA-DMhMwUyE.js} +1 -1
- package/viewer/dist/assets/{treeView-BLDUP644-CY6Ph5Pu.js → treeView-BLDUP644-aGCNEij1.js} +1 -1
- package/viewer/dist/assets/{treemap-LRROVOQU-DChSA_Qx.js → treemap-LRROVOQU-DTgPn5R6.js} +1 -1
- package/viewer/dist/assets/{vennDiagram-CIIHVFJN-C01WznAC.js → vennDiagram-CIIHVFJN-BZygSp3k.js} +1 -1
- package/viewer/dist/assets/{wardley-L42UT6IY-BJ8uNoJu.js → wardley-L42UT6IY-BSxi2k9r.js} +1 -1
- package/viewer/dist/assets/{wardleyDiagram-YWT4CUSO-DwDEzlVm.js → wardleyDiagram-YWT4CUSO-ZBjbz3Mj.js} +1 -1
- package/viewer/dist/assets/{xychartDiagram-2RQKCTM6-BCvIDwU0.js → xychartDiagram-2RQKCTM6-VFJI2tv3.js} +1 -1
- package/viewer/dist/index.html +3 -3
- package/viewer/dist/assets/architecture-7EHR7CIX-CGfWeim3.js +0 -1
- package/viewer/dist/assets/channel-19IdUS_c.js +0 -1
- package/viewer/dist/assets/chunk-LZXEDZCA-CclT9MXr.js +0 -2
- package/viewer/dist/assets/chunk-QZHKN3VN-BKc_Kg2Z.js +0 -1
- package/viewer/dist/assets/chunk-WU5MYG2G-9ssTSMzt.js +0 -1
- package/viewer/dist/assets/classDiagram-4FO5ZUOK-DXv85WFd.js +0 -1
- package/viewer/dist/assets/classDiagram-v2-Q7XG4LA2-DXv85WFd.js +0 -1
- package/viewer/dist/assets/eventmodeling-FCH6USID-D3KRSuC1.js +0 -1
- package/viewer/dist/assets/index-CDR-riG2.css +0 -2
- package/viewer/dist/assets/index-DRPsTWe2.js +0 -98
- package/viewer/dist/assets/src-CAMdANUp.js +0 -1
- 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).
|
|
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**,
|
|
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) {
|
package/dist/cli/index.js.map
CHANGED
|
@@ -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;
|
|
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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"}
|
package/dist/core/resolve.js
CHANGED
|
@@ -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
|
-
|
|
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);
|
package/dist/core/resolve.js.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/core/scaffold.d.ts
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
+
}>;
|
package/dist/core/scaffold.js
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import { access, mkdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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'),
|
|
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,
|
|
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"}
|
package/dist/mcp/server.js
CHANGED
|
@@ -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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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', {
|