@donotdev/mcp-server 0.0.3 → 0.0.4
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 +3 -16
- package/dist/index.js +59 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,11 +4,11 @@ Intelligence Engine for the DoNotDev framework. Provides type intelligence, conv
|
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
7
|
-
### For Consumers (via
|
|
7
|
+
### For Consumers (via bunx)
|
|
8
8
|
|
|
9
9
|
Pre-configured by `dndev init`. To re-configure: `dndev agent`.
|
|
10
10
|
|
|
11
|
-
Manual setup — add to `.mcp.json`:
|
|
11
|
+
Manual setup — add to `.mcp.json` / `.gemini/settings.json`:
|
|
12
12
|
|
|
13
13
|
```json
|
|
14
14
|
{
|
|
@@ -21,20 +21,7 @@ Manual setup — add to `.mcp.json`:
|
|
|
21
21
|
}
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
```json
|
|
27
|
-
{
|
|
28
|
-
"mcpServers": {
|
|
29
|
-
"donotdev": {
|
|
30
|
-
"command": "node",
|
|
31
|
-
"args": ["/path/to/dndev/packages/mcp-server/dist/index.js"]
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Build: `cd packages/mcp-server && bun run build`
|
|
24
|
+
**Note:** Use `bunx` — bun is a project requirement.
|
|
38
25
|
|
|
39
26
|
## Tools (9)
|
|
40
27
|
|
package/dist/index.js
CHANGED
|
@@ -1,67 +1,81 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Server as
|
|
3
|
-
`)
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
`)}function
|
|
10
|
-
`);
|
|
11
|
-
`)
|
|
12
|
-
|
|
13
|
-
|
|
2
|
+
import{Server as ie}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as ae}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as ce,ListToolsRequestSchema as le}from"@modelcontextprotocol/sdk/types.js";import{readFileSync as $,existsSync as d,readdirSync as I,statSync as pe,writeFileSync as R,mkdirSync as T}from"fs";import{join as f,resolve as W}from"path";const q=2,ee=10;function ue(){let r=process.cwd();for(let o=0;o<ee;o++){if(d(f(r,"AI.md"))||d(f(r,".dndev"))||d(f(r,"package.json")))return r;const a=W(r,"..");if(a===r)break;r=a}return process.cwd()}const w=ue(),x=f(w,".dndev"),G=f(x,"protocol.json"),N=f(x,"LESSONS.md"),D=f(x,"implementation.md"),B=f(x,"captain-log.json"),te=f(x,"args.json"),de=f(w,"docs","architecture"),H=f(w,"packages","cli","templates","root-consumer","guides","dndev"),O=[{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:q,currentPhase:null,phaseName:null,agent:null,startedAt:null,completedPhases:[],lookedUpSymbols:[],pendingReview:!1,currentModule:null};function L(){if(!d(G))return{...F,completedPhases:[],lookedUpSymbols:[]};try{const r=JSON.parse($(G,"utf-8"));return r.version!==q?{...F,completedPhases:[],lookedUpSymbols:[]}:{...F,...r}}catch{return{...F,completedPhases:[],lookedUpSymbols:[]}}}function U(r){d(x)||T(x,{recursive:!0}),R(G,JSON.stringify(r,null,2))}const J={platform:"firebase",strictness:"enforced",features:["crud","auth","i18n","billing","oauth","functions"],region:"europe-west1"};function ne(){if(!d(te))return{...J};try{const r=JSON.parse($(te,"utf-8"));return{...J,...r}}catch{return{...J}}}function me(){const r=z();if(!r)return[];let o=null;for(const n of["GOTCHAS.md","GOTCHAS.md.example"]){const s=f(r,n);if(d(s)){o=s;break}}if(!o)return[];const a=$(o,"utf-8"),l=[],e=a.split(/^## /m);for(const n of e){if(!n.trim())continue;const s=n.indexOf(`
|
|
3
|
+
`);if(s===-1)continue;const t=n.substring(0,s).trim(),i=n.substring(s+1).trim(),c=t.match(/\[Phase\s+([\d,\s]+)\]/i),p=[];if(c?.[1])for(const m of c[1].split(",")){const u=parseInt(m.trim());isNaN(u)||p.push(u)}p.length>0&&i&&l.push({title:t.replace(/\s*\[Phase.*?\]/,""),phases:p,content:i})}return l}function fe(r,o){const a=me();if(a.length===0)return"";const l=a.filter(s=>s.phases.includes(r));if(l.length===0)return"";const e={CRUD:"crud",Functions:"functions",i18n:"i18n"},n=o?l.filter(s=>{const t=e[s.title];return!t||o.includes(t)}):l;return n.length===0?"":n.map(s=>`### ${s.title}
|
|
4
|
+
|
|
5
|
+
${s.content}`).join(`
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
`)}function he(r){const o=W(w,r);return o.startsWith(w)?o:null}function ge(r){if(!d(N))return"";const o=$(N,"utf-8");if(!r)return o;const a=o.split(`
|
|
10
|
+
`),l=[];for(const n of a){if(!n.startsWith("- [")){l.push(n);continue}const s=n.match(/\[Phase (\d+):/),t=s?parseInt(s[1]):null,i=[...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(h=>h.trim().toLowerCase())).filter(Boolean),c=i.length===0,p=r.phase!==void 0&&t!==null&&(t===r.phase||t===r.phase-1),m=!!r.module&&i.includes(r.module.toLowerCase());(c||p||m)&&l.push(n)}const e=l.join(`
|
|
11
|
+
`).trim();return!e||e==="# Lessons Learned"?"":e}function se(r,o="",a){const l=a&&a.length>0?`[${a.join(", ")}]`:"",e=`
|
|
12
|
+
- [${new Date().toLocaleDateString()}]${o}${l} ${r}`;d(x)||T(x,{recursive:!0}),d(N)?R(N,$(N,"utf-8")+e):R(N,`# Lessons Learned
|
|
13
|
+
`+e)}function j(){return d(D)?$(D,"utf-8"):null}function V(r){d(x)||T(x,{recursive:!0}),R(D,r)}function P(r,o){const a=r.split(`
|
|
14
|
+
`),l=[];let e=null,n=0,s=0;for(const i of a)/^###?\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++,s++,e&&e.done++,e&&e.total++):/^\s*- \[ \]/.test(i)&&(s++,e&&e.total++);e&&l.push(e);let t=r;if(o){const i=o.toLowerCase(),c=[];let p=!1;for(const m of a)/^###?\s/.test(m)&&(p=m.toLowerCase().includes(i)),p&&c.push(m);t=c.length>0?c.join(`
|
|
15
|
+
`):r}return{stats:{total:s,done:n,pending:s-n,percent:s>0?Math.round(n/s*100):0},sections:l.filter(i=>i.total>0),filtered:t}}function ye(r){const o=["# Implementation Progress","",`**Last updated:** ${new Date().toISOString()}`,"**Current phase:** 0 - BRAINSTORM",""];for(const a of r){o.push(`### ${a.title}`);for(const l of a.items)o.push(`- [ ] ${l}`);o.push("")}return o.push("## Notes",""),o.join(`
|
|
16
|
+
`)}function $e(){const r=K();if(!r)return null;const o=Z(r,"spec_template");if(!o)return null;const a=[{title:"Phase 0: BRAINSTORM",items:["Spec complete","User validated"]}],l=o.matchAll(/### Entity:\s*\[?(\w+)\]?/g),e=[];for(const t of l)t[1]&&t[1]!=="Name"&&e.push(t[1]);const n=o.matchAll(/\|\s*`?\/[\w-]*`?\s*\|\s*(\w+Page)\s*\|/g),s=[];for(const t of n)t[1]&&s.push(t[1]);return a.push({title:"Phase 1: SCAFFOLD",items:s.length>0?s.map(t=>`${t}.tsx`):["(generate from validated spec)"]}),a.push({title:"Phase 2: ENTITIES",items:e.length>0?e.map(t=>`${t} entity`):["(generate from validated spec)"]}),a.push({title:"Phase 3: COMPOSE",items:s.length>0?s.map(t=>`${t} composition`):["(generate from validated spec)"]}),a.push({title:"Phase 4: CONFIGURE",items:["Tests generated","Firestore rules","Config finalized","Mobile check"]}),a}function Se(r,o){const a=j();if(!a)return;const l=a.replace(/\*\*Current phase:\*\*\s*.*/,`**Current phase:** ${r} - ${o}`);l!==a&&V(l.replace(/\*\*Last updated:\*\*\s*.*/,`**Last updated:** ${new Date().toISOString()}`))}function M(){if(!d(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 xe(r){const o=M();o.sessions.push(r),d(x)||T(x,{recursive:!0}),R(B,JSON.stringify(o,null,2))}const Y=["@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"],ve=["@donotdev/cli"],we=[...Y,...ve];function be(){let r=w;for(let o=0;o<ee;o++){const a=f(r,"node_modules");if(d(a))return a;const l=W(r,"..");if(l===r)break;r=l}return null}function z(){const r=f(w,"guides","dndev");return d(r)?r:d(H)?H:null}function K(){const r=f(w,"guides","wai-way");if(d(r))return r;const o=f(w,"packages","cli","templates","root-consumer","guides","wai-way");return d(o)?o:null}function Z(r,o){for(const a of[".md",".md.example"]){const l=f(r,o+a);if(d(l))return $(l,"utf-8")}return null}function Q(r){const o=[];if(!d(r))return o;const a=I(r);for(const l of a){const e=f(r,l);try{pe(e).isDirectory()?o.push(...Q(e)):l.endsWith(".d.ts")&&o.push(e)}catch{}}return o}function _e(r,o){const a=new RegExp(`(\\/\\*\\*[\\s\\S]*?\\*\\/\\s*)?(?:export\\s+)?(?:interface|type|const|function|class)\\s+${o}\\b[\\s\\S]*?(?:;|\\{(?:[^{}]*|\\{[^{}]*\\})*\\})`,"g"),l=r.match(a);return l?l[0].trim():null}function Pe(r){const o=[],a=r.matchAll(/export\s*\{([^}]+)\}/g);for(const t of a)if(t[1]){const i=t[1].split(",").map(c=>c.trim().split(" ")[0]).filter(c=>!!c);o.push(...i)}const l=r.matchAll(/export\s+interface\s+(\w+)/g);for(const t of l)t[1]&&o.push(t[1]);const e=r.matchAll(/export\s+type\s+(\w+)/g);for(const t of e)t[1]&&o.push(t[1]);const n=r.matchAll(/export\s+const\s+(\w+)/g);for(const t of n)t[1]&&o.push(t[1]);const s=r.matchAll(/export\s+default\s+(\w+)/g);for(const t of s)t[1]&&o.push(t[1]);return[...new Set(o)].filter(t=>t&&t!=="default")}function ke(r){if(!d(r))return null;try{const o=$(r,"utf-8").split(`
|
|
17
|
+
`);let a=!1;for(const l of o){if(l.startsWith("# ")){a=!0;continue}if(a&&l.trim().length>0)return l.trim()}}catch{}return null}function oe(r,o){const a=d(f(r,"context_map.json"))?f(r,"context_map.json"):f(r,"context_map.json.example");if(!d(a))return null;try{const l=JSON.parse($(a,"utf-8")),e=Object.keys(l.phases||{}).find(n=>n.startsWith(`${o}_`));if(e&&l.phases[e])return l.phases[e]}catch{}return null}function Ee(r){const o=[];if(!d(r))return[`File not found: ${r}`];const a=$(r,"utf-8").split(`
|
|
18
|
+
`),l=r.split("/").pop()||"";if(!l.endsWith(".tsx")&&!l.endsWith(".ts"))return[];for(let n=0;n<a.length;n++){const s=a[n],t=n+1;/style\s*=\s*\{\{/.test(s)&&o.push(`L${t}: Inline style found. Use className or framework utilities.`),/fontSize|font-size/i.test(s)&&!/\/\/|\/\*|\*/.test(s.trimStart().substring(0,2))&&o.push(`L${t}: fontSize detected. Use Text level or Card title/subtitle props.`),/text-align:\s*(left|right)|textAlign:\s*['"]?(left|right)/i.test(s)&&o.push(`L${t}: textAlign left/right detected. Use start/end for RTL support.`),/\brequire\s*\(/.test(s)&&!/\/\/|\/\*|\*/.test(s.trimStart().substring(0,2))&&o.push(`L${t}: require() found. Use ESM import.`),/\.toLocaleDateString|\.toLocaleTimeString|new Intl\.DateTimeFormat/i.test(s)&&o.push(`L${t}: Manual date formatting. Use formatDate() from @donotdev/core.`)}const e=a.map((n,s)=>({line:n,num:s+1})).filter(({line:n})=>/^import\s/.test(n));if(e.length>1){let n=0;for(const{line:s,num:t}of e){let i=4;if(/from\s+['"]react/.test(s)?i=1:/from\s+['"]@donotdev\//.test(s)?i=3:/from\s+['"][^.]/.test(s)&&(i=2),i<n){o.push(`L${t}: Import order violation. Expected: React > vendors > @donotdev > relative.`);break}n=i}}return o}function Ce(r,o){const a=[],l=/<([A-Z]\w+)/g;for(const e of r){if(!d(e))continue;const n=$(e,"utf-8");if(!/@donotdev\//.test(n))continue;const s=n.matchAll(l);for(const t of s){const i=t[1];i&&(["React","Fragment","Suspense","Provider","Router","Route","Switch"].includes(i)||!o.includes(i)&&!o.includes(`${i}Props`)&&a.push(`${e}: <${i}> used but lookup_symbol("${i}") was never called. Props may be hallucinated.`))}}return[...new Set(a)]}const X=new ie({name:"donotdev-mcp",version:"0.0.3"},{capabilities:{tools:{}}});X.setRequestHandler(le,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(ce,async r=>{const{name:o,arguments:a}=r.params,l=be();if(o==="start_phase"){const e=a?.phase,n=a?.module,s=O.find(g=>g.id===e);if(!s)return{content:[{type:"text",text:`Invalid phase: ${e}. Valid: 0-4.`}],isError:!0};const t=K();if(!t)return{content:[{type:"text",text:"Error: guides/wai-way/ directory not found."}],isError:!0};const i=Z(f(t,"blueprints"),s.blueprint),c=Z(f(t,"agents"),s.agentFile),p=oe(t,e),m=ge({phase:e,module:n}),u=L();u.version=q,u.currentPhase=s.id,u.phaseName=s.name,u.agent=s.agent,u.startedAt=new Date().toISOString(),u.lookedUpSymbols=[],u.pendingReview=!1,u.currentModule=n||null,U(u);const h=[`# Phase ${s.id}: ${s.name}`,`**Agent:** ${s.agent}`];n&&h.push(`**Module:** ${n}`);const y=u.completedPhases.sort().map(g=>{const S=O.find(_=>_.id===g);return S?S.name:`${g}`}),v=j(),k=v?P(v):null,E=M(),C=[`
|
|
19
|
+
## Project State`];if(C.push(`- **Phase:** ${s.id}/4 ${s.name}${y.length>0?` (done: ${y.join(", ")})`:""}`),k&&C.push(`- **Progress:** ${k.stats.done}/${k.stats.total} (${k.stats.percent}%)`),E.sessions.length>0&&C.push(`- **Sessions so far:** ${E.sessions.length}`),h.push(...C),p&&h.push(`
|
|
20
|
+
## Phase Context`,`**Goal:** ${p.goal}`,`**Done when:** ${p.done_when}`,`**Output:** ${p.output}`,"**Read these files first:**",...p.read_files.map(g=>`- ${g}`)),i&&h.push(`
|
|
14
21
|
---
|
|
15
22
|
## Blueprint
|
|
16
23
|
|
|
17
|
-
${i}`),
|
|
24
|
+
${i}`),c&&h.push(`
|
|
18
25
|
---
|
|
19
26
|
## Agent Definition
|
|
20
27
|
|
|
21
|
-
${
|
|
28
|
+
${c}`),m.trim().length>20&&h.push(`
|
|
22
29
|
---
|
|
23
30
|
## Lessons (Filtered)
|
|
24
31
|
|
|
25
|
-
${
|
|
32
|
+
${m}`),v){const{stats:g,filtered:S}=P(v,`Phase ${e}`);h.push(`
|
|
26
33
|
---
|
|
27
|
-
## Implementation Progress`,`**Overall:** ${
|
|
34
|
+
## Implementation Progress`,`**Overall:** ${g.done}/${g.total} (${g.percent}%)`,"",S,"",'Use `update_progress({ item: "...", done: true })` to tick items as you complete them.')}else e>0&&h.push(`
|
|
28
35
|
---
|
|
29
|
-
## Implementation Progress`,"No implementation.md found. Consider running `init_implementation({ from_spec: true })` to track progress across sessions.");
|
|
36
|
+
## Implementation Progress`,"No implementation.md found. Consider running `init_implementation({ from_spec: true })` to track progress across sessions.");h.push(`
|
|
30
37
|
---
|
|
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)")
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
## 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)");const b=ne();h.push(`
|
|
39
|
+
---
|
|
40
|
+
## Project Args`,`- **Platform:** ${b.platform}`,`- **Strictness:** ${b.strictness}`,`- **Features:** ${b.features.join(", ")}`,`- **Region:** ${b.region}`);const A=fe(e,b.features);return A&&h.push(`
|
|
41
|
+
---
|
|
42
|
+
## Gotchas (Phase ${e})
|
|
43
|
+
|
|
44
|
+
${A}`),!i&&!c&&h.push(`
|
|
45
|
+
No blueprint or agent definition found for this phase.`),{content:[{type:"text",text:h.join(`
|
|
46
|
+
`)}]}}if(o==="complete_phase"){const e=L();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,s=e.phaseName,t=e.currentModule?` [${e.currentModule}]`:"",i=a?.files||[],c=[],p=[];if(i.length>0){for(const S of i){const _=he(S);if(!_){c.push(`- BLOCKED: "${S}" resolves outside project root. Skipped.`);continue}p.push(_)}for(let S=0;S<p.length;S++){const _=Ee(p[S]);_.length>0&&c.push(`
|
|
47
|
+
### ${i[S]}`,..._.map(re=>`- ${re}`))}const g=Ce(p,e.lookedUpSymbols);g.length>0&&(c.push(`
|
|
48
|
+
### Unverified Component Usage`),c.push(...g.map(S=>`- ${S}`)))}const m=K();if(m){const g=oe(m,n);g&&c.push(`
|
|
49
|
+
### Done Criteria`,`**Required:** ${g.done_when}`,`**Expected output:** ${g.output}`,"","Verify these criteria are met.")}const u=ne(),h=c.some(g=>g.startsWith("- L")||g.includes("Unverified Component")||g.includes("BLOCKED"));if(h&&u.strictness==="enforced")return{content:[{type:"text",text:[`## Validation Failed: Phase ${n} (${s})${t}`,"","**Status:** ISSUES FOUND \u2014 fix before completing phase","**Strictness:** enforced (change in .dndev/args.json to override)",...c,"","### Type Check","Run `bunx tsc --noEmit` to verify TypeScript compiles without errors.","","Fix the issues above, then call `complete_phase({ files: [...] })` again."].join(`
|
|
50
|
+
`)}]};h&&u.strictness==="warnings"&&c.unshift(`
|
|
51
|
+
### Warnings (non-blocking)`,"**Strictness:** warnings \u2014 issues reported but phase can proceed.");const y=a?.lesson;y&&se(y,` [Phase ${n}: ${s}]`);const v=a?.summary;e.pendingLogData={files_touched:p.length,summary:v},e.pendingReview=!0,U(e);const k=e.lookedUpSymbols.length,E=O.find(g=>g.id===n+1),C=E?`Next: Phase ${E.id} (${E.name})`:"This was the final phase.";let b="";const A=j();if(A){const{stats:g}=P(A,`Phase ${n}`);b=`**Implementation:** ${g.done}/${g.total} items (${g.percent}%)`}return{content:[{type:"text",text:[`## Phase ${n} (${s})${t} \u2014 Ready for Review`,"",`**Validation:** ${i.length>0?"PASSED":"No files provided (skipped)"}`,`**Symbols looked up:** ${k} (${e.lookedUpSymbols.join(", ")||"none"})`,b,y?`**Lesson recorded:** ${y}`:"","","### 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.","",C].filter(Boolean).join(`
|
|
52
|
+
`)}]}}if(o==="approve_phase"){const e=L();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,s=e.phaseName,t=M();xe({id:t.sessions.length+1,date:new Date().toISOString().split("T")[0],phase:n,phase_name:s||O[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} (${s}) 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=O.find(p=>p.id===n+1),c=i?`Next: call start_phase(${i.id}) to begin ${i.name}.`:"All phases complete. App is ready.";return i&&Se(i.id,i.name),{content:[{type:"text",text:`Phase ${n} (${s}) approved and completed.
|
|
53
|
+
${c}`}]}}if(o==="get_phase_status"){const e=L(),n=e.completedPhases.sort().map(s=>{const t=O.find(i=>i.id===s);return t?`${t.id}: ${t.name}`:`${s}`}).join(", ");return e.currentPhase===null?{content:[{type:"text",text:`No active phase.
|
|
40
54
|
Completed: ${n||"none"}
|
|
41
55
|
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(
|
|
43
|
-
`)}]}}if(
|
|
44
|
-
Also add a \`// GOTCHA: ...\` comment in the relevant source file.`}]}}if(
|
|
45
|
-
Pass force: true to overwrite, or use update_progress/get_progress to work with existing file.`}]}}let t;if(n){const p
|
|
56
|
+
`)}]}}if(o==="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((c,p)=>c+p.files_touched,0),s=e.sessions.reduce((c,p)=>c+p.symbols_used,0),t=[...new Set(e.sessions.map(c=>c.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:** ${s}`,"","### Timeline"];for(const c of e.sessions){const p=c.module?` [${c.module}]`:"";i.push(`${c.id}. **[${c.date}]** Phase ${c.phase} (${c.phase_name})${p} \u2014 ${c.outcome}`)}return i.push("","Raw JSON: .dndev/captain-log.json"),{content:[{type:"text",text:i.join(`
|
|
57
|
+
`)}]}}if(o==="record_lesson"){const e=a?.lesson,n=a?.tags,s=L(),t=s.currentPhase!==null?` [Phase ${s.currentPhase}: ${s.phaseName}]`:"";return se(e,t,n),{content:[{type:"text",text:`Lesson recorded${n&&n.length>0?` [${n.join(", ")}]`:""}. Filtered by tags on next start_phase().
|
|
58
|
+
Also add a \`// GOTCHA: ...\` comment in the relevant source file.`}]}}if(o==="init_implementation"){const e=a?.sections,n=a?.from_spec,s=a?.force;if(d(D)&&!s){const p=j(),{stats:m}=P(p);return{content:[{type:"text",text:`implementation.md already exists (${m.done}/${m.total} items done, ${m.percent}%).
|
|
59
|
+
Pass force: true to overwrite, or use update_progress/get_progress to work with existing file.`}]}}let t;if(n){const p=$e();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=ye(t);V(i);const{stats:c}=P(i);return{content:[{type:"text",text:`implementation.md created with ${c.total} items across ${t.length} sections.
|
|
46
60
|
|
|
47
|
-
${i}`}]}}if(
|
|
48
|
-
`),
|
|
49
|
-
`);if(
|
|
50
|
-
${
|
|
61
|
+
${i}`}]}}if(o==="update_progress"){const e=a?.item,n=a?.done,s=a?.note,t=j();if(!t)return{content:[{type:"text",text:"No implementation.md found. Call init_implementation() first."}],isError:!0};const i=t.split(`
|
|
62
|
+
`),c=e.toLowerCase();let p=!1,m="";for(let y=0;y<i.length;y++){const v=i[y];if(/^\s*- \[[ x]\]/i.test(v)&&v.toLowerCase().includes(c)){n?i[y]=v.replace(/- \[ \]/,"- [x]"):i[y]=v.replace(/- \[x\]/i,"- [ ]"),m=i[y],p=!0;break}}if(!p)return{content:[{type:"text",text:`No checklist item matching "${e}" found in implementation.md.`}],isError:!0};let u=i.join(`
|
|
63
|
+
`);if(u=u.replace(/\*\*Last updated:\*\*\s*.*/,`**Last updated:** ${new Date().toISOString()}`),s){const y=`- ${s}`;u.includes("## Notes")?u=u.replace("## Notes",`## Notes
|
|
64
|
+
${y}`):u+=`
|
|
51
65
|
## Notes
|
|
52
|
-
${
|
|
53
|
-
`}
|
|
54
|
-
Progress: ${
|
|
55
|
-
`)}]}}if(!l&&!["get_guide","get_guideline"].includes(
|
|
66
|
+
${y}
|
|
67
|
+
`}V(u);const{stats:h}=P(u);return{content:[{type:"text",text:`Updated: ${m.trim()}
|
|
68
|
+
Progress: ${h.done}/${h.total} (${h.percent}%)`}]}}if(o==="get_progress"){const e=a?.section,n=j();if(!n)return{content:[{type:"text",text:"No implementation.md found. Call init_implementation() first."}],isError:!0};const{stats:s,sections:t,filtered:i}=P(n,e),c=["## Implementation Progress",`**Overall:** ${s.done}/${s.total} (${s.percent}%)`,""];if(t.length>0){c.push("### By Section");for(const p of t){const m=p.total>0?Math.round(p.done/p.total*100):0;c.push(`- **${p.title}:** ${p.done}/${p.total} (${m}%)`)}c.push("")}return c.push("---","",i),{content:[{type:"text",text:c.join(`
|
|
69
|
+
`)}]}}if(!l&&!["get_guide","get_guideline"].includes(o))return{content:[{type:"text",text:"Error: node_modules not found. Run bun install first."}],isError:!0};if(o==="get_guide"){const e=a?.topic,n=z();if(!n)return{content:[{type:"text",text:"Error: guides directory not found."}],isError:!0};const s=I(n).filter(t=>t.endsWith(".md")||t.endsWith(".md.example"));for(const t of s)if(t.toLowerCase().includes(e.toLowerCase()))return{content:[{type:"text",text:`[GUIDE] ${e} (from ${t}):
|
|
56
70
|
|
|
57
|
-
${$(
|
|
71
|
+
${$(f(n,t),"utf-8")}`}]};return{content:[{type:"text",text:`No guide found for: ${e}`}],isError:!0}}if(o==="get_guideline"){const e=a?.topic,[n,s]=e.split(":"),t=[{dir:H,type:"GUIDE"},{dir:de,type:"ARCHITECTURE"}];for(const i of t){if(!d(i.dir))continue;const c=I(i.dir).filter(p=>p.endsWith(".md")||p.endsWith(".md.example"));for(const p of c)if(n&&p.toLowerCase().includes(n.toLowerCase())){const m=$(f(i.dir,p),"utf-8");if(!s){const h=m.match(/^([\s\S]*?)(?=##|$)/),y=h&&h[1]?h[1].trim():"No overview found.";return{content:[{type:"text",text:`[${i.type}] Overview for ${n}:
|
|
58
72
|
|
|
59
|
-
${
|
|
73
|
+
${y}`}]}}const u=m.match(new RegExp(`##\\s+.*${s}.*\\n([\\s\\S]*?)(?=##|$)`,"i"));if(u&&u[1])return{content:[{type:"text",text:`[${i.type}] ${n} > ${s}:
|
|
60
74
|
|
|
61
|
-
${
|
|
75
|
+
${u[1].trim()}`}]}}}return{content:[{type:"text",text:`No guideline found for: ${e}`}],isError:!0}}if(o==="search_framework"){const e=(a?.query).toLowerCase(),n=[],s=z();if(s){const t=I(s).filter(i=>i.endsWith(".md")||i.endsWith(".md.example"));for(const i of t)$(f(s,i),"utf-8").toLowerCase().includes(e)&&n.push(`[GUIDE] ${i}`)}if(l)for(const t of Y){const i=f(l,t,"dist");if(d(i)){for(const c of Q(i))if($(c,"utf-8").toLowerCase().includes(e)){const p=Pe($(c,"utf-8")).filter(m=>m.toLowerCase().includes(e));p.length>0&&n.push(`[SYMBOL] ${t}: ${p.join(", ")}`)}}}return{content:[{type:"text",text:n.length>0?`Results:
|
|
62
76
|
${n.join(`
|
|
63
|
-
`)}`:"No results found."}]}}if(
|
|
77
|
+
`)}`:"No results found."}]}}if(o==="lookup_symbol"){const e=a?.symbol;if(!l)return{content:[{type:"text",text:"Error: node_modules not found. Run bun install first."}],isError:!0};const n=L();n.lookedUpSymbols.includes(e)||(n.lookedUpSymbols.push(e),U(n));const s=[e,`${e}Props`,`${e.replace(/Props$/,"")}Props`],t=["@donotdev/core","@donotdev/ui","@donotdev/crud"],i=[...t,...Y.filter(c=>!t.includes(c))];for(const c of i){const p=f(l,c,"dist");if(d(p))for(const m of Q(p)){const u=$(m,"utf-8");for(const h of s){const y=_e(u,h);if(y)return{content:[{type:"text",text:`[TYPE INTELLIGENCE] ${c}:
|
|
64
78
|
|
|
65
|
-
${
|
|
66
|
-
`];for(const n of
|
|
67
|
-
`)}]}}return{content:[{type:"text",text:`Unknown tool: ${
|
|
79
|
+
${y}`}]}}}}return{content:[{type:"text",text:`Not found: ${e}`}],isError:!0}}if(o==="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
|
|
80
|
+
`];for(const n of we){const s=f(l,n,"README.md"),t=ke(s);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(`
|
|
81
|
+
`)}]}}return{content:[{type:"text",text:`Unknown tool: ${o}`}],isError:!0}});async function Ne(){const r=new ae;await X.connect(r)}Ne().catch(console.error);
|