@donotdev/mcp-server 0.0.1 → 0.0.3
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 +86 -45
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +64 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,102 +1,143 @@
|
|
|
1
1
|
# DoNotDev MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Intelligence Engine for the DoNotDev framework. Provides type intelligence, convention enforcement, WAI-WAY phase tracking, and project-level memory with a closed lessons loop.
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
7
|
-
###
|
|
7
|
+
### For Consumers (via npx/bunx)
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
cd packages/mcp-server
|
|
11
|
-
bun install
|
|
12
|
-
bun run build
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
### 2. Configure Claude Code
|
|
9
|
+
Pre-configured by `dndev init`. To re-configure: `dndev agent`.
|
|
16
10
|
|
|
17
|
-
|
|
11
|
+
Manual setup — add to `.mcp.json`:
|
|
18
12
|
|
|
19
13
|
```json
|
|
20
14
|
{
|
|
21
15
|
"mcpServers": {
|
|
22
16
|
"donotdev": {
|
|
23
|
-
"command": "
|
|
24
|
-
"args": ["/
|
|
17
|
+
"command": "bunx",
|
|
18
|
+
"args": ["@donotdev/mcp-server"]
|
|
25
19
|
}
|
|
26
20
|
}
|
|
27
21
|
}
|
|
28
22
|
```
|
|
29
23
|
|
|
30
|
-
|
|
24
|
+
### For Framework Development
|
|
31
25
|
|
|
32
26
|
```json
|
|
33
27
|
{
|
|
34
28
|
"mcpServers": {
|
|
35
29
|
"donotdev": {
|
|
36
30
|
"command": "node",
|
|
37
|
-
"args": ["/
|
|
31
|
+
"args": ["/path/to/dndev/packages/mcp-server/dist/index.js"]
|
|
38
32
|
}
|
|
39
33
|
}
|
|
40
34
|
}
|
|
41
35
|
```
|
|
42
36
|
|
|
43
|
-
|
|
37
|
+
Build: `cd packages/mcp-server && bun run build`
|
|
38
|
+
|
|
39
|
+
## Tools (9)
|
|
40
|
+
|
|
41
|
+
### Phase Tracking
|
|
42
|
+
|
|
43
|
+
#### `start_phase`
|
|
44
|
+
Begin a WAI-WAY phase. Returns blueprint, agent definition, context, and lessons from previous sessions.
|
|
44
45
|
|
|
45
|
-
```
|
|
46
|
-
|
|
46
|
+
```
|
|
47
|
+
Input: { "phase": 0 }
|
|
48
|
+
Input: { "phase": 3, "module": "billing" } ← scoped to module (large projects)
|
|
49
|
+
Output: Blueprint + agent persona + context + lessons + enforcement rules
|
|
47
50
|
```
|
|
48
51
|
|
|
49
|
-
|
|
52
|
+
#### `complete_phase`
|
|
53
|
+
Validate files and submit phase for user review. Convention checks + symbol verification run automatically when files are provided. Does NOT auto-advance.
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
```
|
|
56
|
+
Input: { "files": ["src/pages/UsersPage.tsx"], "lesson": "Access rules must match spec" }
|
|
57
|
+
Output: Validation result. If clean → pending review. If issues → fix list.
|
|
58
|
+
```
|
|
52
59
|
|
|
53
|
-
|
|
60
|
+
#### `approve_phase`
|
|
61
|
+
User confirmed the phase output. Marks phase as completed.
|
|
54
62
|
|
|
55
63
|
```
|
|
56
|
-
Input:
|
|
57
|
-
Output:
|
|
64
|
+
Input: {}
|
|
65
|
+
Output: Phase approved. Next: call start_phase(2) to begin ENTITIES.
|
|
58
66
|
```
|
|
59
67
|
|
|
68
|
+
#### `get_phase_status`
|
|
69
|
+
Current phase, symbols tracked, review status, completed phases.
|
|
70
|
+
|
|
60
71
|
```
|
|
61
|
-
Input:
|
|
62
|
-
Output:
|
|
72
|
+
Input: {}
|
|
73
|
+
Output: Active: Phase 1 (SCAFFOLD), Symbols: 3, Pending review: no, Completed: 0: BRAINSTORM
|
|
63
74
|
```
|
|
64
75
|
|
|
65
|
-
###
|
|
76
|
+
### Knowledge
|
|
66
77
|
|
|
67
|
-
|
|
78
|
+
#### `lookup_symbol`
|
|
79
|
+
Get TypeScript types for any DoNotDev symbol. Reads actual `.d.ts` files. **Tracked per phase** — `complete_phase` flags components used without lookup.
|
|
68
80
|
|
|
69
81
|
```
|
|
70
|
-
Input:
|
|
71
|
-
Output:
|
|
82
|
+
Input: { "symbol": "DataTable" }
|
|
83
|
+
Output: Full DataTableProps interface with JSDoc from @donotdev/crud
|
|
72
84
|
```
|
|
73
85
|
|
|
74
|
-
|
|
86
|
+
#### `get_guide`
|
|
87
|
+
Fetch framework setup guides from `guides/dndev/`.
|
|
75
88
|
|
|
76
|
-
|
|
89
|
+
```
|
|
90
|
+
Input: { "topic": "CRUD" }
|
|
91
|
+
Output: CRUD setup guide content
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### `get_guideline`
|
|
95
|
+
Fetch architecture guidelines. Supports `topic:section` for deep dives.
|
|
77
96
|
|
|
78
97
|
```
|
|
79
|
-
Input:
|
|
80
|
-
Output:
|
|
98
|
+
Input: { "topic": "styling:colors" }
|
|
99
|
+
Output: Colors section of styling guideline
|
|
81
100
|
```
|
|
82
101
|
|
|
83
|
-
|
|
102
|
+
#### `search_framework`
|
|
103
|
+
Search across all guides and exported symbols.
|
|
84
104
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
105
|
+
```
|
|
106
|
+
Input: { "query": "EntityList" }
|
|
107
|
+
Output: [SYMBOL] @donotdev/crud: EntityList, EntityListProps
|
|
108
|
+
[GUIDE] CRUD_SETUP.md
|
|
109
|
+
```
|
|
90
110
|
|
|
91
|
-
|
|
111
|
+
### Memory
|
|
92
112
|
|
|
93
|
-
|
|
113
|
+
#### `record_lesson`
|
|
114
|
+
Append a lesson to `.dndev/LESSONS.md`. Auto-tags with current phase. **Lessons are returned on the next `start_phase` call** — closing the loop.
|
|
94
115
|
|
|
95
|
-
```
|
|
96
|
-
|
|
116
|
+
```
|
|
117
|
+
Input: { "lesson": "Firebase emulator must be running for auth tests" }
|
|
118
|
+
Output: Lesson recorded. It will be included in the next start_phase() call.
|
|
119
|
+
```
|
|
97
120
|
|
|
98
|
-
|
|
99
|
-
- `lookup_component` to get the actual props
|
|
121
|
+
## How It Works
|
|
100
122
|
|
|
101
|
-
NEVER guess props. If MCP unavailable, ask user.
|
|
102
123
|
```
|
|
124
|
+
start_phase(N) → blueprint + context + lessons
|
|
125
|
+
↓
|
|
126
|
+
work → follow blueprint, lookup_symbol for every component
|
|
127
|
+
↓
|
|
128
|
+
complete_phase(files) → convention check + symbol verification + submit for review
|
|
129
|
+
↓
|
|
130
|
+
[user reviews]
|
|
131
|
+
↓
|
|
132
|
+
approve_phase() → phase done, advance
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
No MCP-level file gating. The IDE handles file write approval natively.
|
|
136
|
+
|
|
137
|
+
## Security
|
|
138
|
+
|
|
139
|
+
- **Path traversal protection:** File paths in `complete_phase` are resolved and verified against project root
|
|
140
|
+
- **Project root discovery:** Walks up from cwd looking for `AI.md`, `.dndev/`, or `package.json`
|
|
141
|
+
- **Lessons capped:** Only the last 50 lines of LESSONS.md are returned to prevent context bloat
|
|
142
|
+
- **Protocol versioning:** Auto-resets on version mismatch (prevents stale state)
|
|
143
|
+
- **Bounded search:** `findNodeModules` limited to 10 directory levels
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,67 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Server as
|
|
3
|
-
|
|
4
|
-
`)
|
|
5
|
-
${
|
|
6
|
-
|
|
2
|
+
import{Server as oe}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as se}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as ie,ListToolsRequestSchema as re}from"@modelcontextprotocol/sdk/types.js";import{readFileSync as $,existsSync as m,readdirSync as T,statSync as ae,writeFileSync as I,mkdirSync as D}from"fs";import{join as h,resolve as W}from"path";const G=2,Z=10;function ce(){let r=process.cwd();for(let s=0;s<Z;s++){if(m(h(r,"AI.md"))||m(h(r,".dndev"))||m(h(r,"package.json")))return r;const c=W(r,"..");if(c===r)break;r=c}return process.cwd()}const w=ce(),x=h(w,".dndev"),q=h(x,"protocol.json"),L=h(x,"LESSONS.md"),j=h(x,"implementation.md"),B=h(x,"captain-log.json"),le=h(w,"docs","architecture"),H=h(w,"packages","cli","templates","root-consumer","guides","dndev"),C=[{id:0,name:"BRAINSTORM",agent:"extractor",blueprint:"0_brainstorm",agentFile:"extractor"},{id:1,name:"SCAFFOLD",agent:"extractor",blueprint:"1_scaffold",agentFile:"extractor"},{id:2,name:"ENTITIES",agent:"architect",blueprint:"2_entities",agentFile:"architect"},{id:3,name:"COMPOSE",agent:"builder",blueprint:"3_compose",agentFile:"builder"},{id:4,name:"CONFIGURE",agent:"polisher",blueprint:"4_configure",agentFile:"polisher"}],F={version:G,currentPhase:null,phaseName:null,agent:null,startedAt:null,completedPhases:[],lookedUpSymbols:[],pendingReview:!1,currentModule:null};function k(){if(!m(q))return{...F,completedPhases:[],lookedUpSymbols:[]};try{const r=JSON.parse($(q,"utf-8"));return r.version!==G?{...F,completedPhases:[],lookedUpSymbols:[]}:{...F,...r}}catch{return{...F,completedPhases:[],lookedUpSymbols:[]}}}function U(r){m(x)||D(x,{recursive:!0}),I(q,JSON.stringify(r,null,2))}function pe(r){const s=W(w,r);return s.startsWith(w)?s:null}function ue(r){if(!m(L))return"";const s=$(L,"utf-8");if(!r)return s;const c=s.split(`
|
|
3
|
+
`),l=[];for(const n of c){if(!n.startsWith("- [")){l.push(n);continue}const o=n.match(/\[Phase (\d+):/),t=o?parseInt(o[1]):null,a=[...n.matchAll(/\[([^\]]*)\]/g)].map(u=>u[1]).filter(u=>!/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(u)&&!/^Phase \d+:/.test(u)).flatMap(u=>u.split(",").map(g=>g.trim().toLowerCase())).filter(Boolean),p=a.length===0,f=r.phase!==void 0&&t!==null&&(t===r.phase||t===r.phase-1),d=!!r.module&&a.includes(r.module.toLowerCase());(p||f||d)&&l.push(n)}const e=l.join(`
|
|
4
|
+
`).trim();return!e||e==="# Lessons Learned"?"":e}function Q(r,s="",c){const l=c&&c.length>0?`[${c.join(", ")}]`:"",e=`
|
|
5
|
+
- [${new Date().toLocaleDateString()}]${s}${l} ${r}`;m(x)||D(x,{recursive:!0}),m(L)?I(L,$(L,"utf-8")+e):I(L,`# Lessons Learned
|
|
6
|
+
`+e)}function O(){return m(j)?$(j,"utf-8"):null}function K(r){m(x)||D(x,{recursive:!0}),I(j,r)}function E(r,s){const c=r.split(`
|
|
7
|
+
`),l=[];let e=null,n=0,o=0;for(const i of c)/^###?\s/.test(i)&&!i.startsWith("# Implementation")&&(e&&l.push(e),e={title:i.replace(/^#+\s*/,""),total:0,done:0}),/^\s*- \[x\]/i.test(i)?(n++,o++,e&&e.done++,e&&e.total++):/^\s*- \[ \]/.test(i)&&(o++,e&&e.total++);e&&l.push(e);let t=r;if(s){const i=s.toLowerCase(),a=[];let p=!1;for(const f of c)/^###?\s/.test(f)&&(p=f.toLowerCase().includes(i)),p&&a.push(f);t=a.length>0?a.join(`
|
|
8
|
+
`):r}return{stats:{total:o,done:n,pending:o-n,percent:o>0?Math.round(n/o*100):0},sections:l.filter(i=>i.total>0),filtered:t}}function de(r){const s=["# Implementation Progress","",`**Last updated:** ${new Date().toISOString()}`,"**Current phase:** 0 - BRAINSTORM",""];for(const c of r){s.push(`### ${c.title}`);for(const l of c.items)s.push(`- [ ] ${l}`);s.push("")}return s.push("## Notes",""),s.join(`
|
|
9
|
+
`)}function me(){const r=V();if(!r)return null;const s=Y(r,"spec_template");if(!s)return null;const c=[{title:"Phase 0: BRAINSTORM",items:["Spec complete","User validated"]}],l=s.matchAll(/### Entity:\s*\[?(\w+)\]?/g),e=[];for(const t of l)t[1]&&t[1]!=="Name"&&e.push(t[1]);const n=s.matchAll(/\|\s*`?\/[\w-]*`?\s*\|\s*(\w+Page)\s*\|/g),o=[];for(const t of n)t[1]&&o.push(t[1]);return c.push({title:"Phase 1: SCAFFOLD",items:o.length>0?o.map(t=>`${t}.tsx`):["(generate from validated spec)"]}),c.push({title:"Phase 2: ENTITIES",items:e.length>0?e.map(t=>`${t} entity`):["(generate from validated spec)"]}),c.push({title:"Phase 3: COMPOSE",items:o.length>0?o.map(t=>`${t} composition`):["(generate from validated spec)"]}),c.push({title:"Phase 4: CONFIGURE",items:["Tests generated","Firestore rules","Config finalized","Mobile check"]}),c}function fe(r,s){const c=O();if(!c)return;const l=c.replace(/\*\*Current phase:\*\*\s*.*/,`**Current phase:** ${r} - ${s}`);l!==c&&K(l.replace(/\*\*Last updated:\*\*\s*.*/,`**Last updated:** ${new Date().toISOString()}`))}function M(){if(!m(B))return{project:w.split("/").pop()||"unknown",started:new Date().toISOString().split("T")[0],sessions:[]};try{return JSON.parse($(B,"utf-8"))}catch{return{project:w.split("/").pop()||"unknown",started:new Date().toISOString().split("T")[0],sessions:[]}}}function he(r){const s=M();s.sessions.push(r),m(x)||D(x,{recursive:!0}),I(B,JSON.stringify(s,null,2))}const J=["@donotdev/core","@donotdev/ui","@donotdev/components","@donotdev/templates","@donotdev/crud","@donotdev/auth","@donotdev/billing","@donotdev/oauth","@donotdev/adv-comps","@donotdev/firebase","@donotdev/functions","@donotdev/mcp-server"],ge=["@donotdev/cli"],ye=[...J,...ge];function $e(){let r=w;for(let s=0;s<Z;s++){const c=h(r,"node_modules");if(m(c))return c;const l=W(r,"..");if(l===r)break;r=l}return null}function ee(){const r=h(w,"guides","dndev");return m(r)?r:m(H)?H:null}function V(){const r=h(w,"guides","wai-way");if(m(r))return r;const s=h(w,"packages","cli","templates","root-consumer","guides","wai-way");return m(s)?s:null}function Y(r,s){for(const c of[".md",".md.example"]){const l=h(r,s+c);if(m(l))return $(l,"utf-8")}return null}function z(r){const s=[];if(!m(r))return s;const c=T(r);for(const l of c){const e=h(r,l);try{ae(e).isDirectory()?s.push(...z(e)):l.endsWith(".d.ts")&&s.push(e)}catch{}}return s}function Se(r,s){const c=new RegExp(`(\\/\\*\\*[\\s\\S]*?\\*\\/\\s*)?(?:export\\s+)?(?:interface|type|const|function|class)\\s+${s}\\b[\\s\\S]*?(?:;|\\{(?:[^{}]*|\\{[^{}]*\\})*\\})`,"g"),l=r.match(c);return l?l[0].trim():null}function xe(r){const s=[],c=r.matchAll(/export\s*\{([^}]+)\}/g);for(const t of c)if(t[1]){const i=t[1].split(",").map(a=>a.trim().split(" ")[0]).filter(a=>!!a);s.push(...i)}const l=r.matchAll(/export\s+interface\s+(\w+)/g);for(const t of l)t[1]&&s.push(t[1]);const e=r.matchAll(/export\s+type\s+(\w+)/g);for(const t of e)t[1]&&s.push(t[1]);const n=r.matchAll(/export\s+const\s+(\w+)/g);for(const t of n)t[1]&&s.push(t[1]);const o=r.matchAll(/export\s+default\s+(\w+)/g);for(const t of o)t[1]&&s.push(t[1]);return[...new Set(s)].filter(t=>t&&t!=="default")}function ve(r){if(!m(r))return null;try{const s=$(r,"utf-8").split(`
|
|
10
|
+
`);let c=!1;for(const l of s){if(l.startsWith("# ")){c=!0;continue}if(c&&l.trim().length>0)return l.trim()}}catch{}return null}function te(r,s){const c=m(h(r,"context_map.json"))?h(r,"context_map.json"):h(r,"context_map.json.example");if(!m(c))return null;try{const l=JSON.parse($(c,"utf-8")),n=Object.keys(l.phases||{}).find(o=>o.startsWith(`${s}_`));if(n&&l.phases[n])return l.phases[n]}catch{}return null}function we(r){const s=[];if(!m(r))return[`File not found: ${r}`];const l=$(r,"utf-8").split(`
|
|
11
|
+
`),e=r.split("/").pop()||"";if(!e.endsWith(".tsx")&&!e.endsWith(".ts"))return[];for(let o=0;o<l.length;o++){const t=l[o],i=o+1;/style\s*=\s*\{\{/.test(t)&&s.push(`L${i}: Inline style found. Use className or framework utilities.`),/fontSize|font-size/i.test(t)&&!/\/\/|\/\*|\*/.test(t.trimStart().substring(0,2))&&s.push(`L${i}: fontSize detected. Use Text level or Card title/subtitle props.`),/text-align:\s*(left|right)|textAlign:\s*['"]?(left|right)/i.test(t)&&s.push(`L${i}: textAlign left/right detected. Use start/end for RTL support.`),/\brequire\s*\(/.test(t)&&!/\/\/|\/\*|\*/.test(t.trimStart().substring(0,2))&&s.push(`L${i}: require() found. Use ESM import.`),/\.toLocaleDateString|\.toLocaleTimeString|new Intl\.DateTimeFormat/i.test(t)&&s.push(`L${i}: Manual date formatting. Use formatDate() from @donotdev/core.`)}const n=l.map((o,t)=>({line:o,num:t+1})).filter(({line:o})=>/^import\s/.test(o));if(n.length>1){let o=0;for(const{line:t,num:i}of n){let a=4;if(/from\s+['"]react/.test(t)?a=1:/from\s+['"]@donotdev\//.test(t)?a=3:/from\s+['"][^.]/.test(t)&&(a=2),a<o){s.push(`L${i}: Import order violation. Expected: React > vendors > @donotdev > relative.`);break}o=a}}return s}function be(r,s){const c=[],l=/<([A-Z]\w+)/g;for(const e of r){if(!m(e))continue;const n=$(e,"utf-8");if(!/@donotdev\//.test(n))continue;const o=n.matchAll(l);for(const t of o){const i=t[1];i&&(["React","Fragment","Suspense","Provider","Router","Route","Switch"].includes(i)||!s.includes(i)&&!s.includes(`${i}Props`)&&c.push(`${e}: <${i}> used but lookup_symbol("${i}") was never called. Props may be hallucinated.`))}}return[...new Set(c)]}const X=new oe({name:"donotdev-mcp",version:"0.0.3"},{capabilities:{tools:{}}});X.setRequestHandler(re,async()=>({tools:[{name:"start_phase",description:"Begin a WAI-WAY phase (0-4). Returns blueprint, agent definition, context, and lessons. For large projects, pass module to scope work.",inputSchema:{type:"object",properties:{phase:{type:"number",description:"Phase number: 0=BRAINSTORM, 1=SCAFFOLD, 2=ENTITIES, 3=COMPOSE, 4=CONFIGURE"},module:{type:"string",description:'Optional module name to scope work (e.g., "user-management", "billing"). For large projects.'}},required:["phase"]}},{name:"complete_phase",description:"Validate and submit phase for user review. Pass files to run convention checks and symbol verification. Phase stays pending until user calls approve_phase.",inputSchema:{type:"object",properties:{files:{type:"array",items:{type:"string"},description:"File paths to validate (relative to project root). Convention checks + symbol usage verification run automatically."},lesson:{type:"string",description:"Optional lesson learned during this phase"},summary:{type:"string",description:`Brief outcome summary for the captain's log (e.g., "8 pages scaffolded, auth flow complete")`}}}},{name:"approve_phase",description:"User approves the completed phase after review. Only call this after complete_phase and user has confirmed the output is correct.",inputSchema:{type:"object",properties:{}}},{name:"get_phase_status",description:"Get current WAI-WAY phase, completed phases, active agent, lookup coverage, and pending review status.",inputSchema:{type:"object",properties:{}}},{name:"lookup_symbol",description:"Get JSDoc context, @examples, and TypeScript types for any DoNotDev symbol. MUST be called before using any @donotdev component.",inputSchema:{type:"object",properties:{symbol:{type:"string",description:'Symbol name (e.g., "defineEntity", "useCrud", "DataTable")'},package:{type:"string",description:"Package name (optional)"}},required:["symbol"]}},{name:"get_guide",description:"Fetch a framework setup guide from guides/dndev/ (e.g., CRUD setup, Auth setup).",inputSchema:{type:"object",properties:{topic:{type:"string",description:'Topic (e.g., "CRUD", "AUTH")'}},required:["topic"]}},{name:"get_guideline",description:"Fetch architectural guidelines for maintenance or app building.",inputSchema:{type:"object",properties:{topic:{type:"string",description:'Topic with optional section (e.g., "styling", "styling:colors")'}},required:["topic"]}},{name:"search_framework",description:"Search across ALL guides and public symbols for a keyword.",inputSchema:{type:"object",properties:{query:{type:"string",description:"Keyword or phrase"}},required:["query"]}},{name:"list_features",description:"List all framework packages with a one-line summary from each README. Call in Phase 0 before designing custom solutions.",inputSchema:{type:"object",properties:{}}},{name:"init_implementation",description:"Create .dndev/implementation.md to track progress across sessions. Pass sections manually or use from_spec to auto-generate from spec_template.md.",inputSchema:{type:"object",properties:{sections:{type:"array",items:{type:"object",properties:{title:{type:"string",description:'Section heading (e.g., "Phase 1: SCAFFOLD", "Auth Module")'},items:{type:"array",items:{type:"string"},description:"Checklist items for this section"}},required:["title","items"]},description:"Manual sections with checklist items"},from_spec:{type:"boolean",description:"Auto-generate sections from spec_template.md (entities \u2192 Phase 2, pages \u2192 Phase 1/3)"},force:{type:"boolean",description:"Overwrite existing implementation.md without warning"}}}},{name:"update_progress",description:"Tick or untick an item in .dndev/implementation.md. Matches by substring. Optionally add a note.",inputSchema:{type:"object",properties:{item:{type:"string",description:'Substring to match the checklist item (e.g., "HomePage", "Product entity")'},done:{type:"boolean",description:"true = tick [x], false = untick [ ]"},note:{type:"string",description:"Optional note appended to the Notes section"}},required:["item","done"]}},{name:"get_progress",description:"Read .dndev/implementation.md with progress stats. Optionally filter to a section.",inputSchema:{type:"object",properties:{section:{type:"string",description:'Filter to section containing this text (e.g., "Phase 1", "SCAFFOLD")'}}}},{name:"get_project_history",description:"Get the captain's log: full session history with metrics. For post-mortem, team review, and project analysis.",inputSchema:{type:"object",properties:{}}},{name:"record_lesson",description:"Record a lesson/gotcha to .dndev/LESSONS.md with inline tags. Filtered by phase + module on next start_phase. Also add a code comment where the gotcha applies.",inputSchema:{type:"object",properties:{lesson:{type:"string",description:"The lesson content (gotcha, quirk, operational knowledge)"},tags:{type:"array",items:{type:"string"},description:'Tags for filtering: entity names, service names, module names (e.g., ["stripe", "billing", "webhook"])'}},required:["lesson"]}}]})),X.setRequestHandler(ie,async r=>{const{name:s,arguments:c}=r.params,l=$e();if(s==="start_phase"){const e=c?.phase,n=c?.module,o=C.find(S=>S.id===e);if(!o)return{content:[{type:"text",text:`Invalid phase: ${e}. Valid: 0-4.`}],isError:!0};const t=V();if(!t)return{content:[{type:"text",text:"Error: guides/wai-way/ directory not found."}],isError:!0};const i=Y(h(t,"blueprints"),o.blueprint),a=Y(h(t,"agents"),o.agentFile),p=te(t,e),f=ue({phase:e,module:n}),d=k();d.version=G,d.currentPhase=o.id,d.phaseName=o.name,d.agent=o.agent,d.startedAt=new Date().toISOString(),d.lookedUpSymbols=[],d.pendingReview=!1,d.currentModule=n||null,U(d);const u=[`# Phase ${o.id}: ${o.name}`,`**Agent:** ${o.agent}`];n&&u.push(`**Module:** ${n}`);const g=d.completedPhases.sort().map(S=>{const N=C.find(y=>y.id===S);return N?N.name:`${S}`}),v=O(),P=v?E(v):null,R=M(),_=[`
|
|
12
|
+
## Project State`];if(_.push(`- **Phase:** ${o.id}/4 ${o.name}${g.length>0?` (done: ${g.join(", ")})`:""}`),P&&_.push(`- **Progress:** ${P.stats.done}/${P.stats.total} (${P.stats.percent}%)`),R.sessions.length>0&&_.push(`- **Sessions so far:** ${R.sessions.length}`),u.push(..._),p&&u.push(`
|
|
13
|
+
## Phase Context`,`**Goal:** ${p.goal}`,`**Done when:** ${p.done_when}`,`**Output:** ${p.output}`,"**Read these files first:**",...p.read_files.map(S=>`- ${S}`)),i&&u.push(`
|
|
14
|
+
---
|
|
15
|
+
## Blueprint
|
|
7
16
|
|
|
8
|
-
${
|
|
17
|
+
${i}`),a&&u.push(`
|
|
18
|
+
---
|
|
19
|
+
## Agent Definition
|
|
9
20
|
|
|
10
|
-
|
|
21
|
+
${a}`),f.trim().length>20&&u.push(`
|
|
22
|
+
---
|
|
23
|
+
## Lessons (Filtered)
|
|
24
|
+
|
|
25
|
+
${f}`),v){const{stats:S,filtered:N}=E(v,`Phase ${e}`);u.push(`
|
|
26
|
+
---
|
|
27
|
+
## Implementation Progress`,`**Overall:** ${S.done}/${S.total} (${S.percent}%)`,"",N,"",'Use `update_progress({ item: "...", done: true })` to tick items as you complete them.')}else e>0&&u.push(`
|
|
28
|
+
---
|
|
29
|
+
## Implementation Progress`,"No implementation.md found. Consider running `init_implementation({ from_spec: true })` to track progress across sessions.");return u.push(`
|
|
30
|
+
---
|
|
31
|
+
## Enforcement Rules`,'- **BEFORE using any @donotdev component:** call `lookup_symbol("ComponentName")`','- **AFTER building:** call `complete_phase({ files: [...], summary: "..." })` \u2014 validates + submits for review',"- **WHEN recording a lesson:** also add a `// GOTCHA: ...` comment in the relevant source file","- Tracked symbols so far: none (call lookup_symbol to add)"),!i&&!a&&u.push(`
|
|
32
|
+
No blueprint or agent definition found for this phase.`),{content:[{type:"text",text:u.join(`
|
|
33
|
+
`)}]}}if(s==="complete_phase"){const e=k();if(e.currentPhase===null)return{content:[{type:"text",text:"No active phase to complete."}],isError:!0};if(e.pendingReview)return{content:[{type:"text",text:"Phase already pending review. Ask the user to review the output, then call approve_phase()."}],isError:!0};const n=e.currentPhase,o=e.phaseName,t=e.currentModule?` [${e.currentModule}]`:"",i=c?.files||[],a=[],p=[];if(i.length>0){for(const b of i){const A=pe(b);if(!A){a.push(`- BLOCKED: "${b}" resolves outside project root. Skipped.`);continue}p.push(A)}for(let b=0;b<p.length;b++){const A=we(p[b]);A.length>0&&a.push(`
|
|
34
|
+
### ${i[b]}`,...A.map(ne=>`- ${ne}`))}const y=be(p,e.lookedUpSymbols);y.length>0&&(a.push(`
|
|
35
|
+
### Unverified Component Usage`),a.push(...y.map(b=>`- ${b}`)))}const f=V();if(f){const y=te(f,n);y&&a.push(`
|
|
36
|
+
### Done Criteria`,`**Required:** ${y.done_when}`,`**Expected output:** ${y.output}`,"","Verify these criteria are met.")}if(a.some(y=>y.startsWith("- L")||y.includes("Unverified Component")||y.includes("BLOCKED")))return{content:[{type:"text",text:[`## Validation Failed: Phase ${n} (${o})${t}`,"","**Status:** ISSUES FOUND \u2014 fix before completing phase",...a,"","### Type Check","Run `bunx tsc --noEmit` to verify TypeScript compiles without errors.","","Fix the issues above, then call `complete_phase({ files: [...] })` again."].join(`
|
|
37
|
+
`)}]};const u=c?.lesson;u&&Q(u,` [Phase ${n}: ${o}]`);const g=c?.summary;e.pendingLogData={files_touched:p.length,summary:g},e.pendingReview=!0,U(e);const v=e.lookedUpSymbols.length,P=C.find(y=>y.id===n+1),R=P?`Next: Phase ${P.id} (${P.name})`:"This was the final phase.";let _="";const S=O();if(S){const{stats:y}=E(S,`Phase ${n}`);_=`**Implementation:** ${y.done}/${y.total} items (${y.percent}%)`}return{content:[{type:"text",text:[`## Phase ${n} (${o})${t} \u2014 Ready for Review`,"",`**Validation:** ${i.length>0?"PASSED":"No files provided (skipped)"}`,`**Symbols looked up:** ${v} (${e.lookedUpSymbols.join(", ")||"none"})`,_,u?`**Lesson recorded:** ${u}`:"","","### User Review Required","","Present the phase output to the user. Ask them to verify:","- Does the output match what they expected?","- Are there any issues or changes needed?","","**After user confirms:** call `approve_phase()` to advance.","**If user wants changes:** make the changes, then call `complete_phase({ files: [...] })` again.","",R].filter(Boolean).join(`
|
|
38
|
+
`)}]}}if(s==="approve_phase"){const e=k();if(!e.pendingReview||e.currentPhase===null)return{content:[{type:"text",text:"No phase pending review. Call complete_phase() first."}],isError:!0};const n=e.currentPhase,o=e.phaseName,t=M();he({id:t.sessions.length+1,date:new Date().toISOString().split("T")[0],phase:n,phase_name:o||C[n]?.name||"UNKNOWN",module:e.currentModule||void 0,started_at:e.startedAt||new Date().toISOString(),completed_at:new Date().toISOString(),outcome:e.pendingLogData?.summary||`Phase ${n} (${o}) completed.`,files_touched:e.pendingLogData?.files_touched||0,symbols_used:e.lookedUpSymbols.length}),e.completedPhases.includes(n)||e.completedPhases.push(n),e.currentPhase=null,e.phaseName=null,e.agent=null,e.startedAt=null,e.pendingReview=!1,e.lookedUpSymbols=[],e.currentModule=null,e.pendingLogData=void 0,U(e);const i=C.find(p=>p.id===n+1),a=i?`Next: call start_phase(${i.id}) to begin ${i.name}.`:"All phases complete. App is ready.";return i&&fe(i.id,i.name),{content:[{type:"text",text:`Phase ${n} (${o}) approved and completed.
|
|
39
|
+
${a}`}]}}if(s==="get_phase_status"){const e=k(),n=e.completedPhases.sort().map(t=>{const i=C.find(a=>a.id===t);return i?`${i.id}: ${i.name}`:`${t}`}).join(", ");return e.currentPhase===null?{content:[{type:"text",text:`No active phase.
|
|
40
|
+
Completed: ${n||"none"}
|
|
41
|
+
Call start_phase(N) to begin a phase.`}]}:{content:[{type:"text",text:[`Active: Phase ${e.currentPhase} (${e.phaseName})`,`Agent: ${e.agent}`,`Started: ${e.startedAt}`,e.currentModule?`Module: ${e.currentModule}`:null,`Pending review: ${e.pendingReview?"YES \u2014 user must approve":"no"}`,`Symbols looked up: ${e.lookedUpSymbols.length} (${e.lookedUpSymbols.join(", ")||"none"})`,`Completed phases: ${n||"none"}`].filter(Boolean).join(`
|
|
42
|
+
`)}]}}if(s==="get_project_history"){const e=M();if(e.sessions.length===0)return{content:[{type:"text",text:"No sessions logged yet. Captain's log entries are created automatically when phases are approved."}]};const n=e.sessions.reduce((a,p)=>a+p.files_touched,0),o=e.sessions.reduce((a,p)=>a+p.symbols_used,0),t=[...new Set(e.sessions.map(a=>a.phase))].length,i=[`## Captain's Log \u2014 ${e.project}`,`**Started:** ${e.started}`,`**Sessions:** ${e.sessions.length}`,`**Phases completed:** ${t}/5`,`**Total files touched:** ${n}`,`**Total symbols used:** ${o}`,"","### Timeline"];for(const a of e.sessions){const p=a.module?` [${a.module}]`:"";i.push(`${a.id}. **[${a.date}]** Phase ${a.phase} (${a.phase_name})${p} \u2014 ${a.outcome}`)}return i.push("","Raw JSON: .dndev/captain-log.json"),{content:[{type:"text",text:i.join(`
|
|
43
|
+
`)}]}}if(s==="record_lesson"){const e=c?.lesson,n=c?.tags,o=k(),t=o.currentPhase!==null?` [Phase ${o.currentPhase}: ${o.phaseName}]`:"";return Q(e,t,n),{content:[{type:"text",text:`Lesson recorded${n&&n.length>0?` [${n.join(", ")}]`:""}. Filtered by tags on next start_phase().
|
|
44
|
+
Also add a \`// GOTCHA: ...\` comment in the relevant source file.`}]}}if(s==="init_implementation"){const e=c?.sections,n=c?.from_spec,o=c?.force;if(m(j)&&!o){const p=O(),{stats:f}=E(p);return{content:[{type:"text",text:`implementation.md already exists (${f.done}/${f.total} items done, ${f.percent}%).
|
|
45
|
+
Pass force: true to overwrite, or use update_progress/get_progress to work with existing file.`}]}}let t;if(n){const p=me();if(!p)return{content:[{type:"text",text:"Could not generate from spec: spec_template.md not found in guides/wai-way/."}],isError:!0};t=p}else if(e&&e.length>0)t=e;else return{content:[{type:"text",text:"Provide sections (array of {title, items}) or set from_spec: true."}],isError:!0};const i=de(t);K(i);const{stats:a}=E(i);return{content:[{type:"text",text:`implementation.md created with ${a.total} items across ${t.length} sections.
|
|
46
|
+
|
|
47
|
+
${i}`}]}}if(s==="update_progress"){const e=c?.item,n=c?.done,o=c?.note,t=O();if(!t)return{content:[{type:"text",text:"No implementation.md found. Call init_implementation() first."}],isError:!0};const i=t.split(`
|
|
48
|
+
`),a=e.toLowerCase();let p=!1,f="";for(let g=0;g<i.length;g++){const v=i[g];if(/^\s*- \[[ x]\]/i.test(v)&&v.toLowerCase().includes(a)){n?i[g]=v.replace(/- \[ \]/,"- [x]"):i[g]=v.replace(/- \[x\]/i,"- [ ]"),f=i[g],p=!0;break}}if(!p)return{content:[{type:"text",text:`No checklist item matching "${e}" found in implementation.md.`}],isError:!0};let d=i.join(`
|
|
49
|
+
`);if(d=d.replace(/\*\*Last updated:\*\*\s*.*/,`**Last updated:** ${new Date().toISOString()}`),o){const g=`- ${o}`;d.includes("## Notes")?d=d.replace("## Notes",`## Notes
|
|
50
|
+
${g}`):d+=`
|
|
51
|
+
## Notes
|
|
52
|
+
${g}
|
|
53
|
+
`}K(d);const{stats:u}=E(d);return{content:[{type:"text",text:`Updated: ${f.trim()}
|
|
54
|
+
Progress: ${u.done}/${u.total} (${u.percent}%)`}]}}if(s==="get_progress"){const e=c?.section,n=O();if(!n)return{content:[{type:"text",text:"No implementation.md found. Call init_implementation() first."}],isError:!0};const{stats:o,sections:t,filtered:i}=E(n,e),a=["## Implementation Progress",`**Overall:** ${o.done}/${o.total} (${o.percent}%)`,""];if(t.length>0){a.push("### By Section");for(const p of t){const f=p.total>0?Math.round(p.done/p.total*100):0;a.push(`- **${p.title}:** ${p.done}/${p.total} (${f}%)`)}a.push("")}return a.push("---","",i),{content:[{type:"text",text:a.join(`
|
|
55
|
+
`)}]}}if(!l&&!["get_guide","get_guideline"].includes(s))return{content:[{type:"text",text:"Error: node_modules not found. Run bun install first."}],isError:!0};if(s==="get_guide"){const e=c?.topic,n=ee();if(!n)return{content:[{type:"text",text:"Error: guides directory not found."}],isError:!0};const o=T(n).filter(t=>t.endsWith(".md")||t.endsWith(".md.example"));for(const t of o)if(t.toLowerCase().includes(e.toLowerCase()))return{content:[{type:"text",text:`[GUIDE] ${e} (from ${t}):
|
|
56
|
+
|
|
57
|
+
${$(h(n,t),"utf-8")}`}]};return{content:[{type:"text",text:`No guide found for: ${e}`}],isError:!0}}if(s==="get_guideline"){const e=c?.topic,[n,o]=e.split(":"),t=[{dir:H,type:"GUIDE"},{dir:le,type:"ARCHITECTURE"}];for(const i of t){if(!m(i.dir))continue;const a=T(i.dir).filter(p=>p.endsWith(".md")||p.endsWith(".md.example"));for(const p of a)if(n&&p.toLowerCase().includes(n.toLowerCase())){const f=$(h(i.dir,p),"utf-8");if(!o){const u=f.match(/^([\s\S]*?)(?=##|$)/),g=u&&u[1]?u[1].trim():"No overview found.";return{content:[{type:"text",text:`[${i.type}] Overview for ${n}:
|
|
58
|
+
|
|
59
|
+
${g}`}]}}const d=f.match(new RegExp(`##\\s+.*${o}.*\\n([\\s\\S]*?)(?=##|$)`,"i"));if(d&&d[1])return{content:[{type:"text",text:`[${i.type}] ${n} > ${o}:
|
|
60
|
+
|
|
61
|
+
${d[1].trim()}`}]}}}return{content:[{type:"text",text:`No guideline found for: ${e}`}],isError:!0}}if(s==="search_framework"){const e=(c?.query).toLowerCase(),n=[],o=ee();if(o){const t=T(o).filter(i=>i.endsWith(".md")||i.endsWith(".md.example"));for(const i of t)$(h(o,i),"utf-8").toLowerCase().includes(e)&&n.push(`[GUIDE] ${i}`)}if(l)for(const t of J){const i=h(l,t,"dist");if(m(i)){for(const a of z(i))if($(a,"utf-8").toLowerCase().includes(e)){const p=xe($(a,"utf-8")).filter(f=>f.toLowerCase().includes(e));p.length>0&&n.push(`[SYMBOL] ${t}: ${p.join(", ")}`)}}}return{content:[{type:"text",text:n.length>0?`Results:
|
|
62
|
+
${n.join(`
|
|
63
|
+
`)}`:"No results found."}]}}if(s==="lookup_symbol"){const e=c?.symbol;if(!l)return{content:[{type:"text",text:"Error: node_modules not found. Run bun install first."}],isError:!0};const n=k();n.lookedUpSymbols.includes(e)||(n.lookedUpSymbols.push(e),U(n));const o=[e,`${e}Props`,`${e.replace(/Props$/,"")}Props`],t=["@donotdev/core","@donotdev/ui","@donotdev/crud"],i=[...t,...J.filter(a=>!t.includes(a))];for(const a of i){const p=h(l,a,"dist");if(m(p))for(const f of z(p)){const d=$(f,"utf-8");for(const u of o){const g=Se(d,u);if(g)return{content:[{type:"text",text:`[TYPE INTELLIGENCE] ${a}:
|
|
64
|
+
|
|
65
|
+
${g}`}]}}}}return{content:[{type:"text",text:`Not found: ${e}`}],isError:!0}}if(s==="list_features"){if(!l)return{content:[{type:"text",text:"Error: node_modules not found. Run bun install first."}],isError:!0};const e=[`## Available Framework Features
|
|
66
|
+
`];for(const n of ye){const o=h(l,n,"README.md"),t=ve(o);e.push(`- **${n}** \u2014 ${t||"(no description available)"}`)}return e.push('\nUse `search_framework("topic")` or `get_guide("TOPIC")` to go deeper on any package.'),{content:[{type:"text",text:e.join(`
|
|
67
|
+
`)}]}}return{content:[{type:"text",text:`Unknown tool: ${s}`}],isError:!0}});async function Pe(){const r=new se;await X.connect(r)}Pe().catch(console.error);
|