glenn-code 1.0.15 → 1.0.16

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.
@@ -1257,7 +1257,7 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
1257
1257
  - **Multi-tab apps**: Use \`browser_tabs\` to manage multiple windows/tabs in legacy applications
1258
1258
  - **Large snapshots**: For pages with huge dropdowns (e.g., country lists), use \`browser_evaluate\` to collapse them first
1259
1259
  - ${e?"In local mode, the browser window will be visible to you":"In container mode, browser runs headless"}
1260
- </ui-verification>`}function pe(n){return n&&n.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var Ee=null;function We(n){Ee=pe(n),console.log(`[ProjectPrompt] Updated (${Ee?Ee.length:0} chars)`)}function jt(){return pe(Ee)}import*as Z from"path";import*as Qe from"fs";import{fileURLToPath as Sn}from"url";var wn=Be(),Ye=Z.dirname(Sn(import.meta.url)),Ot=[Z.resolve(Ye,"../mcps/stdio-server.js"),Z.resolve(Ye,"./adapters/mcps/stdio-server.js")],ue=Ot.find(n=>Qe.existsSync(n))||Ot[0];console.log("[MCP] Stdio server path resolved:",ue);console.log("[MCP] __dirname:",Ye);console.log("[MCP] File exists:",Qe.existsSync(ue));async function Ae(n,e={}){let{model:t,sessionId:o=null,projectId:s=null,apiUrl:r=null,callbacks:i={},debugLog:a,abortController:c,debugMode:l=!1,isLocal:d=process.env.LOCAL_MODE==="true",projectConfig:u=W(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:g=!0}=e,w=At(a),v=new Set,b=o||"",D,j,f,O=!1,q=!1,F=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[ue]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",s||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",F),console.log("[MCP] File exists check:",ue);try{for await(let p of bn({prompt:n,options:{abortController:c,cwd:F,resume:o??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","Skill","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...d?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_fill_form","mcp__playwright__browser_select_option","mcp__playwright__browser_press_key","mcp__playwright__browser_hover","mcp__playwright__browser_drag","mcp__playwright__browser_file_upload","mcp__playwright__browser_handle_dialog","mcp__playwright__browser_close","mcp__playwright__browser_resize","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_screen_capture","mcp__playwright__browser_screen_move_mouse","mcp__playwright__browser_screen_click","mcp__playwright__browser_screen_drag","mcp__playwright__browser_screen_type","mcp__playwright__browser_evaluate","mcp__playwright__browser_run_code","mcp__playwright__browser_tabs","mcp__playwright__browser_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:t,mcpServers:{"agent-insights":wn,"agent-planning":{type:"stdio",command:"node",args:[ue],env:{WORKSPACE_DIR:F,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:s||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${Z.join(F,".playwright-data")}`,...d?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:g?{scaffolding:Ke,debugger:be,planning:Se,backend:qe,frontend:Ge,"project-context":we}:{debugger:be,planning:Se,"project-context":we},systemPrompt:Dt({debugMode:l,projectPrompt:jt(),isLocal:d,projectConfig:u,useDefaultStack:g})}}))if(!(!p.uuid||v.has(p.uuid))){switch(v.add(p.uuid),w?.log(p),i.onRawMessage?.(p),p.type){case"assistant":if(p.message?.content)for(let C of p.message.content)C.type==="text"?i.onAssistantText?.(C.text):C.type==="tool_use"?i.onAssistantToolUse?.(C.name,C.input):C.type==="tool_result"&&i.onAssistantToolResult?.(C.tool_use_id,C.content);break;case"user":i.onUserMessage?.(p);break;case"result":if(p.subtype==="success")D=p.result,j=p.total_cost_usd,i.onResult?.(p.result,p.total_cost_usd);else{let C=`Agent error: ${p.subtype}`;f=C,i.onError?.(C)}q=!0;break;case"system":switch(p.subtype){case"init":b=p.session_id,w?.setSessionId(p.session_id),i.onSystemInit?.(p.session_id);break;case"status":i.onSystemStatus?.(JSON.stringify(p));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":i.onStreamEvent?.(p);break;case"tool_progress":i.onToolProgress?.(p);break;case"auth_status":i.onAuthStatus?.(p);break}if(q)break}}catch(p){p instanceof Error&&p.name==="AbortError"||p instanceof Error&&p.message.includes("aborted by user")?(O=!0,i.onAborted?.()):(f=p instanceof Error?p.message:String(p),i.onError?.(f))}finally{w?.stop()}return console.log("after try"),{sessionId:b,result:D,cost:j,error:f,aborted:O}}import{spawn as Ut,execSync as ee}from"child_process";import*as R from"fs";import*as Mt from"http";import*as V from"path";function xe(n){let{workspaceDir:e,apiPort:t,webPort:o,onBackendError:s,onFrontendError:r,projectConfig:i=W(e)}=n,a=t??i.services?.backend?.port??5338,c=o??i.services?.frontend?.port??5173,l=H(i),d=B(i),u=i.services?.backend?.startCommand??"dotnet run",g=i.services?.backend?.buildCommand??"dotnet build",w=i.services?.backend?.typecheckCommand??"dotnet build --no-restore",v=i.services?.frontend?.startCommand??"npm run dev",b=i.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",D=i.services?.backend?.logFile??"/tmp/api.log",j=i.services?.frontend?.logFile??"/tmp/web.log",f=null,O=null,q=y=>new Promise(m=>{let S=setTimeout(()=>m(!1),3e3);Mt.get(`http://localhost:${y}`,x=>{clearTimeout(S),m(x.statusCode!==void 0&&x.statusCode<500)}).on("error",()=>{clearTimeout(S),m(!1)})}),F=y=>{try{let m=ee(`lsof -ti:${y} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(m){let S=m.split(`
1260
+ </ui-verification>`}function pe(n){return n&&n.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var Ee=null;function We(n){Ee=pe(n),console.log(`[ProjectPrompt] Updated (${Ee?Ee.length:0} chars)`)}function jt(){return pe(Ee)}import*as Z from"path";import*as Qe from"fs";import{fileURLToPath as Sn}from"url";var wn=Be(),Ye=Z.dirname(Sn(import.meta.url)),Ot=[Z.resolve(Ye,"../mcps/stdio-server.js"),Z.resolve(Ye,"./adapters/mcps/stdio-server.js")],ue=Ot.find(n=>Qe.existsSync(n))||Ot[0];console.log("[MCP] Stdio server path resolved:",ue);console.log("[MCP] __dirname:",Ye);console.log("[MCP] File exists:",Qe.existsSync(ue));async function Ae(n,e={}){let{model:t,sessionId:o=null,projectId:s=null,apiUrl:r=null,callbacks:i={},debugLog:a,abortController:c,debugMode:l=!1,isLocal:d=process.env.LOCAL_MODE==="true",projectConfig:u=W(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:g=!0}=e,w=At(a),v=new Set,b=o||"",D,j,f,O=!1,q=!1,F=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[ue]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",s||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",F),console.log("[MCP] File exists check:",ue);try{for await(let p of bn({prompt:n,options:{abortController:c,cwd:F,resume:o??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","Skill","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...d?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__agent-planning__reference_projects_list","mcp__agent-planning__reference_project_tree","mcp__agent-planning__reference_project_read_file","mcp__agent-planning__reference_project_download","mcp__agent-planning__reference_project_update_status","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_fill_form","mcp__playwright__browser_select_option","mcp__playwright__browser_press_key","mcp__playwright__browser_hover","mcp__playwright__browser_drag","mcp__playwright__browser_file_upload","mcp__playwright__browser_handle_dialog","mcp__playwright__browser_close","mcp__playwright__browser_resize","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_screen_capture","mcp__playwright__browser_screen_move_mouse","mcp__playwright__browser_screen_click","mcp__playwright__browser_screen_drag","mcp__playwright__browser_screen_type","mcp__playwright__browser_evaluate","mcp__playwright__browser_run_code","mcp__playwright__browser_tabs","mcp__playwright__browser_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:t,mcpServers:{"agent-insights":wn,"agent-planning":{type:"stdio",command:"node",args:[ue],env:{WORKSPACE_DIR:F,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:s||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${Z.join(F,".playwright-data")}`,...d?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:g?{scaffolding:Ke,debugger:be,planning:Se,backend:qe,frontend:Ge,"project-context":we}:{debugger:be,planning:Se,"project-context":we},systemPrompt:Dt({debugMode:l,projectPrompt:jt(),isLocal:d,projectConfig:u,useDefaultStack:g})}}))if(!(!p.uuid||v.has(p.uuid))){switch(v.add(p.uuid),w?.log(p),i.onRawMessage?.(p),p.type){case"assistant":if(p.message?.content)for(let C of p.message.content)C.type==="text"?i.onAssistantText?.(C.text):C.type==="tool_use"?i.onAssistantToolUse?.(C.name,C.input):C.type==="tool_result"&&i.onAssistantToolResult?.(C.tool_use_id,C.content);break;case"user":i.onUserMessage?.(p);break;case"result":if(p.subtype==="success")D=p.result,j=p.total_cost_usd,i.onResult?.(p.result,p.total_cost_usd);else{let C=`Agent error: ${p.subtype}`;f=C,i.onError?.(C)}q=!0;break;case"system":switch(p.subtype){case"init":b=p.session_id,w?.setSessionId(p.session_id),i.onSystemInit?.(p.session_id);break;case"status":i.onSystemStatus?.(JSON.stringify(p));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":i.onStreamEvent?.(p);break;case"tool_progress":i.onToolProgress?.(p);break;case"auth_status":i.onAuthStatus?.(p);break}if(q)break}}catch(p){p instanceof Error&&p.name==="AbortError"||p instanceof Error&&p.message.includes("aborted by user")?(O=!0,i.onAborted?.()):(f=p instanceof Error?p.message:String(p),i.onError?.(f))}finally{w?.stop()}return console.log("after try"),{sessionId:b,result:D,cost:j,error:f,aborted:O}}import{spawn as Ut,execSync as ee}from"child_process";import*as R from"fs";import*as Mt from"http";import*as V from"path";function xe(n){let{workspaceDir:e,apiPort:t,webPort:o,onBackendError:s,onFrontendError:r,projectConfig:i=W(e)}=n,a=t??i.services?.backend?.port??5338,c=o??i.services?.frontend?.port??5173,l=H(i),d=B(i),u=i.services?.backend?.startCommand??"dotnet run",g=i.services?.backend?.buildCommand??"dotnet build",w=i.services?.backend?.typecheckCommand??"dotnet build --no-restore",v=i.services?.frontend?.startCommand??"npm run dev",b=i.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",D=i.services?.backend?.logFile??"/tmp/api.log",j=i.services?.frontend?.logFile??"/tmp/web.log",f=null,O=null,q=y=>new Promise(m=>{let S=setTimeout(()=>m(!1),3e3);Mt.get(`http://localhost:${y}`,x=>{clearTimeout(S),m(x.statusCode!==void 0&&x.statusCode<500)}).on("error",()=>{clearTimeout(S),m(!1)})}),F=y=>{try{let m=ee(`lsof -ti:${y} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(m){let S=m.split(`
1261
1261
  `).filter(Boolean);console.log(`[Services] Killing ${S.length} process(es) on port ${y}: ${S.join(", ")}`);for(let _ of S)try{process.kill(parseInt(_,10),"SIGKILL")}catch{}}}catch{try{ee(`fuser -k ${y}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},p=(y,m)=>new Promise(S=>{if(!y||!y.pid){S();return}console.log(`[Services] Stopping ${m} (PID: ${y.pid})...`);try{process.kill(-y.pid,"SIGTERM")}catch{try{y.kill("SIGTERM")}catch{}}setTimeout(S,500)}),C=async()=>{if(!l||!R.existsSync(l)){console.log("[Services] No backend found, skipping backend start");return}F(a),console.log(`[Services] Starting backend with: ${u}...`);let y=V.dirname(D);R.existsSync(y)||R.mkdirSync(y,{recursive:!0});let m=R.createWriteStream(D,{flags:"a"}),[S,..._]=u.split(" ");f=Ut(S,_,{cwd:l,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let x="",E=P=>{let Q=P.toString();m.write(Q);let L=(x+Q).split(`
1262
1262
  `);x=L.pop()||"";for(let $ of L)$&&vn($)&&s&&s($)};f.stdout?.on("data",E),f.stderr?.on("data",E),f.on("exit",P=>{m.end(),P!==0&&P!==null&&s&&s(`Process exited with code ${P}`)}),f.unref()},de=async()=>{if(!d||!R.existsSync(V.join(d,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}F(c),console.log(`[Services] Starting frontend with: ${v}...`);let y=V.dirname(j);R.existsSync(y)||R.mkdirSync(y,{recursive:!0});let[m,...S]=v.split(" ");O=Ut(m,S,{cwd:d,stdio:["ignore",R.openSync(j,"w"),R.openSync(j,"w")],detached:!0,shell:!0}),O.unref()},ft=async()=>{await p(f,"backend"),f=null,F(a)},mt=async()=>{await p(O,"frontend"),O=null,F(c)},$e=async()=>{let[y,m]=await Promise.all([q(a),q(c)]);return{api:y,web:m}},ht=async(y=15e3)=>{let m=Date.now(),S=1e3,_=0,x=null,E=null;for(console.log(`[Services] Waiting for health (timeout: ${y}ms)...`);Date.now()-m<y;){_++;let L=await $e(),$=Date.now()-m;if(L.web&&E===null&&(E=$,console.log(`[Services] \u2713 Frontend (Vite) ready after ${$}ms`)),L.api&&x===null&&(x=$,console.log(`[Services] \u2713 Backend (dotnet) ready after ${$}ms`)),console.log(`[Services] Health poll #${_} (${$}ms): api=${L.api}, web=${L.web}`),L.api&&L.web)return console.log(`[Services] \u2713 Both services healthy after ${$}ms (${_} polls)`),L;await new Promise(Vt=>setTimeout(Vt,S))}let P=await $e(),Q=Date.now()-m;return P.web&&E===null&&console.log(`[Services] \u2713 Frontend (Vite) ready after ${Q}ms`),P.api&&x===null&&console.log(`[Services] \u2713 Backend (dotnet) ready after ${Q}ms`),console.log(`[Services] \u26A0 Health timeout after ${Q}ms: api=${P.api}, web=${P.web}`),P},zt=async y=>{console.log(`[Services] Restarting ${y}...`);let m={success:!0},S=y==="backend"||y==="both",_=y==="frontend"||y==="both";if(S&&await ft(),_&&await mt(),S&&l&&R.existsSync(l)){console.log(`[Services] Building backend with: ${g}...`);try{ee(g,{cwd:l,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),console.log("[Services] \u2713 Backend build succeeded")}catch(E){let P=E;m.success=!1,m.backendError=P.stderr||P.stdout||"Build failed",console.error("[Services] \u2717 Backend build failed")}}if(_&&d&&R.existsSync(V.join(d,"package.json"))){console.log(`[Services] Type-checking frontend with: ${b}...`);try{ee(b,{cwd:d,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),console.log("[Services] \u2713 Frontend types OK")}catch(E){let P=E;m.success=!1,m.frontendError=P.stderr||P.stdout||"Type check failed",console.error("[Services] \u2717 Frontend type check failed")}}console.log(`[Services] Starting ${y}...`),S&&await C(),_&&await de();let x=await ht(1e4);if(S&&!x.api){m.success=!1;let E=await St("backend",20),P=E.length>0?`
1263
1263
  Recent logs:
package/dist/cli.js CHANGED
@@ -1257,7 +1257,7 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
1257
1257
  - **Multi-tab apps**: Use \`browser_tabs\` to manage multiple windows/tabs in legacy applications
1258
1258
  - **Large snapshots**: For pages with huge dropdowns (e.g., country lists), use \`browser_evaluate\` to collapse them first
1259
1259
  - ${e?"In local mode, the browser window will be visible to you":"In container mode, browser runs headless"}
1260
- </ui-verification>`}function pe(n){return n&&n.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var Ee=null;function We(n){Ee=pe(n),console.log(`[ProjectPrompt] Updated (${Ee?Ee.length:0} chars)`)}function jt(){return pe(Ee)}import*as Z from"path";import*as Qe from"fs";import{fileURLToPath as Sn}from"url";var wn=Be(),Ye=Z.dirname(Sn(import.meta.url)),Ot=[Z.resolve(Ye,"../mcps/stdio-server.js"),Z.resolve(Ye,"./adapters/mcps/stdio-server.js")],ue=Ot.find(n=>Qe.existsSync(n))||Ot[0];console.log("[MCP] Stdio server path resolved:",ue);console.log("[MCP] __dirname:",Ye);console.log("[MCP] File exists:",Qe.existsSync(ue));async function Ae(n,e={}){let{model:t,sessionId:o=null,projectId:s=null,apiUrl:r=null,callbacks:i={},debugLog:a,abortController:c,debugMode:l=!1,isLocal:d=process.env.LOCAL_MODE==="true",projectConfig:u=W(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:g=!0}=e,w=At(a),v=new Set,b=o||"",D,j,f,O=!1,q=!1,F=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[ue]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",s||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",F),console.log("[MCP] File exists check:",ue);try{for await(let p of bn({prompt:n,options:{abortController:c,cwd:F,resume:o??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","Skill","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...d?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_fill_form","mcp__playwright__browser_select_option","mcp__playwright__browser_press_key","mcp__playwright__browser_hover","mcp__playwright__browser_drag","mcp__playwright__browser_file_upload","mcp__playwright__browser_handle_dialog","mcp__playwright__browser_close","mcp__playwright__browser_resize","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_screen_capture","mcp__playwright__browser_screen_move_mouse","mcp__playwright__browser_screen_click","mcp__playwright__browser_screen_drag","mcp__playwright__browser_screen_type","mcp__playwright__browser_evaluate","mcp__playwright__browser_run_code","mcp__playwright__browser_tabs","mcp__playwright__browser_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:t,mcpServers:{"agent-insights":wn,"agent-planning":{type:"stdio",command:"node",args:[ue],env:{WORKSPACE_DIR:F,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:s||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${Z.join(F,".playwright-data")}`,...d?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:g?{scaffolding:Ke,debugger:be,planning:Se,backend:qe,frontend:Ge,"project-context":we}:{debugger:be,planning:Se,"project-context":we},systemPrompt:Dt({debugMode:l,projectPrompt:jt(),isLocal:d,projectConfig:u,useDefaultStack:g})}}))if(!(!p.uuid||v.has(p.uuid))){switch(v.add(p.uuid),w?.log(p),i.onRawMessage?.(p),p.type){case"assistant":if(p.message?.content)for(let C of p.message.content)C.type==="text"?i.onAssistantText?.(C.text):C.type==="tool_use"?i.onAssistantToolUse?.(C.name,C.input):C.type==="tool_result"&&i.onAssistantToolResult?.(C.tool_use_id,C.content);break;case"user":i.onUserMessage?.(p);break;case"result":if(p.subtype==="success")D=p.result,j=p.total_cost_usd,i.onResult?.(p.result,p.total_cost_usd);else{let C=`Agent error: ${p.subtype}`;f=C,i.onError?.(C)}q=!0;break;case"system":switch(p.subtype){case"init":b=p.session_id,w?.setSessionId(p.session_id),i.onSystemInit?.(p.session_id);break;case"status":i.onSystemStatus?.(JSON.stringify(p));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":i.onStreamEvent?.(p);break;case"tool_progress":i.onToolProgress?.(p);break;case"auth_status":i.onAuthStatus?.(p);break}if(q)break}}catch(p){p instanceof Error&&p.name==="AbortError"||p instanceof Error&&p.message.includes("aborted by user")?(O=!0,i.onAborted?.()):(f=p instanceof Error?p.message:String(p),i.onError?.(f))}finally{w?.stop()}return console.log("after try"),{sessionId:b,result:D,cost:j,error:f,aborted:O}}import{spawn as Ut,execSync as ee}from"child_process";import*as R from"fs";import*as Mt from"http";import*as V from"path";function xe(n){let{workspaceDir:e,apiPort:t,webPort:o,onBackendError:s,onFrontendError:r,projectConfig:i=W(e)}=n,a=t??i.services?.backend?.port??5338,c=o??i.services?.frontend?.port??5173,l=H(i),d=B(i),u=i.services?.backend?.startCommand??"dotnet run",g=i.services?.backend?.buildCommand??"dotnet build",w=i.services?.backend?.typecheckCommand??"dotnet build --no-restore",v=i.services?.frontend?.startCommand??"npm run dev",b=i.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",D=i.services?.backend?.logFile??"/tmp/api.log",j=i.services?.frontend?.logFile??"/tmp/web.log",f=null,O=null,q=y=>new Promise(m=>{let S=setTimeout(()=>m(!1),3e3);Mt.get(`http://localhost:${y}`,x=>{clearTimeout(S),m(x.statusCode!==void 0&&x.statusCode<500)}).on("error",()=>{clearTimeout(S),m(!1)})}),F=y=>{try{let m=ee(`lsof -ti:${y} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(m){let S=m.split(`
1260
+ </ui-verification>`}function pe(n){return n&&n.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var Ee=null;function We(n){Ee=pe(n),console.log(`[ProjectPrompt] Updated (${Ee?Ee.length:0} chars)`)}function jt(){return pe(Ee)}import*as Z from"path";import*as Qe from"fs";import{fileURLToPath as Sn}from"url";var wn=Be(),Ye=Z.dirname(Sn(import.meta.url)),Ot=[Z.resolve(Ye,"../mcps/stdio-server.js"),Z.resolve(Ye,"./adapters/mcps/stdio-server.js")],ue=Ot.find(n=>Qe.existsSync(n))||Ot[0];console.log("[MCP] Stdio server path resolved:",ue);console.log("[MCP] __dirname:",Ye);console.log("[MCP] File exists:",Qe.existsSync(ue));async function Ae(n,e={}){let{model:t,sessionId:o=null,projectId:s=null,apiUrl:r=null,callbacks:i={},debugLog:a,abortController:c,debugMode:l=!1,isLocal:d=process.env.LOCAL_MODE==="true",projectConfig:u=W(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:g=!0}=e,w=At(a),v=new Set,b=o||"",D,j,f,O=!1,q=!1,F=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[ue]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",s||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",F),console.log("[MCP] File exists check:",ue);try{for await(let p of bn({prompt:n,options:{abortController:c,cwd:F,resume:o??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","Skill","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...d?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__agent-planning__reference_projects_list","mcp__agent-planning__reference_project_tree","mcp__agent-planning__reference_project_read_file","mcp__agent-planning__reference_project_download","mcp__agent-planning__reference_project_update_status","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_fill_form","mcp__playwright__browser_select_option","mcp__playwright__browser_press_key","mcp__playwright__browser_hover","mcp__playwright__browser_drag","mcp__playwright__browser_file_upload","mcp__playwright__browser_handle_dialog","mcp__playwright__browser_close","mcp__playwright__browser_resize","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_screen_capture","mcp__playwright__browser_screen_move_mouse","mcp__playwright__browser_screen_click","mcp__playwright__browser_screen_drag","mcp__playwright__browser_screen_type","mcp__playwright__browser_evaluate","mcp__playwright__browser_run_code","mcp__playwright__browser_tabs","mcp__playwright__browser_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:t,mcpServers:{"agent-insights":wn,"agent-planning":{type:"stdio",command:"node",args:[ue],env:{WORKSPACE_DIR:F,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:s||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${Z.join(F,".playwright-data")}`,...d?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:g?{scaffolding:Ke,debugger:be,planning:Se,backend:qe,frontend:Ge,"project-context":we}:{debugger:be,planning:Se,"project-context":we},systemPrompt:Dt({debugMode:l,projectPrompt:jt(),isLocal:d,projectConfig:u,useDefaultStack:g})}}))if(!(!p.uuid||v.has(p.uuid))){switch(v.add(p.uuid),w?.log(p),i.onRawMessage?.(p),p.type){case"assistant":if(p.message?.content)for(let C of p.message.content)C.type==="text"?i.onAssistantText?.(C.text):C.type==="tool_use"?i.onAssistantToolUse?.(C.name,C.input):C.type==="tool_result"&&i.onAssistantToolResult?.(C.tool_use_id,C.content);break;case"user":i.onUserMessage?.(p);break;case"result":if(p.subtype==="success")D=p.result,j=p.total_cost_usd,i.onResult?.(p.result,p.total_cost_usd);else{let C=`Agent error: ${p.subtype}`;f=C,i.onError?.(C)}q=!0;break;case"system":switch(p.subtype){case"init":b=p.session_id,w?.setSessionId(p.session_id),i.onSystemInit?.(p.session_id);break;case"status":i.onSystemStatus?.(JSON.stringify(p));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":i.onStreamEvent?.(p);break;case"tool_progress":i.onToolProgress?.(p);break;case"auth_status":i.onAuthStatus?.(p);break}if(q)break}}catch(p){p instanceof Error&&p.name==="AbortError"||p instanceof Error&&p.message.includes("aborted by user")?(O=!0,i.onAborted?.()):(f=p instanceof Error?p.message:String(p),i.onError?.(f))}finally{w?.stop()}return console.log("after try"),{sessionId:b,result:D,cost:j,error:f,aborted:O}}import{spawn as Ut,execSync as ee}from"child_process";import*as R from"fs";import*as Mt from"http";import*as V from"path";function xe(n){let{workspaceDir:e,apiPort:t,webPort:o,onBackendError:s,onFrontendError:r,projectConfig:i=W(e)}=n,a=t??i.services?.backend?.port??5338,c=o??i.services?.frontend?.port??5173,l=H(i),d=B(i),u=i.services?.backend?.startCommand??"dotnet run",g=i.services?.backend?.buildCommand??"dotnet build",w=i.services?.backend?.typecheckCommand??"dotnet build --no-restore",v=i.services?.frontend?.startCommand??"npm run dev",b=i.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",D=i.services?.backend?.logFile??"/tmp/api.log",j=i.services?.frontend?.logFile??"/tmp/web.log",f=null,O=null,q=y=>new Promise(m=>{let S=setTimeout(()=>m(!1),3e3);Mt.get(`http://localhost:${y}`,x=>{clearTimeout(S),m(x.statusCode!==void 0&&x.statusCode<500)}).on("error",()=>{clearTimeout(S),m(!1)})}),F=y=>{try{let m=ee(`lsof -ti:${y} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(m){let S=m.split(`
1261
1261
  `).filter(Boolean);console.log(`[Services] Killing ${S.length} process(es) on port ${y}: ${S.join(", ")}`);for(let _ of S)try{process.kill(parseInt(_,10),"SIGKILL")}catch{}}}catch{try{ee(`fuser -k ${y}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},p=(y,m)=>new Promise(S=>{if(!y||!y.pid){S();return}console.log(`[Services] Stopping ${m} (PID: ${y.pid})...`);try{process.kill(-y.pid,"SIGTERM")}catch{try{y.kill("SIGTERM")}catch{}}setTimeout(S,500)}),C=async()=>{if(!l||!R.existsSync(l)){console.log("[Services] No backend found, skipping backend start");return}F(a),console.log(`[Services] Starting backend with: ${u}...`);let y=V.dirname(D);R.existsSync(y)||R.mkdirSync(y,{recursive:!0});let m=R.createWriteStream(D,{flags:"a"}),[S,..._]=u.split(" ");f=Ut(S,_,{cwd:l,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let x="",E=P=>{let Q=P.toString();m.write(Q);let L=(x+Q).split(`
1262
1262
  `);x=L.pop()||"";for(let $ of L)$&&vn($)&&s&&s($)};f.stdout?.on("data",E),f.stderr?.on("data",E),f.on("exit",P=>{m.end(),P!==0&&P!==null&&s&&s(`Process exited with code ${P}`)}),f.unref()},de=async()=>{if(!d||!R.existsSync(V.join(d,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}F(c),console.log(`[Services] Starting frontend with: ${v}...`);let y=V.dirname(j);R.existsSync(y)||R.mkdirSync(y,{recursive:!0});let[m,...S]=v.split(" ");O=Ut(m,S,{cwd:d,stdio:["ignore",R.openSync(j,"w"),R.openSync(j,"w")],detached:!0,shell:!0}),O.unref()},ft=async()=>{await p(f,"backend"),f=null,F(a)},mt=async()=>{await p(O,"frontend"),O=null,F(c)},$e=async()=>{let[y,m]=await Promise.all([q(a),q(c)]);return{api:y,web:m}},ht=async(y=15e3)=>{let m=Date.now(),S=1e3,_=0,x=null,E=null;for(console.log(`[Services] Waiting for health (timeout: ${y}ms)...`);Date.now()-m<y;){_++;let L=await $e(),$=Date.now()-m;if(L.web&&E===null&&(E=$,console.log(`[Services] \u2713 Frontend (Vite) ready after ${$}ms`)),L.api&&x===null&&(x=$,console.log(`[Services] \u2713 Backend (dotnet) ready after ${$}ms`)),console.log(`[Services] Health poll #${_} (${$}ms): api=${L.api}, web=${L.web}`),L.api&&L.web)return console.log(`[Services] \u2713 Both services healthy after ${$}ms (${_} polls)`),L;await new Promise(Vt=>setTimeout(Vt,S))}let P=await $e(),Q=Date.now()-m;return P.web&&E===null&&console.log(`[Services] \u2713 Frontend (Vite) ready after ${Q}ms`),P.api&&x===null&&console.log(`[Services] \u2713 Backend (dotnet) ready after ${Q}ms`),console.log(`[Services] \u26A0 Health timeout after ${Q}ms: api=${P.api}, web=${P.web}`),P},zt=async y=>{console.log(`[Services] Restarting ${y}...`);let m={success:!0},S=y==="backend"||y==="both",_=y==="frontend"||y==="both";if(S&&await ft(),_&&await mt(),S&&l&&R.existsSync(l)){console.log(`[Services] Building backend with: ${g}...`);try{ee(g,{cwd:l,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),console.log("[Services] \u2713 Backend build succeeded")}catch(E){let P=E;m.success=!1,m.backendError=P.stderr||P.stdout||"Build failed",console.error("[Services] \u2717 Backend build failed")}}if(_&&d&&R.existsSync(V.join(d,"package.json"))){console.log(`[Services] Type-checking frontend with: ${b}...`);try{ee(b,{cwd:d,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),console.log("[Services] \u2713 Frontend types OK")}catch(E){let P=E;m.success=!1,m.frontendError=P.stderr||P.stdout||"Type check failed",console.error("[Services] \u2717 Frontend type check failed")}}console.log(`[Services] Starting ${y}...`),S&&await C(),_&&await de();let x=await ht(1e4);if(S&&!x.api){m.success=!1;let E=await St("backend",20),P=E.length>0?`
1263
1263
  Recent logs:
package/dist/core.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Glenn Code - Bundled and minified
2
2
  // (c) DNM Lab - All rights reserved
3
3
 
4
- import{createSdkMcpServer as pt}from"@anthropic-ai/claude-agent-sdk";import{tool as oe}from"@anthropic-ai/claude-agent-sdk";import{z as se}from"zod";import*as re from"fs";import*as De from"path";var D=null;function Oe(e){D=e}function me(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}var It=oe("save_specification",`Save a specification to the database.
4
+ import{createSdkMcpServer as pt}from"@anthropic-ai/claude-agent-sdk";import{tool as se}from"@anthropic-ai/claude-agent-sdk";import{z as oe}from"zod";import*as re from"fs";import*as De from"path";var D=null;function Oe(e){D=e}function me(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}var It=se("save_specification",`Save a specification to the database.
5
5
 
6
6
  IMPORTANT: First write the specification content to a file using the Write tool, then call this with the file path.
7
7
 
@@ -12,30 +12,30 @@ Workflow:
12
12
 
13
13
  Example:
14
14
  1. Write tool \u2192 save to /tmp/user-auth-spec.md
15
- 2. save_specification(name: "User Authentication", filePath: "/tmp/user-auth-spec.md")`,{name:se.string().describe("Specification name (e.g., 'User Authentication')"),filePath:se.string().describe("Path to the file containing the specification content (written via Write tool)")},async e=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=De.resolve(e.filePath);if(!re.existsSync(t))return{content:[{type:"text",text:`\u274C File not found: ${e.filePath}. Use Write tool first to create the file.`}]};let n=re.readFileSync(t,"utf-8"),a=me(e.name);return await D.saveSpecification(a,n),{content:[{type:"text",text:`\u2705 Saved specification: ${a}.spec.md`}]}}),Dt=oe("read_specification","Read the content of a specification.",{name:se.string().describe("Name of the specification to read")},async e=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=me(e.name),n=await D.getSpecification(t);return n?{content:[{type:"text",text:n}]}:{content:[{type:"text",text:`\u274C Specification "${e.name}" not found.`}]}}),Ot=oe("list_specifications","List all specifications.",{},async()=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let e=await D.listSpecifications();return e.length===0?{content:[{type:"text",text:"\u{1F4CB} No specifications found."}]}:{content:[{type:"text",text:`\u{1F4CB} Specifications:
15
+ 2. save_specification(name: "User Authentication", filePath: "/tmp/user-auth-spec.md")`,{name:oe.string().describe("Specification name (e.g., 'User Authentication')"),filePath:oe.string().describe("Path to the file containing the specification content (written via Write tool)")},async e=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=De.resolve(e.filePath);if(!re.existsSync(t))return{content:[{type:"text",text:`\u274C File not found: ${e.filePath}. Use Write tool first to create the file.`}]};let n=re.readFileSync(t,"utf-8"),a=me(e.name);return await D.saveSpecification(a,n),{content:[{type:"text",text:`\u2705 Saved specification: ${a}.spec.md`}]}}),Dt=se("read_specification","Read the content of a specification.",{name:oe.string().describe("Name of the specification to read")},async e=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=me(e.name),n=await D.getSpecification(t);return n?{content:[{type:"text",text:n}]}:{content:[{type:"text",text:`\u274C Specification "${e.name}" not found.`}]}}),Ot=se("list_specifications","List all specifications.",{},async()=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let e=await D.listSpecifications();return e.length===0?{content:[{type:"text",text:"\u{1F4CB} No specifications found."}]}:{content:[{type:"text",text:`\u{1F4CB} Specifications:
16
16
 
17
17
  ${e.map(n=>`- ${n.name} (${n.slug}.spec.md)`).join(`
18
- `)}`}]}}),Ft=oe("delete_specification","Delete a specification.",{name:se.string().describe("Name of the specification to delete")},async e=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=me(e.name);return await D.deleteSpecification(t)?{content:[{type:"text",text:`\u{1F5D1}\uFE0F Deleted: ${t}.spec.md`}]}:{content:[{type:"text",text:`\u274C Specification "${e.name}" not found.`}]}});import{tool as M}from"@anthropic-ai/claude-agent-sdk";import{z as Q}from"zod";var y=null;function Fe(e){y=e}var Ne=M("restart_services",`Rebuild and restart services. Call this after making backend changes (.cs files) to apply them.
18
+ `)}`}]}}),Ft=se("delete_specification","Delete a specification.",{name:oe.string().describe("Name of the specification to delete")},async e=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=me(e.name);return await D.deleteSpecification(t)?{content:[{type:"text",text:`\u{1F5D1}\uFE0F Deleted: ${t}.spec.md`}]}:{content:[{type:"text",text:`\u274C Specification "${e.name}" not found.`}]}});import{tool as M}from"@anthropic-ai/claude-agent-sdk";import{z as Q}from"zod";var y=null;function Fe(e){y=e}var Ne=M("restart_services",`Rebuild and restart services. Call this after making backend changes (.cs files) to apply them.
19
19
 
20
20
  Parameters:
21
21
  - target: "backend" (default) | "frontend" | "both"
22
22
 
23
23
  Use target="backend" after .cs file changes (faster, only restarts .NET).
24
24
  Use target="frontend" if Vite is stuck (rare, HMR usually works).
25
- Use target="both" if unsure or both need restart.`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to restart")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] restart_services called (target: ${t})`);let n=await y.restartServices(t);if(!n.success){let s=[];return n.backendError&&s.push(`Backend: ${n.backendError}`),n.frontendError&&s.push(`Frontend: ${n.frontendError}`),{content:[{type:"text",text:`\u274C Restart failed:
26
- ${s.join(`
25
+ Use target="both" if unsure or both need restart.`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to restart")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] restart_services called (target: ${t})`);let n=await y.restartServices(t);if(!n.success){let o=[];return n.backendError&&o.push(`Backend: ${n.backendError}`),n.frontendError&&o.push(`Frontend: ${n.frontendError}`),{content:[{type:"text",text:`\u274C Restart failed:
26
+ ${o.join(`
27
27
 
28
28
  `)}`}]}}return{content:[{type:"text",text:`\u2705 ${t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend"} restarted successfully.`}]}}),Ue=M("stop_services",`Stop running services. Call this BEFORE running scaffold to prevent port conflicts and ensure clean migrations.
29
29
 
30
30
  Parameters:
31
31
  - target: "backend" (default) | "frontend" | "both"
32
32
 
33
- Use target="backend" before running scaffold (required for migrations).`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] stop_services called (target: ${t})`);let n=t==="backend"||t==="both",a=t==="frontend"||t==="both";n&&await y.stopBackend(),a&&await y.stopFrontend();let s=5e3,r=Date.now(),o=!n,f=!a;for(;Date.now()-r<s;){let u=await y.checkHealth();if(n&&!u.api&&(o=!0),a&&!u.web&&(f=!0),o&&f)break;await new Promise(T=>setTimeout(T,500))}let _=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";return{content:[{type:"text",text:o&&f?`\u2705 ${_} stopped successfully.`:`\u26A0\uFE0F ${_} stop requested but health check still shows running. Proceeding anyway.`}]}}),Le=M("start_services",`Start services. Call this AFTER running scaffold to bring services back up.
33
+ Use target="backend" before running scaffold (required for migrations).`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] stop_services called (target: ${t})`);let n=t==="backend"||t==="both",a=t==="frontend"||t==="both";n&&await y.stopBackend(),a&&await y.stopFrontend();let o=5e3,r=Date.now(),s=!n,f=!a;for(;Date.now()-r<o;){let u=await y.checkHealth();if(n&&!u.api&&(s=!0),a&&!u.web&&(f=!0),s&&f)break;await new Promise(T=>setTimeout(T,500))}let _=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";return{content:[{type:"text",text:s&&f?`\u2705 ${_} stopped successfully.`:`\u26A0\uFE0F ${_} stop requested but health check still shows running. Proceeding anyway.`}]}}),Le=M("start_services",`Start services. Call this AFTER running scaffold to bring services back up.
34
34
 
35
35
  Parameters:
36
36
  - target: "backend" (default) | "frontend" | "both"
37
37
 
38
- Use target="backend" after running scaffold.`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to start")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] start_services called (target: ${t})`);let n=t==="backend"||t==="both",a=t==="frontend"||t==="both";n&&await y.startBackend(),a&&await y.startFrontend();let s=await y.waitForHealth(15e3),r=!n||s.api,o=!a||s.web,f=r&&o,_=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";if(!f){let g=[];return n&&!s.api&&g.push("Backend failed to start"),a&&!s.web&&g.push("Frontend failed to start"),{content:[{type:"text",text:`\u274C ${_} failed to start: ${g.join(", ")}`}]}}return{content:[{type:"text",text:`\u2705 ${_} started successfully.`}]}}),Me=M("check_service_health",`Check if services are running and healthy. Returns current status of backend API and frontend.
38
+ Use target="backend" after running scaffold.`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to start")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] start_services called (target: ${t})`);let n=t==="backend"||t==="both",a=t==="frontend"||t==="both";n&&await y.startBackend(),a&&await y.startFrontend();let o=await y.waitForHealth(15e3),r=!n||o.api,s=!a||o.web,f=r&&s,_=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";if(!f){let g=[];return n&&!o.api&&g.push("Backend failed to start"),a&&!o.web&&g.push("Frontend failed to start"),{content:[{type:"text",text:`\u274C ${_} failed to start: ${g.join(", ")}`}]}}return{content:[{type:"text",text:`\u2705 ${_} started successfully.`}]}}),Me=M("check_service_health",`Check if services are running and healthy. Returns current status of backend API and frontend.
39
39
  Note: This only checks if ports are responding, NOT build/type errors. Use check_backend_build or check_frontend_types for that.`,{},async()=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=await y.checkHealth();return{content:[{type:"text",text:`Service Health:
40
40
  \u{1F5A5}\uFE0F Backend API: ${e.api?"\u2705 running":"\u274C not running"}
41
41
  \u{1F310} Frontend: ${e.web?"\u2705 running":"\u274C not running"}`}]}}),je=M("check_backend_build","Run dotnet build to check for C# compilation errors. Use this after fixing backend code to verify the fix works.",{},async()=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};console.log("[MCP] check_backend_build called");let e=await y.checkBackendBuild();return e.success?{content:[{type:"text",text:"\u2705 Backend build succeeded - no errors"}]}:{content:[{type:"text",text:`\u274C Backend build failed:
@@ -55,9 +55,9 @@ Parameters:
55
55
  - pattern: string (e.g., "users", "api/reports")`,{pattern:Q.string().describe("Text to search for in endpoint paths")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=await y.checkSwaggerEndpoints(e.pattern);return t.foundEndpoints.length===0?{content:[{type:"text",text:`\u274C No endpoints found matching "${e.pattern}" (Total endpoints: ${t.totalEndpoints})`}]}:{content:[{type:"text",text:`\u2705 Found ${t.foundEndpoints.length} matching endpoints:
56
56
 
57
57
  ${t.foundEndpoints.join(`
58
- `)}`}]}});import*as P from"fs";import*as V from"path";var $e="/tmp";function he(e){let t=e?V.join(e,".sdd","insights"):V.join(process.cwd(),".sdd","insights");return{async reportInsight(n,a){P.mkdirSync(t,{recursive:!0});let s=a.type==="learning"?"learnings.jsonl":"bugs.jsonl",r=V.join(t,s);P.appendFileSync(r,JSON.stringify(a)+`
59
- `,"utf-8")},async reportProgress(n,a,s){let r=V.join($e,`agent-progress-${n}.json`),o={timestamp:new Date().toISOString(),event:a,data:s};P.appendFileSync(r,JSON.stringify(o)+`
60
- `,"utf-8")}}}function ye(e){let t=V.join($e,`agent-questions-${e}.json`);if(!P.existsSync(t))return null;try{let n=P.readFileSync(t,"utf-8");return P.unlinkSync(t),JSON.parse(n)}catch{return null}}import*as w from"fs";import*as ae from"path";function be(e){let t=ae.join(e,".sdd","specifications");function n(){w.existsSync(t)||w.mkdirSync(t,{recursive:!0})}function a(s){return ae.join(t,`${s}.spec.md`)}return{async saveSpecification(s,r){n(),w.writeFileSync(a(s),r,"utf-8")},async getSpecification(s){n();let r=a(s);return w.existsSync(r)?w.readFileSync(r,"utf-8"):null},async listSpecifications(){return n(),w.readdirSync(t).filter(r=>r.endsWith(".spec.md")).map(r=>{let o=w.readFileSync(ae.join(t,r),"utf-8"),f=o.match(/name: "([^"]+)"/),_=o.match(/status: (\w+)/),g=o.match(/version: "([^"]+)"/);return{slug:r.replace(".spec.md",""),name:f?.[1]||r,status:_?.[1]||"unknown",version:g?.[1]||"1.0.0"}})},async deleteSpecification(s){n();let r=a(s);return w.existsSync(r)?(w.unlinkSync(r),!0):!1},async specificationExists(s){return n(),w.existsSync(a(s))}}}function _e(){return pt({name:"agent-insights",version:"1.0.0",tools:[Ne,Ue,Le,Me,je,Be]})}import{query as Ct}from"@anthropic-ai/claude-agent-sdk";var ke={description:"Delegate to this agent when scaffolding new entities or creating CRUD features. This agent handles entity generation using the scaffold CLI with multi-entity support.",model:"inherit",prompt:`You are a scaffolding specialist. Your ONLY job is:
58
+ `)}`}]}});import*as P from"fs";import*as V from"path";var $e="/tmp";function he(e){let t=e?V.join(e,".sdd","insights"):V.join(process.cwd(),".sdd","insights");return{async reportInsight(n,a){P.mkdirSync(t,{recursive:!0});let o=a.type==="learning"?"learnings.jsonl":"bugs.jsonl",r=V.join(t,o);P.appendFileSync(r,JSON.stringify(a)+`
59
+ `,"utf-8")},async reportProgress(n,a,o){let r=V.join($e,`agent-progress-${n}.json`),s={timestamp:new Date().toISOString(),event:a,data:o};P.appendFileSync(r,JSON.stringify(s)+`
60
+ `,"utf-8")}}}function ye(e){let t=V.join($e,`agent-questions-${e}.json`);if(!P.existsSync(t))return null;try{let n=P.readFileSync(t,"utf-8");return P.unlinkSync(t),JSON.parse(n)}catch{return null}}import*as w from"fs";import*as ae from"path";function be(e){let t=ae.join(e,".sdd","specifications");function n(){w.existsSync(t)||w.mkdirSync(t,{recursive:!0})}function a(o){return ae.join(t,`${o}.spec.md`)}return{async saveSpecification(o,r){n(),w.writeFileSync(a(o),r,"utf-8")},async getSpecification(o){n();let r=a(o);return w.existsSync(r)?w.readFileSync(r,"utf-8"):null},async listSpecifications(){return n(),w.readdirSync(t).filter(r=>r.endsWith(".spec.md")).map(r=>{let s=w.readFileSync(ae.join(t,r),"utf-8"),f=s.match(/name: "([^"]+)"/),_=s.match(/status: (\w+)/),g=s.match(/version: "([^"]+)"/);return{slug:r.replace(".spec.md",""),name:f?.[1]||r,status:_?.[1]||"unknown",version:g?.[1]||"1.0.0"}})},async deleteSpecification(o){n();let r=a(o);return w.existsSync(r)?(w.unlinkSync(r),!0):!1},async specificationExists(o){return n(),w.existsSync(a(o))}}}function _e(){return pt({name:"agent-insights",version:"1.0.0",tools:[Ne,Ue,Le,Me,je,Be]})}import{query as Ct}from"@anthropic-ai/claude-agent-sdk";var ke={description:"Delegate to this agent when scaffolding new entities or creating CRUD features. This agent handles entity generation using the scaffold CLI with multi-entity support.",model:"inherit",prompt:`You are a scaffolding specialist. Your ONLY job is:
61
61
  1. Check existing features (quick ls)
62
62
  2. Write ALL entities to ONE /tmp/scaffold.json (big bang, no phasing!)
63
63
  3. Run scaffold CLI ONCE
@@ -773,12 +773,12 @@ Before saving, verify your context:
773
773
  5. **Keep it scannable** - Use bullets, clear sections
774
774
  6. **Update, don't replace** - If context exists, improve it rather than starting fresh`};import*as z from"fs";import*as qe from"path";var de=class{buffer=[];flushInterval=null;sessionId=null;outputDir;flushIntervalMs;constructor(t){this.outputDir=t.outputDir||"/tmp/agent-debug",this.flushIntervalMs=t.flushIntervalMs||3e3,t.enabled&&(this.ensureOutputDir(),this.startFlushInterval())}ensureOutputDir(){z.existsSync(this.outputDir)||z.mkdirSync(this.outputDir,{recursive:!0})}startFlushInterval(){this.flushInterval=setInterval(()=>{this.flush()},this.flushIntervalMs)}setSessionId(t){this.sessionId=t}log(t){let a={timestamp:new Date().toISOString(),message:t};this.buffer.push(JSON.stringify(a,null,2)+`
775
775
  ---
776
- `)}getLogFilePath(){let n=(this.sessionId||"unknown").replace(/[^a-zA-Z0-9-]/g,"_").slice(0,50),a=new Date().toISOString().split("T")[0];return qe.join(this.outputDir,`session-${a}-${n}.log`)}flush(){if(this.buffer.length===0)return;let t=this.buffer.join("");this.buffer=[];let n=this.getLogFilePath();z.appendFileSync(n,t)}stop(){this.flushInterval&&(clearInterval(this.flushInterval),this.flushInterval=null),this.flush()}};function We(e){return e?typeof e=="boolean"?e?new de({enabled:!0}):null:e.enabled?new de(e):null:null}import{z as c}from"zod";import*as J from"fs/promises";import*as E from"path";var Ge=c.object({port:c.number(),startCommand:c.string(),buildCommand:c.string().optional(),typecheckCommand:c.string().optional(),logFile:c.string().optional(),healthEndpoint:c.string().optional(),extensions:c.array(c.string())}),ut=c.object({port:c.number(),type:c.enum(["postgres","mysql","sqlite","mongodb"])}),gt=c.object({command:c.string(),schemaPath:c.string().optional()}),ft=c.object({email:c.string().optional(),name:c.string().optional(),commitPrefix:c.string().optional()}),mt=c.object({enabled:c.boolean(),port:c.number().optional()}),ee=c.object({version:c.literal("1.0"),paths:c.object({workspace:c.string(),backend:c.string().optional(),frontend:c.string().optional(),features:c.object({backend:c.string().optional(),frontend:c.string().optional()}).optional()}),services:c.object({backend:Ge.optional(),frontend:Ge.optional(),database:ut.optional()}).optional(),scaffold:gt.optional(),git:ft.optional(),techStack:c.object({backend:c.array(c.string()).optional(),frontend:c.array(c.string()).optional(),patterns:c.array(c.string()).optional()}).optional(),tunnel:mt.optional()});function O(e){return{version:"1.0",paths:{workspace:e||process.env.WORKSPACE_DIR||"/workspace",backend:"packages/dotnet-api",frontend:"packages/backoffice-web",features:{backend:"Source/Features",frontend:"src/applications/super-admin/features"}},services:{backend:{port:5338,startCommand:"dotnet run",buildCommand:"dotnet build",typecheckCommand:"dotnet build --no-restore",logFile:"/tmp/api.log",healthEndpoint:"http://localhost:5338",extensions:[".cs",".csproj"]},frontend:{port:5173,startCommand:"npm run dev",typecheckCommand:"npx tsc -p tsconfig.app.json --noEmit",logFile:"/tmp/web.log",healthEndpoint:"http://localhost:5173",extensions:[".ts",".tsx",".json"]},database:{port:43594,type:"postgres"}},scaffold:{command:"scaffold --schema /tmp/scaffold.json --output /workspace --force --full",schemaPath:"/tmp/scaffold.json"},git:{email:"agent@dotnetmentor.se",name:"Agent",commitPrefix:"[agent]"},techStack:{backend:[".NET 9","PostgreSQL","MediatR (CQRS)","EF Core"],frontend:["React 19","TypeScript","Vite","MUI","TanStack Query"],patterns:["Vertical slices","CQRS","Soft delete","Timestamps"]},tunnel:{enabled:!0,port:5173}}}var Ye="project-config.json",He=".sdd";async function Ke(e){let{workspaceDir:t,providedConfig:n,requireConfig:a}=e;if(n)return{config:ee.parse(n),source:"provided"};let s=E.join(t,He,Ye);try{let r=await J.readFile(s,"utf-8"),o=JSON.parse(r);return{config:ee.parse(o),source:"file",configPath:s}}catch(r){r instanceof Error&&"code"in r&&r.code!=="ENOENT"&&console.warn(`[Config] Warning: Invalid project-config.json at ${s}:`,r instanceof c.ZodError?r.errors:r.message)}if(a)throw new Error(`No project configuration found at ${s} and requireConfig is true`);return{config:O(t),source:"default"}}async function Qe(e,t){let n=E.join(e,He),a=E.join(n,Ye);await J.mkdir(n,{recursive:!0});let s=ee.parse(t);return await J.writeFile(a,JSON.stringify(s,null,2),"utf-8"),a}function pe(e,t){if(t)return E.isAbsolute(t)?t:E.join(e.paths.workspace,t)}function F(e){return pe(e,e.paths.backend)}function N(e){return pe(e,e.paths.frontend)}function te(e){let t=F(e);if(!(!t||!e.paths.features?.backend))return E.join(t,e.paths.features.backend)}function X(e){let t=N(e);if(!(!t||!e.paths.features?.frontend))return E.join(t,e.paths.features.frontend)}function Ve(e,t){if(!e.services?.backend?.extensions)return!1;let n=E.extname(t).toLowerCase();return e.services.backend.extensions.includes(n)}function ze(e,t){if(!e.services?.frontend?.extensions)return!1;let n=E.extname(t).toLowerCase();return e.services.frontend.extensions.includes(n)}function ue(e){let t=[];return e.techStack?.backend?.length&&t.push(`**Backend:** ${e.techStack.backend.join(", ")}`),e.techStack?.frontend?.length&&t.push(`**Frontend:** ${e.techStack.frontend.join(", ")}`),e.techStack?.patterns?.length&&t.push(`**Patterns:** ${e.techStack.patterns.join(", ")}`),t.join(`
776
+ `)}getLogFilePath(){let n=(this.sessionId||"unknown").replace(/[^a-zA-Z0-9-]/g,"_").slice(0,50),a=new Date().toISOString().split("T")[0];return qe.join(this.outputDir,`session-${a}-${n}.log`)}flush(){if(this.buffer.length===0)return;let t=this.buffer.join("");this.buffer=[];let n=this.getLogFilePath();z.appendFileSync(n,t)}stop(){this.flushInterval&&(clearInterval(this.flushInterval),this.flushInterval=null),this.flush()}};function We(e){return e?typeof e=="boolean"?e?new de({enabled:!0}):null:e.enabled?new de(e):null:null}import{z as c}from"zod";import*as J from"fs/promises";import*as E from"path";var Ge=c.object({port:c.number(),startCommand:c.string(),buildCommand:c.string().optional(),typecheckCommand:c.string().optional(),logFile:c.string().optional(),healthEndpoint:c.string().optional(),extensions:c.array(c.string())}),ut=c.object({port:c.number(),type:c.enum(["postgres","mysql","sqlite","mongodb"])}),gt=c.object({command:c.string(),schemaPath:c.string().optional()}),ft=c.object({email:c.string().optional(),name:c.string().optional(),commitPrefix:c.string().optional()}),mt=c.object({enabled:c.boolean(),port:c.number().optional()}),ee=c.object({version:c.literal("1.0"),paths:c.object({workspace:c.string(),backend:c.string().optional(),frontend:c.string().optional(),features:c.object({backend:c.string().optional(),frontend:c.string().optional()}).optional()}),services:c.object({backend:Ge.optional(),frontend:Ge.optional(),database:ut.optional()}).optional(),scaffold:gt.optional(),git:ft.optional(),techStack:c.object({backend:c.array(c.string()).optional(),frontend:c.array(c.string()).optional(),patterns:c.array(c.string()).optional()}).optional(),tunnel:mt.optional()});function O(e){return{version:"1.0",paths:{workspace:e||process.env.WORKSPACE_DIR||"/workspace",backend:"packages/dotnet-api",frontend:"packages/backoffice-web",features:{backend:"Source/Features",frontend:"src/applications/super-admin/features"}},services:{backend:{port:5338,startCommand:"dotnet run",buildCommand:"dotnet build",typecheckCommand:"dotnet build --no-restore",logFile:"/tmp/api.log",healthEndpoint:"http://localhost:5338",extensions:[".cs",".csproj"]},frontend:{port:5173,startCommand:"npm run dev",typecheckCommand:"npx tsc -p tsconfig.app.json --noEmit",logFile:"/tmp/web.log",healthEndpoint:"http://localhost:5173",extensions:[".ts",".tsx",".json"]},database:{port:43594,type:"postgres"}},scaffold:{command:"scaffold --schema /tmp/scaffold.json --output /workspace --force --full",schemaPath:"/tmp/scaffold.json"},git:{email:"agent@dotnetmentor.se",name:"Agent",commitPrefix:"[agent]"},techStack:{backend:[".NET 9","PostgreSQL","MediatR (CQRS)","EF Core"],frontend:["React 19","TypeScript","Vite","MUI","TanStack Query"],patterns:["Vertical slices","CQRS","Soft delete","Timestamps"]},tunnel:{enabled:!0,port:5173}}}var Ye="project-config.json",He=".sdd";async function Ke(e){let{workspaceDir:t,providedConfig:n,requireConfig:a}=e;if(n)return{config:ee.parse(n),source:"provided"};let o=E.join(t,He,Ye);try{let r=await J.readFile(o,"utf-8"),s=JSON.parse(r);return{config:ee.parse(s),source:"file",configPath:o}}catch(r){r instanceof Error&&"code"in r&&r.code!=="ENOENT"&&console.warn(`[Config] Warning: Invalid project-config.json at ${o}:`,r instanceof c.ZodError?r.errors:r.message)}if(a)throw new Error(`No project configuration found at ${o} and requireConfig is true`);return{config:O(t),source:"default"}}async function Qe(e,t){let n=E.join(e,He),a=E.join(n,Ye);await J.mkdir(n,{recursive:!0});let o=ee.parse(t);return await J.writeFile(a,JSON.stringify(o,null,2),"utf-8"),a}function pe(e,t){if(t)return E.isAbsolute(t)?t:E.join(e.paths.workspace,t)}function F(e){return pe(e,e.paths.backend)}function N(e){return pe(e,e.paths.frontend)}function te(e){let t=F(e);if(!(!t||!e.paths.features?.backend))return E.join(t,e.paths.features.backend)}function X(e){let t=N(e);if(!(!t||!e.paths.features?.frontend))return E.join(t,e.paths.features.frontend)}function Ve(e,t){if(!e.services?.backend?.extensions)return!1;let n=E.extname(t).toLowerCase();return e.services.backend.extensions.includes(n)}function ze(e,t){if(!e.services?.frontend?.extensions)return!1;let n=E.extname(t).toLowerCase();return e.services.frontend.extensions.includes(n)}function ue(e){let t=[];return e.techStack?.backend?.length&&t.push(`**Backend:** ${e.techStack.backend.join(", ")}`),e.techStack?.frontend?.length&&t.push(`**Frontend:** ${e.techStack.frontend.join(", ")}`),e.techStack?.patterns?.length&&t.push(`**Patterns:** ${e.techStack.patterns.join(", ")}`),t.join(`
777
777
  `)}function Je(e){let t=[];return e.paths.backend&&t.push(`- Backend: ${F(e)}`),e.paths.frontend&&t.push(`- Frontend: ${N(e)}`),e.paths.features?.backend&&t.push(`- Backend Features: ${te(e)}`),e.paths.features?.frontend&&t.push(`- Frontend Features: ${X(e)}`),t.join(`
778
778
  `)}function ge(e){let t=[];return e.services?.backend&&t.push(`| Backend | ${F(e)} | ${e.services.backend.port} | ${e.services.backend.logFile||"N/A"} |`),e.services?.frontend&&t.push(`| Frontend | ${N(e)} | ${e.services.frontend.port} | ${e.services.frontend.logFile||"N/A"} |`),e.services?.database&&t.push(`| Database (${e.services.database.type}) | localhost | ${e.services.database.port} | - |`),t.length===0?"":`| Service | Location | Port | Logs |
779
779
  |---------|----------|------|------|
780
780
  ${t.join(`
781
- `)}`}function Xe(e={}){let{debugMode:t=!1,projectPrompt:n=null,isLocal:a=!1,projectConfig:s=O(),useDefaultStack:r=!0}=e,o=n?`
781
+ `)}`}function Xe(e={}){let{debugMode:t=!1,projectPrompt:n=null,isLocal:a=!1,projectConfig:o=O(),useDefaultStack:r=!0}=e,s=n?`
782
782
  <project_context>
783
783
  ${n}
784
784
  </project_context>
@@ -823,7 +823,7 @@ Then STOP. Do not continue. Do not try to fix it. Wait for user.
823
823
 
824
824
  ---
825
825
 
826
- `:"",_=ht(s),g=yt(s,a),u=kt(s,r),T=bt(r),R=r?wt(s):"",B=r?St(s,a):"",$=_t(r);return`${o}${f}# Agent Instructions
826
+ `:"",_=ht(o),g=yt(o,a),u=kt(o,r),T=bt(r),R=r?wt(o):"",B=r?St(o,a):"",$=_t(r);return`${s}${f}# Agent Instructions
827
827
 
828
828
  ## \u26D4 CRITICAL: EVERYTHING IS A KANBAN TASK
829
829
 
@@ -967,16 +967,16 @@ ${$}
967
967
  ${R}
968
968
 
969
969
  ${B}
970
- `}function ht(e){let t=ue(e),n=F(e),a=N(e),s=te(e),r=X(e),o=[];s&&o.push(`- Backend: "${s}/{Entity}/"`),r&&o.push(`- Frontend: "${r}/{entity}/"`);let f=e.techStack?.patterns?.length?`
970
+ `}function ht(e){let t=ue(e),n=F(e),a=N(e),o=te(e),r=X(e),s=[];o&&s.push(`- Backend: "${o}/{Entity}/"`),r&&s.push(`- Frontend: "${r}/{entity}/"`);let f=e.techStack?.patterns?.length?`
971
971
  **Key patterns:**
972
972
  ${e.techStack.patterns.map(u=>`- ${u}`).join(`
973
973
  `)}`:"",g=e.techStack?.backend?.some(u=>u.includes(".NET")||u.includes("C#"))??!1?`
974
974
 
975
975
  **\u26A0\uFE0F User entity exists!** ASP.NET Identity "ApplicationUser" at "Features/Users/" - don't scaffold User, just relate to it.`:"";return`<tech-stack>
976
976
  ${t}
977
- ${o.length>0?`
977
+ ${s.length>0?`
978
978
  **Architecture:** Feature folders
979
- ${o.join(`
979
+ ${s.join(`
980
980
  `)}`:""}
981
981
  ${f}${g}
982
982
  </tech-stack>`}function yt(e,t){return t?`<environment>
@@ -1112,11 +1112,11 @@ Check \`.claude/skills/\` for project-specific patterns and conventions.`}functi
1112
1112
  - Asks questions, designs spec
1113
1113
  - **Saves spec using save_specification MCP tool**
1114
1114
  - **After @planning completes: STOP and wait for user approval!**
1115
- `;if(t){let a=!!e.scaffold?.command,s=!!e.services?.backend,r=!!e.services?.frontend;a&&(n+=`
1115
+ `;if(t){let a=!!e.scaffold?.command,o=!!e.services?.backend,r=!!e.services?.frontend;a&&(n+=`
1116
1116
  **@scaffolding** - Create CRUD entities, database, backend & frontend:
1117
1117
  - "Add X management" \u2192 one-shot all entities, everything from entity creation to frontend hooks and pages!
1118
1118
  - NEVER write scaffold JSON yourself
1119
- `),s&&(n+=`
1119
+ `),o&&(n+=`
1120
1120
  **@backend** - Custom backend code (non-scaffold):
1121
1121
  - Custom queries, endpoints, lookups
1122
1122
  - Runs build + swagger generation before done
@@ -1178,7 +1178,7 @@ queryClient.invalidateQueries({ queryKey: getGetApiPeopleQueryKey() })
1178
1178
 
1179
1179
  Check the project's existing patterns for API calls and data fetching.
1180
1180
  Look for existing feature code to understand the correct patterns before writing new API calls.
1181
- </frontend-api>`:""}function St(e,t){if(!e.services?.frontend)return"";let n=e.services.frontend.port,a=X(e),s=`http://localhost:${n}`;return`<ui-verification>
1181
+ </frontend-api>`:""}function St(e,t){if(!e.services?.frontend)return"";let n=e.services.frontend.port,a=X(e),o=`http://localhost:${n}`;return`<ui-verification>
1182
1182
  ## \u{1F50D} UI Verification with Playwright
1183
1183
 
1184
1184
  You have access to **Playwright MCP** tools to verify the UI works correctly after making changes.
@@ -1236,7 +1236,7 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
1236
1236
  - To test forms and interactions
1237
1237
 
1238
1238
  **Verification workflow:**
1239
- 1. Navigate to the app: \`browser_navigate\` to \`${a?.includes("super-admin")?`${s}/super-admin/[feature]`:`${s}/[feature]`}\`
1239
+ 1. Navigate to the app: \`browser_navigate\` to \`${a?.includes("super-admin")?`${o}/super-admin/[feature]`:`${o}/[feature]`}\`
1240
1240
  2. Get page structure: \`browser_snapshot\` returns the accessibility tree (LLM-friendly)
1241
1241
  3. Optional: Take screenshot for user: \`browser_take_screenshot\`
1242
1242
  4. Verify expected elements exist in the snapshot
@@ -1260,13 +1260,13 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
1260
1260
  - **Multi-tab apps**: Use \`browser_tabs\` to manage multiple windows/tabs in legacy applications
1261
1261
  - **Large snapshots**: For pages with huge dropdowns (e.g., country lists), use \`browser_evaluate\` to collapse them first
1262
1262
  - ${t?"In local mode, the browser window will be visible to you":"In container mode, browser runs headless"}
1263
- </ui-verification>`}function Ze(e){return e&&e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var vt=null;function et(){return Ze(vt)}import*as W from"path";import*as Ce from"fs";import{fileURLToPath as Tt}from"url";var Et=_e(),ve=W.dirname(Tt(import.meta.url)),tt=[W.resolve(ve,"../mcps/stdio-server.js"),W.resolve(ve,"./adapters/mcps/stdio-server.js")],ne=tt.find(e=>Ce.existsSync(e))||tt[0];console.log("[MCP] Stdio server path resolved:",ne);console.log("[MCP] __dirname:",ve);console.log("[MCP] File exists:",Ce.existsSync(ne));async function nt(e,t={}){let{model:n,sessionId:a=null,projectId:s=null,apiUrl:r=null,callbacks:o={},debugLog:f,abortController:_,debugMode:g=!1,isLocal:u=process.env.LOCAL_MODE==="true",projectConfig:T=O(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:R=!0}=t,B=We(f),$=new Set,H=a||"",K,q,C,U=!1,Z=!1,I=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[ne]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",s||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",I),console.log("[MCP] File exists check:",ne);try{for await(let d of Ct({prompt:e,options:{abortController:_,cwd:I,resume:a??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","Skill","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...u?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_fill_form","mcp__playwright__browser_select_option","mcp__playwright__browser_press_key","mcp__playwright__browser_hover","mcp__playwright__browser_drag","mcp__playwright__browser_file_upload","mcp__playwright__browser_handle_dialog","mcp__playwright__browser_close","mcp__playwright__browser_resize","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_screen_capture","mcp__playwright__browser_screen_move_mouse","mcp__playwright__browser_screen_click","mcp__playwright__browser_screen_drag","mcp__playwright__browser_screen_type","mcp__playwright__browser_evaluate","mcp__playwright__browser_run_code","mcp__playwright__browser_tabs","mcp__playwright__browser_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:n,mcpServers:{"agent-insights":Et,"agent-planning":{type:"stdio",command:"node",args:[ne],env:{WORKSPACE_DIR:I,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:s||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${W.join(I,".playwright-data")}`,...u?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:R?{scaffolding:ke,debugger:ie,planning:ce,backend:we,frontend:Se,"project-context":le}:{debugger:ie,planning:ce,"project-context":le},systemPrompt:Xe({debugMode:g,projectPrompt:et(),isLocal:u,projectConfig:T,useDefaultStack:R})}}))if(!(!d.uuid||$.has(d.uuid))){switch($.add(d.uuid),B?.log(d),o.onRawMessage?.(d),d.type){case"assistant":if(d.message?.content)for(let S of d.message.content)S.type==="text"?o.onAssistantText?.(S.text):S.type==="tool_use"?o.onAssistantToolUse?.(S.name,S.input):S.type==="tool_result"&&o.onAssistantToolResult?.(S.tool_use_id,S.content);break;case"user":o.onUserMessage?.(d);break;case"result":if(d.subtype==="success")K=d.result,q=d.total_cost_usd,o.onResult?.(d.result,d.total_cost_usd);else{let S=`Agent error: ${d.subtype}`;C=S,o.onError?.(S)}Z=!0;break;case"system":switch(d.subtype){case"init":H=d.session_id,B?.setSessionId(d.session_id),o.onSystemInit?.(d.session_id);break;case"status":o.onSystemStatus?.(JSON.stringify(d));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":o.onStreamEvent?.(d);break;case"tool_progress":o.onToolProgress?.(d);break;case"auth_status":o.onAuthStatus?.(d);break}if(Z)break}}catch(d){d instanceof Error&&d.name==="AbortError"||d instanceof Error&&d.message.includes("aborted by user")?(U=!0,o.onAborted?.()):(C=d instanceof Error?d.message:String(d),o.onError?.(C))}finally{B?.stop()}return console.log("after try"),{sessionId:H,result:K,cost:q,error:C,aborted:U}}function st(e){return`User answered the questions:
1263
+ </ui-verification>`}function Ze(e){return e&&e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var vt=null;function et(){return Ze(vt)}import*as W from"path";import*as Ce from"fs";import{fileURLToPath as Tt}from"url";var Et=_e(),ve=W.dirname(Tt(import.meta.url)),tt=[W.resolve(ve,"../mcps/stdio-server.js"),W.resolve(ve,"./adapters/mcps/stdio-server.js")],ne=tt.find(e=>Ce.existsSync(e))||tt[0];console.log("[MCP] Stdio server path resolved:",ne);console.log("[MCP] __dirname:",ve);console.log("[MCP] File exists:",Ce.existsSync(ne));async function nt(e,t={}){let{model:n,sessionId:a=null,projectId:o=null,apiUrl:r=null,callbacks:s={},debugLog:f,abortController:_,debugMode:g=!1,isLocal:u=process.env.LOCAL_MODE==="true",projectConfig:T=O(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:R=!0}=t,B=We(f),$=new Set,H=a||"",K,q,C,U=!1,Z=!1,I=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[ne]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",o||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",I),console.log("[MCP] File exists check:",ne);try{for await(let d of Ct({prompt:e,options:{abortController:_,cwd:I,resume:a??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","Skill","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...u?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__agent-planning__reference_projects_list","mcp__agent-planning__reference_project_tree","mcp__agent-planning__reference_project_read_file","mcp__agent-planning__reference_project_download","mcp__agent-planning__reference_project_update_status","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_fill_form","mcp__playwright__browser_select_option","mcp__playwright__browser_press_key","mcp__playwright__browser_hover","mcp__playwright__browser_drag","mcp__playwright__browser_file_upload","mcp__playwright__browser_handle_dialog","mcp__playwright__browser_close","mcp__playwright__browser_resize","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_screen_capture","mcp__playwright__browser_screen_move_mouse","mcp__playwright__browser_screen_click","mcp__playwright__browser_screen_drag","mcp__playwright__browser_screen_type","mcp__playwright__browser_evaluate","mcp__playwright__browser_run_code","mcp__playwright__browser_tabs","mcp__playwright__browser_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:n,mcpServers:{"agent-insights":Et,"agent-planning":{type:"stdio",command:"node",args:[ne],env:{WORKSPACE_DIR:I,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:o||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${W.join(I,".playwright-data")}`,...u?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:R?{scaffolding:ke,debugger:ie,planning:ce,backend:we,frontend:Se,"project-context":le}:{debugger:ie,planning:ce,"project-context":le},systemPrompt:Xe({debugMode:g,projectPrompt:et(),isLocal:u,projectConfig:T,useDefaultStack:R})}}))if(!(!d.uuid||$.has(d.uuid))){switch($.add(d.uuid),B?.log(d),s.onRawMessage?.(d),d.type){case"assistant":if(d.message?.content)for(let S of d.message.content)S.type==="text"?s.onAssistantText?.(S.text):S.type==="tool_use"?s.onAssistantToolUse?.(S.name,S.input):S.type==="tool_result"&&s.onAssistantToolResult?.(S.tool_use_id,S.content);break;case"user":s.onUserMessage?.(d);break;case"result":if(d.subtype==="success")K=d.result,q=d.total_cost_usd,s.onResult?.(d.result,d.total_cost_usd);else{let S=`Agent error: ${d.subtype}`;C=S,s.onError?.(S)}Z=!0;break;case"system":switch(d.subtype){case"init":H=d.session_id,B?.setSessionId(d.session_id),s.onSystemInit?.(d.session_id);break;case"status":s.onSystemStatus?.(JSON.stringify(d));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":s.onStreamEvent?.(d);break;case"tool_progress":s.onToolProgress?.(d);break;case"auth_status":s.onAuthStatus?.(d);break}if(Z)break}}catch(d){d instanceof Error&&d.name==="AbortError"||d instanceof Error&&d.message.includes("aborted by user")?(U=!0,s.onAborted?.()):(C=d instanceof Error?d.message:String(d),s.onError?.(C))}finally{B?.stop()}return console.log("after try"),{sessionId:H,result:K,cost:q,error:C,aborted:U}}function ot(e){return`User answered the questions:
1264
1264
 
1265
- ${Object.entries(e).map(([n,a])=>{let s=Array.isArray(a)?a.join(", "):a;return`${n}: ${s}`}).join(`
1266
- `)}`}import{spawn as ot,execSync as G}from"child_process";import*as b from"fs";import*as rt from"http";import*as j from"path";function at(e){let{workspaceDir:t,apiPort:n,webPort:a,onBackendError:s,onFrontendError:r,projectConfig:o=O(t)}=e,f=n??o.services?.backend?.port??5338,_=a??o.services?.frontend?.port??5173,g=F(o),u=N(o),T=o.services?.backend?.startCommand??"dotnet run",R=o.services?.backend?.buildCommand??"dotnet build",B=o.services?.backend?.typecheckCommand??"dotnet build --no-restore",$=o.services?.frontend?.startCommand??"npm run dev",H=o.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",K=o.services?.backend?.logFile??"/tmp/api.log",q=o.services?.frontend?.logFile??"/tmp/web.log",C=null,U=null,Z=l=>new Promise(i=>{let p=setTimeout(()=>i(!1),3e3);rt.get(`http://localhost:${l}`,v=>{clearTimeout(p),i(v.statusCode!==void 0&&v.statusCode<500)}).on("error",()=>{clearTimeout(p),i(!1)})}),I=l=>{try{let i=G(`lsof -ti:${l} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(i){let p=i.split(`
1267
- `).filter(Boolean);console.log(`[Services] Killing ${p.length} process(es) on port ${l}: ${p.join(", ")}`);for(let m of p)try{process.kill(parseInt(m,10),"SIGKILL")}catch{}}}catch{try{G(`fuser -k ${l}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},d=(l,i)=>new Promise(p=>{if(!l||!l.pid){p();return}console.log(`[Services] Stopping ${i} (PID: ${l.pid})...`);try{process.kill(-l.pid,"SIGTERM")}catch{try{l.kill("SIGTERM")}catch{}}setTimeout(p,500)}),S=async()=>{if(!g||!b.existsSync(g)){console.log("[Services] No backend found, skipping backend start");return}I(f),console.log(`[Services] Starting backend with: ${T}...`);let l=j.dirname(K);b.existsSync(l)||b.mkdirSync(l,{recursive:!0});let i=b.createWriteStream(K,{flags:"a"}),[p,...m]=T.split(" ");C=ot(p,m,{cwd:g,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let v="",k=h=>{let L=h.toString();i.write(L);let A=(v+L).split(`
1268
- `);v=A.pop()||"";for(let x of A)x&&Pt(x)&&s&&s(x)};C.stdout?.on("data",k),C.stderr?.on("data",k),C.on("exit",h=>{i.end(),h!==0&&h!==null&&s&&s(`Process exited with code ${h}`)}),C.unref()},Te=async()=>{if(!u||!b.existsSync(j.join(u,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}I(_),console.log(`[Services] Starting frontend with: ${$}...`);let l=j.dirname(q);b.existsSync(l)||b.mkdirSync(l,{recursive:!0});let[i,...p]=$.split(" ");U=ot(i,p,{cwd:u,stdio:["ignore",b.openSync(q,"w"),b.openSync(q,"w")],detached:!0,shell:!0}),U.unref()},Ee=async()=>{await d(C,"backend"),C=null,I(f)},Pe=async()=>{await d(U,"frontend"),U=null,I(_)},fe=async()=>{let[l,i]=await Promise.all([Z(f),Z(_)]);return{api:l,web:i}},Ae=async(l=15e3)=>{let i=Date.now(),p=1e3,m=0,v=null,k=null;for(console.log(`[Services] Waiting for health (timeout: ${l}ms)...`);Date.now()-i<l;){m++;let A=await fe(),x=Date.now()-i;if(A.web&&k===null&&(k=x,console.log(`[Services] \u2713 Frontend (Vite) ready after ${x}ms`)),A.api&&v===null&&(v=x,console.log(`[Services] \u2713 Backend (dotnet) ready after ${x}ms`)),console.log(`[Services] Health poll #${m} (${x}ms): api=${A.api}, web=${A.web}`),A.api&&A.web)return console.log(`[Services] \u2713 Both services healthy after ${x}ms (${m} polls)`),A;await new Promise(dt=>setTimeout(dt,p))}let h=await fe(),L=Date.now()-i;return h.web&&k===null&&console.log(`[Services] \u2713 Frontend (Vite) ready after ${L}ms`),h.api&&v===null&&console.log(`[Services] \u2713 Backend (dotnet) ready after ${L}ms`),console.log(`[Services] \u26A0 Health timeout after ${L}ms: api=${h.api}, web=${h.web}`),h},ct=async l=>{console.log(`[Services] Restarting ${l}...`);let i={success:!0},p=l==="backend"||l==="both",m=l==="frontend"||l==="both";if(p&&await Ee(),m&&await Pe(),p&&g&&b.existsSync(g)){console.log(`[Services] Building backend with: ${R}...`);try{G(R,{cwd:g,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),console.log("[Services] \u2713 Backend build succeeded")}catch(k){let h=k;i.success=!1,i.backendError=h.stderr||h.stdout||"Build failed",console.error("[Services] \u2717 Backend build failed")}}if(m&&u&&b.existsSync(j.join(u,"package.json"))){console.log(`[Services] Type-checking frontend with: ${H}...`);try{G(H,{cwd:u,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),console.log("[Services] \u2713 Frontend types OK")}catch(k){let h=k;i.success=!1,i.frontendError=h.stderr||h.stdout||"Type check failed",console.error("[Services] \u2717 Frontend type check failed")}}console.log(`[Services] Starting ${l}...`),p&&await S(),m&&await Te();let v=await Ae(1e4);if(p&&!v.api){i.success=!1;let k=await Ie("backend",20),h=k.length>0?`
1265
+ ${Object.entries(e).map(([n,a])=>{let o=Array.isArray(a)?a.join(", "):a;return`${n}: ${o}`}).join(`
1266
+ `)}`}import{spawn as st,execSync as G}from"child_process";import*as b from"fs";import*as rt from"http";import*as j from"path";function at(e){let{workspaceDir:t,apiPort:n,webPort:a,onBackendError:o,onFrontendError:r,projectConfig:s=O(t)}=e,f=n??s.services?.backend?.port??5338,_=a??s.services?.frontend?.port??5173,g=F(s),u=N(s),T=s.services?.backend?.startCommand??"dotnet run",R=s.services?.backend?.buildCommand??"dotnet build",B=s.services?.backend?.typecheckCommand??"dotnet build --no-restore",$=s.services?.frontend?.startCommand??"npm run dev",H=s.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",K=s.services?.backend?.logFile??"/tmp/api.log",q=s.services?.frontend?.logFile??"/tmp/web.log",C=null,U=null,Z=l=>new Promise(i=>{let p=setTimeout(()=>i(!1),3e3);rt.get(`http://localhost:${l}`,v=>{clearTimeout(p),i(v.statusCode!==void 0&&v.statusCode<500)}).on("error",()=>{clearTimeout(p),i(!1)})}),I=l=>{try{let i=G(`lsof -ti:${l} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(i){let p=i.split(`
1267
+ `).filter(Boolean);console.log(`[Services] Killing ${p.length} process(es) on port ${l}: ${p.join(", ")}`);for(let m of p)try{process.kill(parseInt(m,10),"SIGKILL")}catch{}}}catch{try{G(`fuser -k ${l}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},d=(l,i)=>new Promise(p=>{if(!l||!l.pid){p();return}console.log(`[Services] Stopping ${i} (PID: ${l.pid})...`);try{process.kill(-l.pid,"SIGTERM")}catch{try{l.kill("SIGTERM")}catch{}}setTimeout(p,500)}),S=async()=>{if(!g||!b.existsSync(g)){console.log("[Services] No backend found, skipping backend start");return}I(f),console.log(`[Services] Starting backend with: ${T}...`);let l=j.dirname(K);b.existsSync(l)||b.mkdirSync(l,{recursive:!0});let i=b.createWriteStream(K,{flags:"a"}),[p,...m]=T.split(" ");C=st(p,m,{cwd:g,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let v="",k=h=>{let L=h.toString();i.write(L);let A=(v+L).split(`
1268
+ `);v=A.pop()||"";for(let x of A)x&&Pt(x)&&o&&o(x)};C.stdout?.on("data",k),C.stderr?.on("data",k),C.on("exit",h=>{i.end(),h!==0&&h!==null&&o&&o(`Process exited with code ${h}`)}),C.unref()},Te=async()=>{if(!u||!b.existsSync(j.join(u,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}I(_),console.log(`[Services] Starting frontend with: ${$}...`);let l=j.dirname(q);b.existsSync(l)||b.mkdirSync(l,{recursive:!0});let[i,...p]=$.split(" ");U=st(i,p,{cwd:u,stdio:["ignore",b.openSync(q,"w"),b.openSync(q,"w")],detached:!0,shell:!0}),U.unref()},Ee=async()=>{await d(C,"backend"),C=null,I(f)},Pe=async()=>{await d(U,"frontend"),U=null,I(_)},fe=async()=>{let[l,i]=await Promise.all([Z(f),Z(_)]);return{api:l,web:i}},Ae=async(l=15e3)=>{let i=Date.now(),p=1e3,m=0,v=null,k=null;for(console.log(`[Services] Waiting for health (timeout: ${l}ms)...`);Date.now()-i<l;){m++;let A=await fe(),x=Date.now()-i;if(A.web&&k===null&&(k=x,console.log(`[Services] \u2713 Frontend (Vite) ready after ${x}ms`)),A.api&&v===null&&(v=x,console.log(`[Services] \u2713 Backend (dotnet) ready after ${x}ms`)),console.log(`[Services] Health poll #${m} (${x}ms): api=${A.api}, web=${A.web}`),A.api&&A.web)return console.log(`[Services] \u2713 Both services healthy after ${x}ms (${m} polls)`),A;await new Promise(dt=>setTimeout(dt,p))}let h=await fe(),L=Date.now()-i;return h.web&&k===null&&console.log(`[Services] \u2713 Frontend (Vite) ready after ${L}ms`),h.api&&v===null&&console.log(`[Services] \u2713 Backend (dotnet) ready after ${L}ms`),console.log(`[Services] \u26A0 Health timeout after ${L}ms: api=${h.api}, web=${h.web}`),h},ct=async l=>{console.log(`[Services] Restarting ${l}...`);let i={success:!0},p=l==="backend"||l==="both",m=l==="frontend"||l==="both";if(p&&await Ee(),m&&await Pe(),p&&g&&b.existsSync(g)){console.log(`[Services] Building backend with: ${R}...`);try{G(R,{cwd:g,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),console.log("[Services] \u2713 Backend build succeeded")}catch(k){let h=k;i.success=!1,i.backendError=h.stderr||h.stdout||"Build failed",console.error("[Services] \u2717 Backend build failed")}}if(m&&u&&b.existsSync(j.join(u,"package.json"))){console.log(`[Services] Type-checking frontend with: ${H}...`);try{G(H,{cwd:u,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),console.log("[Services] \u2713 Frontend types OK")}catch(k){let h=k;i.success=!1,i.frontendError=h.stderr||h.stdout||"Type check failed",console.error("[Services] \u2717 Frontend type check failed")}}console.log(`[Services] Starting ${l}...`),p&&await S(),m&&await Te();let v=await Ae(1e4);if(p&&!v.api){i.success=!1;let k=await Ie("backend",20),h=k.length>0?`
1269
1269
  Recent logs:
1270
1270
  ${k.join(`
1271
1271
  `)}`:"";i.backendError||(i.backendError=`Backend failed to start.${h}`)}return m&&!v.web&&(i.success=!1,i.frontendError||(i.frontendError="Frontend failed to start")),i},xe=async()=>{if(!g||!b.existsSync(g))return{success:!0};try{return G(B,{cwd:g,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),{success:!0}}catch(l){let i=l;return{success:!1,errors:i.stdout||i.stderr||"Build failed"}}},Re=async()=>{if(!u||!b.existsSync(j.join(u,"package.json")))return{success:!0};try{return G(H,{cwd:u,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),{success:!0}}catch(l){let i=l;return{success:!1,errors:i.stdout||i.stderr||"Type check failed"}}},lt=async()=>{let[l,i]=await Promise.all([xe(),Re()]);return{backend:l,frontend:i}},Ie=async(l,i)=>{let p=l==="backend"?K:q;if(!b.existsSync(p))return[`Log file not found: ${p}`];try{return G(`tail -n ${i} ${p}`,{encoding:"utf-8"}).split(`
1272
- `).filter(Boolean)}catch(m){return[`Error reading logs: ${m instanceof Error?m.message:String(m)}`]}};return{startBackend:S,startFrontend:Te,stopBackend:Ee,stopFrontend:Pe,restartServices:ct,checkHealth:fe,waitForHealth:Ae,getProcesses:()=>({backend:C,frontend:U}),checkBackendBuild:xe,checkFrontendTypes:Re,runTypeChecks:lt,tailLogs:Ie,checkSwaggerEndpoints:async l=>{let i=j.join(t,"swagger.json");if(!b.existsSync(i))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let p=JSON.parse(b.readFileSync(i,"utf-8")),m=Object.keys(p.paths||{}),v=[];for(let k of m)if(k.toLowerCase().includes(l.toLowerCase())){let h=Object.keys(p.paths[k]);for(let L of h)v.push(`${L.toUpperCase()} ${k}`)}return{foundEndpoints:v,totalEndpoints:m.length}}catch(p){return{foundEndpoints:[`Error parsing swagger.json: ${p instanceof Error?p.message:String(p)}`],totalEndpoints:0}}}}}function Pt(e){let t=e.toLowerCase();return t.includes("exception")||t.includes("fail:")||t.includes("[err]")||t.includes("[error]")}import{execSync as Y}from"child_process";function it(e){let t=async()=>{try{return Y("git status --porcelain",{cwd:e,encoding:"utf-8"}).trim().length>0}catch{return!1}},n=async()=>{try{return Y("git branch --show-current",{cwd:e,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:t,commitAndPush:async(s,r)=>{try{if(!await t())return{success:!0,noChanges:!0};let o=s.length>60?s.substring(0,60)+"...":s,_=`${r?"[agent] (partial)":"[agent]"} ${o}`;Y("git add -A",{cwd:e}),Y(`git commit -m "${_.replace(/"/g,'\\"')}"`,{cwd:e,encoding:"utf-8"});let g=Y("git rev-parse --short HEAD",{cwd:e,encoding:"utf-8"}).trim();try{Y("git push",{cwd:e,stdio:"pipe"})}catch(u){let T=u instanceof Error?u.message:String(u);if(T.includes("no upstream branch")){let R=await n();Y(`git push --set-upstream origin ${R}`,{cwd:e,stdio:"pipe"})}else return console.error("[Git] Push failed:",T),{success:!0,commitHash:g,error:`Committed but push failed: ${T}`}}return{success:!0,commitHash:g}}catch(o){let f=o instanceof Error?o.message:String(o);return console.error("[Git] Error:",f),{success:!1,error:f}}},getCurrentBranch:n}}export{ee as ProjectConfigSchema,be as createFilePlanningTransport,he as createFileTransport,it as createLocalGitManager,at as createLocalServiceManager,_e as createMcpServer,st as formatAnswersAsPrompt,te as getBackendFeaturesPath,F as getBackendPath,O as getDefaultConfig,X as getFrontendFeaturesPath,N as getFrontendPath,Je as getPathsDescription,ge as getServicesDescription,ue as getTechStackDescription,Ve as isBackendFile,ze as isFrontendFile,Ke as loadProjectConfig,ye as readPendingQuestions,pe as resolveConfigPath,nt as runAgent,Qe as saveProjectConfig,Oe as setPlanningTransport,Fe as setServiceManager};
1272
+ `).filter(Boolean)}catch(m){return[`Error reading logs: ${m instanceof Error?m.message:String(m)}`]}};return{startBackend:S,startFrontend:Te,stopBackend:Ee,stopFrontend:Pe,restartServices:ct,checkHealth:fe,waitForHealth:Ae,getProcesses:()=>({backend:C,frontend:U}),checkBackendBuild:xe,checkFrontendTypes:Re,runTypeChecks:lt,tailLogs:Ie,checkSwaggerEndpoints:async l=>{let i=j.join(t,"swagger.json");if(!b.existsSync(i))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let p=JSON.parse(b.readFileSync(i,"utf-8")),m=Object.keys(p.paths||{}),v=[];for(let k of m)if(k.toLowerCase().includes(l.toLowerCase())){let h=Object.keys(p.paths[k]);for(let L of h)v.push(`${L.toUpperCase()} ${k}`)}return{foundEndpoints:v,totalEndpoints:m.length}}catch(p){return{foundEndpoints:[`Error parsing swagger.json: ${p instanceof Error?p.message:String(p)}`],totalEndpoints:0}}}}}function Pt(e){let t=e.toLowerCase();return t.includes("exception")||t.includes("fail:")||t.includes("[err]")||t.includes("[error]")}import{execSync as Y}from"child_process";function it(e){let t=async()=>{try{return Y("git status --porcelain",{cwd:e,encoding:"utf-8"}).trim().length>0}catch{return!1}},n=async()=>{try{return Y("git branch --show-current",{cwd:e,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:t,commitAndPush:async(o,r)=>{try{if(!await t())return{success:!0,noChanges:!0};let s=o.length>60?o.substring(0,60)+"...":o,_=`${r?"[agent] (partial)":"[agent]"} ${s}`;Y("git add -A",{cwd:e}),Y(`git commit -m "${_.replace(/"/g,'\\"')}"`,{cwd:e,encoding:"utf-8"});let g=Y("git rev-parse --short HEAD",{cwd:e,encoding:"utf-8"}).trim();try{Y("git push",{cwd:e,stdio:"pipe"})}catch(u){let T=u instanceof Error?u.message:String(u);if(T.includes("no upstream branch")){let R=await n();Y(`git push --set-upstream origin ${R}`,{cwd:e,stdio:"pipe"})}else return console.error("[Git] Push failed:",T),{success:!0,commitHash:g,error:`Committed but push failed: ${T}`}}return{success:!0,commitHash:g}}catch(s){let f=s instanceof Error?s.message:String(s);return console.error("[Git] Error:",f),{success:!1,error:f}}},getCurrentBranch:n}}export{ee as ProjectConfigSchema,be as createFilePlanningTransport,he as createFileTransport,it as createLocalGitManager,at as createLocalServiceManager,_e as createMcpServer,ot as formatAnswersAsPrompt,te as getBackendFeaturesPath,F as getBackendPath,O as getDefaultConfig,X as getFrontendFeaturesPath,N as getFrontendPath,Je as getPathsDescription,ge as getServicesDescription,ue as getTechStackDescription,Ve as isBackendFile,ze as isFrontendFile,Ke as loadProjectConfig,ye as readPendingQuestions,pe as resolveConfigPath,nt as runAgent,Qe as saveProjectConfig,Oe as setPlanningTransport,Fe as setServiceManager};
package/dist/index.js CHANGED
@@ -15,14 +15,14 @@ Workflow:
15
15
  2. save_specification(name: "User Authentication", filePath: "/tmp/user-auth-spec.md")`,{name:le.string().describe("Specification name (e.g., 'User Authentication')"),filePath:le.string().describe("Path to the file containing the specification content (written via Write tool)")},async e=>{if(!M)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=Ze.resolve(e.filePath);if(!de.existsSync(t))return{content:[{type:"text",text:`\u274C File not found: ${e.filePath}. Use Write tool first to create the file.`}]};let n=de.readFileSync(t,"utf-8"),s=Fe(e.name);return await M.saveSpecification(s,n),{content:[{type:"text",text:`\u2705 Saved specification: ${s}.spec.md`}]}}),On=pe("read_specification","Read the content of a specification.",{name:le.string().describe("Name of the specification to read")},async e=>{if(!M)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=Fe(e.name),n=await M.getSpecification(t);return n?{content:[{type:"text",text:n}]}:{content:[{type:"text",text:`\u274C Specification "${e.name}" not found.`}]}}),Dn=pe("list_specifications","List all specifications.",{},async()=>{if(!M)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let e=await M.listSpecifications();return e.length===0?{content:[{type:"text",text:"\u{1F4CB} No specifications found."}]}:{content:[{type:"text",text:`\u{1F4CB} Specifications:
16
16
 
17
17
  ${e.map(n=>`- ${n.name} (${n.slug}.spec.md)`).join(`
18
- `)}`}]}}),Fn=pe("delete_specification","Delete a specification.",{name:le.string().describe("Name of the specification to delete")},async e=>{if(!M)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=Fe(e.name);return await M.deleteSpecification(t)?{content:[{type:"text",text:`\u{1F5D1}\uFE0F Deleted: ${t}.spec.md`}]}:{content:[{type:"text",text:`\u274C Specification "${e.name}" not found.`}]}});import{tool as q}from"@anthropic-ai/claude-agent-sdk";import{z as X}from"zod";var _=null;function ge(e){_=e}var et=q("restart_services",`Rebuild and restart services. Call this after making backend changes (.cs files) to apply them.
18
+ `)}`}]}}),Fn=pe("delete_specification","Delete a specification.",{name:le.string().describe("Name of the specification to delete")},async e=>{if(!M)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=Fe(e.name);return await M.deleteSpecification(t)?{content:[{type:"text",text:`\u{1F5D1}\uFE0F Deleted: ${t}.spec.md`}]}:{content:[{type:"text",text:`\u274C Specification "${e.name}" not found.`}]}});import{tool as q}from"@anthropic-ai/claude-agent-sdk";import{z as X}from"zod";var w=null;function ge(e){w=e}var et=q("restart_services",`Rebuild and restart services. Call this after making backend changes (.cs files) to apply them.
19
19
 
20
20
  Parameters:
21
21
  - target: "backend" (default) | "frontend" | "both"
22
22
 
23
23
  Use target="backend" after .cs file changes (faster, only restarts .NET).
24
24
  Use target="frontend" if Vite is stuck (rare, HMR usually works).
25
- Use target="both" if unsure or both need restart.`,{target:X.enum(["backend","frontend","both"]).default("backend").describe("Which service to restart")},async e=>{if(!_)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] restart_services called (target: ${t})`);let n=await _.restartServices(t);if(!n.success){let o=[];return n.backendError&&o.push(`Backend: ${n.backendError}`),n.frontendError&&o.push(`Frontend: ${n.frontendError}`),{content:[{type:"text",text:`\u274C Restart failed:
25
+ Use target="both" if unsure or both need restart.`,{target:X.enum(["backend","frontend","both"]).default("backend").describe("Which service to restart")},async e=>{if(!w)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] restart_services called (target: ${t})`);let n=await w.restartServices(t);if(!n.success){let o=[];return n.backendError&&o.push(`Backend: ${n.backendError}`),n.frontendError&&o.push(`Frontend: ${n.frontendError}`),{content:[{type:"text",text:`\u274C Restart failed:
26
26
  ${o.join(`
27
27
 
28
28
  `)}`}]}}return{content:[{type:"text",text:`\u2705 ${t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend"} restarted successfully.`}]}}),tt=q("stop_services",`Stop running services. Call this BEFORE running scaffold to prevent port conflicts and ensure clean migrations.
@@ -30,29 +30,29 @@ ${o.join(`
30
30
  Parameters:
31
31
  - target: "backend" (default) | "frontend" | "both"
32
32
 
33
- Use target="backend" before running scaffold (required for migrations).`,{target:X.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async e=>{if(!_)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] stop_services called (target: ${t})`);let n=t==="backend"||t==="both",s=t==="frontend"||t==="both";n&&await _.stopBackend(),s&&await _.stopFrontend();let o=5e3,r=Date.now(),i=!n,l=!s;for(;Date.now()-r<o;){let m=await _.checkHealth();if(n&&!m.api&&(i=!0),s&&!m.web&&(l=!0),i&&l)break;await new Promise(v=>setTimeout(v,500))}let h=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";return{content:[{type:"text",text:i&&l?`\u2705 ${h} stopped successfully.`:`\u26A0\uFE0F ${h} stop requested but health check still shows running. Proceeding anyway.`}]}}),nt=q("start_services",`Start services. Call this AFTER running scaffold to bring services back up.
33
+ Use target="backend" before running scaffold (required for migrations).`,{target:X.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async e=>{if(!w)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] stop_services called (target: ${t})`);let n=t==="backend"||t==="both",s=t==="frontend"||t==="both";n&&await w.stopBackend(),s&&await w.stopFrontend();let o=5e3,r=Date.now(),i=!n,l=!s;for(;Date.now()-r<o;){let m=await w.checkHealth();if(n&&!m.api&&(i=!0),s&&!m.web&&(l=!0),i&&l)break;await new Promise(v=>setTimeout(v,500))}let h=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";return{content:[{type:"text",text:i&&l?`\u2705 ${h} stopped successfully.`:`\u26A0\uFE0F ${h} stop requested but health check still shows running. Proceeding anyway.`}]}}),nt=q("start_services",`Start services. Call this AFTER running scaffold to bring services back up.
34
34
 
35
35
  Parameters:
36
36
  - target: "backend" (default) | "frontend" | "both"
37
37
 
38
- Use target="backend" after running scaffold.`,{target:X.enum(["backend","frontend","both"]).default("backend").describe("Which service to start")},async e=>{if(!_)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] start_services called (target: ${t})`);let n=t==="backend"||t==="both",s=t==="frontend"||t==="both";n&&await _.startBackend(),s&&await _.startFrontend();let o=await _.waitForHealth(15e3),r=!n||o.api,i=!s||o.web,l=r&&i,h=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";if(!l){let f=[];return n&&!o.api&&f.push("Backend failed to start"),s&&!o.web&&f.push("Frontend failed to start"),{content:[{type:"text",text:`\u274C ${h} failed to start: ${f.join(", ")}`}]}}return{content:[{type:"text",text:`\u2705 ${h} started successfully.`}]}}),ot=q("check_service_health",`Check if services are running and healthy. Returns current status of backend API and frontend.
39
- Note: This only checks if ports are responding, NOT build/type errors. Use check_backend_build or check_frontend_types for that.`,{},async()=>{if(!_)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=await _.checkHealth();return{content:[{type:"text",text:`Service Health:
38
+ Use target="backend" after running scaffold.`,{target:X.enum(["backend","frontend","both"]).default("backend").describe("Which service to start")},async e=>{if(!w)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] start_services called (target: ${t})`);let n=t==="backend"||t==="both",s=t==="frontend"||t==="both";n&&await w.startBackend(),s&&await w.startFrontend();let o=await w.waitForHealth(15e3),r=!n||o.api,i=!s||o.web,l=r&&i,h=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";if(!l){let f=[];return n&&!o.api&&f.push("Backend failed to start"),s&&!o.web&&f.push("Frontend failed to start"),{content:[{type:"text",text:`\u274C ${h} failed to start: ${f.join(", ")}`}]}}return{content:[{type:"text",text:`\u2705 ${h} started successfully.`}]}}),ot=q("check_service_health",`Check if services are running and healthy. Returns current status of backend API and frontend.
39
+ Note: This only checks if ports are responding, NOT build/type errors. Use check_backend_build or check_frontend_types for that.`,{},async()=>{if(!w)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=await w.checkHealth();return{content:[{type:"text",text:`Service Health:
40
40
  \u{1F5A5}\uFE0F Backend API: ${e.api?"\u2705 running":"\u274C not running"}
41
- \u{1F310} Frontend: ${e.web?"\u2705 running":"\u274C not running"}`}]}}),st=q("check_backend_build","Run dotnet build to check for C# compilation errors. Use this after fixing backend code to verify the fix works.",{},async()=>{if(!_)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};console.log("[MCP] check_backend_build called");let e=await _.checkBackendBuild();return e.success?{content:[{type:"text",text:"\u2705 Backend build succeeded - no errors"}]}:{content:[{type:"text",text:`\u274C Backend build failed:
41
+ \u{1F310} Frontend: ${e.web?"\u2705 running":"\u274C not running"}`}]}}),st=q("check_backend_build","Run dotnet build to check for C# compilation errors. Use this after fixing backend code to verify the fix works.",{},async()=>{if(!w)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};console.log("[MCP] check_backend_build called");let e=await w.checkBackendBuild();return e.success?{content:[{type:"text",text:"\u2705 Backend build succeeded - no errors"}]}:{content:[{type:"text",text:`\u274C Backend build failed:
42
42
 
43
- ${e.errors}`}]}}),rt=q("check_frontend_types","Run TypeScript type check (tsc) on frontend code. Use this after fixing frontend code to verify the fix works.",{},async()=>{if(!_)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};console.log("[MCP] check_frontend_types called");let e=await _.checkFrontendTypes();return e.success?{content:[{type:"text",text:"\u2705 Frontend type check passed - no errors"}]}:{content:[{type:"text",text:`\u274C Frontend type errors:
43
+ ${e.errors}`}]}}),rt=q("check_frontend_types","Run TypeScript type check (tsc) on frontend code. Use this after fixing frontend code to verify the fix works.",{},async()=>{if(!w)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};console.log("[MCP] check_frontend_types called");let e=await w.checkFrontendTypes();return e.success?{content:[{type:"text",text:"\u2705 Frontend type check passed - no errors"}]}:{content:[{type:"text",text:`\u274C Frontend type errors:
44
44
 
45
45
  ${e.errors}`}]}}),Ln=q("tail_service_log",`Get the last N lines of service logs. Use this to debug startup failures or verify that the application is running correctly.
46
46
 
47
47
  Parameters:
48
48
  - target: "backend" | "frontend"
49
- - lines: number (default 50)`,{target:X.enum(["backend","frontend"]).describe("Which log to read"),lines:X.number().default(50).describe("Number of lines to read")},async e=>{if(!_)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=await _.tailLogs(e.target,e.lines);return{content:[{type:"text",text:`Last ${e.lines} lines of ${e.target} log:
49
+ - lines: number (default 50)`,{target:X.enum(["backend","frontend"]).describe("Which log to read"),lines:X.number().default(50).describe("Number of lines to read")},async e=>{if(!w)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=await w.tailLogs(e.target,e.lines);return{content:[{type:"text",text:`Last ${e.lines} lines of ${e.target} log:
50
50
 
51
51
  ${t.join(`
52
52
  `)}`}]}}),jn=q("check_swagger_endpoints",`Search for endpoints in the generated swagger.json. Use this to verify that new backend endpoints are correctly exposed.
53
53
 
54
54
  Parameters:
55
- - pattern: string (e.g., "users", "api/reports")`,{pattern:X.string().describe("Text to search for in endpoint paths")},async e=>{if(!_)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=await _.checkSwaggerEndpoints(e.pattern);return t.foundEndpoints.length===0?{content:[{type:"text",text:`\u274C No endpoints found matching "${e.pattern}" (Total endpoints: ${t.totalEndpoints})`}]}:{content:[{type:"text",text:`\u2705 Found ${t.foundEndpoints.length} matching endpoints:
55
+ - pattern: string (e.g., "users", "api/reports")`,{pattern:X.string().describe("Text to search for in endpoint paths")},async e=>{if(!w)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=await w.checkSwaggerEndpoints(e.pattern);return t.foundEndpoints.length===0?{content:[{type:"text",text:`\u274C No endpoints found matching "${e.pattern}" (Total endpoints: ${t.totalEndpoints})`}]}:{content:[{type:"text",text:`\u2705 Found ${t.foundEndpoints.length} matching endpoints:
56
56
 
57
57
  ${t.foundEndpoints.join(`
58
58
  `)}`}]}});import*as W from"fs";import*as it from"path";var $t="/tmp";function te(e){let t=it.join($t,`agent-questions-${e}.json`);if(!W.existsSync(t))return null;try{let n=W.readFileSync(t,"utf-8");return W.unlinkSync(t),JSON.parse(n)}catch{return null}}import*as P from"fs";import*as fe from"path";function ne(e){let t=fe.join(e,".sdd","specifications");function n(){P.existsSync(t)||P.mkdirSync(t,{recursive:!0})}function s(o){return fe.join(t,`${o}.spec.md`)}return{async saveSpecification(o,r){n(),P.writeFileSync(s(o),r,"utf-8")},async getSpecification(o){n();let r=s(o);return P.existsSync(r)?P.readFileSync(r,"utf-8"):null},async listSpecifications(){return n(),P.readdirSync(t).filter(r=>r.endsWith(".spec.md")).map(r=>{let i=P.readFileSync(fe.join(t,r),"utf-8"),l=i.match(/name: "([^"]+)"/),h=i.match(/status: (\w+)/),f=i.match(/version: "([^"]+)"/);return{slug:r.replace(".spec.md",""),name:l?.[1]||r,status:h?.[1]||"unknown",version:f?.[1]||"1.0.0"}})},async deleteSpecification(o){n();let r=s(o);return P.existsSync(r)?(P.unlinkSync(r),!0):!1},async specificationExists(o){return n(),P.existsSync(s(o))}}}function Ue(){return Bt({name:"agent-insights",version:"1.0.0",tools:[et,tt,nt,ot,st,rt]})}import{query as rn}from"@anthropic-ai/claude-agent-sdk";var Ne={description:"Delegate to this agent when scaffolding new entities or creating CRUD features. This agent handles entity generation using the scaffold CLI with multi-entity support.",model:"inherit",prompt:`You are a scaffolding specialist. Your ONLY job is:
@@ -771,7 +771,7 @@ Before saving, verify your context:
771
771
  5. **Keep it scannable** - Use bullets, clear sections
772
772
  6. **Update, don't replace** - If context exists, improve it rather than starting fresh`};import*as Z from"fs";import*as ct from"path";var be=class{buffer=[];flushInterval=null;sessionId=null;outputDir;flushIntervalMs;constructor(t){this.outputDir=t.outputDir||"/tmp/agent-debug",this.flushIntervalMs=t.flushIntervalMs||3e3,t.enabled&&(this.ensureOutputDir(),this.startFlushInterval())}ensureOutputDir(){Z.existsSync(this.outputDir)||Z.mkdirSync(this.outputDir,{recursive:!0})}startFlushInterval(){this.flushInterval=setInterval(()=>{this.flush()},this.flushIntervalMs)}setSessionId(t){this.sessionId=t}log(t){let s={timestamp:new Date().toISOString(),message:t};this.buffer.push(JSON.stringify(s,null,2)+`
773
773
  ---
774
- `)}getLogFilePath(){let n=(this.sessionId||"unknown").replace(/[^a-zA-Z0-9-]/g,"_").slice(0,50),s=new Date().toISOString().split("T")[0];return ct.join(this.outputDir,`session-${s}-${n}.log`)}flush(){if(this.buffer.length===0)return;let t=this.buffer.join("");this.buffer=[];let n=this.getLogFilePath();Z.appendFileSync(n,t)}stop(){this.flushInterval&&(clearInterval(this.flushInterval),this.flushInterval=null),this.flush()}};function lt(e){return e?typeof e=="boolean"?e?new be({enabled:!0}):null:e.enabled?new be(e):null:null}import{z as d}from"zod";import*as je from"fs/promises";import*as K from"path";var pt=d.object({port:d.number(),startCommand:d.string(),buildCommand:d.string().optional(),typecheckCommand:d.string().optional(),logFile:d.string().optional(),healthEndpoint:d.string().optional(),extensions:d.array(d.string())}),qt=d.object({port:d.number(),type:d.enum(["postgres","mysql","sqlite","mongodb"])}),Wt=d.object({command:d.string(),schemaPath:d.string().optional()}),Gt=d.object({email:d.string().optional(),name:d.string().optional(),commitPrefix:d.string().optional()}),Yt=d.object({enabled:d.boolean(),port:d.number().optional()}),dt=d.object({version:d.literal("1.0"),paths:d.object({workspace:d.string(),backend:d.string().optional(),frontend:d.string().optional(),features:d.object({backend:d.string().optional(),frontend:d.string().optional()}).optional()}),services:d.object({backend:pt.optional(),frontend:pt.optional(),database:qt.optional()}).optional(),scaffold:Wt.optional(),git:Gt.optional(),techStack:d.object({backend:d.array(d.string()).optional(),frontend:d.array(d.string()).optional(),patterns:d.array(d.string()).optional()}).optional(),tunnel:Yt.optional()});function G(e){return{version:"1.0",paths:{workspace:e||process.env.WORKSPACE_DIR||"/workspace",backend:"packages/dotnet-api",frontend:"packages/backoffice-web",features:{backend:"Source/Features",frontend:"src/applications/super-admin/features"}},services:{backend:{port:5338,startCommand:"dotnet run",buildCommand:"dotnet build",typecheckCommand:"dotnet build --no-restore",logFile:"/tmp/api.log",healthEndpoint:"http://localhost:5338",extensions:[".cs",".csproj"]},frontend:{port:5173,startCommand:"npm run dev",typecheckCommand:"npx tsc -p tsconfig.app.json --noEmit",logFile:"/tmp/web.log",healthEndpoint:"http://localhost:5173",extensions:[".ts",".tsx",".json"]},database:{port:43594,type:"postgres"}},scaffold:{command:"scaffold --schema /tmp/scaffold.json --output /workspace --force --full",schemaPath:"/tmp/scaffold.json"},git:{email:"agent@dotnetmentor.se",name:"Agent",commitPrefix:"[agent]"},techStack:{backend:[".NET 9","PostgreSQL","MediatR (CQRS)","EF Core"],frontend:["React 19","TypeScript","Vite","MUI","TanStack Query"],patterns:["Vertical slices","CQRS","Soft delete","Timestamps"]},tunnel:{enabled:!0,port:5173}}}function we(e,t){if(t)return K.isAbsolute(t)?t:K.join(e.paths.workspace,t)}function Y(e){return we(e,e.paths.backend)}function H(e){return we(e,e.paths.frontend)}function _e(e){let t=Y(e);if(!(!t||!e.paths.features?.backend))return K.join(t,e.paths.features.backend)}function oe(e){let t=H(e);if(!(!t||!e.paths.features?.frontend))return K.join(t,e.paths.features.frontend)}function ke(e){let t=[];return e.techStack?.backend?.length&&t.push(`**Backend:** ${e.techStack.backend.join(", ")}`),e.techStack?.frontend?.length&&t.push(`**Frontend:** ${e.techStack.frontend.join(", ")}`),e.techStack?.patterns?.length&&t.push(`**Patterns:** ${e.techStack.patterns.join(", ")}`),t.join(`
774
+ `)}getLogFilePath(){let n=(this.sessionId||"unknown").replace(/[^a-zA-Z0-9-]/g,"_").slice(0,50),s=new Date().toISOString().split("T")[0];return ct.join(this.outputDir,`session-${s}-${n}.log`)}flush(){if(this.buffer.length===0)return;let t=this.buffer.join("");this.buffer=[];let n=this.getLogFilePath();Z.appendFileSync(n,t)}stop(){this.flushInterval&&(clearInterval(this.flushInterval),this.flushInterval=null),this.flush()}};function lt(e){return e?typeof e=="boolean"?e?new be({enabled:!0}):null:e.enabled?new be(e):null:null}import{z as d}from"zod";import*as je from"fs/promises";import*as K from"path";var pt=d.object({port:d.number(),startCommand:d.string(),buildCommand:d.string().optional(),typecheckCommand:d.string().optional(),logFile:d.string().optional(),healthEndpoint:d.string().optional(),extensions:d.array(d.string())}),qt=d.object({port:d.number(),type:d.enum(["postgres","mysql","sqlite","mongodb"])}),Wt=d.object({command:d.string(),schemaPath:d.string().optional()}),Gt=d.object({email:d.string().optional(),name:d.string().optional(),commitPrefix:d.string().optional()}),Yt=d.object({enabled:d.boolean(),port:d.number().optional()}),dt=d.object({version:d.literal("1.0"),paths:d.object({workspace:d.string(),backend:d.string().optional(),frontend:d.string().optional(),features:d.object({backend:d.string().optional(),frontend:d.string().optional()}).optional()}),services:d.object({backend:pt.optional(),frontend:pt.optional(),database:qt.optional()}).optional(),scaffold:Wt.optional(),git:Gt.optional(),techStack:d.object({backend:d.array(d.string()).optional(),frontend:d.array(d.string()).optional(),patterns:d.array(d.string()).optional()}).optional(),tunnel:Yt.optional()});function G(e){return{version:"1.0",paths:{workspace:e||process.env.WORKSPACE_DIR||"/workspace",backend:"packages/dotnet-api",frontend:"packages/backoffice-web",features:{backend:"Source/Features",frontend:"src/applications/super-admin/features"}},services:{backend:{port:5338,startCommand:"dotnet run",buildCommand:"dotnet build",typecheckCommand:"dotnet build --no-restore",logFile:"/tmp/api.log",healthEndpoint:"http://localhost:5338",extensions:[".cs",".csproj"]},frontend:{port:5173,startCommand:"npm run dev",typecheckCommand:"npx tsc -p tsconfig.app.json --noEmit",logFile:"/tmp/web.log",healthEndpoint:"http://localhost:5173",extensions:[".ts",".tsx",".json"]},database:{port:43594,type:"postgres"}},scaffold:{command:"scaffold --schema /tmp/scaffold.json --output /workspace --force --full",schemaPath:"/tmp/scaffold.json"},git:{email:"agent@dotnetmentor.se",name:"Agent",commitPrefix:"[agent]"},techStack:{backend:[".NET 9","PostgreSQL","MediatR (CQRS)","EF Core"],frontend:["React 19","TypeScript","Vite","MUI","TanStack Query"],patterns:["Vertical slices","CQRS","Soft delete","Timestamps"]},tunnel:{enabled:!0,port:5173}}}function _e(e,t){if(t)return K.isAbsolute(t)?t:K.join(e.paths.workspace,t)}function Y(e){return _e(e,e.paths.backend)}function H(e){return _e(e,e.paths.frontend)}function we(e){let t=Y(e);if(!(!t||!e.paths.features?.backend))return K.join(t,e.paths.features.backend)}function oe(e){let t=H(e);if(!(!t||!e.paths.features?.frontend))return K.join(t,e.paths.features.frontend)}function ke(e){let t=[];return e.techStack?.backend?.length&&t.push(`**Backend:** ${e.techStack.backend.join(", ")}`),e.techStack?.frontend?.length&&t.push(`**Frontend:** ${e.techStack.frontend.join(", ")}`),e.techStack?.patterns?.length&&t.push(`**Patterns:** ${e.techStack.patterns.join(", ")}`),t.join(`
775
775
  `)}function Se(e){let t=[];return e.services?.backend&&t.push(`| Backend | ${Y(e)} | ${e.services.backend.port} | ${e.services.backend.logFile||"N/A"} |`),e.services?.frontend&&t.push(`| Frontend | ${H(e)} | ${e.services.frontend.port} | ${e.services.frontend.logFile||"N/A"} |`),e.services?.database&&t.push(`| Database (${e.services.database.type}) | localhost | ${e.services.database.port} | - |`),t.length===0?"":`| Service | Location | Port | Logs |
776
776
  |---------|----------|------|------|
777
777
  ${t.join(`
@@ -964,7 +964,7 @@ ${O}
964
964
  ${T}
965
965
 
966
966
  ${S}
967
- `}function Jt(e){let t=ke(e),n=Y(e),s=H(e),o=_e(e),r=oe(e),i=[];o&&i.push(`- Backend: "${o}/{Entity}/"`),r&&i.push(`- Frontend: "${r}/{entity}/"`);let l=e.techStack?.patterns?.length?`
967
+ `}function Jt(e){let t=ke(e),n=Y(e),s=H(e),o=we(e),r=oe(e),i=[];o&&i.push(`- Backend: "${o}/{Entity}/"`),r&&i.push(`- Frontend: "${r}/{entity}/"`);let l=e.techStack?.patterns?.length?`
968
968
  **Key patterns:**
969
969
  ${e.techStack.patterns.map(m=>`- ${m}`).join(`
970
970
  `)}`:"",f=e.techStack?.backend?.some(m=>m.includes(".NET")||m.includes("C#"))??!1?`
@@ -1257,16 +1257,16 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
1257
1257
  - **Multi-tab apps**: Use \`browser_tabs\` to manage multiple windows/tabs in legacy applications
1258
1258
  - **Large snapshots**: For pages with huge dropdowns (e.g., country lists), use \`browser_evaluate\` to collapse them first
1259
1259
  - ${t?"In local mode, the browser window will be visible to you":"In container mode, browser runs headless"}
1260
- </ui-verification>`}function gt(e){return e&&e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var sn=null;function ft(){return gt(sn)}import*as V from"path";import*as Be from"fs";import{fileURLToPath as an}from"url";var cn=Ue(),$e=V.dirname(an(import.meta.url)),mt=[V.resolve($e,"../mcps/stdio-server.js"),V.resolve($e,"./adapters/mcps/stdio-server.js")],se=mt.find(e=>Be.existsSync(e))||mt[0];console.log("[MCP] Stdio server path resolved:",se);console.log("[MCP] __dirname:",$e);console.log("[MCP] File exists:",Be.existsSync(se));async function ve(e,t={}){let{model:n,sessionId:s=null,projectId:o=null,apiUrl:r=null,callbacks:i={},debugLog:l,abortController:h,debugMode:f=!1,isLocal:m=process.env.LOCAL_MODE==="true",projectConfig:v=G(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:T=!0}=t,S=lt(l),O=new Set,y=s||"",R,U,x,$=!1,ee=!1,N=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[se]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",o||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",N),console.log("[MCP] File exists check:",se);try{for await(let u of rn({prompt:e,options:{abortController:h,cwd:N,resume:s??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","Skill","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...m?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_fill_form","mcp__playwright__browser_select_option","mcp__playwright__browser_press_key","mcp__playwright__browser_hover","mcp__playwright__browser_drag","mcp__playwright__browser_file_upload","mcp__playwright__browser_handle_dialog","mcp__playwright__browser_close","mcp__playwright__browser_resize","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_screen_capture","mcp__playwright__browser_screen_move_mouse","mcp__playwright__browser_screen_click","mcp__playwright__browser_screen_drag","mcp__playwright__browser_screen_type","mcp__playwright__browser_evaluate","mcp__playwright__browser_run_code","mcp__playwright__browser_tabs","mcp__playwright__browser_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:n,mcpServers:{"agent-insights":cn,"agent-planning":{type:"stdio",command:"node",args:[se],env:{WORKSPACE_DIR:N,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:o||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${V.join(N,".playwright-data")}`,...m?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:T?{scaffolding:Ne,debugger:me,planning:he,backend:Me,frontend:Le,"project-context":ye}:{debugger:me,planning:he,"project-context":ye},systemPrompt:ut({debugMode:f,projectPrompt:ft(),isLocal:m,projectConfig:v,useDefaultStack:T})}}))if(!(!u.uuid||O.has(u.uuid))){switch(O.add(u.uuid),S?.log(u),i.onRawMessage?.(u),u.type){case"assistant":if(u.message?.content)for(let E of u.message.content)E.type==="text"?i.onAssistantText?.(E.text):E.type==="tool_use"?i.onAssistantToolUse?.(E.name,E.input):E.type==="tool_result"&&i.onAssistantToolResult?.(E.tool_use_id,E.content);break;case"user":i.onUserMessage?.(u);break;case"result":if(u.subtype==="success")R=u.result,U=u.total_cost_usd,i.onResult?.(u.result,u.total_cost_usd);else{let E=`Agent error: ${u.subtype}`;x=E,i.onError?.(E)}ee=!0;break;case"system":switch(u.subtype){case"init":y=u.session_id,S?.setSessionId(u.session_id),i.onSystemInit?.(u.session_id);break;case"status":i.onSystemStatus?.(JSON.stringify(u));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":i.onStreamEvent?.(u);break;case"tool_progress":i.onToolProgress?.(u);break;case"auth_status":i.onAuthStatus?.(u);break}if(ee)break}}catch(u){u instanceof Error&&u.name==="AbortError"||u instanceof Error&&u.message.includes("aborted by user")?($=!0,i.onAborted?.()):(x=u instanceof Error?u.message:String(u),i.onError?.(x))}finally{S?.stop()}return console.log("after try"),{sessionId:y,result:R,cost:U,error:x,aborted:$}}function Ce(e){return`User answered the questions:
1260
+ </ui-verification>`}function gt(e){return e&&e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var sn=null;function ft(){return gt(sn)}import*as V from"path";import*as Be from"fs";import{fileURLToPath as an}from"url";var cn=Ue(),$e=V.dirname(an(import.meta.url)),mt=[V.resolve($e,"../mcps/stdio-server.js"),V.resolve($e,"./adapters/mcps/stdio-server.js")],se=mt.find(e=>Be.existsSync(e))||mt[0];console.log("[MCP] Stdio server path resolved:",se);console.log("[MCP] __dirname:",$e);console.log("[MCP] File exists:",Be.existsSync(se));async function ve(e,t={}){let{model:n,sessionId:s=null,projectId:o=null,apiUrl:r=null,callbacks:i={},debugLog:l,abortController:h,debugMode:f=!1,isLocal:m=process.env.LOCAL_MODE==="true",projectConfig:v=G(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:T=!0}=t,S=lt(l),O=new Set,y=s||"",R,U,x,$=!1,ee=!1,N=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[se]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",o||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",N),console.log("[MCP] File exists check:",se);try{for await(let u of rn({prompt:e,options:{abortController:h,cwd:N,resume:s??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","Skill","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...m?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__agent-planning__reference_projects_list","mcp__agent-planning__reference_project_tree","mcp__agent-planning__reference_project_read_file","mcp__agent-planning__reference_project_download","mcp__agent-planning__reference_project_update_status","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_fill_form","mcp__playwright__browser_select_option","mcp__playwright__browser_press_key","mcp__playwright__browser_hover","mcp__playwright__browser_drag","mcp__playwright__browser_file_upload","mcp__playwright__browser_handle_dialog","mcp__playwright__browser_close","mcp__playwright__browser_resize","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_screen_capture","mcp__playwright__browser_screen_move_mouse","mcp__playwright__browser_screen_click","mcp__playwright__browser_screen_drag","mcp__playwright__browser_screen_type","mcp__playwright__browser_evaluate","mcp__playwright__browser_run_code","mcp__playwright__browser_tabs","mcp__playwright__browser_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:n,mcpServers:{"agent-insights":cn,"agent-planning":{type:"stdio",command:"node",args:[se],env:{WORKSPACE_DIR:N,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:o||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${V.join(N,".playwright-data")}`,...m?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:T?{scaffolding:Ne,debugger:me,planning:he,backend:Me,frontend:Le,"project-context":ye}:{debugger:me,planning:he,"project-context":ye},systemPrompt:ut({debugMode:f,projectPrompt:ft(),isLocal:m,projectConfig:v,useDefaultStack:T})}}))if(!(!u.uuid||O.has(u.uuid))){switch(O.add(u.uuid),S?.log(u),i.onRawMessage?.(u),u.type){case"assistant":if(u.message?.content)for(let E of u.message.content)E.type==="text"?i.onAssistantText?.(E.text):E.type==="tool_use"?i.onAssistantToolUse?.(E.name,E.input):E.type==="tool_result"&&i.onAssistantToolResult?.(E.tool_use_id,E.content);break;case"user":i.onUserMessage?.(u);break;case"result":if(u.subtype==="success")R=u.result,U=u.total_cost_usd,i.onResult?.(u.result,u.total_cost_usd);else{let E=`Agent error: ${u.subtype}`;x=E,i.onError?.(E)}ee=!0;break;case"system":switch(u.subtype){case"init":y=u.session_id,S?.setSessionId(u.session_id),i.onSystemInit?.(u.session_id);break;case"status":i.onSystemStatus?.(JSON.stringify(u));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":i.onStreamEvent?.(u);break;case"tool_progress":i.onToolProgress?.(u);break;case"auth_status":i.onAuthStatus?.(u);break}if(ee)break}}catch(u){u instanceof Error&&u.name==="AbortError"||u instanceof Error&&u.message.includes("aborted by user")?($=!0,i.onAborted?.()):(x=u instanceof Error?u.message:String(u),i.onError?.(x))}finally{S?.stop()}return console.log("after try"),{sessionId:y,result:R,cost:U,error:x,aborted:$}}function Ce(e){return`User answered the questions:
1261
1261
 
1262
1262
  ${Object.entries(e).map(([n,s])=>{let o=Array.isArray(s)?s.join(", "):s;return`${n}: ${o}`}).join(`
1263
1263
  `)}`}import{spawn as ht,execSync as z}from"child_process";import*as k from"fs";import*as yt from"http";import*as Q from"path";function Te(e){let{workspaceDir:t,apiPort:n,webPort:s,onBackendError:o,onFrontendError:r,projectConfig:i=G(t)}=e,l=n??i.services?.backend?.port??5338,h=s??i.services?.frontend?.port??5173,f=Y(i),m=H(i),v=i.services?.backend?.startCommand??"dotnet run",T=i.services?.backend?.buildCommand??"dotnet build",S=i.services?.backend?.typecheckCommand??"dotnet build --no-restore",O=i.services?.frontend?.startCommand??"npm run dev",y=i.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",R=i.services?.backend?.logFile??"/tmp/api.log",U=i.services?.frontend?.logFile??"/tmp/web.log",x=null,$=null,ee=p=>new Promise(c=>{let g=setTimeout(()=>c(!1),3e3);yt.get(`http://localhost:${p}`,A=>{clearTimeout(g),c(A.statusCode!==void 0&&A.statusCode<500)}).on("error",()=>{clearTimeout(g),c(!1)})}),N=p=>{try{let c=z(`lsof -ti:${p} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(c){let g=c.split(`
1264
- `).filter(Boolean);console.log(`[Services] Killing ${g.length} process(es) on port ${p}: ${g.join(", ")}`);for(let b of g)try{process.kill(parseInt(b,10),"SIGKILL")}catch{}}}catch{try{z(`fuser -k ${p}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},u=(p,c)=>new Promise(g=>{if(!p||!p.pid){g();return}console.log(`[Services] Stopping ${c} (PID: ${p.pid})...`);try{process.kill(-p.pid,"SIGTERM")}catch{try{p.kill("SIGTERM")}catch{}}setTimeout(g,500)}),E=async()=>{if(!f||!k.existsSync(f)){console.log("[Services] No backend found, skipping backend start");return}N(l),console.log(`[Services] Starting backend with: ${v}...`);let p=Q.dirname(R);k.existsSync(p)||k.mkdirSync(p,{recursive:!0});let c=k.createWriteStream(R,{flags:"a"}),[g,...b]=v.split(" ");x=ht(g,b,{cwd:f,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let A="",C=w=>{let B=w.toString();c.write(B);let D=(A+B).split(`
1265
- `);A=D.pop()||"";for(let F of D)F&&ln(F)&&o&&o(F)};x.stdout?.on("data",C),x.stderr?.on("data",C),x.on("exit",w=>{c.end(),w!==0&&w!==null&&o&&o(`Process exited with code ${w}`)}),x.unref()},He=async()=>{if(!m||!k.existsSync(Q.join(m,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}N(h),console.log(`[Services] Starting frontend with: ${O}...`);let p=Q.dirname(U);k.existsSync(p)||k.mkdirSync(p,{recursive:!0});let[c,...g]=O.split(" ");$=ht(c,g,{cwd:m,stdio:["ignore",k.openSync(U,"w"),k.openSync(U,"w")],detached:!0,shell:!0}),$.unref()},Qe=async()=>{await u(x,"backend"),x=null,N(l)},Ke=async()=>{await u($,"frontend"),$=null,N(h)},De=async()=>{let[p,c]=await Promise.all([ee(l),ee(h)]);return{api:p,web:c}},Ve=async(p=15e3)=>{let c=Date.now(),g=1e3,b=0,A=null,C=null;for(console.log(`[Services] Waiting for health (timeout: ${p}ms)...`);Date.now()-c<p;){b++;let D=await De(),F=Date.now()-c;if(D.web&&C===null&&(C=F,console.log(`[Services] \u2713 Frontend (Vite) ready after ${F}ms`)),D.api&&A===null&&(A=F,console.log(`[Services] \u2713 Backend (dotnet) ready after ${F}ms`)),console.log(`[Services] Health poll #${b} (${F}ms): api=${D.api}, web=${D.web}`),D.api&&D.web)return console.log(`[Services] \u2713 Both services healthy after ${F}ms (${b} polls)`),D;await new Promise(jt=>setTimeout(jt,g))}let w=await De(),B=Date.now()-c;return w.web&&C===null&&console.log(`[Services] \u2713 Frontend (Vite) ready after ${B}ms`),w.api&&A===null&&console.log(`[Services] \u2713 Backend (dotnet) ready after ${B}ms`),console.log(`[Services] \u26A0 Health timeout after ${B}ms: api=${w.api}, web=${w.web}`),w},Mt=async p=>{console.log(`[Services] Restarting ${p}...`);let c={success:!0},g=p==="backend"||p==="both",b=p==="frontend"||p==="both";if(g&&await Qe(),b&&await Ke(),g&&f&&k.existsSync(f)){console.log(`[Services] Building backend with: ${T}...`);try{z(T,{cwd:f,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),console.log("[Services] \u2713 Backend build succeeded")}catch(C){let w=C;c.success=!1,c.backendError=w.stderr||w.stdout||"Build failed",console.error("[Services] \u2717 Backend build failed")}}if(b&&m&&k.existsSync(Q.join(m,"package.json"))){console.log(`[Services] Type-checking frontend with: ${y}...`);try{z(y,{cwd:m,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),console.log("[Services] \u2713 Frontend types OK")}catch(C){let w=C;c.success=!1,c.frontendError=w.stderr||w.stdout||"Type check failed",console.error("[Services] \u2717 Frontend type check failed")}}console.log(`[Services] Starting ${p}...`),g&&await E(),b&&await He();let A=await Ve(1e4);if(g&&!A.api){c.success=!1;let C=await Xe("backend",20),w=C.length>0?`
1264
+ `).filter(Boolean);console.log(`[Services] Killing ${g.length} process(es) on port ${p}: ${g.join(", ")}`);for(let b of g)try{process.kill(parseInt(b,10),"SIGKILL")}catch{}}}catch{try{z(`fuser -k ${p}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},u=(p,c)=>new Promise(g=>{if(!p||!p.pid){g();return}console.log(`[Services] Stopping ${c} (PID: ${p.pid})...`);try{process.kill(-p.pid,"SIGTERM")}catch{try{p.kill("SIGTERM")}catch{}}setTimeout(g,500)}),E=async()=>{if(!f||!k.existsSync(f)){console.log("[Services] No backend found, skipping backend start");return}N(l),console.log(`[Services] Starting backend with: ${v}...`);let p=Q.dirname(R);k.existsSync(p)||k.mkdirSync(p,{recursive:!0});let c=k.createWriteStream(R,{flags:"a"}),[g,...b]=v.split(" ");x=ht(g,b,{cwd:f,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let A="",C=_=>{let B=_.toString();c.write(B);let D=(A+B).split(`
1265
+ `);A=D.pop()||"";for(let F of D)F&&ln(F)&&o&&o(F)};x.stdout?.on("data",C),x.stderr?.on("data",C),x.on("exit",_=>{c.end(),_!==0&&_!==null&&o&&o(`Process exited with code ${_}`)}),x.unref()},He=async()=>{if(!m||!k.existsSync(Q.join(m,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}N(h),console.log(`[Services] Starting frontend with: ${O}...`);let p=Q.dirname(U);k.existsSync(p)||k.mkdirSync(p,{recursive:!0});let[c,...g]=O.split(" ");$=ht(c,g,{cwd:m,stdio:["ignore",k.openSync(U,"w"),k.openSync(U,"w")],detached:!0,shell:!0}),$.unref()},Qe=async()=>{await u(x,"backend"),x=null,N(l)},Ke=async()=>{await u($,"frontend"),$=null,N(h)},De=async()=>{let[p,c]=await Promise.all([ee(l),ee(h)]);return{api:p,web:c}},Ve=async(p=15e3)=>{let c=Date.now(),g=1e3,b=0,A=null,C=null;for(console.log(`[Services] Waiting for health (timeout: ${p}ms)...`);Date.now()-c<p;){b++;let D=await De(),F=Date.now()-c;if(D.web&&C===null&&(C=F,console.log(`[Services] \u2713 Frontend (Vite) ready after ${F}ms`)),D.api&&A===null&&(A=F,console.log(`[Services] \u2713 Backend (dotnet) ready after ${F}ms`)),console.log(`[Services] Health poll #${b} (${F}ms): api=${D.api}, web=${D.web}`),D.api&&D.web)return console.log(`[Services] \u2713 Both services healthy after ${F}ms (${b} polls)`),D;await new Promise(jt=>setTimeout(jt,g))}let _=await De(),B=Date.now()-c;return _.web&&C===null&&console.log(`[Services] \u2713 Frontend (Vite) ready after ${B}ms`),_.api&&A===null&&console.log(`[Services] \u2713 Backend (dotnet) ready after ${B}ms`),console.log(`[Services] \u26A0 Health timeout after ${B}ms: api=${_.api}, web=${_.web}`),_},Mt=async p=>{console.log(`[Services] Restarting ${p}...`);let c={success:!0},g=p==="backend"||p==="both",b=p==="frontend"||p==="both";if(g&&await Qe(),b&&await Ke(),g&&f&&k.existsSync(f)){console.log(`[Services] Building backend with: ${T}...`);try{z(T,{cwd:f,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),console.log("[Services] \u2713 Backend build succeeded")}catch(C){let _=C;c.success=!1,c.backendError=_.stderr||_.stdout||"Build failed",console.error("[Services] \u2717 Backend build failed")}}if(b&&m&&k.existsSync(Q.join(m,"package.json"))){console.log(`[Services] Type-checking frontend with: ${y}...`);try{z(y,{cwd:m,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),console.log("[Services] \u2713 Frontend types OK")}catch(C){let _=C;c.success=!1,c.frontendError=_.stderr||_.stdout||"Type check failed",console.error("[Services] \u2717 Frontend type check failed")}}console.log(`[Services] Starting ${p}...`),g&&await E(),b&&await He();let A=await Ve(1e4);if(g&&!A.api){c.success=!1;let C=await Xe("backend",20),_=C.length>0?`
1266
1266
  Recent logs:
1267
1267
  ${C.join(`
1268
- `)}`:"";c.backendError||(c.backendError=`Backend failed to start.${w}`)}return b&&!A.web&&(c.success=!1,c.frontendError||(c.frontendError="Frontend failed to start")),c},ze=async()=>{if(!f||!k.existsSync(f))return{success:!0};try{return z(S,{cwd:f,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),{success:!0}}catch(p){let c=p;return{success:!1,errors:c.stdout||c.stderr||"Build failed"}}},Je=async()=>{if(!m||!k.existsSync(Q.join(m,"package.json")))return{success:!0};try{return z(y,{cwd:m,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),{success:!0}}catch(p){let c=p;return{success:!1,errors:c.stdout||c.stderr||"Type check failed"}}},Lt=async()=>{let[p,c]=await Promise.all([ze(),Je()]);return{backend:p,frontend:c}},Xe=async(p,c)=>{let g=p==="backend"?R:U;if(!k.existsSync(g))return[`Log file not found: ${g}`];try{return z(`tail -n ${c} ${g}`,{encoding:"utf-8"}).split(`
1269
- `).filter(Boolean)}catch(b){return[`Error reading logs: ${b instanceof Error?b.message:String(b)}`]}};return{startBackend:E,startFrontend:He,stopBackend:Qe,stopFrontend:Ke,restartServices:Mt,checkHealth:De,waitForHealth:Ve,getProcesses:()=>({backend:x,frontend:$}),checkBackendBuild:ze,checkFrontendTypes:Je,runTypeChecks:Lt,tailLogs:Xe,checkSwaggerEndpoints:async p=>{let c=Q.join(t,"swagger.json");if(!k.existsSync(c))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let g=JSON.parse(k.readFileSync(c,"utf-8")),b=Object.keys(g.paths||{}),A=[];for(let C of b)if(C.toLowerCase().includes(p.toLowerCase())){let w=Object.keys(g.paths[C]);for(let B of w)A.push(`${B.toUpperCase()} ${C}`)}return{foundEndpoints:A,totalEndpoints:b.length}}catch(g){return{foundEndpoints:[`Error parsing swagger.json: ${g instanceof Error?g.message:String(g)}`],totalEndpoints:0}}}}}function ln(e){let t=e.toLowerCase();return t.includes("exception")||t.includes("fail:")||t.includes("[err]")||t.includes("[error]")}import{execSync as J}from"child_process";function Pe(e){let t=async()=>{try{return J("git status --porcelain",{cwd:e,encoding:"utf-8"}).trim().length>0}catch{return!1}},n=async()=>{try{return J("git branch --show-current",{cwd:e,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:t,commitAndPush:async(o,r)=>{try{if(!await t())return{success:!0,noChanges:!0};let i=o.length>60?o.substring(0,60)+"...":o,h=`${r?"[agent] (partial)":"[agent]"} ${i}`;J("git add -A",{cwd:e}),J(`git commit -m "${h.replace(/"/g,'\\"')}"`,{cwd:e,encoding:"utf-8"});let f=J("git rev-parse --short HEAD",{cwd:e,encoding:"utf-8"}).trim();try{J("git push",{cwd:e,stdio:"pipe"})}catch(m){let v=m instanceof Error?m.message:String(m);if(v.includes("no upstream branch")){let T=await n();J(`git push --set-upstream origin ${T}`,{cwd:e,stdio:"pipe"})}else return console.error("[Git] Push failed:",v),{success:!0,commitHash:f,error:`Committed but push failed: ${v}`}}return{success:!0,commitHash:f}}catch(i){let l=i instanceof Error?i.message:String(i);return console.error("[Git] Error:",l),{success:!1,error:l}}},getCurrentBranch:n}}import ie from"inquirer";var re="\u2192 Other (type custom answer)";async function Ee(e){let t={};for(let n of e){let s=await pn(n);t[n.id]=s}return t}async function pn(e){return e.multiSelect&&e.options.length>0?dn(e):e.options.length>0?un(e):gn(e)}async function dn(e){let t=[...e.options];e.allowFreeText&&t.push(re);let{selection:n}=await ie.prompt([{type:"checkbox",name:"selection",message:e.question,choices:t}]);if(n.includes(re)){let{customAnswer:s}=await ie.prompt([{type:"input",name:"customAnswer",message:"Enter your custom answer:"}]);return[...n.filter(r=>r!==re),s]}return n}async function un(e){let t=[...e.options];e.allowFreeText&&t.push(re);let{selection:n}=await ie.prompt([{type:"select",name:"selection",message:e.question,choices:t}]);if(n===re){let{customAnswer:s}=await ie.prompt([{type:"input",name:"customAnswer",message:"Enter your custom answer:"}]);return s}return n}async function gn(e){let{answer:t}=await ie.prompt([{type:"input",name:"answer",message:e.question}]);return t}import*as L from"fs";import*as Ae from"path";var fn=".sdd/cli-sessions.json";function qe(){return Ae.join(process.cwd(),fn)}function mn(){let e=Ae.dirname(qe());L.existsSync(e)||L.mkdirSync(e,{recursive:!0})}function We(){try{let e=qe();if(!L.existsSync(e))return[];let t=L.readFileSync(e,"utf-8");return JSON.parse(t).sort((s,o)=>new Date(o.lastUsedAt).getTime()-new Date(s.lastUsedAt).getTime())}catch{return[]}}function bt(e){mn();let t=We(),n=t.findIndex(o=>o.sessionId===e.sessionId);n>=0?t[n]={...t[n],lastUsedAt:e.lastUsedAt,turnCount:e.turnCount}:t.unshift(e);let s=t.slice(0,20);L.writeFileSync(qe(),JSON.stringify(s,null,2))}function wt(e){let t=e.firstPrompt.length>50?e.firstPrompt.slice(0,50)+"...":e.firstPrompt,n=new Date(e.lastUsedAt),s=hn(n);return`${t} (${s}, ${e.turnCount} turns)`}function hn(e){let n=new Date().getTime()-e.getTime(),s=Math.floor(n/6e4),o=Math.floor(n/36e5),r=Math.floor(n/864e5);return s<1?"just now":s<60?`${s}m ago`:o<24?`${o}h ago`:r<7?`${r}d ago`:e.toLocaleDateString()}var _t={reset:"\x1B[0m",dim:"\x1B[2m",bold:"\x1B[1m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",red:"\x1B[31m",gray:"\x1B[90m"};function a(e,t){return`${_t[e]}${t}${_t.reset}`}var yn={Bash:"\u26A1",Read:"\u{1F4D6}",Write:"\u270F\uFE0F",Edit:"\u{1F527}",MultiEdit:"\u{1F527}",Glob:"\u{1F50D}",Grep:"\u{1F50E}",LS:"\u{1F4C1}",WebFetch:"\u{1F310}",NotebookRead:"\u{1F4D3}",NotebookEdit:"\u{1F4DD}",Task:"\u{1F4CB}",ask_questions:"\u2753",save_specification:"\u{1F4C4}",list_specifications:"\u{1F4CB}",read_specification:"\u{1F4D6}",delete_specification:"\u{1F5D1}\uFE0F",scaffold_entity:"\u{1F3D7}\uFE0F"};function bn(e){return yn[e]||"\u{1F527}"}function ae(e,t){return e.length<=t?e:e.slice(0,t-3)+"..."}function kt(e){let t=e.split("/");return t.length<=3?e:".../"+t.slice(-2).join("/")}function Ge(e){console.log(`
1268
+ `)}`:"";c.backendError||(c.backendError=`Backend failed to start.${_}`)}return b&&!A.web&&(c.success=!1,c.frontendError||(c.frontendError="Frontend failed to start")),c},ze=async()=>{if(!f||!k.existsSync(f))return{success:!0};try{return z(S,{cwd:f,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),{success:!0}}catch(p){let c=p;return{success:!1,errors:c.stdout||c.stderr||"Build failed"}}},Je=async()=>{if(!m||!k.existsSync(Q.join(m,"package.json")))return{success:!0};try{return z(y,{cwd:m,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),{success:!0}}catch(p){let c=p;return{success:!1,errors:c.stdout||c.stderr||"Type check failed"}}},Lt=async()=>{let[p,c]=await Promise.all([ze(),Je()]);return{backend:p,frontend:c}},Xe=async(p,c)=>{let g=p==="backend"?R:U;if(!k.existsSync(g))return[`Log file not found: ${g}`];try{return z(`tail -n ${c} ${g}`,{encoding:"utf-8"}).split(`
1269
+ `).filter(Boolean)}catch(b){return[`Error reading logs: ${b instanceof Error?b.message:String(b)}`]}};return{startBackend:E,startFrontend:He,stopBackend:Qe,stopFrontend:Ke,restartServices:Mt,checkHealth:De,waitForHealth:Ve,getProcesses:()=>({backend:x,frontend:$}),checkBackendBuild:ze,checkFrontendTypes:Je,runTypeChecks:Lt,tailLogs:Xe,checkSwaggerEndpoints:async p=>{let c=Q.join(t,"swagger.json");if(!k.existsSync(c))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let g=JSON.parse(k.readFileSync(c,"utf-8")),b=Object.keys(g.paths||{}),A=[];for(let C of b)if(C.toLowerCase().includes(p.toLowerCase())){let _=Object.keys(g.paths[C]);for(let B of _)A.push(`${B.toUpperCase()} ${C}`)}return{foundEndpoints:A,totalEndpoints:b.length}}catch(g){return{foundEndpoints:[`Error parsing swagger.json: ${g instanceof Error?g.message:String(g)}`],totalEndpoints:0}}}}}function ln(e){let t=e.toLowerCase();return t.includes("exception")||t.includes("fail:")||t.includes("[err]")||t.includes("[error]")}import{execSync as J}from"child_process";function Pe(e){let t=async()=>{try{return J("git status --porcelain",{cwd:e,encoding:"utf-8"}).trim().length>0}catch{return!1}},n=async()=>{try{return J("git branch --show-current",{cwd:e,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:t,commitAndPush:async(o,r)=>{try{if(!await t())return{success:!0,noChanges:!0};let i=o.length>60?o.substring(0,60)+"...":o,h=`${r?"[agent] (partial)":"[agent]"} ${i}`;J("git add -A",{cwd:e}),J(`git commit -m "${h.replace(/"/g,'\\"')}"`,{cwd:e,encoding:"utf-8"});let f=J("git rev-parse --short HEAD",{cwd:e,encoding:"utf-8"}).trim();try{J("git push",{cwd:e,stdio:"pipe"})}catch(m){let v=m instanceof Error?m.message:String(m);if(v.includes("no upstream branch")){let T=await n();J(`git push --set-upstream origin ${T}`,{cwd:e,stdio:"pipe"})}else return console.error("[Git] Push failed:",v),{success:!0,commitHash:f,error:`Committed but push failed: ${v}`}}return{success:!0,commitHash:f}}catch(i){let l=i instanceof Error?i.message:String(i);return console.error("[Git] Error:",l),{success:!1,error:l}}},getCurrentBranch:n}}import ie from"inquirer";var re="\u2192 Other (type custom answer)";async function Ee(e){let t={};for(let n of e){let s=await pn(n);t[n.id]=s}return t}async function pn(e){return e.multiSelect&&e.options.length>0?dn(e):e.options.length>0?un(e):gn(e)}async function dn(e){let t=[...e.options];e.allowFreeText&&t.push(re);let{selection:n}=await ie.prompt([{type:"checkbox",name:"selection",message:e.question,choices:t}]);if(n.includes(re)){let{customAnswer:s}=await ie.prompt([{type:"input",name:"customAnswer",message:"Enter your custom answer:"}]);return[...n.filter(r=>r!==re),s]}return n}async function un(e){let t=[...e.options];e.allowFreeText&&t.push(re);let{selection:n}=await ie.prompt([{type:"select",name:"selection",message:e.question,choices:t}]);if(n===re){let{customAnswer:s}=await ie.prompt([{type:"input",name:"customAnswer",message:"Enter your custom answer:"}]);return s}return n}async function gn(e){let{answer:t}=await ie.prompt([{type:"input",name:"answer",message:e.question}]);return t}import*as L from"fs";import*as Ae from"path";var fn=".sdd/cli-sessions.json";function qe(){return Ae.join(process.cwd(),fn)}function mn(){let e=Ae.dirname(qe());L.existsSync(e)||L.mkdirSync(e,{recursive:!0})}function We(){try{let e=qe();if(!L.existsSync(e))return[];let t=L.readFileSync(e,"utf-8");return JSON.parse(t).sort((s,o)=>new Date(o.lastUsedAt).getTime()-new Date(s.lastUsedAt).getTime())}catch{return[]}}function bt(e){mn();let t=We(),n=t.findIndex(o=>o.sessionId===e.sessionId);n>=0?t[n]={...t[n],lastUsedAt:e.lastUsedAt,turnCount:e.turnCount}:t.unshift(e);let s=t.slice(0,20);L.writeFileSync(qe(),JSON.stringify(s,null,2))}function _t(e){let t=e.firstPrompt.length>50?e.firstPrompt.slice(0,50)+"...":e.firstPrompt,n=new Date(e.lastUsedAt),s=hn(n);return`${t} (${s}, ${e.turnCount} turns)`}function hn(e){let n=new Date().getTime()-e.getTime(),s=Math.floor(n/6e4),o=Math.floor(n/36e5),r=Math.floor(n/864e5);return s<1?"just now":s<60?`${s}m ago`:o<24?`${o}h ago`:r<7?`${r}d ago`:e.toLocaleDateString()}var wt={reset:"\x1B[0m",dim:"\x1B[2m",bold:"\x1B[1m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",red:"\x1B[31m",gray:"\x1B[90m"};function a(e,t){return`${wt[e]}${t}${wt.reset}`}var yn={Bash:"\u26A1",Read:"\u{1F4D6}",Write:"\u270F\uFE0F",Edit:"\u{1F527}",MultiEdit:"\u{1F527}",Glob:"\u{1F50D}",Grep:"\u{1F50E}",LS:"\u{1F4C1}",WebFetch:"\u{1F310}",NotebookRead:"\u{1F4D3}",NotebookEdit:"\u{1F4DD}",Task:"\u{1F4CB}",ask_questions:"\u2753",save_specification:"\u{1F4C4}",list_specifications:"\u{1F4CB}",read_specification:"\u{1F4D6}",delete_specification:"\u{1F5D1}\uFE0F",scaffold_entity:"\u{1F3D7}\uFE0F"};function bn(e){return yn[e]||"\u{1F527}"}function ae(e,t){return e.length<=t?e:e.slice(0,t-3)+"..."}function kt(e){let t=e.split("/");return t.length<=3?e:".../"+t.slice(-2).join("/")}function Ge(e){console.log(`
1270
1270
  ${a("bold",a("cyan","\u2550".repeat(60)))}`),console.log(a("bold",` ${e}`)),console.log(`${a("bold",a("cyan","\u2550".repeat(60)))}
1271
1271
  `)}function St(e){let t=e.split(`
1272
1272
  `);for(let n of t)n.trim()?console.log(` ${n}`):console.log()}function vt(e,t){if(t&&typeof t=="object"){let s=t;if(e==="Task"&&s.subagent_type){let o=String(s.subagent_type);if(console.log(`
@@ -1281,7 +1281,7 @@ ${a("dim","\u2192 Answers sent, agent continuing...")}
1281
1281
  `)}function xe(){console.log(a("dim",`
1282
1282
  `+"\u2500".repeat(50)+`
1283
1283
  `))}function xt(e,t){let n="";if(typeof t=="string")n=ae(t,100);else if(Array.isArray(t)){let s=t.find(o=>typeof o=="object"&&o!==null&&o.type==="text");if(s&&typeof s=="object"){let o=s.text;if(typeof o=="string"){let r=o.split(`
1284
- `).filter(i=>i.trim());r.length>3?n=r.slice(0,3).join(" \u21B5 ")+` (+${r.length-3} more lines)`:n=r.join(" \u21B5 "),n=ae(n,100)}}}console.log(n?` ${a("dim","\u2190")} ${a("gray",n)}`:` ${a("dim","\u2190")} ${a("green","done")}`)}function It(e){return{onAssistantText:St,onAssistantToolUse:(t,n)=>{if(vt(t,n),["Write","Edit","MultiEdit"].includes(t)){let s=n,o=s.file_path||s.target_file;o&&e.onFileModified(o)}},onAssistantToolResult:xt,onResult:(t,n)=>{e.onCostUpdate(n),Ct(t,n)},onError:Tt,onAborted:Pt,onSystemInit:t=>{e.sessionId||(e.onSessionInit(t),console.log(` Session: ${t.slice(0,12)}...`))}}}import I from"inquirer";import*as j from"fs";import*as Ie from"path";var{Separator:wn}=I,_n=".sdd/api-config.json";function Ye(){return Ie.join(process.cwd(),_n)}function kn(){let e=Ie.dirname(Ye());j.existsSync(e)||j.mkdirSync(e,{recursive:!0})}function Sn(){try{let e=Ye();if(!j.existsSync(e))return null;let t=j.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return null}}function vn(e){kn();let t={...e,lastUsedAt:new Date().toISOString()};j.writeFileSync(Ye(),JSON.stringify(t,null,2))}async function Rt(){let{options:e}=await I.prompt([{type:"checkbox",name:"options",message:"Session options:",choices:[{name:"\u{1F50D} Auto-typecheck (self-correcting mode)",value:"typecheck"},{name:"\u{1F4E6} Auto-commit & push",value:"autoCommit"}]}]),{model:t}=await I.prompt([{type:"list",name:"model",message:"Select Claude model:",choices:[{name:"\u{1F916} Auto (let Claude choose)",value:"auto"},{name:"\u{1F9E0} Opus 4.5 (most capable)",value:"claude-opus-4-5-20251101"},{name:"\u26A1 Sonnet 4.5 (fast & capable)",value:"claude-sonnet-4-5-20250929"},{name:"\u{1F4A8} Haiku 4.5 (fastest)",value:"claude-haiku-4-5-20251001"}],default:"auto"}]);return{typecheck:e.includes("typecheck"),autoCommit:e.includes("autoCommit"),model:t}}async function Ot(){let e=We();if(e.length===0){let{prompt:r}=await I.prompt([{type:"input",name:"prompt",message:"What would you like to build?"}]);return{sessionId:null,firstPrompt:r,isResume:!1,existingTurnCount:0}}let t=[{name:"\u{1F195} Start new session",value:"new"},new wn("\u2500\u2500\u2500 Previous Sessions \u2500\u2500\u2500"),...e.map(r=>({name:wt(r),value:r.sessionId}))],{selection:n}=await I.prompt([{type:"select",name:"selection",message:"Select a session:",choices:t}]);if(n==="new"){let{prompt:r}=await I.prompt([{type:"input",name:"prompt",message:"What would you like to build?"}]);return{sessionId:null,firstPrompt:r,isResume:!1,existingTurnCount:0}}let s=e.find(r=>r.sessionId===n);console.log(`
1284
+ `).filter(i=>i.trim());r.length>3?n=r.slice(0,3).join(" \u21B5 ")+` (+${r.length-3} more lines)`:n=r.join(" \u21B5 "),n=ae(n,100)}}}console.log(n?` ${a("dim","\u2190")} ${a("gray",n)}`:` ${a("dim","\u2190")} ${a("green","done")}`)}function It(e){return{onAssistantText:St,onAssistantToolUse:(t,n)=>{if(vt(t,n),["Write","Edit","MultiEdit"].includes(t)){let s=n,o=s.file_path||s.target_file;o&&e.onFileModified(o)}},onAssistantToolResult:xt,onResult:(t,n)=>{e.onCostUpdate(n),Ct(t,n)},onError:Tt,onAborted:Pt,onSystemInit:t=>{e.sessionId||(e.onSessionInit(t),console.log(` Session: ${t.slice(0,12)}...`))}}}import I from"inquirer";import*as j from"fs";import*as Ie from"path";var{Separator:_n}=I,wn=".sdd/api-config.json";function Ye(){return Ie.join(process.cwd(),wn)}function kn(){let e=Ie.dirname(Ye());j.existsSync(e)||j.mkdirSync(e,{recursive:!0})}function Sn(){try{let e=Ye();if(!j.existsSync(e))return null;let t=j.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return null}}function vn(e){kn();let t={...e,lastUsedAt:new Date().toISOString()};j.writeFileSync(Ye(),JSON.stringify(t,null,2))}async function Rt(){let{options:e}=await I.prompt([{type:"checkbox",name:"options",message:"Session options:",choices:[{name:"\u{1F50D} Auto-typecheck (self-correcting mode)",value:"typecheck"},{name:"\u{1F4E6} Auto-commit & push",value:"autoCommit"}]}]),{model:t}=await I.prompt([{type:"list",name:"model",message:"Select Claude model:",choices:[{name:"\u{1F916} Auto (let Claude choose)",value:"auto"},{name:"\u{1F9E0} Opus 4.5 (most capable)",value:"claude-opus-4-5-20251101"},{name:"\u26A1 Sonnet 4.5 (fast & capable)",value:"claude-sonnet-4-5-20250929"},{name:"\u{1F4A8} Haiku 4.5 (fastest)",value:"claude-haiku-4-5-20251001"}],default:"auto"}]);return{typecheck:e.includes("typecheck"),autoCommit:e.includes("autoCommit"),model:t}}async function Ot(){let e=We();if(e.length===0){let{prompt:r}=await I.prompt([{type:"input",name:"prompt",message:"What would you like to build?"}]);return{sessionId:null,firstPrompt:r,isResume:!1,existingTurnCount:0}}let t=[{name:"\u{1F195} Start new session",value:"new"},new _n("\u2500\u2500\u2500 Previous Sessions \u2500\u2500\u2500"),...e.map(r=>({name:_t(r),value:r.sessionId}))],{selection:n}=await I.prompt([{type:"select",name:"selection",message:"Select a session:",choices:t}]);if(n==="new"){let{prompt:r}=await I.prompt([{type:"input",name:"prompt",message:"What would you like to build?"}]);return{sessionId:null,firstPrompt:r,isResume:!1,existingTurnCount:0}}let s=e.find(r=>r.sessionId===n);console.log(`
1285
1285
  \u{1F4C2} Resuming session: ${s.firstPrompt.slice(0,50)}...`);let{prompt:o}=await I.prompt([{type:"input",name:"prompt",message:"Continue with:"}]);return{sessionId:s.sessionId,firstPrompt:s.firstPrompt,isResume:!0,existingTurnCount:s.turnCount||0}}async function Re(){let{action:e}=await I.prompt([{type:"select",name:"action",message:"What would you like to do?",choices:[{name:"\u{1F4DD} Send follow-up message",value:"followup"},{name:"\u{1F6AA} Exit",value:"exit"}]}]);if(e==="exit")return null;let{prompt:t}=await I.prompt([{type:"input",name:"prompt",message:"Your message:"}]);return t}async function Dt(){let{action:e}=await I.prompt([{type:"select",name:"action",message:"What would you like to do?",choices:[{name:"Continue with a new prompt",value:"continue"},{name:"Exit (session saved)",value:"exit"}]}]);return e}async function Ft(){let e=process.env.API_URL||process.env.MASTER_URL,t=process.env.PROJECT_ID,n=Sn();if(n&&!e&&!t){console.log(`
1286
1286
  \u{1F4CB} Previous API Configuration Found
1287
1287
  `),console.log(` \u{1F517} API URL: ${n.apiUrl}`),console.log(` \u{1F194} Project ID: ${n.projectId}`);let{action:r}=await I.prompt([{type:"select",name:"action",message:"What would you like to do?",choices:[{name:"\u2705 Proceed with saved configuration",value:"proceed"},{name:"\u{1F504} Override with new configuration",value:"override"}]}]);if(r==="proceed")return{apiUrl:n.apiUrl,projectId:n.projectId}}let s=await I.prompt([{type:"input",name:"apiUrl",message:"API URL (backend endpoint):",default:e||n?.apiUrl||"http://localhost:5338",validate:r=>{if(!r.trim())return"API URL is required";try{return new URL(r),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:t||n?.projectId,validate:r=>r.trim()?!0:"Project ID is required"}]),o={apiUrl:s.apiUrl,projectId:s.projectId};return vn(o),o}async function Ut(e,t){if(t.size===0)return console.log(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glenn-code",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "Glenn Code - Connect your local development environment to DNM Lab",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",