glenn-code 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Glenn Code - Bundled and minified
2
2
  // (c) DNM Lab - All rights reserved
3
3
 
4
- import B from"inquirer";import*as K from"fs";import*as pe from"path";import*as Yt from"os";import{createSdkMcpServer as Xt}from"@anthropic-ai/claude-agent-sdk";import{tool as ge}from"@anthropic-ai/claude-agent-sdk";import{z as de}from"zod";import*as fe from"fs";import*as kt from"path";var q=null;function se(n){q=n}function $e(n){return n.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}var Ln=ge("save_specification",`Save a specification to the database.
4
+ import K from"inquirer";import*as N from"fs";import*as Y from"path";import*as Qt from"os";import{fileURLToPath as En}from"url";import{createSdkMcpServer as Zt}from"@anthropic-ai/claude-agent-sdk";import{tool as fe}from"@anthropic-ai/claude-agent-sdk";import{z as ge}from"zod";import*as me from"fs";import*as wt from"path";var G=null;function ie(n){G=n}function He(n){return n.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}var Bn=fe("save_specification",`Save a specification to the database.
5
5
 
6
6
  IMPORTANT: First write the specification content to a file using the Write tool, then call this with the file path.
7
7
 
@@ -12,50 +12,50 @@ Workflow:
12
12
 
13
13
  Example:
14
14
  1. Write tool \u2192 save to /tmp/user-auth-spec.md
15
- 2. save_specification(name: "User Authentication", filePath: "/tmp/user-auth-spec.md")`,{name:de.string().describe("Specification name (e.g., 'User Authentication')"),filePath:de.string().describe("Path to the file containing the specification content (written via Write tool)")},async n=>{if(!q)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let e=kt.resolve(n.filePath);if(!fe.existsSync(e))return{content:[{type:"text",text:`\u274C File not found: ${n.filePath}. Use Write tool first to create the file.`}]};let t=fe.readFileSync(e,"utf-8"),s=$e(n.name);return await q.saveSpecification(s,t),{content:[{type:"text",text:`\u2705 Saved specification: ${s}.spec.md`}]}}),$n=ge("read_specification","Read the content of a specification.",{name:de.string().describe("Name of the specification to read")},async n=>{if(!q)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let e=$e(n.name),t=await q.getSpecification(e);return t?{content:[{type:"text",text:t}]}:{content:[{type:"text",text:`\u274C Specification "${n.name}" not found.`}]}}),Hn=ge("list_specifications","List all specifications.",{},async()=>{if(!q)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let n=await q.listSpecifications();return n.length===0?{content:[{type:"text",text:"\u{1F4CB} No specifications found."}]}:{content:[{type:"text",text:`\u{1F4CB} Specifications:
15
+ 2. save_specification(name: "User Authentication", filePath: "/tmp/user-auth-spec.md")`,{name:ge.string().describe("Specification name (e.g., 'User Authentication')"),filePath:ge.string().describe("Path to the file containing the specification content (written via Write tool)")},async n=>{if(!G)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let e=wt.resolve(n.filePath);if(!me.existsSync(e))return{content:[{type:"text",text:`\u274C File not found: ${n.filePath}. Use Write tool first to create the file.`}]};let t=me.readFileSync(e,"utf-8"),o=He(n.name);return await G.saveSpecification(o,t),{content:[{type:"text",text:`\u2705 Saved specification: ${o}.spec.md`}]}}),Kn=fe("read_specification","Read the content of a specification.",{name:ge.string().describe("Name of the specification to read")},async n=>{if(!G)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let e=He(n.name),t=await G.getSpecification(e);return t?{content:[{type:"text",text:t}]}:{content:[{type:"text",text:`\u274C Specification "${n.name}" not found.`}]}}),qn=fe("list_specifications","List all specifications.",{},async()=>{if(!G)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let n=await G.listSpecifications();return n.length===0?{content:[{type:"text",text:"\u{1F4CB} No specifications found."}]}:{content:[{type:"text",text:`\u{1F4CB} Specifications:
16
16
 
17
17
  ${n.map(t=>`- ${t.name} (${t.slug}.spec.md)`).join(`
18
- `)}`}]}}),Bn=ge("delete_specification","Delete a specification.",{name:de.string().describe("Name of the specification to delete")},async n=>{if(!q)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let e=$e(n.name);return await q.deleteSpecification(e)?{content:[{type:"text",text:`\u{1F5D1}\uFE0F Deleted: ${e}.spec.md`}]}:{content:[{type:"text",text:`\u274C Specification "${n.name}" not found.`}]}});import{tool as Y}from"@anthropic-ai/claude-agent-sdk";import{z as ee}from"zod";var T=null;function me(n){T=n}var vt=Y("restart_services",`Rebuild and restart services. Call this after making backend changes (.cs files) to apply them.
18
+ `)}`}]}}),Gn=fe("delete_specification","Delete a specification.",{name:ge.string().describe("Name of the specification to delete")},async n=>{if(!G)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let e=He(n.name);return await G.deleteSpecification(e)?{content:[{type:"text",text:`\u{1F5D1}\uFE0F Deleted: ${e}.spec.md`}]}:{content:[{type:"text",text:`\u274C Specification "${n.name}" not found.`}]}});import{tool as z}from"@anthropic-ai/claude-agent-sdk";import{z as ne}from"zod";var T=null;function he(n){T=n}var kt=z("restart_services",`Rebuild and restart services. Call this after making backend changes (.cs files) to apply them.
19
19
 
20
20
  Parameters:
21
21
  - target: "backend" (default) | "frontend" | "both"
22
22
 
23
23
  Use target="backend" after .cs file changes (faster, only restarts .NET).
24
24
  Use target="frontend" if Vite is stuck (rare, HMR usually works).
25
- Use target="both" if unsure or both need restart.`,{target:ee.enum(["backend","frontend","both"]).default("backend").describe("Which service to restart")},async n=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=n.target||"backend";console.log(`[MCP] restart_services called (target: ${e})`);let t=await T.restartServices(e);if(!t.success){let o=[];return t.backendError&&o.push(`Backend: ${t.backendError}`),t.frontendError&&o.push(`Frontend: ${t.frontendError}`),{content:[{type:"text",text:`\u274C Restart failed:
26
- ${o.join(`
25
+ Use target="both" if unsure or both need restart.`,{target:ne.enum(["backend","frontend","both"]).default("backend").describe("Which service to restart")},async n=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=n.target||"backend";console.log(`[MCP] restart_services called (target: ${e})`);let t=await T.restartServices(e);if(!t.success){let s=[];return t.backendError&&s.push(`Backend: ${t.backendError}`),t.frontendError&&s.push(`Frontend: ${t.frontendError}`),{content:[{type:"text",text:`\u274C Restart failed:
26
+ ${s.join(`
27
27
 
28
- `)}`}]}}return{content:[{type:"text",text:`\u2705 ${e==="both"?"Backend and frontend":e==="backend"?"Backend":"Frontend"} restarted successfully.`}]}}),Ct=Y("stop_services",`Stop running services. Call this BEFORE running scaffold to prevent port conflicts and ensure clean migrations.
28
+ `)}`}]}}return{content:[{type:"text",text:`\u2705 ${e==="both"?"Backend and frontend":e==="backend"?"Backend":"Frontend"} restarted successfully.`}]}}),vt=z("stop_services",`Stop running services. Call this BEFORE running scaffold to prevent port conflicts and ensure clean migrations.
29
29
 
30
30
  Parameters:
31
31
  - target: "backend" (default) | "frontend" | "both"
32
32
 
33
- Use target="backend" before running scaffold (required for migrations).`,{target:ee.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async n=>{if(!T)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",s=e==="frontend"||e==="both";t&&await T.stopBackend(),s&&await T.stopFrontend();let o=5e3,r=Date.now(),i=!t,a=!s;for(;Date.now()-r<o;){let p=await T.checkHealth();if(t&&!p.api&&(i=!0),s&&!p.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.`}]}}),wt=Y("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(!T)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 T.stopBackend(),o&&await T.stopFrontend();let s=5e3,r=Date.now(),i=!t,a=!o;for(;Date.now()-r<s;){let d=await T.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.
34
34
 
35
35
  Parameters:
36
36
  - target: "backend" (default) | "frontend" | "both"
37
37
 
38
- Use target="backend" after running scaffold.`,{target:ee.enum(["backend","frontend","both"]).default("backend").describe("Which service to start")},async n=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=n.target||"backend";console.log(`[MCP] start_services called (target: ${e})`);let t=e==="backend"||e==="both",s=e==="frontend"||e==="both";t&&await T.startBackend(),s&&await T.startFrontend();let o=await T.waitForHealth(15e3),r=!t||o.api,i=!s||o.web,a=r&&i,c=e==="both"?"Backend and frontend":e==="backend"?"Backend":"Frontend";if(!a){let l=[];return t&&!o.api&&l.push("Backend failed to start"),s&&!o.web&&l.push("Frontend failed to start"),{content:[{type:"text",text:`\u274C ${c} failed to start: ${l.join(", ")}`}]}}return{content:[{type:"text",text:`\u2705 ${c} started successfully.`}]}}),Pt=Y("check_service_health",`Check if services are running and healthy. Returns current status of backend API and frontend.
38
+ Use target="backend" after running scaffold.`,{target:ne.enum(["backend","frontend","both"]).default("backend").describe("Which service to start")},async n=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=n.target||"backend";console.log(`[MCP] start_services called (target: ${e})`);let t=e==="backend"||e==="both",o=e==="frontend"||e==="both";t&&await T.startBackend(),o&&await T.startFrontend();let s=await T.waitForHealth(15e3),r=!t||s.api,i=!o||s.web,a=r&&i,c=e==="both"?"Backend and frontend":e==="backend"?"Backend":"Frontend";if(!a){let l=[];return t&&!s.api&&l.push("Backend failed to start"),o&&!s.web&&l.push("Frontend failed to start"),{content:[{type:"text",text:`\u274C ${c} failed to start: ${l.join(", ")}`}]}}return{content:[{type:"text",text:`\u2705 ${c} started successfully.`}]}}),_t=z("check_service_health",`Check if services are running and healthy. Returns current status of backend API and frontend.
39
39
  Note: This only checks if ports are responding, NOT build/type errors. Use check_backend_build or check_frontend_types for that.`,{},async()=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let n=await T.checkHealth();return{content:[{type:"text",text:`Service Health:
40
40
  \u{1F5A5}\uFE0F Backend API: ${n.api?"\u2705 running":"\u274C not running"}
41
- \u{1F310} Frontend: ${n.web?"\u2705 running":"\u274C not running"}`}]}}),_t=Y("check_backend_build","Run dotnet build to check for C# compilation errors. Use this after fixing backend code to verify the fix works.",{},async()=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};console.log("[MCP] check_backend_build called");let n=await T.checkBackendBuild();return n.success?{content:[{type:"text",text:"\u2705 Backend build succeeded - no errors"}]}:{content:[{type:"text",text:`\u274C Backend build failed:
41
+ \u{1F310} Frontend: ${n.web?"\u2705 running":"\u274C not running"}`}]}}),Pt=z("check_backend_build","Run dotnet build to check for C# compilation errors. Use this after fixing backend code to verify the fix works.",{},async()=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};console.log("[MCP] check_backend_build called");let n=await T.checkBackendBuild();return n.success?{content:[{type:"text",text:"\u2705 Backend build succeeded - no errors"}]}:{content:[{type:"text",text:`\u274C Backend build failed:
42
42
 
43
- ${n.errors}`}]}}),Tt=Y("check_frontend_types","Run TypeScript type check (tsc) on frontend code. Use this after fixing frontend code to verify the fix works.",{},async()=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};console.log("[MCP] check_frontend_types called");let n=await T.checkFrontendTypes();return n.success?{content:[{type:"text",text:"\u2705 Frontend type check passed - no errors"}]}:{content:[{type:"text",text:`\u274C Frontend type errors:
43
+ ${n.errors}`}]}}),Tt=z("check_frontend_types","Run TypeScript type check (tsc) on frontend code. Use this after fixing frontend code to verify the fix works.",{},async()=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};console.log("[MCP] check_frontend_types called");let n=await T.checkFrontendTypes();return n.success?{content:[{type:"text",text:"\u2705 Frontend type check passed - no errors"}]}:{content:[{type:"text",text:`\u274C Frontend type errors:
44
44
 
45
- ${n.errors}`}]}}),Wn=Y("tail_service_log",`Get the last N lines of service logs. Use this to debug startup failures or verify that the application is running correctly.
45
+ ${n.errors}`}]}}),zn=z("tail_service_log",`Get the last N lines of service logs. Use this to debug startup failures or verify that the application is running correctly.
46
46
 
47
47
  Parameters:
48
48
  - target: "backend" | "frontend"
49
- - lines: number (default 50)`,{target:ee.enum(["backend","frontend"]).describe("Which log to read"),lines:ee.number().default(50).describe("Number of lines to read")},async n=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=await T.tailLogs(n.target,n.lines);return{content:[{type:"text",text:`Last ${n.lines} lines of ${n.target} log:
49
+ - lines: number (default 50)`,{target:ne.enum(["backend","frontend"]).describe("Which log to read"),lines:ne.number().default(50).describe("Number of lines to read")},async n=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=await T.tailLogs(n.target,n.lines);return{content:[{type:"text",text:`Last ${n.lines} lines of ${n.target} log:
50
50
 
51
51
  ${e.join(`
52
- `)}`}]}}),Yn=Y("check_swagger_endpoints",`Search for endpoints in the generated swagger.json. Use this to verify that new backend endpoints are correctly exposed.
52
+ `)}`}]}}),Jn=z("check_swagger_endpoints",`Search for endpoints in the generated swagger.json. Use this to verify that new backend endpoints are correctly exposed.
53
53
 
54
54
  Parameters:
55
- - pattern: string (e.g., "users", "api/reports")`,{pattern:ee.string().describe("Text to search for in endpoint paths")},async n=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=await T.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:
55
+ - pattern: string (e.g., "users", "api/reports")`,{pattern:ne.string().describe("Text to search for in endpoint paths")},async n=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=await T.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 re from"fs";import*as Vt from"path";import*as x from"fs";import*as he from"path";function ie(n){let e=he.join(n,".sdd","specifications");function t(){x.existsSync(e)||x.mkdirSync(e,{recursive:!0})}function s(o){return he.join(e,`${o}.spec.md`)}return{async saveSpecification(o,r){t(),x.writeFileSync(s(o),r,"utf-8")},async getSpecification(o){t();let r=s(o);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(he.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(o){t();let r=s(o);return x.existsSync(r)?(x.unlinkSync(r),!0):!1},async specificationExists(o){return t(),x.existsSync(s(o))}}}function He(){return Xt({name:"agent-insights",version:"1.0.0",tools:[vt,Ct,wt,Pt,_t,Tt]})}import{query as yn}from"@anthropic-ai/claude-agent-sdk";var Be={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 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,Tt]})}import{query as bn}from"@anthropic-ai/claude-agent-sdk";var Ke={description:"Delegate to this agent when scaffolding new entities or creating CRUD features. This agent handles entity generation using the scaffold CLI with multi-entity support.",model:"inherit",prompt:`You are a scaffolding specialist. Your ONLY job is:
59
59
  1. Check existing features (quick ls)
60
60
  2. Write ALL entities to ONE /tmp/scaffold.json (big bang, no phasing!)
61
61
  3. Run scaffold CLI ONCE
@@ -239,7 +239,7 @@ DO NOT phase/split into multiple runs - that's slower and error-prone.
239
239
  - Enrollment depends on Student + CourseOffering - CLI handles it
240
240
  - ONE scaffold run creates ALL 6 entities with correct FK relationships
241
241
 
242
- REMEMBER: ls \u2192 Write \u2192 Run \u2192 Restart \u2192 STOP. No verification needed.`};var ye={description:"Delegate to this agent to fix errors, type issues, or bugs in the code",model:"inherit",prompt:`You are a bug-fixing specialist. Your job is to FIX errors, not just report them.
242
+ REMEMBER: ls \u2192 Write \u2192 Run \u2192 Restart \u2192 STOP. No verification needed.`};var be={description:"Delegate to this agent to fix errors, type issues, or bugs in the code",model:"inherit",prompt:`You are a bug-fixing specialist. Your job is to FIX errors, not just report them.
243
243
 
244
244
  ## Process
245
245
  1. Read the error message carefully - understand WHAT and WHERE
@@ -269,7 +269,7 @@ REMEMBER: ls \u2192 Write \u2192 Run \u2192 Restart \u2192 STOP. No verification
269
269
  - Fix the ROOT CAUSE, not symptoms
270
270
  - Make minimal changes
271
271
  - Don't add unnecessary code
272
- - If multiple files have the same issue, fix all of them`};var be={description:"Delegate to this agent for specification-driven development. Use when the user wants to plan, specify, or design a feature before implementation.",model:"inherit",prompt:`You are a Spec-Driven Development (SDD) specialist.
272
+ - If multiple files have the same issue, fix all of them`};var Se={description:"Delegate to this agent for specification-driven development. Use when the user wants to plan, specify, or design a feature before implementation.",model:"inherit",prompt:`You are a Spec-Driven Development (SDD) specialist.
273
273
 
274
274
  ## \u26D4 CRITICAL RULES - READ FIRST
275
275
 
@@ -428,7 +428,7 @@ Save your spec. The spec will appear in the user's UI.
428
428
  - Ask ONE question at a time (not all at once!)
429
429
  - STOP asking if user says "start implementing"
430
430
  - NEVER output the spec as text - use save_specification
431
- - Maximum 7 questions - don't exhaust the user!`};var Ke={description:"Delegate to this agent for writing new backend features (non-scaffold). Custom endpoints, queries, commands, business logic.",model:"inherit",prompt:`You are a backend specialist for .NET 9 / ASP.NET Core.
431
+ - Maximum 7 questions - don't exhaust the user!`};var qe={description:"Delegate to this agent for writing new backend features (non-scaffold). Custom endpoints, queries, commands, business logic.",model:"inherit",prompt:`You are a backend specialist for .NET 9 / ASP.NET Core.
432
432
 
433
433
  ## YOUR ROLE
434
434
  Write custom backend code that goes BEYOND what scaffold generates:
@@ -500,7 +500,7 @@ public class Get{EntityB}LookupHandler : IQueryHandler<Get{EntityB}LookupQuery,
500
500
  2. If build succeeds, generate swagger: \`mcp__agent-insights__restart_services\` (this regenerates swagger)
501
501
  3. Report what you created and STOP
502
502
 
503
- **\u26A0\uFE0F CRITICAL:** Always generate swagger before frontend work begins!`};var qe={description:"Delegate to this agent for writing frontend features. Components, hooks, pages using generated API hooks.",model:"inherit",prompt:`You are a frontend specialist for React 19 + TypeScript + MUI.
503
+ **\u26A0\uFE0F CRITICAL:** Always generate swagger before frontend work begins!`};var Ge={description:"Delegate to this agent for writing frontend features. Components, hooks, pages using generated API hooks.",model:"inherit",prompt:`You are a frontend specialist for React 19 + TypeScript + MUI.
504
504
 
505
505
  ## YOUR ROLE
506
506
  Write frontend code using GENERATED API hooks (never manual fetch):
@@ -575,7 +575,7 @@ queryClient.invalidateQueries({ queryKey: getGetApi{Entities}QueryKey() })
575
575
  ## WHEN DONE
576
576
  1. Run typecheck: \`cd /workspace/packages/backoffice-web && npx tsc --noEmit\`
577
577
  2. If errors, fix them
578
- 3. Report what you created and STOP`};var Se={description:"Delegate to this agent to help the user write or improve their project context. This agent researches the codebase, asks strategic questions about product goals and users, then writes comprehensive project context.",model:"inherit",prompt:`You are a Project Context Specialist - an expert at understanding codebases and extracting the essential context that helps AI agents work effectively on projects.
578
+ 3. Report what you created and STOP`};var we={description:"Delegate to this agent to help the user write or improve their project context. This agent researches the codebase, asks strategic questions about product goals and users, then writes comprehensive project context.",model:"inherit",prompt:`You are a Project Context Specialist - an expert at understanding codebases and extracting the essential context that helps AI agents work effectively on projects.
579
579
 
580
580
  ## \u26D4 CRITICAL RULES - READ FIRST
581
581
 
@@ -769,13 +769,13 @@ Before saving, verify your context:
769
769
  3. **Show patterns** - Include small code examples for common tasks
770
770
  4. **Prioritize** - Put most critical info in <critical_rules>
771
771
  5. **Keep it scannable** - Use bullets, clear sections
772
- 6. **Update, don't replace** - If context exists, improve it rather than starting fresh`};import*as te from"fs";import*as Et from"path";var ke=class{buffer=[];flushInterval=null;sessionId=null;outputDir;flushIntervalMs;constructor(e){this.outputDir=e.outputDir||"/tmp/agent-debug",this.flushIntervalMs=e.flushIntervalMs||3e3,e.enabled&&(this.ensureOutputDir(),this.startFlushInterval())}ensureOutputDir(){te.existsSync(this.outputDir)||te.mkdirSync(this.outputDir,{recursive:!0})}startFlushInterval(){this.flushInterval=setInterval(()=>{this.flush()},this.flushIntervalMs)}setSessionId(e){this.sessionId=e}log(e){let s={timestamp:new Date().toISOString(),message:e};this.buffer.push(JSON.stringify(s,null,2)+`
772
+ 6. **Update, don't replace** - If context exists, improve it rather than starting fresh`};import*as oe from"fs";import*as Et from"path";var ke=class{buffer=[];flushInterval=null;sessionId=null;outputDir;flushIntervalMs;constructor(e){this.outputDir=e.outputDir||"/tmp/agent-debug",this.flushIntervalMs=e.flushIntervalMs||3e3,e.enabled&&(this.ensureOutputDir(),this.startFlushInterval())}ensureOutputDir(){oe.existsSync(this.outputDir)||oe.mkdirSync(this.outputDir,{recursive:!0})}startFlushInterval(){this.flushInterval=setInterval(()=>{this.flush()},this.flushIntervalMs)}setSessionId(e){this.sessionId=e}log(e){let o={timestamp:new Date().toISOString(),message:e};this.buffer.push(JSON.stringify(o,null,2)+`
773
773
  ---
774
- `)}getLogFilePath(){let t=(this.sessionId||"unknown").replace(/[^a-zA-Z0-9-]/g,"_").slice(0,50),s=new Date().toISOString().split("T")[0];return Et.join(this.outputDir,`session-${s}-${t}.log`)}flush(){if(this.buffer.length===0)return;let e=this.buffer.join("");this.buffer=[];let t=this.getLogFilePath();te.appendFileSync(t,e)}stop(){this.flushInterval&&(clearInterval(this.flushInterval),this.flushInterval=null),this.flush()}};function At(n){return n?typeof n=="boolean"?n?new ke({enabled:!0}):null:n.enabled?new ke(n):null:null}import{z as y}from"zod";import*as Ce from"fs/promises";import*as Q from"path";var xt=y.object({port:y.number(),startCommand:y.string(),buildCommand:y.string().optional(),typecheckCommand:y.string().optional(),logFile:y.string().optional(),healthEndpoint:y.string().optional(),extensions:y.array(y.string())}),Zt=y.object({port:y.number(),type:y.enum(["postgres","mysql","sqlite","mongodb"])}),en=y.object({command:y.string(),schemaPath:y.string().optional()}),tn=y.object({email:y.string().optional(),name:y.string().optional(),commitPrefix:y.string().optional()}),nn=y.object({enabled:y.boolean(),port:y.number().optional()}),ve=y.object({version:y.literal("1.0"),paths:y.object({workspace:y.string(),backend:y.string().optional(),frontend:y.string().optional(),features:y.object({backend:y.string().optional(),frontend:y.string().optional()}).optional()}),services:y.object({backend:xt.optional(),frontend:xt.optional(),database:Zt.optional()}).optional(),scaffold:en.optional(),git:tn.optional(),techStack:y.object({backend:y.array(y.string()).optional(),frontend:y.array(y.string()).optional(),patterns:y.array(y.string()).optional()}).optional(),tunnel:nn.optional()});function G(n){return{version:"1.0",paths:{workspace:n||process.env.WORKSPACE_DIR||"/workspace",backend:"packages/dotnet-api",frontend:"packages/backoffice-web",features:{backend:"Source/Features",frontend:"src/applications/super-admin/features"}},services:{backend:{port:5338,startCommand:"dotnet run",buildCommand:"dotnet build",typecheckCommand:"dotnet build --no-restore",logFile:"/tmp/api.log",healthEndpoint:"http://localhost:5338",extensions:[".cs",".csproj"]},frontend:{port:5173,startCommand:"npm run dev",typecheckCommand:"npx tsc -p tsconfig.app.json --noEmit",logFile:"/tmp/web.log",healthEndpoint:"http://localhost:5173",extensions:[".ts",".tsx",".json"]},database:{port:43594,type:"postgres"}},scaffold:{command:"scaffold --schema /tmp/scaffold.json --output /workspace --force --full",schemaPath:"/tmp/scaffold.json"},git:{email:"agent@dotnetmentor.se",name:"Agent",commitPrefix:"[agent]"},techStack:{backend:[".NET 9","PostgreSQL","MediatR (CQRS)","EF Core"],frontend:["React 19","TypeScript","Vite","MUI","TanStack Query"],patterns:["Vertical slices","CQRS","Soft delete","Timestamps"]},tunnel:{enabled:!0,port:5173}}}var on="project-config.json",sn=".sdd";async function we(n){let{workspaceDir:e,providedConfig:t,requireConfig:s}=n;if(t)return{config:ve.parse(t),source:"provided"};let o=Q.join(e,sn,on);try{let r=await Ce.readFile(o,"utf-8"),i=JSON.parse(r);return{config:ve.parse(i),source:"file",configPath:o}}catch(r){r instanceof Error&&"code"in r&&r.code!=="ENOENT"&&console.warn(`[Config] Warning: Invalid project-config.json at ${o}:`,r instanceof y.ZodError?r.errors:r.message)}if(s)throw new Error(`No project configuration found at ${o} and requireConfig is true`);return{config:G(e),source:"default"}}function Pe(n,e){if(e)return Q.isAbsolute(e)?e:Q.join(n.paths.workspace,e)}function $(n){return Pe(n,n.paths.backend)}function H(n){return Pe(n,n.paths.frontend)}function _e(n){let e=$(n);if(!(!e||!n.paths.features?.backend))return Q.join(e,n.paths.features.backend)}function ae(n){let e=H(n);if(!(!e||!n.paths.features?.frontend))return Q.join(e,n.paths.features.frontend)}function Te(n){let e=[];return n.techStack?.backend?.length&&e.push(`**Backend:** ${n.techStack.backend.join(", ")}`),n.techStack?.frontend?.length&&e.push(`**Frontend:** ${n.techStack.frontend.join(", ")}`),n.techStack?.patterns?.length&&e.push(`**Patterns:** ${n.techStack.patterns.join(", ")}`),e.join(`
775
- `)}function Re(n){let e=[];return n.services?.backend&&e.push(`| Backend | ${$(n)} | ${n.services.backend.port} | ${n.services.backend.logFile||"N/A"} |`),n.services?.frontend&&e.push(`| Frontend | ${H(n)} | ${n.services.frontend.port} | ${n.services.frontend.logFile||"N/A"} |`),n.services?.database&&e.push(`| Database (${n.services.database.type}) | localhost | ${n.services.database.port} | - |`),e.length===0?"":`| Service | Location | Port | Logs |
774
+ `)}getLogFilePath(){let t=(this.sessionId||"unknown").replace(/[^a-zA-Z0-9-]/g,"_").slice(0,50),o=new Date().toISOString().split("T")[0];return Et.join(this.outputDir,`session-${o}-${t}.log`)}flush(){if(this.buffer.length===0)return;let e=this.buffer.join("");this.buffer=[];let t=this.getLogFilePath();oe.appendFileSync(t,e)}stop(){this.flushInterval&&(clearInterval(this.flushInterval),this.flushInterval=null),this.flush()}};function At(n){return n?typeof n=="boolean"?n?new ke({enabled:!0}):null:n.enabled?new ke(n):null:null}import{z as h}from"zod";import*as Ce from"fs/promises";import*as J from"path";var xt=h.object({port:h.number(),startCommand:h.string(),buildCommand:h.string().optional(),typecheckCommand:h.string().optional(),logFile:h.string().optional(),healthEndpoint:h.string().optional(),extensions:h.array(h.string())}),en=h.object({port:h.number(),type:h.enum(["postgres","mysql","sqlite","mongodb"])}),tn=h.object({command:h.string(),schemaPath:h.string().optional()}),nn=h.object({email:h.string().optional(),name:h.string().optional(),commitPrefix:h.string().optional()}),on=h.object({enabled:h.boolean(),port:h.number().optional()}),ve=h.object({version:h.literal("1.0"),paths:h.object({workspace:h.string(),backend:h.string().optional(),frontend:h.string().optional(),features:h.object({backend:h.string().optional(),frontend:h.string().optional()}).optional()}),services:h.object({backend:xt.optional(),frontend:xt.optional(),database:en.optional()}).optional(),scaffold:tn.optional(),git:nn.optional(),techStack:h.object({backend:h.array(h.string()).optional(),frontend:h.array(h.string()).optional(),patterns:h.array(h.string()).optional()}).optional(),tunnel:on.optional()});function W(n){return{version:"1.0",paths:{workspace:n||process.env.WORKSPACE_DIR||"/workspace",backend:"packages/dotnet-api",frontend:"packages/backoffice-web",features:{backend:"Source/Features",frontend:"src/applications/super-admin/features"}},services:{backend:{port:5338,startCommand:"dotnet run",buildCommand:"dotnet build",typecheckCommand:"dotnet build --no-restore",logFile:"/tmp/api.log",healthEndpoint:"http://localhost:5338",extensions:[".cs",".csproj"]},frontend:{port:5173,startCommand:"npm run dev",typecheckCommand:"npx tsc -p tsconfig.app.json --noEmit",logFile:"/tmp/web.log",healthEndpoint:"http://localhost:5173",extensions:[".ts",".tsx",".json"]},database:{port:43594,type:"postgres"}},scaffold:{command:"scaffold --schema /tmp/scaffold.json --output /workspace --force --full",schemaPath:"/tmp/scaffold.json"},git:{email:"agent@dotnetmentor.se",name:"Agent",commitPrefix:"[agent]"},techStack:{backend:[".NET 9","PostgreSQL","MediatR (CQRS)","EF Core"],frontend:["React 19","TypeScript","Vite","MUI","TanStack Query"],patterns:["Vertical slices","CQRS","Soft delete","Timestamps"]},tunnel:{enabled:!0,port:5173}}}var sn="project-config.json",rn=".sdd";async function _e(n){let{workspaceDir:e,providedConfig:t,requireConfig:o}=n;if(t)return{config:ve.parse(t),source:"provided"};let s=J.join(e,rn,sn);try{let r=await Ce.readFile(s,"utf-8"),i=JSON.parse(r);return{config:ve.parse(i),source:"file",configPath:s}}catch(r){r instanceof Error&&"code"in r&&r.code!=="ENOENT"&&console.warn(`[Config] Warning: Invalid project-config.json at ${s}:`,r instanceof h.ZodError?r.errors:r.message)}if(o)throw new Error(`No project configuration found at ${s} and requireConfig is true`);return{config:W(e),source:"default"}}function Pe(n,e){if(e)return J.isAbsolute(e)?e:J.join(n.paths.workspace,e)}function H(n){return Pe(n,n.paths.backend)}function B(n){return Pe(n,n.paths.frontend)}function Te(n){let e=H(n);if(!(!e||!n.paths.features?.backend))return J.join(e,n.paths.features.backend)}function le(n){let e=B(n);if(!(!e||!n.paths.features?.frontend))return J.join(e,n.paths.features.frontend)}function Re(n){let e=[];return n.techStack?.backend?.length&&e.push(`**Backend:** ${n.techStack.backend.join(", ")}`),n.techStack?.frontend?.length&&e.push(`**Frontend:** ${n.techStack.frontend.join(", ")}`),n.techStack?.patterns?.length&&e.push(`**Patterns:** ${n.techStack.patterns.join(", ")}`),e.join(`
775
+ `)}function Ie(n){let e=[];return n.services?.backend&&e.push(`| Backend | ${H(n)} | ${n.services.backend.port} | ${n.services.backend.logFile||"N/A"} |`),n.services?.frontend&&e.push(`| Frontend | ${B(n)} | ${n.services.frontend.port} | ${n.services.frontend.logFile||"N/A"} |`),n.services?.database&&e.push(`| Database (${n.services.database.type}) | localhost | ${n.services.database.port} | - |`),e.length===0?"":`| Service | Location | Port | Logs |
776
776
  |---------|----------|------|------|
777
777
  ${e.join(`
778
- `)}`}function Dt(n={}){let{debugMode:e=!1,projectPrompt:t=null,isLocal:s=!1,projectConfig:o=G(),useDefaultStack:r=!0}=n,i=t?`
778
+ `)}`}function Dt(n={}){let{debugMode:e=!1,projectPrompt:t=null,isLocal:o=!1,projectConfig:s=W(),useDefaultStack:r=!0}=n,i=t?`
779
779
  <project_context>
780
780
  ${t}
781
781
  </project_context>
@@ -820,7 +820,7 @@ Then STOP. Do not continue. Do not try to fix it. Wait for user.
820
820
 
821
821
  ---
822
822
 
823
- `:"",c=un(o),l=pn(o,s),p=fn(o,r),u=dn(r),d=r?mn(o):"",v=r?hn(o,s):"",w=gn(r);return`${i}${a}# Agent Instructions
823
+ `:"",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
824
824
 
825
825
  ## \u26D4 CRITICAL: EVERYTHING IS A KANBAN TASK
826
826
 
@@ -957,17 +957,17 @@ ${c}
957
957
 
958
958
  ${l}
959
959
 
960
- ${p}
961
-
962
- ${w}
963
-
964
960
  ${d}
965
961
 
966
962
  ${v}
967
- `}function un(n){let e=Te(n),t=$(n),s=H(n),o=_e(n),r=ae(n),i=[];o&&i.push(`- Backend: "${o}/{Entity}/"`),r&&i.push(`- Frontend: "${r}/{entity}/"`);let a=n.techStack?.patterns?.length?`
963
+
964
+ ${g}
965
+
966
+ ${w}
967
+ `}function un(n){let e=Re(n),t=H(n),o=B(n),s=Te(n),r=le(n),i=[];s&&i.push(`- Backend: "${s}/{Entity}/"`),r&&i.push(`- Frontend: "${r}/{entity}/"`);let a=n.techStack?.patterns?.length?`
968
968
  **Key patterns:**
969
- ${n.techStack.patterns.map(p=>`- ${p}`).join(`
970
- `)}`:"",l=n.techStack?.backend?.some(p=>p.includes(".NET")||p.includes("C#"))??!1?`
969
+ ${n.techStack.patterns.map(d=>`- ${d}`).join(`
970
+ `)}`:"",l=n.techStack?.backend?.some(d=>d.includes(".NET")||d.includes("C#"))??!1?`
971
971
 
972
972
  **\u26A0\uFE0F User entity exists!** ASP.NET Identity "ApplicationUser" at "Features/Users/" - don't scaffold User, just relate to it.`:"";return`<tech-stack>
973
973
  ${e}
@@ -976,7 +976,7 @@ ${i.length>0?`
976
976
  ${i.join(`
977
977
  `)}`:""}
978
978
  ${a}${l}
979
- </tech-stack>`}function pn(n,e){return e?`<environment>
979
+ </tech-stack>`}function dn(n,e){return e?`<environment>
980
980
  **LOCAL MODE - User manages their own services.**
981
981
 
982
982
  You are running on the user's local machine, NOT in a Docker container.
@@ -997,11 +997,11 @@ The user is responsible for:
997
997
  </environment>`:`<environment>
998
998
  Docker container with pre-started services:
999
999
 
1000
- ${Re(n)}
1000
+ ${Ie(n)}
1001
1001
  ${n.tunnel?.enabled?"| Tunnel | - | - | Exposes frontend to user |":""}
1002
1002
 
1003
1003
  User sees app through **Cloudflare tunnel** (live preview in browser).
1004
- </environment>`}function dn(n){return n?`**\u2705 ALWAYS delegate to these subagents:**
1004
+ </environment>`}function gn(n){return n?`**\u2705 ALWAYS delegate to these subagents:**
1005
1005
 
1006
1006
  | Trigger | Subagent | Example |
1007
1007
  |---------|----------|---------|
@@ -1021,7 +1021,7 @@ User sees app through **Cloudflare tunnel** (live preview in browser).
1021
1021
  **Flow:** @planning \u2192 **[Create Kanban Cards]** \u2192 Execute work \u2192 @debugger (if issues)
1022
1022
 
1023
1023
  Note: For this project, you implement the code directly after planning (no stack-specific subagents).
1024
- Check \`.claude/skills/\` for project-specific patterns and conventions.`}function gn(n){return n?`<workflow>
1024
+ Check \`.claude/skills/\` for project-specific patterns and conventions.`}function fn(n){return n?`<workflow>
1025
1025
  **Standard feature flow:**
1026
1026
  1. **@planning** - Gather requirements, design spec, saves it.
1027
1027
  2. **\u23F8\uFE0F STOP & WAIT** - Tell user: "Spec is ready! Review it and tell me when to implement."
@@ -1104,19 +1104,21 @@ Check \`.claude/skills/\` for project-specific patterns and conventions.`}functi
1104
1104
  - After @planning \u2192 STOP and wait for user to approve the spec
1105
1105
  - Check project's existing code for patterns before implementing
1106
1106
  - Use @debugger if you encounter build errors or bugs
1107
- </workflow>`}function fn(n,e){let t=`**@planning** - Design before building:
1107
+ </workflow>`}function mn(n,e){let t=`**@planning** - Design before building:
1108
1108
  - Complex features \u2192 plan first
1109
1109
  - Asks questions, designs spec
1110
1110
  - **Saves spec using save_specification MCP tool**
1111
1111
  - **After @planning completes: STOP and wait for user approval!**
1112
- `;if(e){let s=!!n.scaffold?.command,o=!!n.services?.backend,r=!!n.services?.frontend;s&&(t+=`
1112
+ `;if(e){let o=!!n.scaffold?.command,s=!!n.services?.backend,r=!!n.services?.frontend;o&&(t+=`
1113
1113
  **@scaffolding** - Create CRUD entities, database, backend & frontend:
1114
1114
  - "Add X management" \u2192 one-shot all entities, everything from entity creation to frontend hooks and pages!
1115
1115
  - NEVER write scaffold JSON yourself
1116
- `),o&&(t+=`
1116
+ `),s&&(t+=`
1117
1117
  **@backend** - Custom backend code (non-scaffold):
1118
1118
  - Custom queries, endpoints, lookups
1119
1119
  - Runs build + swagger generation before done
1120
+ - **Run \`./scripts/generate-swagger.sh\` after adding/changing endpoints**
1121
+ - **Run \`./scripts/generate-signalr.sh\` after adding/changing SignalR hubs**
1120
1122
  ${r?"- **Must complete before @frontend starts!**":""}
1121
1123
  `),r&&(t+=`
1122
1124
  **@frontend** - UI components:
@@ -1135,7 +1137,7 @@ ${r?"- **Must complete before @frontend starts!**":""}
1135
1137
  **\u26A0\uFE0F MANDATORY DELEGATION - You MUST use subagents:**
1136
1138
 
1137
1139
  ${t}
1138
- </subagents>`}function mn(n){return n.services?.frontend?n.techStack?.frontend?.some(t=>t.includes("TanStack")||t.includes("React Query")||t.includes("MUI"))??!1?`<frontend-api>
1140
+ </subagents>`}function hn(n){return n.services?.frontend?n.techStack?.frontend?.some(t=>t.includes("TanStack")||t.includes("React Query")||t.includes("MUI"))??!1?`<frontend-api>
1139
1141
  ## Orval-Generated API Hooks
1140
1142
 
1141
1143
  Frontend uses **Orval** to auto-generate React Query hooks from Swagger. Import from 'api/queries-commands.ts'.
@@ -1164,12 +1166,16 @@ queryClient.invalidateQueries({ queryKey: getGetApiPeopleQueryKey() })
1164
1166
  '''
1165
1167
 
1166
1168
  **Important:** Check existing feature code (e.g., 'features/person/') for correct patterns before writing new API calls.
1169
+
1170
+ **If hooks are missing or stale:**
1171
+ - Run \`./scripts/generate-swagger.sh\` to regenerate API hooks from backend swagger
1172
+ - Run \`./scripts/generate-signalr.sh\` to regenerate SignalR hub client code
1167
1173
  </frontend-api>`:`<frontend-api>
1168
1174
  ## API Integration
1169
1175
 
1170
1176
  Check the project's existing patterns for API calls and data fetching.
1171
1177
  Look for existing feature code to understand the correct patterns before writing new API calls.
1172
- </frontend-api>`:""}function hn(n,e){if(!n.services?.frontend)return"";let t=n.services.frontend.port,s=ae(n),o=`http://localhost:${t}`;return`<ui-verification>
1178
+ </frontend-api>`:""}function yn(n,e){if(!n.services?.frontend)return"";let t=n.services.frontend.port,o=le(n),s=`http://localhost:${t}`;return`<ui-verification>
1173
1179
  ## \u{1F50D} UI Verification with Playwright
1174
1180
 
1175
1181
  You have access to **Playwright MCP** tools to verify the UI works correctly after making changes.
@@ -1185,6 +1191,27 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
1185
1191
  - \`browser_scroll_down/up\` - Scroll the page
1186
1192
  - \`browser_wait\` - Wait for page to settle
1187
1193
 
1194
+ *Forms and input:*
1195
+ - \`browser_fill_form\` - Fill multiple form fields at once
1196
+ - \`browser_select_option\` - Select dropdown options
1197
+ - \`browser_press_key\` - Press keyboard keys (Enter, Escape, Tab, etc.)
1198
+ - \`browser_hover\` - Hover over elements
1199
+ - \`browser_drag\` - Drag and drop between elements
1200
+ - \`browser_file_upload\` - Upload files to file inputs
1201
+
1202
+ *Dialog handling (IMPORTANT for alerts/confirms/prompts):*
1203
+ - \`browser_handle_dialog\` - Accept or dismiss alert, confirm, and prompt dialogs
1204
+ - Use \`accept: true\` to click OK/Yes, \`accept: false\` to click Cancel/No
1205
+ - For prompt dialogs, provide \`promptText\` with the text to enter
1206
+
1207
+ *Browser lifecycle (YOU control when browser closes):*
1208
+ - \`browser_close\` - Explicitly close the browser when you're done
1209
+ - \`browser_resize\` - Resize the browser viewport
1210
+
1211
+ *Debugging:*
1212
+ - \`browser_console_messages\` - Get browser console output (errors, warnings, logs)
1213
+ - \`browser_network_requests\` - See network activity (API calls, failed requests)
1214
+
1188
1215
  *Vision/coordinate-based (for canvas, WebGL, Three.js, games):*
1189
1216
  - \`browser_screen_click\` - Click at specific x,y coordinates
1190
1217
  - \`browser_screen_move_mouse\` - Move mouse to x,y position
@@ -1204,7 +1231,7 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
1204
1231
  - To test forms and interactions
1205
1232
 
1206
1233
  **Verification workflow:**
1207
- 1. Navigate to the app: \`browser_navigate\` to \`${s?.includes("super-admin")?`${o}/super-admin/[feature]`:`${o}/[feature]`}\`
1234
+ 1. Navigate to the app: \`browser_navigate\` to \`${o?.includes("super-admin")?`${s}/super-admin/[feature]`:`${s}/[feature]`}\`
1208
1235
  2. Get page structure: \`browser_snapshot\` returns the accessibility tree (LLM-friendly)
1209
1236
  3. Optional: Take screenshot for user: \`browser_take_screenshot\`
1210
1237
  4. Verify expected elements exist in the snapshot
@@ -1215,20 +1242,23 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
1215
1242
  - Use \`browser_screen_click\` for canvas/WebGL/Three.js - accessibility tools can't see inside canvas
1216
1243
  - Use \`browser_evaluate\` to run JavaScript when you need complex interactions
1217
1244
  - Use screenshots when you want to show the user what you see
1245
+ - **Dialog handling**: If an alert/confirm/prompt appears, use \`browser_handle_dialog\` immediately to dismiss it
1246
+ - **Browser lifecycle**: YOU control when to close the browser with \`browser_close\`. Keep it open if you need to do more interactions.
1247
+ - Use \`browser_console_messages\` to check for JavaScript errors when debugging
1218
1248
  - ${e?"In local mode, the browser window will be visible to you":"In container mode, browser runs headless"}
1219
- </ui-verification>`}function ce(n){return n&&n.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var Ie=null;function Ge(n){Ie=ce(n),console.log(`[ProjectPrompt] Updated (${Ie?Ie.length:0} chars)`)}function jt(){return ce(Ie)}import*as ue from"path";import*as Ye from"fs";import{fileURLToPath as bn}from"url";var Sn=He(),We=ue.dirname(bn(import.meta.url)),Ot=[ue.resolve(We,"../mcps/stdio-server.js"),ue.resolve(We,"./adapters/mcps/stdio-server.js")],le=Ot.find(n=>Ye.existsSync(n))||Ot[0];console.log("[MCP] Stdio server path resolved:",le);console.log("[MCP] __dirname:",We);console.log("[MCP] File exists:",Ye.existsSync(le));async function Ee(n,e={}){let{model:t,sessionId:s=null,projectId:o=null,apiUrl:r=null,callbacks:i={},debugLog:a,abortController:c,debugMode:l=!1,isLocal:p=process.env.LOCAL_MODE==="true",projectConfig:u=G(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:d=!0}=e,v=At(a),w=new Set,S=s||"",O,j,f,M=!1,V=!1,g=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[le]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",o||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",g),console.log("[MCP] File exists check:",le);try{for await(let h of yn({prompt:n,options:{abortController:c,cwd:g,resume:s??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...p?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_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_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:t,mcpServers:{"agent-insights":Sn,"agent-planning":{type:"stdio",command:"node",args:[le],env:{WORKSPACE_DIR:g,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:o||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",...p?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:d?{scaffolding:Be,debugger:ye,planning:be,backend:Ke,frontend:qe,"project-context":Se}:{debugger:ye,planning:be,"project-context":Se},systemPrompt:Dt({debugMode:l,projectPrompt:jt(),isLocal:p,projectConfig:u,useDefaultStack:d})}}))if(!(!h.uuid||w.has(h.uuid))){switch(w.add(h.uuid),v?.log(h),i.onRawMessage?.(h),h.type){case"assistant":if(h.message?.content)for(let E of h.message.content)E.type==="text"?i.onAssistantText?.(E.text):E.type==="tool_use"?i.onAssistantToolUse?.(E.name,E.input):E.type==="tool_result"&&i.onAssistantToolResult?.(E.tool_use_id,E.content);break;case"user":i.onUserMessage?.(h);break;case"result":if(h.subtype==="success")O=h.result,j=h.total_cost_usd,i.onResult?.(h.result,h.total_cost_usd);else{let E=`Agent error: ${h.subtype}`;f=E,i.onError?.(E)}V=!0;break;case"system":switch(h.subtype){case"init":S=h.session_id,v?.setSessionId(h.session_id),i.onSystemInit?.(h.session_id);break;case"status":i.onSystemStatus?.(JSON.stringify(h));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":i.onStreamEvent?.(h);break;case"tool_progress":i.onToolProgress?.(h);break;case"auth_status":i.onAuthStatus?.(h);break}if(V)break}}catch(h){h instanceof Error&&h.name==="AbortError"||h instanceof Error&&h.message.includes("aborted by user")?(M=!0,i.onAborted?.()):(f=h instanceof Error?h.message:String(h),i.onError?.(f))}finally{v?.stop()}return console.log("after try"),{sessionId:S,result:O,cost:j,error:f,aborted:M}}import{spawn as Mt,execSync as X}from"child_process";import*as R from"fs";import*as Ut from"http";import*as J from"path";function Ae(n){let{workspaceDir:e,apiPort:t,webPort:s,onBackendError:o,onFrontendError:r,projectConfig:i=G(e)}=n,a=t??i.services?.backend?.port??5338,c=s??i.services?.frontend?.port??5173,l=$(i),p=H(i),u=i.services?.backend?.startCommand??"dotnet run",d=i.services?.backend?.buildCommand??"dotnet build",v=i.services?.backend?.typecheckCommand??"dotnet build --no-restore",w=i.services?.frontend?.startCommand??"npm run dev",S=i.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",O=i.services?.backend?.logFile??"/tmp/api.log",j=i.services?.frontend?.logFile??"/tmp/web.log",f=null,M=null,V=b=>new Promise(m=>{let k=setTimeout(()=>m(!1),3e3);Ut.get(`http://localhost:${b}`,D=>{clearTimeout(k),m(D.statusCode!==void 0&&D.statusCode<500)}).on("error",()=>{clearTimeout(k),m(!1)})}),g=b=>{try{let m=X(`lsof -ti:${b} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(m){let k=m.split(`
1220
- `).filter(Boolean);console.log(`[Services] Killing ${k.length} process(es) on port ${b}: ${k.join(", ")}`);for(let P of k)try{process.kill(parseInt(P,10),"SIGKILL")}catch{}}}catch{try{X(`fuser -k ${b}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},h=(b,m)=>new Promise(k=>{if(!b||!b.pid){k();return}console.log(`[Services] Stopping ${m} (PID: ${b.pid})...`);try{process.kill(-b.pid,"SIGTERM")}catch{try{b.kill("SIGTERM")}catch{}}setTimeout(k,500)}),E=async()=>{if(!l||!R.existsSync(l)){console.log("[Services] No backend found, skipping backend start");return}g(a),console.log(`[Services] Starting backend with: ${u}...`);let b=J.dirname(O);R.existsSync(b)||R.mkdirSync(b,{recursive:!0});let m=R.createWriteStream(O,{flags:"a"}),[k,...P]=u.split(" ");f=Mt(k,P,{cwd:l,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let D="",A=_=>{let W=_.toString();m.write(W);let F=(D+W).split(`
1221
- `);D=F.pop()||"";for(let L of F)L&&vn(L)&&o&&o(L)};f.stdout?.on("data",A),f.stderr?.on("data",A),f.on("exit",_=>{m.end(),_!==0&&_!==null&&o&&o(`Process exited with code ${_}`)}),f.unref()},gt=async()=>{if(!p||!R.existsSync(J.join(p,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}g(c),console.log(`[Services] Starting frontend with: ${w}...`);let b=J.dirname(j);R.existsSync(b)||R.mkdirSync(b,{recursive:!0});let[m,...k]=w.split(" ");M=Mt(m,k,{cwd:p,stdio:["ignore",R.openSync(j,"w"),R.openSync(j,"w")],detached:!0,shell:!0}),M.unref()},ft=async()=>{await h(f,"backend"),f=null,g(a)},mt=async()=>{await h(M,"frontend"),M=null,g(c)},Le=async()=>{let[b,m]=await Promise.all([V(a),V(c)]);return{api:b,web:m}},ht=async(b=15e3)=>{let m=Date.now(),k=1e3,P=0,D=null,A=null;for(console.log(`[Services] Waiting for health (timeout: ${b}ms)...`);Date.now()-m<b;){P++;let F=await Le(),L=Date.now()-m;if(F.web&&A===null&&(A=L,console.log(`[Services] \u2713 Frontend (Vite) ready after ${L}ms`)),F.api&&D===null&&(D=L,console.log(`[Services] \u2713 Backend (dotnet) ready after ${L}ms`)),console.log(`[Services] Health poll #${P} (${L}ms): api=${F.api}, web=${F.web}`),F.api&&F.web)return console.log(`[Services] \u2713 Both services healthy after ${L}ms (${P} polls)`),F;await new Promise(zt=>setTimeout(zt,k))}let _=await Le(),W=Date.now()-m;return _.web&&A===null&&console.log(`[Services] \u2713 Frontend (Vite) ready after ${W}ms`),_.api&&D===null&&console.log(`[Services] \u2713 Backend (dotnet) ready after ${W}ms`),console.log(`[Services] \u26A0 Health timeout after ${W}ms: api=${_.api}, web=${_.web}`),_},Qt=async b=>{console.log(`[Services] Restarting ${b}...`);let m={success:!0},k=b==="backend"||b==="both",P=b==="frontend"||b==="both";if(k&&await ft(),P&&await mt(),k&&l&&R.existsSync(l)){console.log(`[Services] Building backend with: ${d}...`);try{X(d,{cwd:l,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),console.log("[Services] \u2713 Backend build succeeded")}catch(A){let _=A;m.success=!1,m.backendError=_.stderr||_.stdout||"Build failed",console.error("[Services] \u2717 Backend build failed")}}if(P&&p&&R.existsSync(J.join(p,"package.json"))){console.log(`[Services] Type-checking frontend with: ${S}...`);try{X(S,{cwd:p,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),console.log("[Services] \u2713 Frontend types OK")}catch(A){let _=A;m.success=!1,m.frontendError=_.stderr||_.stdout||"Type check failed",console.error("[Services] \u2717 Frontend type check failed")}}console.log(`[Services] Starting ${b}...`),k&&await E(),P&&await gt();let D=await ht(1e4);if(k&&!D.api){m.success=!1;let A=await St("backend",20),_=A.length>0?`
1249
+ </ui-verification>`}function pe(n){return n&&n.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var Ee=null;function We(n){Ee=pe(n),console.log(`[ProjectPrompt] Updated (${Ee?Ee.length:0} chars)`)}function jt(){return pe(Ee)}import*as Z from"path";import*as Qe from"fs";import{fileURLToPath as Sn}from"url";var wn=Be(),Ye=Z.dirname(Sn(import.meta.url)),Ot=[Z.resolve(Ye,"../mcps/stdio-server.js"),Z.resolve(Ye,"./adapters/mcps/stdio-server.js")],ue=Ot.find(n=>Qe.existsSync(n))||Ot[0];console.log("[MCP] Stdio server path resolved:",ue);console.log("[MCP] __dirname:",Ye);console.log("[MCP] File exists:",Qe.existsSync(ue));async function Ae(n,e={}){let{model:t,sessionId:o=null,projectId:s=null,apiUrl:r=null,callbacks:i={},debugLog:a,abortController:c,debugMode:l=!1,isLocal:d=process.env.LOCAL_MODE==="true",projectConfig:u=W(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:g=!0}=e,w=At(a),v=new Set,b=o||"",D,j,f,O=!1,q=!1,F=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[ue]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",s||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",F),console.log("[MCP] File exists check:",ue);try{for await(let p of bn({prompt:n,options:{abortController:c,cwd:F,resume:o??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...d?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_fill_form","mcp__playwright__browser_select_option","mcp__playwright__browser_press_key","mcp__playwright__browser_hover","mcp__playwright__browser_drag","mcp__playwright__browser_file_upload","mcp__playwright__browser_handle_dialog","mcp__playwright__browser_close","mcp__playwright__browser_resize","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_screen_capture","mcp__playwright__browser_screen_move_mouse","mcp__playwright__browser_screen_click","mcp__playwright__browser_screen_drag","mcp__playwright__browser_screen_type","mcp__playwright__browser_evaluate","mcp__playwright__browser_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:t,mcpServers:{"agent-insights":wn,"agent-planning":{type:"stdio",command:"node",args:[ue],env:{WORKSPACE_DIR:F,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:s||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${Z.join(F,".playwright-data")}`,...d?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:g?{scaffolding:Ke,debugger:be,planning:Se,backend:qe,frontend:Ge,"project-context":we}:{debugger:be,planning:Se,"project-context":we},systemPrompt:Dt({debugMode:l,projectPrompt:jt(),isLocal:d,projectConfig:u,useDefaultStack:g})}}))if(!(!p.uuid||v.has(p.uuid))){switch(v.add(p.uuid),w?.log(p),i.onRawMessage?.(p),p.type){case"assistant":if(p.message?.content)for(let C of p.message.content)C.type==="text"?i.onAssistantText?.(C.text):C.type==="tool_use"?i.onAssistantToolUse?.(C.name,C.input):C.type==="tool_result"&&i.onAssistantToolResult?.(C.tool_use_id,C.content);break;case"user":i.onUserMessage?.(p);break;case"result":if(p.subtype==="success")D=p.result,j=p.total_cost_usd,i.onResult?.(p.result,p.total_cost_usd);else{let C=`Agent error: ${p.subtype}`;f=C,i.onError?.(C)}q=!0;break;case"system":switch(p.subtype){case"init":b=p.session_id,w?.setSessionId(p.session_id),i.onSystemInit?.(p.session_id);break;case"status":i.onSystemStatus?.(JSON.stringify(p));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":i.onStreamEvent?.(p);break;case"tool_progress":i.onToolProgress?.(p);break;case"auth_status":i.onAuthStatus?.(p);break}if(q)break}}catch(p){p instanceof Error&&p.name==="AbortError"||p instanceof Error&&p.message.includes("aborted by user")?(O=!0,i.onAborted?.()):(f=p instanceof Error?p.message:String(p),i.onError?.(f))}finally{w?.stop()}return console.log("after try"),{sessionId:b,result:D,cost:j,error:f,aborted:O}}import{spawn as Ut,execSync as ee}from"child_process";import*as R from"fs";import*as Mt from"http";import*as V from"path";function xe(n){let{workspaceDir:e,apiPort:t,webPort:o,onBackendError:s,onFrontendError:r,projectConfig:i=W(e)}=n,a=t??i.services?.backend?.port??5338,c=o??i.services?.frontend?.port??5173,l=H(i),d=B(i),u=i.services?.backend?.startCommand??"dotnet run",g=i.services?.backend?.buildCommand??"dotnet build",w=i.services?.backend?.typecheckCommand??"dotnet build --no-restore",v=i.services?.frontend?.startCommand??"npm run dev",b=i.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",D=i.services?.backend?.logFile??"/tmp/api.log",j=i.services?.frontend?.logFile??"/tmp/web.log",f=null,O=null,q=y=>new Promise(m=>{let S=setTimeout(()=>m(!1),3e3);Mt.get(`http://localhost:${y}`,x=>{clearTimeout(S),m(x.statusCode!==void 0&&x.statusCode<500)}).on("error",()=>{clearTimeout(S),m(!1)})}),F=y=>{try{let m=ee(`lsof -ti:${y} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(m){let S=m.split(`
1250
+ `).filter(Boolean);console.log(`[Services] Killing ${S.length} process(es) on port ${y}: ${S.join(", ")}`);for(let _ of S)try{process.kill(parseInt(_,10),"SIGKILL")}catch{}}}catch{try{ee(`fuser -k ${y}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},p=(y,m)=>new Promise(S=>{if(!y||!y.pid){S();return}console.log(`[Services] Stopping ${m} (PID: ${y.pid})...`);try{process.kill(-y.pid,"SIGTERM")}catch{try{y.kill("SIGTERM")}catch{}}setTimeout(S,500)}),C=async()=>{if(!l||!R.existsSync(l)){console.log("[Services] No backend found, skipping backend start");return}F(a),console.log(`[Services] Starting backend with: ${u}...`);let y=V.dirname(D);R.existsSync(y)||R.mkdirSync(y,{recursive:!0});let m=R.createWriteStream(D,{flags:"a"}),[S,..._]=u.split(" ");f=Ut(S,_,{cwd:l,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let x="",E=P=>{let Q=P.toString();m.write(Q);let L=(x+Q).split(`
1251
+ `);x=L.pop()||"";for(let $ of L)$&&vn($)&&s&&s($)};f.stdout?.on("data",E),f.stderr?.on("data",E),f.on("exit",P=>{m.end(),P!==0&&P!==null&&s&&s(`Process exited with code ${P}`)}),f.unref()},de=async()=>{if(!d||!R.existsSync(V.join(d,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}F(c),console.log(`[Services] Starting frontend with: ${v}...`);let y=V.dirname(j);R.existsSync(y)||R.mkdirSync(y,{recursive:!0});let[m,...S]=v.split(" ");O=Ut(m,S,{cwd:d,stdio:["ignore",R.openSync(j,"w"),R.openSync(j,"w")],detached:!0,shell:!0}),O.unref()},ft=async()=>{await p(f,"backend"),f=null,F(a)},mt=async()=>{await p(O,"frontend"),O=null,F(c)},$e=async()=>{let[y,m]=await Promise.all([q(a),q(c)]);return{api:y,web:m}},ht=async(y=15e3)=>{let m=Date.now(),S=1e3,_=0,x=null,E=null;for(console.log(`[Services] Waiting for health (timeout: ${y}ms)...`);Date.now()-m<y;){_++;let L=await $e(),$=Date.now()-m;if(L.web&&E===null&&(E=$,console.log(`[Services] \u2713 Frontend (Vite) ready after ${$}ms`)),L.api&&x===null&&(x=$,console.log(`[Services] \u2713 Backend (dotnet) ready after ${$}ms`)),console.log(`[Services] Health poll #${_} (${$}ms): api=${L.api}, web=${L.web}`),L.api&&L.web)return console.log(`[Services] \u2713 Both services healthy after ${$}ms (${_} polls)`),L;await new Promise(Vt=>setTimeout(Vt,S))}let P=await $e(),Q=Date.now()-m;return P.web&&E===null&&console.log(`[Services] \u2713 Frontend (Vite) ready after ${Q}ms`),P.api&&x===null&&console.log(`[Services] \u2713 Backend (dotnet) ready after ${Q}ms`),console.log(`[Services] \u26A0 Health timeout after ${Q}ms: api=${P.api}, web=${P.web}`),P},zt=async y=>{console.log(`[Services] Restarting ${y}...`);let m={success:!0},S=y==="backend"||y==="both",_=y==="frontend"||y==="both";if(S&&await ft(),_&&await mt(),S&&l&&R.existsSync(l)){console.log(`[Services] Building backend with: ${g}...`);try{ee(g,{cwd:l,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),console.log("[Services] \u2713 Backend build succeeded")}catch(E){let P=E;m.success=!1,m.backendError=P.stderr||P.stdout||"Build failed",console.error("[Services] \u2717 Backend build failed")}}if(_&&d&&R.existsSync(V.join(d,"package.json"))){console.log(`[Services] Type-checking frontend with: ${b}...`);try{ee(b,{cwd:d,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),console.log("[Services] \u2713 Frontend types OK")}catch(E){let P=E;m.success=!1,m.frontendError=P.stderr||P.stdout||"Type check failed",console.error("[Services] \u2717 Frontend type check failed")}}console.log(`[Services] Starting ${y}...`),S&&await C(),_&&await de();let x=await ht(1e4);if(S&&!x.api){m.success=!1;let E=await St("backend",20),P=E.length>0?`
1222
1252
  Recent logs:
1223
- ${A.join(`
1224
- `)}`:"";m.backendError||(m.backendError=`Backend failed to start.${_}`)}return P&&!D.web&&(m.success=!1,m.frontendError||(m.frontendError="Frontend failed to start")),m},yt=async()=>{if(!l||!R.existsSync(l))return{success:!0};try{return X(v,{cwd:l,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),{success:!0}}catch(b){let m=b;return{success:!1,errors:m.stdout||m.stderr||"Build failed"}}},bt=async()=>{if(!p||!R.existsSync(J.join(p,"package.json")))return{success:!0};try{return X(S,{cwd:p,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),{success:!0}}catch(b){let m=b;return{success:!1,errors:m.stdout||m.stderr||"Type check failed"}}},Jt=async()=>{let[b,m]=await Promise.all([yt(),bt()]);return{backend:b,frontend:m}},St=async(b,m)=>{let k=b==="backend"?O:j;if(!R.existsSync(k))return[`Log file not found: ${k}`];try{return X(`tail -n ${m} ${k}`,{encoding:"utf-8"}).split(`
1225
- `).filter(Boolean)}catch(P){return[`Error reading logs: ${P instanceof Error?P.message:String(P)}`]}};return{startBackend:E,startFrontend:gt,stopBackend:ft,stopFrontend:mt,restartServices:Qt,checkHealth:Le,waitForHealth:ht,getProcesses:()=>({backend:f,frontend:M}),checkBackendBuild:yt,checkFrontendTypes:bt,runTypeChecks:Jt,tailLogs:St,checkSwaggerEndpoints:async b=>{let m=J.join(e,"swagger.json");if(!R.existsSync(m))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let k=JSON.parse(R.readFileSync(m,"utf-8")),P=Object.keys(k.paths||{}),D=[];for(let A of P)if(A.toLowerCase().includes(b.toLowerCase())){let _=Object.keys(k.paths[A]);for(let W of _)D.push(`${W.toUpperCase()} ${A}`)}return{foundEndpoints:D,totalEndpoints:P.length}}catch(k){return{foundEndpoints:[`Error parsing swagger.json: ${k instanceof Error?k.message:String(k)}`],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 Z}from"child_process";function xe(n){let e=async()=>{try{return Z("git status --porcelain",{cwd:n,encoding:"utf-8"}).trim().length>0}catch{return!1}},t=async()=>{try{return Z("git branch --show-current",{cwd:n,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:e,commitAndPush:async(o,r)=>{try{if(!await e())return{success:!0,noChanges:!0};let i=o.length>60?o.substring(0,60)+"...":o,c=`${r?"[agent] (partial)":"[agent]"} ${i}`;Z("git add -A",{cwd:n}),Z(`git commit -m "${c.replace(/"/g,'\\"')}"`,{cwd:n,encoding:"utf-8"});let l=Z("git rev-parse --short HEAD",{cwd:n,encoding:"utf-8"}).trim();try{Z("git push",{cwd:n,stdio:"pipe"})}catch(p){let u=p instanceof Error?p.message:String(p);if(u.includes("no upstream branch")){let d=await t();Z(`git push --set-upstream origin ${d}`,{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 ne from"@microsoft/signalr";var z=class{constructor(e,t){this.connection=e;this.receiverMethod=t}dispose=()=>{for(let e of this.receiverMethod)this.connection.off(e.methodName,e.method)}},Nt=n=>{if(n==="IDeploymentHub")return Qe.Instance;if(n==="IKanbanHub")return ze.Instance;if(n==="IAgentHub")return Xe.Instance;if(n==="ISlaveHub")return et.Instance;if(n==="ITestHub")return nt.Instance},Ft=n=>{if(n==="IDeploymentHubClient")return st.Instance;if(n==="IKanbanBoardClient")return rt.Instance;if(n==="IAgentReceiver")return it.Instance;if(n==="IBackofficeReceiver")return at.Instance;if(n==="ISlaveHubClient")return ct.Instance;if(n==="ITestHubClient")return lt.Instance},Qe=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Je(e)},Je=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)},ze=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Ve(e)},Ve=class{constructor(e){this.connection=e}joinBoard=async e=>await this.connection.invoke("JoinBoard",e);leaveBoard=async e=>await this.connection.invoke("LeaveBoard",e)},Xe=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Ze(e)},Ze=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,s)=>await this.connection.invoke("ReportSessionOutput",e,t,s);reportSessionCost=async(e,t,s)=>await this.connection.invoke("ReportSessionCost",e,t,s);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,s)=>await this.connection.invoke("SessionCompleted",e,t,s);initSessionCompleted=async(e,t,s,o)=>await this.connection.invoke("InitSessionCompleted",e,t,s,o);reportPreviewBuild=async(e,t,s)=>await this.connection.invoke("ReportPreviewBuild",e,t,s);saveSpecification=async(e,t,s,o)=>await this.connection.invoke("SaveSpecification",e,t,s,o);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,s,o)=>await this.connection.invoke("SendPrompt",e,t,s,o);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)},et=class n{static Instance=new n;constructor(){}createHubProxy=e=>new tt(e)},tt=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)},nt=class n{static Instance=new n;constructor(){}createHubProxy=e=>new ot(e)},ot=class{constructor(e){this.connection=e}joinProject=async e=>await this.connection.invoke("JoinProject",e);leaveProject=async e=>await this.connection.invoke("LeaveProject",e)},st=class n{static Instance=new n;constructor(){}register=(e,t)=>{let s=(...i)=>t.deploy(...i),o=(...i)=>t.stop(...i);e.on("Deploy",s),e.on("Stop",o);let r=[{methodName:"Deploy",method:s},{methodName:"Stop",method:o}];return new z(e,r)}},rt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let s=(...f)=>t.boardUpdated(...f),o=(...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),p=(...f)=>t.cardDeleted(...f),u=(...f)=>t.subtaskCreated(...f),d=(...f)=>t.subtaskUpdated(...f),v=(...f)=>t.subtaskDeleted(...f),w=(...f)=>t.noteCreated(...f),S=(...f)=>t.noteUpdated(...f),O=(...f)=>t.noteDeleted(...f);e.on("BoardUpdated",s),e.on("BoardDeleted",o),e.on("ColumnCreated",r),e.on("ColumnUpdated",i),e.on("ColumnDeleted",a),e.on("CardCreated",c),e.on("CardUpdated",l),e.on("CardDeleted",p),e.on("SubtaskCreated",u),e.on("SubtaskUpdated",d),e.on("SubtaskDeleted",v),e.on("NoteCreated",w),e.on("NoteUpdated",S),e.on("NoteDeleted",O);let j=[{methodName:"BoardUpdated",method:s},{methodName:"BoardDeleted",method:o},{methodName:"ColumnCreated",method:r},{methodName:"ColumnUpdated",method:i},{methodName:"ColumnDeleted",method:a},{methodName:"CardCreated",method:c},{methodName:"CardUpdated",method:l},{methodName:"CardDeleted",method:p},{methodName:"SubtaskCreated",method:u},{methodName:"SubtaskUpdated",method:d},{methodName:"SubtaskDeleted",method:v},{methodName:"NoteCreated",method:w},{methodName:"NoteUpdated",method:S},{methodName:"NoteDeleted",method:O}];return new z(e,j)}},it=class n{static Instance=new n;constructor(){}register=(e,t)=>{let s=(...u)=>t.initSession(...u),o=(...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",s),e.on("RunPrompt",o),e.on("SetModel",r),e.on("StopAgent",i),e.on("Ping",a),e.on("SwitchSession",c),e.on("ProjectPromptUpdated",l);let p=[{methodName:"InitSession",method:s},{methodName:"RunPrompt",method:o},{methodName:"SetModel",method:r},{methodName:"StopAgent",method:i},{methodName:"Ping",method:a},{methodName:"SwitchSession",method:c},{methodName:"ProjectPromptUpdated",method:l}];return new z(e,p)}},at=class n{static Instance=new n;constructor(){}register=(e,t)=>{let s=(...S)=>t.sessionOutput(...S),o=(...S)=>t.sessionCost(...S),r=(...S)=>t.sessionCompleted(...S),i=(...S)=>t.containerStatus(...S),a=(...S)=>t.slaveCapacityUpdate(...S),c=(...S)=>t.previewBuildStatus(...S),l=(...S)=>t.previewReload(...S),p=(...S)=>t.questionsReceived(...S),u=(...S)=>t.specificationUpdated(...S),d=(...S)=>t.singleQuestionReceived(...S),v=(...S)=>t.questionSessionEnded(...S);e.on("SessionOutput",s),e.on("SessionCost",o),e.on("SessionCompleted",r),e.on("ContainerStatus",i),e.on("SlaveCapacityUpdate",a),e.on("PreviewBuildStatus",c),e.on("PreviewReload",l),e.on("QuestionsReceived",p),e.on("SpecificationUpdated",u),e.on("SingleQuestionReceived",d),e.on("QuestionSessionEnded",v);let w=[{methodName:"SessionOutput",method:s},{methodName:"SessionCost",method:o},{methodName:"SessionCompleted",method:r},{methodName:"ContainerStatus",method:i},{methodName:"SlaveCapacityUpdate",method:a},{methodName:"PreviewBuildStatus",method:c},{methodName:"PreviewReload",method:l},{methodName:"QuestionsReceived",method:p},{methodName:"SpecificationUpdated",method:u},{methodName:"SingleQuestionReceived",method:d},{methodName:"QuestionSessionEnded",method:v}];return new z(e,w)}},ct=class n{static Instance=new n;constructor(){}register=(e,t)=>{let s=(...i)=>t.startContainer(...i),o=(...i)=>t.terminateContainer(...i);e.on("StartContainer",s),e.on("TerminateContainer",o);let r=[{methodName:"StartContainer",method:s},{methodName:"TerminateContainer",method:o}];return new z(e,r)}},lt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let s=(...l)=>t.testRunCreated(...l),o=(...l)=>t.testRunUpdated(...l),r=(...l)=>t.testSuiteCreated(...l),i=(...l)=>t.testCreated(...l),a=(...l)=>t.testUpdated(...l);e.on("TestRunCreated",s),e.on("TestRunUpdated",o),e.on("TestSuiteCreated",r),e.on("TestCreated",i),e.on("TestUpdated",a);let c=[{methodName:"TestRunCreated",method:s},{methodName:"TestRunUpdated",method:o},{methodName:"TestSuiteCreated",method:r},{methodName:"TestCreated",method:i},{methodName:"TestUpdated",method:a}];return new z(e,c)}};var De=class{constructor(e){this.config=e;this.connection=new ne.HubConnectionBuilder().withUrl(`${e.masterUrl}/api/hubs/agent`,{headers:{"X-Container-Id":e.containerId,"X-Project-Key":e.projectKey}}).withAutomaticReconnect({nextRetryDelayInMilliseconds:t=>{let s=t.previousRetryCount+1,o=Math.min(1e3*Math.pow(2,t.previousRetryCount),6e4);return console.log(`[SignalR] Reconnect attempt ${s} (will retry in ${o}ms)`),o}}).withServerTimeout(3e5).withKeepAliveInterval(1e4).configureLogging(ne.LogLevel.Information).build(),this.hubProxy=Nt("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,s,o){if(this.currentStatus=e,s&&(this.currentSessionId=s),o&&(this.currentProjectId=o),this.connection.state===ne.HubConnectionState.Connected)try{let r={status:e,error:t,sessionId:s,projectId:o,tunnelUrl:this.tunnelUrl??void 0};await this.hubProxy.reportStatus(this.config.containerId,r),console.log(`[Status] ${e}${t?` (${t})`:""}${this.tunnelUrl?` [${this.tunnelUrl}]`:""}${s?` [session: ${s}]`:""}`)}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 je=class{constructor(e,t){this.hubProxy=e;this.projectId=t}async saveSpecification(e,t){let s=Cn(t),o=await this.hubProxy.saveSpecification(this.projectId,e,s,t);if(!o.success)throw new Error(o.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 wn,execSync as Pn,spawn as _n}from"child_process";import{promisify as Tn}from"util";import{createHash as Lt}from"crypto";import*as C from"fs";import*as N from"path";var Oe=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("."),s=t.length>2?t.slice(-2).join("."):this.domain;return`${e}.${s}`}async addRoute(e,t){let s=this.buildHostname(e);if(console.log(`[TunnelManager] Adding route: ${s} -> localhost:${t}`),!this.isConfigured())return console.log("[TunnelManager] Not configured, skipping route addition"),`https://${s}`;try{let o=await this.getConfig(),r=o.ingress.findIndex(a=>a.hostname===s);if(r!==-1)o.ingress[r].service=`http://localhost:${t}`;else{let a=o.ingress.findIndex(l=>!l.hostname),c={hostname:s,service:`http://localhost:${t}`};a!==-1?o.ingress.splice(a,0,c):(o.ingress.push(c),o.ingress.push({service:"http_status:404"}))}await this.putConfig(o),await this.ensureDnsPointsToTunnel(s);let i=`https://${s}`;return console.log(`[TunnelManager] \u2713 Route added: ${i}`),i}catch(o){throw console.error("[TunnelManager] Failed to add route:",o),o}}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 s=await this.getConfig();s.ingress=s.ingress.filter(o=>o.hostname!==t),s.ingress.some(o=>!o.hostname)||s.ingress.push({service:"http_status:404"}),await this.putConfig(s),console.log(`[TunnelManager] \u2713 Route removed: ${t}`)}catch(s){throw console.error("[TunnelManager] Failed to remove route:",s),s}}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(s=>s.message).join(", ")}`);return t.result?.config||{ingress:[{service:"http_status:404"}]}}async putConfig(e){let s=await(await fetch(this.tunnelApiBase,{method:"PUT",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({config:e})})).json();if(!s.success)throw new Error(`Cloudflare API error: ${s.errors.map(o=>o.message).join(", ")}`);console.log("[TunnelManager] Configuration updated via Cloudflare API")}async ensureDnsPointsToTunnel(e){let t=`${this.tunnelId}.cfargotunnel.com`,s=`https://api.cloudflare.com/client/v4/zones/${this.zoneId}/dns_records`;try{let r=await(await fetch(`${s}?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(`${s}/${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(o){console.error(`[TunnelManager] Error updating DNS for ${e}:`,o)}}};var $t=Tn(wn),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,s)=>{let o=_n(n,[],{shell:!0,stdio:"inherit",cwd:e.cwd}),r=null;e.timeout&&(r=setTimeout(()=>{o.kill(),s(new Error(`Command timed out: ${n}`))},e.timeout)),o.on("close",i=>{r&&clearTimeout(r),i===0?t():s(new Error(`Command failed with code ${i}: ${n}`))}),o.on("error",i=>{r&&clearTimeout(r),s(i)})})}var Ht="main",Me=class{constructor(e,t,s,o){this.connection=e;this.serviceManager=t;this.workspaceDir=s;this.gitHubPat=o}currentBranchName=Ht;currentRepoUrl="";lastLockfileHash=null;tunnelManager=null;currentPreviewSubdomain=null;getTunnelManager(){return this.tunnelManager||(this.tunnelManager=new Oe),this.tunnelManager}async prewarmWorkspace(){let e=new I("TOTAL INIT"),t=process.env.REPO_URL,s=process.env.BRANCH_NAME||Ht,o=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: ${s}`),console.log(`[Init] Preview: ${o||"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,s),this.currentRepoUrl=t,this.currentBranchName=s,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 p=new I("Start frontend (vite)");await this.serviceManager.startFrontend(),p.stop();let u=new I("Wait for health check");await this.serviceManager.waitForHealth(3e4),u.stop(),o&&r&&await this.setupPreviewSubdomainFromEnv(o,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),s=this.extractRepoPath(this.currentRepoUrl),o=t!==s,r=e.branchName!==this.currentBranchName;o||r?(console.log("[InitSession] Repo/branch mismatch - updating workspace..."),console.log(`[InitSession] Current: ${s}@${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 s=new I("Setup preview subdomain");console.log(`[Preview] Setting up preview subdomain: ${e} (${t})`);let o=this.getTunnelManager();if(!o){console.log("[Preview] TunnelManager not available, skipping subdomain setup"),s.stop();return}if(!o.isConfigured()){console.log("[Preview] Cloudflare not configured, skipping subdomain setup"),s.stop();return}try{let r=parseInt(process.env.ALLOCATED_HOST_PORT||"5173"),i=await o.addRoute(e,r);this.currentPreviewSubdomain=e,this.connection.setTunnelUrl(i),s.stop(),console.log(`[Preview] \u2713 Preview subdomain configured: ${i}`)}catch(r){s.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 s=this.extractRepoPath(e.repoUrl),o=this.extractRepoPath(this.currentRepoUrl);if(s!==o){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 C.existsSync(e)?Lt("sha256").update(C.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:s}=await $t(`git diff ${e}..HEAD --name-only`,{cwd:this.workspaceDir,timeout:3e4}),o=s.trim();if(!o)return console.log(`[CodeDiff] No file differences between ${e} and ${t}`),!1;let r=o.split(`
1226
- `).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(s){return console.error("[CodeDiff] Failed to check diff:",s instanceof Error?s.message:s),!0}}async installDependencies(){let e=new I("TOTAL INSTALL DEPENDENCIES");if(C.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(C.existsSync(N.join(this.workspaceDir,"packages/backoffice-web/package.json"))){let t=N.join(this.workspaceDir,"packages/backoffice-web"),s=N.join(t,"node_modules"),o=N.join(t,"package-lock.json"),r=N.join(s,".lockhash"),i="/opt/cache";C.mkdirSync(i,{recursive:!0});let a=C.existsSync(o)?Lt("sha256").update(C.readFileSync(o)).digest("hex"):null,c=C.existsSync(r)?C.readFileSync(r,"utf-8").trim():null,l=N.join(i,"node_modules","current_hash"),p=C.existsSync(l)?C.readFileSync(l,"utf-8").trim():null,u=p?N.join(i,"node_modules",p):null,d=a?N.join(i,"node_modules",a):null;if(console.log(`[Install] node_modules exists: ${C.existsSync(s)}`),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): ${p?.substring(0,8)||"null"}`),console.log(`[Install] exactMatchCache exists: ${d?C.existsSync(d):!1}`),console.log(`[Install] dockerCachedNodeModules exists: ${u?C.existsSync(u):!1}`),!C.existsSync(s)){if(d&&C.existsSync(d)){let w=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 "${d}" "${s}"`),a&&C.writeFileSync(r,a),c=a,w.stop()}else if(u&&C.existsSync(u)){let w=new I("Copy Docker-cached node_modules (as base)");console.log(`[Install] Copying Docker-cached node_modules as base (hash ${p?.substring(0,8)}, need ${a?.substring(0,8)})...`),await U(`cp -r "${u}" "${s}"`),w.stop()}}let v=!C.existsSync(s)||a&&a!==c;if(console.log(`[Install] needsInstall: ${v}`),!v)console.log("[Install] npm install skipped (cache OK)");else{let w=new I("npm install --prefer-offline");await U("npm install --prefer-offline --no-progress --fund=false",{cwd:t,timeout:18e4}),w.stop(),a&&(C.mkdirSync(s,{recursive:!0}),C.writeFileSync(r,a))}this.lastLockfileHash=a}e.stop()}};function Bt(n,e,t){let s=n.getHubProxy(),o=process.env.CONTAINER_ID||"unknown";return{onAssistantText:async r=>{let i={type:"Text",content:r,isError:!1};await s.reportSessionOutput(o,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 s.reportSessionOutput(o,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 s.reportSessionOutput(o,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 s.reportSessionOutput(o,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 s.reportSessionOutput(o,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 s.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:Rn(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};s.reportSessionCost(o,e,a).catch(c=>{console.error("Failed to report cost:",c)})}}}}var Ue={inputPerMillion:3,outputPerMillion:15,cacheReadPerMillion:.3,cacheCreationPerMillion:3.75};function Rn(n){return(n.input_tokens||0)/1e6*Ue.inputPerMillion+(n.output_tokens||0)/1e6*Ue.outputPerMillion+(n.cache_read_input_tokens||0)/1e6*Ue.cacheReadPerMillion+(n.cache_creation_input_tokens||0)/1e6*Ue.cacheCreationPerMillion}import*as oe from"fs";import*as Kt from"path";function qt(n,e){let t=Kt.join(n,`.agent-questions-${e}.json`);if(!oe.existsSync(t))return null;try{let s=oe.readFileSync(t,"utf-8");return oe.unlinkSync(t),JSON.parse(s)}catch(s){return console.error("Failed to read questions file:",s),null}}async function Gt(n,e,t,s){if(e.size===0)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let o=s?$(s):`${t}/packages/dotnet-api`,r=s?H(s):`${t}/packages/backoffice-web`,i=s?.services?.backend?.extensions??[".cs",".csproj"],a=s?.services?.frontend?.extensions??[".ts",".tsx"],c=o&&[...e].some(d=>d.includes(o)||i.some(v=>d.endsWith(v))),l=r&&[...e].some(d=>d.includes(r)||a.some(v=>d.endsWith(v)));if(!c&&!l)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let p=[];c&&p.push("backend"),l&&p.push("frontend"),console.log(`[TypeCheck] Checking ${p.join(" & ")}...`);let u=[];if(c){let d=await n.checkBackendBuild();!d.success&&d.errors&&u.push(`## Backend Build Errors (in ${o})
1253
+ ${E.join(`
1254
+ `)}`:"";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||!R.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||!R.existsSync(V.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"}}},Jt=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(!R.existsSync(S))return[`Log file not found: ${S}`];try{return ee(`tail -n ${m} ${S}`,{encoding:"utf-8"}).split(`
1255
+ `).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:Jt,tailLogs:St,checkSwaggerEndpoints:async y=>{let m=V.join(e,"swagger.json");if(!R.existsSync(m))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let S=JSON.parse(R.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)}},Nt=n=>{if(n==="IDeploymentHub")return ze.Instance;if(n==="IKanbanHub")return Ve.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 Je(e)},Je=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)},Ve=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=(...i)=>t.startContainer(...i),s=(...i)=>t.terminateContainer(...i);e.on("StartContainer",o),e.on("TerminateContainer",s);let r=[{methodName:"StartContainer",method:o},{methodName:"TerminateContainer",method:s}];return new X(e,r)}},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=Nt("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 Tn}from"child_process";import{promisify as Rn}from"util";import{createHash as Lt}from"crypto";import*as k from"fs";import*as M 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=Rn(_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=Tn(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",Me=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=M.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(`
1256
+ `).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(M.join(this.workspaceDir,"packages/dotnet-api"))){let t=new I("dotnet restore");await U("dotnet restore",{cwd:M.join(this.workspaceDir,"packages/dotnet-api"),timeout:12e4}),t.stop()}if(k.existsSync(M.join(this.workspaceDir,"packages/backoffice-web/package.json"))){let t=M.join(this.workspaceDir,"packages/backoffice-web"),o=M.join(t,"node_modules"),s=M.join(t,"package-lock.json"),r=M.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=M.join(i,"node_modules","current_hash"),d=k.existsSync(l)?k.readFileSync(l,"utf-8").trim():null,u=d?M.join(i,"node_modules",d):null,g=a?M.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 Ne={inputPerMillion:3,outputPerMillion:15,cacheReadPerMillion:.3,cacheCreationPerMillion:3.75};function In(n){return(n.input_tokens||0)/1e6*Ne.inputPerMillion+(n.output_tokens||0)/1e6*Ne.outputPerMillion+(n.cache_read_input_tokens||0)/1e6*Ne.cacheReadPerMillion+(n.cache_creation_input_tokens||0)/1e6*Ne.cacheCreationPerMillion}import*as re from"fs";import*as Kt from"path";function qt(n,e){let t=Kt.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 Gt(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})
1227
1257
  \`\`\`
1228
- ${d.errors}
1229
- \`\`\``)}if(l){let d=await n.checkFrontendTypes();!d.success&&d.errors&&u.push(`## Frontend Type Errors (in ${r})
1258
+ ${g.errors}
1259
+ \`\`\``)}if(l){let g=await n.checkFrontendTypes();!g.success&&g.errors&&u.push(`## Frontend Type Errors (in ${r})
1230
1260
  \`\`\`
1231
- ${d.errors}
1261
+ ${g.errors}
1232
1262
  \`\`\``)}return u.length>0?(console.log("[TypeCheck] \u274C Type errors found - will auto-correct"),{passed:!1,errorPrompt:`@debugger Type/build errors detected. Fix them.
1233
1263
 
1234
1264
  WORKSPACE: ${t}
@@ -1237,24 +1267,24 @@ ${u.join(`
1237
1267
 
1238
1268
  `)}
1239
1269
 
1240
- 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,s,o){if(!await n.hasChanges()){console.log("[Git] No changes to commit");return}console.log("[Git] Committing changes...");let i=await n.commitAndPush(s,o);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 Ne=class{constructor(e,t,s,o,r=!0,i=!0,a,c,l=!0){this.connection=e;this.lifecycle=t;this.serviceManager=s;this.workspaceDir=o;this.enableAutoTypecheck=r;this.enableAutoCommit=i;this.setupPlanningTransport=a;this.projectConfig=c;this.useDefaultStack=l;this.gitManager=xe(o)}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,s,o=!1){let r=ce(t);if(console.log(`[Receiver] RunPrompt for session ${e}${s?` (ClaudeSessionId: ${s})`:""}${o?" [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(s&&i!==s){console.warn(`[Receiver] Claude session not found in container: ${s}`);let l=this.connection.getHubProxy();await l.reportSessionOutput(process.env.CONTAINER_ID||"unknown",e,{type:"System",event:"sessionNotFound",content:s,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=o,this.currentAbortController=new AbortController;let a=i||null,c=new Set;try{let l=await Ee(r,{model:this.currentSession.model,sessionId:a||null,projectId:this.currentSession.projectId||null,apiUrl:this.connection.getMasterUrl(),debugLog:!0,debugMode:o,abortController:this.currentAbortController,projectConfig:this.projectConfig,useDefaultStack:this.useDefaultStack,callbacks:Bt(this.connection,e,{onClaudeSessionId:d=>{this.claudeSessionIdMap.set(e,d)},onFileModified:d=>{c.add(d)}})}),p=qt(this.workspaceDir,e);if(p&&p.length>0){console.log(`[Receiver] ${p.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 d=await Gt(this.serviceManager,c,this.workspaceDir,this.projectConfig);if(!d.passed&&d.errorPrompt){console.log("[Receiver] Type errors detected, auto-correcting..."),await this.runPrompt(e,d.errorPrompt,s);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 p=l instanceof Error&&l.name==="AbortError",u=l instanceof Error?l.message:"Unknown error";p?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,p?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)`),Ge(e)}async fetchProjectPrompt(e){try{let t=this.connection.getMasterUrl(),s=this.connection.getProjectKey(),o=await fetch(`${t}/api/projects/${e}/prompt`,{method:"GET",headers:{"Content-Type":"application/json","X-Project-Key":s}});if(!o.ok){console.log(`[Receiver] Failed to fetch project prompt: ${o.status}`);return}let r=await o.json();r.prompt?(Ge(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";function In(n){let{useDefaultStack:e,projectConfig:t,configSource:s,tunnelPort:o,enableAutoTypecheck:r,enableAutoCommit:i}=n;if(console.log(`
1241
- \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: ${s.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: ${o}`.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
1242
- `)}var En=".sdd/local-signalr-config.json";function dt(n){return pe.join(n,En)}function An(n){try{let e=dt(n);if(!K.existsSync(e))return null;let t=K.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return null}}function pt(n,e){let t=pe.dirname(dt(n));K.existsSync(t)||K.mkdirSync(t,{recursive:!0}),K.writeFileSync(dt(n),JSON.stringify(e,null,2))}function Fe(){return`local-${Yt.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${Date.now().toString(36)}`}function xn(n){return!!(n.version||n.paths||n.services||n.techStack)}function Dn(n,e){return xn(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 jn(n){console.log(`
1270
+ 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=qt(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 Gt(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(N.existsSync(e)){let t=JSON.parse(N.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(`
1271
+ \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
1272
+ `)}var Dn=".sdd/local-signalr-config.json";function gt(n){return Y.join(n,Dn)}function jn(n){try{let e=gt(n);if(!N.existsSync(e))return null;let t=N.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return null}}function dt(n,e){let t=Y.dirname(gt(n));N.existsSync(t)||N.mkdirSync(t,{recursive:!0}),N.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 Mn(n){console.log(`
1243
1273
  \u{1F527} Interactive Project Config Builder
1244
1274
  `),console.log(" Answer the following questions to configure your project."),console.log(` Press Enter to skip optional questions.
1245
- `);let{projectType:e}=await B.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(`
1246
- \u{1F4F1} Frontend Configuration:`);let o=await B.prompt([{type:"input",name:"path",message:"Frontend path (relative to workspace):",default:e==="fullstack"?"./frontend":"."},{type:"number",name:"port",message:"Dev server port:",default:3e3},{type:"input",name:"startCommand",message:"Start command (or Enter to skip):",default:"npm run dev"},{type:"input",name:"typecheckCommand",message:"Typecheck command (or Enter to skip):",default:"npx tsc --noEmit"},{type:"input",name:"extensions",message:"File extensions (comma-separated):",default:".ts,.tsx,.js,.jsx"}]);t.paths={workspace:t.paths?.workspace||".",...t.paths,frontend:o.path||"."},t.services={...t.services,frontend:{port:o.port||3e3,startCommand:o.startCommand||"npm run dev",typecheckCommand:o.typecheckCommand||void 0,extensions:o.extensions?o.extensions.split(",").map(r=>r.trim()):[".ts",".tsx",".js",".jsx"]}},t.tunnel={enabled:!0,port:o.port||3e3}}if(e==="backend"||e==="fullstack"){console.log(`
1247
- \u2699\uFE0F Backend Configuration:`);let o=await B.prompt([{type:"input",name:"path",message:"Backend path (relative to workspace):",default:e==="fullstack"?"./backend":"."},{type:"number",name:"port",message:"API server port:",default:8080},{type:"input",name:"startCommand",message:"Start command (or Enter to skip):",default:""},{type:"input",name:"buildCommand",message:"Build command (or Enter to skip):",default:""},{type:"input",name:"extensions",message:"File extensions (comma-separated):",default:".ts,.js"}]);t.paths={workspace:t.paths?.workspace||".",...t.paths,backend:o.path||"."},t.services={...t.services,backend:{port:o.port||8080,startCommand:o.startCommand||void 0,buildCommand:o.buildCommand||void 0,extensions:o.extensions?o.extensions.split(",").map(r=>r.trim()):[".ts",".js"]}},e==="backend"&&(t.tunnel={enabled:!0,port:o.port||8080})}console.log(`
1248
- \u{1F4DA} Tech Stack (optional - helps AI understand your project):`);let{addTechStack:s}=await B.prompt([{type:"confirm",name:"addTechStack",message:"Add tech stack info?",default:!1}]);if(s){let o=await B.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={},o.frontend&&(t.techStack.frontend=o.frontend.split(",").map(r=>r.trim())),o.backend&&(t.techStack.backend=o.backend.split(",").map(r=>r.trim())),o.patterns&&(t.techStack.patterns=o.patterns.split(",").map(r=>r.trim()))}return t}function Wt(n){try{let e={},t=n.match(/MASTER_URL=(\S+)/);t&&(e.masterUrl=t[1].trim());let s=n.match(/PROJECT_ID=(\S+)/);s&&(e.projectId=s[1].trim());let o=n.match(/PROJECT_KEY=(\S+)/);return o&&(e.projectKey=o[1].trim()),e.masterUrl&&e.projectId&&e.projectKey?e:null}catch{return null}}async function On(n){let e=An(n);console.log(`
1275
+ `);let{projectType:e}=await K.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(`
1276
+ \u{1F4F1} Frontend Configuration:`);let s=await K.prompt([{type:"input",name:"path",message:"Frontend path (relative to workspace):",default:e==="fullstack"?"./frontend":"."},{type:"number",name:"port",message:"Dev server port:",default:3e3},{type:"input",name:"startCommand",message:"Start command (or Enter to skip):",default:"npm run dev"},{type:"input",name:"typecheckCommand",message:"Typecheck command (or Enter to skip):",default:"npx tsc --noEmit"},{type:"input",name:"extensions",message:"File extensions (comma-separated):",default:".ts,.tsx,.js,.jsx"}]);t.paths={workspace:t.paths?.workspace||".",...t.paths,frontend:s.path||"."},t.services={...t.services,frontend:{port:s.port||3e3,startCommand:s.startCommand||"npm run dev",typecheckCommand:s.typecheckCommand||void 0,extensions:s.extensions?s.extensions.split(",").map(r=>r.trim()):[".ts",".tsx",".js",".jsx"]}},t.tunnel={enabled:!0,port:s.port||3e3}}if(e==="backend"||e==="fullstack"){console.log(`
1277
+ \u2699\uFE0F Backend Configuration:`);let s=await K.prompt([{type:"input",name:"path",message:"Backend path (relative to workspace):",default:e==="fullstack"?"./backend":"."},{type:"number",name:"port",message:"API server port:",default:8080},{type:"input",name:"startCommand",message:"Start command (or Enter to skip):",default:""},{type:"input",name:"buildCommand",message:"Build command (or Enter to skip):",default:""},{type:"input",name:"extensions",message:"File extensions (comma-separated):",default:".ts,.js"}]);t.paths={workspace:t.paths?.workspace||".",...t.paths,backend:s.path||"."},t.services={...t.services,backend:{port:s.port||8080,startCommand:s.startCommand||void 0,buildCommand:s.buildCommand||void 0,extensions:s.extensions?s.extensions.split(",").map(r=>r.trim()):[".ts",".js"]}},e==="backend"&&(t.tunnel={enabled:!0,port:s.port||8080})}console.log(`
1278
+ \u{1F4DA} Tech Stack (optional - helps AI understand your project):`);let{addTechStack:o}=await K.prompt([{type:"confirm",name:"addTechStack",message:"Add tech stack info?",default:!1}]);if(o){let s=await K.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 Nn(n){let e=jn(n);console.log(`
1249
1279
  \u{1F3E0} Local SignalR Mode - Jack into the real system
1250
- `);let t=e?.masterUrl&&e?.projectId&&e?.projectKey,{configMethod:s}=await B.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(s==="saved"&&t){let a=Dn(e,n),c=a?.tunnel?.port||e.tunnelPort||5173;return console.log(`
1251
- \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||Fe(),enableTunnel:e.enableTunnel!==!1,tunnelPort:c,embeddedProjectConfig:a}}if(s==="paste"){let{pastedConfig:a}=await B.prompt([{type:"input",name:"pastedConfig",message:"Paste the config (MASTER_URL=... PROJECT_ID=... PROJECT_KEY=...):",validate:d=>Wt(d)?!0:"Could not parse config. Make sure it contains MASTER_URL=..., PROJECT_ID=..., and PROJECT_KEY=..."}]),c=Wt(a),l=e?.containerId||Fe(),p=e?.tunnelPort||5173,u={masterUrl:c.masterUrl,projectId:c.projectId,projectKey:c.projectKey,containerId:l,enableTunnel:!0,tunnelPort:p};return console.log(`
1252
- \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 ${p})`),pt(n,u),{...u,embeddedProjectConfig:null}}if(s==="interactive"){console.log(`
1253
- \u{1F50C} Connection Configuration:`);let a=await B.prompt([{type:"input",name:"masterUrl",message:"Master URL (backend):",default:e?.masterUrl||process.env.MASTER_URL||"http://localhost:5338",validate:d=>{if(!d.trim())return"Master URL is required";try{return new URL(d),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:e?.projectId||process.env.PROJECT_ID,validate:d=>d.trim()?!0:"Project ID is required"},{type:"password",name:"projectKey",message:"Project Key (pk_proj_...):",mask:"*",default:e?.projectKey||process.env.PROJECT_KEY,validate:d=>d.trim()?d.startsWith("pk_proj_")?!0:"Project Key must start with pk_proj_":"Project Key is required"}]),c=await jn(n),l=e?.containerId||Fe(),p=c?.tunnel?.port||e?.tunnelPort||5173,u={masterUrl:a.masterUrl,projectId:a.projectId,projectKey:a.projectKey,containerId:l,enableTunnel:!0,tunnelPort:p,...c&&{version:c.version,paths:c.paths,services:c.services,techStack:c.techStack,tunnel:c.tunnel}};return console.log(`
1254
- \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 ${p})`),console.log(c?" Project config: custom stack":" Project config: default .NET/React stack"),pt(n,u),{masterUrl:u.masterUrl,projectId:u.projectId,projectKey:u.projectKey,containerId:u.containerId,enableTunnel:u.enableTunnel,tunnelPort:u.tunnelPort,embeddedProjectConfig:c}}let o=await B.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||Fe()},{type:"confirm",name:"enableTunnel",message:"Enable preview URL reporting (report local dev server URL to backend)?",default:e?.enableTunnel!==!1}]),r=e?.tunnelPort||5173;o.enableTunnel&&(r=(await B.prompt([{type:"number",name:"tunnelPort",message:"Preview port (local dev server port to report):",default:r}])).tunnelPort||r);let i={...o,tunnelPort:r};return pt(n,i),{...i,embeddedProjectConfig:null}}async function Mn(){let n=process.env.LOCAL_MODE==="true",e=process.env.WORKSPACE_DIR||process.cwd();console.log(n?"=== Local SignalR Agent ===":"=== SignalR Agent Starting ==="),console.log(`\u{1F4C1} Workspace: ${e}`),n&&!K.existsSync(pe.join(e,".git"))&&console.warn("\u26A0\uFE0F Warning: Not a git repository. Some features may not work.");let t,s,o=!1,r=5173,i=null;if(n){let g=await On(e);t={masterUrl:g.masterUrl,containerId:g.containerId,projectKey:g.projectKey,workspaceDir:e},s=g.projectId,o=g.enableTunnel,r=g.tunnelPort,i=g.embeddedProjectConfig,process.env.PROJECT_ID=g.projectId,process.env.CONTAINER_ID=g.containerId,process.env.PROJECT_KEY=g.projectKey}else{let g=process.env.PROJECT_KEY;g||(console.error("\u274C PROJECT_KEY environment variable is required"),process.exit(1)),t={masterUrl:process.env.MASTER_URL||"http://host.docker.internal:5338",containerId:process.env.CONTAINER_ID||"unknown",projectKey:g,workspaceDir:e}}console.log(`
1255
- \u{1F50C} Connecting to backend...`),console.log(` Container ID: ${t.containerId}`),console.log(` Project Key: ${t.projectKey.substring(0,16)}...`),console.log(` Gateway URL: ${t.masterUrl}`);let a=new De(t),c=0,l=10;for(;c<l;)try{await a.connect();break}catch(g){if(c++,console.error(`Connection attempt ${c} failed:`,g instanceof Error?g.message:g),c<l){let h=Math.min(1e3*Math.pow(2,c),3e4);await new Promise(E=>setTimeout(E,h))}}c>=l&&(console.error("\u274C Failed to connect to gateway after max retries"),process.exit(1)),n&&console.log("\u2705 Connected to backend");let p=await a.getSecrets();n?(console.log("\u2705 Handshake complete"),p.gitHubPat&&console.log(" GitHub PAT received from backend")):console.log("\u2705 Secrets received");let u,d=!0,v="default";if(n){if(i)u=i,v="embedded in local-signalr-config.json",d=!1,console.log("[Config] Using embedded project config from local-signalr-config.json");else{let g=await we({workspaceDir:t.workspaceDir});u=g.config,v=g.source==="file"?g.configPath||".sdd/project-config.json":"default (no config file)",g.source==="file"&&(d=!1)}d||u.tunnel?.port&&(r=u.tunnel.port)}let w=Ae({workspaceDir:t.workspaceDir,projectConfig:u});me(w);let S=ie(t.workspaceDir);se(S);let O=g=>{console.log(`[Planning] Setting up SignalR transport for project ${g}`),S=new je(a.getHubProxy(),g),se(S)};n&&s&&O(s);let j;n?j={prewarmWorkspace:async()=>{console.log("[LocalMode] Skipping prewarm - using existing workspace")},handleInitSession:async g=>{console.log(`[LocalMode] Session init for ${g.sessionId}`),console.log("[LocalMode] Skipping branch switch - using current branch"),await a.reportStatus("Ready",void 0,g.sessionId,g.projectId),await a.getHubProxy().initSessionCompleted(t.containerId,g.sessionId,!0,"")}}:j=new Me(a,w,t.workspaceDir,p.gitHubPat||null);let f=process.env.DISABLE_AUTO_TYPECHECK!=="true",M=process.env.DISABLE_AUTO_COMMIT!=="true"&&!n;n&&u?In({useDefaultStack:d,projectConfig:u,configSource:v,tunnelPort:r,enableAutoTypecheck:f,enableAutoCommit:M}):(console.log(`[Config] Auto-typecheck: ${f?"enabled":"disabled"}`),console.log(`[Config] Auto-commit: ${M?"enabled":"disabled"}`));let V=new Ne(a,j,w,t.workspaceDir,f,M,O,u,d);if(a.registerReceiver(V),n){if(o){let g=`http://localhost:${r}`;a.setTunnelUrl(g),console.log(`
1256
- \u{1F310} Preview available at: ${g}`)}await a.reportStatus("Ready",void 0,void 0,s),console.log(`
1280
+ `);let t=e?.masterUrl&&e?.projectId&&e?.projectKey,{configMethod:o}=await K.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(`
1281
+ \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 K.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(`
1282
+ \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(`
1283
+ \u{1F50C} Connection Configuration:`);let a=await K.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 Mn(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(`
1284
+ \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 K.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 K.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&&!N.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 Nn(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(`
1285
+ \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 Me(c,b,o.workspaceDir,u.gitHubPat||null);let O=process.env.DISABLE_AUTO_TYPECHECK!=="true",q=process.env.DISABLE_AUTO_COMMIT!=="true"&&!n;n&&g?xn({useDefaultStack:w,projectConfig:g,configSource:v,tunnelPort:i,enableAutoTypecheck:O,enableAutoCommit:q}):(console.log(`[Config] Auto-typecheck: ${O?"enabled":"disabled"}`),console.log(`[Config] Auto-commit: ${q?"enabled":"disabled"}`));let F=new Fe(c,f,b,o.workspaceDir,O,q,j,g,w);if(c.registerReceiver(F),n){if(r){let p=`http://localhost:${i}`;c.setTunnelUrl(p),console.log(`
1286
+ \u{1F310} Preview available at: ${p}`)}await c.reportStatus("Ready",void 0,void 0,s),console.log(`
1257
1287
  \u2705 Agent ready and waiting for commands`),console.log(` Open the frontend and select this agent to start working
1258
1288
  `),console.log(` Press Ctrl+C to disconnect
1259
1289
  `),process.on("SIGINT",async()=>{console.log(`
1260
- \u{1F44B} Disconnecting...`),await a.reportStatus("Error"),process.exit(0)})}else{let g=process.env.SESSION_ID,h=process.env.PROJECT_ID;await a.reportStatus("Initiating",void 0,g,h),await j.prewarmWorkspace(),await a.reportStatus("WarmedUp",void 0,g,h),console.log("\u2705 Agent initialized, waiting for InitSession"),console.log(` Session: ${g||"none"}`),console.log(` Repo: ${process.env.REPO_URL?"configured":"NOT SET"}`)}}import.meta.url===`file://${process.argv[1]}`&&Mn().catch(n=>{console.error("Fatal error:",n),process.exit(1)});export{Mn as startSignalRAgent};
1290
+ \u{1F44B} Disconnecting...`),await c.reportStatus("Error"),process.exit(0)})}else{let p=process.env.SESSION_ID,C=process.env.PROJECT_ID;await c.reportStatus("Initiating",void 0,p,C),await f.prewarmWorkspace(),await c.reportStatus("WarmedUp",void 0,p,C),console.log("\u2705 Agent initialized, waiting for InitSession"),console.log(` Session: ${p||"none"}`),console.log(` Repo: ${process.env.REPO_URL?"configured":"NOT SET"}`)}}import.meta.url===`file://${process.argv[1]}`&&Fn().catch(n=>{console.error("Fatal error:",n),process.exit(1)});export{Fn as startSignalRAgent};