glenn-code 1.0.18 → 1.0.19

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.
@@ -30,7 +30,7 @@ ${s.join(`
30
30
  Parameters:
31
31
  - target: "backend" (default) | "frontend" | "both"
32
32
 
33
- Use target="backend" before running scaffold (required for migrations).`,{target:ne.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async n=>{if(!R)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=n.target||"backend";console.log(`[MCP] stop_services called (target: ${e})`);let t=e==="backend"||e==="both",o=e==="frontend"||e==="both";t&&await R.stopBackend(),o&&await R.stopFrontend();let s=5e3,r=Date.now(),i=!t,a=!o;for(;Date.now()-r<s;){let d=await R.checkHealth();if(t&&!d.api&&(i=!0),o&&!d.web&&(a=!0),i&&a)break;await new Promise(u=>setTimeout(u,500))}let c=e==="both"?"Backend and frontend":e==="backend"?"Backend":"Frontend";return{content:[{type:"text",text:i&&a?`\u2705 ${c} stopped successfully.`:`\u26A0\uFE0F ${c} stop requested but health check still shows running. Proceeding anyway.`}]}}),Ct=z("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:ne.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async n=>{if(!R)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=n.target||"backend";console.log(`[MCP] stop_services called (target: ${e})`);let t=e==="backend"||e==="both",o=e==="frontend"||e==="both";t&&await R.stopBackend(),o&&await R.stopFrontend();let s=5e3,r=Date.now(),i=!t,a=!o;for(;Date.now()-r<s;){let u=await R.checkHealth();if(t&&!u.api&&(i=!0),o&&!u.web&&(a=!0),i&&a)break;await new Promise(d=>setTimeout(d,500))}let c=e==="both"?"Backend and frontend":e==="backend"?"Backend":"Frontend";return{content:[{type:"text",text:i&&a?`\u2705 ${c} stopped successfully.`:`\u26A0\uFE0F ${c} stop requested but health check still shows running. Proceeding anyway.`}]}}),Ct=z("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"
@@ -55,7 +55,7 @@ Parameters:
55
55
  - pattern: string (e.g., "users", "api/reports")`,{pattern:ne.string().describe("Text to search for in endpoint paths")},async n=>{if(!R)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=await R.checkSwaggerEndpoints(n.pattern);return e.foundEndpoints.length===0?{content:[{type:"text",text:`\u274C No endpoints found matching "${n.pattern}" (Total endpoints: ${e.totalEndpoints})`}]}:{content:[{type:"text",text:`\u2705 Found ${e.foundEndpoints.length} matching endpoints:
56
56
 
57
57
  ${e.foundEndpoints.join(`
58
- `)}`}]}});import*as ae from"fs";import*as Xt from"path";import*as A from"fs";import*as ye from"path";function ce(n){let e=ye.join(n,".sdd","specifications");function t(){A.existsSync(e)||A.mkdirSync(e,{recursive:!0})}function o(s){return ye.join(e,`${s}.spec.md`)}return{async saveSpecification(s,r){t(),A.writeFileSync(o(s),r,"utf-8")},async getSpecification(s){t();let r=o(s);return A.existsSync(r)?A.readFileSync(r,"utf-8"):null},async listSpecifications(){return t(),A.readdirSync(e).filter(r=>r.endsWith(".spec.md")).map(r=>{let i=A.readFileSync(ye.join(e,r),"utf-8"),a=i.match(/name: "([^"]+)"/),c=i.match(/status: (\w+)/),l=i.match(/version: "([^"]+)"/);return{slug:r.replace(".spec.md",""),name:a?.[1]||r,status:c?.[1]||"unknown",version:l?.[1]||"1.0.0"}})},async deleteSpecification(s){t();let r=o(s);return A.existsSync(r)?(A.unlinkSync(r),!0):!1},async specificationExists(s){return t(),A.existsSync(o(s))}}}function Be(){return Zt({name:"agent-insights",version:"1.0.0",tools:[kt,vt,Ct,_t,Pt,Rt]})}import{query as bn}from"@anthropic-ai/claude-agent-sdk";var qe={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 ae from"fs";import*as Xt from"path";import*as x from"fs";import*as ye from"path";function ce(n){let e=ye.join(n,".sdd","specifications");function t(){x.existsSync(e)||x.mkdirSync(e,{recursive:!0})}function o(s){return ye.join(e,`${s}.spec.md`)}return{async saveSpecification(s,r){t(),x.writeFileSync(o(s),r,"utf-8")},async getSpecification(s){t();let r=o(s);return x.existsSync(r)?x.readFileSync(r,"utf-8"):null},async listSpecifications(){return t(),x.readdirSync(e).filter(r=>r.endsWith(".spec.md")).map(r=>{let i=x.readFileSync(ye.join(e,r),"utf-8"),a=i.match(/name: "([^"]+)"/),c=i.match(/status: (\w+)/),l=i.match(/version: "([^"]+)"/);return{slug:r.replace(".spec.md",""),name:a?.[1]||r,status:c?.[1]||"unknown",version:l?.[1]||"1.0.0"}})},async deleteSpecification(s){t();let r=o(s);return x.existsSync(r)?(x.unlinkSync(r),!0):!1},async specificationExists(s){return t(),x.existsSync(o(s))}}}function Be(){return Zt({name:"agent-insights",version:"1.0.0",tools:[kt,vt,Ct,_t,Pt,Rt]})}import{query as bn}from"@anthropic-ai/claude-agent-sdk";var qe={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:
59
59
  1. Read the spec if referenced in the card description
60
60
  2. Check existing features (quick ls)
61
61
  3. Write ALL entities to ONE /tmp/scaffold.json (big bang, no phasing!)
@@ -881,7 +881,7 @@ Then STOP. Do not continue. Do not try to fix it. Wait for user.
881
881
 
882
882
  ---
883
883
 
884
- `:"",c=un(s),l=dn(s,o),d=mn(s,r),u=gn(r),g=r?hn(s):"",w=r?yn(s,o):"",v=fn(r);return`${i}${a}# Agent Instructions
884
+ `:"",c=dn(s),l=un(s,o),u=mn(s,r),d=gn(r),g=r?hn(s):"",w=r?yn(s,o):"",v=fn(r);return`${i}${a}# Agent Instructions
885
885
 
886
886
  ## \u26D4 CRITICAL: EVERYTHING IS A KANBAN TASK
887
887
 
@@ -1005,7 +1005,7 @@ When user asks to build ANY feature, your FIRST action is to delegate to @planni
1005
1005
  - Use EnterPlanMode or any built-in planning
1006
1006
  - Skip the @planning step
1007
1007
 
1008
- ${u}
1008
+ ${d}
1009
1009
 
1010
1010
  ---
1011
1011
 
@@ -1018,17 +1018,17 @@ ${c}
1018
1018
 
1019
1019
  ${l}
1020
1020
 
1021
- ${d}
1021
+ ${u}
1022
1022
 
1023
1023
  ${v}
1024
1024
 
1025
1025
  ${g}
1026
1026
 
1027
1027
  ${w}
1028
- `}function un(n){let e=Te(n),t=H(n),o=B(n),s=Re(n),r=le(n),i=[];s&&i.push(`- Backend: "${s}/{Entity}/"`),r&&i.push(`- Frontend: "${r}/{entity}/"`);let a=n.techStack?.patterns?.length?`
1028
+ `}function dn(n){let e=Te(n),t=H(n),o=B(n),s=Re(n),r=le(n),i=[];s&&i.push(`- Backend: "${s}/{Entity}/"`),r&&i.push(`- Frontend: "${r}/{entity}/"`);let a=n.techStack?.patterns?.length?`
1029
1029
  **Key patterns:**
1030
- ${n.techStack.patterns.map(d=>`- ${d}`).join(`
1031
- `)}`:"",l=n.techStack?.backend?.some(d=>d.includes(".NET")||d.includes("C#"))??!1?`
1030
+ ${n.techStack.patterns.map(u=>`- ${u}`).join(`
1031
+ `)}`:"",l=n.techStack?.backend?.some(u=>u.includes(".NET")||u.includes("C#"))??!1?`
1032
1032
 
1033
1033
  **\u26A0\uFE0F User entity exists!** ASP.NET Identity "ApplicationUser" at "Features/Users/" - don't scaffold User, just relate to it.`:"";return`<tech-stack>
1034
1034
  ${e}
@@ -1037,7 +1037,7 @@ ${i.length>0?`
1037
1037
  ${i.join(`
1038
1038
  `)}`:""}
1039
1039
  ${a}${l}
1040
- </tech-stack>`}function dn(n,e){return e?`<environment>
1040
+ </tech-stack>`}function un(n,e){return e?`<environment>
1041
1041
  **LOCAL MODE - User manages their own services.**
1042
1042
 
1043
1043
  You are running on the user's local machine, NOT in a Docker container.
@@ -1334,30 +1334,30 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
1334
1334
  - **Multi-tab apps**: Use \`browser_tabs\` to manage multiple windows/tabs in legacy applications
1335
1335
  - **Large snapshots**: For pages with huge dropdowns (e.g., country lists), use \`browser_evaluate\` to collapse them first
1336
1336
  - ${e?"In local mode, the browser window will be visible to you":"In container mode, browser runs headless"}
1337
- </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,G=!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:qe,debugger:be,planning:Se,backend:Ge,frontend:Ke,"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)}G=!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(G)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 T from"fs";import*as Nt from"http";import*as J 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,G=y=>new Promise(m=>{let S=setTimeout(()=>m(!1),3e3);Nt.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(`
1338
- `).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||!T.existsSync(l)){console.log("[Services] No backend found, skipping backend start");return}F(a),console.log(`[Services] Starting backend with: ${u}...`);let y=J.dirname(D);T.existsSync(y)||T.mkdirSync(y,{recursive:!0});let m=T.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(`
1339
- `);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||!T.existsSync(J.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=J.dirname(j);T.existsSync(y)||T.mkdirSync(y,{recursive:!0});let[m,...S]=v.split(" ");O=Ut(m,S,{cwd:d,stdio:["ignore",T.openSync(j,"w"),T.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([G(a),G(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(Jt=>setTimeout(Jt,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&&T.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&&T.existsSync(J.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?`
1337
+ </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")],de=Ot.find(n=>Qe.existsSync(n))||Ot[0];console.log("[MCP] Stdio server path resolved:",de);console.log("[MCP] __dirname:",Ye);console.log("[MCP] File exists:",Qe.existsSync(de));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:u=process.env.LOCAL_MODE==="true",projectConfig:d=W(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:g=!0}=e,w=At(a),v=new Set,A=o||"",D,b,f,O=!1,G=!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:",[de]),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:",de);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",...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:t,mcpServers:{"agent-insights":wn,"agent-planning":{type:"stdio",command:"node",args:[de],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")}`,...u?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:g?{scaffolding:qe,debugger:be,planning:Se,backend:Ge,frontend:Ke,"project-context":we}:{debugger:be,planning:Se,"project-context":we},systemPrompt:Dt({debugMode:l,projectPrompt:jt(),isLocal:u,projectConfig:d,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,b=p.total_cost_usd,i.onResult?.(p.result,p.total_cost_usd);else{let C=`Agent error: ${p.subtype}`;f=C,i.onError?.(C)}G=!0;break;case"system":switch(p.subtype){case"init":A=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(G)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:A,result:D,cost:b,error:f,aborted:O}}import{spawn as Ut,execSync as ee}from"child_process";import*as T from"fs";import*as Nt from"http";import*as J 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),u=B(i),d=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",A=i.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",D=i.services?.backend?.logFile??"/tmp/api.log",b=i.services?.frontend?.logFile??"/tmp/web.log",f=null,O=null,G=y=>new Promise(m=>{let S=setTimeout(()=>m(!1),3e3);Nt.get(`http://localhost:${y}`,j=>{clearTimeout(S),m(j.statusCode!==void 0&&j.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(`
1338
+ `).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||!T.existsSync(l)){console.log("[Services] No backend found, skipping backend start");return}F(a),console.log(`[Services] Starting backend with: ${d}...`);let y=J.dirname(D);T.existsSync(y)||T.mkdirSync(y,{recursive:!0});let m=T.createWriteStream(D,{flags:"a"}),[S,..._]=d.split(" ");f=Ut(S,_,{cwd:l,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let j="",E=P=>{let Q=P.toString();m.write(Q);let L=(j+Q).split(`
1339
+ `);j=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()},ue=async()=>{if(!u||!T.existsSync(J.join(u,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}F(c),console.log(`[Services] Starting frontend with: ${v}...`);let y=J.dirname(b);T.existsSync(y)||T.mkdirSync(y,{recursive:!0});let[m,...S]=v.split(" ");O=Ut(m,S,{cwd:u,stdio:["ignore",T.openSync(b,"w"),T.openSync(b,"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([G(a),G(c)]);return{api:y,web:m}},ht=async(y=15e3)=>{let m=Date.now(),S=1e3,_=0,j=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&&j===null&&(j=$,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(Jt=>setTimeout(Jt,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&&j===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&&T.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(_&&u&&T.existsSync(J.join(u,"package.json"))){console.log(`[Services] Type-checking frontend with: ${A}...`);try{ee(A,{cwd:u,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 ue();let j=await ht(1e4);if(S&&!j.api){m.success=!1;let E=await St("backend",20),P=E.length>0?`
1340
1340
  Recent logs:
1341
1341
  ${E.join(`
1342
- `)}`:"";m.backendError||(m.backendError=`Backend failed to start.${P}`)}return _&&!x.web&&(m.success=!1,m.frontendError||(m.frontendError="Frontend failed to start")),m},yt=async()=>{if(!l||!T.existsSync(l))return{success:!0};try{return ee(w,{cwd:l,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),{success:!0}}catch(y){let m=y;return{success:!1,errors:m.stdout||m.stderr||"Build failed"}}},bt=async()=>{if(!d||!T.existsSync(J.join(d,"package.json")))return{success:!0};try{return ee(b,{cwd:d,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),{success:!0}}catch(y){let m=y;return{success:!1,errors:m.stdout||m.stderr||"Type check failed"}}},Vt=async()=>{let[y,m]=await Promise.all([yt(),bt()]);return{backend:y,frontend:m}},St=async(y,m)=>{let S=y==="backend"?D:j;if(!T.existsSync(S))return[`Log file not found: ${S}`];try{return ee(`tail -n ${m} ${S}`,{encoding:"utf-8"}).split(`
1343
- `).filter(Boolean)}catch(_){return[`Error reading logs: ${_ instanceof Error?_.message:String(_)}`]}};return{startBackend:C,startFrontend:de,stopBackend:ft,stopFrontend:mt,restartServices:zt,checkHealth:$e,waitForHealth:ht,getProcesses:()=>({backend:f,frontend:O}),checkBackendBuild:yt,checkFrontendTypes:bt,runTypeChecks:Vt,tailLogs:St,checkSwaggerEndpoints:async y=>{let m=J.join(e,"swagger.json");if(!T.existsSync(m))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let S=JSON.parse(T.readFileSync(m,"utf-8")),_=Object.keys(S.paths||{}),x=[];for(let E of _)if(E.toLowerCase().includes(y.toLowerCase())){let P=Object.keys(S.paths[E]);for(let Q of P)x.push(`${Q.toUpperCase()} ${E}`)}return{foundEndpoints:x,totalEndpoints:_.length}}catch(S){return{foundEndpoints:[`Error parsing swagger.json: ${S instanceof Error?S.message:String(S)}`],totalEndpoints:0}}}}}function vn(n){let e=n.toLowerCase();return e.includes("exception")||e.includes("fail:")||e.includes("[err]")||e.includes("[error]")}import{execSync as te}from"child_process";function De(n){let e=async()=>{try{return te("git status --porcelain",{cwd:n,encoding:"utf-8"}).trim().length>0}catch{return!1}},t=async()=>{try{return te("git branch --show-current",{cwd:n,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:e,commitAndPush:async(s,r)=>{try{if(!await e())return{success:!0,noChanges:!0};let i=s.length>60?s.substring(0,60)+"...":s,c=`${r?"[agent] (partial)":"[agent]"} ${i}`;te("git add -A",{cwd:n}),te(`git commit -m "${c.replace(/"/g,'\\"')}"`,{cwd:n,encoding:"utf-8"});let l=te("git rev-parse --short HEAD",{cwd:n,encoding:"utf-8"}).trim();try{te("git push",{cwd:n,stdio:"pipe"})}catch(d){let u=d instanceof Error?d.message:String(d);if(u.includes("no upstream branch")){let g=await t();te(`git push --set-upstream origin ${g}`,{cwd:n,stdio:"pipe"})}else return console.error("[Git] Push failed:",u),{success:!0,commitHash:l,error:`Committed but push failed: ${u}`}}return{success:!0,commitHash:l}}catch(i){let a=i instanceof Error?i.message:String(i);return console.error("[Git] Error:",a),{success:!1,error:a}}},getCurrentBranch:t}}import*as se from"@microsoft/signalr";var X=class{constructor(e,t){this.connection=e;this.receiverMethod=t}dispose=()=>{for(let e of this.receiverMethod)this.connection.off(e.methodName,e.method)}},Mt=n=>{if(n==="IDeploymentHub")return ze.Instance;if(n==="IKanbanHub")return Je.Instance;if(n==="IAgentHub")return Ze.Instance;if(n==="ISlaveHub")return tt.Instance;if(n==="ITestHub")return ot.Instance},Ft=n=>{if(n==="IDeploymentHubClient")return rt.Instance;if(n==="IKanbanBoardClient")return it.Instance;if(n==="IAgentReceiver")return at.Instance;if(n==="IBackofficeReceiver")return ct.Instance;if(n==="ISlaveHubClient")return lt.Instance;if(n==="ITestHubClient")return pt.Instance},ze=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Ve(e)},Ve=class{constructor(e){this.connection=e}registerMachine=async(e,t)=>await this.connection.invoke("RegisterMachine",e,t);reportDeploymentStatus=async e=>await this.connection.invoke("ReportDeploymentStatus",e);reportHealth=async e=>await this.connection.invoke("ReportHealth",e);reportMachineStatus=async e=>await this.connection.invoke("ReportMachineStatus",e);sendDeploymentLog=async e=>await this.connection.invoke("SendDeploymentLog",e)},Je=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Xe(e)},Xe=class{constructor(e){this.connection=e}joinBoard=async e=>await this.connection.invoke("JoinBoard",e);leaveBoard=async e=>await this.connection.invoke("LeaveBoard",e)},Ze=class n{static Instance=new n;constructor(){}createHubProxy=e=>new et(e)},et=class{constructor(e){this.connection=e}registerAgent=async e=>await this.connection.invoke("RegisterAgent",e);getSecrets=async()=>await this.connection.invoke("GetSecrets");reportStatus=async(e,t)=>await this.connection.invoke("ReportStatus",e,t);reportSessionOutput=async(e,t,o)=>await this.connection.invoke("ReportSessionOutput",e,t,o);reportSessionCost=async(e,t,o)=>await this.connection.invoke("ReportSessionCost",e,t,o);reportInsight=async(e,t)=>await this.connection.invoke("ReportInsight",e,t);setClaudeSessionId=async(e,t)=>await this.connection.invoke("SetClaudeSessionId",e,t);sessionCompleted=async(e,t,o)=>await this.connection.invoke("SessionCompleted",e,t,o);initSessionCompleted=async(e,t,o,s)=>await this.connection.invoke("InitSessionCompleted",e,t,o,s);reportPreviewBuild=async(e,t,o)=>await this.connection.invoke("ReportPreviewBuild",e,t,o);saveSpecification=async(e,t,o,s)=>await this.connection.invoke("SaveSpecification",e,t,o,s);getSpecification=async(e,t)=>await this.connection.invoke("GetSpecification",e,t);listSpecifications=async e=>await this.connection.invoke("ListSpecifications",e);deleteSpecification=async(e,t)=>await this.connection.invoke("DeleteSpecification",e,t);watchContainer=async e=>await this.connection.invoke("WatchContainer",e);watchSession=async e=>await this.connection.invoke("WatchSession",e);watchSlaves=async()=>await this.connection.invoke("WatchSlaves");unwatchSlaves=async()=>await this.connection.invoke("UnwatchSlaves");sendPrompt=async(e,t,o,s)=>await this.connection.invoke("SendPrompt",e,t,o,s);stopSession=async e=>await this.connection.invoke("StopSession",e);switchSession=async(e,t)=>await this.connection.invoke("SwitchSession",e,t);createSession=async e=>await this.connection.invoke("CreateSession",e)},tt=class n{static Instance=new n;constructor(){}createHubProxy=e=>new nt(e)},nt=class{constructor(e){this.connection=e}register=async(e,t)=>await this.connection.invoke("Register",e,t);getSecrets=async()=>await this.connection.invoke("GetSecrets");containerCreated=async e=>await this.connection.invoke("ContainerCreated",e);containerReady=async e=>await this.connection.invoke("ContainerReady",e);reportCapacity=async e=>await this.connection.invoke("ReportCapacity",e)},ot=class n{static Instance=new n;constructor(){}createHubProxy=e=>new st(e)},st=class{constructor(e){this.connection=e}joinProject=async e=>await this.connection.invoke("JoinProject",e);leaveProject=async e=>await this.connection.invoke("LeaveProject",e)},rt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...i)=>t.deploy(...i),s=(...i)=>t.stop(...i);e.on("Deploy",o),e.on("Stop",s);let r=[{methodName:"Deploy",method:o},{methodName:"Stop",method:s}];return new X(e,r)}},it=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...f)=>t.boardUpdated(...f),s=(...f)=>t.boardDeleted(...f),r=(...f)=>t.columnCreated(...f),i=(...f)=>t.columnUpdated(...f),a=(...f)=>t.columnDeleted(...f),c=(...f)=>t.cardCreated(...f),l=(...f)=>t.cardUpdated(...f),d=(...f)=>t.cardDeleted(...f),u=(...f)=>t.subtaskCreated(...f),g=(...f)=>t.subtaskUpdated(...f),w=(...f)=>t.subtaskDeleted(...f),v=(...f)=>t.noteCreated(...f),b=(...f)=>t.noteUpdated(...f),D=(...f)=>t.noteDeleted(...f);e.on("BoardUpdated",o),e.on("BoardDeleted",s),e.on("ColumnCreated",r),e.on("ColumnUpdated",i),e.on("ColumnDeleted",a),e.on("CardCreated",c),e.on("CardUpdated",l),e.on("CardDeleted",d),e.on("SubtaskCreated",u),e.on("SubtaskUpdated",g),e.on("SubtaskDeleted",w),e.on("NoteCreated",v),e.on("NoteUpdated",b),e.on("NoteDeleted",D);let j=[{methodName:"BoardUpdated",method:o},{methodName:"BoardDeleted",method:s},{methodName:"ColumnCreated",method:r},{methodName:"ColumnUpdated",method:i},{methodName:"ColumnDeleted",method:a},{methodName:"CardCreated",method:c},{methodName:"CardUpdated",method:l},{methodName:"CardDeleted",method:d},{methodName:"SubtaskCreated",method:u},{methodName:"SubtaskUpdated",method:g},{methodName:"SubtaskDeleted",method:w},{methodName:"NoteCreated",method:v},{methodName:"NoteUpdated",method:b},{methodName:"NoteDeleted",method:D}];return new X(e,j)}},at=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...u)=>t.initSession(...u),s=(...u)=>t.runPrompt(...u),r=(...u)=>t.setModel(...u),i=()=>t.stopAgent(),a=()=>t.ping(),c=(...u)=>t.switchSession(...u),l=(...u)=>t.projectPromptUpdated(...u);e.on("InitSession",o),e.on("RunPrompt",s),e.on("SetModel",r),e.on("StopAgent",i),e.on("Ping",a),e.on("SwitchSession",c),e.on("ProjectPromptUpdated",l);let d=[{methodName:"InitSession",method:o},{methodName:"RunPrompt",method:s},{methodName:"SetModel",method:r},{methodName:"StopAgent",method:i},{methodName:"Ping",method:a},{methodName:"SwitchSession",method:c},{methodName:"ProjectPromptUpdated",method:l}];return new X(e,d)}},ct=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...b)=>t.sessionOutput(...b),s=(...b)=>t.sessionCost(...b),r=(...b)=>t.sessionCompleted(...b),i=(...b)=>t.containerStatus(...b),a=(...b)=>t.slaveCapacityUpdate(...b),c=(...b)=>t.previewBuildStatus(...b),l=(...b)=>t.previewReload(...b),d=(...b)=>t.questionsReceived(...b),u=(...b)=>t.specificationUpdated(...b),g=(...b)=>t.singleQuestionReceived(...b),w=(...b)=>t.questionSessionEnded(...b);e.on("SessionOutput",o),e.on("SessionCost",s),e.on("SessionCompleted",r),e.on("ContainerStatus",i),e.on("SlaveCapacityUpdate",a),e.on("PreviewBuildStatus",c),e.on("PreviewReload",l),e.on("QuestionsReceived",d),e.on("SpecificationUpdated",u),e.on("SingleQuestionReceived",g),e.on("QuestionSessionEnded",w);let v=[{methodName:"SessionOutput",method:o},{methodName:"SessionCost",method:s},{methodName:"SessionCompleted",method:r},{methodName:"ContainerStatus",method:i},{methodName:"SlaveCapacityUpdate",method:a},{methodName:"PreviewBuildStatus",method:c},{methodName:"PreviewReload",method:l},{methodName:"QuestionsReceived",method:d},{methodName:"SpecificationUpdated",method:u},{methodName:"SingleQuestionReceived",method:g},{methodName:"QuestionSessionEnded",method:w}];return new X(e,v)}},lt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...a)=>t.startContainer(...a),s=(...a)=>t.terminateContainer(...a),r=()=>t.reconnect();e.on("StartContainer",o),e.on("TerminateContainer",s),e.on("Reconnect",r);let i=[{methodName:"StartContainer",method:o},{methodName:"TerminateContainer",method:s},{methodName:"Reconnect",method:r}];return new X(e,i)}},pt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...l)=>t.testRunCreated(...l),s=(...l)=>t.testRunUpdated(...l),r=(...l)=>t.testSuiteCreated(...l),i=(...l)=>t.testCreated(...l),a=(...l)=>t.testUpdated(...l);e.on("TestRunCreated",o),e.on("TestRunUpdated",s),e.on("TestSuiteCreated",r),e.on("TestCreated",i),e.on("TestUpdated",a);let c=[{methodName:"TestRunCreated",method:o},{methodName:"TestRunUpdated",method:s},{methodName:"TestSuiteCreated",method:r},{methodName:"TestCreated",method:i},{methodName:"TestUpdated",method:a}];return new X(e,c)}};var je=class{constructor(e){this.config=e;this.connection=new se.HubConnectionBuilder().withUrl(`${e.masterUrl}/api/hubs/agent`,{headers:{"X-Container-Id":e.containerId,"X-Project-Key":e.projectKey}}).withAutomaticReconnect({nextRetryDelayInMilliseconds:t=>{let o=t.previousRetryCount+1,s=Math.min(1e3*Math.pow(2,t.previousRetryCount),6e4);return console.log(`[SignalR] Reconnect attempt ${o} (will retry in ${s}ms)`),s}}).withServerTimeout(3e5).withKeepAliveInterval(1e4).configureLogging(se.LogLevel.Information).build(),this.hubProxy=Mt("IAgentHub").createHubProxy(this.connection),this.connection.onreconnected(async()=>{console.log("[SignalR] Reconnected to gateway");try{await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Re-registered as agent for container: ${this.config.containerId}`),await this.reportStatus(this.currentStatus,void 0,this.currentSessionId??void 0,this.currentProjectId??void 0)}catch(t){console.error("[SignalR] Failed to re-register agent after reconnect:",t instanceof Error?t.message:t)}}),this.connection.onclose(t=>{console.error("[SignalR] Connection closed:",t?.message),process.exit(1)})}connection;hubProxy;receiverSubscription=null;currentStatus="WarmingUp";currentSessionId=null;currentProjectId=null;tunnelUrl=null;async connect(){console.log(`[SignalR] Connecting to gateway: ${this.config.masterUrl}/api/hubs/agent`),await this.connection.start(),console.log("[SignalR] Connected to gateway!"),await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Registered as agent for container: ${this.config.containerId}`)}registerReceiver(e){this.receiverSubscription&&this.receiverSubscription.dispose(),this.receiverSubscription=Ft("IAgentReceiver").register(this.connection,e)}getHubProxy(){return this.hubProxy}getMasterUrl(){return this.config.masterUrl}getProjectKey(){return this.config.projectKey}getConnection(){return this.connection}setTunnelUrl(e){this.tunnelUrl=e}async reportStatus(e,t,o,s){if(this.currentStatus=e,o&&(this.currentSessionId=o),s&&(this.currentProjectId=s),this.connection.state===se.HubConnectionState.Connected)try{let r={status:e,error:t,sessionId:o,projectId:s,tunnelUrl:this.tunnelUrl??void 0};await this.hubProxy.reportStatus(this.config.containerId,r),console.log(`[Status] ${e}${t?` (${t})`:""}${this.tunnelUrl?` [${this.tunnelUrl}]`:""}${o?` [session: ${o}]`:""}`)}catch(r){console.error("[SignalR] Failed to report status:",r)}}async getSecrets(){let e=await this.hubProxy.getSecrets();if(!e.success)throw new Error(`Failed to get secrets: ${e.error}`);return e.secrets||{}}};function Cn(n){let e=n.match(/name:\s*"([^"]+)"/);if(e)return e[1];let t=n.match(/^#\s+(.+)$/m);return t?t[1].trim():"Untitled Specification"}var Oe=class{constructor(e,t){this.hubProxy=e;this.projectId=t}async saveSpecification(e,t){let o=Cn(t),s=await this.hubProxy.saveSpecification(this.projectId,e,o,t);if(!s.success)throw new Error(s.error||"Failed to save specification")}async getSpecification(e){let t=await this.hubProxy.getSpecification(this.projectId,e);return t.success&&t.content||null}async listSpecifications(){let e=await this.hubProxy.listSpecifications(this.projectId);return!e.success||!e.specifications?[]:e.specifications.map(t=>({slug:t.slug,name:t.name,status:t.status,version:"1.0.0"}))}async deleteSpecification(e){return(await this.hubProxy.deleteSpecification(this.projectId,e)).success}async specificationExists(e){let t=await this.hubProxy.getSpecification(this.projectId,e);return t.success&&t.content!=null}};import{exec as _n,execSync as Pn,spawn as Rn}from"child_process";import{promisify as Tn}from"util";import{createHash as Lt}from"crypto";import*as k from"fs";import*as N from"path";var Ue=class{domain;accountId;tunnelId;apiToken;zoneId;tunnelApiBase;constructor(){this.domain=process.env.CLOUDFLARE_DEPLOY_DOMAIN||process.env.DEPLOY_DOMAIN||"vibecodementor.net",this.accountId=process.env.CLOUDFLARE_ACCOUNT_ID||"",this.tunnelId=process.env.CLOUDFLARE_TUNNEL_ID||"",this.apiToken=process.env.CLOUDFLARE_API_TOKEN||"",this.zoneId=process.env.CLOUDFLARE_ZONE_ID||"",this.tunnelApiBase=`https://api.cloudflare.com/client/v4/accounts/${this.accountId}/cfd_tunnel/${this.tunnelId}/configurations`}isConfigured(){return!!(this.accountId&&this.tunnelId&&this.apiToken&&this.zoneId)}buildHostname(e){let t=this.domain.split("."),o=t.length>2?t.slice(-2).join("."):this.domain;return`${e}.${o}`}async addRoute(e,t){let o=this.buildHostname(e);if(console.log(`[TunnelManager] Adding route: ${o} -> localhost:${t}`),!this.isConfigured())return console.log("[TunnelManager] Not configured, skipping route addition"),`https://${o}`;try{let s=await this.getConfig(),r=s.ingress.findIndex(a=>a.hostname===o);if(r!==-1)s.ingress[r].service=`http://localhost:${t}`;else{let a=s.ingress.findIndex(l=>!l.hostname),c={hostname:o,service:`http://localhost:${t}`};a!==-1?s.ingress.splice(a,0,c):(s.ingress.push(c),s.ingress.push({service:"http_status:404"}))}await this.putConfig(s),await this.ensureDnsPointsToTunnel(o);let i=`https://${o}`;return console.log(`[TunnelManager] \u2713 Route added: ${i}`),i}catch(s){throw console.error("[TunnelManager] Failed to add route:",s),s}}async removeRoute(e){let t=this.buildHostname(e);if(console.log(`[TunnelManager] Removing route: ${t}`),!this.isConfigured()){console.log("[TunnelManager] Not configured, skipping route removal");return}try{let o=await this.getConfig();o.ingress=o.ingress.filter(s=>s.hostname!==t),o.ingress.some(s=>!s.hostname)||o.ingress.push({service:"http_status:404"}),await this.putConfig(o),console.log(`[TunnelManager] \u2713 Route removed: ${t}`)}catch(o){throw console.error("[TunnelManager] Failed to remove route:",o),o}}async getConfig(){let t=await(await fetch(this.tunnelApiBase,{method:"GET",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"}})).json();if(!t.success)throw new Error(`Cloudflare API error: ${t.errors.map(o=>o.message).join(", ")}`);return t.result?.config||{ingress:[{service:"http_status:404"}]}}async putConfig(e){let o=await(await fetch(this.tunnelApiBase,{method:"PUT",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({config:e})})).json();if(!o.success)throw new Error(`Cloudflare API error: ${o.errors.map(s=>s.message).join(", ")}`);console.log("[TunnelManager] Configuration updated via Cloudflare API")}async ensureDnsPointsToTunnel(e){let t=`${this.tunnelId}.cfargotunnel.com`,o=`https://api.cloudflare.com/client/v4/zones/${this.zoneId}/dns_records`;try{let r=await(await fetch(`${o}?name=${e}&type=CNAME`,{method:"GET",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"}})).json();if(!r.success||!r.result?.length){console.log(`[TunnelManager] No DNS record found for ${e}, skipping DNS update`);return}let i=r.result[0];if(i.content===t){console.log(`[TunnelManager] DNS already points to correct tunnel: ${e}`);return}console.log(`[TunnelManager] Updating DNS: ${e} from ${i.content} to ${t}`);let c=await(await fetch(`${o}/${i.id}`,{method:"PATCH",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({type:"CNAME",name:e,content:t,proxied:!0})})).json();c.success?console.log(`[TunnelManager] \u2713 DNS updated: ${e} -> ${t}`):console.error(`[TunnelManager] Failed to update DNS: ${c.errors.map(l=>l.message).join(", ")}`)}catch(s){console.error(`[TunnelManager] Error updating DNS for ${e}:`,s)}}};var $t=Tn(_n),I=class{start;label;constructor(e){this.label=e,this.start=Date.now(),console.log(`[TIMING] \u23F1 START: ${e}`)}stop(){let e=Date.now()-this.start,t=(e/1e3).toFixed(2);return console.log(`[TIMING] \u2713 DONE: ${this.label} (${t}s)`),e}};function U(n,e={}){return new Promise((t,o)=>{let s=Rn(n,[],{shell:!0,stdio:"inherit",cwd:e.cwd}),r=null;e.timeout&&(r=setTimeout(()=>{s.kill(),o(new Error(`Command timed out: ${n}`))},e.timeout)),s.on("close",i=>{r&&clearTimeout(r),i===0?t():o(new Error(`Command failed with code ${i}: ${n}`))}),s.on("error",i=>{r&&clearTimeout(r),o(i)})})}var Ht="main",Ne=class{constructor(e,t,o,s){this.connection=e;this.serviceManager=t;this.workspaceDir=o;this.gitHubPat=s}currentBranchName=Ht;currentRepoUrl="";lastLockfileHash=null;tunnelManager=null;currentPreviewSubdomain=null;getTunnelManager(){return this.tunnelManager||(this.tunnelManager=new Ue),this.tunnelManager}async prewarmWorkspace(){let e=new I("TOTAL INIT"),t=process.env.REPO_URL,o=process.env.BRANCH_NAME||Ht,s=process.env.PREVIEW_SUBDOMAIN,r=process.env.PREVIEW_HOSTNAME,i=process.env.PROJECT_GITHUB_PAT;if(console.log("[Init] Starting single-phase init..."),console.log(`[Init] Repo: ${t?this.extractRepoPath(t):"NOT SET"}`),console.log(`[Init] Branch: ${o}`),console.log(`[Init] Preview: ${s||"none"}`),!t)throw console.error("[Init] \u274C REPO_URL not set - cannot initialize workspace"),new Error("REPO_URL environment variable is required");i&&(this.gitHubPat=i);let a=new I("Clean workspace");Pn("rm -rf /workspace/* /workspace/.* 2>/dev/null || true",{stdio:"inherit"}),a.stop();let c=new I("Clone repository");await this.cloneRepository(t,o),this.currentRepoUrl=t,this.currentBranchName=o,c.stop(),await this.installDependencies(),this.lastLockfileHash=this.getLockfileHash(),console.log("[Init] Starting services...");let l=new I("Start backend (dotnet)");await this.serviceManager.startBackend(),l.stop();let d=new I("Start frontend (vite)");await this.serviceManager.startFrontend(),d.stop();let u=new I("Wait for health check");await this.serviceManager.waitForHealth(3e4),u.stop(),s&&r&&await this.setupPreviewSubdomainFromEnv(s,r),e.stop(),console.log("[Init] \u2713 Workspace ready")}async handleInitSession(e){console.log(`[InitSession] Session ${e.sessionId} starting...`);let t=this.extractRepoPath(e.repoUrl),o=this.extractRepoPath(this.currentRepoUrl),s=t!==o,r=e.branchName!==this.currentBranchName;s||r?(console.log("[InitSession] Repo/branch mismatch - updating workspace..."),console.log(`[InitSession] Current: ${o}@${this.currentBranchName}`),console.log(`[InitSession] Requested: ${t}@${e.branchName}`),await this.connection.reportStatus("Ready",void 0,e.sessionId,e.projectId),await this.connection.getHubProxy().initSessionCompleted(process.env.CONTAINER_ID||"unknown",e.sessionId,!0,""),this.handleRepoChange(e).catch(async a=>{let c=a instanceof Error?a.message:"Unknown error";console.error("[InitSession] Background update failed:",c),await this.connection.reportStatus("Error",c,e.sessionId,e.projectId)})):(console.log("[InitSession] Workspace already matches - ready immediately"),await this.connection.reportStatus("Ready",void 0,e.sessionId,e.projectId),await this.connection.getHubProxy().initSessionCompleted(process.env.CONTAINER_ID||"unknown",e.sessionId,!0,"")),console.log(`[InitSession] \u2713 Ready for session ${e.sessionId}`)}async setupPreviewSubdomainFromEnv(e,t){let o=new I("Setup preview subdomain");console.log(`[Preview] Setting up preview subdomain: ${e} (${t})`);let s=this.getTunnelManager();if(!s){console.log("[Preview] TunnelManager not available, skipping subdomain setup"),o.stop();return}if(!s.isConfigured()){console.log("[Preview] Cloudflare not configured, skipping subdomain setup"),o.stop();return}try{let r=parseInt(process.env.ALLOCATED_HOST_PORT||"5173"),i=await s.addRoute(e,r);this.currentPreviewSubdomain=e,this.connection.setTunnelUrl(i),o.stop(),console.log(`[Preview] \u2713 Preview subdomain configured: ${i}`)}catch(r){o.stop(),console.error("[Preview] Failed to set up subdomain route:",r)}}async handleRepoChange(e){let t=new I("TOTAL REPO CHANGE");e.gitHubPat&&e.gitHubPat!==this.gitHubPat&&(console.log("[RepoChange] Updating git credentials"),this.gitHubPat=e.gitHubPat,await U(`echo "https://${e.gitHubPat}@github.com" > ~/.git-credentials`));let o=this.extractRepoPath(e.repoUrl),s=this.extractRepoPath(this.currentRepoUrl);if(o!==s){let i=new I("Clone new repository");await this.cloneRepository(e.repoUrl,e.branchName),this.currentRepoUrl=e.repoUrl,this.currentBranchName=e.branchName,i.stop(),await this.installDependencies();let a=new I("Restart services");await this.serviceManager.restartServices("both"),a.stop()}else{let i=new I(`Switch branch to ${e.branchName}`);await this.switchBranch(e.branchName),i.stop(),await this.checkNeedsReinstall()&&await this.installDependencies();let c=new I("Restart services");await this.serviceManager.restartServices("both"),c.stop()}e.previewSubdomain&&e.previewHostname&&await this.setupPreviewSubdomainFromEnv(e.previewSubdomain,e.previewHostname),t.stop(),console.log("[RepoChange] \u2713 Workspace updated")}extractRepoPath(e){let t=e.match(/github\.com[:/]([^/]+\/[^/]+?)(?:\.git)?$/);return t?t[1]:e}async cloneRepository(e,t){console.log(`[Clone] Clearing workspace and cloning ${this.extractRepoPath(e)} @ ${t}`),await U("rm -rf /workspace/* /workspace/.* 2>/dev/null || true"),await U(`git clone -b ${t} "${e}" /workspace`,{timeout:12e4}),await U('cd /workspace && git config user.email "agent@dotnetmentor.se" && git config user.name "Agent"'),console.log("[Clone] \u2713 Repository cloned")}async switchBranch(e){if(console.log(`[Branch] Switching from ${this.currentBranchName} to ${e}`),e===this.currentBranchName)console.log(`[Branch] Already on ${e}, pulling latest...`),await U(`git pull origin ${e}`,{cwd:this.workspaceDir,timeout:6e4});else{await U("git fetch origin",{cwd:this.workspaceDir,timeout:6e4});let{stdout:t}=await $t(`git ls-remote --heads origin ${e}`,{cwd:this.workspaceDir,timeout:3e4});t.trim().length>0?await U(`git checkout -B ${e} origin/${e}`,{cwd:this.workspaceDir,timeout:3e4}):(console.log(`[Branch] Branch ${e} doesn't exist on remote, creating from main...`),await U(`git checkout -b ${e}`,{cwd:this.workspaceDir,timeout:3e4})),this.currentBranchName=e}console.log(`[Branch] \u2713 Now on ${e}`)}getLockfileHash(){let e=N.join(this.workspaceDir,"packages/backoffice-web/package-lock.json");return k.existsSync(e)?Lt("sha256").update(k.readFileSync(e)).digest("hex"):null}async checkNeedsReinstall(){let e=this.getLockfileHash();return!e||!this.lastLockfileHash?!0:e!==this.lastLockfileHash}async checkCodeDiff(e,t){if(e===t)return!1;try{let{stdout:o}=await $t(`git diff ${e}..HEAD --name-only`,{cwd:this.workspaceDir,timeout:3e4}),s=o.trim();if(!s)return console.log(`[CodeDiff] No file differences between ${e} and ${t}`),!1;let r=s.split(`
1344
- `).filter(c=>c.length>0);console.log(`[CodeDiff] ${r.length} files changed between ${e} and ${t}`);let i=[".cs",".tsx",".ts",".json",".csproj"],a=r.some(c=>i.some(l=>c.endsWith(l)));return a&&console.log("[CodeDiff] Found code changes that require service restart"),a}catch(o){return console.error("[CodeDiff] Failed to check diff:",o instanceof Error?o.message:o),!0}}async installDependencies(){let e=new I("TOTAL INSTALL DEPENDENCIES");if(k.existsSync(N.join(this.workspaceDir,"packages/dotnet-api"))){let t=new I("dotnet restore");await U("dotnet restore",{cwd:N.join(this.workspaceDir,"packages/dotnet-api"),timeout:12e4}),t.stop()}if(k.existsSync(N.join(this.workspaceDir,"packages/backoffice-web/package.json"))){let t=N.join(this.workspaceDir,"packages/backoffice-web"),o=N.join(t,"node_modules"),s=N.join(t,"package-lock.json"),r=N.join(o,".lockhash"),i="/opt/cache";k.mkdirSync(i,{recursive:!0});let a=k.existsSync(s)?Lt("sha256").update(k.readFileSync(s)).digest("hex"):null,c=k.existsSync(r)?k.readFileSync(r,"utf-8").trim():null,l=N.join(i,"node_modules","current_hash"),d=k.existsSync(l)?k.readFileSync(l,"utf-8").trim():null,u=d?N.join(i,"node_modules",d):null,g=a?N.join(i,"node_modules",a):null;if(console.log(`[Install] node_modules exists: ${k.existsSync(o)}`),console.log(`[Install] lockHash (current branch): ${a?.substring(0,8)||"null"}`),console.log(`[Install] installedHash: ${c?.substring(0,8)||"null"}`),console.log(`[Install] dockerCacheHash (from image): ${d?.substring(0,8)||"null"}`),console.log(`[Install] exactMatchCache exists: ${g?k.existsSync(g):!1}`),console.log(`[Install] dockerCachedNodeModules exists: ${u?k.existsSync(u):!1}`),!k.existsSync(o)){if(g&&k.existsSync(g)){let v=new I("Copy exact-match cached node_modules");console.log(`[Install] Copying exact-match cached node_modules (hash ${a?.substring(0,8)})...`),await U(`cp -r "${g}" "${o}"`),a&&k.writeFileSync(r,a),c=a,v.stop()}else if(u&&k.existsSync(u)){let v=new I("Copy Docker-cached node_modules (as base)");console.log(`[Install] Copying Docker-cached node_modules as base (hash ${d?.substring(0,8)}, need ${a?.substring(0,8)})...`),await U(`cp -r "${u}" "${o}"`),v.stop()}}let w=!k.existsSync(o)||a&&a!==c;if(console.log(`[Install] needsInstall: ${w}`),!w)console.log("[Install] npm install skipped (cache OK)");else{let v=new I("npm install --prefer-offline");await U("npm install --prefer-offline --no-progress --fund=false",{cwd:t,timeout:18e4}),v.stop(),a&&(k.mkdirSync(o,{recursive:!0}),k.writeFileSync(r,a))}this.lastLockfileHash=a}e.stop()}};function Bt(n,e,t){let o=n.getHubProxy(),s=process.env.CONTAINER_ID||"unknown";return{onAssistantText:async r=>{let i={type:"Text",content:r,isError:!1};await o.reportSessionOutput(s,e,i).catch(a=>{console.error("Failed to report assistant text:",a)})},onAssistantToolUse:async(r,i)=>{if(["Write","Edit","MultiEdit"].includes(r)){let c=i,l=c.file_path||c.target_file;l&&t.onFileModified(l)}let a={type:"ToolUse",toolName:r,toolInput:JSON.stringify(i),isError:!1};await o.reportSessionOutput(s,e,a).catch(c=>{console.error("Failed to report tool use:",c)})},onResult:async(r,i)=>{let a={type:"System",event:"completed",content:r,cost:i,isError:!1};await o.reportSessionOutput(s,e,a).catch(c=>{console.error("Failed to report result:",c)})},onError:async r=>{let i={type:"System",event:"error",content:r,isError:!0};await o.reportSessionOutput(s,e,i).catch(a=>{console.error("Failed to report error:",a)})},onAborted:async()=>{let r={type:"System",event:"aborted",content:"Agent stopped by user",isError:!1};await o.reportSessionOutput(s,e,r).catch(i=>{console.error("Failed to report abort:",i)})},onSystemInit:async r=>{console.log(`[Callbacks] Claude session initialized: ${r}`),t.onClaudeSessionId(r),await o.setClaudeSessionId(e,r).catch(i=>{console.error("Failed to set Claude session ID:",i)})},onRawMessage:r=>{if(r.type==="assistant"&&r.message?.usage){let i=r.message.usage,a={totalCostUsd:In(i),inputTokens:i.input_tokens||0,outputTokens:i.output_tokens||0,cacheCreationTokens:i.cache_creation_input_tokens||0,cacheReadTokens:i.cache_read_input_tokens||0};o.reportSessionCost(s,e,a).catch(c=>{console.error("Failed to report cost:",c)})}}}}var Me={inputPerMillion:3,outputPerMillion:15,cacheReadPerMillion:.3,cacheCreationPerMillion:3.75};function In(n){return(n.input_tokens||0)/1e6*Me.inputPerMillion+(n.output_tokens||0)/1e6*Me.outputPerMillion+(n.cache_read_input_tokens||0)/1e6*Me.cacheReadPerMillion+(n.cache_creation_input_tokens||0)/1e6*Me.cacheCreationPerMillion}import*as re from"fs";import*as qt from"path";function Gt(n,e){let t=qt.join(n,`.agent-questions-${e}.json`);if(!re.existsSync(t))return null;try{let o=re.readFileSync(t,"utf-8");return re.unlinkSync(t),JSON.parse(o)}catch(o){return console.error("Failed to read questions file:",o),null}}async function Kt(n,e,t,o){if(e.size===0)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let s=o?H(o):`${t}/packages/dotnet-api`,r=o?B(o):`${t}/packages/backoffice-web`,i=o?.services?.backend?.extensions??[".cs",".csproj"],a=o?.services?.frontend?.extensions??[".ts",".tsx"],c=s&&[...e].some(g=>g.includes(s)||i.some(w=>g.endsWith(w))),l=r&&[...e].some(g=>g.includes(r)||a.some(w=>g.endsWith(w)));if(!c&&!l)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let d=[];c&&d.push("backend"),l&&d.push("frontend"),console.log(`[TypeCheck] Checking ${d.join(" & ")}...`);let u=[];if(c){let g=await n.checkBackendBuild();!g.success&&g.errors&&u.push(`## Backend Build Errors (in ${s})
1342
+ `)}`:"";m.backendError||(m.backendError=`Backend failed to start.${P}`)}return _&&!j.web&&(m.success=!1,m.frontendError||(m.frontendError="Frontend failed to start")),m},yt=async()=>{if(!l||!T.existsSync(l))return{success:!0};try{return ee(w,{cwd:l,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),{success:!0}}catch(y){let m=y;return{success:!1,errors:m.stdout||m.stderr||"Build failed"}}},bt=async()=>{if(!u||!T.existsSync(J.join(u,"package.json")))return{success:!0};try{return ee(A,{cwd:u,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),{success:!0}}catch(y){let m=y;return{success:!1,errors:m.stdout||m.stderr||"Type check failed"}}},Vt=async()=>{let[y,m]=await Promise.all([yt(),bt()]);return{backend:y,frontend:m}},St=async(y,m)=>{let S=y==="backend"?D:b;if(!T.existsSync(S))return[`Log file not found: ${S}`];try{return ee(`tail -n ${m} ${S}`,{encoding:"utf-8"}).split(`
1343
+ `).filter(Boolean)}catch(_){return[`Error reading logs: ${_ instanceof Error?_.message:String(_)}`]}};return{startBackend:C,startFrontend:ue,stopBackend:ft,stopFrontend:mt,restartServices:zt,checkHealth:$e,waitForHealth:ht,getProcesses:()=>({backend:f,frontend:O}),checkBackendBuild:yt,checkFrontendTypes:bt,runTypeChecks:Vt,tailLogs:St,checkSwaggerEndpoints:async y=>{let m=J.join(e,"swagger.json");if(!T.existsSync(m))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let S=JSON.parse(T.readFileSync(m,"utf-8")),_=Object.keys(S.paths||{}),j=[];for(let E of _)if(E.toLowerCase().includes(y.toLowerCase())){let P=Object.keys(S.paths[E]);for(let Q of P)j.push(`${Q.toUpperCase()} ${E}`)}return{foundEndpoints:j,totalEndpoints:_.length}}catch(S){return{foundEndpoints:[`Error parsing swagger.json: ${S instanceof Error?S.message:String(S)}`],totalEndpoints:0}}}}}function vn(n){let e=n.toLowerCase();return e.includes("exception")||e.includes("fail:")||e.includes("[err]")||e.includes("[error]")}import{execSync as te}from"child_process";function De(n){let e=async()=>{try{return te("git status --porcelain",{cwd:n,encoding:"utf-8"}).trim().length>0}catch{return!1}},t=async()=>{try{return te("git branch --show-current",{cwd:n,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:e,commitAndPush:async(s,r)=>{try{if(!await e())return{success:!0,noChanges:!0};let i=s.length>60?s.substring(0,60)+"...":s,c=`${r?"[agent] (partial)":"[agent]"} ${i}`;te("git add -A",{cwd:n}),te(`git commit -m "${c.replace(/"/g,'\\"')}"`,{cwd:n,encoding:"utf-8"});let l=te("git rev-parse --short HEAD",{cwd:n,encoding:"utf-8"}).trim();try{te("git push",{cwd:n,stdio:"pipe"})}catch(u){let d=u instanceof Error?u.message:String(u);if(d.includes("no upstream branch")){let g=await t();te(`git push --set-upstream origin ${g}`,{cwd:n,stdio:"pipe"})}else return console.error("[Git] Push failed:",d),{success:!0,commitHash:l,error:`Committed but push failed: ${d}`}}return{success:!0,commitHash:l}}catch(i){let a=i instanceof Error?i.message:String(i);return console.error("[Git] Error:",a),{success:!1,error:a}}},getCurrentBranch:t}}import*as se from"@microsoft/signalr";var X=class{constructor(e,t){this.connection=e;this.receiverMethod=t}dispose=()=>{for(let e of this.receiverMethod)this.connection.off(e.methodName,e.method)}},Mt=n=>{if(n==="IDeploymentHub")return ze.Instance;if(n==="IKanbanHub")return Je.Instance;if(n==="IAgentHub")return Ze.Instance;if(n==="ISlaveHub")return tt.Instance;if(n==="ITestHub")return ot.Instance},Ft=n=>{if(n==="IDeploymentHubClient")return rt.Instance;if(n==="IKanbanBoardClient")return it.Instance;if(n==="IAgentReceiver")return at.Instance;if(n==="IBackofficeReceiver")return ct.Instance;if(n==="ISlaveHubClient")return lt.Instance;if(n==="ITestHubClient")return pt.Instance},ze=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Ve(e)},Ve=class{constructor(e){this.connection=e}registerMachine=async(e,t)=>await this.connection.invoke("RegisterMachine",e,t);reportDeploymentStatus=async e=>await this.connection.invoke("ReportDeploymentStatus",e);reportHealth=async e=>await this.connection.invoke("ReportHealth",e);reportMachineStatus=async e=>await this.connection.invoke("ReportMachineStatus",e);sendDeploymentLog=async e=>await this.connection.invoke("SendDeploymentLog",e)},Je=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Xe(e)},Xe=class{constructor(e){this.connection=e}joinBoard=async e=>await this.connection.invoke("JoinBoard",e);leaveBoard=async e=>await this.connection.invoke("LeaveBoard",e)},Ze=class n{static Instance=new n;constructor(){}createHubProxy=e=>new et(e)},et=class{constructor(e){this.connection=e}registerAgent=async e=>await this.connection.invoke("RegisterAgent",e);getSecrets=async()=>await this.connection.invoke("GetSecrets");reportStatus=async(e,t)=>await this.connection.invoke("ReportStatus",e,t);reportSessionOutput=async(e,t,o)=>await this.connection.invoke("ReportSessionOutput",e,t,o);reportSessionCost=async(e,t,o)=>await this.connection.invoke("ReportSessionCost",e,t,o);reportInsight=async(e,t)=>await this.connection.invoke("ReportInsight",e,t);setClaudeSessionId=async(e,t)=>await this.connection.invoke("SetClaudeSessionId",e,t);sessionCompleted=async(e,t,o)=>await this.connection.invoke("SessionCompleted",e,t,o);initSessionCompleted=async(e,t,o,s)=>await this.connection.invoke("InitSessionCompleted",e,t,o,s);reportPreviewBuild=async(e,t,o)=>await this.connection.invoke("ReportPreviewBuild",e,t,o);saveSpecification=async(e,t,o,s)=>await this.connection.invoke("SaveSpecification",e,t,o,s);getSpecification=async(e,t)=>await this.connection.invoke("GetSpecification",e,t);listSpecifications=async e=>await this.connection.invoke("ListSpecifications",e);deleteSpecification=async(e,t)=>await this.connection.invoke("DeleteSpecification",e,t);watchContainer=async e=>await this.connection.invoke("WatchContainer",e);watchSession=async e=>await this.connection.invoke("WatchSession",e);watchSlaves=async()=>await this.connection.invoke("WatchSlaves");unwatchSlaves=async()=>await this.connection.invoke("UnwatchSlaves");sendPrompt=async(e,t,o,s)=>await this.connection.invoke("SendPrompt",e,t,o,s);stopSession=async e=>await this.connection.invoke("StopSession",e);switchSession=async(e,t)=>await this.connection.invoke("SwitchSession",e,t);createSession=async e=>await this.connection.invoke("CreateSession",e)},tt=class n{static Instance=new n;constructor(){}createHubProxy=e=>new nt(e)},nt=class{constructor(e){this.connection=e}register=async(e,t)=>await this.connection.invoke("Register",e,t);getSecrets=async()=>await this.connection.invoke("GetSecrets");containerCreated=async e=>await this.connection.invoke("ContainerCreated",e);containerReady=async e=>await this.connection.invoke("ContainerReady",e);reportCapacity=async e=>await this.connection.invoke("ReportCapacity",e)},ot=class n{static Instance=new n;constructor(){}createHubProxy=e=>new st(e)},st=class{constructor(e){this.connection=e}joinProject=async e=>await this.connection.invoke("JoinProject",e);leaveProject=async e=>await this.connection.invoke("LeaveProject",e)},rt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...i)=>t.deploy(...i),s=(...i)=>t.stop(...i);e.on("Deploy",o),e.on("Stop",s);let r=[{methodName:"Deploy",method:o},{methodName:"Stop",method:s}];return new X(e,r)}},it=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...f)=>t.boardUpdated(...f),s=(...f)=>t.boardDeleted(...f),r=(...f)=>t.columnCreated(...f),i=(...f)=>t.columnUpdated(...f),a=(...f)=>t.columnDeleted(...f),c=(...f)=>t.cardCreated(...f),l=(...f)=>t.cardUpdated(...f),u=(...f)=>t.cardDeleted(...f),d=(...f)=>t.subtaskCreated(...f),g=(...f)=>t.subtaskUpdated(...f),w=(...f)=>t.subtaskDeleted(...f),v=(...f)=>t.noteCreated(...f),A=(...f)=>t.noteUpdated(...f),D=(...f)=>t.noteDeleted(...f);e.on("BoardUpdated",o),e.on("BoardDeleted",s),e.on("ColumnCreated",r),e.on("ColumnUpdated",i),e.on("ColumnDeleted",a),e.on("CardCreated",c),e.on("CardUpdated",l),e.on("CardDeleted",u),e.on("SubtaskCreated",d),e.on("SubtaskUpdated",g),e.on("SubtaskDeleted",w),e.on("NoteCreated",v),e.on("NoteUpdated",A),e.on("NoteDeleted",D);let b=[{methodName:"BoardUpdated",method:o},{methodName:"BoardDeleted",method:s},{methodName:"ColumnCreated",method:r},{methodName:"ColumnUpdated",method:i},{methodName:"ColumnDeleted",method:a},{methodName:"CardCreated",method:c},{methodName:"CardUpdated",method:l},{methodName:"CardDeleted",method:u},{methodName:"SubtaskCreated",method:d},{methodName:"SubtaskUpdated",method:g},{methodName:"SubtaskDeleted",method:w},{methodName:"NoteCreated",method:v},{methodName:"NoteUpdated",method:A},{methodName:"NoteDeleted",method:D}];return new X(e,b)}},at=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...d)=>t.initSession(...d),s=(...d)=>t.runPrompt(...d),r=(...d)=>t.setModel(...d),i=()=>t.stopAgent(),a=()=>t.ping(),c=(...d)=>t.switchSession(...d),l=(...d)=>t.projectPromptUpdated(...d);e.on("InitSession",o),e.on("RunPrompt",s),e.on("SetModel",r),e.on("StopAgent",i),e.on("Ping",a),e.on("SwitchSession",c),e.on("ProjectPromptUpdated",l);let u=[{methodName:"InitSession",method:o},{methodName:"RunPrompt",method:s},{methodName:"SetModel",method:r},{methodName:"StopAgent",method:i},{methodName:"Ping",method:a},{methodName:"SwitchSession",method:c},{methodName:"ProjectPromptUpdated",method:l}];return new X(e,u)}},ct=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...b)=>t.sessionOutput(...b),s=(...b)=>t.sessionCost(...b),r=(...b)=>t.sessionCompleted(...b),i=(...b)=>t.containerStatus(...b),a=(...b)=>t.slaveCapacityUpdate(...b),c=(...b)=>t.previewBuildStatus(...b),l=(...b)=>t.previewReload(...b),u=(...b)=>t.questionsReceived(...b),d=(...b)=>t.specificationUpdated(...b),g=(...b)=>t.singleQuestionReceived(...b),w=(...b)=>t.questionSessionEnded(...b),v=(...b)=>t.creditsUpdated(...b),A=(...b)=>t.insufficientCredits(...b);e.on("SessionOutput",o),e.on("SessionCost",s),e.on("SessionCompleted",r),e.on("ContainerStatus",i),e.on("SlaveCapacityUpdate",a),e.on("PreviewBuildStatus",c),e.on("PreviewReload",l),e.on("QuestionsReceived",u),e.on("SpecificationUpdated",d),e.on("SingleQuestionReceived",g),e.on("QuestionSessionEnded",w),e.on("CreditsUpdated",v),e.on("InsufficientCredits",A);let D=[{methodName:"SessionOutput",method:o},{methodName:"SessionCost",method:s},{methodName:"SessionCompleted",method:r},{methodName:"ContainerStatus",method:i},{methodName:"SlaveCapacityUpdate",method:a},{methodName:"PreviewBuildStatus",method:c},{methodName:"PreviewReload",method:l},{methodName:"QuestionsReceived",method:u},{methodName:"SpecificationUpdated",method:d},{methodName:"SingleQuestionReceived",method:g},{methodName:"QuestionSessionEnded",method:w},{methodName:"CreditsUpdated",method:v},{methodName:"InsufficientCredits",method:A}];return new X(e,D)}},lt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...a)=>t.startContainer(...a),s=(...a)=>t.terminateContainer(...a),r=()=>t.reconnect();e.on("StartContainer",o),e.on("TerminateContainer",s),e.on("Reconnect",r);let i=[{methodName:"StartContainer",method:o},{methodName:"TerminateContainer",method:s},{methodName:"Reconnect",method:r}];return new X(e,i)}},pt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...l)=>t.testRunCreated(...l),s=(...l)=>t.testRunUpdated(...l),r=(...l)=>t.testSuiteCreated(...l),i=(...l)=>t.testCreated(...l),a=(...l)=>t.testUpdated(...l);e.on("TestRunCreated",o),e.on("TestRunUpdated",s),e.on("TestSuiteCreated",r),e.on("TestCreated",i),e.on("TestUpdated",a);let c=[{methodName:"TestRunCreated",method:o},{methodName:"TestRunUpdated",method:s},{methodName:"TestSuiteCreated",method:r},{methodName:"TestCreated",method:i},{methodName:"TestUpdated",method:a}];return new X(e,c)}};var je=class{constructor(e){this.config=e;let t={"X-Container-Id":e.containerId,"X-Project-Key":e.projectKey};e.isByok&&(t["X-Is-Byok"]="true"),this.connection=new se.HubConnectionBuilder().withUrl(`${e.masterUrl}/api/hubs/agent`,{headers:t}).withAutomaticReconnect({nextRetryDelayInMilliseconds:o=>{let s=o.previousRetryCount+1,r=Math.min(1e3*Math.pow(2,o.previousRetryCount),6e4);return console.log(`[SignalR] Reconnect attempt ${s} (will retry in ${r}ms)`),r}}).withServerTimeout(3e5).withKeepAliveInterval(1e4).configureLogging(se.LogLevel.Information).build(),this.hubProxy=Mt("IAgentHub").createHubProxy(this.connection),this.connection.onreconnected(async()=>{console.log("[SignalR] Reconnected to gateway");try{await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Re-registered as agent for container: ${this.config.containerId}`),await this.reportStatus(this.currentStatus,void 0,this.currentSessionId??void 0,this.currentProjectId??void 0)}catch(o){console.error("[SignalR] Failed to re-register agent after reconnect:",o instanceof Error?o.message:o)}}),this.connection.onclose(o=>{console.error("[SignalR] Connection closed:",o?.message),process.exit(1)})}connection;hubProxy;receiverSubscription=null;currentStatus="WarmingUp";currentSessionId=null;currentProjectId=null;tunnelUrl=null;async connect(){console.log(`[SignalR] Connecting to gateway: ${this.config.masterUrl}/api/hubs/agent`),this.config.isByok&&console.log("[SignalR] BYOK mode enabled - credits will not be deducted"),await this.connection.start(),console.log("[SignalR] Connected to gateway!"),await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Registered as agent for container: ${this.config.containerId}`)}registerReceiver(e){this.receiverSubscription&&this.receiverSubscription.dispose(),this.receiverSubscription=Ft("IAgentReceiver").register(this.connection,e)}getHubProxy(){return this.hubProxy}getMasterUrl(){return this.config.masterUrl}getProjectKey(){return this.config.projectKey}getConnection(){return this.connection}setTunnelUrl(e){this.tunnelUrl=e}async reportStatus(e,t,o,s){if(this.currentStatus=e,o&&(this.currentSessionId=o),s&&(this.currentProjectId=s),this.connection.state===se.HubConnectionState.Connected)try{let r={status:e,error:t,sessionId:o,projectId:s,tunnelUrl:this.tunnelUrl??void 0};await this.hubProxy.reportStatus(this.config.containerId,r),console.log(`[Status] ${e}${t?` (${t})`:""}${this.tunnelUrl?` [${this.tunnelUrl}]`:""}${o?` [session: ${o}]`:""}`)}catch(r){console.error("[SignalR] Failed to report status:",r)}}async getSecrets(){let e=await this.hubProxy.getSecrets();if(!e.success)throw new Error(`Failed to get secrets: ${e.error}`);return e.secrets||{}}};function Cn(n){let e=n.match(/name:\s*"([^"]+)"/);if(e)return e[1];let t=n.match(/^#\s+(.+)$/m);return t?t[1].trim():"Untitled Specification"}var Oe=class{constructor(e,t){this.hubProxy=e;this.projectId=t}async saveSpecification(e,t){let o=Cn(t),s=await this.hubProxy.saveSpecification(this.projectId,e,o,t);if(!s.success)throw new Error(s.error||"Failed to save specification")}async getSpecification(e){let t=await this.hubProxy.getSpecification(this.projectId,e);return t.success&&t.content||null}async listSpecifications(){let e=await this.hubProxy.listSpecifications(this.projectId);return!e.success||!e.specifications?[]:e.specifications.map(t=>({slug:t.slug,name:t.name,status:t.status,version:"1.0.0"}))}async deleteSpecification(e){return(await this.hubProxy.deleteSpecification(this.projectId,e)).success}async specificationExists(e){let t=await this.hubProxy.getSpecification(this.projectId,e);return t.success&&t.content!=null}};import{exec as _n,execSync as Pn,spawn as Rn}from"child_process";import{promisify as Tn}from"util";import{createHash as Lt}from"crypto";import*as k from"fs";import*as N from"path";var Ue=class{domain;accountId;tunnelId;apiToken;zoneId;tunnelApiBase;constructor(){this.domain=process.env.CLOUDFLARE_DEPLOY_DOMAIN||process.env.DEPLOY_DOMAIN||"vibecodementor.net",this.accountId=process.env.CLOUDFLARE_ACCOUNT_ID||"",this.tunnelId=process.env.CLOUDFLARE_TUNNEL_ID||"",this.apiToken=process.env.CLOUDFLARE_API_TOKEN||"",this.zoneId=process.env.CLOUDFLARE_ZONE_ID||"",this.tunnelApiBase=`https://api.cloudflare.com/client/v4/accounts/${this.accountId}/cfd_tunnel/${this.tunnelId}/configurations`}isConfigured(){return!!(this.accountId&&this.tunnelId&&this.apiToken&&this.zoneId)}buildHostname(e){let t=this.domain.split("."),o=t.length>2?t.slice(-2).join("."):this.domain;return`${e}.${o}`}async addRoute(e,t){let o=this.buildHostname(e);if(console.log(`[TunnelManager] Adding route: ${o} -> localhost:${t}`),!this.isConfigured())return console.log("[TunnelManager] Not configured, skipping route addition"),`https://${o}`;try{let s=await this.getConfig(),r=s.ingress.findIndex(a=>a.hostname===o);if(r!==-1)s.ingress[r].service=`http://localhost:${t}`;else{let a=s.ingress.findIndex(l=>!l.hostname),c={hostname:o,service:`http://localhost:${t}`};a!==-1?s.ingress.splice(a,0,c):(s.ingress.push(c),s.ingress.push({service:"http_status:404"}))}await this.putConfig(s),await this.ensureDnsPointsToTunnel(o);let i=`https://${o}`;return console.log(`[TunnelManager] \u2713 Route added: ${i}`),i}catch(s){throw console.error("[TunnelManager] Failed to add route:",s),s}}async removeRoute(e){let t=this.buildHostname(e);if(console.log(`[TunnelManager] Removing route: ${t}`),!this.isConfigured()){console.log("[TunnelManager] Not configured, skipping route removal");return}try{let o=await this.getConfig();o.ingress=o.ingress.filter(s=>s.hostname!==t),o.ingress.some(s=>!s.hostname)||o.ingress.push({service:"http_status:404"}),await this.putConfig(o),console.log(`[TunnelManager] \u2713 Route removed: ${t}`)}catch(o){throw console.error("[TunnelManager] Failed to remove route:",o),o}}async getConfig(){let t=await(await fetch(this.tunnelApiBase,{method:"GET",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"}})).json();if(!t.success)throw new Error(`Cloudflare API error: ${t.errors.map(o=>o.message).join(", ")}`);return t.result?.config||{ingress:[{service:"http_status:404"}]}}async putConfig(e){let o=await(await fetch(this.tunnelApiBase,{method:"PUT",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({config:e})})).json();if(!o.success)throw new Error(`Cloudflare API error: ${o.errors.map(s=>s.message).join(", ")}`);console.log("[TunnelManager] Configuration updated via Cloudflare API")}async ensureDnsPointsToTunnel(e){let t=`${this.tunnelId}.cfargotunnel.com`,o=`https://api.cloudflare.com/client/v4/zones/${this.zoneId}/dns_records`;try{let r=await(await fetch(`${o}?name=${e}&type=CNAME`,{method:"GET",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"}})).json();if(!r.success||!r.result?.length){console.log(`[TunnelManager] No DNS record found for ${e}, skipping DNS update`);return}let i=r.result[0];if(i.content===t){console.log(`[TunnelManager] DNS already points to correct tunnel: ${e}`);return}console.log(`[TunnelManager] Updating DNS: ${e} from ${i.content} to ${t}`);let c=await(await fetch(`${o}/${i.id}`,{method:"PATCH",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({type:"CNAME",name:e,content:t,proxied:!0})})).json();c.success?console.log(`[TunnelManager] \u2713 DNS updated: ${e} -> ${t}`):console.error(`[TunnelManager] Failed to update DNS: ${c.errors.map(l=>l.message).join(", ")}`)}catch(s){console.error(`[TunnelManager] Error updating DNS for ${e}:`,s)}}};var $t=Tn(_n),I=class{start;label;constructor(e){this.label=e,this.start=Date.now(),console.log(`[TIMING] \u23F1 START: ${e}`)}stop(){let e=Date.now()-this.start,t=(e/1e3).toFixed(2);return console.log(`[TIMING] \u2713 DONE: ${this.label} (${t}s)`),e}};function U(n,e={}){return new Promise((t,o)=>{let s=Rn(n,[],{shell:!0,stdio:"inherit",cwd:e.cwd}),r=null;e.timeout&&(r=setTimeout(()=>{s.kill(),o(new Error(`Command timed out: ${n}`))},e.timeout)),s.on("close",i=>{r&&clearTimeout(r),i===0?t():o(new Error(`Command failed with code ${i}: ${n}`))}),s.on("error",i=>{r&&clearTimeout(r),o(i)})})}var Ht="main",Ne=class{constructor(e,t,o,s){this.connection=e;this.serviceManager=t;this.workspaceDir=o;this.gitHubPat=s}currentBranchName=Ht;currentRepoUrl="";lastLockfileHash=null;tunnelManager=null;currentPreviewSubdomain=null;getTunnelManager(){return this.tunnelManager||(this.tunnelManager=new Ue),this.tunnelManager}async prewarmWorkspace(){let e=new I("TOTAL INIT"),t=process.env.REPO_URL,o=process.env.BRANCH_NAME||Ht,s=process.env.PREVIEW_SUBDOMAIN,r=process.env.PREVIEW_HOSTNAME,i=process.env.PROJECT_GITHUB_PAT;if(console.log("[Init] Starting single-phase init..."),console.log(`[Init] Repo: ${t?this.extractRepoPath(t):"NOT SET"}`),console.log(`[Init] Branch: ${o}`),console.log(`[Init] Preview: ${s||"none"}`),!t)throw console.error("[Init] \u274C REPO_URL not set - cannot initialize workspace"),new Error("REPO_URL environment variable is required");i&&(this.gitHubPat=i);let a=new I("Clean workspace");Pn("rm -rf /workspace/* /workspace/.* 2>/dev/null || true",{stdio:"inherit"}),a.stop();let c=new I("Clone repository");await this.cloneRepository(t,o),this.currentRepoUrl=t,this.currentBranchName=o,c.stop(),await this.installDependencies(),this.lastLockfileHash=this.getLockfileHash(),console.log("[Init] Starting services...");let l=new I("Start backend (dotnet)");await this.serviceManager.startBackend(),l.stop();let u=new I("Start frontend (vite)");await this.serviceManager.startFrontend(),u.stop();let d=new I("Wait for health check");await this.serviceManager.waitForHealth(3e4),d.stop(),s&&r&&await this.setupPreviewSubdomainFromEnv(s,r),e.stop(),console.log("[Init] \u2713 Workspace ready")}async handleInitSession(e){console.log(`[InitSession] Session ${e.sessionId} starting...`);let t=this.extractRepoPath(e.repoUrl),o=this.extractRepoPath(this.currentRepoUrl),s=t!==o,r=e.branchName!==this.currentBranchName;s||r?(console.log("[InitSession] Repo/branch mismatch - updating workspace..."),console.log(`[InitSession] Current: ${o}@${this.currentBranchName}`),console.log(`[InitSession] Requested: ${t}@${e.branchName}`),await this.connection.reportStatus("Ready",void 0,e.sessionId,e.projectId),await this.connection.getHubProxy().initSessionCompleted(process.env.CONTAINER_ID||"unknown",e.sessionId,!0,""),this.handleRepoChange(e).catch(async a=>{let c=a instanceof Error?a.message:"Unknown error";console.error("[InitSession] Background update failed:",c),await this.connection.reportStatus("Error",c,e.sessionId,e.projectId)})):(console.log("[InitSession] Workspace already matches - ready immediately"),await this.connection.reportStatus("Ready",void 0,e.sessionId,e.projectId),await this.connection.getHubProxy().initSessionCompleted(process.env.CONTAINER_ID||"unknown",e.sessionId,!0,"")),console.log(`[InitSession] \u2713 Ready for session ${e.sessionId}`)}async setupPreviewSubdomainFromEnv(e,t){let o=new I("Setup preview subdomain");console.log(`[Preview] Setting up preview subdomain: ${e} (${t})`);let s=this.getTunnelManager();if(!s){console.log("[Preview] TunnelManager not available, skipping subdomain setup"),o.stop();return}if(!s.isConfigured()){console.log("[Preview] Cloudflare not configured, skipping subdomain setup"),o.stop();return}try{let r=parseInt(process.env.ALLOCATED_HOST_PORT||"5173"),i=await s.addRoute(e,r);this.currentPreviewSubdomain=e,this.connection.setTunnelUrl(i),o.stop(),console.log(`[Preview] \u2713 Preview subdomain configured: ${i}`)}catch(r){o.stop(),console.error("[Preview] Failed to set up subdomain route:",r)}}async handleRepoChange(e){let t=new I("TOTAL REPO CHANGE");e.gitHubPat&&e.gitHubPat!==this.gitHubPat&&(console.log("[RepoChange] Updating git credentials"),this.gitHubPat=e.gitHubPat,await U(`echo "https://${e.gitHubPat}@github.com" > ~/.git-credentials`));let o=this.extractRepoPath(e.repoUrl),s=this.extractRepoPath(this.currentRepoUrl);if(o!==s){let i=new I("Clone new repository");await this.cloneRepository(e.repoUrl,e.branchName),this.currentRepoUrl=e.repoUrl,this.currentBranchName=e.branchName,i.stop(),await this.installDependencies();let a=new I("Restart services");await this.serviceManager.restartServices("both"),a.stop()}else{let i=new I(`Switch branch to ${e.branchName}`);await this.switchBranch(e.branchName),i.stop(),await this.checkNeedsReinstall()&&await this.installDependencies();let c=new I("Restart services");await this.serviceManager.restartServices("both"),c.stop()}e.previewSubdomain&&e.previewHostname&&await this.setupPreviewSubdomainFromEnv(e.previewSubdomain,e.previewHostname),t.stop(),console.log("[RepoChange] \u2713 Workspace updated")}extractRepoPath(e){let t=e.match(/github\.com[:/]([^/]+\/[^/]+?)(?:\.git)?$/);return t?t[1]:e}async cloneRepository(e,t){console.log(`[Clone] Clearing workspace and cloning ${this.extractRepoPath(e)} @ ${t}`),await U("rm -rf /workspace/* /workspace/.* 2>/dev/null || true"),await U(`git clone -b ${t} "${e}" /workspace`,{timeout:12e4}),await U('cd /workspace && git config user.email "agent@dotnetmentor.se" && git config user.name "Agent"'),console.log("[Clone] \u2713 Repository cloned")}async switchBranch(e){if(console.log(`[Branch] Switching from ${this.currentBranchName} to ${e}`),e===this.currentBranchName)console.log(`[Branch] Already on ${e}, pulling latest...`),await U(`git pull origin ${e}`,{cwd:this.workspaceDir,timeout:6e4});else{await U("git fetch origin",{cwd:this.workspaceDir,timeout:6e4});let{stdout:t}=await $t(`git ls-remote --heads origin ${e}`,{cwd:this.workspaceDir,timeout:3e4});t.trim().length>0?await U(`git checkout -B ${e} origin/${e}`,{cwd:this.workspaceDir,timeout:3e4}):(console.log(`[Branch] Branch ${e} doesn't exist on remote, creating from main...`),await U(`git checkout -b ${e}`,{cwd:this.workspaceDir,timeout:3e4})),this.currentBranchName=e}console.log(`[Branch] \u2713 Now on ${e}`)}getLockfileHash(){let e=N.join(this.workspaceDir,"packages/backoffice-web/package-lock.json");return k.existsSync(e)?Lt("sha256").update(k.readFileSync(e)).digest("hex"):null}async checkNeedsReinstall(){let e=this.getLockfileHash();return!e||!this.lastLockfileHash?!0:e!==this.lastLockfileHash}async checkCodeDiff(e,t){if(e===t)return!1;try{let{stdout:o}=await $t(`git diff ${e}..HEAD --name-only`,{cwd:this.workspaceDir,timeout:3e4}),s=o.trim();if(!s)return console.log(`[CodeDiff] No file differences between ${e} and ${t}`),!1;let r=s.split(`
1344
+ `).filter(c=>c.length>0);console.log(`[CodeDiff] ${r.length} files changed between ${e} and ${t}`);let i=[".cs",".tsx",".ts",".json",".csproj"],a=r.some(c=>i.some(l=>c.endsWith(l)));return a&&console.log("[CodeDiff] Found code changes that require service restart"),a}catch(o){return console.error("[CodeDiff] Failed to check diff:",o instanceof Error?o.message:o),!0}}async installDependencies(){let e=new I("TOTAL INSTALL DEPENDENCIES");if(k.existsSync(N.join(this.workspaceDir,"packages/dotnet-api"))){let t=new I("dotnet restore");await U("dotnet restore",{cwd:N.join(this.workspaceDir,"packages/dotnet-api"),timeout:12e4}),t.stop()}if(k.existsSync(N.join(this.workspaceDir,"packages/backoffice-web/package.json"))){let t=N.join(this.workspaceDir,"packages/backoffice-web"),o=N.join(t,"node_modules"),s=N.join(t,"package-lock.json"),r=N.join(o,".lockhash"),i="/opt/cache";k.mkdirSync(i,{recursive:!0});let a=k.existsSync(s)?Lt("sha256").update(k.readFileSync(s)).digest("hex"):null,c=k.existsSync(r)?k.readFileSync(r,"utf-8").trim():null,l=N.join(i,"node_modules","current_hash"),u=k.existsSync(l)?k.readFileSync(l,"utf-8").trim():null,d=u?N.join(i,"node_modules",u):null,g=a?N.join(i,"node_modules",a):null;if(console.log(`[Install] node_modules exists: ${k.existsSync(o)}`),console.log(`[Install] lockHash (current branch): ${a?.substring(0,8)||"null"}`),console.log(`[Install] installedHash: ${c?.substring(0,8)||"null"}`),console.log(`[Install] dockerCacheHash (from image): ${u?.substring(0,8)||"null"}`),console.log(`[Install] exactMatchCache exists: ${g?k.existsSync(g):!1}`),console.log(`[Install] dockerCachedNodeModules exists: ${d?k.existsSync(d):!1}`),!k.existsSync(o)){if(g&&k.existsSync(g)){let v=new I("Copy exact-match cached node_modules");console.log(`[Install] Copying exact-match cached node_modules (hash ${a?.substring(0,8)})...`),await U(`cp -r "${g}" "${o}"`),a&&k.writeFileSync(r,a),c=a,v.stop()}else if(d&&k.existsSync(d)){let v=new I("Copy Docker-cached node_modules (as base)");console.log(`[Install] Copying Docker-cached node_modules as base (hash ${u?.substring(0,8)}, need ${a?.substring(0,8)})...`),await U(`cp -r "${d}" "${o}"`),v.stop()}}let w=!k.existsSync(o)||a&&a!==c;if(console.log(`[Install] needsInstall: ${w}`),!w)console.log("[Install] npm install skipped (cache OK)");else{let v=new I("npm install --prefer-offline");await U("npm install --prefer-offline --no-progress --fund=false",{cwd:t,timeout:18e4}),v.stop(),a&&(k.mkdirSync(o,{recursive:!0}),k.writeFileSync(r,a))}this.lastLockfileHash=a}e.stop()}};function Bt(n,e,t){let o=n.getHubProxy(),s=process.env.CONTAINER_ID||"unknown";return{onAssistantText:async r=>{let i={type:"Text",content:r,isError:!1};await o.reportSessionOutput(s,e,i).catch(a=>{console.error("Failed to report assistant text:",a)})},onAssistantToolUse:async(r,i)=>{if(["Write","Edit","MultiEdit"].includes(r)){let c=i,l=c.file_path||c.target_file;l&&t.onFileModified(l)}let a={type:"ToolUse",toolName:r,toolInput:JSON.stringify(i),isError:!1};await o.reportSessionOutput(s,e,a).catch(c=>{console.error("Failed to report tool use:",c)})},onResult:async(r,i)=>{let a={type:"System",event:"completed",content:r,cost:i,isError:!1};await o.reportSessionOutput(s,e,a).catch(c=>{console.error("Failed to report result:",c)})},onError:async r=>{let i={type:"System",event:"error",content:r,isError:!0};await o.reportSessionOutput(s,e,i).catch(a=>{console.error("Failed to report error:",a)})},onAborted:async()=>{let r={type:"System",event:"aborted",content:"Agent stopped by user",isError:!1};await o.reportSessionOutput(s,e,r).catch(i=>{console.error("Failed to report abort:",i)})},onSystemInit:async r=>{console.log(`[Callbacks] Claude session initialized: ${r}`),t.onClaudeSessionId(r),await o.setClaudeSessionId(e,r).catch(i=>{console.error("Failed to set Claude session ID:",i)})},onRawMessage:r=>{if(r.type==="assistant"&&r.message?.usage){let i=r.message.usage,a={totalCostUsd:In(i),inputTokens:i.input_tokens||0,outputTokens:i.output_tokens||0,cacheCreationTokens:i.cache_creation_input_tokens||0,cacheReadTokens:i.cache_read_input_tokens||0};o.reportSessionCost(s,e,a).catch(c=>{console.error("Failed to report cost:",c)})}}}}var Me={inputPerMillion:3,outputPerMillion:15,cacheReadPerMillion:.3,cacheCreationPerMillion:3.75};function In(n){return(n.input_tokens||0)/1e6*Me.inputPerMillion+(n.output_tokens||0)/1e6*Me.outputPerMillion+(n.cache_read_input_tokens||0)/1e6*Me.cacheReadPerMillion+(n.cache_creation_input_tokens||0)/1e6*Me.cacheCreationPerMillion}import*as re from"fs";import*as qt from"path";function Gt(n,e){let t=qt.join(n,`.agent-questions-${e}.json`);if(!re.existsSync(t))return null;try{let o=re.readFileSync(t,"utf-8");return re.unlinkSync(t),JSON.parse(o)}catch(o){return console.error("Failed to read questions file:",o),null}}async function Kt(n,e,t,o){if(e.size===0)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let s=o?H(o):`${t}/packages/dotnet-api`,r=o?B(o):`${t}/packages/backoffice-web`,i=o?.services?.backend?.extensions??[".cs",".csproj"],a=o?.services?.frontend?.extensions??[".ts",".tsx"],c=s&&[...e].some(g=>g.includes(s)||i.some(w=>g.endsWith(w))),l=r&&[...e].some(g=>g.includes(r)||a.some(w=>g.endsWith(w)));if(!c&&!l)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let u=[];c&&u.push("backend"),l&&u.push("frontend"),console.log(`[TypeCheck] Checking ${u.join(" & ")}...`);let d=[];if(c){let g=await n.checkBackendBuild();!g.success&&g.errors&&d.push(`## Backend Build Errors (in ${s})
1345
1345
  \`\`\`
1346
1346
  ${g.errors}
1347
- \`\`\``)}if(l){let g=await n.checkFrontendTypes();!g.success&&g.errors&&u.push(`## Frontend Type Errors (in ${r})
1347
+ \`\`\``)}if(l){let g=await n.checkFrontendTypes();!g.success&&g.errors&&d.push(`## Frontend Type Errors (in ${r})
1348
1348
  \`\`\`
1349
1349
  ${g.errors}
1350
- \`\`\``)}return u.length>0?(console.log("[TypeCheck] \u274C Type errors found - will auto-correct"),{passed:!1,errorPrompt:`@debugger Type/build errors detected. Fix them.
1350
+ \`\`\``)}return d.length>0?(console.log("[TypeCheck] \u274C Type errors found - will auto-correct"),{passed:!1,errorPrompt:`@debugger Type/build errors detected. Fix them.
1351
1351
 
1352
1352
  WORKSPACE: ${t}
1353
1353
 
1354
- ${u.join(`
1354
+ ${d.join(`
1355
1355
 
1356
1356
  `)}
1357
1357
 
1358
- File paths like "src/..." are relative to the package directory shown in parentheses.`}):(console.log("[TypeCheck] \u2713 All type checks passed"),{passed:!0})}async function ut(n,e,t,o,s){if(!await n.hasChanges()){console.log("[Git] No changes to commit");return}console.log("[Git] Committing changes...");let i=await n.commitAndPush(o,s);if(i.noChanges)console.log("[Git] No changes to commit");else if(i.success){let a=await n.getCurrentBranch();console.log(`[Git] \u2713 Committed ${i.commitHash||""}`),console.log(`[Git] \u2192 Pushed to ${a}`),await e.getHubProxy().reportSessionOutput(process.env.CONTAINER_ID||"unknown",t,{type:"System",event:"committed",content:`Changes committed to ${a}`,isError:!1}).catch(l=>{console.error("[Git] Failed to report commit:",l)})}else console.error(`[Git] \u274C Git error: ${i.error}`)}var Fe=class{constructor(e,t,o,s,r=!0,i=!0,a,c,l=!0){this.connection=e;this.lifecycle=t;this.serviceManager=o;this.workspaceDir=s;this.enableAutoTypecheck=r;this.enableAutoCommit=i;this.setupPlanningTransport=a;this.projectConfig=c;this.useDefaultStack=l;this.gitManager=De(s)}currentSession=null;claudeSessionIdMap=new Map;currentAbortController=null;gitManager;async initSession(e){console.log(`[Receiver] InitSession for session ${e.sessionId}`),this.setupPlanningTransport&&e.projectId&&this.setupPlanningTransport(e.projectId),e.projectId&&await this.fetchProjectPrompt(e.projectId),this.currentSession={sessionId:e.sessionId,projectId:e.projectId,branchName:e.branchName,model:e.model==="auto"?void 0:e.model||"claude-haiku-4-5-20251001",debugMode:e.debugMode},await this.lifecycle.handleInitSession(e),e.prompt&&await this.runPrompt(e.sessionId,e.prompt,e.claudeSessionId)}async runPrompt(e,t,o,s=!1){let r=pe(t);if(console.log(`[Receiver] RunPrompt for session ${e}${o?` (ClaudeSessionId: ${o})`:""}${s?" [DEBUG MODE]":""}: ${r.substring(0,50)}...`),(!this.currentSession||this.currentSession.sessionId!==e)&&(console.log(`[Receiver] Session mismatch: expected ${this.currentSession?.sessionId}, got ${e}, switching...`),await this.switchSession(e),!this.currentSession)){console.error("[Receiver] Failed to create session context");return}let i=this.claudeSessionIdMap.get(e);if(o&&i!==o){console.warn(`[Receiver] Claude session not found in container: ${o}`);let l=this.connection.getHubProxy();await l.reportSessionOutput(process.env.CONTAINER_ID||"unknown",e,{type:"System",event:"sessionNotFound",content:o,isError:!0}),await this.connection.reportStatus("Ready","Session lost - summary needed",e,this.currentSession.projectId),await l.sessionCompleted(process.env.CONTAINER_ID||"unknown",e,0);return}await this.connection.reportStatus("Active",void 0,e,this.currentSession.projectId),this.currentSession.debugMode=s,this.currentAbortController=new AbortController;let a=i||null,c=new Set;try{let l=await Ae(r,{model:this.currentSession.model,sessionId:a||null,projectId:this.currentSession.projectId||null,apiUrl:this.connection.getMasterUrl(),debugLog:!0,debugMode:s,abortController:this.currentAbortController,projectConfig:this.projectConfig,useDefaultStack:this.useDefaultStack,callbacks:Bt(this.connection,e,{onClaudeSessionId:g=>{this.claudeSessionIdMap.set(e,g)},onFileModified:g=>{c.add(g)}})}),d=Gt(this.workspaceDir,e);if(d&&d.length>0){console.log(`[Receiver] ${d.length} pending questions found, waiting for user answers`),await this.connection.reportStatus("Ready",void 0,e,this.currentSession.projectId),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,0);return}if(this.enableAutoTypecheck&&!l.error&&!l.aborted){let g=await Kt(this.serviceManager,c,this.workspaceDir,this.projectConfig);if(!g.passed&&g.errorPrompt){console.log("[Receiver] Type errors detected, auto-correcting..."),await this.runPrompt(e,g.errorPrompt,o);return}}this.enableAutoCommit&&!l.error&&!l.aborted?await ut(this.gitManager,this.connection,e,t,!1):(l.error||l.aborted)&&this.enableAutoCommit&&await ut(this.gitManager,this.connection,e,t,!0),await this.connection.reportStatus("Ready",void 0,e,this.currentSession.projectId),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,l.error?1:0)}catch(l){let d=l instanceof Error&&l.name==="AbortError",u=l instanceof Error?l.message:"Unknown error";d?console.log(`[Receiver] Agent aborted for session ${e}`):(console.error("[Receiver] Agent error:",u),await this.connection.reportStatus("Error",u,e,this.currentSession.projectId)),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,d?0:1)}finally{this.currentAbortController=null}}async setModel(e){console.log(`[Receiver] Model changed to: ${e}`),this.currentSession&&(this.currentSession.model=e==="auto"?void 0:e)}async stopAgent(){console.log("[Receiver] Stop command received"),this.currentAbortController&&this.currentAbortController.abort()}async ping(){await this.connection.getConnection().invoke("Pong",process.env.CONTAINER_ID||"unknown")}async switchSession(e){console.log(`[Receiver] SwitchSession to ${e}`),this.currentSession?this.currentSession.sessionId=e:(console.log(`[Receiver] Creating new session context for ${e}`),this.currentSession={sessionId:e,projectId:process.env.PROJECT_ID||"unknown",branchName:"main",model:void 0,debugMode:!1})}async projectPromptUpdated(e){console.log(`[Receiver] ProjectPromptUpdated received (${e.length} chars)`),We(e)}async fetchProjectPrompt(e){try{let t=this.connection.getMasterUrl(),o=this.connection.getProjectKey(),s=await fetch(`${t}/api/projects/${e}/prompt`,{method:"GET",headers:{"Content-Type":"application/json","X-Project-Key":o}});if(!s.ok){console.log(`[Receiver] Failed to fetch project prompt: ${s.status}`);return}let r=await s.json();r.prompt?(We(r.prompt),console.log(`[Receiver] Initial project prompt loaded (${r.prompt.length} chars)`)):console.log(`[Receiver] No project prompt set for project ${e}`)}catch(t){console.error("[Receiver] Error fetching project prompt:",t)}}};process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT=process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT||"60000";var Wt=Y.dirname(En(import.meta.url));function An(){let n=[Y.resolve(Wt,"../package.json"),Y.resolve(Wt,"../../../package.json")];for(let e of n)try{if(M.existsSync(e)){let t=JSON.parse(M.readFileSync(e,"utf-8"));if(t.name==="glenn-code")return t.version||"unknown"}}catch{}return"unknown"}function xn(n){let{useDefaultStack:e,projectConfig:t,configSource:o,tunnelPort:s,enableAutoTypecheck:r,enableAutoCommit:i}=n;if(console.log(`
1358
+ File paths like "src/..." are relative to the package directory shown in parentheses.`}):(console.log("[TypeCheck] \u2713 All type checks passed"),{passed:!0})}async function dt(n,e,t,o,s){if(!await n.hasChanges()){console.log("[Git] No changes to commit");return}console.log("[Git] Committing changes...");let i=await n.commitAndPush(o,s);if(i.noChanges)console.log("[Git] No changes to commit");else if(i.success){let a=await n.getCurrentBranch();console.log(`[Git] \u2713 Committed ${i.commitHash||""}`),console.log(`[Git] \u2192 Pushed to ${a}`),await e.getHubProxy().reportSessionOutput(process.env.CONTAINER_ID||"unknown",t,{type:"System",event:"committed",content:`Changes committed to ${a}`,isError:!1}).catch(l=>{console.error("[Git] Failed to report commit:",l)})}else console.error(`[Git] \u274C Git error: ${i.error}`)}var Fe=class{constructor(e,t,o,s,r=!0,i=!0,a,c,l=!0){this.connection=e;this.lifecycle=t;this.serviceManager=o;this.workspaceDir=s;this.enableAutoTypecheck=r;this.enableAutoCommit=i;this.setupPlanningTransport=a;this.projectConfig=c;this.useDefaultStack=l;this.gitManager=De(s)}currentSession=null;claudeSessionIdMap=new Map;currentAbortController=null;gitManager;async initSession(e){console.log(`[Receiver] InitSession for session ${e.sessionId}`),this.setupPlanningTransport&&e.projectId&&this.setupPlanningTransport(e.projectId),e.projectId&&await this.fetchProjectPrompt(e.projectId),this.currentSession={sessionId:e.sessionId,projectId:e.projectId,branchName:e.branchName,model:e.model==="auto"?void 0:e.model||"claude-haiku-4-5-20251001",debugMode:e.debugMode},await this.lifecycle.handleInitSession(e),e.prompt&&await this.runPrompt(e.sessionId,e.prompt,e.claudeSessionId)}async runPrompt(e,t,o,s=!1){let r=pe(t);if(console.log(`[Receiver] RunPrompt for session ${e}${o?` (ClaudeSessionId: ${o})`:""}${s?" [DEBUG MODE]":""}: ${r.substring(0,50)}...`),(!this.currentSession||this.currentSession.sessionId!==e)&&(console.log(`[Receiver] Session mismatch: expected ${this.currentSession?.sessionId}, got ${e}, switching...`),await this.switchSession(e),!this.currentSession)){console.error("[Receiver] Failed to create session context");return}let i=this.claudeSessionIdMap.get(e);if(o&&i!==o){console.warn(`[Receiver] Claude session not found in container: ${o}`);let l=this.connection.getHubProxy();await l.reportSessionOutput(process.env.CONTAINER_ID||"unknown",e,{type:"System",event:"sessionNotFound",content:o,isError:!0}),await this.connection.reportStatus("Ready","Session lost - summary needed",e,this.currentSession.projectId),await l.sessionCompleted(process.env.CONTAINER_ID||"unknown",e,0);return}await this.connection.reportStatus("Active",void 0,e,this.currentSession.projectId),this.currentSession.debugMode=s,this.currentAbortController=new AbortController;let a=i||null,c=new Set;try{let l=await Ae(r,{model:this.currentSession.model,sessionId:a||null,projectId:this.currentSession.projectId||null,apiUrl:this.connection.getMasterUrl(),debugLog:!0,debugMode:s,abortController:this.currentAbortController,projectConfig:this.projectConfig,useDefaultStack:this.useDefaultStack,callbacks:Bt(this.connection,e,{onClaudeSessionId:g=>{this.claudeSessionIdMap.set(e,g)},onFileModified:g=>{c.add(g)}})}),u=Gt(this.workspaceDir,e);if(u&&u.length>0){console.log(`[Receiver] ${u.length} pending questions found, waiting for user answers`),await this.connection.reportStatus("Ready",void 0,e,this.currentSession.projectId),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,0);return}if(this.enableAutoTypecheck&&!l.error&&!l.aborted){let g=await Kt(this.serviceManager,c,this.workspaceDir,this.projectConfig);if(!g.passed&&g.errorPrompt){console.log("[Receiver] Type errors detected, auto-correcting..."),await this.runPrompt(e,g.errorPrompt,o);return}}this.enableAutoCommit&&!l.error&&!l.aborted?await dt(this.gitManager,this.connection,e,t,!1):(l.error||l.aborted)&&this.enableAutoCommit&&await dt(this.gitManager,this.connection,e,t,!0),await this.connection.reportStatus("Ready",void 0,e,this.currentSession.projectId),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,l.error?1:0)}catch(l){let u=l instanceof Error&&l.name==="AbortError",d=l instanceof Error?l.message:"Unknown error";u?console.log(`[Receiver] Agent aborted for session ${e}`):(console.error("[Receiver] Agent error:",d),await this.connection.reportStatus("Error",d,e,this.currentSession.projectId)),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,u?0:1)}finally{this.currentAbortController=null}}async setModel(e){console.log(`[Receiver] Model changed to: ${e}`),this.currentSession&&(this.currentSession.model=e==="auto"?void 0:e)}async stopAgent(){console.log("[Receiver] Stop command received"),this.currentAbortController&&this.currentAbortController.abort()}async ping(){await this.connection.getConnection().invoke("Pong",process.env.CONTAINER_ID||"unknown")}async switchSession(e){console.log(`[Receiver] SwitchSession to ${e}`),this.currentSession?this.currentSession.sessionId=e:(console.log(`[Receiver] Creating new session context for ${e}`),this.currentSession={sessionId:e,projectId:process.env.PROJECT_ID||"unknown",branchName:"main",model:void 0,debugMode:!1})}async projectPromptUpdated(e){console.log(`[Receiver] ProjectPromptUpdated received (${e.length} chars)`),We(e)}async fetchProjectPrompt(e){try{let t=this.connection.getMasterUrl(),o=this.connection.getProjectKey(),s=await fetch(`${t}/api/projects/${e}/prompt`,{method:"GET",headers:{"Content-Type":"application/json","X-Project-Key":o}});if(!s.ok){console.log(`[Receiver] Failed to fetch project prompt: ${s.status}`);return}let r=await s.json();r.prompt?(We(r.prompt),console.log(`[Receiver] Initial project prompt loaded (${r.prompt.length} chars)`)):console.log(`[Receiver] No project prompt set for project ${e}`)}catch(t){console.error("[Receiver] Error fetching project prompt:",t)}}};process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT=process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT||"60000";var Wt=Y.dirname(En(import.meta.url));function An(){let n=[Y.resolve(Wt,"../package.json"),Y.resolve(Wt,"../../../package.json")];for(let e of n)try{if(M.existsSync(e)){let t=JSON.parse(M.readFileSync(e,"utf-8"));if(t.name==="glenn-code")return t.version||"unknown"}}catch{}return"unknown"}function xn(n){let{useDefaultStack:e,projectConfig:t,configSource:o,tunnelPort:s,enableAutoTypecheck:r,enableAutoCommit:i}=n;if(console.log(`
1359
1359
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`),console.log("\u2551 AGENT CONFIGURATION \u2551"),console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),e?(console.log("\u2551 Mode: DEFAULT STACK (.NET/React) \u2551"),console.log("\u2551 Agents: scaffolding, backend, frontend, debugger, \u2551"),console.log("\u2551 planning, project-context \u2551")):(console.log("\u2551 Mode: CUSTOM STACK (generic agents only) \u2551"),console.log("\u2551 Agents: debugger, planning, project-context \u2551")),console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),console.log(`\u2551 Config source: ${o.padEnd(42)}\u2551`),console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),console.log("\u2551 SERVICES: \u2551"),t.services?.backend){let a=t.services.backend;console.log(`\u2551 Backend: port ${a.port}, extensions: ${a.extensions?.join(", ")||"N/A"}`.padEnd(62)+"\u2551")}else console.log("\u2551 Backend: not configured \u2551");if(t.services?.frontend){let a=t.services.frontend;console.log(`\u2551 Frontend: port ${a.port}, extensions: ${a.extensions?.join(", ")||"N/A"}`.padEnd(62)+"\u2551")}else console.log("\u2551 Frontend: not configured \u2551");t.techStack&&(console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),console.log("\u2551 TECH STACK: \u2551"),t.techStack.backend?.length&&console.log(`\u2551 Backend: ${t.techStack.backend.join(", ")}`.padEnd(62)+"\u2551"),t.techStack.frontend?.length&&console.log(`\u2551 Frontend: ${t.techStack.frontend.join(", ")}`.padEnd(62)+"\u2551"),t.techStack.patterns?.length&&console.log(`\u2551 Patterns: ${t.techStack.patterns.join(", ")}`.padEnd(62)+"\u2551")),console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),console.log("\u2551 SETTINGS: \u2551"),console.log(`\u2551 Tunnel port: ${s}`.padEnd(62)+"\u2551"),console.log(`\u2551 Auto-typecheck: ${r?"enabled":"disabled"}`.padEnd(62)+"\u2551"),console.log(`\u2551 Auto-commit: ${i?"enabled":"disabled"}`.padEnd(62)+"\u2551"),console.log(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1360
- `)}var Dn=".sdd/local-signalr-config.json";function gt(n){return Y.join(n,Dn)}function jn(n){try{let e=gt(n);if(!M.existsSync(e))return null;let t=M.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return null}}function dt(n,e){let t=Y.dirname(gt(n));M.existsSync(t)||M.mkdirSync(t,{recursive:!0}),M.writeFileSync(gt(n),JSON.stringify(e,null,2))}function Le(){return`local-${Qt.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${Date.now().toString(36)}`}function On(n){return!!(n.version||n.paths||n.services||n.techStack)}function Un(n,e){return On(n)?{version:n.version||"1.0",paths:n.paths||{workspace:e},services:n.services,techStack:n.techStack,tunnel:n.tunnel,scaffold:n.scaffold,git:n.git}:null}async function Nn(n){console.log(`
1360
+ `)}var Dn=".sdd/local-signalr-config.json";function gt(n){return Y.join(n,Dn)}function jn(n){try{let e=gt(n);if(!M.existsSync(e))return null;let t=M.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return null}}function ut(n,e){let t=Y.dirname(gt(n));M.existsSync(t)||M.mkdirSync(t,{recursive:!0}),M.writeFileSync(gt(n),JSON.stringify(e,null,2))}function Le(){return`local-${Qt.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${Date.now().toString(36)}`}function On(n){return!!(n.version||n.paths||n.services||n.techStack)}function Un(n,e){return On(n)?{version:n.version||"1.0",paths:n.paths||{workspace:e},services:n.services,techStack:n.techStack,tunnel:n.tunnel,scaffold:n.scaffold,git:n.git}:null}async function Nn(n){console.log(`
1361
1361
  \u{1F527} Interactive Project Config Builder
1362
1362
  `),console.log(" Answer the following questions to configure your project."),console.log(` Press Enter to skip optional questions.
1363
1363
  `);let{projectType:e}=await q.prompt([{type:"select",name:"projectType",message:"What type of project is this?",choices:[{name:"Frontend only (React, Next.js, Vue, etc.)",value:"frontend"},{name:"Backend only (Node, Python, Go, etc.)",value:"backend"},{name:"Fullstack (Frontend + Backend)",value:"fullstack"},{name:"Skip - use default .NET/React stack",value:"skip"}]}]);if(e==="skip")return null;let t={version:"1.0",paths:{workspace:"."}};if(e==="frontend"||e==="fullstack"){console.log(`
@@ -1366,11 +1366,11 @@ File paths like "src/..." are relative to the package directory shown in parenth
1366
1366
  \u{1F4DA} Tech Stack (optional - helps AI understand your project):`);let{addTechStack:o}=await q.prompt([{type:"confirm",name:"addTechStack",message:"Add tech stack info?",default:!1}]);if(o){let s=await q.prompt([{type:"input",name:"frontend",message:"Frontend technologies (comma-separated, or Enter to skip):",default:""},{type:"input",name:"backend",message:"Backend technologies (comma-separated, or Enter to skip):",default:""},{type:"input",name:"patterns",message:"Patterns/practices (comma-separated, or Enter to skip):",default:""}]);t.techStack={},s.frontend&&(t.techStack.frontend=s.frontend.split(",").map(r=>r.trim())),s.backend&&(t.techStack.backend=s.backend.split(",").map(r=>r.trim())),s.patterns&&(t.techStack.patterns=s.patterns.split(",").map(r=>r.trim()))}return t}function Yt(n){try{let e={},t=n.match(/MASTER_URL=(\S+)/);t&&(e.masterUrl=t[1].trim());let o=n.match(/PROJECT_ID=(\S+)/);o&&(e.projectId=o[1].trim());let s=n.match(/PROJECT_KEY=(\S+)/);return s&&(e.projectKey=s[1].trim()),e.masterUrl&&e.projectId&&e.projectKey?e:null}catch{return null}}async function Mn(n){let e=jn(n);console.log(`
1367
1367
  \u{1F3E0} Local SignalR Mode - Jack into the real system
1368
1368
  `);let t=e?.masterUrl&&e?.projectId&&e?.projectKey,{configMethod:o}=await q.prompt([{type:"select",name:"configMethod",message:"How would you like to configure?",choices:[...t?[{name:`Use saved config (${e.projectId})`,value:"saved"}]:[],{name:"Paste config from frontend",value:"paste"},{name:"Enter manually",value:"manual"},{name:"Build project config interactively",value:"interactive"}]}]);if(o==="saved"&&t){let a=Un(e,n),c=a?.tunnel?.port||e.tunnelPort||5173;return console.log(`
1369
- \u{1F4CB} Using saved configuration:`),console.log(` Master URL: ${e.masterUrl}`),console.log(` Project ID: ${e.projectId}`),console.log(` Project Key: ${e.projectKey.substring(0,16)}...`),console.log(` Container ID: ${e.containerId}`),console.log(` Tunnel: ${e.enableTunnel!==!1?`enabled (port ${c})`:"disabled"}`),a&&console.log(" Project config: embedded in local-signalr-config.json"),{masterUrl:e.masterUrl,projectId:e.projectId,projectKey:e.projectKey,containerId:e.containerId||Le(),enableTunnel:e.enableTunnel!==!1,tunnelPort:c,embeddedProjectConfig:a}}if(o==="paste"){let{pastedConfig:a}=await q.prompt([{type:"input",name:"pastedConfig",message:"Paste the config (MASTER_URL=... PROJECT_ID=... PROJECT_KEY=...):",validate:g=>Yt(g)?!0:"Could not parse config. Make sure it contains MASTER_URL=..., PROJECT_ID=..., and PROJECT_KEY=..."}]),c=Yt(a),l=e?.containerId||Le(),d=e?.tunnelPort||5173,u={masterUrl:c.masterUrl,projectId:c.projectId,projectKey:c.projectKey,containerId:l,enableTunnel:!0,tunnelPort:d};return console.log(`
1370
- \u{1F4CB} Parsed configuration:`),console.log(` Master URL: ${u.masterUrl}`),console.log(` Project ID: ${u.projectId}`),console.log(` Project Key: ${u.projectKey.substring(0,16)}...`),console.log(` Container ID: ${u.containerId}`),console.log(` Tunnel: enabled (port ${d})`),dt(n,u),{...u,embeddedProjectConfig:null}}if(o==="interactive"){console.log(`
1371
- \u{1F50C} Connection Configuration:`);let a=await q.prompt([{type:"input",name:"masterUrl",message:"Master URL (backend):",default:e?.masterUrl||process.env.MASTER_URL||"http://localhost:5338",validate:g=>{if(!g.trim())return"Master URL is required";try{return new URL(g),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:e?.projectId||process.env.PROJECT_ID,validate:g=>g.trim()?!0:"Project ID is required"},{type:"password",name:"projectKey",message:"Project Key (pk_proj_...):",mask:"*",default:e?.projectKey||process.env.PROJECT_KEY,validate:g=>g.trim()?g.startsWith("pk_proj_")?!0:"Project Key must start with pk_proj_":"Project Key is required"}]),c=await Nn(n),l=e?.containerId||Le(),d=c?.tunnel?.port||e?.tunnelPort||5173,u={masterUrl:a.masterUrl,projectId:a.projectId,projectKey:a.projectKey,containerId:l,enableTunnel:!0,tunnelPort:d,...c&&{version:c.version,paths:c.paths,services:c.services,techStack:c.techStack,tunnel:c.tunnel}};return console.log(`
1372
- \u{1F4CB} Configuration built:`),console.log(` Master URL: ${u.masterUrl}`),console.log(` Project ID: ${u.projectId}`),console.log(` Project Key: ${u.projectKey.substring(0,16)}...`),console.log(` Container ID: ${u.containerId}`),console.log(` Tunnel: enabled (port ${d})`),console.log(c?" Project config: custom stack":" Project config: default .NET/React stack"),dt(n,u),{masterUrl:u.masterUrl,projectId:u.projectId,projectKey:u.projectKey,containerId:u.containerId,enableTunnel:u.enableTunnel,tunnelPort:u.tunnelPort,embeddedProjectConfig:c}}let s=await q.prompt([{type:"input",name:"masterUrl",message:"Master URL (backend):",default:e?.masterUrl||process.env.MASTER_URL||"http://localhost:5338",validate:a=>{if(!a.trim())return"Master URL is required";try{return new URL(a),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:e?.projectId||process.env.PROJECT_ID,validate:a=>a.trim()?!0:"Project ID is required"},{type:"password",name:"projectKey",message:"Project Key (pk_proj_...):",mask:"*",default:e?.projectKey||process.env.PROJECT_KEY,validate:a=>a.trim()?a.startsWith("pk_proj_")?!0:"Project Key must start with pk_proj_":"Project Key is required"},{type:"input",name:"containerId",message:"Container ID (unique identifier for this agent):",default:e?.containerId||Le()},{type:"confirm",name:"enableTunnel",message:"Enable preview URL reporting (report local dev server URL to backend)?",default:e?.enableTunnel!==!1}]),r=e?.tunnelPort||5173;s.enableTunnel&&(r=(await q.prompt([{type:"number",name:"tunnelPort",message:"Preview port (local dev server port to report):",default:r}])).tunnelPort||r);let i={...s,tunnelPort:r};return dt(n,i),{...i,embeddedProjectConfig:null}}async function Fn(){let n=process.env.LOCAL_MODE==="true",e=process.env.WORKSPACE_DIR||process.cwd(),t=An();console.log(n?`=== Glenn Code v${t} ===`:`=== SignalR Agent v${t} ===`),console.log(`Workspace: ${e}`),n&&!M.existsSync(Y.join(e,".git"))&&console.warn("\u26A0\uFE0F Warning: Not a git repository. Some features may not work.");let o,s,r=!1,i=5173,a=null;if(n){let p=await Mn(e);o={masterUrl:p.masterUrl,containerId:p.containerId,projectKey:p.projectKey,workspaceDir:e},s=p.projectId,r=p.enableTunnel,i=p.tunnelPort,a=p.embeddedProjectConfig,process.env.PROJECT_ID=p.projectId,process.env.CONTAINER_ID=p.containerId,process.env.PROJECT_KEY=p.projectKey}else{let p=process.env.PROJECT_KEY;p||(console.error("\u274C PROJECT_KEY environment variable is required"),process.exit(1)),o={masterUrl:process.env.MASTER_URL||"http://host.docker.internal:5338",containerId:process.env.CONTAINER_ID||"unknown",projectKey:p,workspaceDir:e}}console.log(`
1373
- \u{1F50C} Connecting to backend...`),console.log(` Container ID: ${o.containerId}`),console.log(` Project Key: ${o.projectKey.substring(0,16)}...`),console.log(` Gateway URL: ${o.masterUrl}`);let c=new je(o),l=0,d=10;for(;l<d;)try{await c.connect();break}catch(p){if(l++,console.error(`Connection attempt ${l} failed:`,p instanceof Error?p.message:p),l<d){let C=Math.min(1e3*Math.pow(2,l),3e4);await new Promise(de=>setTimeout(de,C))}}l>=d&&(console.error("\u274C Failed to connect to gateway after max retries"),process.exit(1)),n&&console.log("\u2705 Connected to backend");let u=await c.getSecrets();n?(console.log("\u2705 Handshake complete"),u.gitHubPat&&console.log(" GitHub PAT received from backend")):console.log("\u2705 Secrets received");let g,w=!0,v="default";if(n){if(a)g=a,v="embedded in local-signalr-config.json",w=!1,console.log("[Config] Using embedded project config from local-signalr-config.json");else{let p=await _e({workspaceDir:o.workspaceDir});g=p.config,v=p.source==="file"?p.configPath||".sdd/project-config.json":"default (no config file)",p.source==="file"&&(w=!1)}w||g.tunnel?.port&&(i=g.tunnel.port)}let b=xe({workspaceDir:o.workspaceDir,projectConfig:g});he(b);let D=ce(o.workspaceDir);ie(D);let j=p=>{console.log(`[Planning] Setting up SignalR transport for project ${p}`),D=new Oe(c.getHubProxy(),p),ie(D)};n&&s&&j(s);let f;n?f={prewarmWorkspace:async()=>{console.log("[LocalMode] Skipping prewarm - using existing workspace")},handleInitSession:async p=>{console.log(`[LocalMode] Session init for ${p.sessionId}`),console.log("[LocalMode] Skipping branch switch - using current branch"),await c.reportStatus("Ready",void 0,p.sessionId,p.projectId),await c.getHubProxy().initSessionCompleted(o.containerId,p.sessionId,!0,"")}}:f=new Ne(c,b,o.workspaceDir,u.gitHubPat||null);let O=process.env.DISABLE_AUTO_TYPECHECK!=="true",G=process.env.DISABLE_AUTO_COMMIT!=="true"&&!n;n&&g?xn({useDefaultStack:w,projectConfig:g,configSource:v,tunnelPort:i,enableAutoTypecheck:O,enableAutoCommit:G}):(console.log(`[Config] Auto-typecheck: ${O?"enabled":"disabled"}`),console.log(`[Config] Auto-commit: ${G?"enabled":"disabled"}`));let F=new Fe(c,f,b,o.workspaceDir,O,G,j,g,w);if(c.registerReceiver(F),n){if(r){let p=`http://localhost:${i}`;c.setTunnelUrl(p),console.log(`
1369
+ \u{1F4CB} Using saved configuration:`),console.log(` Master URL: ${e.masterUrl}`),console.log(` Project ID: ${e.projectId}`),console.log(` Project Key: ${e.projectKey.substring(0,16)}...`),console.log(` Container ID: ${e.containerId}`),console.log(` Tunnel: ${e.enableTunnel!==!1?`enabled (port ${c})`:"disabled"}`),a&&console.log(" Project config: embedded in local-signalr-config.json"),{masterUrl:e.masterUrl,projectId:e.projectId,projectKey:e.projectKey,containerId:e.containerId||Le(),enableTunnel:e.enableTunnel!==!1,tunnelPort:c,embeddedProjectConfig:a}}if(o==="paste"){let{pastedConfig:a}=await q.prompt([{type:"input",name:"pastedConfig",message:"Paste the config (MASTER_URL=... PROJECT_ID=... PROJECT_KEY=...):",validate:g=>Yt(g)?!0:"Could not parse config. Make sure it contains MASTER_URL=..., PROJECT_ID=..., and PROJECT_KEY=..."}]),c=Yt(a),l=e?.containerId||Le(),u=e?.tunnelPort||5173,d={masterUrl:c.masterUrl,projectId:c.projectId,projectKey:c.projectKey,containerId:l,enableTunnel:!0,tunnelPort:u};return console.log(`
1370
+ \u{1F4CB} Parsed configuration:`),console.log(` Master URL: ${d.masterUrl}`),console.log(` Project ID: ${d.projectId}`),console.log(` Project Key: ${d.projectKey.substring(0,16)}...`),console.log(` Container ID: ${d.containerId}`),console.log(` Tunnel: enabled (port ${u})`),ut(n,d),{...d,embeddedProjectConfig:null}}if(o==="interactive"){console.log(`
1371
+ \u{1F50C} Connection Configuration:`);let a=await q.prompt([{type:"input",name:"masterUrl",message:"Master URL (backend):",default:e?.masterUrl||process.env.MASTER_URL||"http://localhost:5338",validate:g=>{if(!g.trim())return"Master URL is required";try{return new URL(g),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:e?.projectId||process.env.PROJECT_ID,validate:g=>g.trim()?!0:"Project ID is required"},{type:"password",name:"projectKey",message:"Project Key (pk_proj_...):",mask:"*",default:e?.projectKey||process.env.PROJECT_KEY,validate:g=>g.trim()?g.startsWith("pk_proj_")?!0:"Project Key must start with pk_proj_":"Project Key is required"}]),c=await Nn(n),l=e?.containerId||Le(),u=c?.tunnel?.port||e?.tunnelPort||5173,d={masterUrl:a.masterUrl,projectId:a.projectId,projectKey:a.projectKey,containerId:l,enableTunnel:!0,tunnelPort:u,...c&&{version:c.version,paths:c.paths,services:c.services,techStack:c.techStack,tunnel:c.tunnel}};return console.log(`
1372
+ \u{1F4CB} Configuration built:`),console.log(` Master URL: ${d.masterUrl}`),console.log(` Project ID: ${d.projectId}`),console.log(` Project Key: ${d.projectKey.substring(0,16)}...`),console.log(` Container ID: ${d.containerId}`),console.log(` Tunnel: enabled (port ${u})`),console.log(c?" Project config: custom stack":" Project config: default .NET/React stack"),ut(n,d),{masterUrl:d.masterUrl,projectId:d.projectId,projectKey:d.projectKey,containerId:d.containerId,enableTunnel:d.enableTunnel,tunnelPort:d.tunnelPort,embeddedProjectConfig:c}}let s=await q.prompt([{type:"input",name:"masterUrl",message:"Master URL (backend):",default:e?.masterUrl||process.env.MASTER_URL||"http://localhost:5338",validate:a=>{if(!a.trim())return"Master URL is required";try{return new URL(a),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:e?.projectId||process.env.PROJECT_ID,validate:a=>a.trim()?!0:"Project ID is required"},{type:"password",name:"projectKey",message:"Project Key (pk_proj_...):",mask:"*",default:e?.projectKey||process.env.PROJECT_KEY,validate:a=>a.trim()?a.startsWith("pk_proj_")?!0:"Project Key must start with pk_proj_":"Project Key is required"},{type:"input",name:"containerId",message:"Container ID (unique identifier for this agent):",default:e?.containerId||Le()},{type:"confirm",name:"enableTunnel",message:"Enable preview URL reporting (report local dev server URL to backend)?",default:e?.enableTunnel!==!1}]),r=e?.tunnelPort||5173;s.enableTunnel&&(r=(await q.prompt([{type:"number",name:"tunnelPort",message:"Preview port (local dev server port to report):",default:r}])).tunnelPort||r);let i={...s,tunnelPort:r};return ut(n,i),{...i,embeddedProjectConfig:null}}async function Fn(){let n=process.env.LOCAL_MODE==="true",e=process.env.WORKSPACE_DIR||process.cwd(),t=An();console.log(n?`=== Glenn Code v${t} ===`:`=== SignalR Agent v${t} ===`),console.log(`Workspace: ${e}`),n&&!M.existsSync(Y.join(e,".git"))&&console.warn("\u26A0\uFE0F Warning: Not a git repository. Some features may not work.");let o,s,r=!1,i=5173,a=null;if(n){let p=await Mn(e);o={masterUrl:p.masterUrl,containerId:p.containerId,projectKey:p.projectKey,workspaceDir:e,isByok:!0},s=p.projectId,r=p.enableTunnel,i=p.tunnelPort,a=p.embeddedProjectConfig,process.env.PROJECT_ID=p.projectId,process.env.CONTAINER_ID=p.containerId,process.env.PROJECT_KEY=p.projectKey}else{let p=process.env.PROJECT_KEY;p||(console.error("\u274C PROJECT_KEY environment variable is required"),process.exit(1)),o={masterUrl:process.env.MASTER_URL||"http://host.docker.internal:5338",containerId:process.env.CONTAINER_ID||"unknown",projectKey:p,workspaceDir:e}}console.log(`
1373
+ \u{1F50C} Connecting to backend...`),console.log(` Container ID: ${o.containerId}`),console.log(` Project Key: ${o.projectKey.substring(0,16)}...`),console.log(` Gateway URL: ${o.masterUrl}`);let c=new je(o),l=0,u=10;for(;l<u;)try{await c.connect();break}catch(p){if(l++,console.error(`Connection attempt ${l} failed:`,p instanceof Error?p.message:p),l<u){let C=Math.min(1e3*Math.pow(2,l),3e4);await new Promise(ue=>setTimeout(ue,C))}}l>=u&&(console.error("\u274C Failed to connect to gateway after max retries"),process.exit(1)),n&&console.log("\u2705 Connected to backend");let d=await c.getSecrets();n?(console.log("\u2705 Handshake complete"),d.gitHubPat&&console.log(" GitHub PAT received from backend")):console.log("\u2705 Secrets received");let g,w=!0,v="default";if(n){if(a)g=a,v="embedded in local-signalr-config.json",w=!1,console.log("[Config] Using embedded project config from local-signalr-config.json");else{let p=await _e({workspaceDir:o.workspaceDir});g=p.config,v=p.source==="file"?p.configPath||".sdd/project-config.json":"default (no config file)",p.source==="file"&&(w=!1)}w||g.tunnel?.port&&(i=g.tunnel.port)}let A=xe({workspaceDir:o.workspaceDir,projectConfig:g});he(A);let D=ce(o.workspaceDir);ie(D);let b=p=>{console.log(`[Planning] Setting up SignalR transport for project ${p}`),D=new Oe(c.getHubProxy(),p),ie(D)};n&&s&&b(s);let f;n?f={prewarmWorkspace:async()=>{console.log("[LocalMode] Skipping prewarm - using existing workspace")},handleInitSession:async p=>{console.log(`[LocalMode] Session init for ${p.sessionId}`),console.log("[LocalMode] Skipping branch switch - using current branch"),await c.reportStatus("Ready",void 0,p.sessionId,p.projectId),await c.getHubProxy().initSessionCompleted(o.containerId,p.sessionId,!0,"")}}:f=new Ne(c,A,o.workspaceDir,d.gitHubPat||null);let O=process.env.DISABLE_AUTO_TYPECHECK!=="true",G=process.env.DISABLE_AUTO_COMMIT!=="true"&&!n;n&&g?xn({useDefaultStack:w,projectConfig:g,configSource:v,tunnelPort:i,enableAutoTypecheck:O,enableAutoCommit:G}):(console.log(`[Config] Auto-typecheck: ${O?"enabled":"disabled"}`),console.log(`[Config] Auto-commit: ${G?"enabled":"disabled"}`));let F=new Fe(c,f,A,o.workspaceDir,O,G,b,g,w);if(c.registerReceiver(F),n){if(r){let p=`http://localhost:${i}`;c.setTunnelUrl(p),console.log(`
1374
1374
  \u{1F310} Preview available at: ${p}`)}await c.reportStatus("Ready",void 0,void 0,s),console.log(`
1375
1375
  \u2705 Agent ready and waiting for commands`),console.log(` Open the frontend and select this agent to start working
1376
1376
  `),console.log(` Press Ctrl+C to disconnect
package/dist/cli.js CHANGED
@@ -30,7 +30,7 @@ ${s.join(`
30
30
  Parameters:
31
31
  - target: "backend" (default) | "frontend" | "both"
32
32
 
33
- Use target="backend" before running scaffold (required for migrations).`,{target:ne.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async n=>{if(!R)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=n.target||"backend";console.log(`[MCP] stop_services called (target: ${e})`);let t=e==="backend"||e==="both",o=e==="frontend"||e==="both";t&&await R.stopBackend(),o&&await R.stopFrontend();let s=5e3,r=Date.now(),i=!t,a=!o;for(;Date.now()-r<s;){let d=await R.checkHealth();if(t&&!d.api&&(i=!0),o&&!d.web&&(a=!0),i&&a)break;await new Promise(u=>setTimeout(u,500))}let c=e==="both"?"Backend and frontend":e==="backend"?"Backend":"Frontend";return{content:[{type:"text",text:i&&a?`\u2705 ${c} stopped successfully.`:`\u26A0\uFE0F ${c} stop requested but health check still shows running. Proceeding anyway.`}]}}),Ct=z("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:ne.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async n=>{if(!R)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=n.target||"backend";console.log(`[MCP] stop_services called (target: ${e})`);let t=e==="backend"||e==="both",o=e==="frontend"||e==="both";t&&await R.stopBackend(),o&&await R.stopFrontend();let s=5e3,r=Date.now(),i=!t,a=!o;for(;Date.now()-r<s;){let u=await R.checkHealth();if(t&&!u.api&&(i=!0),o&&!u.web&&(a=!0),i&&a)break;await new Promise(d=>setTimeout(d,500))}let c=e==="both"?"Backend and frontend":e==="backend"?"Backend":"Frontend";return{content:[{type:"text",text:i&&a?`\u2705 ${c} stopped successfully.`:`\u26A0\uFE0F ${c} stop requested but health check still shows running. Proceeding anyway.`}]}}),Ct=z("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"
@@ -55,7 +55,7 @@ Parameters:
55
55
  - pattern: string (e.g., "users", "api/reports")`,{pattern:ne.string().describe("Text to search for in endpoint paths")},async n=>{if(!R)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=await R.checkSwaggerEndpoints(n.pattern);return e.foundEndpoints.length===0?{content:[{type:"text",text:`\u274C No endpoints found matching "${n.pattern}" (Total endpoints: ${e.totalEndpoints})`}]}:{content:[{type:"text",text:`\u2705 Found ${e.foundEndpoints.length} matching endpoints:
56
56
 
57
57
  ${e.foundEndpoints.join(`
58
- `)}`}]}});import*as ae from"fs";import*as Xt from"path";import*as A from"fs";import*as ye from"path";function ce(n){let e=ye.join(n,".sdd","specifications");function t(){A.existsSync(e)||A.mkdirSync(e,{recursive:!0})}function o(s){return ye.join(e,`${s}.spec.md`)}return{async saveSpecification(s,r){t(),A.writeFileSync(o(s),r,"utf-8")},async getSpecification(s){t();let r=o(s);return A.existsSync(r)?A.readFileSync(r,"utf-8"):null},async listSpecifications(){return t(),A.readdirSync(e).filter(r=>r.endsWith(".spec.md")).map(r=>{let i=A.readFileSync(ye.join(e,r),"utf-8"),a=i.match(/name: "([^"]+)"/),c=i.match(/status: (\w+)/),l=i.match(/version: "([^"]+)"/);return{slug:r.replace(".spec.md",""),name:a?.[1]||r,status:c?.[1]||"unknown",version:l?.[1]||"1.0.0"}})},async deleteSpecification(s){t();let r=o(s);return A.existsSync(r)?(A.unlinkSync(r),!0):!1},async specificationExists(s){return t(),A.existsSync(o(s))}}}function Be(){return Zt({name:"agent-insights",version:"1.0.0",tools:[kt,vt,Ct,_t,Pt,Rt]})}import{query as bn}from"@anthropic-ai/claude-agent-sdk";var qe={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 ae from"fs";import*as Xt from"path";import*as x from"fs";import*as ye from"path";function ce(n){let e=ye.join(n,".sdd","specifications");function t(){x.existsSync(e)||x.mkdirSync(e,{recursive:!0})}function o(s){return ye.join(e,`${s}.spec.md`)}return{async saveSpecification(s,r){t(),x.writeFileSync(o(s),r,"utf-8")},async getSpecification(s){t();let r=o(s);return x.existsSync(r)?x.readFileSync(r,"utf-8"):null},async listSpecifications(){return t(),x.readdirSync(e).filter(r=>r.endsWith(".spec.md")).map(r=>{let i=x.readFileSync(ye.join(e,r),"utf-8"),a=i.match(/name: "([^"]+)"/),c=i.match(/status: (\w+)/),l=i.match(/version: "([^"]+)"/);return{slug:r.replace(".spec.md",""),name:a?.[1]||r,status:c?.[1]||"unknown",version:l?.[1]||"1.0.0"}})},async deleteSpecification(s){t();let r=o(s);return x.existsSync(r)?(x.unlinkSync(r),!0):!1},async specificationExists(s){return t(),x.existsSync(o(s))}}}function Be(){return Zt({name:"agent-insights",version:"1.0.0",tools:[kt,vt,Ct,_t,Pt,Rt]})}import{query as bn}from"@anthropic-ai/claude-agent-sdk";var qe={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:
59
59
  1. Read the spec if referenced in the card description
60
60
  2. Check existing features (quick ls)
61
61
  3. Write ALL entities to ONE /tmp/scaffold.json (big bang, no phasing!)
@@ -881,7 +881,7 @@ Then STOP. Do not continue. Do not try to fix it. Wait for user.
881
881
 
882
882
  ---
883
883
 
884
- `:"",c=un(s),l=dn(s,o),d=mn(s,r),u=gn(r),g=r?hn(s):"",w=r?yn(s,o):"",v=fn(r);return`${i}${a}# Agent Instructions
884
+ `:"",c=dn(s),l=un(s,o),u=mn(s,r),d=gn(r),g=r?hn(s):"",w=r?yn(s,o):"",v=fn(r);return`${i}${a}# Agent Instructions
885
885
 
886
886
  ## \u26D4 CRITICAL: EVERYTHING IS A KANBAN TASK
887
887
 
@@ -1005,7 +1005,7 @@ When user asks to build ANY feature, your FIRST action is to delegate to @planni
1005
1005
  - Use EnterPlanMode or any built-in planning
1006
1006
  - Skip the @planning step
1007
1007
 
1008
- ${u}
1008
+ ${d}
1009
1009
 
1010
1010
  ---
1011
1011
 
@@ -1018,17 +1018,17 @@ ${c}
1018
1018
 
1019
1019
  ${l}
1020
1020
 
1021
- ${d}
1021
+ ${u}
1022
1022
 
1023
1023
  ${v}
1024
1024
 
1025
1025
  ${g}
1026
1026
 
1027
1027
  ${w}
1028
- `}function un(n){let e=Te(n),t=H(n),o=B(n),s=Re(n),r=le(n),i=[];s&&i.push(`- Backend: "${s}/{Entity}/"`),r&&i.push(`- Frontend: "${r}/{entity}/"`);let a=n.techStack?.patterns?.length?`
1028
+ `}function dn(n){let e=Te(n),t=H(n),o=B(n),s=Re(n),r=le(n),i=[];s&&i.push(`- Backend: "${s}/{Entity}/"`),r&&i.push(`- Frontend: "${r}/{entity}/"`);let a=n.techStack?.patterns?.length?`
1029
1029
  **Key patterns:**
1030
- ${n.techStack.patterns.map(d=>`- ${d}`).join(`
1031
- `)}`:"",l=n.techStack?.backend?.some(d=>d.includes(".NET")||d.includes("C#"))??!1?`
1030
+ ${n.techStack.patterns.map(u=>`- ${u}`).join(`
1031
+ `)}`:"",l=n.techStack?.backend?.some(u=>u.includes(".NET")||u.includes("C#"))??!1?`
1032
1032
 
1033
1033
  **\u26A0\uFE0F User entity exists!** ASP.NET Identity "ApplicationUser" at "Features/Users/" - don't scaffold User, just relate to it.`:"";return`<tech-stack>
1034
1034
  ${e}
@@ -1037,7 +1037,7 @@ ${i.length>0?`
1037
1037
  ${i.join(`
1038
1038
  `)}`:""}
1039
1039
  ${a}${l}
1040
- </tech-stack>`}function dn(n,e){return e?`<environment>
1040
+ </tech-stack>`}function un(n,e){return e?`<environment>
1041
1041
  **LOCAL MODE - User manages their own services.**
1042
1042
 
1043
1043
  You are running on the user's local machine, NOT in a Docker container.
@@ -1334,30 +1334,30 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
1334
1334
  - **Multi-tab apps**: Use \`browser_tabs\` to manage multiple windows/tabs in legacy applications
1335
1335
  - **Large snapshots**: For pages with huge dropdowns (e.g., country lists), use \`browser_evaluate\` to collapse them first
1336
1336
  - ${e?"In local mode, the browser window will be visible to you":"In container mode, browser runs headless"}
1337
- </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,G=!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:qe,debugger:be,planning:Se,backend:Ge,frontend:Ke,"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)}G=!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(G)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 T from"fs";import*as Nt from"http";import*as J 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,G=y=>new Promise(m=>{let S=setTimeout(()=>m(!1),3e3);Nt.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(`
1338
- `).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||!T.existsSync(l)){console.log("[Services] No backend found, skipping backend start");return}F(a),console.log(`[Services] Starting backend with: ${u}...`);let y=J.dirname(D);T.existsSync(y)||T.mkdirSync(y,{recursive:!0});let m=T.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(`
1339
- `);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||!T.existsSync(J.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=J.dirname(j);T.existsSync(y)||T.mkdirSync(y,{recursive:!0});let[m,...S]=v.split(" ");O=Ut(m,S,{cwd:d,stdio:["ignore",T.openSync(j,"w"),T.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([G(a),G(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(Jt=>setTimeout(Jt,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&&T.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&&T.existsSync(J.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?`
1337
+ </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")],de=Ot.find(n=>Qe.existsSync(n))||Ot[0];console.log("[MCP] Stdio server path resolved:",de);console.log("[MCP] __dirname:",Ye);console.log("[MCP] File exists:",Qe.existsSync(de));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:u=process.env.LOCAL_MODE==="true",projectConfig:d=W(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:g=!0}=e,w=At(a),v=new Set,A=o||"",D,b,f,O=!1,G=!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:",[de]),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:",de);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",...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:t,mcpServers:{"agent-insights":wn,"agent-planning":{type:"stdio",command:"node",args:[de],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")}`,...u?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:g?{scaffolding:qe,debugger:be,planning:Se,backend:Ge,frontend:Ke,"project-context":we}:{debugger:be,planning:Se,"project-context":we},systemPrompt:Dt({debugMode:l,projectPrompt:jt(),isLocal:u,projectConfig:d,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,b=p.total_cost_usd,i.onResult?.(p.result,p.total_cost_usd);else{let C=`Agent error: ${p.subtype}`;f=C,i.onError?.(C)}G=!0;break;case"system":switch(p.subtype){case"init":A=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(G)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:A,result:D,cost:b,error:f,aborted:O}}import{spawn as Ut,execSync as ee}from"child_process";import*as T from"fs";import*as Nt from"http";import*as J 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),u=B(i),d=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",A=i.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",D=i.services?.backend?.logFile??"/tmp/api.log",b=i.services?.frontend?.logFile??"/tmp/web.log",f=null,O=null,G=y=>new Promise(m=>{let S=setTimeout(()=>m(!1),3e3);Nt.get(`http://localhost:${y}`,j=>{clearTimeout(S),m(j.statusCode!==void 0&&j.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(`
1338
+ `).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||!T.existsSync(l)){console.log("[Services] No backend found, skipping backend start");return}F(a),console.log(`[Services] Starting backend with: ${d}...`);let y=J.dirname(D);T.existsSync(y)||T.mkdirSync(y,{recursive:!0});let m=T.createWriteStream(D,{flags:"a"}),[S,..._]=d.split(" ");f=Ut(S,_,{cwd:l,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let j="",E=P=>{let Q=P.toString();m.write(Q);let L=(j+Q).split(`
1339
+ `);j=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()},ue=async()=>{if(!u||!T.existsSync(J.join(u,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}F(c),console.log(`[Services] Starting frontend with: ${v}...`);let y=J.dirname(b);T.existsSync(y)||T.mkdirSync(y,{recursive:!0});let[m,...S]=v.split(" ");O=Ut(m,S,{cwd:u,stdio:["ignore",T.openSync(b,"w"),T.openSync(b,"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([G(a),G(c)]);return{api:y,web:m}},ht=async(y=15e3)=>{let m=Date.now(),S=1e3,_=0,j=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&&j===null&&(j=$,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(Jt=>setTimeout(Jt,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&&j===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&&T.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(_&&u&&T.existsSync(J.join(u,"package.json"))){console.log(`[Services] Type-checking frontend with: ${A}...`);try{ee(A,{cwd:u,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 ue();let j=await ht(1e4);if(S&&!j.api){m.success=!1;let E=await St("backend",20),P=E.length>0?`
1340
1340
  Recent logs:
1341
1341
  ${E.join(`
1342
- `)}`:"";m.backendError||(m.backendError=`Backend failed to start.${P}`)}return _&&!x.web&&(m.success=!1,m.frontendError||(m.frontendError="Frontend failed to start")),m},yt=async()=>{if(!l||!T.existsSync(l))return{success:!0};try{return ee(w,{cwd:l,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),{success:!0}}catch(y){let m=y;return{success:!1,errors:m.stdout||m.stderr||"Build failed"}}},bt=async()=>{if(!d||!T.existsSync(J.join(d,"package.json")))return{success:!0};try{return ee(b,{cwd:d,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),{success:!0}}catch(y){let m=y;return{success:!1,errors:m.stdout||m.stderr||"Type check failed"}}},Vt=async()=>{let[y,m]=await Promise.all([yt(),bt()]);return{backend:y,frontend:m}},St=async(y,m)=>{let S=y==="backend"?D:j;if(!T.existsSync(S))return[`Log file not found: ${S}`];try{return ee(`tail -n ${m} ${S}`,{encoding:"utf-8"}).split(`
1343
- `).filter(Boolean)}catch(_){return[`Error reading logs: ${_ instanceof Error?_.message:String(_)}`]}};return{startBackend:C,startFrontend:de,stopBackend:ft,stopFrontend:mt,restartServices:zt,checkHealth:$e,waitForHealth:ht,getProcesses:()=>({backend:f,frontend:O}),checkBackendBuild:yt,checkFrontendTypes:bt,runTypeChecks:Vt,tailLogs:St,checkSwaggerEndpoints:async y=>{let m=J.join(e,"swagger.json");if(!T.existsSync(m))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let S=JSON.parse(T.readFileSync(m,"utf-8")),_=Object.keys(S.paths||{}),x=[];for(let E of _)if(E.toLowerCase().includes(y.toLowerCase())){let P=Object.keys(S.paths[E]);for(let Q of P)x.push(`${Q.toUpperCase()} ${E}`)}return{foundEndpoints:x,totalEndpoints:_.length}}catch(S){return{foundEndpoints:[`Error parsing swagger.json: ${S instanceof Error?S.message:String(S)}`],totalEndpoints:0}}}}}function vn(n){let e=n.toLowerCase();return e.includes("exception")||e.includes("fail:")||e.includes("[err]")||e.includes("[error]")}import{execSync as te}from"child_process";function De(n){let e=async()=>{try{return te("git status --porcelain",{cwd:n,encoding:"utf-8"}).trim().length>0}catch{return!1}},t=async()=>{try{return te("git branch --show-current",{cwd:n,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:e,commitAndPush:async(s,r)=>{try{if(!await e())return{success:!0,noChanges:!0};let i=s.length>60?s.substring(0,60)+"...":s,c=`${r?"[agent] (partial)":"[agent]"} ${i}`;te("git add -A",{cwd:n}),te(`git commit -m "${c.replace(/"/g,'\\"')}"`,{cwd:n,encoding:"utf-8"});let l=te("git rev-parse --short HEAD",{cwd:n,encoding:"utf-8"}).trim();try{te("git push",{cwd:n,stdio:"pipe"})}catch(d){let u=d instanceof Error?d.message:String(d);if(u.includes("no upstream branch")){let g=await t();te(`git push --set-upstream origin ${g}`,{cwd:n,stdio:"pipe"})}else return console.error("[Git] Push failed:",u),{success:!0,commitHash:l,error:`Committed but push failed: ${u}`}}return{success:!0,commitHash:l}}catch(i){let a=i instanceof Error?i.message:String(i);return console.error("[Git] Error:",a),{success:!1,error:a}}},getCurrentBranch:t}}import*as se from"@microsoft/signalr";var X=class{constructor(e,t){this.connection=e;this.receiverMethod=t}dispose=()=>{for(let e of this.receiverMethod)this.connection.off(e.methodName,e.method)}},Mt=n=>{if(n==="IDeploymentHub")return ze.Instance;if(n==="IKanbanHub")return Je.Instance;if(n==="IAgentHub")return Ze.Instance;if(n==="ISlaveHub")return tt.Instance;if(n==="ITestHub")return ot.Instance},Ft=n=>{if(n==="IDeploymentHubClient")return rt.Instance;if(n==="IKanbanBoardClient")return it.Instance;if(n==="IAgentReceiver")return at.Instance;if(n==="IBackofficeReceiver")return ct.Instance;if(n==="ISlaveHubClient")return lt.Instance;if(n==="ITestHubClient")return pt.Instance},ze=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Ve(e)},Ve=class{constructor(e){this.connection=e}registerMachine=async(e,t)=>await this.connection.invoke("RegisterMachine",e,t);reportDeploymentStatus=async e=>await this.connection.invoke("ReportDeploymentStatus",e);reportHealth=async e=>await this.connection.invoke("ReportHealth",e);reportMachineStatus=async e=>await this.connection.invoke("ReportMachineStatus",e);sendDeploymentLog=async e=>await this.connection.invoke("SendDeploymentLog",e)},Je=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Xe(e)},Xe=class{constructor(e){this.connection=e}joinBoard=async e=>await this.connection.invoke("JoinBoard",e);leaveBoard=async e=>await this.connection.invoke("LeaveBoard",e)},Ze=class n{static Instance=new n;constructor(){}createHubProxy=e=>new et(e)},et=class{constructor(e){this.connection=e}registerAgent=async e=>await this.connection.invoke("RegisterAgent",e);getSecrets=async()=>await this.connection.invoke("GetSecrets");reportStatus=async(e,t)=>await this.connection.invoke("ReportStatus",e,t);reportSessionOutput=async(e,t,o)=>await this.connection.invoke("ReportSessionOutput",e,t,o);reportSessionCost=async(e,t,o)=>await this.connection.invoke("ReportSessionCost",e,t,o);reportInsight=async(e,t)=>await this.connection.invoke("ReportInsight",e,t);setClaudeSessionId=async(e,t)=>await this.connection.invoke("SetClaudeSessionId",e,t);sessionCompleted=async(e,t,o)=>await this.connection.invoke("SessionCompleted",e,t,o);initSessionCompleted=async(e,t,o,s)=>await this.connection.invoke("InitSessionCompleted",e,t,o,s);reportPreviewBuild=async(e,t,o)=>await this.connection.invoke("ReportPreviewBuild",e,t,o);saveSpecification=async(e,t,o,s)=>await this.connection.invoke("SaveSpecification",e,t,o,s);getSpecification=async(e,t)=>await this.connection.invoke("GetSpecification",e,t);listSpecifications=async e=>await this.connection.invoke("ListSpecifications",e);deleteSpecification=async(e,t)=>await this.connection.invoke("DeleteSpecification",e,t);watchContainer=async e=>await this.connection.invoke("WatchContainer",e);watchSession=async e=>await this.connection.invoke("WatchSession",e);watchSlaves=async()=>await this.connection.invoke("WatchSlaves");unwatchSlaves=async()=>await this.connection.invoke("UnwatchSlaves");sendPrompt=async(e,t,o,s)=>await this.connection.invoke("SendPrompt",e,t,o,s);stopSession=async e=>await this.connection.invoke("StopSession",e);switchSession=async(e,t)=>await this.connection.invoke("SwitchSession",e,t);createSession=async e=>await this.connection.invoke("CreateSession",e)},tt=class n{static Instance=new n;constructor(){}createHubProxy=e=>new nt(e)},nt=class{constructor(e){this.connection=e}register=async(e,t)=>await this.connection.invoke("Register",e,t);getSecrets=async()=>await this.connection.invoke("GetSecrets");containerCreated=async e=>await this.connection.invoke("ContainerCreated",e);containerReady=async e=>await this.connection.invoke("ContainerReady",e);reportCapacity=async e=>await this.connection.invoke("ReportCapacity",e)},ot=class n{static Instance=new n;constructor(){}createHubProxy=e=>new st(e)},st=class{constructor(e){this.connection=e}joinProject=async e=>await this.connection.invoke("JoinProject",e);leaveProject=async e=>await this.connection.invoke("LeaveProject",e)},rt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...i)=>t.deploy(...i),s=(...i)=>t.stop(...i);e.on("Deploy",o),e.on("Stop",s);let r=[{methodName:"Deploy",method:o},{methodName:"Stop",method:s}];return new X(e,r)}},it=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...f)=>t.boardUpdated(...f),s=(...f)=>t.boardDeleted(...f),r=(...f)=>t.columnCreated(...f),i=(...f)=>t.columnUpdated(...f),a=(...f)=>t.columnDeleted(...f),c=(...f)=>t.cardCreated(...f),l=(...f)=>t.cardUpdated(...f),d=(...f)=>t.cardDeleted(...f),u=(...f)=>t.subtaskCreated(...f),g=(...f)=>t.subtaskUpdated(...f),w=(...f)=>t.subtaskDeleted(...f),v=(...f)=>t.noteCreated(...f),b=(...f)=>t.noteUpdated(...f),D=(...f)=>t.noteDeleted(...f);e.on("BoardUpdated",o),e.on("BoardDeleted",s),e.on("ColumnCreated",r),e.on("ColumnUpdated",i),e.on("ColumnDeleted",a),e.on("CardCreated",c),e.on("CardUpdated",l),e.on("CardDeleted",d),e.on("SubtaskCreated",u),e.on("SubtaskUpdated",g),e.on("SubtaskDeleted",w),e.on("NoteCreated",v),e.on("NoteUpdated",b),e.on("NoteDeleted",D);let j=[{methodName:"BoardUpdated",method:o},{methodName:"BoardDeleted",method:s},{methodName:"ColumnCreated",method:r},{methodName:"ColumnUpdated",method:i},{methodName:"ColumnDeleted",method:a},{methodName:"CardCreated",method:c},{methodName:"CardUpdated",method:l},{methodName:"CardDeleted",method:d},{methodName:"SubtaskCreated",method:u},{methodName:"SubtaskUpdated",method:g},{methodName:"SubtaskDeleted",method:w},{methodName:"NoteCreated",method:v},{methodName:"NoteUpdated",method:b},{methodName:"NoteDeleted",method:D}];return new X(e,j)}},at=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...u)=>t.initSession(...u),s=(...u)=>t.runPrompt(...u),r=(...u)=>t.setModel(...u),i=()=>t.stopAgent(),a=()=>t.ping(),c=(...u)=>t.switchSession(...u),l=(...u)=>t.projectPromptUpdated(...u);e.on("InitSession",o),e.on("RunPrompt",s),e.on("SetModel",r),e.on("StopAgent",i),e.on("Ping",a),e.on("SwitchSession",c),e.on("ProjectPromptUpdated",l);let d=[{methodName:"InitSession",method:o},{methodName:"RunPrompt",method:s},{methodName:"SetModel",method:r},{methodName:"StopAgent",method:i},{methodName:"Ping",method:a},{methodName:"SwitchSession",method:c},{methodName:"ProjectPromptUpdated",method:l}];return new X(e,d)}},ct=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...b)=>t.sessionOutput(...b),s=(...b)=>t.sessionCost(...b),r=(...b)=>t.sessionCompleted(...b),i=(...b)=>t.containerStatus(...b),a=(...b)=>t.slaveCapacityUpdate(...b),c=(...b)=>t.previewBuildStatus(...b),l=(...b)=>t.previewReload(...b),d=(...b)=>t.questionsReceived(...b),u=(...b)=>t.specificationUpdated(...b),g=(...b)=>t.singleQuestionReceived(...b),w=(...b)=>t.questionSessionEnded(...b);e.on("SessionOutput",o),e.on("SessionCost",s),e.on("SessionCompleted",r),e.on("ContainerStatus",i),e.on("SlaveCapacityUpdate",a),e.on("PreviewBuildStatus",c),e.on("PreviewReload",l),e.on("QuestionsReceived",d),e.on("SpecificationUpdated",u),e.on("SingleQuestionReceived",g),e.on("QuestionSessionEnded",w);let v=[{methodName:"SessionOutput",method:o},{methodName:"SessionCost",method:s},{methodName:"SessionCompleted",method:r},{methodName:"ContainerStatus",method:i},{methodName:"SlaveCapacityUpdate",method:a},{methodName:"PreviewBuildStatus",method:c},{methodName:"PreviewReload",method:l},{methodName:"QuestionsReceived",method:d},{methodName:"SpecificationUpdated",method:u},{methodName:"SingleQuestionReceived",method:g},{methodName:"QuestionSessionEnded",method:w}];return new X(e,v)}},lt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...a)=>t.startContainer(...a),s=(...a)=>t.terminateContainer(...a),r=()=>t.reconnect();e.on("StartContainer",o),e.on("TerminateContainer",s),e.on("Reconnect",r);let i=[{methodName:"StartContainer",method:o},{methodName:"TerminateContainer",method:s},{methodName:"Reconnect",method:r}];return new X(e,i)}},pt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...l)=>t.testRunCreated(...l),s=(...l)=>t.testRunUpdated(...l),r=(...l)=>t.testSuiteCreated(...l),i=(...l)=>t.testCreated(...l),a=(...l)=>t.testUpdated(...l);e.on("TestRunCreated",o),e.on("TestRunUpdated",s),e.on("TestSuiteCreated",r),e.on("TestCreated",i),e.on("TestUpdated",a);let c=[{methodName:"TestRunCreated",method:o},{methodName:"TestRunUpdated",method:s},{methodName:"TestSuiteCreated",method:r},{methodName:"TestCreated",method:i},{methodName:"TestUpdated",method:a}];return new X(e,c)}};var je=class{constructor(e){this.config=e;this.connection=new se.HubConnectionBuilder().withUrl(`${e.masterUrl}/api/hubs/agent`,{headers:{"X-Container-Id":e.containerId,"X-Project-Key":e.projectKey}}).withAutomaticReconnect({nextRetryDelayInMilliseconds:t=>{let o=t.previousRetryCount+1,s=Math.min(1e3*Math.pow(2,t.previousRetryCount),6e4);return console.log(`[SignalR] Reconnect attempt ${o} (will retry in ${s}ms)`),s}}).withServerTimeout(3e5).withKeepAliveInterval(1e4).configureLogging(se.LogLevel.Information).build(),this.hubProxy=Mt("IAgentHub").createHubProxy(this.connection),this.connection.onreconnected(async()=>{console.log("[SignalR] Reconnected to gateway");try{await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Re-registered as agent for container: ${this.config.containerId}`),await this.reportStatus(this.currentStatus,void 0,this.currentSessionId??void 0,this.currentProjectId??void 0)}catch(t){console.error("[SignalR] Failed to re-register agent after reconnect:",t instanceof Error?t.message:t)}}),this.connection.onclose(t=>{console.error("[SignalR] Connection closed:",t?.message),process.exit(1)})}connection;hubProxy;receiverSubscription=null;currentStatus="WarmingUp";currentSessionId=null;currentProjectId=null;tunnelUrl=null;async connect(){console.log(`[SignalR] Connecting to gateway: ${this.config.masterUrl}/api/hubs/agent`),await this.connection.start(),console.log("[SignalR] Connected to gateway!"),await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Registered as agent for container: ${this.config.containerId}`)}registerReceiver(e){this.receiverSubscription&&this.receiverSubscription.dispose(),this.receiverSubscription=Ft("IAgentReceiver").register(this.connection,e)}getHubProxy(){return this.hubProxy}getMasterUrl(){return this.config.masterUrl}getProjectKey(){return this.config.projectKey}getConnection(){return this.connection}setTunnelUrl(e){this.tunnelUrl=e}async reportStatus(e,t,o,s){if(this.currentStatus=e,o&&(this.currentSessionId=o),s&&(this.currentProjectId=s),this.connection.state===se.HubConnectionState.Connected)try{let r={status:e,error:t,sessionId:o,projectId:s,tunnelUrl:this.tunnelUrl??void 0};await this.hubProxy.reportStatus(this.config.containerId,r),console.log(`[Status] ${e}${t?` (${t})`:""}${this.tunnelUrl?` [${this.tunnelUrl}]`:""}${o?` [session: ${o}]`:""}`)}catch(r){console.error("[SignalR] Failed to report status:",r)}}async getSecrets(){let e=await this.hubProxy.getSecrets();if(!e.success)throw new Error(`Failed to get secrets: ${e.error}`);return e.secrets||{}}};function Cn(n){let e=n.match(/name:\s*"([^"]+)"/);if(e)return e[1];let t=n.match(/^#\s+(.+)$/m);return t?t[1].trim():"Untitled Specification"}var Oe=class{constructor(e,t){this.hubProxy=e;this.projectId=t}async saveSpecification(e,t){let o=Cn(t),s=await this.hubProxy.saveSpecification(this.projectId,e,o,t);if(!s.success)throw new Error(s.error||"Failed to save specification")}async getSpecification(e){let t=await this.hubProxy.getSpecification(this.projectId,e);return t.success&&t.content||null}async listSpecifications(){let e=await this.hubProxy.listSpecifications(this.projectId);return!e.success||!e.specifications?[]:e.specifications.map(t=>({slug:t.slug,name:t.name,status:t.status,version:"1.0.0"}))}async deleteSpecification(e){return(await this.hubProxy.deleteSpecification(this.projectId,e)).success}async specificationExists(e){let t=await this.hubProxy.getSpecification(this.projectId,e);return t.success&&t.content!=null}};import{exec as _n,execSync as Pn,spawn as Rn}from"child_process";import{promisify as Tn}from"util";import{createHash as Lt}from"crypto";import*as k from"fs";import*as N from"path";var Ue=class{domain;accountId;tunnelId;apiToken;zoneId;tunnelApiBase;constructor(){this.domain=process.env.CLOUDFLARE_DEPLOY_DOMAIN||process.env.DEPLOY_DOMAIN||"vibecodementor.net",this.accountId=process.env.CLOUDFLARE_ACCOUNT_ID||"",this.tunnelId=process.env.CLOUDFLARE_TUNNEL_ID||"",this.apiToken=process.env.CLOUDFLARE_API_TOKEN||"",this.zoneId=process.env.CLOUDFLARE_ZONE_ID||"",this.tunnelApiBase=`https://api.cloudflare.com/client/v4/accounts/${this.accountId}/cfd_tunnel/${this.tunnelId}/configurations`}isConfigured(){return!!(this.accountId&&this.tunnelId&&this.apiToken&&this.zoneId)}buildHostname(e){let t=this.domain.split("."),o=t.length>2?t.slice(-2).join("."):this.domain;return`${e}.${o}`}async addRoute(e,t){let o=this.buildHostname(e);if(console.log(`[TunnelManager] Adding route: ${o} -> localhost:${t}`),!this.isConfigured())return console.log("[TunnelManager] Not configured, skipping route addition"),`https://${o}`;try{let s=await this.getConfig(),r=s.ingress.findIndex(a=>a.hostname===o);if(r!==-1)s.ingress[r].service=`http://localhost:${t}`;else{let a=s.ingress.findIndex(l=>!l.hostname),c={hostname:o,service:`http://localhost:${t}`};a!==-1?s.ingress.splice(a,0,c):(s.ingress.push(c),s.ingress.push({service:"http_status:404"}))}await this.putConfig(s),await this.ensureDnsPointsToTunnel(o);let i=`https://${o}`;return console.log(`[TunnelManager] \u2713 Route added: ${i}`),i}catch(s){throw console.error("[TunnelManager] Failed to add route:",s),s}}async removeRoute(e){let t=this.buildHostname(e);if(console.log(`[TunnelManager] Removing route: ${t}`),!this.isConfigured()){console.log("[TunnelManager] Not configured, skipping route removal");return}try{let o=await this.getConfig();o.ingress=o.ingress.filter(s=>s.hostname!==t),o.ingress.some(s=>!s.hostname)||o.ingress.push({service:"http_status:404"}),await this.putConfig(o),console.log(`[TunnelManager] \u2713 Route removed: ${t}`)}catch(o){throw console.error("[TunnelManager] Failed to remove route:",o),o}}async getConfig(){let t=await(await fetch(this.tunnelApiBase,{method:"GET",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"}})).json();if(!t.success)throw new Error(`Cloudflare API error: ${t.errors.map(o=>o.message).join(", ")}`);return t.result?.config||{ingress:[{service:"http_status:404"}]}}async putConfig(e){let o=await(await fetch(this.tunnelApiBase,{method:"PUT",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({config:e})})).json();if(!o.success)throw new Error(`Cloudflare API error: ${o.errors.map(s=>s.message).join(", ")}`);console.log("[TunnelManager] Configuration updated via Cloudflare API")}async ensureDnsPointsToTunnel(e){let t=`${this.tunnelId}.cfargotunnel.com`,o=`https://api.cloudflare.com/client/v4/zones/${this.zoneId}/dns_records`;try{let r=await(await fetch(`${o}?name=${e}&type=CNAME`,{method:"GET",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"}})).json();if(!r.success||!r.result?.length){console.log(`[TunnelManager] No DNS record found for ${e}, skipping DNS update`);return}let i=r.result[0];if(i.content===t){console.log(`[TunnelManager] DNS already points to correct tunnel: ${e}`);return}console.log(`[TunnelManager] Updating DNS: ${e} from ${i.content} to ${t}`);let c=await(await fetch(`${o}/${i.id}`,{method:"PATCH",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({type:"CNAME",name:e,content:t,proxied:!0})})).json();c.success?console.log(`[TunnelManager] \u2713 DNS updated: ${e} -> ${t}`):console.error(`[TunnelManager] Failed to update DNS: ${c.errors.map(l=>l.message).join(", ")}`)}catch(s){console.error(`[TunnelManager] Error updating DNS for ${e}:`,s)}}};var $t=Tn(_n),I=class{start;label;constructor(e){this.label=e,this.start=Date.now(),console.log(`[TIMING] \u23F1 START: ${e}`)}stop(){let e=Date.now()-this.start,t=(e/1e3).toFixed(2);return console.log(`[TIMING] \u2713 DONE: ${this.label} (${t}s)`),e}};function U(n,e={}){return new Promise((t,o)=>{let s=Rn(n,[],{shell:!0,stdio:"inherit",cwd:e.cwd}),r=null;e.timeout&&(r=setTimeout(()=>{s.kill(),o(new Error(`Command timed out: ${n}`))},e.timeout)),s.on("close",i=>{r&&clearTimeout(r),i===0?t():o(new Error(`Command failed with code ${i}: ${n}`))}),s.on("error",i=>{r&&clearTimeout(r),o(i)})})}var Ht="main",Ne=class{constructor(e,t,o,s){this.connection=e;this.serviceManager=t;this.workspaceDir=o;this.gitHubPat=s}currentBranchName=Ht;currentRepoUrl="";lastLockfileHash=null;tunnelManager=null;currentPreviewSubdomain=null;getTunnelManager(){return this.tunnelManager||(this.tunnelManager=new Ue),this.tunnelManager}async prewarmWorkspace(){let e=new I("TOTAL INIT"),t=process.env.REPO_URL,o=process.env.BRANCH_NAME||Ht,s=process.env.PREVIEW_SUBDOMAIN,r=process.env.PREVIEW_HOSTNAME,i=process.env.PROJECT_GITHUB_PAT;if(console.log("[Init] Starting single-phase init..."),console.log(`[Init] Repo: ${t?this.extractRepoPath(t):"NOT SET"}`),console.log(`[Init] Branch: ${o}`),console.log(`[Init] Preview: ${s||"none"}`),!t)throw console.error("[Init] \u274C REPO_URL not set - cannot initialize workspace"),new Error("REPO_URL environment variable is required");i&&(this.gitHubPat=i);let a=new I("Clean workspace");Pn("rm -rf /workspace/* /workspace/.* 2>/dev/null || true",{stdio:"inherit"}),a.stop();let c=new I("Clone repository");await this.cloneRepository(t,o),this.currentRepoUrl=t,this.currentBranchName=o,c.stop(),await this.installDependencies(),this.lastLockfileHash=this.getLockfileHash(),console.log("[Init] Starting services...");let l=new I("Start backend (dotnet)");await this.serviceManager.startBackend(),l.stop();let d=new I("Start frontend (vite)");await this.serviceManager.startFrontend(),d.stop();let u=new I("Wait for health check");await this.serviceManager.waitForHealth(3e4),u.stop(),s&&r&&await this.setupPreviewSubdomainFromEnv(s,r),e.stop(),console.log("[Init] \u2713 Workspace ready")}async handleInitSession(e){console.log(`[InitSession] Session ${e.sessionId} starting...`);let t=this.extractRepoPath(e.repoUrl),o=this.extractRepoPath(this.currentRepoUrl),s=t!==o,r=e.branchName!==this.currentBranchName;s||r?(console.log("[InitSession] Repo/branch mismatch - updating workspace..."),console.log(`[InitSession] Current: ${o}@${this.currentBranchName}`),console.log(`[InitSession] Requested: ${t}@${e.branchName}`),await this.connection.reportStatus("Ready",void 0,e.sessionId,e.projectId),await this.connection.getHubProxy().initSessionCompleted(process.env.CONTAINER_ID||"unknown",e.sessionId,!0,""),this.handleRepoChange(e).catch(async a=>{let c=a instanceof Error?a.message:"Unknown error";console.error("[InitSession] Background update failed:",c),await this.connection.reportStatus("Error",c,e.sessionId,e.projectId)})):(console.log("[InitSession] Workspace already matches - ready immediately"),await this.connection.reportStatus("Ready",void 0,e.sessionId,e.projectId),await this.connection.getHubProxy().initSessionCompleted(process.env.CONTAINER_ID||"unknown",e.sessionId,!0,"")),console.log(`[InitSession] \u2713 Ready for session ${e.sessionId}`)}async setupPreviewSubdomainFromEnv(e,t){let o=new I("Setup preview subdomain");console.log(`[Preview] Setting up preview subdomain: ${e} (${t})`);let s=this.getTunnelManager();if(!s){console.log("[Preview] TunnelManager not available, skipping subdomain setup"),o.stop();return}if(!s.isConfigured()){console.log("[Preview] Cloudflare not configured, skipping subdomain setup"),o.stop();return}try{let r=parseInt(process.env.ALLOCATED_HOST_PORT||"5173"),i=await s.addRoute(e,r);this.currentPreviewSubdomain=e,this.connection.setTunnelUrl(i),o.stop(),console.log(`[Preview] \u2713 Preview subdomain configured: ${i}`)}catch(r){o.stop(),console.error("[Preview] Failed to set up subdomain route:",r)}}async handleRepoChange(e){let t=new I("TOTAL REPO CHANGE");e.gitHubPat&&e.gitHubPat!==this.gitHubPat&&(console.log("[RepoChange] Updating git credentials"),this.gitHubPat=e.gitHubPat,await U(`echo "https://${e.gitHubPat}@github.com" > ~/.git-credentials`));let o=this.extractRepoPath(e.repoUrl),s=this.extractRepoPath(this.currentRepoUrl);if(o!==s){let i=new I("Clone new repository");await this.cloneRepository(e.repoUrl,e.branchName),this.currentRepoUrl=e.repoUrl,this.currentBranchName=e.branchName,i.stop(),await this.installDependencies();let a=new I("Restart services");await this.serviceManager.restartServices("both"),a.stop()}else{let i=new I(`Switch branch to ${e.branchName}`);await this.switchBranch(e.branchName),i.stop(),await this.checkNeedsReinstall()&&await this.installDependencies();let c=new I("Restart services");await this.serviceManager.restartServices("both"),c.stop()}e.previewSubdomain&&e.previewHostname&&await this.setupPreviewSubdomainFromEnv(e.previewSubdomain,e.previewHostname),t.stop(),console.log("[RepoChange] \u2713 Workspace updated")}extractRepoPath(e){let t=e.match(/github\.com[:/]([^/]+\/[^/]+?)(?:\.git)?$/);return t?t[1]:e}async cloneRepository(e,t){console.log(`[Clone] Clearing workspace and cloning ${this.extractRepoPath(e)} @ ${t}`),await U("rm -rf /workspace/* /workspace/.* 2>/dev/null || true"),await U(`git clone -b ${t} "${e}" /workspace`,{timeout:12e4}),await U('cd /workspace && git config user.email "agent@dotnetmentor.se" && git config user.name "Agent"'),console.log("[Clone] \u2713 Repository cloned")}async switchBranch(e){if(console.log(`[Branch] Switching from ${this.currentBranchName} to ${e}`),e===this.currentBranchName)console.log(`[Branch] Already on ${e}, pulling latest...`),await U(`git pull origin ${e}`,{cwd:this.workspaceDir,timeout:6e4});else{await U("git fetch origin",{cwd:this.workspaceDir,timeout:6e4});let{stdout:t}=await $t(`git ls-remote --heads origin ${e}`,{cwd:this.workspaceDir,timeout:3e4});t.trim().length>0?await U(`git checkout -B ${e} origin/${e}`,{cwd:this.workspaceDir,timeout:3e4}):(console.log(`[Branch] Branch ${e} doesn't exist on remote, creating from main...`),await U(`git checkout -b ${e}`,{cwd:this.workspaceDir,timeout:3e4})),this.currentBranchName=e}console.log(`[Branch] \u2713 Now on ${e}`)}getLockfileHash(){let e=N.join(this.workspaceDir,"packages/backoffice-web/package-lock.json");return k.existsSync(e)?Lt("sha256").update(k.readFileSync(e)).digest("hex"):null}async checkNeedsReinstall(){let e=this.getLockfileHash();return!e||!this.lastLockfileHash?!0:e!==this.lastLockfileHash}async checkCodeDiff(e,t){if(e===t)return!1;try{let{stdout:o}=await $t(`git diff ${e}..HEAD --name-only`,{cwd:this.workspaceDir,timeout:3e4}),s=o.trim();if(!s)return console.log(`[CodeDiff] No file differences between ${e} and ${t}`),!1;let r=s.split(`
1344
- `).filter(c=>c.length>0);console.log(`[CodeDiff] ${r.length} files changed between ${e} and ${t}`);let i=[".cs",".tsx",".ts",".json",".csproj"],a=r.some(c=>i.some(l=>c.endsWith(l)));return a&&console.log("[CodeDiff] Found code changes that require service restart"),a}catch(o){return console.error("[CodeDiff] Failed to check diff:",o instanceof Error?o.message:o),!0}}async installDependencies(){let e=new I("TOTAL INSTALL DEPENDENCIES");if(k.existsSync(N.join(this.workspaceDir,"packages/dotnet-api"))){let t=new I("dotnet restore");await U("dotnet restore",{cwd:N.join(this.workspaceDir,"packages/dotnet-api"),timeout:12e4}),t.stop()}if(k.existsSync(N.join(this.workspaceDir,"packages/backoffice-web/package.json"))){let t=N.join(this.workspaceDir,"packages/backoffice-web"),o=N.join(t,"node_modules"),s=N.join(t,"package-lock.json"),r=N.join(o,".lockhash"),i="/opt/cache";k.mkdirSync(i,{recursive:!0});let a=k.existsSync(s)?Lt("sha256").update(k.readFileSync(s)).digest("hex"):null,c=k.existsSync(r)?k.readFileSync(r,"utf-8").trim():null,l=N.join(i,"node_modules","current_hash"),d=k.existsSync(l)?k.readFileSync(l,"utf-8").trim():null,u=d?N.join(i,"node_modules",d):null,g=a?N.join(i,"node_modules",a):null;if(console.log(`[Install] node_modules exists: ${k.existsSync(o)}`),console.log(`[Install] lockHash (current branch): ${a?.substring(0,8)||"null"}`),console.log(`[Install] installedHash: ${c?.substring(0,8)||"null"}`),console.log(`[Install] dockerCacheHash (from image): ${d?.substring(0,8)||"null"}`),console.log(`[Install] exactMatchCache exists: ${g?k.existsSync(g):!1}`),console.log(`[Install] dockerCachedNodeModules exists: ${u?k.existsSync(u):!1}`),!k.existsSync(o)){if(g&&k.existsSync(g)){let v=new I("Copy exact-match cached node_modules");console.log(`[Install] Copying exact-match cached node_modules (hash ${a?.substring(0,8)})...`),await U(`cp -r "${g}" "${o}"`),a&&k.writeFileSync(r,a),c=a,v.stop()}else if(u&&k.existsSync(u)){let v=new I("Copy Docker-cached node_modules (as base)");console.log(`[Install] Copying Docker-cached node_modules as base (hash ${d?.substring(0,8)}, need ${a?.substring(0,8)})...`),await U(`cp -r "${u}" "${o}"`),v.stop()}}let w=!k.existsSync(o)||a&&a!==c;if(console.log(`[Install] needsInstall: ${w}`),!w)console.log("[Install] npm install skipped (cache OK)");else{let v=new I("npm install --prefer-offline");await U("npm install --prefer-offline --no-progress --fund=false",{cwd:t,timeout:18e4}),v.stop(),a&&(k.mkdirSync(o,{recursive:!0}),k.writeFileSync(r,a))}this.lastLockfileHash=a}e.stop()}};function Bt(n,e,t){let o=n.getHubProxy(),s=process.env.CONTAINER_ID||"unknown";return{onAssistantText:async r=>{let i={type:"Text",content:r,isError:!1};await o.reportSessionOutput(s,e,i).catch(a=>{console.error("Failed to report assistant text:",a)})},onAssistantToolUse:async(r,i)=>{if(["Write","Edit","MultiEdit"].includes(r)){let c=i,l=c.file_path||c.target_file;l&&t.onFileModified(l)}let a={type:"ToolUse",toolName:r,toolInput:JSON.stringify(i),isError:!1};await o.reportSessionOutput(s,e,a).catch(c=>{console.error("Failed to report tool use:",c)})},onResult:async(r,i)=>{let a={type:"System",event:"completed",content:r,cost:i,isError:!1};await o.reportSessionOutput(s,e,a).catch(c=>{console.error("Failed to report result:",c)})},onError:async r=>{let i={type:"System",event:"error",content:r,isError:!0};await o.reportSessionOutput(s,e,i).catch(a=>{console.error("Failed to report error:",a)})},onAborted:async()=>{let r={type:"System",event:"aborted",content:"Agent stopped by user",isError:!1};await o.reportSessionOutput(s,e,r).catch(i=>{console.error("Failed to report abort:",i)})},onSystemInit:async r=>{console.log(`[Callbacks] Claude session initialized: ${r}`),t.onClaudeSessionId(r),await o.setClaudeSessionId(e,r).catch(i=>{console.error("Failed to set Claude session ID:",i)})},onRawMessage:r=>{if(r.type==="assistant"&&r.message?.usage){let i=r.message.usage,a={totalCostUsd:In(i),inputTokens:i.input_tokens||0,outputTokens:i.output_tokens||0,cacheCreationTokens:i.cache_creation_input_tokens||0,cacheReadTokens:i.cache_read_input_tokens||0};o.reportSessionCost(s,e,a).catch(c=>{console.error("Failed to report cost:",c)})}}}}var Me={inputPerMillion:3,outputPerMillion:15,cacheReadPerMillion:.3,cacheCreationPerMillion:3.75};function In(n){return(n.input_tokens||0)/1e6*Me.inputPerMillion+(n.output_tokens||0)/1e6*Me.outputPerMillion+(n.cache_read_input_tokens||0)/1e6*Me.cacheReadPerMillion+(n.cache_creation_input_tokens||0)/1e6*Me.cacheCreationPerMillion}import*as re from"fs";import*as qt from"path";function Gt(n,e){let t=qt.join(n,`.agent-questions-${e}.json`);if(!re.existsSync(t))return null;try{let o=re.readFileSync(t,"utf-8");return re.unlinkSync(t),JSON.parse(o)}catch(o){return console.error("Failed to read questions file:",o),null}}async function Kt(n,e,t,o){if(e.size===0)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let s=o?H(o):`${t}/packages/dotnet-api`,r=o?B(o):`${t}/packages/backoffice-web`,i=o?.services?.backend?.extensions??[".cs",".csproj"],a=o?.services?.frontend?.extensions??[".ts",".tsx"],c=s&&[...e].some(g=>g.includes(s)||i.some(w=>g.endsWith(w))),l=r&&[...e].some(g=>g.includes(r)||a.some(w=>g.endsWith(w)));if(!c&&!l)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let d=[];c&&d.push("backend"),l&&d.push("frontend"),console.log(`[TypeCheck] Checking ${d.join(" & ")}...`);let u=[];if(c){let g=await n.checkBackendBuild();!g.success&&g.errors&&u.push(`## Backend Build Errors (in ${s})
1342
+ `)}`:"";m.backendError||(m.backendError=`Backend failed to start.${P}`)}return _&&!j.web&&(m.success=!1,m.frontendError||(m.frontendError="Frontend failed to start")),m},yt=async()=>{if(!l||!T.existsSync(l))return{success:!0};try{return ee(w,{cwd:l,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),{success:!0}}catch(y){let m=y;return{success:!1,errors:m.stdout||m.stderr||"Build failed"}}},bt=async()=>{if(!u||!T.existsSync(J.join(u,"package.json")))return{success:!0};try{return ee(A,{cwd:u,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),{success:!0}}catch(y){let m=y;return{success:!1,errors:m.stdout||m.stderr||"Type check failed"}}},Vt=async()=>{let[y,m]=await Promise.all([yt(),bt()]);return{backend:y,frontend:m}},St=async(y,m)=>{let S=y==="backend"?D:b;if(!T.existsSync(S))return[`Log file not found: ${S}`];try{return ee(`tail -n ${m} ${S}`,{encoding:"utf-8"}).split(`
1343
+ `).filter(Boolean)}catch(_){return[`Error reading logs: ${_ instanceof Error?_.message:String(_)}`]}};return{startBackend:C,startFrontend:ue,stopBackend:ft,stopFrontend:mt,restartServices:zt,checkHealth:$e,waitForHealth:ht,getProcesses:()=>({backend:f,frontend:O}),checkBackendBuild:yt,checkFrontendTypes:bt,runTypeChecks:Vt,tailLogs:St,checkSwaggerEndpoints:async y=>{let m=J.join(e,"swagger.json");if(!T.existsSync(m))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let S=JSON.parse(T.readFileSync(m,"utf-8")),_=Object.keys(S.paths||{}),j=[];for(let E of _)if(E.toLowerCase().includes(y.toLowerCase())){let P=Object.keys(S.paths[E]);for(let Q of P)j.push(`${Q.toUpperCase()} ${E}`)}return{foundEndpoints:j,totalEndpoints:_.length}}catch(S){return{foundEndpoints:[`Error parsing swagger.json: ${S instanceof Error?S.message:String(S)}`],totalEndpoints:0}}}}}function vn(n){let e=n.toLowerCase();return e.includes("exception")||e.includes("fail:")||e.includes("[err]")||e.includes("[error]")}import{execSync as te}from"child_process";function De(n){let e=async()=>{try{return te("git status --porcelain",{cwd:n,encoding:"utf-8"}).trim().length>0}catch{return!1}},t=async()=>{try{return te("git branch --show-current",{cwd:n,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:e,commitAndPush:async(s,r)=>{try{if(!await e())return{success:!0,noChanges:!0};let i=s.length>60?s.substring(0,60)+"...":s,c=`${r?"[agent] (partial)":"[agent]"} ${i}`;te("git add -A",{cwd:n}),te(`git commit -m "${c.replace(/"/g,'\\"')}"`,{cwd:n,encoding:"utf-8"});let l=te("git rev-parse --short HEAD",{cwd:n,encoding:"utf-8"}).trim();try{te("git push",{cwd:n,stdio:"pipe"})}catch(u){let d=u instanceof Error?u.message:String(u);if(d.includes("no upstream branch")){let g=await t();te(`git push --set-upstream origin ${g}`,{cwd:n,stdio:"pipe"})}else return console.error("[Git] Push failed:",d),{success:!0,commitHash:l,error:`Committed but push failed: ${d}`}}return{success:!0,commitHash:l}}catch(i){let a=i instanceof Error?i.message:String(i);return console.error("[Git] Error:",a),{success:!1,error:a}}},getCurrentBranch:t}}import*as se from"@microsoft/signalr";var X=class{constructor(e,t){this.connection=e;this.receiverMethod=t}dispose=()=>{for(let e of this.receiverMethod)this.connection.off(e.methodName,e.method)}},Mt=n=>{if(n==="IDeploymentHub")return ze.Instance;if(n==="IKanbanHub")return Je.Instance;if(n==="IAgentHub")return Ze.Instance;if(n==="ISlaveHub")return tt.Instance;if(n==="ITestHub")return ot.Instance},Ft=n=>{if(n==="IDeploymentHubClient")return rt.Instance;if(n==="IKanbanBoardClient")return it.Instance;if(n==="IAgentReceiver")return at.Instance;if(n==="IBackofficeReceiver")return ct.Instance;if(n==="ISlaveHubClient")return lt.Instance;if(n==="ITestHubClient")return pt.Instance},ze=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Ve(e)},Ve=class{constructor(e){this.connection=e}registerMachine=async(e,t)=>await this.connection.invoke("RegisterMachine",e,t);reportDeploymentStatus=async e=>await this.connection.invoke("ReportDeploymentStatus",e);reportHealth=async e=>await this.connection.invoke("ReportHealth",e);reportMachineStatus=async e=>await this.connection.invoke("ReportMachineStatus",e);sendDeploymentLog=async e=>await this.connection.invoke("SendDeploymentLog",e)},Je=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Xe(e)},Xe=class{constructor(e){this.connection=e}joinBoard=async e=>await this.connection.invoke("JoinBoard",e);leaveBoard=async e=>await this.connection.invoke("LeaveBoard",e)},Ze=class n{static Instance=new n;constructor(){}createHubProxy=e=>new et(e)},et=class{constructor(e){this.connection=e}registerAgent=async e=>await this.connection.invoke("RegisterAgent",e);getSecrets=async()=>await this.connection.invoke("GetSecrets");reportStatus=async(e,t)=>await this.connection.invoke("ReportStatus",e,t);reportSessionOutput=async(e,t,o)=>await this.connection.invoke("ReportSessionOutput",e,t,o);reportSessionCost=async(e,t,o)=>await this.connection.invoke("ReportSessionCost",e,t,o);reportInsight=async(e,t)=>await this.connection.invoke("ReportInsight",e,t);setClaudeSessionId=async(e,t)=>await this.connection.invoke("SetClaudeSessionId",e,t);sessionCompleted=async(e,t,o)=>await this.connection.invoke("SessionCompleted",e,t,o);initSessionCompleted=async(e,t,o,s)=>await this.connection.invoke("InitSessionCompleted",e,t,o,s);reportPreviewBuild=async(e,t,o)=>await this.connection.invoke("ReportPreviewBuild",e,t,o);saveSpecification=async(e,t,o,s)=>await this.connection.invoke("SaveSpecification",e,t,o,s);getSpecification=async(e,t)=>await this.connection.invoke("GetSpecification",e,t);listSpecifications=async e=>await this.connection.invoke("ListSpecifications",e);deleteSpecification=async(e,t)=>await this.connection.invoke("DeleteSpecification",e,t);watchContainer=async e=>await this.connection.invoke("WatchContainer",e);watchSession=async e=>await this.connection.invoke("WatchSession",e);watchSlaves=async()=>await this.connection.invoke("WatchSlaves");unwatchSlaves=async()=>await this.connection.invoke("UnwatchSlaves");sendPrompt=async(e,t,o,s)=>await this.connection.invoke("SendPrompt",e,t,o,s);stopSession=async e=>await this.connection.invoke("StopSession",e);switchSession=async(e,t)=>await this.connection.invoke("SwitchSession",e,t);createSession=async e=>await this.connection.invoke("CreateSession",e)},tt=class n{static Instance=new n;constructor(){}createHubProxy=e=>new nt(e)},nt=class{constructor(e){this.connection=e}register=async(e,t)=>await this.connection.invoke("Register",e,t);getSecrets=async()=>await this.connection.invoke("GetSecrets");containerCreated=async e=>await this.connection.invoke("ContainerCreated",e);containerReady=async e=>await this.connection.invoke("ContainerReady",e);reportCapacity=async e=>await this.connection.invoke("ReportCapacity",e)},ot=class n{static Instance=new n;constructor(){}createHubProxy=e=>new st(e)},st=class{constructor(e){this.connection=e}joinProject=async e=>await this.connection.invoke("JoinProject",e);leaveProject=async e=>await this.connection.invoke("LeaveProject",e)},rt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...i)=>t.deploy(...i),s=(...i)=>t.stop(...i);e.on("Deploy",o),e.on("Stop",s);let r=[{methodName:"Deploy",method:o},{methodName:"Stop",method:s}];return new X(e,r)}},it=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...f)=>t.boardUpdated(...f),s=(...f)=>t.boardDeleted(...f),r=(...f)=>t.columnCreated(...f),i=(...f)=>t.columnUpdated(...f),a=(...f)=>t.columnDeleted(...f),c=(...f)=>t.cardCreated(...f),l=(...f)=>t.cardUpdated(...f),u=(...f)=>t.cardDeleted(...f),d=(...f)=>t.subtaskCreated(...f),g=(...f)=>t.subtaskUpdated(...f),w=(...f)=>t.subtaskDeleted(...f),v=(...f)=>t.noteCreated(...f),A=(...f)=>t.noteUpdated(...f),D=(...f)=>t.noteDeleted(...f);e.on("BoardUpdated",o),e.on("BoardDeleted",s),e.on("ColumnCreated",r),e.on("ColumnUpdated",i),e.on("ColumnDeleted",a),e.on("CardCreated",c),e.on("CardUpdated",l),e.on("CardDeleted",u),e.on("SubtaskCreated",d),e.on("SubtaskUpdated",g),e.on("SubtaskDeleted",w),e.on("NoteCreated",v),e.on("NoteUpdated",A),e.on("NoteDeleted",D);let b=[{methodName:"BoardUpdated",method:o},{methodName:"BoardDeleted",method:s},{methodName:"ColumnCreated",method:r},{methodName:"ColumnUpdated",method:i},{methodName:"ColumnDeleted",method:a},{methodName:"CardCreated",method:c},{methodName:"CardUpdated",method:l},{methodName:"CardDeleted",method:u},{methodName:"SubtaskCreated",method:d},{methodName:"SubtaskUpdated",method:g},{methodName:"SubtaskDeleted",method:w},{methodName:"NoteCreated",method:v},{methodName:"NoteUpdated",method:A},{methodName:"NoteDeleted",method:D}];return new X(e,b)}},at=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...d)=>t.initSession(...d),s=(...d)=>t.runPrompt(...d),r=(...d)=>t.setModel(...d),i=()=>t.stopAgent(),a=()=>t.ping(),c=(...d)=>t.switchSession(...d),l=(...d)=>t.projectPromptUpdated(...d);e.on("InitSession",o),e.on("RunPrompt",s),e.on("SetModel",r),e.on("StopAgent",i),e.on("Ping",a),e.on("SwitchSession",c),e.on("ProjectPromptUpdated",l);let u=[{methodName:"InitSession",method:o},{methodName:"RunPrompt",method:s},{methodName:"SetModel",method:r},{methodName:"StopAgent",method:i},{methodName:"Ping",method:a},{methodName:"SwitchSession",method:c},{methodName:"ProjectPromptUpdated",method:l}];return new X(e,u)}},ct=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...b)=>t.sessionOutput(...b),s=(...b)=>t.sessionCost(...b),r=(...b)=>t.sessionCompleted(...b),i=(...b)=>t.containerStatus(...b),a=(...b)=>t.slaveCapacityUpdate(...b),c=(...b)=>t.previewBuildStatus(...b),l=(...b)=>t.previewReload(...b),u=(...b)=>t.questionsReceived(...b),d=(...b)=>t.specificationUpdated(...b),g=(...b)=>t.singleQuestionReceived(...b),w=(...b)=>t.questionSessionEnded(...b),v=(...b)=>t.creditsUpdated(...b),A=(...b)=>t.insufficientCredits(...b);e.on("SessionOutput",o),e.on("SessionCost",s),e.on("SessionCompleted",r),e.on("ContainerStatus",i),e.on("SlaveCapacityUpdate",a),e.on("PreviewBuildStatus",c),e.on("PreviewReload",l),e.on("QuestionsReceived",u),e.on("SpecificationUpdated",d),e.on("SingleQuestionReceived",g),e.on("QuestionSessionEnded",w),e.on("CreditsUpdated",v),e.on("InsufficientCredits",A);let D=[{methodName:"SessionOutput",method:o},{methodName:"SessionCost",method:s},{methodName:"SessionCompleted",method:r},{methodName:"ContainerStatus",method:i},{methodName:"SlaveCapacityUpdate",method:a},{methodName:"PreviewBuildStatus",method:c},{methodName:"PreviewReload",method:l},{methodName:"QuestionsReceived",method:u},{methodName:"SpecificationUpdated",method:d},{methodName:"SingleQuestionReceived",method:g},{methodName:"QuestionSessionEnded",method:w},{methodName:"CreditsUpdated",method:v},{methodName:"InsufficientCredits",method:A}];return new X(e,D)}},lt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...a)=>t.startContainer(...a),s=(...a)=>t.terminateContainer(...a),r=()=>t.reconnect();e.on("StartContainer",o),e.on("TerminateContainer",s),e.on("Reconnect",r);let i=[{methodName:"StartContainer",method:o},{methodName:"TerminateContainer",method:s},{methodName:"Reconnect",method:r}];return new X(e,i)}},pt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...l)=>t.testRunCreated(...l),s=(...l)=>t.testRunUpdated(...l),r=(...l)=>t.testSuiteCreated(...l),i=(...l)=>t.testCreated(...l),a=(...l)=>t.testUpdated(...l);e.on("TestRunCreated",o),e.on("TestRunUpdated",s),e.on("TestSuiteCreated",r),e.on("TestCreated",i),e.on("TestUpdated",a);let c=[{methodName:"TestRunCreated",method:o},{methodName:"TestRunUpdated",method:s},{methodName:"TestSuiteCreated",method:r},{methodName:"TestCreated",method:i},{methodName:"TestUpdated",method:a}];return new X(e,c)}};var je=class{constructor(e){this.config=e;let t={"X-Container-Id":e.containerId,"X-Project-Key":e.projectKey};e.isByok&&(t["X-Is-Byok"]="true"),this.connection=new se.HubConnectionBuilder().withUrl(`${e.masterUrl}/api/hubs/agent`,{headers:t}).withAutomaticReconnect({nextRetryDelayInMilliseconds:o=>{let s=o.previousRetryCount+1,r=Math.min(1e3*Math.pow(2,o.previousRetryCount),6e4);return console.log(`[SignalR] Reconnect attempt ${s} (will retry in ${r}ms)`),r}}).withServerTimeout(3e5).withKeepAliveInterval(1e4).configureLogging(se.LogLevel.Information).build(),this.hubProxy=Mt("IAgentHub").createHubProxy(this.connection),this.connection.onreconnected(async()=>{console.log("[SignalR] Reconnected to gateway");try{await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Re-registered as agent for container: ${this.config.containerId}`),await this.reportStatus(this.currentStatus,void 0,this.currentSessionId??void 0,this.currentProjectId??void 0)}catch(o){console.error("[SignalR] Failed to re-register agent after reconnect:",o instanceof Error?o.message:o)}}),this.connection.onclose(o=>{console.error("[SignalR] Connection closed:",o?.message),process.exit(1)})}connection;hubProxy;receiverSubscription=null;currentStatus="WarmingUp";currentSessionId=null;currentProjectId=null;tunnelUrl=null;async connect(){console.log(`[SignalR] Connecting to gateway: ${this.config.masterUrl}/api/hubs/agent`),this.config.isByok&&console.log("[SignalR] BYOK mode enabled - credits will not be deducted"),await this.connection.start(),console.log("[SignalR] Connected to gateway!"),await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Registered as agent for container: ${this.config.containerId}`)}registerReceiver(e){this.receiverSubscription&&this.receiverSubscription.dispose(),this.receiverSubscription=Ft("IAgentReceiver").register(this.connection,e)}getHubProxy(){return this.hubProxy}getMasterUrl(){return this.config.masterUrl}getProjectKey(){return this.config.projectKey}getConnection(){return this.connection}setTunnelUrl(e){this.tunnelUrl=e}async reportStatus(e,t,o,s){if(this.currentStatus=e,o&&(this.currentSessionId=o),s&&(this.currentProjectId=s),this.connection.state===se.HubConnectionState.Connected)try{let r={status:e,error:t,sessionId:o,projectId:s,tunnelUrl:this.tunnelUrl??void 0};await this.hubProxy.reportStatus(this.config.containerId,r),console.log(`[Status] ${e}${t?` (${t})`:""}${this.tunnelUrl?` [${this.tunnelUrl}]`:""}${o?` [session: ${o}]`:""}`)}catch(r){console.error("[SignalR] Failed to report status:",r)}}async getSecrets(){let e=await this.hubProxy.getSecrets();if(!e.success)throw new Error(`Failed to get secrets: ${e.error}`);return e.secrets||{}}};function Cn(n){let e=n.match(/name:\s*"([^"]+)"/);if(e)return e[1];let t=n.match(/^#\s+(.+)$/m);return t?t[1].trim():"Untitled Specification"}var Oe=class{constructor(e,t){this.hubProxy=e;this.projectId=t}async saveSpecification(e,t){let o=Cn(t),s=await this.hubProxy.saveSpecification(this.projectId,e,o,t);if(!s.success)throw new Error(s.error||"Failed to save specification")}async getSpecification(e){let t=await this.hubProxy.getSpecification(this.projectId,e);return t.success&&t.content||null}async listSpecifications(){let e=await this.hubProxy.listSpecifications(this.projectId);return!e.success||!e.specifications?[]:e.specifications.map(t=>({slug:t.slug,name:t.name,status:t.status,version:"1.0.0"}))}async deleteSpecification(e){return(await this.hubProxy.deleteSpecification(this.projectId,e)).success}async specificationExists(e){let t=await this.hubProxy.getSpecification(this.projectId,e);return t.success&&t.content!=null}};import{exec as _n,execSync as Pn,spawn as Rn}from"child_process";import{promisify as Tn}from"util";import{createHash as Lt}from"crypto";import*as k from"fs";import*as N from"path";var Ue=class{domain;accountId;tunnelId;apiToken;zoneId;tunnelApiBase;constructor(){this.domain=process.env.CLOUDFLARE_DEPLOY_DOMAIN||process.env.DEPLOY_DOMAIN||"vibecodementor.net",this.accountId=process.env.CLOUDFLARE_ACCOUNT_ID||"",this.tunnelId=process.env.CLOUDFLARE_TUNNEL_ID||"",this.apiToken=process.env.CLOUDFLARE_API_TOKEN||"",this.zoneId=process.env.CLOUDFLARE_ZONE_ID||"",this.tunnelApiBase=`https://api.cloudflare.com/client/v4/accounts/${this.accountId}/cfd_tunnel/${this.tunnelId}/configurations`}isConfigured(){return!!(this.accountId&&this.tunnelId&&this.apiToken&&this.zoneId)}buildHostname(e){let t=this.domain.split("."),o=t.length>2?t.slice(-2).join("."):this.domain;return`${e}.${o}`}async addRoute(e,t){let o=this.buildHostname(e);if(console.log(`[TunnelManager] Adding route: ${o} -> localhost:${t}`),!this.isConfigured())return console.log("[TunnelManager] Not configured, skipping route addition"),`https://${o}`;try{let s=await this.getConfig(),r=s.ingress.findIndex(a=>a.hostname===o);if(r!==-1)s.ingress[r].service=`http://localhost:${t}`;else{let a=s.ingress.findIndex(l=>!l.hostname),c={hostname:o,service:`http://localhost:${t}`};a!==-1?s.ingress.splice(a,0,c):(s.ingress.push(c),s.ingress.push({service:"http_status:404"}))}await this.putConfig(s),await this.ensureDnsPointsToTunnel(o);let i=`https://${o}`;return console.log(`[TunnelManager] \u2713 Route added: ${i}`),i}catch(s){throw console.error("[TunnelManager] Failed to add route:",s),s}}async removeRoute(e){let t=this.buildHostname(e);if(console.log(`[TunnelManager] Removing route: ${t}`),!this.isConfigured()){console.log("[TunnelManager] Not configured, skipping route removal");return}try{let o=await this.getConfig();o.ingress=o.ingress.filter(s=>s.hostname!==t),o.ingress.some(s=>!s.hostname)||o.ingress.push({service:"http_status:404"}),await this.putConfig(o),console.log(`[TunnelManager] \u2713 Route removed: ${t}`)}catch(o){throw console.error("[TunnelManager] Failed to remove route:",o),o}}async getConfig(){let t=await(await fetch(this.tunnelApiBase,{method:"GET",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"}})).json();if(!t.success)throw new Error(`Cloudflare API error: ${t.errors.map(o=>o.message).join(", ")}`);return t.result?.config||{ingress:[{service:"http_status:404"}]}}async putConfig(e){let o=await(await fetch(this.tunnelApiBase,{method:"PUT",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({config:e})})).json();if(!o.success)throw new Error(`Cloudflare API error: ${o.errors.map(s=>s.message).join(", ")}`);console.log("[TunnelManager] Configuration updated via Cloudflare API")}async ensureDnsPointsToTunnel(e){let t=`${this.tunnelId}.cfargotunnel.com`,o=`https://api.cloudflare.com/client/v4/zones/${this.zoneId}/dns_records`;try{let r=await(await fetch(`${o}?name=${e}&type=CNAME`,{method:"GET",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"}})).json();if(!r.success||!r.result?.length){console.log(`[TunnelManager] No DNS record found for ${e}, skipping DNS update`);return}let i=r.result[0];if(i.content===t){console.log(`[TunnelManager] DNS already points to correct tunnel: ${e}`);return}console.log(`[TunnelManager] Updating DNS: ${e} from ${i.content} to ${t}`);let c=await(await fetch(`${o}/${i.id}`,{method:"PATCH",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({type:"CNAME",name:e,content:t,proxied:!0})})).json();c.success?console.log(`[TunnelManager] \u2713 DNS updated: ${e} -> ${t}`):console.error(`[TunnelManager] Failed to update DNS: ${c.errors.map(l=>l.message).join(", ")}`)}catch(s){console.error(`[TunnelManager] Error updating DNS for ${e}:`,s)}}};var $t=Tn(_n),I=class{start;label;constructor(e){this.label=e,this.start=Date.now(),console.log(`[TIMING] \u23F1 START: ${e}`)}stop(){let e=Date.now()-this.start,t=(e/1e3).toFixed(2);return console.log(`[TIMING] \u2713 DONE: ${this.label} (${t}s)`),e}};function U(n,e={}){return new Promise((t,o)=>{let s=Rn(n,[],{shell:!0,stdio:"inherit",cwd:e.cwd}),r=null;e.timeout&&(r=setTimeout(()=>{s.kill(),o(new Error(`Command timed out: ${n}`))},e.timeout)),s.on("close",i=>{r&&clearTimeout(r),i===0?t():o(new Error(`Command failed with code ${i}: ${n}`))}),s.on("error",i=>{r&&clearTimeout(r),o(i)})})}var Ht="main",Ne=class{constructor(e,t,o,s){this.connection=e;this.serviceManager=t;this.workspaceDir=o;this.gitHubPat=s}currentBranchName=Ht;currentRepoUrl="";lastLockfileHash=null;tunnelManager=null;currentPreviewSubdomain=null;getTunnelManager(){return this.tunnelManager||(this.tunnelManager=new Ue),this.tunnelManager}async prewarmWorkspace(){let e=new I("TOTAL INIT"),t=process.env.REPO_URL,o=process.env.BRANCH_NAME||Ht,s=process.env.PREVIEW_SUBDOMAIN,r=process.env.PREVIEW_HOSTNAME,i=process.env.PROJECT_GITHUB_PAT;if(console.log("[Init] Starting single-phase init..."),console.log(`[Init] Repo: ${t?this.extractRepoPath(t):"NOT SET"}`),console.log(`[Init] Branch: ${o}`),console.log(`[Init] Preview: ${s||"none"}`),!t)throw console.error("[Init] \u274C REPO_URL not set - cannot initialize workspace"),new Error("REPO_URL environment variable is required");i&&(this.gitHubPat=i);let a=new I("Clean workspace");Pn("rm -rf /workspace/* /workspace/.* 2>/dev/null || true",{stdio:"inherit"}),a.stop();let c=new I("Clone repository");await this.cloneRepository(t,o),this.currentRepoUrl=t,this.currentBranchName=o,c.stop(),await this.installDependencies(),this.lastLockfileHash=this.getLockfileHash(),console.log("[Init] Starting services...");let l=new I("Start backend (dotnet)");await this.serviceManager.startBackend(),l.stop();let u=new I("Start frontend (vite)");await this.serviceManager.startFrontend(),u.stop();let d=new I("Wait for health check");await this.serviceManager.waitForHealth(3e4),d.stop(),s&&r&&await this.setupPreviewSubdomainFromEnv(s,r),e.stop(),console.log("[Init] \u2713 Workspace ready")}async handleInitSession(e){console.log(`[InitSession] Session ${e.sessionId} starting...`);let t=this.extractRepoPath(e.repoUrl),o=this.extractRepoPath(this.currentRepoUrl),s=t!==o,r=e.branchName!==this.currentBranchName;s||r?(console.log("[InitSession] Repo/branch mismatch - updating workspace..."),console.log(`[InitSession] Current: ${o}@${this.currentBranchName}`),console.log(`[InitSession] Requested: ${t}@${e.branchName}`),await this.connection.reportStatus("Ready",void 0,e.sessionId,e.projectId),await this.connection.getHubProxy().initSessionCompleted(process.env.CONTAINER_ID||"unknown",e.sessionId,!0,""),this.handleRepoChange(e).catch(async a=>{let c=a instanceof Error?a.message:"Unknown error";console.error("[InitSession] Background update failed:",c),await this.connection.reportStatus("Error",c,e.sessionId,e.projectId)})):(console.log("[InitSession] Workspace already matches - ready immediately"),await this.connection.reportStatus("Ready",void 0,e.sessionId,e.projectId),await this.connection.getHubProxy().initSessionCompleted(process.env.CONTAINER_ID||"unknown",e.sessionId,!0,"")),console.log(`[InitSession] \u2713 Ready for session ${e.sessionId}`)}async setupPreviewSubdomainFromEnv(e,t){let o=new I("Setup preview subdomain");console.log(`[Preview] Setting up preview subdomain: ${e} (${t})`);let s=this.getTunnelManager();if(!s){console.log("[Preview] TunnelManager not available, skipping subdomain setup"),o.stop();return}if(!s.isConfigured()){console.log("[Preview] Cloudflare not configured, skipping subdomain setup"),o.stop();return}try{let r=parseInt(process.env.ALLOCATED_HOST_PORT||"5173"),i=await s.addRoute(e,r);this.currentPreviewSubdomain=e,this.connection.setTunnelUrl(i),o.stop(),console.log(`[Preview] \u2713 Preview subdomain configured: ${i}`)}catch(r){o.stop(),console.error("[Preview] Failed to set up subdomain route:",r)}}async handleRepoChange(e){let t=new I("TOTAL REPO CHANGE");e.gitHubPat&&e.gitHubPat!==this.gitHubPat&&(console.log("[RepoChange] Updating git credentials"),this.gitHubPat=e.gitHubPat,await U(`echo "https://${e.gitHubPat}@github.com" > ~/.git-credentials`));let o=this.extractRepoPath(e.repoUrl),s=this.extractRepoPath(this.currentRepoUrl);if(o!==s){let i=new I("Clone new repository");await this.cloneRepository(e.repoUrl,e.branchName),this.currentRepoUrl=e.repoUrl,this.currentBranchName=e.branchName,i.stop(),await this.installDependencies();let a=new I("Restart services");await this.serviceManager.restartServices("both"),a.stop()}else{let i=new I(`Switch branch to ${e.branchName}`);await this.switchBranch(e.branchName),i.stop(),await this.checkNeedsReinstall()&&await this.installDependencies();let c=new I("Restart services");await this.serviceManager.restartServices("both"),c.stop()}e.previewSubdomain&&e.previewHostname&&await this.setupPreviewSubdomainFromEnv(e.previewSubdomain,e.previewHostname),t.stop(),console.log("[RepoChange] \u2713 Workspace updated")}extractRepoPath(e){let t=e.match(/github\.com[:/]([^/]+\/[^/]+?)(?:\.git)?$/);return t?t[1]:e}async cloneRepository(e,t){console.log(`[Clone] Clearing workspace and cloning ${this.extractRepoPath(e)} @ ${t}`),await U("rm -rf /workspace/* /workspace/.* 2>/dev/null || true"),await U(`git clone -b ${t} "${e}" /workspace`,{timeout:12e4}),await U('cd /workspace && git config user.email "agent@dotnetmentor.se" && git config user.name "Agent"'),console.log("[Clone] \u2713 Repository cloned")}async switchBranch(e){if(console.log(`[Branch] Switching from ${this.currentBranchName} to ${e}`),e===this.currentBranchName)console.log(`[Branch] Already on ${e}, pulling latest...`),await U(`git pull origin ${e}`,{cwd:this.workspaceDir,timeout:6e4});else{await U("git fetch origin",{cwd:this.workspaceDir,timeout:6e4});let{stdout:t}=await $t(`git ls-remote --heads origin ${e}`,{cwd:this.workspaceDir,timeout:3e4});t.trim().length>0?await U(`git checkout -B ${e} origin/${e}`,{cwd:this.workspaceDir,timeout:3e4}):(console.log(`[Branch] Branch ${e} doesn't exist on remote, creating from main...`),await U(`git checkout -b ${e}`,{cwd:this.workspaceDir,timeout:3e4})),this.currentBranchName=e}console.log(`[Branch] \u2713 Now on ${e}`)}getLockfileHash(){let e=N.join(this.workspaceDir,"packages/backoffice-web/package-lock.json");return k.existsSync(e)?Lt("sha256").update(k.readFileSync(e)).digest("hex"):null}async checkNeedsReinstall(){let e=this.getLockfileHash();return!e||!this.lastLockfileHash?!0:e!==this.lastLockfileHash}async checkCodeDiff(e,t){if(e===t)return!1;try{let{stdout:o}=await $t(`git diff ${e}..HEAD --name-only`,{cwd:this.workspaceDir,timeout:3e4}),s=o.trim();if(!s)return console.log(`[CodeDiff] No file differences between ${e} and ${t}`),!1;let r=s.split(`
1344
+ `).filter(c=>c.length>0);console.log(`[CodeDiff] ${r.length} files changed between ${e} and ${t}`);let i=[".cs",".tsx",".ts",".json",".csproj"],a=r.some(c=>i.some(l=>c.endsWith(l)));return a&&console.log("[CodeDiff] Found code changes that require service restart"),a}catch(o){return console.error("[CodeDiff] Failed to check diff:",o instanceof Error?o.message:o),!0}}async installDependencies(){let e=new I("TOTAL INSTALL DEPENDENCIES");if(k.existsSync(N.join(this.workspaceDir,"packages/dotnet-api"))){let t=new I("dotnet restore");await U("dotnet restore",{cwd:N.join(this.workspaceDir,"packages/dotnet-api"),timeout:12e4}),t.stop()}if(k.existsSync(N.join(this.workspaceDir,"packages/backoffice-web/package.json"))){let t=N.join(this.workspaceDir,"packages/backoffice-web"),o=N.join(t,"node_modules"),s=N.join(t,"package-lock.json"),r=N.join(o,".lockhash"),i="/opt/cache";k.mkdirSync(i,{recursive:!0});let a=k.existsSync(s)?Lt("sha256").update(k.readFileSync(s)).digest("hex"):null,c=k.existsSync(r)?k.readFileSync(r,"utf-8").trim():null,l=N.join(i,"node_modules","current_hash"),u=k.existsSync(l)?k.readFileSync(l,"utf-8").trim():null,d=u?N.join(i,"node_modules",u):null,g=a?N.join(i,"node_modules",a):null;if(console.log(`[Install] node_modules exists: ${k.existsSync(o)}`),console.log(`[Install] lockHash (current branch): ${a?.substring(0,8)||"null"}`),console.log(`[Install] installedHash: ${c?.substring(0,8)||"null"}`),console.log(`[Install] dockerCacheHash (from image): ${u?.substring(0,8)||"null"}`),console.log(`[Install] exactMatchCache exists: ${g?k.existsSync(g):!1}`),console.log(`[Install] dockerCachedNodeModules exists: ${d?k.existsSync(d):!1}`),!k.existsSync(o)){if(g&&k.existsSync(g)){let v=new I("Copy exact-match cached node_modules");console.log(`[Install] Copying exact-match cached node_modules (hash ${a?.substring(0,8)})...`),await U(`cp -r "${g}" "${o}"`),a&&k.writeFileSync(r,a),c=a,v.stop()}else if(d&&k.existsSync(d)){let v=new I("Copy Docker-cached node_modules (as base)");console.log(`[Install] Copying Docker-cached node_modules as base (hash ${u?.substring(0,8)}, need ${a?.substring(0,8)})...`),await U(`cp -r "${d}" "${o}"`),v.stop()}}let w=!k.existsSync(o)||a&&a!==c;if(console.log(`[Install] needsInstall: ${w}`),!w)console.log("[Install] npm install skipped (cache OK)");else{let v=new I("npm install --prefer-offline");await U("npm install --prefer-offline --no-progress --fund=false",{cwd:t,timeout:18e4}),v.stop(),a&&(k.mkdirSync(o,{recursive:!0}),k.writeFileSync(r,a))}this.lastLockfileHash=a}e.stop()}};function Bt(n,e,t){let o=n.getHubProxy(),s=process.env.CONTAINER_ID||"unknown";return{onAssistantText:async r=>{let i={type:"Text",content:r,isError:!1};await o.reportSessionOutput(s,e,i).catch(a=>{console.error("Failed to report assistant text:",a)})},onAssistantToolUse:async(r,i)=>{if(["Write","Edit","MultiEdit"].includes(r)){let c=i,l=c.file_path||c.target_file;l&&t.onFileModified(l)}let a={type:"ToolUse",toolName:r,toolInput:JSON.stringify(i),isError:!1};await o.reportSessionOutput(s,e,a).catch(c=>{console.error("Failed to report tool use:",c)})},onResult:async(r,i)=>{let a={type:"System",event:"completed",content:r,cost:i,isError:!1};await o.reportSessionOutput(s,e,a).catch(c=>{console.error("Failed to report result:",c)})},onError:async r=>{let i={type:"System",event:"error",content:r,isError:!0};await o.reportSessionOutput(s,e,i).catch(a=>{console.error("Failed to report error:",a)})},onAborted:async()=>{let r={type:"System",event:"aborted",content:"Agent stopped by user",isError:!1};await o.reportSessionOutput(s,e,r).catch(i=>{console.error("Failed to report abort:",i)})},onSystemInit:async r=>{console.log(`[Callbacks] Claude session initialized: ${r}`),t.onClaudeSessionId(r),await o.setClaudeSessionId(e,r).catch(i=>{console.error("Failed to set Claude session ID:",i)})},onRawMessage:r=>{if(r.type==="assistant"&&r.message?.usage){let i=r.message.usage,a={totalCostUsd:In(i),inputTokens:i.input_tokens||0,outputTokens:i.output_tokens||0,cacheCreationTokens:i.cache_creation_input_tokens||0,cacheReadTokens:i.cache_read_input_tokens||0};o.reportSessionCost(s,e,a).catch(c=>{console.error("Failed to report cost:",c)})}}}}var Me={inputPerMillion:3,outputPerMillion:15,cacheReadPerMillion:.3,cacheCreationPerMillion:3.75};function In(n){return(n.input_tokens||0)/1e6*Me.inputPerMillion+(n.output_tokens||0)/1e6*Me.outputPerMillion+(n.cache_read_input_tokens||0)/1e6*Me.cacheReadPerMillion+(n.cache_creation_input_tokens||0)/1e6*Me.cacheCreationPerMillion}import*as re from"fs";import*as qt from"path";function Gt(n,e){let t=qt.join(n,`.agent-questions-${e}.json`);if(!re.existsSync(t))return null;try{let o=re.readFileSync(t,"utf-8");return re.unlinkSync(t),JSON.parse(o)}catch(o){return console.error("Failed to read questions file:",o),null}}async function Kt(n,e,t,o){if(e.size===0)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let s=o?H(o):`${t}/packages/dotnet-api`,r=o?B(o):`${t}/packages/backoffice-web`,i=o?.services?.backend?.extensions??[".cs",".csproj"],a=o?.services?.frontend?.extensions??[".ts",".tsx"],c=s&&[...e].some(g=>g.includes(s)||i.some(w=>g.endsWith(w))),l=r&&[...e].some(g=>g.includes(r)||a.some(w=>g.endsWith(w)));if(!c&&!l)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let u=[];c&&u.push("backend"),l&&u.push("frontend"),console.log(`[TypeCheck] Checking ${u.join(" & ")}...`);let d=[];if(c){let g=await n.checkBackendBuild();!g.success&&g.errors&&d.push(`## Backend Build Errors (in ${s})
1345
1345
  \`\`\`
1346
1346
  ${g.errors}
1347
- \`\`\``)}if(l){let g=await n.checkFrontendTypes();!g.success&&g.errors&&u.push(`## Frontend Type Errors (in ${r})
1347
+ \`\`\``)}if(l){let g=await n.checkFrontendTypes();!g.success&&g.errors&&d.push(`## Frontend Type Errors (in ${r})
1348
1348
  \`\`\`
1349
1349
  ${g.errors}
1350
- \`\`\``)}return u.length>0?(console.log("[TypeCheck] \u274C Type errors found - will auto-correct"),{passed:!1,errorPrompt:`@debugger Type/build errors detected. Fix them.
1350
+ \`\`\``)}return d.length>0?(console.log("[TypeCheck] \u274C Type errors found - will auto-correct"),{passed:!1,errorPrompt:`@debugger Type/build errors detected. Fix them.
1351
1351
 
1352
1352
  WORKSPACE: ${t}
1353
1353
 
1354
- ${u.join(`
1354
+ ${d.join(`
1355
1355
 
1356
1356
  `)}
1357
1357
 
1358
- File paths like "src/..." are relative to the package directory shown in parentheses.`}):(console.log("[TypeCheck] \u2713 All type checks passed"),{passed:!0})}async function ut(n,e,t,o,s){if(!await n.hasChanges()){console.log("[Git] No changes to commit");return}console.log("[Git] Committing changes...");let i=await n.commitAndPush(o,s);if(i.noChanges)console.log("[Git] No changes to commit");else if(i.success){let a=await n.getCurrentBranch();console.log(`[Git] \u2713 Committed ${i.commitHash||""}`),console.log(`[Git] \u2192 Pushed to ${a}`),await e.getHubProxy().reportSessionOutput(process.env.CONTAINER_ID||"unknown",t,{type:"System",event:"committed",content:`Changes committed to ${a}`,isError:!1}).catch(l=>{console.error("[Git] Failed to report commit:",l)})}else console.error(`[Git] \u274C Git error: ${i.error}`)}var Fe=class{constructor(e,t,o,s,r=!0,i=!0,a,c,l=!0){this.connection=e;this.lifecycle=t;this.serviceManager=o;this.workspaceDir=s;this.enableAutoTypecheck=r;this.enableAutoCommit=i;this.setupPlanningTransport=a;this.projectConfig=c;this.useDefaultStack=l;this.gitManager=De(s)}currentSession=null;claudeSessionIdMap=new Map;currentAbortController=null;gitManager;async initSession(e){console.log(`[Receiver] InitSession for session ${e.sessionId}`),this.setupPlanningTransport&&e.projectId&&this.setupPlanningTransport(e.projectId),e.projectId&&await this.fetchProjectPrompt(e.projectId),this.currentSession={sessionId:e.sessionId,projectId:e.projectId,branchName:e.branchName,model:e.model==="auto"?void 0:e.model||"claude-haiku-4-5-20251001",debugMode:e.debugMode},await this.lifecycle.handleInitSession(e),e.prompt&&await this.runPrompt(e.sessionId,e.prompt,e.claudeSessionId)}async runPrompt(e,t,o,s=!1){let r=pe(t);if(console.log(`[Receiver] RunPrompt for session ${e}${o?` (ClaudeSessionId: ${o})`:""}${s?" [DEBUG MODE]":""}: ${r.substring(0,50)}...`),(!this.currentSession||this.currentSession.sessionId!==e)&&(console.log(`[Receiver] Session mismatch: expected ${this.currentSession?.sessionId}, got ${e}, switching...`),await this.switchSession(e),!this.currentSession)){console.error("[Receiver] Failed to create session context");return}let i=this.claudeSessionIdMap.get(e);if(o&&i!==o){console.warn(`[Receiver] Claude session not found in container: ${o}`);let l=this.connection.getHubProxy();await l.reportSessionOutput(process.env.CONTAINER_ID||"unknown",e,{type:"System",event:"sessionNotFound",content:o,isError:!0}),await this.connection.reportStatus("Ready","Session lost - summary needed",e,this.currentSession.projectId),await l.sessionCompleted(process.env.CONTAINER_ID||"unknown",e,0);return}await this.connection.reportStatus("Active",void 0,e,this.currentSession.projectId),this.currentSession.debugMode=s,this.currentAbortController=new AbortController;let a=i||null,c=new Set;try{let l=await Ae(r,{model:this.currentSession.model,sessionId:a||null,projectId:this.currentSession.projectId||null,apiUrl:this.connection.getMasterUrl(),debugLog:!0,debugMode:s,abortController:this.currentAbortController,projectConfig:this.projectConfig,useDefaultStack:this.useDefaultStack,callbacks:Bt(this.connection,e,{onClaudeSessionId:g=>{this.claudeSessionIdMap.set(e,g)},onFileModified:g=>{c.add(g)}})}),d=Gt(this.workspaceDir,e);if(d&&d.length>0){console.log(`[Receiver] ${d.length} pending questions found, waiting for user answers`),await this.connection.reportStatus("Ready",void 0,e,this.currentSession.projectId),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,0);return}if(this.enableAutoTypecheck&&!l.error&&!l.aborted){let g=await Kt(this.serviceManager,c,this.workspaceDir,this.projectConfig);if(!g.passed&&g.errorPrompt){console.log("[Receiver] Type errors detected, auto-correcting..."),await this.runPrompt(e,g.errorPrompt,o);return}}this.enableAutoCommit&&!l.error&&!l.aborted?await ut(this.gitManager,this.connection,e,t,!1):(l.error||l.aborted)&&this.enableAutoCommit&&await ut(this.gitManager,this.connection,e,t,!0),await this.connection.reportStatus("Ready",void 0,e,this.currentSession.projectId),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,l.error?1:0)}catch(l){let d=l instanceof Error&&l.name==="AbortError",u=l instanceof Error?l.message:"Unknown error";d?console.log(`[Receiver] Agent aborted for session ${e}`):(console.error("[Receiver] Agent error:",u),await this.connection.reportStatus("Error",u,e,this.currentSession.projectId)),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,d?0:1)}finally{this.currentAbortController=null}}async setModel(e){console.log(`[Receiver] Model changed to: ${e}`),this.currentSession&&(this.currentSession.model=e==="auto"?void 0:e)}async stopAgent(){console.log("[Receiver] Stop command received"),this.currentAbortController&&this.currentAbortController.abort()}async ping(){await this.connection.getConnection().invoke("Pong",process.env.CONTAINER_ID||"unknown")}async switchSession(e){console.log(`[Receiver] SwitchSession to ${e}`),this.currentSession?this.currentSession.sessionId=e:(console.log(`[Receiver] Creating new session context for ${e}`),this.currentSession={sessionId:e,projectId:process.env.PROJECT_ID||"unknown",branchName:"main",model:void 0,debugMode:!1})}async projectPromptUpdated(e){console.log(`[Receiver] ProjectPromptUpdated received (${e.length} chars)`),We(e)}async fetchProjectPrompt(e){try{let t=this.connection.getMasterUrl(),o=this.connection.getProjectKey(),s=await fetch(`${t}/api/projects/${e}/prompt`,{method:"GET",headers:{"Content-Type":"application/json","X-Project-Key":o}});if(!s.ok){console.log(`[Receiver] Failed to fetch project prompt: ${s.status}`);return}let r=await s.json();r.prompt?(We(r.prompt),console.log(`[Receiver] Initial project prompt loaded (${r.prompt.length} chars)`)):console.log(`[Receiver] No project prompt set for project ${e}`)}catch(t){console.error("[Receiver] Error fetching project prompt:",t)}}};process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT=process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT||"60000";var Wt=Y.dirname(En(import.meta.url));function An(){let n=[Y.resolve(Wt,"../package.json"),Y.resolve(Wt,"../../../package.json")];for(let e of n)try{if(M.existsSync(e)){let t=JSON.parse(M.readFileSync(e,"utf-8"));if(t.name==="glenn-code")return t.version||"unknown"}}catch{}return"unknown"}function xn(n){let{useDefaultStack:e,projectConfig:t,configSource:o,tunnelPort:s,enableAutoTypecheck:r,enableAutoCommit:i}=n;if(console.log(`
1358
+ File paths like "src/..." are relative to the package directory shown in parentheses.`}):(console.log("[TypeCheck] \u2713 All type checks passed"),{passed:!0})}async function dt(n,e,t,o,s){if(!await n.hasChanges()){console.log("[Git] No changes to commit");return}console.log("[Git] Committing changes...");let i=await n.commitAndPush(o,s);if(i.noChanges)console.log("[Git] No changes to commit");else if(i.success){let a=await n.getCurrentBranch();console.log(`[Git] \u2713 Committed ${i.commitHash||""}`),console.log(`[Git] \u2192 Pushed to ${a}`),await e.getHubProxy().reportSessionOutput(process.env.CONTAINER_ID||"unknown",t,{type:"System",event:"committed",content:`Changes committed to ${a}`,isError:!1}).catch(l=>{console.error("[Git] Failed to report commit:",l)})}else console.error(`[Git] \u274C Git error: ${i.error}`)}var Fe=class{constructor(e,t,o,s,r=!0,i=!0,a,c,l=!0){this.connection=e;this.lifecycle=t;this.serviceManager=o;this.workspaceDir=s;this.enableAutoTypecheck=r;this.enableAutoCommit=i;this.setupPlanningTransport=a;this.projectConfig=c;this.useDefaultStack=l;this.gitManager=De(s)}currentSession=null;claudeSessionIdMap=new Map;currentAbortController=null;gitManager;async initSession(e){console.log(`[Receiver] InitSession for session ${e.sessionId}`),this.setupPlanningTransport&&e.projectId&&this.setupPlanningTransport(e.projectId),e.projectId&&await this.fetchProjectPrompt(e.projectId),this.currentSession={sessionId:e.sessionId,projectId:e.projectId,branchName:e.branchName,model:e.model==="auto"?void 0:e.model||"claude-haiku-4-5-20251001",debugMode:e.debugMode},await this.lifecycle.handleInitSession(e),e.prompt&&await this.runPrompt(e.sessionId,e.prompt,e.claudeSessionId)}async runPrompt(e,t,o,s=!1){let r=pe(t);if(console.log(`[Receiver] RunPrompt for session ${e}${o?` (ClaudeSessionId: ${o})`:""}${s?" [DEBUG MODE]":""}: ${r.substring(0,50)}...`),(!this.currentSession||this.currentSession.sessionId!==e)&&(console.log(`[Receiver] Session mismatch: expected ${this.currentSession?.sessionId}, got ${e}, switching...`),await this.switchSession(e),!this.currentSession)){console.error("[Receiver] Failed to create session context");return}let i=this.claudeSessionIdMap.get(e);if(o&&i!==o){console.warn(`[Receiver] Claude session not found in container: ${o}`);let l=this.connection.getHubProxy();await l.reportSessionOutput(process.env.CONTAINER_ID||"unknown",e,{type:"System",event:"sessionNotFound",content:o,isError:!0}),await this.connection.reportStatus("Ready","Session lost - summary needed",e,this.currentSession.projectId),await l.sessionCompleted(process.env.CONTAINER_ID||"unknown",e,0);return}await this.connection.reportStatus("Active",void 0,e,this.currentSession.projectId),this.currentSession.debugMode=s,this.currentAbortController=new AbortController;let a=i||null,c=new Set;try{let l=await Ae(r,{model:this.currentSession.model,sessionId:a||null,projectId:this.currentSession.projectId||null,apiUrl:this.connection.getMasterUrl(),debugLog:!0,debugMode:s,abortController:this.currentAbortController,projectConfig:this.projectConfig,useDefaultStack:this.useDefaultStack,callbacks:Bt(this.connection,e,{onClaudeSessionId:g=>{this.claudeSessionIdMap.set(e,g)},onFileModified:g=>{c.add(g)}})}),u=Gt(this.workspaceDir,e);if(u&&u.length>0){console.log(`[Receiver] ${u.length} pending questions found, waiting for user answers`),await this.connection.reportStatus("Ready",void 0,e,this.currentSession.projectId),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,0);return}if(this.enableAutoTypecheck&&!l.error&&!l.aborted){let g=await Kt(this.serviceManager,c,this.workspaceDir,this.projectConfig);if(!g.passed&&g.errorPrompt){console.log("[Receiver] Type errors detected, auto-correcting..."),await this.runPrompt(e,g.errorPrompt,o);return}}this.enableAutoCommit&&!l.error&&!l.aborted?await dt(this.gitManager,this.connection,e,t,!1):(l.error||l.aborted)&&this.enableAutoCommit&&await dt(this.gitManager,this.connection,e,t,!0),await this.connection.reportStatus("Ready",void 0,e,this.currentSession.projectId),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,l.error?1:0)}catch(l){let u=l instanceof Error&&l.name==="AbortError",d=l instanceof Error?l.message:"Unknown error";u?console.log(`[Receiver] Agent aborted for session ${e}`):(console.error("[Receiver] Agent error:",d),await this.connection.reportStatus("Error",d,e,this.currentSession.projectId)),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,u?0:1)}finally{this.currentAbortController=null}}async setModel(e){console.log(`[Receiver] Model changed to: ${e}`),this.currentSession&&(this.currentSession.model=e==="auto"?void 0:e)}async stopAgent(){console.log("[Receiver] Stop command received"),this.currentAbortController&&this.currentAbortController.abort()}async ping(){await this.connection.getConnection().invoke("Pong",process.env.CONTAINER_ID||"unknown")}async switchSession(e){console.log(`[Receiver] SwitchSession to ${e}`),this.currentSession?this.currentSession.sessionId=e:(console.log(`[Receiver] Creating new session context for ${e}`),this.currentSession={sessionId:e,projectId:process.env.PROJECT_ID||"unknown",branchName:"main",model:void 0,debugMode:!1})}async projectPromptUpdated(e){console.log(`[Receiver] ProjectPromptUpdated received (${e.length} chars)`),We(e)}async fetchProjectPrompt(e){try{let t=this.connection.getMasterUrl(),o=this.connection.getProjectKey(),s=await fetch(`${t}/api/projects/${e}/prompt`,{method:"GET",headers:{"Content-Type":"application/json","X-Project-Key":o}});if(!s.ok){console.log(`[Receiver] Failed to fetch project prompt: ${s.status}`);return}let r=await s.json();r.prompt?(We(r.prompt),console.log(`[Receiver] Initial project prompt loaded (${r.prompt.length} chars)`)):console.log(`[Receiver] No project prompt set for project ${e}`)}catch(t){console.error("[Receiver] Error fetching project prompt:",t)}}};process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT=process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT||"60000";var Wt=Y.dirname(En(import.meta.url));function An(){let n=[Y.resolve(Wt,"../package.json"),Y.resolve(Wt,"../../../package.json")];for(let e of n)try{if(M.existsSync(e)){let t=JSON.parse(M.readFileSync(e,"utf-8"));if(t.name==="glenn-code")return t.version||"unknown"}}catch{}return"unknown"}function xn(n){let{useDefaultStack:e,projectConfig:t,configSource:o,tunnelPort:s,enableAutoTypecheck:r,enableAutoCommit:i}=n;if(console.log(`
1359
1359
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`),console.log("\u2551 AGENT CONFIGURATION \u2551"),console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),e?(console.log("\u2551 Mode: DEFAULT STACK (.NET/React) \u2551"),console.log("\u2551 Agents: scaffolding, backend, frontend, debugger, \u2551"),console.log("\u2551 planning, project-context \u2551")):(console.log("\u2551 Mode: CUSTOM STACK (generic agents only) \u2551"),console.log("\u2551 Agents: debugger, planning, project-context \u2551")),console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),console.log(`\u2551 Config source: ${o.padEnd(42)}\u2551`),console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),console.log("\u2551 SERVICES: \u2551"),t.services?.backend){let a=t.services.backend;console.log(`\u2551 Backend: port ${a.port}, extensions: ${a.extensions?.join(", ")||"N/A"}`.padEnd(62)+"\u2551")}else console.log("\u2551 Backend: not configured \u2551");if(t.services?.frontend){let a=t.services.frontend;console.log(`\u2551 Frontend: port ${a.port}, extensions: ${a.extensions?.join(", ")||"N/A"}`.padEnd(62)+"\u2551")}else console.log("\u2551 Frontend: not configured \u2551");t.techStack&&(console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),console.log("\u2551 TECH STACK: \u2551"),t.techStack.backend?.length&&console.log(`\u2551 Backend: ${t.techStack.backend.join(", ")}`.padEnd(62)+"\u2551"),t.techStack.frontend?.length&&console.log(`\u2551 Frontend: ${t.techStack.frontend.join(", ")}`.padEnd(62)+"\u2551"),t.techStack.patterns?.length&&console.log(`\u2551 Patterns: ${t.techStack.patterns.join(", ")}`.padEnd(62)+"\u2551")),console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),console.log("\u2551 SETTINGS: \u2551"),console.log(`\u2551 Tunnel port: ${s}`.padEnd(62)+"\u2551"),console.log(`\u2551 Auto-typecheck: ${r?"enabled":"disabled"}`.padEnd(62)+"\u2551"),console.log(`\u2551 Auto-commit: ${i?"enabled":"disabled"}`.padEnd(62)+"\u2551"),console.log(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1360
- `)}var Dn=".sdd/local-signalr-config.json";function gt(n){return Y.join(n,Dn)}function jn(n){try{let e=gt(n);if(!M.existsSync(e))return null;let t=M.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return null}}function dt(n,e){let t=Y.dirname(gt(n));M.existsSync(t)||M.mkdirSync(t,{recursive:!0}),M.writeFileSync(gt(n),JSON.stringify(e,null,2))}function Le(){return`local-${Qt.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${Date.now().toString(36)}`}function On(n){return!!(n.version||n.paths||n.services||n.techStack)}function Un(n,e){return On(n)?{version:n.version||"1.0",paths:n.paths||{workspace:e},services:n.services,techStack:n.techStack,tunnel:n.tunnel,scaffold:n.scaffold,git:n.git}:null}async function Nn(n){console.log(`
1360
+ `)}var Dn=".sdd/local-signalr-config.json";function gt(n){return Y.join(n,Dn)}function jn(n){try{let e=gt(n);if(!M.existsSync(e))return null;let t=M.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return null}}function ut(n,e){let t=Y.dirname(gt(n));M.existsSync(t)||M.mkdirSync(t,{recursive:!0}),M.writeFileSync(gt(n),JSON.stringify(e,null,2))}function Le(){return`local-${Qt.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${Date.now().toString(36)}`}function On(n){return!!(n.version||n.paths||n.services||n.techStack)}function Un(n,e){return On(n)?{version:n.version||"1.0",paths:n.paths||{workspace:e},services:n.services,techStack:n.techStack,tunnel:n.tunnel,scaffold:n.scaffold,git:n.git}:null}async function Nn(n){console.log(`
1361
1361
  \u{1F527} Interactive Project Config Builder
1362
1362
  `),console.log(" Answer the following questions to configure your project."),console.log(` Press Enter to skip optional questions.
1363
1363
  `);let{projectType:e}=await q.prompt([{type:"select",name:"projectType",message:"What type of project is this?",choices:[{name:"Frontend only (React, Next.js, Vue, etc.)",value:"frontend"},{name:"Backend only (Node, Python, Go, etc.)",value:"backend"},{name:"Fullstack (Frontend + Backend)",value:"fullstack"},{name:"Skip - use default .NET/React stack",value:"skip"}]}]);if(e==="skip")return null;let t={version:"1.0",paths:{workspace:"."}};if(e==="frontend"||e==="fullstack"){console.log(`
@@ -1366,11 +1366,11 @@ File paths like "src/..." are relative to the package directory shown in parenth
1366
1366
  \u{1F4DA} Tech Stack (optional - helps AI understand your project):`);let{addTechStack:o}=await q.prompt([{type:"confirm",name:"addTechStack",message:"Add tech stack info?",default:!1}]);if(o){let s=await q.prompt([{type:"input",name:"frontend",message:"Frontend technologies (comma-separated, or Enter to skip):",default:""},{type:"input",name:"backend",message:"Backend technologies (comma-separated, or Enter to skip):",default:""},{type:"input",name:"patterns",message:"Patterns/practices (comma-separated, or Enter to skip):",default:""}]);t.techStack={},s.frontend&&(t.techStack.frontend=s.frontend.split(",").map(r=>r.trim())),s.backend&&(t.techStack.backend=s.backend.split(",").map(r=>r.trim())),s.patterns&&(t.techStack.patterns=s.patterns.split(",").map(r=>r.trim()))}return t}function Yt(n){try{let e={},t=n.match(/MASTER_URL=(\S+)/);t&&(e.masterUrl=t[1].trim());let o=n.match(/PROJECT_ID=(\S+)/);o&&(e.projectId=o[1].trim());let s=n.match(/PROJECT_KEY=(\S+)/);return s&&(e.projectKey=s[1].trim()),e.masterUrl&&e.projectId&&e.projectKey?e:null}catch{return null}}async function Mn(n){let e=jn(n);console.log(`
1367
1367
  \u{1F3E0} Local SignalR Mode - Jack into the real system
1368
1368
  `);let t=e?.masterUrl&&e?.projectId&&e?.projectKey,{configMethod:o}=await q.prompt([{type:"select",name:"configMethod",message:"How would you like to configure?",choices:[...t?[{name:`Use saved config (${e.projectId})`,value:"saved"}]:[],{name:"Paste config from frontend",value:"paste"},{name:"Enter manually",value:"manual"},{name:"Build project config interactively",value:"interactive"}]}]);if(o==="saved"&&t){let a=Un(e,n),c=a?.tunnel?.port||e.tunnelPort||5173;return console.log(`
1369
- \u{1F4CB} Using saved configuration:`),console.log(` Master URL: ${e.masterUrl}`),console.log(` Project ID: ${e.projectId}`),console.log(` Project Key: ${e.projectKey.substring(0,16)}...`),console.log(` Container ID: ${e.containerId}`),console.log(` Tunnel: ${e.enableTunnel!==!1?`enabled (port ${c})`:"disabled"}`),a&&console.log(" Project config: embedded in local-signalr-config.json"),{masterUrl:e.masterUrl,projectId:e.projectId,projectKey:e.projectKey,containerId:e.containerId||Le(),enableTunnel:e.enableTunnel!==!1,tunnelPort:c,embeddedProjectConfig:a}}if(o==="paste"){let{pastedConfig:a}=await q.prompt([{type:"input",name:"pastedConfig",message:"Paste the config (MASTER_URL=... PROJECT_ID=... PROJECT_KEY=...):",validate:g=>Yt(g)?!0:"Could not parse config. Make sure it contains MASTER_URL=..., PROJECT_ID=..., and PROJECT_KEY=..."}]),c=Yt(a),l=e?.containerId||Le(),d=e?.tunnelPort||5173,u={masterUrl:c.masterUrl,projectId:c.projectId,projectKey:c.projectKey,containerId:l,enableTunnel:!0,tunnelPort:d};return console.log(`
1370
- \u{1F4CB} Parsed configuration:`),console.log(` Master URL: ${u.masterUrl}`),console.log(` Project ID: ${u.projectId}`),console.log(` Project Key: ${u.projectKey.substring(0,16)}...`),console.log(` Container ID: ${u.containerId}`),console.log(` Tunnel: enabled (port ${d})`),dt(n,u),{...u,embeddedProjectConfig:null}}if(o==="interactive"){console.log(`
1371
- \u{1F50C} Connection Configuration:`);let a=await q.prompt([{type:"input",name:"masterUrl",message:"Master URL (backend):",default:e?.masterUrl||process.env.MASTER_URL||"http://localhost:5338",validate:g=>{if(!g.trim())return"Master URL is required";try{return new URL(g),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:e?.projectId||process.env.PROJECT_ID,validate:g=>g.trim()?!0:"Project ID is required"},{type:"password",name:"projectKey",message:"Project Key (pk_proj_...):",mask:"*",default:e?.projectKey||process.env.PROJECT_KEY,validate:g=>g.trim()?g.startsWith("pk_proj_")?!0:"Project Key must start with pk_proj_":"Project Key is required"}]),c=await Nn(n),l=e?.containerId||Le(),d=c?.tunnel?.port||e?.tunnelPort||5173,u={masterUrl:a.masterUrl,projectId:a.projectId,projectKey:a.projectKey,containerId:l,enableTunnel:!0,tunnelPort:d,...c&&{version:c.version,paths:c.paths,services:c.services,techStack:c.techStack,tunnel:c.tunnel}};return console.log(`
1372
- \u{1F4CB} Configuration built:`),console.log(` Master URL: ${u.masterUrl}`),console.log(` Project ID: ${u.projectId}`),console.log(` Project Key: ${u.projectKey.substring(0,16)}...`),console.log(` Container ID: ${u.containerId}`),console.log(` Tunnel: enabled (port ${d})`),console.log(c?" Project config: custom stack":" Project config: default .NET/React stack"),dt(n,u),{masterUrl:u.masterUrl,projectId:u.projectId,projectKey:u.projectKey,containerId:u.containerId,enableTunnel:u.enableTunnel,tunnelPort:u.tunnelPort,embeddedProjectConfig:c}}let s=await q.prompt([{type:"input",name:"masterUrl",message:"Master URL (backend):",default:e?.masterUrl||process.env.MASTER_URL||"http://localhost:5338",validate:a=>{if(!a.trim())return"Master URL is required";try{return new URL(a),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:e?.projectId||process.env.PROJECT_ID,validate:a=>a.trim()?!0:"Project ID is required"},{type:"password",name:"projectKey",message:"Project Key (pk_proj_...):",mask:"*",default:e?.projectKey||process.env.PROJECT_KEY,validate:a=>a.trim()?a.startsWith("pk_proj_")?!0:"Project Key must start with pk_proj_":"Project Key is required"},{type:"input",name:"containerId",message:"Container ID (unique identifier for this agent):",default:e?.containerId||Le()},{type:"confirm",name:"enableTunnel",message:"Enable preview URL reporting (report local dev server URL to backend)?",default:e?.enableTunnel!==!1}]),r=e?.tunnelPort||5173;s.enableTunnel&&(r=(await q.prompt([{type:"number",name:"tunnelPort",message:"Preview port (local dev server port to report):",default:r}])).tunnelPort||r);let i={...s,tunnelPort:r};return dt(n,i),{...i,embeddedProjectConfig:null}}async function Fn(){let n=process.env.LOCAL_MODE==="true",e=process.env.WORKSPACE_DIR||process.cwd(),t=An();console.log(n?`=== Glenn Code v${t} ===`:`=== SignalR Agent v${t} ===`),console.log(`Workspace: ${e}`),n&&!M.existsSync(Y.join(e,".git"))&&console.warn("\u26A0\uFE0F Warning: Not a git repository. Some features may not work.");let o,s,r=!1,i=5173,a=null;if(n){let p=await Mn(e);o={masterUrl:p.masterUrl,containerId:p.containerId,projectKey:p.projectKey,workspaceDir:e},s=p.projectId,r=p.enableTunnel,i=p.tunnelPort,a=p.embeddedProjectConfig,process.env.PROJECT_ID=p.projectId,process.env.CONTAINER_ID=p.containerId,process.env.PROJECT_KEY=p.projectKey}else{let p=process.env.PROJECT_KEY;p||(console.error("\u274C PROJECT_KEY environment variable is required"),process.exit(1)),o={masterUrl:process.env.MASTER_URL||"http://host.docker.internal:5338",containerId:process.env.CONTAINER_ID||"unknown",projectKey:p,workspaceDir:e}}console.log(`
1373
- \u{1F50C} Connecting to backend...`),console.log(` Container ID: ${o.containerId}`),console.log(` Project Key: ${o.projectKey.substring(0,16)}...`),console.log(` Gateway URL: ${o.masterUrl}`);let c=new je(o),l=0,d=10;for(;l<d;)try{await c.connect();break}catch(p){if(l++,console.error(`Connection attempt ${l} failed:`,p instanceof Error?p.message:p),l<d){let C=Math.min(1e3*Math.pow(2,l),3e4);await new Promise(de=>setTimeout(de,C))}}l>=d&&(console.error("\u274C Failed to connect to gateway after max retries"),process.exit(1)),n&&console.log("\u2705 Connected to backend");let u=await c.getSecrets();n?(console.log("\u2705 Handshake complete"),u.gitHubPat&&console.log(" GitHub PAT received from backend")):console.log("\u2705 Secrets received");let g,w=!0,v="default";if(n){if(a)g=a,v="embedded in local-signalr-config.json",w=!1,console.log("[Config] Using embedded project config from local-signalr-config.json");else{let p=await _e({workspaceDir:o.workspaceDir});g=p.config,v=p.source==="file"?p.configPath||".sdd/project-config.json":"default (no config file)",p.source==="file"&&(w=!1)}w||g.tunnel?.port&&(i=g.tunnel.port)}let b=xe({workspaceDir:o.workspaceDir,projectConfig:g});he(b);let D=ce(o.workspaceDir);ie(D);let j=p=>{console.log(`[Planning] Setting up SignalR transport for project ${p}`),D=new Oe(c.getHubProxy(),p),ie(D)};n&&s&&j(s);let f;n?f={prewarmWorkspace:async()=>{console.log("[LocalMode] Skipping prewarm - using existing workspace")},handleInitSession:async p=>{console.log(`[LocalMode] Session init for ${p.sessionId}`),console.log("[LocalMode] Skipping branch switch - using current branch"),await c.reportStatus("Ready",void 0,p.sessionId,p.projectId),await c.getHubProxy().initSessionCompleted(o.containerId,p.sessionId,!0,"")}}:f=new Ne(c,b,o.workspaceDir,u.gitHubPat||null);let O=process.env.DISABLE_AUTO_TYPECHECK!=="true",G=process.env.DISABLE_AUTO_COMMIT!=="true"&&!n;n&&g?xn({useDefaultStack:w,projectConfig:g,configSource:v,tunnelPort:i,enableAutoTypecheck:O,enableAutoCommit:G}):(console.log(`[Config] Auto-typecheck: ${O?"enabled":"disabled"}`),console.log(`[Config] Auto-commit: ${G?"enabled":"disabled"}`));let F=new Fe(c,f,b,o.workspaceDir,O,G,j,g,w);if(c.registerReceiver(F),n){if(r){let p=`http://localhost:${i}`;c.setTunnelUrl(p),console.log(`
1369
+ \u{1F4CB} Using saved configuration:`),console.log(` Master URL: ${e.masterUrl}`),console.log(` Project ID: ${e.projectId}`),console.log(` Project Key: ${e.projectKey.substring(0,16)}...`),console.log(` Container ID: ${e.containerId}`),console.log(` Tunnel: ${e.enableTunnel!==!1?`enabled (port ${c})`:"disabled"}`),a&&console.log(" Project config: embedded in local-signalr-config.json"),{masterUrl:e.masterUrl,projectId:e.projectId,projectKey:e.projectKey,containerId:e.containerId||Le(),enableTunnel:e.enableTunnel!==!1,tunnelPort:c,embeddedProjectConfig:a}}if(o==="paste"){let{pastedConfig:a}=await q.prompt([{type:"input",name:"pastedConfig",message:"Paste the config (MASTER_URL=... PROJECT_ID=... PROJECT_KEY=...):",validate:g=>Yt(g)?!0:"Could not parse config. Make sure it contains MASTER_URL=..., PROJECT_ID=..., and PROJECT_KEY=..."}]),c=Yt(a),l=e?.containerId||Le(),u=e?.tunnelPort||5173,d={masterUrl:c.masterUrl,projectId:c.projectId,projectKey:c.projectKey,containerId:l,enableTunnel:!0,tunnelPort:u};return console.log(`
1370
+ \u{1F4CB} Parsed configuration:`),console.log(` Master URL: ${d.masterUrl}`),console.log(` Project ID: ${d.projectId}`),console.log(` Project Key: ${d.projectKey.substring(0,16)}...`),console.log(` Container ID: ${d.containerId}`),console.log(` Tunnel: enabled (port ${u})`),ut(n,d),{...d,embeddedProjectConfig:null}}if(o==="interactive"){console.log(`
1371
+ \u{1F50C} Connection Configuration:`);let a=await q.prompt([{type:"input",name:"masterUrl",message:"Master URL (backend):",default:e?.masterUrl||process.env.MASTER_URL||"http://localhost:5338",validate:g=>{if(!g.trim())return"Master URL is required";try{return new URL(g),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:e?.projectId||process.env.PROJECT_ID,validate:g=>g.trim()?!0:"Project ID is required"},{type:"password",name:"projectKey",message:"Project Key (pk_proj_...):",mask:"*",default:e?.projectKey||process.env.PROJECT_KEY,validate:g=>g.trim()?g.startsWith("pk_proj_")?!0:"Project Key must start with pk_proj_":"Project Key is required"}]),c=await Nn(n),l=e?.containerId||Le(),u=c?.tunnel?.port||e?.tunnelPort||5173,d={masterUrl:a.masterUrl,projectId:a.projectId,projectKey:a.projectKey,containerId:l,enableTunnel:!0,tunnelPort:u,...c&&{version:c.version,paths:c.paths,services:c.services,techStack:c.techStack,tunnel:c.tunnel}};return console.log(`
1372
+ \u{1F4CB} Configuration built:`),console.log(` Master URL: ${d.masterUrl}`),console.log(` Project ID: ${d.projectId}`),console.log(` Project Key: ${d.projectKey.substring(0,16)}...`),console.log(` Container ID: ${d.containerId}`),console.log(` Tunnel: enabled (port ${u})`),console.log(c?" Project config: custom stack":" Project config: default .NET/React stack"),ut(n,d),{masterUrl:d.masterUrl,projectId:d.projectId,projectKey:d.projectKey,containerId:d.containerId,enableTunnel:d.enableTunnel,tunnelPort:d.tunnelPort,embeddedProjectConfig:c}}let s=await q.prompt([{type:"input",name:"masterUrl",message:"Master URL (backend):",default:e?.masterUrl||process.env.MASTER_URL||"http://localhost:5338",validate:a=>{if(!a.trim())return"Master URL is required";try{return new URL(a),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:e?.projectId||process.env.PROJECT_ID,validate:a=>a.trim()?!0:"Project ID is required"},{type:"password",name:"projectKey",message:"Project Key (pk_proj_...):",mask:"*",default:e?.projectKey||process.env.PROJECT_KEY,validate:a=>a.trim()?a.startsWith("pk_proj_")?!0:"Project Key must start with pk_proj_":"Project Key is required"},{type:"input",name:"containerId",message:"Container ID (unique identifier for this agent):",default:e?.containerId||Le()},{type:"confirm",name:"enableTunnel",message:"Enable preview URL reporting (report local dev server URL to backend)?",default:e?.enableTunnel!==!1}]),r=e?.tunnelPort||5173;s.enableTunnel&&(r=(await q.prompt([{type:"number",name:"tunnelPort",message:"Preview port (local dev server port to report):",default:r}])).tunnelPort||r);let i={...s,tunnelPort:r};return ut(n,i),{...i,embeddedProjectConfig:null}}async function Fn(){let n=process.env.LOCAL_MODE==="true",e=process.env.WORKSPACE_DIR||process.cwd(),t=An();console.log(n?`=== Glenn Code v${t} ===`:`=== SignalR Agent v${t} ===`),console.log(`Workspace: ${e}`),n&&!M.existsSync(Y.join(e,".git"))&&console.warn("\u26A0\uFE0F Warning: Not a git repository. Some features may not work.");let o,s,r=!1,i=5173,a=null;if(n){let p=await Mn(e);o={masterUrl:p.masterUrl,containerId:p.containerId,projectKey:p.projectKey,workspaceDir:e,isByok:!0},s=p.projectId,r=p.enableTunnel,i=p.tunnelPort,a=p.embeddedProjectConfig,process.env.PROJECT_ID=p.projectId,process.env.CONTAINER_ID=p.containerId,process.env.PROJECT_KEY=p.projectKey}else{let p=process.env.PROJECT_KEY;p||(console.error("\u274C PROJECT_KEY environment variable is required"),process.exit(1)),o={masterUrl:process.env.MASTER_URL||"http://host.docker.internal:5338",containerId:process.env.CONTAINER_ID||"unknown",projectKey:p,workspaceDir:e}}console.log(`
1373
+ \u{1F50C} Connecting to backend...`),console.log(` Container ID: ${o.containerId}`),console.log(` Project Key: ${o.projectKey.substring(0,16)}...`),console.log(` Gateway URL: ${o.masterUrl}`);let c=new je(o),l=0,u=10;for(;l<u;)try{await c.connect();break}catch(p){if(l++,console.error(`Connection attempt ${l} failed:`,p instanceof Error?p.message:p),l<u){let C=Math.min(1e3*Math.pow(2,l),3e4);await new Promise(ue=>setTimeout(ue,C))}}l>=u&&(console.error("\u274C Failed to connect to gateway after max retries"),process.exit(1)),n&&console.log("\u2705 Connected to backend");let d=await c.getSecrets();n?(console.log("\u2705 Handshake complete"),d.gitHubPat&&console.log(" GitHub PAT received from backend")):console.log("\u2705 Secrets received");let g,w=!0,v="default";if(n){if(a)g=a,v="embedded in local-signalr-config.json",w=!1,console.log("[Config] Using embedded project config from local-signalr-config.json");else{let p=await _e({workspaceDir:o.workspaceDir});g=p.config,v=p.source==="file"?p.configPath||".sdd/project-config.json":"default (no config file)",p.source==="file"&&(w=!1)}w||g.tunnel?.port&&(i=g.tunnel.port)}let A=xe({workspaceDir:o.workspaceDir,projectConfig:g});he(A);let D=ce(o.workspaceDir);ie(D);let b=p=>{console.log(`[Planning] Setting up SignalR transport for project ${p}`),D=new Oe(c.getHubProxy(),p),ie(D)};n&&s&&b(s);let f;n?f={prewarmWorkspace:async()=>{console.log("[LocalMode] Skipping prewarm - using existing workspace")},handleInitSession:async p=>{console.log(`[LocalMode] Session init for ${p.sessionId}`),console.log("[LocalMode] Skipping branch switch - using current branch"),await c.reportStatus("Ready",void 0,p.sessionId,p.projectId),await c.getHubProxy().initSessionCompleted(o.containerId,p.sessionId,!0,"")}}:f=new Ne(c,A,o.workspaceDir,d.gitHubPat||null);let O=process.env.DISABLE_AUTO_TYPECHECK!=="true",G=process.env.DISABLE_AUTO_COMMIT!=="true"&&!n;n&&g?xn({useDefaultStack:w,projectConfig:g,configSource:v,tunnelPort:i,enableAutoTypecheck:O,enableAutoCommit:G}):(console.log(`[Config] Auto-typecheck: ${O?"enabled":"disabled"}`),console.log(`[Config] Auto-commit: ${G?"enabled":"disabled"}`));let F=new Fe(c,f,A,o.workspaceDir,O,G,b,g,w);if(c.registerReceiver(F),n){if(r){let p=`http://localhost:${i}`;c.setTunnelUrl(p),console.log(`
1374
1374
  \u{1F310} Preview available at: ${p}`)}await c.reportStatus("Ready",void 0,void 0,s),console.log(`
1375
1375
  \u2705 Agent ready and waiting for commands`),console.log(` Open the frontend and select this agent to start working
1376
1376
  `),console.log(` Press Ctrl+C to disconnect
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glenn-code",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "description": "Glenn Code - Connect your local development environment to DNM Lab",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",