@donotdev/mcp-server 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +67 -56
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,85 +1,96 @@
1
1
  #!/usr/bin/env node
2
- import{Server as de}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as fe}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as me,ListToolsRequestSchema as he}from"@modelcontextprotocol/sdk/types.js";import{appendFileSync as ge,readFileSync as x,existsSync as h,readdirSync as N,statSync as ye,writeFileSync as U,mkdirSync as j}from"fs";import{join as m,resolve as H,normalize as V,sep as Se,basename as se}from"path";const J=2,re=10;function xe(){let o=process.cwd();for(let a=0;a<re;a++){if(h(m(o,"AI.md"))||h(m(o,".dndev"))||h(m(o,"package.json")))return o;const n=H(o,"..");if(n===o)break;o=n}return process.cwd()}const $=xe(),w=m($,".dndev"),K=m(w,"protocol.json"),I=m(w,"LESSONS.md"),F=m(w,"implementation.md"),z=m(w,"captain-log.json"),ie=m(w,"args.json"),$e=m($,"docs","architecture"),D=m($,"packages","cli","templates","root-consumer","guides","dndev"),T=[{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"}],M={version:J,currentPhase:null,phaseName:null,agent:null,startedAt:null,completedPhases:[],lookedUpSymbols:[],pendingReview:!1,currentModule:null};function O(){if(!h(K))return{...M,completedPhases:[],lookedUpSymbols:[]};try{const o=JSON.parse(x(K,"utf-8"));return o.version!==J?{...M,completedPhases:[],lookedUpSymbols:[]}:{...M,...o}}catch{return{...M,completedPhases:[],lookedUpSymbols:[]}}}function G(o){h(w)||j(w,{recursive:!0}),U(K,JSON.stringify(o,null,2))}const Y={platform:"firebase",strictness:"enforced",features:["crud","auth","i18n","billing","oauth","functions"],region:"europe-west1"};function ae(){if(!h(ie))return{...Y};try{const o=JSON.parse(x(ie,"utf-8"));return{...Y,...o}}catch{return{...Y}}}function be(){const o=Q();if(!o)return[];let a=null;for(const r of["GOTCHAS.md","GOTCHAS.md.example"]){const s=m(o,r);if(h(s)){a=s;break}}if(!a)return[];const n=x(a,"utf-8"),l=[],e=n.split(/^## /m);for(const r of e){if(!r.trim())continue;const s=r.indexOf(`
3
- `);if(s===-1)continue;const t=r.substring(0,s).trim(),i=r.substring(s+1).trim(),c=t.match(/\[Phase\s+([\d,\s]+)\]/i),p=[];if(c?.[1])for(const f of c[1].split(",")){const u=parseInt(f.trim());isNaN(u)||p.push(u)}p.length>0&&i&&l.push({title:t.replace(/\s*\[Phase.*?\]/,""),phases:p,content:i})}return l}function we(o,a){const n=be();if(n.length===0)return"";const l=n.filter(s=>s.phases.includes(o));if(l.length===0)return"";const e={CRUD:"crud",Functions:"functions",i18n:"i18n"},r=a?l.filter(s=>{const t=e[s.title];return!t||a.includes(t)}):l;return r.length===0?"":r.map(s=>`### ${s.title}
2
+ import{Server as Se}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as $e}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as ve,ListToolsRequestSchema as be}from"@modelcontextprotocol/sdk/types.js";import{appendFileSync as xe,readFileSync as $,existsSync as h,readdirSync as M,statSync as we,writeFileSync as A,mkdirSync as N}from"fs";import{join as g,resolve as H,normalize as G,sep as _e,basename as re}from"path";const J=2,ie=10;function Ee(){let e=process.cwd();for(let o=0;o<ie;o++){if(h(g(e,"AI.md"))||h(g(e,".dndev"))||h(g(e,"package.json")))return e;const s=H(e,"..");if(s===e)break;e=s}return process.cwd()}const v=Ee(),b=g(v,".dndev"),V=g(b,"protocol.json"),_=g(b,"LESSONS.md"),W=g(b,"implementation.md"),z=g(b,"captain-log.json"),ae=g(b,"args.json"),Pe=g(v,"docs","architecture"),B=g(v,"packages","cli","templates","root-consumer","guides","dndev"),L=[{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"}],q={version:J,currentPhase:null,phaseName:null,agent:null,startedAt:null,completedPhases:[],lookedUpSymbols:[],pendingReview:!1,currentModule:null};function E(){if(!h(V))return{...q,completedPhases:[],lookedUpSymbols:[]};try{const e=JSON.parse($(V,"utf-8"));return e.version!==J?{...q,completedPhases:[],lookedUpSymbols:[]}:{...q,...e}}catch{return{...q,completedPhases:[],lookedUpSymbols:[]}}}function R(e){h(b)||N(b,{recursive:!0}),A(V,JSON.stringify(e,null,2))}function Ce(e){const o=E();o.toolCallCounts||(o.toolCallCounts={}),o.toolCallCounts[e]=(o.toolCallCounts[e]??0)+1,R(o)}function ke(){const e=E();e.lessonsRecorded=(e.lessonsRecorded??0)+1,R(e)}function Re(e){const o=E();o.lessonsScored||(o.lessonsScored={helpful:0,harmful:0}),o.lessonsScored[e]++,R(o)}function le(){const e=E();if((e.toolCallCounts?Object.values(e.toolCallCounts).reduce((s,c)=>s+c,0):0)===0)return;const o={id:I().sessions.length+1,date:new Date().toISOString().split("T")[0],phase:e.currentPhase??-1,phase_name:e.phaseName||"ad-hoc",module:e.currentModule||void 0,started_at:e.startedAt||new Date().toISOString(),completed_at:new Date().toISOString(),outcome:e.pendingLogData?.summary||(e.currentPhase!==null?`Phase ${e.currentPhase} (${e.phaseName}) \u2014 session ended without approval`:"Ad-hoc session"),files_touched:e.pendingLogData?.files_touched||0,symbols_used:e.lookedUpSymbols?.length||0,tool_calls:e.toolCallCounts&&Object.keys(e.toolCallCounts).length>0?e.toolCallCounts:void 0,lessons_recorded:e.lessonsRecorded||void 0,lessons_scored:e.lessonsScored&&(e.lessonsScored.helpful>0||e.lessonsScored.harmful>0)?e.lessonsScored:void 0,validation:e.pendingLogData?.validation};fe(o),e.toolCallCounts=void 0,e.lessonsRecorded=void 0,e.lessonsScored=void 0,e.pendingLogData=void 0,R(e)}process.on("exit",()=>{try{le()}catch{}}),process.on("SIGINT",()=>{process.exit(0)}),process.on("SIGTERM",()=>{process.exit(0)});const K={platform:"firebase",strictness:"enforced",features:["crud","auth","i18n","billing","oauth","functions"],region:"europe-west1"};function ce(){if(!h(ae))return{...K};try{const e=JSON.parse($(ae,"utf-8"));return{...K,...e}}catch{return{...K}}}function Te(){const e=ee();if(!e)return[];let o=null;for(const r of["GOTCHAS.md","GOTCHAS.md.example"]){const i=g(e,r);if(h(i)){o=i;break}}if(!o)return[];const s=$(o,"utf-8"),c=[],t=s.split(/^## /m);for(const r of t){if(!r.trim())continue;const i=r.indexOf(`
3
+ `);if(i===-1)continue;const n=r.substring(0,i).trim(),a=r.substring(i+1).trim(),l=n.match(/\[Phase\s+([\d,\s]+)\]/i),u=[];if(l?.[1])for(const d of l[1].split(",")){const f=parseInt(d.trim());isNaN(f)||u.push(f)}u.length>0&&a&&c.push({title:n.replace(/\s*\[Phase.*?\]/,""),phases:u,content:a})}return c}function Oe(e,o){const s=Te();if(s.length===0)return"";const c=s.filter(i=>i.phases.includes(e));if(c.length===0)return"";const t={CRUD:"crud",Functions:"functions",i18n:"i18n"},r=o?c.filter(i=>{const n=t[i.title];return!n||o.includes(n)}):c;return r.length===0?"":r.map(i=>`### ${i.title}
4
4
 
5
- ${s.content}`).join(`
5
+ ${i.content}`).join(`
6
6
 
7
7
  ---
8
8
 
9
- `)}function ve(o){const a=V($)+Se,n=V(H($,o));return!n.startsWith(a)&&n!==V($)?null:n}function Ee(o){if(!h(I))return"";const a=x(I,"utf-8");if(!o)return a;const n=a.split(`
10
- `),l=[];for(const r of n){if(!r.startsWith("- [")){l.push(r);continue}const s=r.match(/\[Phase (\d+):/),t=s?parseInt(s[1]):null,c=[...r.matchAll(/\[([^\]]*)\]/g)].map(d=>d[1]).filter(d=>!/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(d)&&!/^Phase \d+:/.test(d)).flatMap(d=>d.split(",").map(g=>g.trim().toLowerCase())).filter(Boolean),p=c.length===0,f=o.phase!==void 0&&t!==null&&(t===o.phase||t===o.phase-1),u=!!o.module&&c.includes(o.module.toLowerCase());(p||f||u)&&l.push(r)}const e=l.join(`
11
- `).trim();return!e||e==="# Lessons Learned"?"":e}function ce(o,a="",n){const l=n&&n.length>0?`[${n.join(", ")}]`:"",e=`
12
- - [${new Date().toISOString().split("T")[0]}]${a}${l} ${o}`;try{h(w)||j(w,{recursive:!0}),h(I)?ge(I,e):U(I,`# Lessons Learned
13
- `+e)}catch{}}function L(){return h(F)?x(F,"utf-8"):null}function X(o){h(w)||j(w,{recursive:!0}),U(F,o)}function P(o,a){const n=o.split(`
14
- `),l=[];let e=null,r=0,s=0;for(const i of n)/^###?\s/.test(i)&&!i.startsWith("# Implementation")&&(e&&l.push(e),e={title:i.replace(/^#+\s*/,""),total:0,done:0}),/^\s*- \[x\]/i.test(i)?(r++,s++,e&&e.done++,e&&e.total++):/^\s*- \[ \]/.test(i)&&(s++,e&&e.total++);e&&l.push(e);let t=o;if(a){const i=a.toLowerCase(),c=[];let p=!1;for(const f of n)/^###?\s/.test(f)&&(p=f.toLowerCase().includes(i)),p&&c.push(f);t=c.length>0?c.join(`
15
- `):o}return{stats:{total:s,done:r,pending:s-r,percent:s>0?Math.round(r/s*100):0},sections:l.filter(i=>i.total>0),filtered:t}}function Pe(o){const a=["# Implementation Progress","",`**Last updated:** ${new Date().toISOString()}`,"**Current phase:** 0 - BRAINSTORM",""];for(const n of o){a.push(`### ${n.title}`);for(const l of n.items)a.push(`- [ ] ${l}`);a.push("")}return a.push("## Notes",""),a.join(`
16
- `)}function _e(){const o=ee();if(!o)return null;const a=te(o,"spec_template");if(!a)return null;const n=[{title:"Phase 0: BRAINSTORM",items:["Spec complete","User validated"]}],l=a.matchAll(/### Entity:\s*\[?(\w+)\]?/g),e=[];for(const t of l)t[1]&&t[1]!=="Name"&&e.push(t[1]);const r=a.matchAll(/\|\s*`?\/[\w-]*`?\s*\|\s*(\w+Page)\s*\|/g),s=[];for(const t of r)t[1]&&s.push(t[1]);return n.push({title:"Phase 1: SCAFFOLD",items:s.length>0?s.map(t=>`${t}.tsx`):["(generate from validated spec)"]}),n.push({title:"Phase 2: ENTITIES",items:e.length>0?e.map(t=>`${t} entity`):["(generate from validated spec)"]}),n.push({title:"Phase 3: COMPOSE",items:s.length>0?s.map(t=>`${t} composition`):["(generate from validated spec)"]}),n.push({title:"Phase 4: CONFIGURE",items:["Tests generated","Firestore rules","Config finalized","Mobile check"]}),n}function Re(o,a){const n=L();if(!n)return;const l=n.replace(/\*\*Current phase:\*\*\s*.*/,`**Current phase:** ${o} - ${a}`);l!==n&&X(l.replace(/\*\*Last updated:\*\*\s*.*/,`**Last updated:** ${new Date().toISOString()}`))}function B(){if(!h(z))return{project:se($)||"unknown",started:new Date().toISOString().split("T")[0],sessions:[]};try{return JSON.parse(x(z,"utf-8"))}catch{return{project:se($)||"unknown",started:new Date().toISOString().split("T")[0],sessions:[]}}}function ke(o){const a=B();a.sessions.push(o),h(w)||j(w,{recursive:!0}),U(z,JSON.stringify(a,null,2))}const Z=["@donotdev/core","@donotdev/ui","@donotdev/components","@donotdev/templates","@donotdev/crud","@donotdev/auth","@donotdev/billing","@donotdev/oauth","@donotdev/adv-comps","@donotdev/firebase","@donotdev/supabase","@donotdev/functions","@donotdev/security","@donotdev/expo","@donotdev/mcp-server"],Ae=["@donotdev/cli"],Te=[...Z,...Ae];function Oe(){let o=$;for(let a=0;a<re;a++){const n=m(o,"node_modules");if(h(n))return n;const l=H(o,"..");if(l===o)break;o=l}return null}function Q(){const o=m($,"guides","dndev");return h(o)?o:h(D)?D:null}function ee(){const o=m($,"guides","wai-way");if(h(o))return o;const a=m($,"packages","cli","templates","root-consumer","guides","wai-way");return h(a)?a:null}function te(o,a){for(const n of[".md",".md.example"]){const l=m(o,a+n);if(h(l))return x(l,"utf-8")}return null}function ne(o,a=5){const n=[];if(!h(o)||a<=0)return n;const l=N(o);for(const e of l){const r=m(o,e);try{ye(r).isDirectory()?n.push(...ne(r,a-1)):e.endsWith(".d.ts")&&n.push(r)}catch{}}return n}function pe(o){return o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Le(o,a){let n=0,l=-1;for(let e=a;e<o.length;e++)if(o[e]==="{")n===0&&(l=e),n++;else if(o[e]==="}"&&(n--,n===0))return o.slice(l,e+1);return""}function Ce(o,a){const n=pe(a),e=new RegExp(`(\\/\\*\\*[\\s\\S]*?\\*\\/\\s*)?(?:export\\s+)?(?:interface|type|const|function|class)\\s+${n}\\b`,"g").exec(o);if(!e)return null;const r=e.index,s=e.index+e[0].length,t=o.slice(s),i=t.indexOf("{"),c=t.indexOf(";");if(c!==-1&&(i===-1||c<i))return o.slice(r,s+c+1).trim();if(i!==-1){const p=Le(o,s+i);if(p)return o.slice(r,s+i+p.length).trim()}return null}function Ie(o){const a=[],n=o.matchAll(/export\s*\{([^}]+)\}/g);for(const t of n)if(t[1]){const i=t[1].split(",").map(c=>{const p=c.trim().split(/\s+as\s+/);return(p.length>1?p[1]:p[0])?.trim()}).filter(c=>!!c);a.push(...i)}const l=o.matchAll(/export\s+interface\s+(\w+)/g);for(const t of l)t[1]&&a.push(t[1]);const e=o.matchAll(/export\s+type\s+(\w+)/g);for(const t of e)t[1]&&a.push(t[1]);const r=o.matchAll(/export\s+const\s+(\w+)/g);for(const t of r)t[1]&&a.push(t[1]);const s=o.matchAll(/export\s+default\s+(\w+)/g);for(const t of s)t[1]&&a.push(t[1]);return[...new Set(a)].filter(t=>t&&t!=="default")}function Ne(o){if(!h(o))return null;try{const a=x(o,"utf-8").split(`
17
- `);let n=!1;for(const l of a){if(l.startsWith("# ")){n=!0;continue}if(n&&l.trim().length>0)return l.trim()}}catch{}return null}function le(o,a){const n=h(m(o,"context_map.json"))?m(o,"context_map.json"):m(o,"context_map.json.example");if(!h(n))return null;try{const l=JSON.parse(x(n,"utf-8")),r=Object.keys(l.phases||{}).find(s=>s.startsWith(`${a}_`));if(r&&l.phases[r])return l.phases[r]}catch{}return null}function Ue(o){const a=[];if(!h(o))return[`File not found: ${o}`];const l=x(o,"utf-8").split(`
18
- `),e=o.split("/").pop()||"";if(!e.endsWith(".tsx")&&!e.endsWith(".ts"))return[];for(let s=0;s<l.length;s++){const t=l[s],i=s+1;/style\s*=\s*\{\{/.test(t)&&a.push(`L${i}: Inline style found. Use className or framework utilities.`),/fontSize|font-size/i.test(t)&&!/\/\/|\/\*|\*/.test(t.trimStart().substring(0,2))&&a.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)&&a.push(`L${i}: textAlign left/right detected. Use start/end for RTL support.`),/\brequire\s*\(/.test(t)&&!/\/\/|\/\*|\*/.test(t.trimStart().substring(0,2))&&a.push(`L${i}: require() found. Use ESM import.`),/\.toLocaleDateString|\.toLocaleTimeString|new Intl\.DateTimeFormat/i.test(t)&&a.push(`L${i}: Manual date formatting. Use formatDate() from @donotdev/core.`)}const r=l.map((s,t)=>({line:s,num:t+1})).filter(({line:s})=>/^import\s/.test(s));if(r.length>1){let s=0;for(const{line:t,num:i}of r){let c=4;if(/from\s+['"]react/.test(t)?c=1:/from\s+['"]@donotdev\//.test(t)?c=3:/from\s+['"][^.]/.test(t)&&(c=2),c<s){a.push(`L${i}: Import order violation. Expected: React > vendors > @donotdev > relative.`);break}s=c}}return a}function je(o,a){const n=[],l=/<([A-Z]\w+)/g;for(const e of o){if(!h(e))continue;const r=x(e,"utf-8");if(!/@donotdev\//.test(r))continue;const s=r.matchAll(l);for(const t of s){const i=t[1];i&&(["React","Fragment","Suspense","Provider","Router","Route","Switch"].includes(i)||!a.includes(i)&&!a.includes(`${i}Props`)&&n.push(`${e}: <${i}> used but lookup_symbol("${i}") was never called. Props may be hallucinated.`))}}return[...new Set(n)]}const oe=new de({name:"donotdev-mcp",version:"0.0.6"},{capabilities:{tools:{}}});oe.setRequestHandler(he,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:"run_typecheck",description:"Run TypeScript type-check on the consumer project. Returns structured results. Run after every code change.",inputSchema:{type:"object",properties:{package:{type:"string",description:"Optional: scope to a specific app/package name or path fragment"}}}},{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"]}},{name:"setup_coach",description:"Get contextual setup instructions for a specific manual step. Reads from guide files \u2014 never accesses .env or secrets. Returns markdown coaching for dashboard-click steps.",inputSchema:{type:"object",properties:{topic:{type:"string",description:'Setup topic (e.g., "stripe-webhook", "oauth-google", "firebase-service-account", "supabase-credentials")'},provider:{type:"string",description:"Optional provider context (firebase, supabase, vercel, stripe)"},projectId:{type:"string",description:"Optional project ID for generating console URLs (public, not a secret)"},region:{type:"string",description:'Optional Firebase Cloud Functions region (e.g., "europe-west1", "us-central1"). Defaults to europe-west1.'}},required:["topic"]}}]}));function v(o,a){if(typeof o!="string")throw new Error(`Expected "${a}" to be a string, got ${typeof o}`)}function Fe(o,a){if(typeof o!="number"||!Number.isFinite(o))throw new Error(`Expected "${a}" to be a number, got ${typeof o}`)}function De(o,a){if(typeof o!="boolean")throw new Error(`Expected "${a}" to be a boolean, got ${typeof o}`)}function ue(o,a){if(!Array.isArray(o)||o.some(n=>typeof n!="string"))throw new Error(`Expected "${a}" to be a string array`)}oe.setRequestHandler(me,async o=>{const{name:a,arguments:n}=o.params,l=Oe();if(a==="start_phase"){Fe(n?.phase,"phase");const e=n.phase,r=n?.module!=null?(v(n.module,"module"),n.module):void 0,s=T.find(y=>y.id===e);if(!s)return{content:[{type:"text",text:`Invalid phase: ${e}. Valid: 0-4.`}],isError:!0};const t=ee();if(!t)return{content:[{type:"text",text:"Error: guides/wai-way/ directory not found."}],isError:!0};const i=te(m(t,"blueprints"),s.blueprint),c=te(m(t,"agents"),s.agentFile),p=le(t,e),f=Ee({phase:e,module:r}),u=O();u.version=J,u.currentPhase=s.id,u.phaseName=s.name,u.agent=s.agent,u.startedAt=new Date().toISOString(),u.lookedUpSymbols=[],u.pendingReview=!1,u.currentModule=r||null,G(u);const d=[`# Phase ${s.id}: ${s.name}`,`**Agent:** ${s.agent}`];r&&d.push(`**Module:** ${r}`);const g=u.completedPhases.sort().map(y=>{const E=T.find(q=>q.id===y);return E?E.name:`${y}`}),b=L(),_=b?P(b):null,R=B(),k=[`
19
- ## Project State`];if(k.push(`- **Phase:** ${s.id}/4 ${s.name}${g.length>0?` (done: ${g.join(", ")})`:""}`),_&&k.push(`- **Progress:** ${_.stats.done}/${_.stats.total} (${_.stats.percent}%)`),R.sessions.length>0&&k.push(`- **Sessions so far:** ${R.sessions.length}`),d.push(...k),p&&d.push(`
20
- ## Phase Context`,`**Goal:** ${p.goal}`,`**Done when:** ${p.done_when}`,`**Output:** ${p.output}`,"**Read these files first:**",...p.read_files.map(y=>`- ${y}`)),i&&d.push(`
9
+ `)}function Ae(e){const o=G(v)+_e,s=G(H(v,e));return!s.startsWith(o)&&s!==G(v)?null:s}const Le=90,Ue=4,Ie=.15;function ue(e){const o=e.match(/\{helpful:(\d+),harmful:(\d+),lastSeen:([^,}]+)(?:,status:([^}]+))?\}$/);return o?{helpful:parseInt(o[1]),harmful:parseInt(o[2]),lastSeen:o[3],status:o[4]||"candidate"}:{helpful:0,harmful:0,lastSeen:null,status:"candidate"}}function je(e){if(e.lastSeen===null)return .5;const o=(Date.now()-new Date(e.lastSeen).getTime())/(1e3*60*60*24),s=Math.pow(.5,o/Le),c=e.helpful-e.harmful*Ue;return(c>=0?.5+Math.min(c/10,.5):Math.max(.1,.5+c/10))*s}function Ne(e){const o=e.helpful+e.harmful;if(o===0)return"candidate";const s=e.harmful/o;return s>.5&&e.harmful>=3?"anti-pattern":s>.25?"deprecated":e.helpful>=10&&s<.1?"proven":e.helpful>=3&&s<.25?"established":"candidate"}function Y(e){return e.replace(/\s*\{helpful:\d+,harmful:\d+,lastSeen:[^}]+\}$/,"")}function Fe(e,o){return`${Y(e)} {helpful:${o.helpful},harmful:${o.harmful},lastSeen:${o.lastSeen},status:${o.status}}`}function De(e){if(!h(_))return"";const o=$(_,"utf-8");if(!e)return o;const s=o.split(`
10
+ `),c=[];for(const r of s){if(!r.startsWith("- [")){c.push(r);continue}const i=ue(r);if(je(i)<Ie||i.status==="deprecated")continue;const n=r.match(/\[Phase (\d+):/),a=n?parseInt(n[1]):null,l=[...r.matchAll(/\[([^\]]*)\]/g)].map(p=>p[1]).filter(p=>!/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(p)&&!/^Phase \d+:/.test(p)&&!/^\d{4}-\d{2}-\d{2}$/.test(p)).flatMap(p=>p.split(",").map(m=>m.trim().toLowerCase())).filter(Boolean),u=l.length===0,d=e.phase!==void 0&&a!==null&&(a===e.phase||a===e.phase-1),f=!!e.module&&l.includes(e.module.toLowerCase());if(u||d||f)if(i.status==="anti-pattern"){const p=Y(r),m=p.replace(/^- \[[^\]]*\](\[[^\]]*\])*\s*/,""),S=p.substring(0,p.length-m.length);c.push(`${S}PITFALL: ${m}`)}else c.push(Y(r))}const t=c.join(`
11
+ `).trim();return!t||t==="# Lessons Learned"?"":t}function pe(e,o="",s){const c=s&&s.length>0?`[${s.join(", ")}]`:"",t=new Date().toISOString().split("T")[0],r=` {helpful:0,harmful:0,lastSeen:${t},status:candidate}`,i=`
12
+ - [${t}]${o}${c} ${e}${r}`;try{h(b)||N(b,{recursive:!0}),h(_)?xe(_,i):A(_,`# Lessons Learned
13
+ `+i)}catch{}}function Me(e,o){if(!h(_))return{found:!1,newStatus:""};const s=$(_,"utf-8").split(`
14
+ `);let c=!1,t="";for(let r=0;r<s.length;r++){if(!s[r].startsWith("- [")||!s[r].toLowerCase().includes(e.toLowerCase()))continue;const i=ue(s[r]),n=new Date().toISOString().split("T")[0];o==="helpful"?i.helpful++:i.harmful++,i.lastSeen=n;const a=Ne(i);t=a,s[r]=Fe(s[r],{helpful:i.helpful,harmful:i.harmful,lastSeen:n,status:a}),c=!0;break}return c&&A(_,s.join(`
15
+ `)),{found:c,newStatus:t}}const Z=g(b,"coverage-baseline.json");function We(){const e=g(v,"coverage","coverage-summary.json");if(h(e))try{const s=JSON.parse($(e,"utf-8")).total;if(s)return{lines:s.lines?.pct??0,branches:s.branches?.pct??0,functions:s.functions?.pct??0}}catch{}const o=g(v,"coverage","lcov.info");if(!h(o))return null;try{const s=$(o,"utf-8");let c=0,t=0,r=0,i=0,n=0,a=0;for(const l of s.split(`
16
+ `))l.startsWith("LH:")&&(c+=parseInt(l.slice(3))||0),l.startsWith("LF:")&&(t+=parseInt(l.slice(3))||0),l.startsWith("BRH:")&&(r+=parseInt(l.slice(4))||0),l.startsWith("BRF:")&&(i+=parseInt(l.slice(4))||0),l.startsWith("FNH:")&&(n+=parseInt(l.slice(4))||0),l.startsWith("FNF:")&&(a+=parseInt(l.slice(4))||0);return{lines:t>0?c/t*100:0,branches:i>0?r/i*100:0,functions:a>0?n/a*100:0}}catch{return null}}function Be(){if(!h(Z))return null;try{return JSON.parse($(Z,"utf-8"))}catch{return null}}function de(e){h(b)||N(b,{recursive:!0});const o={...e,timestamp:new Date().toISOString()};A(Z,JSON.stringify(o,null,2))}function qe(){const e=We();if(!e)return null;const o=Be();if(!o)return de(e),`- Coverage baseline set: ${e.lines.toFixed(1)}% lines, ${e.branches.toFixed(1)}% branches, ${e.functions.toFixed(1)}% functions`;const s=[];return e.lines<o.lines-.5&&s.push(`Lines: ${e.lines.toFixed(1)}% (was ${o.lines.toFixed(1)}%)`),e.branches<o.branches-.5&&s.push(`Branches: ${e.branches.toFixed(1)}% (was ${o.branches.toFixed(1)}%)`),e.functions<o.functions-.5&&s.push(`Functions: ${e.functions.toFixed(1)}% (was ${o.functions.toFixed(1)}%)`),s.length>0?`- BLOCKED: Test coverage decreased:
17
+ ${s.join(`
18
+ `)}
19
+ Run tests with coverage and add tests for new code.`:(de(e),null)}function U(){return h(W)?$(W,"utf-8"):null}function X(e){h(b)||N(b,{recursive:!0}),A(W,e)}function O(e,o){const s=e.split(`
20
+ `),c=[];let t=null,r=0,i=0;for(const a of s)/^###?\s/.test(a)&&!a.startsWith("# Implementation")&&(t&&c.push(t),t={title:a.replace(/^#+\s*/,""),total:0,done:0}),/^\s*- \[x\]/i.test(a)?(r++,i++,t&&t.done++,t&&t.total++):/^\s*- \[ \]/.test(a)&&(i++,t&&t.total++);t&&c.push(t);let n=e;if(o){const a=o.toLowerCase(),l=[];let u=!1;for(const d of s)/^###?\s/.test(d)&&(u=d.toLowerCase().includes(a)),u&&l.push(d);n=l.length>0?l.join(`
21
+ `):e}return{stats:{total:i,done:r,pending:i-r,percent:i>0?Math.round(r/i*100):0},sections:c.filter(a=>a.total>0),filtered:n}}function He(e){const o=["# Implementation Progress","",`**Last updated:** ${new Date().toISOString()}`,"**Current phase:** 0 - BRAINSTORM",""];for(const s of e){o.push(`### ${s.title}`);for(const c of s.items)o.push(`- [ ] ${c}`);o.push("")}return o.push("## Notes",""),o.join(`
22
+ `)}function Ge(){const e=te();if(!e)return null;const o=se(e,"spec_template");if(!o)return null;const s=[{title:"Phase 0: BRAINSTORM",items:["Spec complete","User validated"]}],c=o.matchAll(/### Entity:\s*\[?(\w+)\]?/g),t=[];for(const n of c)n[1]&&n[1]!=="Name"&&t.push(n[1]);const r=o.matchAll(/\|\s*`?\/[\w-]*`?\s*\|\s*(\w+Page)\s*\|/g),i=[];for(const n of r)n[1]&&i.push(n[1]);return s.push({title:"Phase 1: SCAFFOLD",items:i.length>0?i.map(n=>`${n}.tsx`):["(generate from validated spec)"]}),s.push({title:"Phase 2: ENTITIES",items:t.length>0?t.map(n=>`${n} entity`):["(generate from validated spec)"]}),s.push({title:"Phase 3: COMPOSE",items:i.length>0?i.map(n=>`${n} composition`):["(generate from validated spec)"]}),s.push({title:"Phase 4: CONFIGURE",items:["Tests generated","Firestore rules","Config finalized","Mobile check"]}),s}function Je(e,o){const s=U();if(!s)return;const c=s.replace(/\*\*Current phase:\*\*\s*.*/,`**Current phase:** ${e} - ${o}`);c!==s&&X(c.replace(/\*\*Last updated:\*\*\s*.*/,`**Last updated:** ${new Date().toISOString()}`))}function I(){if(!h(z))return{project:re(v)||"unknown",started:new Date().toISOString().split("T")[0],sessions:[]};try{return JSON.parse($(z,"utf-8"))}catch{return{project:re(v)||"unknown",started:new Date().toISOString().split("T")[0],sessions:[]}}}function fe(e){const o=I();o.sessions.push(e),h(b)||N(b,{recursive:!0}),A(z,JSON.stringify(o,null,2))}const Q=["@donotdev/core","@donotdev/ui","@donotdev/components","@donotdev/templates","@donotdev/crud","@donotdev/auth","@donotdev/billing","@donotdev/oauth","@donotdev/components","@donotdev/firebase","@donotdev/supabase","@donotdev/functions","@donotdev/security","@donotdev/expo","@donotdev/mcp-server"],Ve=["@donotdev/cli"],ze=[...Q,...Ve];function Ke(){let e=v;for(let o=0;o<ie;o++){const s=g(e,"node_modules");if(h(s))return s;const c=H(e,"..");if(c===e)break;e=c}return null}function ee(){const e=g(v,"guides","dndev");return h(e)?e:h(B)?B:null}function te(){const e=g(v,"guides","wai-way");if(h(e))return e;const o=g(v,"packages","cli","templates","root-consumer","guides","wai-way");return h(o)?o:null}function se(e,o){for(const s of[".md",".md.example"]){const c=g(e,o+s);if(h(c))return $(c,"utf-8")}return null}function oe(e,o=5){const s=[];if(!h(e)||o<=0)return s;const c=M(e);for(const t of c){const r=g(e,t);try{we(r).isDirectory()?s.push(...oe(r,o-1)):t.endsWith(".d.ts")&&s.push(r)}catch{}}return s}function me(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Ye(e,o){let s=0,c=-1;for(let t=o;t<e.length;t++)if(e[t]==="{")s===0&&(c=t),s++;else if(e[t]==="}"&&(s--,s===0))return e.slice(c,t+1);return""}function Ze(e,o){const s=me(o),c=new RegExp(`(\\/\\*\\*[\\s\\S]*?\\*\\/\\s*)?(?:export\\s+)?(?:declare\\s+)?(?:interface|type|const|function|class)\\s+${s}\\b`,"g").exec(e);if(!c)return null;const t=c.index,r=c.index+c[0].length,i=e.slice(r),n=i.indexOf("{"),a=i.indexOf(";");if(a!==-1&&(n===-1||a<n))return e.slice(t,r+a+1).trim();if(n!==-1){const l=Ye(e,r+n);if(l)return e.slice(t,r+n+l.length).trim()}return null}function Xe(e){const o=[],s=e.matchAll(/export\s*\{([^}]+)\}/g);for(const n of s)if(n[1]){const a=n[1].split(",").map(l=>{const u=l.trim().split(/\s+as\s+/);return(u.length>1?u[1]:u[0])?.trim()}).filter(l=>!!l);o.push(...a)}const c=e.matchAll(/export\s+interface\s+(\w+)/g);for(const n of c)n[1]&&o.push(n[1]);const t=e.matchAll(/export\s+type\s+(\w+)/g);for(const n of t)n[1]&&o.push(n[1]);const r=e.matchAll(/export\s+const\s+(\w+)/g);for(const n of r)n[1]&&o.push(n[1]);const i=e.matchAll(/export\s+default\s+(\w+)/g);for(const n of i)n[1]&&o.push(n[1]);return[...new Set(o)].filter(n=>n&&n!=="default")}function Qe(e){if(!h(e))return null;try{const o=$(e,"utf-8").split(`
23
+ `);let s=!1;for(const c of o){if(c.startsWith("# ")){s=!0;continue}if(s&&c.trim().length>0)return c.trim()}}catch{}return null}function he(e,o){const s=h(g(e,"context_map.json"))?g(e,"context_map.json"):g(e,"context_map.json.example");if(!h(s))return null;try{const c=JSON.parse($(s,"utf-8")),t=Object.keys(c.phases||{}).find(r=>r.startsWith(`${o}_`));if(t&&c.phases[t])return c.phases[t]}catch{}return null}function et(e){const o=[];if(!h(e))return[`File not found: ${e}`];const s=$(e,"utf-8"),c=s.split(`
24
+ `),t=e.split("/").pop()||"";if(!t.endsWith(".tsx")&&!t.endsWith(".ts"))return[];for(let i=0;i<c.length;i++){const n=c[i],a=i+1;/style\s*=\s*\{\{/.test(n)&&o.push(`L${a}: Inline style found. Use className or framework utilities.`),/fontSize|font-size/i.test(n)&&!/\/\/|\/\*|\*/.test(n.trimStart().substring(0,2))&&o.push(`L${a}: fontSize detected. Use Text level or Card title/subtitle props.`),/text-align:\s*(left|right)|textAlign:\s*['"]?(left|right)/i.test(n)&&o.push(`L${a}: textAlign left/right detected. Use start/end for RTL support.`),/\brequire\s*\(/.test(n)&&!/\/\/|\/\*|\*/.test(n.trimStart().substring(0,2))&&o.push(`L${a}: require() found. Use ESM import.`),/\.toLocaleDateString|\.toLocaleTimeString|new Intl\.DateTimeFormat/i.test(n)&&o.push(`L${a}: Manual date formatting. Use formatDate() from @donotdev/core.`),/@donotdev\//.test(s)&&/(?:gap|spacing|size)=["'](?:xs|sm|md|lg|xl|2xl)["']/.test(n)&&o.push(`L${a}: Invalid size token. DoNotDev uses none/tight/medium/large \u2014 not xs/sm/md/lg/xl.`)}const r=c.map((i,n)=>({line:i,num:n+1})).filter(({line:i})=>/^import\s/.test(i));if(r.length>1){let i=0;for(const{line:n,num:a}of r){let l=4;if(/from\s+['"]react/.test(n)?l=1:/from\s+['"]@donotdev\//.test(n)?l=3:/from\s+['"][^.]/.test(n)&&(l=2),l<i){o.push(`L${a}: Import order violation. Expected: React > vendors > @donotdev > relative.`);break}i=l}}return o}function tt(e,o){const s=[],c=/<([A-Z]\w+)/g;for(const t of e){if(!h(t))continue;const r=$(t,"utf-8");if(!/@donotdev\//.test(r))continue;const i=r.matchAll(c);for(const n of i){const a=n[1];a&&(["React","Fragment","Suspense","Provider","Router","Route","Switch"].includes(a)||!o.includes(a)&&!o.includes(`${a}Props`)&&s.push(`${t}: <${a}> used but lookup_symbol("${a}") was never called. Props may be hallucinated.`))}}return[...new Set(s)]}const ne=new Se({name:"donotdev-mcp",version:"0.0.6"},{capabilities:{tools:{}}});ne.setRequestHandler(be,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:"run_typecheck",description:"Run TypeScript type-check on the consumer project. Returns structured results. Run after every code change.",inputSchema:{type:"object",properties:{package:{type:"string",description:"Optional: scope to a specific app/package name or path fragment"}}}},{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"]}},{name:"score_lesson",description:"Score a lesson as helpful or harmful. Updates confidence metadata. Lessons decay over time (half-life: 90 days). High harmful ratio auto-converts to anti-pattern.",inputSchema:{type:"object",properties:{lesson:{type:"string",description:'Substring to match the lesson (e.g., "Zustand store actions", "use client directive")'},outcome:{type:"string",enum:["helpful","harmful"],description:"Whether this lesson helped or harmed the current task"}},required:["lesson","outcome"]}},{name:"setup_coach",description:"Get contextual setup instructions for a specific manual step. Reads from guide files \u2014 never accesses .env or secrets. Returns markdown coaching for dashboard-click steps.",inputSchema:{type:"object",properties:{topic:{type:"string",description:'Setup topic (e.g., "stripe-webhook", "oauth-google", "firebase-service-account", "supabase-credentials")'},provider:{type:"string",description:"Optional provider context (firebase, supabase, vercel, stripe)"},projectId:{type:"string",description:"Optional project ID for generating console URLs (public, not a secret)"},region:{type:"string",description:'Optional Firebase Cloud Functions region (e.g., "europe-west1", "us-central1"). Defaults to europe-west1.'}},required:["topic"]}}]}));function x(e,o){if(typeof e!="string")throw new Error(`Expected "${o}" to be a string, got ${typeof e}`)}function st(e,o){if(typeof e!="number"||!Number.isFinite(e))throw new Error(`Expected "${o}" to be a number, got ${typeof e}`)}function ot(e,o){if(typeof e!="boolean")throw new Error(`Expected "${o}" to be a boolean, got ${typeof e}`)}function ge(e,o){if(!Array.isArray(e)||e.some(s=>typeof s!="string"))throw new Error(`Expected "${o}" to be a string array`)}ne.setRequestHandler(ve,async e=>{const{name:o,arguments:s}=e.params,c=Ke();if(o!=="start_phase"&&o!=="approve_phase"&&Ce(o),o==="start_phase"){le(),st(s?.phase,"phase");const t=s.phase,r=s?.module!=null?(x(s.module,"module"),s.module):void 0,i=L.find(y=>y.id===t);if(!i)return{content:[{type:"text",text:`Invalid phase: ${t}. Valid: 0-4.`}],isError:!0};const n=te();if(!n)return{content:[{type:"text",text:"Error: guides/wai-way/ directory not found."}],isError:!0};const a=se(g(n,"blueprints"),i.blueprint),l=se(g(n,"agents"),i.agentFile),u=he(n,t),d=De({phase:t,module:r}),f=E();f.version=J,f.currentPhase=i.id,f.phaseName=i.name,f.agent=i.agent,f.startedAt=new Date().toISOString(),f.lookedUpSymbols=[],f.pendingReview=!1,f.currentModule=r||null,f.toolCallCounts=void 0,f.lessonsRecorded=void 0,f.lessonsScored=void 0,R(f);const p=[`# Phase ${i.id}: ${i.name}`,`**Agent:** ${i.agent}`];r&&p.push(`**Module:** ${r}`);const m=f.completedPhases.sort().map(y=>{const w=L.find(T=>T.id===y);return w?w.name:`${y}`}),S=U(),P=S?O(S):null,F=I(),C=[`
25
+ ## Project State`];if(C.push(`- **Phase:** ${i.id}/4 ${i.name}${m.length>0?` (done: ${m.join(", ")})`:""}`),P&&C.push(`- **Progress:** ${P.stats.done}/${P.stats.total} (${P.stats.percent}%)`),F.sessions.length>0&&C.push(`- **Sessions so far:** ${F.sessions.length}`),p.push(...C),u&&p.push(`
26
+ ## Phase Context`,`**Goal:** ${u.goal}`,`**Done when:** ${u.done_when}`,`**Output:** ${u.output}`,"**Read these files first:**",...u.read_files.map(y=>`- ${y}`)),a&&p.push(`
21
27
  ---
22
28
  ## Blueprint
23
29
 
24
- ${i}`),c&&d.push(`
30
+ ${a}`),l&&p.push(`
25
31
  ---
26
32
  ## Agent Definition
27
33
 
28
- ${c}`),f.trim().length>20&&d.push(`
34
+ ${l}`),d.trim().length>20&&p.push(`
29
35
  ---
30
36
  ## Lessons (Filtered)
31
37
 
32
- ${f}`),b){const{stats:y,filtered:E}=P(b,`Phase ${e}`);d.push(`
38
+ ${d}`),S){const{stats:y,filtered:w}=O(S,`Phase ${t}`);p.push(`
33
39
  ---
34
- ## Implementation Progress`,`**Overall:** ${y.done}/${y.total} (${y.percent}%)`,"",E,"",'Use `update_progress({ item: "...", done: true })` to tick items as you complete them.')}else e>0&&d.push(`
40
+ ## Implementation Progress`,`**Overall:** ${y.done}/${y.total} (${y.percent}%)`,"",w,"",'Use `update_progress({ item: "...", done: true })` to tick items as you complete them.')}else t>0&&p.push(`
35
41
  ---
36
- ## Implementation Progress`,"No implementation.md found. Consider running `init_implementation({ from_spec: true })` to track progress across sessions.");d.push(`
42
+ ## Implementation Progress`,"No implementation.md found. Consider running `init_implementation({ from_spec: true })` to track progress across sessions.");p.push(`
37
43
  ---
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","- **NEVER create .md files** unless explicitly asked \u2014 session notes go in `.dndev/`","- Tracked symbols so far: none (call lookup_symbol to add)");const C={0:[],1:[],2:[],3:["After composing, run `/grill` to review code quality before moving to Phase 4."],4:["Run `/grill` for a staff-engineer code review.","Run `/techdebt` to surface cleanup items before shipping."]}[e]||[];C.length>0&&d.push(`
44
+ ## 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","- **NEVER create .md files** unless explicitly asked \u2014 session notes go in `.dndev/`","- Tracked symbols so far: none (call lookup_symbol to add)");const D={0:[],1:[],2:[],3:["After composing, run `/grill` to review code quality before moving to Phase 4."],4:["Run `/grill` for a staff-engineer code review.","Run `/techdebt` to surface cleanup items before shipping."]}[t]||[];D.length>0&&p.push(`
39
45
  ---
40
- ## Recommended Skills`,...C.map(y=>`- ${y}`));const A=ae();d.push(`
46
+ ## Recommended Skills`,...D.map(y=>`- ${y}`));const k=ce();p.push(`
41
47
  ---
42
- ## Project Args`,`- **Platform:** ${A.platform}`,`- **Strictness:** ${A.strictness}`,`- **Features:** ${A.features.join(", ")}`,`- **Region:** ${A.region}`);const S=we(e,A.features);return S&&d.push(`
48
+ ## Project Args`,`- **Platform:** ${k.platform}`,`- **Strictness:** ${k.strictness}`,`- **Features:** ${k.features.join(", ")}`,`- **Region:** ${k.region}`);const j=Oe(t,k.features);return j&&p.push(`
43
49
  ---
44
- ## Gotchas (Phase ${e})
50
+ ## Gotchas (Phase ${t})
45
51
 
46
- ${S}`),!i&&!c&&d.push(`
47
- No blueprint or agent definition found for this phase.`),{content:[{type:"text",text:d.join(`
48
- `)}]}}if(a==="complete_phase"){const e=O();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 r=e.currentPhase,s=e.phaseName,t=e.currentModule?` [${e.currentModule}]`:"",i=n?.files!=null?(ue(n.files,"files"),n.files):[],c=[],p=[];if(i.length>0){for(const y of i){const E=ve(y);if(!E){c.push(`- BLOCKED: "${y}" resolves outside project root. Skipped.`);continue}p.push(E)}for(let y=0;y<p.length;y++){const E=Ue(p[y]);E.length>0&&c.push(`
49
- ### ${i[y]}`,...E.map(q=>`- ${q}`))}const S=je(p,e.lookedUpSymbols);S.length>0&&(c.push(`
50
- ### Unverified Component Usage`),c.push(...S.map(y=>`- ${y}`)))}const f=ee();if(f){const S=le(f,r);S&&c.push(`
51
- ### Done Criteria`,`**Required:** ${S.done_when}`,`**Expected output:** ${S.output}`,"","Verify these criteria are met.")}const u=ae(),d=c.some(S=>S.startsWith("- L")||S.includes("Unverified Component")||S.includes("BLOCKED"));if(d&&u.strictness==="enforced")return{content:[{type:"text",text:[`## Validation Failed: Phase ${r} (${s})${t}`,"","**Status:** ISSUES FOUND \u2014 fix before completing phase","**Strictness:** enforced (change in .dndev/args.json to override)",...c,"","### Type Check","Run `dndev type-check` or `bun run type-check` to verify TypeScript compiles without errors.","","Fix the issues above, then call `complete_phase({ files: [...] })` again."].join(`
52
- `)}]};d&&u.strictness==="warnings"&&c.unshift(`
53
- ### Warnings (non-blocking)`,"**Strictness:** warnings \u2014 issues reported but phase can proceed.");const g=n?.lesson!=null?(v(n.lesson,"lesson"),n.lesson):void 0;g&&ce(g,` [Phase ${r}: ${s}]`);const b=n?.summary!=null?(v(n.summary,"summary"),n.summary):void 0;e.pendingLogData={files_touched:p.length,summary:b},e.pendingReview=!0,G(e);const _=e.lookedUpSymbols.length,R=T.find(S=>S.id===r+1),k=R?`Next: Phase ${R.id} (${R.name})`:"This was the final phase.";let W="";const C=L();if(C){const{stats:S}=P(C,`Phase ${r}`);W=`**Implementation:** ${S.done}/${S.total} items (${S.percent}%)`}return{content:[{type:"text",text:[`## Phase ${r} (${s})${t} \u2014 Ready for Review`,"",`**Validation:** ${i.length>0?"PASSED":"No files provided (skipped)"}`,`**Symbols looked up:** ${_} (${e.lookedUpSymbols.join(", ")||"none"})`,W,g?`**Lesson recorded:** ${g}`:"","","### 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.","",k].filter(Boolean).join(`
54
- `)}]}}if(a==="approve_phase"){const e=O();if(!e.pendingReview||e.currentPhase===null)return{content:[{type:"text",text:"No phase pending review. Call complete_phase() first."}],isError:!0};const r=e.currentPhase,s=e.phaseName,t=B();ke({id:t.sessions.length+1,date:new Date().toISOString().split("T")[0],phase:r,phase_name:s||T[r]?.name||"UNKNOWN",module:e.currentModule||void 0,started_at:e.startedAt||new Date().toISOString(),completed_at:new Date().toISOString(),outcome:e.pendingLogData?.summary||`Phase ${r} (${s}) completed.`,files_touched:e.pendingLogData?.files_touched||0,symbols_used:e.lookedUpSymbols.length}),e.completedPhases.includes(r)||e.completedPhases.push(r),e.currentPhase=null,e.phaseName=null,e.agent=null,e.startedAt=null,e.pendingReview=!1,e.lookedUpSymbols=[],e.currentModule=null,e.pendingLogData=void 0,G(e);const i=T.find(p=>p.id===r+1),c=i?`Next: call start_phase(${i.id}) to begin ${i.name}.`:"All phases complete. App is ready.";return i&&Re(i.id,i.name),{content:[{type:"text",text:`Phase ${r} (${s}) approved and completed.
55
- ${c}`}]}}if(a==="get_phase_status"){const e=O(),r=e.completedPhases.sort().map(t=>{const i=T.find(c=>c.id===t);return i?`${i.id}: ${i.name}`:`${t}`}).join(", ");return e.currentPhase===null?{content:[{type:"text",text:`No active phase.
52
+ ${j}`),!a&&!l&&p.push(`
53
+ No blueprint or agent definition found for this phase.`),{content:[{type:"text",text:p.join(`
54
+ `)}]}}if(o==="complete_phase"){const t=E();if(t.currentPhase===null)return{content:[{type:"text",text:"No active phase to complete."}],isError:!0};if(t.pendingReview)return{content:[{type:"text",text:"Phase already pending review. Ask the user to review the output, then call approve_phase()."}],isError:!0};const r=t.currentPhase,i=t.phaseName,n=t.currentModule?` [${t.currentModule}]`:"",a=s?.files!=null?(ge(s.files,"files"),s.files):[],l=[],u=[];if(a.length>0){for(const w of a){const T=Ae(w);if(!T){l.push(`- BLOCKED: "${w}" resolves outside project root. Skipped.`);continue}u.push(T)}for(let w=0;w<u.length;w++){const T=et(u[w]);T.length>0&&l.push(`
55
+ ### ${a[w]}`,...T.map(ye=>`- ${ye}`))}const y=tt(u,t.lookedUpSymbols);y.length>0&&(l.push(`
56
+ ### Unverified Component Usage`),l.push(...y.map(w=>`- ${w}`)))}const d=qe();d&&(l.push(`
57
+ ### Test Coverage`),l.push(d));const f=te();if(f){const y=he(f,r);y&&l.push(`
58
+ ### Done Criteria`,`**Required:** ${y.done_when}`,`**Expected output:** ${y.output}`,"","Verify these criteria are met.")}const p=ce(),m=l.some(y=>y.startsWith("- L")||y.includes("Unverified Component")||y.includes("BLOCKED"));if(m&&p.strictness==="enforced")return{content:[{type:"text",text:[`## Validation Failed: Phase ${r} (${i})${n}`,"","**Status:** ISSUES FOUND \u2014 fix before completing phase","**Strictness:** enforced (change in .dndev/args.json to override)",...l,"","### Type Check","Run `dndev type-check` or `bun run type-check` to verify TypeScript compiles without errors.","","Fix the issues above, then call `complete_phase({ files: [...] })` again."].join(`
59
+ `)}]};m&&p.strictness==="warnings"&&l.unshift(`
60
+ ### Warnings (non-blocking)`,"**Strictness:** warnings \u2014 issues reported but phase can proceed.");const S=s?.lesson!=null?(x(s.lesson,"lesson"),s.lesson):void 0;S&&pe(S,` [Phase ${r}: ${i}]`);const P=s?.summary!=null?(x(s.summary,"summary"),s.summary):void 0;t.pendingLogData={files_touched:u.length,summary:P,validation:m?"warnings":"passed"},t.pendingReview=!0,R(t);const F=t.lookedUpSymbols.length,C=L.find(y=>y.id===r+1),D=C?`Next: Phase ${C.id} (${C.name})`:"This was the final phase.";let k="";const j=U();if(j){const{stats:y}=O(j,`Phase ${r}`);k=`**Implementation:** ${y.done}/${y.total} items (${y.percent}%)`}return{content:[{type:"text",text:[`## Phase ${r} (${i})${n} \u2014 Ready for Review`,"",`**Validation:** ${a.length>0?"PASSED":"No files provided (skipped)"}`,`**Symbols looked up:** ${F} (${t.lookedUpSymbols.join(", ")||"none"})`,k,S?`**Lesson recorded:** ${S}`:"","","### 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.","",D].filter(Boolean).join(`
61
+ `)}]}}if(o==="approve_phase"){const t=E();if(!t.pendingReview||t.currentPhase===null)return{content:[{type:"text",text:"No phase pending review. Call complete_phase() first."}],isError:!0};const r=t.currentPhase,i=t.phaseName,n=I();fe({id:n.sessions.length+1,date:new Date().toISOString().split("T")[0],phase:r,phase_name:i||L[r]?.name||"UNKNOWN",module:t.currentModule||void 0,started_at:t.startedAt||new Date().toISOString(),completed_at:new Date().toISOString(),outcome:t.pendingLogData?.summary||`Phase ${r} (${i}) completed.`,files_touched:t.pendingLogData?.files_touched||0,symbols_used:t.lookedUpSymbols.length,tool_calls:t.toolCallCounts&&Object.keys(t.toolCallCounts).length>0?t.toolCallCounts:void 0,lessons_recorded:t.lessonsRecorded||void 0,lessons_scored:t.lessonsScored&&(t.lessonsScored.helpful>0||t.lessonsScored.harmful>0)?t.lessonsScored:void 0,validation:t.pendingLogData?.validation}),t.completedPhases.includes(r)||t.completedPhases.push(r),t.currentPhase=null,t.phaseName=null,t.agent=null,t.startedAt=null,t.pendingReview=!1,t.lookedUpSymbols=[],t.currentModule=null,t.pendingLogData=void 0,t.toolCallCounts=void 0,t.lessonsRecorded=void 0,t.lessonsScored=void 0,R(t);const a=L.find(u=>u.id===r+1),l=a?`Next: call start_phase(${a.id}) to begin ${a.name}.`:"All phases complete. App is ready.";return a&&Je(a.id,a.name),{content:[{type:"text",text:`Phase ${r} (${i}) approved and completed.
62
+ ${l}`}]}}if(o==="get_phase_status"){const t=E(),r=t.completedPhases.sort().map(i=>{const n=L.find(a=>a.id===i);return n?`${n.id}: ${n.name}`:`${i}`}).join(", ");return t.currentPhase===null?{content:[{type:"text",text:`No active phase.
56
63
  Completed: ${r||"none"}
57
- 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: ${r||"none"}`].filter(Boolean).join(`
58
- `)}]}}if(a==="get_project_history"){const e=B();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 r=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:** ${r}`,`**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(`
59
- `)}]}}if(a==="run_typecheck"){const e=n?.package!=null?(v(n.package,"package"),n.package):void 0,{execFileSync:r}=await import("child_process"),s=["tc"];e&&s.push(e);try{const t=r("dndev",s,{cwd:$,encoding:"utf8",env:{...process.env,FORCE_COLOR:"0"},stdio:["ignore","pipe","pipe"]}),i=t.split(`
60
- `).find(c=>c.startsWith("RESULT:"));return{content:[{type:"text",text:t.trim()+(i?"":`
61
- RESULT: unknown`)}]}}catch(t){return{content:[{type:"text",text:((t.stdout??"")+(t.stderr??"")).trim()||"Type-check failed with no output"}],isError:!0}}}if(a==="record_lesson"){v(n?.lesson,"lesson");const e=n.lesson,r=n?.tags!=null?(ue(n.tags,"tags"),n.tags):void 0,s=O(),t=s.currentPhase!==null?` [Phase ${s.currentPhase}: ${s.phaseName}]`:"";return ce(e,t,r),{content:[{type:"text",text:`Lesson recorded${r&&r.length>0?` [${r.join(", ")}]`:""}. Filtered by tags on next start_phase().
62
- Also add a \`// GOTCHA: ...\` comment in the relevant source file.`}]}}if(a==="setup_coach"){v(n?.topic,"topic");const e=n.topic,r=typeof n?.provider=="string"?n.provider:void 0,s=typeof n?.projectId=="string"?n.projectId:void 0,t={"stripe-webhook":"SETUP_STRIPE.md.example","stripe-keys":"SETUP_STRIPE.md.example",stripe:"SETUP_STRIPE.md.example","oauth-google":"SETUP_OAUTH_PROVIDERS.md.example","oauth-github":"SETUP_OAUTH_PROVIDERS.md.example","oauth-apple":"SETUP_OAUTH_PROVIDERS.md.example",oauth:"SETUP_OAUTH_PROVIDERS.md.example","firebase-service-account":"SETUP_FIREBASE.md.example","firebase-enable-services":"SETUP_FIREBASE.md.example",firebase:"SETUP_FIREBASE.md.example","supabase-credentials":"SETUP_SUPABASE.md.example","supabase-secret-key":"SETUP_SUPABASE.md.example","supabase-rls":"SETUP_SUPABASE.md.example",supabase:"SETUP_SUPABASE.md.example","vercel-domain":"SETUP_VERCEL.md.example",vercel:"SETUP_VERCEL.md.example","auth-firebase":"SETUP_AUTH.md.example","auth-supabase":"SETUP_AUTH.md.example",auth:"SETUP_AUTH.md.example",billing:"SETUP_BILLING.md.example"},i=t[e.toLowerCase()]??t[r?.toLowerCase()??""],c=[i?m(D,i):null,m($,"guides","dndev",i||`SETUP_${e.toUpperCase()}.md.example`),m($,"guides","dndev",i||`SETUP_${e.toUpperCase()}.md`)].filter(Boolean);let p="";for(const u of c)if(h(u)){p=x(u,"utf-8");break}if(!p)return{content:[{type:"text",text:`No guide found for topic "${e}". Available topics: ${Object.keys(t).join(", ")}`}]};s&&(p=p.replace(/\{\{PROJECT_ID\}\}/g,s));const f=n?.region;return p=p.replace(/\{\{REGION\}\}/g,typeof f=="string"?f:"europe-west1"),{content:[{type:"text",text:p}]}}if(a==="init_implementation"){const e=n?.sections,r=n?.from_spec,s=n?.force;if(r!==void 0&&typeof r!="boolean")throw new Error("from_spec must be a boolean");if(s!==void 0&&typeof s!="boolean")throw new Error("force must be a boolean");if(e!==void 0){if(!Array.isArray(e))throw new Error("sections must be an array");for(const p of e){if(!p||typeof p!="object")throw new Error("each section must be an object");if(typeof p.title!="string")throw new Error("section.title must be a string");if(!Array.isArray(p.items))throw new Error("section.items must be an array")}}if(h(F)&&!s){const p=L(),{stats:f}=P(p);return{content:[{type:"text",text:`implementation.md already exists (${f.done}/${f.total} items done, ${f.percent}%).
63
- Pass force: true to overwrite, or use update_progress/get_progress to work with existing file.`}]}}let t;if(r){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=Pe(t);X(i);const{stats:c}=P(i);return{content:[{type:"text",text:`implementation.md created with ${c.total} items across ${t.length} sections.
64
+ Call start_phase(N) to begin a phase.`}]}:{content:[{type:"text",text:[`Active: Phase ${t.currentPhase} (${t.phaseName})`,`Agent: ${t.agent}`,`Started: ${t.startedAt}`,t.currentModule?`Module: ${t.currentModule}`:null,`Pending review: ${t.pendingReview?"YES \u2014 user must approve":"no"}`,`Symbols looked up: ${t.lookedUpSymbols.length} (${t.lookedUpSymbols.join(", ")||"none"})`,`Completed phases: ${r||"none"}`].filter(Boolean).join(`
65
+ `)}]}}if(o==="get_project_history"){const t=I();if(t.sessions.length===0)return{content:[{type:"text",text:"No sessions logged yet. Captain's log entries are created automatically when phases are approved."}]};const r=t.sessions.reduce((l,u)=>l+u.files_touched,0),i=t.sessions.reduce((l,u)=>l+u.symbols_used,0),n=[...new Set(t.sessions.map(l=>l.phase))].length,a=[`## Captain's Log \u2014 ${t.project}`,`**Started:** ${t.started}`,`**Sessions:** ${t.sessions.length}`,`**Phases completed:** ${n}/5`,`**Total files touched:** ${r}`,`**Total symbols used:** ${i}`,"","### Timeline"];for(const l of t.sessions){const u=l.module?` [${l.module}]`:"",d=[];if(l.files_touched&&d.push(`${l.files_touched} files`),l.symbols_used&&d.push(`${l.symbols_used} symbols`),l.tool_calls){const p=Object.values(l.tool_calls).reduce((m,S)=>m+S,0);d.push(`${p} tool calls`)}if(l.lessons_recorded&&d.push(`${l.lessons_recorded} lessons`),l.lessons_scored){const p=l.lessons_scored;d.push(`scored ${p.helpful}H/${p.harmful}X`)}l.validation&&d.push(`validation: ${l.validation}`);const f=d.length>0?` (${d.join(", ")})`:"";a.push(`${l.id}. **[${l.date}]** Phase ${l.phase} (${l.phase_name})${u} \u2014 ${l.outcome}${f}`)}return a.push("","Raw JSON: .dndev/captain-log.json"),{content:[{type:"text",text:a.join(`
66
+ `)}]}}if(o==="run_typecheck"){const t=s?.package!=null?(x(s.package,"package"),s.package):void 0,{execFileSync:r}=await import("child_process"),i=["tc"];t&&i.push(t);try{const n=r("dndev",i,{cwd:v,encoding:"utf8",env:{...process.env,FORCE_COLOR:"0"},stdio:["ignore","pipe","pipe"]}),a=n.split(`
67
+ `).find(l=>l.startsWith("RESULT:"));return{content:[{type:"text",text:n.trim()+(a?"":`
68
+ RESULT: unknown`)}]}}catch(n){return{content:[{type:"text",text:((n.stdout??"")+(n.stderr??"")).trim()||"Type-check failed with no output"}],isError:!0}}}if(o==="record_lesson"){x(s?.lesson,"lesson");const t=s.lesson,r=s?.tags!=null?(ge(s.tags,"tags"),s.tags):void 0,i=E(),n=i.currentPhase!==null?` [Phase ${i.currentPhase}: ${i.phaseName}]`:"",a=h(_)?$(_,"utf-8"):"",l=t.toLowerCase().split(/\s+/).filter(m=>m.length>4),u=a.split(`
69
+ `).filter(m=>{if(!m.startsWith("- ["))return!1;const S=m.toLowerCase();return l.filter(P=>S.includes(P)).length>=Math.min(3,l.length)}),d=I(),f=d.sessions.filter(m=>r&&r.length>0&&m.module?r.some(S=>m.module.toLowerCase().includes(S.toLowerCase())):i.currentPhase!==null?m.phase===i.currentPhase:!1);let p="";return u.length>0&&(p=`
70
+ **Similar lesson exists** \u2014 consider scoring the existing one instead of recording a duplicate.`),f.length>0?p+=`
71
+ **Corroborated** by ${f.length} prior session(s) in same phase/module.`:d.sessions.length>0&&(p+=`
72
+ **Uncorroborated** \u2014 no prior sessions match. Starts as candidate, needs validation.`),pe(t,n,r),ke(),{content:[{type:"text",text:`Lesson recorded${r&&r.length>0?` [${r.join(", ")}]`:""}. Filtered by tags on next start_phase().${p}
73
+ Also add a \`// GOTCHA: ...\` comment in the relevant source file.`}]}}if(o==="score_lesson"){x(s?.lesson,"lesson"),x(s?.outcome,"outcome");const t=s.lesson,r=s.outcome;if(r!=="helpful"&&r!=="harmful")return{content:[{type:"text",text:'outcome must be "helpful" or "harmful"'}],isError:!0};const{found:i,newStatus:n}=Me(t,r);return i&&Re(r),i?{content:[{type:"text",text:`Lesson scored as ${r}. Status: ${{candidate:"candidate",established:"established (3+ helpful)",proven:"proven (10+ helpful)",deprecated:"DEPRECATED (>25% harmful)","anti-pattern":"ANTI-PATTERN (>50% harmful, auto-inverted as PITFALL)"}[n]??n}.`}]}:{content:[{type:"text",text:`No lesson found matching "${t}".`}],isError:!0}}if(o==="setup_coach"){x(s?.topic,"topic");const t=s.topic,r=typeof s?.provider=="string"?s.provider:void 0,i=typeof s?.projectId=="string"?s.projectId:void 0,n={"stripe-webhook":"SETUP_STRIPE.md.example","stripe-keys":"SETUP_STRIPE.md.example",stripe:"SETUP_STRIPE.md.example","oauth-google":"SETUP_OAUTH_PROVIDERS.md.example","oauth-github":"SETUP_OAUTH_PROVIDERS.md.example","oauth-apple":"SETUP_OAUTH_PROVIDERS.md.example",oauth:"SETUP_OAUTH_PROVIDERS.md.example","firebase-service-account":"SETUP_FIREBASE.md.example","firebase-enable-services":"SETUP_FIREBASE.md.example",firebase:"SETUP_FIREBASE.md.example","supabase-credentials":"SETUP_SUPABASE.md.example","supabase-secret-key":"SETUP_SUPABASE.md.example","supabase-rls":"SETUP_SUPABASE.md.example",supabase:"SETUP_SUPABASE.md.example","vercel-domain":"SETUP_VERCEL.md.example",vercel:"SETUP_VERCEL.md.example","auth-firebase":"SETUP_AUTH.md.example","auth-supabase":"SETUP_AUTH.md.example",auth:"SETUP_AUTH.md.example",billing:"SETUP_BILLING.md.example"},a=n[t.toLowerCase()]??n[r?.toLowerCase()??""],l=[a?g(B,a):null,g(v,"guides","dndev",a||`SETUP_${t.toUpperCase()}.md.example`),g(v,"guides","dndev",a||`SETUP_${t.toUpperCase()}.md`)].filter(Boolean);let u="";for(const f of l)if(h(f)){u=$(f,"utf-8");break}if(!u)return{content:[{type:"text",text:`No guide found for topic "${t}". Available topics: ${Object.keys(n).join(", ")}`}]};i&&(u=u.replace(/\{\{PROJECT_ID\}\}/g,i));const d=s?.region;return u=u.replace(/\{\{REGION\}\}/g,typeof d=="string"?d:"europe-west1"),{content:[{type:"text",text:u}]}}if(o==="init_implementation"){const t=s?.sections,r=s?.from_spec,i=s?.force;if(r!==void 0&&typeof r!="boolean")throw new Error("from_spec must be a boolean");if(i!==void 0&&typeof i!="boolean")throw new Error("force must be a boolean");if(t!==void 0){if(!Array.isArray(t))throw new Error("sections must be an array");for(const u of t){if(!u||typeof u!="object")throw new Error("each section must be an object");if(typeof u.title!="string")throw new Error("section.title must be a string");if(!Array.isArray(u.items))throw new Error("section.items must be an array")}}if(h(W)&&!i){const u=U(),{stats:d}=O(u);return{content:[{type:"text",text:`implementation.md already exists (${d.done}/${d.total} items done, ${d.percent}%).
74
+ Pass force: true to overwrite, or use update_progress/get_progress to work with existing file.`}]}}let n;if(r){const u=Ge();if(!u)return{content:[{type:"text",text:"Could not generate from spec: spec_template.md not found in guides/wai-way/."}],isError:!0};n=u}else if(t&&t.length>0)n=t;else return{content:[{type:"text",text:"Provide sections (array of {title, items}) or set from_spec: true."}],isError:!0};const a=He(n);X(a);const{stats:l}=O(a);return{content:[{type:"text",text:`implementation.md created with ${l.total} items across ${n.length} sections.
64
75
 
65
- ${i}`}]}}if(a==="update_progress"){v(n?.item,"item"),De(n?.done,"done");const e=n.item,r=n.done,s=n?.note!=null?(v(n.note,"note"),n.note):void 0,t=L();if(!t)return{content:[{type:"text",text:"No implementation.md found. Call init_implementation() first."}],isError:!0};const i=t.split(`
66
- `),c=e.toLowerCase();let p=!1,f="";for(let g=0;g<i.length;g++){const b=i[g];if(/^\s*- \[[ x]\]/i.test(b)&&b.toLowerCase().includes(c)){r?i[g]=b.replace(/- \[ \]/,"- [x]"):i[g]=b.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 u=i.join(`
67
- `);if(u=u.replace(/\*\*Last updated:\*\*\s*.*/,`**Last updated:** ${new Date().toISOString()}`),s){const g=`- ${s}`;u.includes("## Notes")?u=u.replace("## Notes",`## Notes
68
- ${g}`):u+=`
76
+ ${a}`}]}}if(o==="update_progress"){x(s?.item,"item"),ot(s?.done,"done");const t=s.item,r=s.done,i=s?.note!=null?(x(s.note,"note"),s.note):void 0,n=U();if(!n)return{content:[{type:"text",text:"No implementation.md found. Call init_implementation() first."}],isError:!0};const a=n.split(`
77
+ `),l=t.toLowerCase();let u=!1,d="";for(let m=0;m<a.length;m++){const S=a[m];if(/^\s*- \[[ x]\]/i.test(S)&&S.toLowerCase().includes(l)){r?a[m]=S.replace(/- \[ \]/,"- [x]"):a[m]=S.replace(/- \[x\]/i,"- [ ]"),d=a[m],u=!0;break}}if(!u)return{content:[{type:"text",text:`No checklist item matching "${t}" found in implementation.md.`}],isError:!0};let f=a.join(`
78
+ `);if(f=f.replace(/\*\*Last updated:\*\*\s*.*/,`**Last updated:** ${new Date().toISOString()}`),i){const m=`- ${i}`;f.includes("## Notes")?f=f.replace("## Notes",`## Notes
79
+ ${m}`):f+=`
69
80
  ## Notes
70
- ${g}
71
- `}X(u);const{stats:d}=P(u);return{content:[{type:"text",text:`Updated: ${f.trim()}
72
- Progress: ${d.done}/${d.total} (${d.percent}%)`}]}}if(a==="get_progress"){const e=n?.section!=null?(v(n.section,"section"),n.section):void 0,r=L();if(!r)return{content:[{type:"text",text:"No implementation.md found. Call init_implementation() first."}],isError:!0};const{stats:s,sections:t,filtered:i}=P(r,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 f=p.total>0?Math.round(p.done/p.total*100):0;c.push(`- **${p.title}:** ${p.done}/${p.total} (${f}%)`)}c.push("")}return c.push("---","",i),{content:[{type:"text",text:c.join(`
73
- `)}]}}if(!l&&!["get_guide","get_guideline","search_framework"].includes(a))return{content:[{type:"text",text:"Error: node_modules not found. Run bun install first."}],isError:!0};if(a==="get_guide"){v(n?.topic,"topic");const e=n.topic,r=Q();if(!r)return{content:[{type:"text",text:"Error: guides directory not found."}],isError:!0};const s=N(r).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}):
81
+ ${m}
82
+ `}X(f);const{stats:p}=O(f);return{content:[{type:"text",text:`Updated: ${d.trim()}
83
+ Progress: ${p.done}/${p.total} (${p.percent}%)`}]}}if(o==="get_progress"){const t=s?.section!=null?(x(s.section,"section"),s.section):void 0,r=U();if(!r)return{content:[{type:"text",text:"No implementation.md found. Call init_implementation() first."}],isError:!0};const{stats:i,sections:n,filtered:a}=O(r,t),l=["## Implementation Progress",`**Overall:** ${i.done}/${i.total} (${i.percent}%)`,""];if(n.length>0){l.push("### By Section");for(const u of n){const d=u.total>0?Math.round(u.done/u.total*100):0;l.push(`- **${u.title}:** ${u.done}/${u.total} (${d}%)`)}l.push("")}return l.push("---","",a),{content:[{type:"text",text:l.join(`
84
+ `)}]}}if(!c&&!["get_guide","get_guideline","search_framework"].includes(o))return{content:[{type:"text",text:"Error: node_modules not found. Run bun install first."}],isError:!0};if(o==="get_guide"){x(s?.topic,"topic");const t=s.topic,r=ee();if(!r)return{content:[{type:"text",text:"Error: guides directory not found."}],isError:!0};const i=M(r).filter(n=>n.endsWith(".md")||n.endsWith(".md.example"));for(const n of i)if(n.toLowerCase().includes(t.toLowerCase()))return{content:[{type:"text",text:`[GUIDE] ${t} (from ${n}):
74
85
 
75
- ${x(m(r,t),"utf-8")}`}]};return{content:[{type:"text",text:`No guide found for: ${e}`}],isError:!0}}if(a==="get_guideline"){v(n?.topic,"topic");const e=n.topic,[r,s]=e.split(":"),t=[{dir:D,type:"GUIDE"},{dir:$e,type:"ARCHITECTURE"}];for(const i of t){if(!h(i.dir))continue;const c=N(i.dir).filter(p=>p.endsWith(".md")||p.endsWith(".md.example"));for(const p of c)if(r&&p.toLowerCase().includes(r.toLowerCase())){const f=x(m(i.dir,p),"utf-8");if(!s){const g=f.match(/^([\s\S]*?)(?=##|$)/),b=g&&g[1]?g[1].trim():"No overview found.";return{content:[{type:"text",text:`[${i.type}] Overview for ${r}:
86
+ ${$(g(r,n),"utf-8")}`}]};return{content:[{type:"text",text:`No guide found for: ${t}`}],isError:!0}}if(o==="get_guideline"){x(s?.topic,"topic");const t=s.topic,[r,i]=t.split(":"),n=[{dir:B,type:"GUIDE"},{dir:Pe,type:"ARCHITECTURE"}];for(const a of n){if(!h(a.dir))continue;const l=M(a.dir).filter(u=>u.endsWith(".md")||u.endsWith(".md.example"));for(const u of l)if(r&&u.toLowerCase().includes(r.toLowerCase())){const d=$(g(a.dir,u),"utf-8");if(!i){const m=d.match(/^([\s\S]*?)(?=##|$)/),S=m&&m[1]?m[1].trim():"No overview found.";return{content:[{type:"text",text:`[${a.type}] Overview for ${r}:
76
87
 
77
- ${b}`}]}}const u=pe(s),d=f.match(new RegExp(`##\\s+.*${u}.*\\n([\\s\\S]*?)(?=##|$)`,"i"));if(d&&d[1])return{content:[{type:"text",text:`[${i.type}] ${r} > ${s}:
88
+ ${S}`}]}}const f=me(i),p=d.match(new RegExp(`##\\s+.*${f}.*\\n([\\s\\S]*?)(?=##|$)`,"i"));if(p&&p[1])return{content:[{type:"text",text:`[${a.type}] ${r} > ${i}:
78
89
 
79
- ${d[1].trim()}`}]}}}return{content:[{type:"text",text:`No guideline found for: ${e}`}],isError:!0}}if(a==="search_framework"){v(n?.query,"query");const e=n.query.toLowerCase(),r=[],s=Q();if(s){const t=N(s).filter(i=>i.endsWith(".md")||i.endsWith(".md.example"));for(const i of t)x(m(s,i),"utf-8").toLowerCase().includes(e)&&r.push(`[GUIDE] ${i}`)}if(l)for(const t of Z){const i=m(l,t,"dist");if(h(i))for(const c of ne(i)){const p=x(c,"utf-8");if(p.toLowerCase().includes(e)){const f=Ie(p).filter(u=>u.toLowerCase().includes(e));f.length>0&&r.push(`[SYMBOL] ${t}: ${f.join(", ")}`)}}}return{content:[{type:"text",text:r.length>0?`Results:
90
+ ${p[1].trim()}`}]}}}return{content:[{type:"text",text:`No guideline found for: ${t}`}],isError:!0}}if(o==="search_framework"){x(s?.query,"query");const t=s.query.toLowerCase(),r=[],i=ee();if(i){const n=M(i).filter(a=>a.endsWith(".md")||a.endsWith(".md.example"));for(const a of n)$(g(i,a),"utf-8").toLowerCase().includes(t)&&r.push(`[GUIDE] ${a}`)}if(c)for(const n of Q){const a=g(c,n,"dist");if(h(a))for(const l of oe(a)){const u=$(l,"utf-8");if(u.toLowerCase().includes(t)){const d=Xe(u).filter(f=>f.toLowerCase().includes(t));d.length>0&&r.push(`[SYMBOL] ${n}: ${d.join(", ")}`)}}}return{content:[{type:"text",text:r.length>0?`Results:
80
91
  ${r.join(`
81
- `)}`:"No results found."}]}}if(a==="lookup_symbol"){v(n?.symbol,"symbol");const e=n.symbol;if(!l)return{content:[{type:"text",text:"Error: node_modules not found. Run bun install first."}],isError:!0};const r=O();r.lookedUpSymbols.includes(e)||(r.lookedUpSymbols.push(e),G(r));const s=[e,`${e}Props`,`${e.replace(/Props$/,"")}Props`],t=["@donotdev/core","@donotdev/ui","@donotdev/crud"],i=[...t,...Z.filter(c=>!t.includes(c))];for(const c of i){const p=m(l,c,"dist");if(h(p))for(const f of ne(p)){const u=x(f,"utf-8");for(const d of s){const g=Ce(u,d);if(g)return{content:[{type:"text",text:`[TYPE INTELLIGENCE] ${c}:
92
+ `)}`:"No results found."}]}}if(o==="lookup_symbol"){x(s?.symbol,"symbol");const t=s.symbol;if(!c)return{content:[{type:"text",text:"Error: node_modules not found. Run bun install first."}],isError:!0};const r=E();r.lookedUpSymbols.includes(t)||(r.lookedUpSymbols.push(t),R(r));const i=[t,`${t}Props`,`${t.replace(/Props$/,"")}Props`],n=["@donotdev/core","@donotdev/ui","@donotdev/crud"],a=[...n,...Q.filter(l=>!n.includes(l))];for(const l of a){const u=g(c,l,"dist");if(h(u))for(const d of oe(u)){const f=$(d,"utf-8");for(const p of i){const m=Ze(f,p);if(m)return{content:[{type:"text",text:`[TYPE INTELLIGENCE] ${l}:
82
93
 
83
- ${g}`}]}}}}return{content:[{type:"text",text:`Not found: ${e}`}],isError:!0}}if(a==="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
84
- `];for(const r of Te){const s=m(l,r,"README.md"),t=Ne(s);e.push(`- **${r}** \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(`
85
- `)}]}}return{content:[{type:"text",text:`Unknown tool: ${a}`}],isError:!0}});async function Me(){const o=new fe;await oe.connect(o)}Me().catch(o=>{process.exit(1)});
94
+ ${m}`}]}}}}return{content:[{type:"text",text:`Not found: ${t}`}],isError:!0}}if(o==="list_features"){if(!c)return{content:[{type:"text",text:"Error: node_modules not found. Run bun install first."}],isError:!0};const t=[`## Available Framework Features
95
+ `];for(const r of ze){const i=g(c,r,"README.md"),n=Qe(i);t.push(`- **${r}** \u2014 ${n||"(no description available)"}`)}return t.push('\nUse `search_framework("topic")` or `get_guide("TOPIC")` to go deeper on any package.'),{content:[{type:"text",text:t.join(`
96
+ `)}]}}return{content:[{type:"text",text:`Unknown tool: ${o}`}],isError:!0}});async function nt(){const e=new $e;await ne.connect(e)}nt().catch(e=>{process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@donotdev/mcp-server",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "MCP server for DoNotDev component type lookups",
5
5
  "type": "module",
6
6
  "private": false,