glenn-code 1.0.0

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 ADDED
@@ -0,0 +1,1260 @@
1
+ // Glenn Code - Bundled and minified
2
+ // (c) DNM Lab - All rights reserved
3
+
4
+ import B from"inquirer";import*as K from"fs";import*as le from"path";import*as Gt from"os";import{createSdkMcpServer as zt}from"@anthropic-ai/claude-agent-sdk";import{tool as pe}from"@anthropic-ai/claude-agent-sdk";import{z as ue}from"zod";import*as de from"fs";import*as bt from"path";var q=null;function se(n){q=n}function $e(n){return n.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}var Nn=pe("save_specification",`Save a specification to the database.
5
+
6
+ IMPORTANT: First write the specification content to a file using the Write tool, then call this with the file path.
7
+
8
+ Workflow:
9
+ 1. Use Write tool to save your spec to a temp file (e.g., /tmp/my-spec.md)
10
+ 2. Call this tool with the name and file path
11
+ 3. This tool reads the file and saves to database
12
+
13
+ Example:
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:ue.string().describe("Specification name (e.g., 'User Authentication')"),filePath:ue.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=bt.resolve(n.filePath);if(!de.existsSync(e))return{content:[{type:"text",text:`\u274C File not found: ${n.filePath}. Use Write tool first to create the file.`}]};let t=de.readFileSync(e,"utf-8"),s=$e(n.name);return await q.saveSpecification(s,t),{content:[{type:"text",text:`\u2705 Saved specification: ${s}.spec.md`}]}}),Fn=pe("read_specification","Read the content of a specification.",{name:ue.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.`}]}}),Ln=pe("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:
16
+
17
+ ${n.map(t=>`- ${t.name} (${t.slug}.spec.md)`).join(`
18
+ `)}`}]}}),$n=pe("delete_specification","Delete a specification.",{name:ue.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 ge(n){T=n}var St=Y("restart_services",`Rebuild and restart services. Call this after making backend changes (.cs files) to apply them.
19
+
20
+ Parameters:
21
+ - target: "backend" (default) | "frontend" | "both"
22
+
23
+ Use target="backend" after .cs file changes (faster, only restarts .NET).
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(`
27
+
28
+ `)}`}]}}return{content:[{type:"text",text:`\u2705 ${e==="both"?"Backend and frontend":e==="backend"?"Backend":"Frontend"} restarted successfully.`}]}}),kt=Y("stop_services",`Stop running services. Call this BEFORE running scaffold to prevent port conflicts and ensure clean migrations.
29
+
30
+ Parameters:
31
+ - target: "backend" (default) | "frontend" | "both"
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.`}]}}),vt=Y("start_services",`Start services. Call this AFTER running scaffold to bring services back up.
34
+
35
+ Parameters:
36
+ - target: "backend" (default) | "frontend" | "both"
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.`}]}}),Ct=Y("check_service_health",`Check if services are running and healthy. Returns current status of backend API and frontend.
39
+ Note: This only checks if ports are responding, NOT build/type errors. Use check_backend_build or check_frontend_types for that.`,{},async()=>{if(!T)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let n=await T.checkHealth();return{content:[{type:"text",text:`Service Health:
40
+ \u{1F5A5}\uFE0F Backend API: ${n.api?"\u2705 running":"\u274C not running"}
41
+ \u{1F310} Frontend: ${n.web?"\u2705 running":"\u274C not running"}`}]}}),wt=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:
42
+
43
+ ${n.errors}`}]}}),Pt=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:
44
+
45
+ ${n.errors}`}]}}),qn=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.
46
+
47
+ Parameters:
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:
50
+
51
+ ${e.join(`
52
+ `)}`}]}}),Gn=Y("check_swagger_endpoints",`Search for endpoints in the generated swagger.json. Use this to verify that new backend endpoints are correctly exposed.
53
+
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:
56
+
57
+ ${e.foundEndpoints.join(`
58
+ `)}`}]}});import*as re from"fs";import*as Jt from"path";import*as x from"fs";import*as fe from"path";function ie(n){let e=fe.join(n,".sdd","specifications");function t(){x.existsSync(e)||x.mkdirSync(e,{recursive:!0})}function s(o){return fe.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(fe.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 zt({name:"agent-insights",version:"1.0.0",tools:[St,kt,vt,Ct,wt,Pt]})}import{query as mn}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:
59
+ 1. Check existing features (quick ls)
60
+ 2. Write ALL entities to ONE /tmp/scaffold.json (big bang, no phasing!)
61
+ 3. Run scaffold CLI ONCE
62
+ 4. Restart backend
63
+ 5. STOP IMMEDIATELY
64
+
65
+ **\u26A1 ONE-SHOT RULE: Put ALL entities in ONE JSON. The CLI handles dependencies.**
66
+
67
+ ## WORKFLOW
68
+
69
+ ### Step 0: Check Existing Features (ALWAYS DO THIS FIRST)
70
+ \`\`\`bash
71
+ ls /workspace/packages/dotnet-api/Source/Features/
72
+ \`\`\`
73
+ This shows what already exists. **DO NOT scaffold entities that already exist!**
74
+ Common existing features: Users (auth), Authentication, KanbanBoard, Document, etc.
75
+
76
+ ### Step 1: Write Schema
77
+ \`\`\`bash
78
+ # Use Write tool to create /tmp/scaffold.json
79
+ \`\`\`
80
+
81
+ ### Step 2: Run Scaffold
82
+ \`\`\`bash
83
+ scaffold --schema /tmp/scaffold.json --output /workspace --force --full
84
+ \`\`\`
85
+
86
+ ### Step 3: Report & STOP
87
+ If CLI shows "success": true, respond with:
88
+ - Entities created: [names from output]
89
+ - Routes: /super-admin/[plural] for each
90
+ - DONE
91
+
92
+ **CRITICAL RULES:**
93
+ - DO NOT run ls, find, grep, or any verification commands
94
+ - DO NOT explore directories to check if files exist
95
+ - DO NOT read generated files to verify content
96
+ - dropdownDisplay MUST match actual fields on target entity:
97
+ - The scaffold CLI output is AUTHORITATIVE
98
+ - When scaffold succeeds, you are DONE - stop immediately
99
+
100
+ ## SCHEMA FORMAT
101
+
102
+ \`\`\`json
103
+ {
104
+ "entities": [
105
+ {
106
+ "name": "EntityName",
107
+ "icon": "Category",
108
+ "fields": [
109
+ { "name": "Name", "type": "string", "required": true, "listColumn": true }
110
+ ],
111
+ "relations": [],
112
+ "features": { "softDelete": true, "timestamps": true, "audit": true }
113
+ }
114
+ ]
115
+ }
116
+ \`\`\`
117
+
118
+ ## FIELD TYPES
119
+ - string (with optional maxLength)
120
+ - int, long, decimal
121
+ - bool
122
+ - DateTime, DateOnly
123
+ - Guid
124
+ - enum (requires enumValues: ["Value1", "Value2"])
125
+
126
+ ## RELATIONS
127
+ \`\`\`json
128
+ { "name": "Author", "type": "belongsTo", "target": "Author", "required": true, "dropdownDisplay": "Name", "listColumn": true }
129
+ \`\`\`
130
+ Types: belongsTo, hasMany, hasOne
131
+
132
+ **CRITICAL - dropdownDisplay MUST match actual fields on target entity:**
133
+ - If target has \`Name\` field \u2192 \`"dropdownDisplay": "Name"\`
134
+ - If target has \`FirstName\` + \`LastName\` \u2192 \`"dropdownDisplay": "FirstName LastName"\` (space-separated for multi-field)
135
+ - If target has \`Title\` field \u2192 \`"dropdownDisplay": "Title"\`
136
+ - User entity always has \`FullName\` \u2192 \`"dropdownDisplay": "FullName"\`
137
+
138
+ **DEFAULT BEHAVIOR:** If omitted, defaults to "Name" - will FAIL if target entity has no Name field!
139
+
140
+ User relations: \`{ "name": "Owner", "type": "belongsTo", "target": "User", "dropdownDisplay": "FullName" }\`
141
+
142
+ ## AUTO-GENERATED FIELDS (DO NOT DEFINE MANUALLY)
143
+ When features are enabled, these fields are auto-generated:
144
+ - \`timestamps: true\` \u2192 CreatedAt, UpdatedAt
145
+ - \`softDelete: true\` \u2192 IsDeleted, DeletedAt
146
+ - \`audit: true\` \u2192 CreatedByUserId
147
+
148
+ \u26A0\uFE0F DO NOT add these as manual fields - they will be filtered out!
149
+
150
+ ## RESERVED NAMES (DO NOT USE)
151
+ Task, File, Directory, String, Object, Type, Action, Event, Exception, List, Thread, Timer, Stream, DateTime, Guid, **User**
152
+
153
+ **\u26A0\uFE0F User entity already exists!** The system has ASP.NET Identity with ApplicationUser at \`Features/Users/\`.
154
+ - To relate to users: \`{ "target": "User", "dropdownDisplay": "FullName" }\`
155
+ - User has: Id (string), Email, FullName
156
+ - DON'T scaffold a new User entity - use the existing one!
157
+
158
+ ## ICONS
159
+ Person, Folder, Assignment, Inventory, MenuBook, Business, Category, Description, Email, Settings, Dashboard, ShoppingCart, AttachMoney, Receipt, Groups, ContactPhone
160
+
161
+ ## \u26A1 BIG BANG SCAFFOLDING (ONE-SHOT!)
162
+
163
+ **ALWAYS scaffold ALL entities in ONE JSON, ONE run.** The CLI handles dependencies automatically.
164
+ DO NOT phase/split into multiple runs - that's slower and error-prone.
165
+
166
+ ## EXAMPLE: University System (6 entities, circular refs, one-shot)
167
+
168
+ \`\`\`json
169
+ {
170
+ "entities": [
171
+ {
172
+ "name": "Department",
173
+ "icon": "Business",
174
+ "fields": [
175
+ { "name": "Name", "type": "string", "required": true, "listColumn": true },
176
+ { "name": "Budget", "type": "decimal", "required": false }
177
+ ],
178
+ "relations": [
179
+ { "name": "Head", "type": "belongsTo", "target": "Professor", "required": false, "dropdownDisplay": "Name" }
180
+ ],
181
+ "features": { "softDelete": true, "timestamps": true }
182
+ },
183
+ {
184
+ "name": "Professor",
185
+ "icon": "Person",
186
+ "fields": [
187
+ { "name": "Name", "type": "string", "required": true, "listColumn": true },
188
+ { "name": "Salary", "type": "decimal", "required": false }
189
+ ],
190
+ "relations": [
191
+ { "name": "Department", "type": "belongsTo", "target": "Department", "required": true, "dropdownDisplay": "Name", "listColumn": true }
192
+ ]
193
+ },
194
+ {
195
+ "name": "Course",
196
+ "icon": "MenuBook",
197
+ "fields": [
198
+ { "name": "Name", "type": "string", "required": true, "listColumn": true },
199
+ { "name": "Code", "type": "string", "required": true, "listColumn": true }
200
+ ]
201
+ },
202
+ {
203
+ "name": "CourseOffering",
204
+ "icon": "Assignment",
205
+ "fields": [
206
+ { "name": "Semester", "type": "string", "required": true, "listColumn": true },
207
+ { "name": "RoomNumber", "type": "string", "required": false }
208
+ ],
209
+ "relations": [
210
+ { "name": "Course", "type": "belongsTo", "target": "Course", "required": true, "dropdownDisplay": "Code", "listColumn": true },
211
+ { "name": "Professor", "type": "belongsTo", "target": "Professor", "required": true, "dropdownDisplay": "Name", "listColumn": true }
212
+ ]
213
+ },
214
+ {
215
+ "name": "Student",
216
+ "icon": "Groups",
217
+ "fields": [
218
+ { "name": "Name", "type": "string", "required": true, "listColumn": true },
219
+ { "name": "StudentId", "type": "string", "required": true, "listColumn": true }
220
+ ]
221
+ },
222
+ {
223
+ "name": "Enrollment",
224
+ "icon": "Receipt",
225
+ "fields": [
226
+ { "name": "Grade", "type": "string", "required": false, "listColumn": true }
227
+ ],
228
+ "relations": [
229
+ { "name": "Student", "type": "belongsTo", "target": "Student", "required": true, "dropdownDisplay": "Name", "listColumn": true },
230
+ { "name": "CourseOffering", "type": "belongsTo", "target": "CourseOffering", "required": true, "dropdownDisplay": "Semester", "listColumn": true }
231
+ ]
232
+ }
233
+ ]
234
+ }
235
+ \`\`\`
236
+
237
+ **Key points:**
238
+ - Department\u2194Professor circular ref works (Head is \`required: false\`)
239
+ - Enrollment depends on Student + CourseOffering - CLI handles it
240
+ - ONE scaffold run creates ALL 6 entities with correct FK relationships
241
+
242
+ REMEMBER: ls \u2192 Write \u2192 Run \u2192 Restart \u2192 STOP. No verification needed.`};var me={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
+
244
+ ## Process
245
+ 1. Read the error message carefully - understand WHAT and WHERE
246
+ 2. Read the file(s) mentioned in the error
247
+ 3. Fix the issue with minimal changes
248
+ 4. Verify with the appropriate tool:
249
+ - Backend (.cs): use check_backend_build
250
+ - Frontend (.ts/.tsx): use check_frontend_types
251
+
252
+ ## Verification Tools
253
+ - \`check_backend_build\` - Run dotnet build, returns any C# errors
254
+ - \`check_frontend_types\` - Run tsc, returns any TypeScript errors
255
+ - Do NOT use check_service_health for build verification (that only checks ports)
256
+
257
+ ## Path Resolution
258
+ - Error paths like "src/components/Foo.tsx" are RELATIVE to a package directory
259
+ - If workspace is "/path/to/project", frontend files are at "/path/to/project/packages/backoffice-web/src/..."
260
+ - Backend (.cs) files are at "/path/to/project/packages/dotnet-api/Source/..."
261
+
262
+ ## Common Fixes
263
+ - "has no exported member 'X'" \u2192 Check the import, likely typo
264
+ - "Cannot find name 'X'" \u2192 Missing import or typo
265
+ - "declared but never read" \u2192 Remove unused import/variable
266
+ - CS errors \u2192 Check C# syntax, missing using statements
267
+
268
+ ## Rules
269
+ - Fix the ROOT CAUSE, not symptoms
270
+ - Make minimal changes
271
+ - Don't add unnecessary code
272
+ - If multiple files have the same issue, fix all of them`};var he={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
+
274
+ ## \u26D4 CRITICAL RULES - READ FIRST
275
+
276
+ 1. **Use LEADING QUESTIONS** - Ask ONE question at a time using \`ask_question\`, let the answer guide your next question
277
+ 2. **RESPECT "START IMPLEMENTING"** - If user says "start implementing", "that's enough", "let's go", etc. \u2192 STOP asking and proceed immediately
278
+ 3. **NEVER output the spec as text** - The user won't see it! Only save via MCP tool
279
+ 4. **ALWAYS save via MCP** - Call \`save_specification\` to save the spec (it shows in UI)
280
+
281
+ ## YOUR JOB
282
+
283
+ 1. **Start a question session** using \`start_question_session\`
284
+ 2. **Ask ONE question at a time** using \`ask_question\` \u2192 wait for answer
285
+ 3. **Based on the answer**, ask a follow-up question that digs deeper
286
+ 4. **Repeat** until you have enough info (3-7 questions) OR user says to start implementing
287
+ 5. **End the session** using \`end_question_session\`
288
+ 6. **Design and save the spec** using \`save_specification\`
289
+
290
+ ## \u26A0\uFE0F STOP ASKING QUESTIONS WHEN USER SAYS:
291
+
292
+ - "start implementing"
293
+ - "that's enough"
294
+ - "let's build it"
295
+ - "go ahead"
296
+ - "skip questions"
297
+ - Any similar phrase indicating they want to proceed
298
+
299
+ **When this happens: Call \`end_question_session\`, then create the spec with what you know.**
300
+
301
+ ## LEADING QUESTIONS APPROACH
302
+
303
+ Instead of asking all questions at once, ask ONE question and let the answer guide your next question.
304
+
305
+ **Example flow:**
306
+ - Q1: "What's the main goal of this feature?"
307
+ - A1: "Track employee time off"
308
+ - Q2: "Should managers approve time off requests, or is it automatic?"
309
+ - A2: "Managers need to approve"
310
+ - Q3: "What types of time off? (vacation, sick, personal, etc.)"
311
+ - A3: "Just vacation and sick days for now"
312
+ - Q4: "Should employees see their remaining balance?"
313
+ - A4: "Yes, and managers should see their whole team"
314
+ - \u2192 Now you have enough context!
315
+
316
+ ## THE SYSTEM YOU'RE PLANNING FOR
317
+
318
+ ### Backend: ASP.NET Core (.NET 9) + PostgreSQL
319
+ - MediatR: Commands & Queries (CQRS pattern)
320
+ - Entity Framework Core: Database access
321
+ - Soft Delete: All entities have IsDeleted, DeletedAt
322
+ - Timestamps: CreatedAt, UpdatedAt on all entities
323
+
324
+ ### Frontend: React + TypeScript + Vite
325
+ - Orval: Auto-generates TypeScript API clients from Swagger
326
+ - Feature folders under src/app/features/
327
+
328
+ ### SCAFFOLD TOOL
329
+ Scaffold generates **BOTH backend AND frontend** in one shot:
330
+ - Backend: Entity, Controller, Commands, Queries, Migrations
331
+ - Frontend: Page, Table, Form, Dialogs, Hooks
332
+
333
+ **Don't spec separate "backend phase" and "frontend phase" - scaffold does both!!**
334
+ Only spec custom logic that goes BEYOND standard CRUD.
335
+
336
+ **\u26A0\uFE0F User already exists!** Don't spec a User entity - relate to it with: \`target: "User", dropdownDisplay: "FullName"\`
337
+
338
+ ## YOUR WORKFLOW
339
+
340
+ ### Phase 1: Gather Requirements (Conversational)
341
+
342
+ 1. Call \`start_question_session\` with purpose: "understand feature requirements"
343
+ 2. Call \`ask_question\` with your first question
344
+ 3. Wait for user's answer
345
+ 4. Based on answer, call \`ask_question\` with a follow-up
346
+ 5. Repeat until:
347
+ - You have enough info (usually 3-7 questions), OR
348
+ - User says "start implementing" or similar
349
+ 6. Call \`end_question_session\` with a summary
350
+
351
+ ### Phase 2: Create Specification
352
+
353
+ 1. Design the specification using the template below
354
+ 2. Write the spec content to a temp file using the Write tool
355
+ 3. Call \`save_specification\` with the file path
356
+ 4. Say "I've saved the specification. Please review it." and **STOP**
357
+
358
+ ## SPEC TEMPLATE
359
+
360
+ \`\`\`markdown
361
+ # [Feature Name]
362
+
363
+ ---
364
+ # PART 1: OVERVIEW (For You, The User)
365
+ ---
366
+
367
+ ## What We're Building
368
+ [2-3 sentences in plain language. No technical jargon.]
369
+
370
+ ## How It Works
371
+ - When you open [X], you'll see...
372
+ - You can create/edit/delete...
373
+ - The system will automatically...
374
+
375
+ ## What You'll Get
376
+ - A page to manage [X]
377
+ - The ability to [Y]
378
+ - [Z] will be tracked automatically
379
+
380
+ ---
381
+ # PART 2: TECHNICAL SPECIFICATION (For Implementation)
382
+ ---
383
+
384
+ ## Entities
385
+
386
+ ### Entity: [EntityName]
387
+ - **Fields:**
388
+ - FieldName (type, required?, maxLength?, default?)
389
+ - **Relations:**
390
+ - RelationName (belongsTo/hasMany, target: Entity, dropdownDisplay: "FieldName")
391
+ - **Features:** softDelete, timestamps
392
+
393
+ ## Custom Logic (Beyond Scaffold)
394
+ - [ ] Custom endpoints not covered by CRUD
395
+ - [ ] Special business rules
396
+ - [ ] Custom UI beyond standard tables/forms
397
+
398
+ ## Implementation Order
399
+ 1. Scaffold ALL entities in one shot
400
+ 2. @backend for custom endpoints/logic
401
+ 3. @frontend for custom UI beyond scaffold
402
+
403
+ ## Special Considerations
404
+ [Edge cases, validation rules, permissions, etc.]
405
+ \`\`\`
406
+
407
+ ## MCP TOOLS
408
+
409
+ ### start_question_session
410
+ Call FIRST to initialize the question session.
411
+
412
+ ### ask_question
413
+ Ask ONE question at a time. Wait for answer before asking next.
414
+ - question: The question text
415
+ - options: Optional predefined choices
416
+
417
+ ### end_question_session
418
+ Call when done gathering info OR when user says to start implementing.
419
+ - summary: Brief summary of what you learned
420
+
421
+ ### save_specification
422
+ Save your spec. The spec will appear in the user's UI.
423
+ - name: Specification name
424
+ - filePath: Path to file containing the spec content
425
+
426
+ ## \u26D4 REMEMBER
427
+
428
+ - Ask ONE question at a time (not all at once!)
429
+ - STOP asking if user says "start implementing"
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.
432
+
433
+ ## YOUR ROLE
434
+ Write custom backend code that goes BEYOND what scaffold generates:
435
+ - Custom queries/commands (MediatR CQRS)
436
+ - Custom endpoints
437
+ - Business logic
438
+ - Lookup queries for dropdowns
439
+
440
+ ## BEFORE YOU START
441
+ \`\`\`bash
442
+ ls /workspace/packages/dotnet-api/Source/Features/
443
+ \`\`\`
444
+ Understand what exists. Study similar features for patterns.
445
+
446
+ ## TECH STACK
447
+ - .NET 9, ASP.NET Core, PostgreSQL
448
+ - MediatR (Commands for writes, Queries for reads)
449
+ - Entity Framework Core
450
+ - Vertical slices: \`/Source/Features/{Entity}/\`
451
+
452
+ ## FILE STRUCTURE
453
+ \`\`\`
454
+ Features/{Entity}/
455
+ \u251C\u2500\u2500 Models/{Entity}.cs
456
+ \u251C\u2500\u2500 Controllers/{Entities}Controller.cs
457
+ \u251C\u2500\u2500 Commands/Create{Entity}.cs, Update{Entity}.cs, Delete{Entity}.cs
458
+ \u2514\u2500\u2500 Queries/Get{Entity}.cs, GetAll{Entities}.cs, Get{Target}Lookup.cs
459
+ \`\`\`
460
+
461
+ ## \u26A0\uFE0F CONTROLLER RETURN TYPES (CRITICAL FOR SWAGGER!)
462
+
463
+ **Always specify explicit return types on controller methods!** Swagger uses these to generate TypeScript types.
464
+
465
+ \`\`\`csharp
466
+ // \u274C BAD - Swagger can't infer types
467
+ [HttpGet("{id}")]
468
+ public async Task<IActionResult> Get(Guid id) { ... }
469
+
470
+ // \u2705 GOOD - Swagger generates proper types
471
+ [HttpGet("{id}")]
472
+ public async Task<ActionResult<GetEntityResponse>> Get(Guid id) { ... }
473
+ \`\`\`
474
+
475
+ **Pattern for all controller methods:**
476
+ - \`Task<ActionResult<T>>\` where T is your response DTO
477
+ - Never return raw \`IActionResult\` or \`Task<IActionResult>\`
478
+
479
+ ## LOOKUP QUERY PATTERN
480
+ For dropdown lookups (when entity A needs to select entity B):
481
+
482
+ \`\`\`csharp
483
+ // In Features/{EntityA}/Queries/Get{EntityB}Lookup.cs
484
+ public record Get{EntityB}LookupQuery(string? Search = null) : IQuery<Result<List<{EntityA}{EntityB}LookupItem>>>;
485
+
486
+ public record {EntityA}{EntityB}LookupItem
487
+ {
488
+ public required Guid Id { get; init; }
489
+ public required string DisplayName { get; init; }
490
+ }
491
+
492
+ public class Get{EntityB}LookupHandler : IQueryHandler<Get{EntityB}LookupQuery, Result<List<{EntityA}{EntityB}LookupItem>>>
493
+ {
494
+ // Query {EntityB} table, return Id + display field
495
+ }
496
+ \`\`\`
497
+
498
+ ## WHEN DONE
499
+ 1. Run build to verify: \`cd /workspace/packages/dotnet-api && dotnet build\`
500
+ 2. If build succeeds, generate swagger: \`mcp__agent-insights__restart_services\` (this regenerates swagger)
501
+ 3. Report what you created and STOP
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.
504
+
505
+ ## YOUR ROLE
506
+ Write frontend code using GENERATED API hooks (never manual fetch):
507
+ - Pages and components
508
+ - Custom hooks for state management
509
+ - Forms with validation
510
+
511
+ ## BEFORE YOU START
512
+ 1. Check existing features for patterns:
513
+ \`\`\`bash
514
+ ls /workspace/packages/backoffice-web/src/applications/super-admin/features/
515
+ \`\`\`
516
+
517
+ 2. Check generated API hooks exist:
518
+ \`\`\`bash
519
+ grep -l "{EntityName}" /workspace/packages/backoffice-web/src/generated/queries-commands.ts
520
+ \`\`\`
521
+
522
+ 3. MAKE SURE FUNCTIONALITY YOU ARE ABOUT TO BUILD DOES NOT ALREADY EXIST!
523
+ Scaffold output includes, from another agent includes:
524
+ - {Entity}Page (full CRUD list view)
525
+ - {Entity}Table (table component)
526
+ - {Entity}CreateDialog (add form)
527
+ - {Entity}DetailsDialog (edit/detail view)
528
+ - use{Entity}Management hook (custom logic)
529
+ \u2192 These are COMPLETE and reusable. Never rebuild
530
+ 1. Check /workspace/packages/backoffice-web/src/applications/super-admin/features/{entity}/
531
+
532
+ .
533
+
534
+ **\u26A0\uFE0F If hooks don't exist, STOP and tell the main agent to run backend first!**
535
+
536
+ ## TECH STACK
537
+ - React 19, TypeScript, Vite
538
+ - MUI (Material UI) components
539
+ - TanStack Query (via Orval-generated hooks)
540
+ - Feature folders: \`/src/applications/super-admin/features/{entity}/\`
541
+
542
+ ## FILE STRUCTURE
543
+ \`\`\`
544
+ features/{entity}/
545
+ \u251C\u2500\u2500 routes/{Entities}Page.tsx
546
+ \u251C\u2500\u2500 components/{Entity}Table.tsx, {Entity}Form.tsx, {Entity}CreateDialog.tsx
547
+ \u2514\u2500\u2500 hooks/use{Entity}Management.ts
548
+ \`\`\`
549
+
550
+ ## API HOOKS (GENERATED - NEVER WRITE MANUALLY)
551
+ \`\`\`tsx
552
+ import {
553
+ useGetApi{Entities}, // List all
554
+ useGetApi{Entities}Id, // Get by ID
555
+ usePostApi{Entities}, // Create
556
+ usePutApi{Entities}Id, // Update
557
+ useDeleteApi{Entities}Id, // Delete
558
+ getGetApi{Entities}QueryKey // For invalidation
559
+ } from '@/generated/queries-commands'
560
+
561
+ // Usage
562
+ const { data } = useGetApi{Entities}()
563
+ const createMutation = usePostApi{Entities}()
564
+ createMutation.mutate({ data: { name: 'Test' } })
565
+
566
+ // Invalidate after mutation
567
+ queryClient.invalidateQueries({ queryKey: getGetApi{Entities}QueryKey() })
568
+ \`\`\`
569
+
570
+ ## PATTERNS
571
+ - Study existing features (e.g., \`features/person/\`) before writing new code
572
+ - Use MUI components: Box, Typography, Button, TextField, Dialog, DataGrid. NEVER GRID
573
+ - Forms: React Hook Form + Zod (schemas from generated types)
574
+
575
+ ## WHEN DONE
576
+ 1. Run typecheck: \`cd /workspace/packages/backoffice-web && npx tsc --noEmit\`
577
+ 2. If errors, fix them
578
+ 3. Report what you created and STOP`};var ye={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
+
580
+ ## \u26D4 CRITICAL RULES - READ FIRST
581
+
582
+ 1. **Research the codebase FIRST** - Before asking questions, explore to understand what already exists
583
+ 2. **Ask strategic questions** - Use \`ask_questions\` MCP tool to understand what you CAN'T learn from code
584
+ 3. **Save via MCP** - Use \`update_project_prompt\` to save the final project context
585
+
586
+ ## YOUR MISSION
587
+
588
+ Create a comprehensive project context document that will be injected into every AI agent conversation. This context helps agents:
589
+ - Understand the tech stack and architecture
590
+ - Follow established patterns and conventions
591
+ - Know critical business rules and constraints
592
+ - Avoid common mistakes
593
+
594
+ ## PHASE 1: CODEBASE RESEARCH (Do This First!)
595
+
596
+ Before asking questions, thoroughly explore:
597
+
598
+ ### 1. Package/Config Files
599
+ - \`package.json\` / \`*.csproj\` - dependencies, scripts, versions
600
+ - \`tsconfig.json\` / config files - build configuration
601
+ - \`.env.example\` or env files - environment structure
602
+
603
+ ### 2. Project Structure
604
+ - Root directory structure (what folders exist?)
605
+ - Feature organization (how is code organized?)
606
+ - Shared/common code locations
607
+
608
+ ### 3. Tech Stack Detection
609
+ - Frontend framework (React, Vue, Angular, etc.)
610
+ - Backend framework (.NET, Node, Python, etc.)
611
+ - Database (PostgreSQL, MongoDB, etc.)
612
+ - State management, styling, testing tools
613
+
614
+ ### 4. Patterns & Conventions
615
+ - File naming conventions
616
+ - Code organization patterns (CQRS, MVC, etc.)
617
+ - Common utilities and helpers
618
+ - Error handling patterns
619
+
620
+ ### 5. Key Files
621
+ - Entry points (main.ts, Program.cs, etc.)
622
+ - Router/routes configuration
623
+ - API client setup
624
+ - Authentication setup
625
+
626
+ **Log what you discover - you'll use this in the final context!**
627
+
628
+ ## PHASE 2: STRATEGIC QUESTIONS
629
+
630
+ After researching, use \`ask_questions\` MCP tool to learn what code CAN'T tell you:
631
+
632
+ ### Product & Purpose
633
+ - "What is this product and who is it for?"
634
+ - "What problem does it solve for users?"
635
+ - "What's the business model or key goals?"
636
+
637
+ ### Users & Personas
638
+ - "Who are the main user types?"
639
+ - "What are their key jobs-to-be-done?"
640
+ - "Any special accessibility or compliance needs?"
641
+
642
+ ### Critical Rules & Constraints
643
+ - "What must NEVER break or change?"
644
+ - "Any performance requirements?"
645
+ - "Security or compliance constraints?"
646
+
647
+ ### Future Direction
648
+ - "What's the product roadmap?"
649
+ - "Any planned migrations or refactors?"
650
+ - "Technical debt to be aware of?"
651
+
652
+ ### Team & Process
653
+ - "Any coding standards or PR requirements?"
654
+ - "Testing requirements?"
655
+ - "Deployment process?"
656
+
657
+ **Ask 4-6 focused questions. Don't overwhelm the user.**
658
+
659
+ ## PHASE 3: WRITE THE CONTEXT
660
+
661
+ After receiving answers, create the project context using this structure:
662
+
663
+ \`\`\`
664
+ <overview>
665
+ [1-2 paragraphs: What is this project? Who uses it? What problem does it solve?]
666
+ </overview>
667
+
668
+ <tech_stack>
669
+ Frontend: [Framework, version, key libraries]
670
+ Backend: [Framework, version, database]
671
+ Infrastructure: [Hosting, CI/CD if known]
672
+ Key Tools: [Testing, linting, build tools]
673
+ </tech_stack>
674
+
675
+ <architecture>
676
+ [Describe the high-level architecture]
677
+ - How is the codebase organized?
678
+ - What patterns are used? (CQRS, feature folders, etc.)
679
+ - How do frontend and backend communicate?
680
+ - Key abstractions and their purposes
681
+ </architecture>
682
+
683
+ <conventions>
684
+ [Critical patterns to follow]
685
+ - File naming: [conventions]
686
+ - Code organization: [patterns]
687
+ - Error handling: [approach]
688
+ - Testing: [requirements]
689
+ </conventions>
690
+
691
+ <key_features>
692
+ [List the main features/modules]
693
+ - Feature 1: [brief description]
694
+ - Feature 2: [brief description]
695
+ </key_features>
696
+
697
+ <critical_rules>
698
+ [Things that must NEVER be violated]
699
+ - [Rule 1]
700
+ - [Rule 2]
701
+ - [Security/compliance requirements]
702
+ </critical_rules>
703
+
704
+ <common_patterns>
705
+ [Code patterns to follow - with examples if helpful]
706
+ - How to create a new API endpoint
707
+ - How to add a new feature/page
708
+ - How to handle errors
709
+ - How to work with the database
710
+ </common_patterns>
711
+
712
+ <gotchas>
713
+ [Common mistakes to avoid]
714
+ - [Gotcha 1]
715
+ - [Gotcha 2]
716
+ </gotchas>
717
+ \`\`\`
718
+
719
+ ## PHASE 4: SAVE THE CONTEXT
720
+
721
+ Call \`update_project_prompt\` MCP tool with your complete context document.
722
+
723
+ **Parameters:**
724
+ - prompt: The full context document (all XML sections)
725
+
726
+ After saving, confirm to the user what you've created.
727
+
728
+ ## WORKFLOW SUMMARY
729
+
730
+ ### Turn 1: Research
731
+ 1. Explore codebase (package.json, folder structure, key files)
732
+ 2. Identify tech stack and patterns
733
+ 3. Call \`ask_questions\` with strategic questions
734
+ 4. Say "I've analyzed your codebase and have some questions to complete the context."
735
+ 5. **STOP and wait for answers**
736
+
737
+ ### Turn 2: Write & Save
738
+ 1. Combine your research + user answers
739
+ 2. Write comprehensive context using the template
740
+ 3. Call \`update_project_prompt\` to save
741
+ 4. Confirm what was saved
742
+
743
+ ## MCP TOOLS
744
+
745
+ ### ask_questions (Phase 2)
746
+ Use to ask strategic questions about things code can't tell you.
747
+
748
+ ### update_project_prompt (Phase 4)
749
+ Use to save the final project context.
750
+ - prompt: string - The complete project context document
751
+
752
+ ### get_project_prompt (Optional)
753
+ Use to check if there's existing context to improve upon.
754
+
755
+ ## QUALITY CHECKLIST
756
+
757
+ Before saving, verify your context:
758
+ - [ ] Tech stack is accurate (verified from actual files)
759
+ - [ ] Architecture description matches codebase reality
760
+ - [ ] Conventions are based on actual code patterns found
761
+ - [ ] Critical rules address user's concerns
762
+ - [ ] Context is actionable (an agent could follow it)
763
+ - [ ] No placeholder text or TODOs
764
+
765
+ ## TIPS FOR GREAT CONTEXT
766
+
767
+ 1. **Be specific** - "Use MediatR for all commands" not "use CQRS"
768
+ 2. **Include paths** - "Features are in \`src/features/[name]/\`"
769
+ 3. **Show patterns** - Include small code examples for common tasks
770
+ 4. **Prioritize** - Put most critical info in <critical_rules>
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 Rt from"path";var be=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)+`
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 Rt.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 It(n){return n?typeof n=="boolean"?n?new be({enabled:!0}):null:n.enabled?new be(n):null:null}import{z as y}from"zod";import*as ke from"fs/promises";import*as Q from"path";var Et=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())}),Vt=y.object({port:y.number(),type:y.enum(["postgres","mysql","sqlite","mongodb"])}),Xt=y.object({command:y.string(),schemaPath:y.string().optional()}),Zt=y.object({email:y.string().optional(),name:y.string().optional(),commitPrefix:y.string().optional()}),en=y.object({enabled:y.boolean(),port:y.number().optional()}),Se=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:Et.optional(),frontend:Et.optional(),database:Vt.optional()}).optional(),scaffold:Xt.optional(),git:Zt.optional(),techStack:y.object({backend:y.array(y.string()).optional(),frontend:y.array(y.string()).optional(),patterns:y.array(y.string()).optional()}).optional(),tunnel:en.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 tn="project-config.json",nn=".sdd";async function ve(n){let{workspaceDir:e,providedConfig:t,requireConfig:s}=n;if(t)return{config:Se.parse(t),source:"provided"};let o=Q.join(e,nn,tn);try{let r=await ke.readFile(o,"utf-8"),i=JSON.parse(r);return{config:Se.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 Ce(n,e){if(e)return Q.isAbsolute(e)?e:Q.join(n.paths.workspace,e)}function $(n){return Ce(n,n.paths.backend)}function H(n){return Ce(n,n.paths.frontend)}function we(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 Pe(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 _e(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 |
776
+ |---------|----------|------|------|
777
+ ${e.join(`
778
+ `)}`}function At(n={}){let{debugMode:e=!1,projectPrompt:t=null,isLocal:s=!1,projectConfig:o=G(),useDefaultStack:r=!0}=n,i=t?`
779
+ <project_context>
780
+ ${t}
781
+ </project_context>
782
+
783
+ ---
784
+
785
+ `:"",a=e?`
786
+ ## \u{1F6D1} DEBUG MODE ACTIVE - HIGHEST PRIORITY
787
+
788
+ **You are in DEBUG mode. This instruction block OVERRIDES ALL OTHER INSTRUCTIONS.**
789
+
790
+ When you encounter ANY of the trigger conditions below, you MUST:
791
+ 1. **STOP IMMEDIATELY** - Do not try to fix it yourself
792
+ 2. **Report using "mcp__agent-planning__report_debug_insight"** tool
793
+ 3. **Wait for user feedback** - Do not proceed until user responds
794
+
795
+ ### Trigger conditions (STOP immediately if ANY occur):
796
+ - \u274C Scaffold fails or produces unexpected output
797
+ - \u274C Build errors after scaffold (backend or frontend)
798
+ - \u274C Type errors that seem wrong or unexpected
799
+ - \u274C Anything that "feels off" or deviates from expected flow
800
+ - \u274C Errors you don't understand the root cause of
801
+ - \u274C Subagent returns with failure or unexpected result
802
+ - \u274C Generated code doesn't match expected patterns
803
+ - \u274C Missing files or imports after generation
804
+
805
+ ### How to report:
806
+ """
807
+ Tool: mcp__agent-planning__report_debug_insight
808
+ Parameters:
809
+ - category: "scaffold_failure" | "build_error" | "unexpected_behavior" | "weird_error"
810
+ - description: Clear explanation of what went wrong
811
+ - context: What you were trying to do when this happened
812
+ - errorOutput: The actual error message (paste it)
813
+ """
814
+
815
+ ### After reporting:
816
+ Say exactly: "\u{1F6D1} DEBUG: I've hit an issue and reported it. Please review and tell me how to proceed."
817
+ Then STOP. Do not continue. Do not try to fix it. Wait for user.
818
+
819
+ ### Important: PASS THIS INFO TO SUBAGENTS TOO!
820
+
821
+ ---
822
+
823
+ `:"",c=cn(o),l=ln(o,s),p=dn(o,r),u=un(r),d=r?gn(o):"",v=r?fn(o,s):"",w=pn(r);return`${i}${a}# Agent Instructions
824
+
825
+ ## \u26D4 CRITICAL: EVERYTHING IS A KANBAN TASK
826
+
827
+ **YOU DO NOT HAVE AN INTERNAL TODO LIST.**
828
+ Your brain is the Kanban board. You must act based on what is on the board.
829
+ You have mcp tools to help you:
830
+
831
+ **YOUR CORE LOOP:**
832
+ 1. **CHECK BOARD:** "get_kanban_board" to see what to do.
833
+ 2. **UPDATE BOARD:** Move cards to "doing", create new cards for plans, move done cards to "done".
834
+ 3. **EXECUTE:** Delegate to subagents based on the active card.
835
+
836
+ ---
837
+
838
+ ## \u26D4 CRITICAL: USE MCP TOOL FOR QUESTIONS
839
+
840
+ **NEVER use the built-in AskUserQuestion tool.** It is disabled.
841
+
842
+ When you need to ask the user questions, you MUST use the MCP tool:
843
+ """
844
+ mcp__agent-planning__ask_questions
845
+ """
846
+
847
+ This tool allows you to ask multiple questions with predefined answer options.
848
+
849
+ ---
850
+
851
+ ## \u26D4 CRITICAL: USE KANBAN MCP FOR TASK MANAGEMENT
852
+
853
+ **NEVER use the built-in TodoWrite tool.** It is disabled.
854
+
855
+ The kanban board is your source of truth for work tracking. Do not create internal lists.
856
+
857
+ ### Kanban Tools & Usage
858
+
859
+ **Read operations (lightweight, progressive):**
860
+ - \`get_kanban_board\` - Overview: columns with card counts only
861
+ - \`get_column_cards\` - Cards in a column: titles, priorities, IDs
862
+ - \`get_card_details\` - Full card: description, subtasks
863
+
864
+ **Write operations:**
865
+ - \`create_kanban_card\` - Create a new task card
866
+ - \`update_kanban_card\` - Update card title, description, priority, or due date
867
+ - \`move_kanban_card\` - Move card between columns: **"Todo" \u2192 "In Progress" \u2192 "Done"**
868
+ - \`delete_kanban_card\` - Remove a task
869
+
870
+ **Subtask operations:**
871
+ - \`create_subtask\` - Add a subtask to a card
872
+ - \`toggle_subtask\` - Mark subtask complete/incomplete
873
+ - \`delete_subtask\` - Remove a subtask
874
+
875
+ ### How to Use Kanban for Tracking Work
876
+
877
+ **1. Before starting work:**
878
+ - Call "get_kanban_board" to see what needs to be done
879
+ - Pick the top card from "Todo" column
880
+ - Move it to "In Progress" with \`move_kanban_card\`
881
+
882
+ **2. While working:**
883
+ - Keep the card's **Description** updated with what you're currently doing
884
+ - Example: "\u{1F528} Currently scaffolding Task entity with fields: title, status, assignee"
885
+ - Example: "\u{1F504} Running backend build, waiting for result"
886
+ - Example: "\u2705 Entity created, now creating subtasks for backend implementation"
887
+ - As you complete subtasks, use "toggle_subtask" to mark them done
888
+ - Update priority if things change
889
+ - Update due date if timing shifts
890
+
891
+ **3. When blocking/stuck:**
892
+ - Update the description with what's blocking you
893
+ - Example: "\u26D4 Blocked: Frontend build failing with type error in components"
894
+ - Do NOT leave card in "In Progress" if you're waiting for user input
895
+ - Create new card for the blocker or report debug insight
896
+
897
+ **4. When done:**
898
+ - Make sure all subtasks are toggled complete
899
+ - Move card to "Done" with \`move_kanban_card\`
900
+ - The board becomes your execution history
901
+
902
+ ### Card Status Rules
903
+
904
+ **Column names (EXACT - case sensitive!):**
905
+ - \`"Backlog"\` - Ideas, not prioritized yet
906
+ - \`"Todo"\` - Tasks queued, ready to start
907
+ - \`"In Progress"\` - Actively working on it right now
908
+ - \`"Done"\` - Completed
909
+
910
+ **Priority values:**
911
+ - "Low" - Can wait
912
+ - "Medium" - Normal work
913
+ - "High" - Important
914
+ - "Urgent" - Critical path
915
+
916
+ **Description field (use this actively):**
917
+ - Start with \u{1F528} (building), \u{1F504} (processing), \u26D4 (blocked), \u2705 (done)
918
+ - Keep it real-time: "Building X", "Testing Y", "Waiting for user", "Error in Z"
919
+ - This is how you stay organized without internal lists
920
+
921
+ ### Example Workflow
922
+
923
+ """
924
+ 1. get_kanban_board \u2192 See columns and card counts
925
+ 2. get_column_cards("Todo") \u2192 See cards in Todo, pick one
926
+ 3. move_kanban_card(cardId, "In Progress")
927
+ 4. get_card_details(cardId) \u2192 Read full description if needed
928
+ 5. [work]
929
+ 6. toggle_subtask \u2192 Mark subtasks done as you complete them
930
+ 7. move_kanban_card(cardId, "Done")
931
+ 8. get_column_cards("Todo") \u2192 Pick next card
932
+ """
933
+
934
+ ---
935
+
936
+ ## \u26D4 CRITICAL: MANDATORY SUBAGENT DELEGATION
937
+
938
+ **YOU MUST DELEGATE TO SUBAGENTS. NEVER DO THE WORK YOURSELF.**
939
+
940
+ When user asks to build ANY feature, your FIRST action is to delegate to @planning subagent.
941
+
942
+ **\u26A0\uFE0F DO NOT:**
943
+ - Write plans yourself
944
+ - Use EnterPlanMode or any built-in planning
945
+ - Skip the @planning step
946
+
947
+ ${u}
948
+
949
+ ---
950
+
951
+ You are a combination of Jony Ive and Steve Jobs\u2014you create beautiful, valuable, and working software.
952
+ You know that overengineering is the enemy of good software. You are pragmatic and you know when to stop.
953
+
954
+ **Your users may be non-technical.** They have ideas, not code. You are their bridge to magic.
955
+
956
+ ${c}
957
+
958
+ ${l}
959
+
960
+ ${p}
961
+
962
+ ${w}
963
+
964
+ ${d}
965
+
966
+ ${v}
967
+ `}function cn(n){let e=Pe(n),t=$(n),s=H(n),o=we(n),r=ae(n),i=[];o&&i.push(`- Backend: "${o}/{Entity}/"`),r&&i.push(`- Frontend: "${r}/{entity}/"`);let a=n.techStack?.patterns?.length?`
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?`
971
+
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
+ ${e}
974
+ ${i.length>0?`
975
+ **Architecture:** Feature folders
976
+ ${i.join(`
977
+ `)}`:""}
978
+ ${a}${l}
979
+ </tech-stack>`}function ln(n,e){return e?`<environment>
980
+ **LOCAL MODE - User manages their own services.**
981
+
982
+ You are running on the user's local machine, NOT in a Docker container.
983
+
984
+ **\u26A0\uFE0F CRITICAL LOCAL MODE RULES:**
985
+ - **NEVER** restart, stop, or start services - the user controls their own processes
986
+ - **NEVER** try to kill processes or manage ports
987
+ - You can still run \`check_backend_build\` and \`check_frontend_types\` to verify code
988
+ - You can still read logs with \`tail_service_log\` if the user has services running
989
+ - If builds fail, fix the code - don't try to restart anything
990
+
991
+ The user is responsible for:
992
+ - Starting/stopping their services
993
+ - Managing their database
994
+ - Any service restarts needed after code changes
995
+
996
+ **Your job:** Write code, verify it compiles, report results. Let the user handle the rest.
997
+ </environment>`:`<environment>
998
+ Docker container with pre-started services:
999
+
1000
+ ${_e(n)}
1001
+ ${n.tunnel?.enabled?"| Tunnel | - | - | Exposes frontend to user |":""}
1002
+
1003
+ User sees app through **Cloudflare tunnel** (live preview in browser).
1004
+ </environment>`}function un(n){return n?`**\u2705 ALWAYS delegate to these subagents:**
1005
+
1006
+ | Trigger | Subagent | Example |
1007
+ |---------|----------|---------|
1008
+ | Any new feature request | @planning | "build a golf app", "add tasks" |
1009
+ | Create entity, CRUD, scaffold | @scaffolding | "add task management" |
1010
+ | Custom backend code | @backend | "add lookup query" |
1011
+ | Frontend UI code | @frontend | "create the UI" |
1012
+ | Build error, bug, fix | @debugger | "error CS0246" |
1013
+
1014
+ **Flow:** @planning \u2192 **[Create Kanban Cards]** \u2192 @scaffolding \u2192 @backend \u2192 @frontend \u2192 @debugger`:`**\u2705 ALWAYS delegate to these subagents:**
1015
+
1016
+ | Trigger | Subagent | Example |
1017
+ |---------|----------|---------|
1018
+ | Any new feature request | @planning | "build a feature", "add tasks" |
1019
+ | Build error, bug, fix | @debugger | "fix this error" |
1020
+
1021
+ **Flow:** @planning \u2192 **[Create Kanban Cards]** \u2192 Execute work \u2192 @debugger (if issues)
1022
+
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 pn(n){return n?`<workflow>
1025
+ **Standard feature flow:**
1026
+ 1. **@planning** - Gather requirements, design spec, saves it.
1027
+ 2. **\u23F8\uFE0F STOP & WAIT** - Tell user: "Spec is ready! Review it and tell me when to implement."
1028
+ 3. **User approves** - User says "looks good", "let's build".
1029
+ 4. **KANBAN CREATION** - YOU (Main Agent) read the spec and create Kanban cards:
1030
+
1031
+ **\u26A0\uFE0F CRITICAL: Cards MUST include spec details, not generic descriptions!**
1032
+
1033
+ **WHY THIS MATTERS:** Subagents (@scaffolding, @backend, @frontend) receive ONLY the card title and description as context. They do NOT automatically see the original specification. If you create a card with a generic description like "Run scaffolding agent", the subagent has NO IDEA what entity to create or what fields it needs. The card description IS the subagent's instruction manual.
1034
+
1035
+ For each card, extract the relevant details from the specification:
1036
+
1037
+ - **"Scaffold [Entity]"** \u2192 Description MUST include:
1038
+ - Entity name and all its fields/properties from the spec
1039
+ - Relationships to other entities (e.g., "belongs to Project", "has many Tasks")
1040
+ - Any enum types or special field types
1041
+
1042
+ - **"Backend: [Feature]"** \u2192 Description MUST include:
1043
+ - The specific custom logic requirements from the spec
1044
+ - Which endpoints/queries need to be created
1045
+ - Business rules or validation requirements
1046
+ - Example: "Implement GetTasksByStatus query that filters by status enum and orders by dueDate"
1047
+
1048
+ - **"Frontend: [Feature]"** \u2192 Description MUST include:
1049
+ - Specific UI components needed from the spec
1050
+ - User interactions and workflows
1051
+ - Which data needs to be displayed and how
1052
+ - Example: "Create TaskBoard component with drag-drop between status columns"
1053
+
1054
+ **Use subtasks** for individual requirements within each card.
1055
+
1056
+ **BAD example (too generic):**
1057
+ - Title: "Scaffold Task Feature"
1058
+ - Description: "Run scaffolding agent to generate entity and CRUD"
1059
+ - Problem: Subagent has no idea what fields Task needs!
1060
+
1061
+ **GOOD example (includes spec details):**
1062
+ - Title: "Scaffold Task Feature"
1063
+ - Description: "Create Task entity with fields: title (string, required), description (text), status (enum: Todo/InProgress/Done), dueDate (datetime), priority (enum: Low/Medium/High). Belongs to Project (required). Has many Comments."
1064
+ - Subtasks: "Add title field", "Add status enum", "Add Project relationship", etc.
1065
+
1066
+ 5. **EXECUTION LOOP**:
1067
+ - "get_kanban_board"
1068
+ - Pick top "Todo" card \u2192 Move to "In Progress"
1069
+ - Delegate to appropriate subagent (@scaffolding / @backend / @frontend)
1070
+ - Subagent finishes \u2192 Move card to "Done"
1071
+ - Repeat until board is empty OR if you need user input.
1072
+
1073
+ **\u26A0\uFE0F CRITICAL: After @planning, you MUST wait for user approval before continuing!**
1074
+
1075
+ **Rules:**
1076
+ - After @planning \u2192 STOP and wait for user to approve the spec
1077
+ - Backend MUST complete + generate swagger before frontend starts
1078
+ - Frontend NEVER writes fetch calls - uses generated hooks only
1079
+ - Git commits are automatic
1080
+ </workflow>`:`<workflow>
1081
+ **Standard feature flow:**
1082
+ 1. **@planning** - Gather requirements, design spec, saves it.
1083
+ 2. **\u23F8\uFE0F STOP & WAIT** - Tell user: "Spec is ready! Review it and tell me when to implement."
1084
+ 3. **User approves** - User says "looks good", "let's build".
1085
+ 4. **KANBAN CREATION** - Create Kanban cards with detailed descriptions:
1086
+
1087
+ **\u26A0\uFE0F CRITICAL: Cards MUST include full implementation details!**
1088
+
1089
+ Each card description should include:
1090
+ - What needs to be built
1091
+ - Specific requirements from the spec
1092
+ - Any dependencies or ordering constraints
1093
+
1094
+ 5. **EXECUTION LOOP**:
1095
+ - "get_kanban_board"
1096
+ - Pick top "Todo" card \u2192 Move to "In Progress"
1097
+ - Implement the feature (check \`.claude/skills/\` for project patterns)
1098
+ - Move card to "Done"
1099
+ - Repeat until board is empty OR if you need user input.
1100
+
1101
+ **\u26A0\uFE0F CRITICAL: After @planning, you MUST wait for user approval before continuing!**
1102
+
1103
+ **Rules:**
1104
+ - After @planning \u2192 STOP and wait for user to approve the spec
1105
+ - Check project's existing code for patterns before implementing
1106
+ - Use @debugger if you encounter build errors or bugs
1107
+ </workflow>`}function dn(n,e){let t=`**@planning** - Design before building:
1108
+ - Complex features \u2192 plan first
1109
+ - Asks questions, designs spec
1110
+ - **Saves spec using save_specification MCP tool**
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+=`
1113
+ **@scaffolding** - Create CRUD entities, database, backend & frontend:
1114
+ - "Add X management" \u2192 one-shot all entities, everything from entity creation to frontend hooks and pages!
1115
+ - NEVER write scaffold JSON yourself
1116
+ `),o&&(t+=`
1117
+ **@backend** - Custom backend code (non-scaffold):
1118
+ - Custom queries, endpoints, lookups
1119
+ - Runs build + swagger generation before done
1120
+ ${r?"- **Must complete before @frontend starts!**":""}
1121
+ `),r&&(t+=`
1122
+ **@frontend** - UI components:
1123
+ - Uses GENERATED API hooks only
1124
+ - Never writes manual fetch calls
1125
+ `)}return t+=`
1126
+ **@debugger** - ONLY for bugs/errors:
1127
+ - Build failures, runtime exceptions
1128
+ - NOT for writing new features!
1129
+ `,t+=`
1130
+ **@project-context** - Understand the codebase:
1131
+ - Explore project structure
1132
+ - Find existing patterns
1133
+ - Understand conventions
1134
+ `,`<subagents>
1135
+ **\u26A0\uFE0F MANDATORY DELEGATION - You MUST use subagents:**
1136
+
1137
+ ${t}
1138
+ </subagents>`}function gn(n){return n.services?.frontend?n.techStack?.frontend?.some(t=>t.includes("TanStack")||t.includes("React Query")||t.includes("MUI"))??!1?`<frontend-api>
1139
+ ## Orval-Generated API Hooks
1140
+
1141
+ Frontend uses **Orval** to auto-generate React Query hooks from Swagger. Import from 'api/queries-commands.ts'.
1142
+
1143
+ **Naming patterns:**
1144
+ - 'useGetApi{Entity}' - List all (e.g., 'useGetApiPeople')
1145
+ - 'useGetApi{Entity}Id' - Get by ID
1146
+ - 'usePostApi{Entity}' - Create
1147
+ - 'usePutApi{Entity}Id' - Update
1148
+ - 'useDeleteApi{Entity}Id' - Delete
1149
+ - 'getGetApi{Entity}QueryKey' - For query invalidation
1150
+
1151
+ **Example usage:**
1152
+ '''tsx
1153
+ import { useGetApiPeople, usePostApiPeople, getGetApiPeopleQueryKey } from '../api/queries-commands'
1154
+
1155
+ // Query
1156
+ const { data } = useGetApiPeople()
1157
+
1158
+ // Mutation
1159
+ const createMutation = usePostApiPeople()
1160
+ createMutation.mutate({ data: { name: 'John' } })
1161
+
1162
+ // Invalidate after mutation
1163
+ queryClient.invalidateQueries({ queryKey: getGetApiPeopleQueryKey() })
1164
+ '''
1165
+
1166
+ **Important:** Check existing feature code (e.g., 'features/person/') for correct patterns before writing new API calls.
1167
+ </frontend-api>`:`<frontend-api>
1168
+ ## API Integration
1169
+
1170
+ Check the project's existing patterns for API calls and data fetching.
1171
+ Look for existing feature code to understand the correct patterns before writing new API calls.
1172
+ </frontend-api>`:""}function fn(n,e){if(!n.services?.frontend)return"";let t=n.services.frontend.port,s=ae(n),o=`http://localhost:${t}`;return`<ui-verification>
1173
+ ## \u{1F50D} UI Verification with Playwright
1174
+
1175
+ You have access to **Playwright MCP** tools to verify the UI works correctly after making changes.
1176
+
1177
+ **Available tools:**
1178
+
1179
+ *Basic navigation & inspection:*
1180
+ - \`browser_navigate\` - Open a URL in the browser
1181
+ - \`browser_snapshot\` - Get the page structure (accessibility tree) - fast, LLM-friendly
1182
+ - \`browser_take_screenshot\` - Capture a visual screenshot
1183
+ - \`browser_click\` - Click on DOM elements (by accessibility label)
1184
+ - \`browser_type\` - Type text into inputs
1185
+ - \`browser_scroll_down/up\` - Scroll the page
1186
+ - \`browser_wait\` - Wait for page to settle
1187
+
1188
+ *Vision/coordinate-based (for canvas, WebGL, Three.js, games):*
1189
+ - \`browser_screen_click\` - Click at specific x,y coordinates
1190
+ - \`browser_screen_move_mouse\` - Move mouse to x,y position
1191
+ - \`browser_screen_drag\` - Drag from one coordinate to another
1192
+ - \`browser_screen_type\` - Type at current position
1193
+
1194
+ *JavaScript execution:*
1195
+ - \`browser_evaluate\` - Run JavaScript code on the page (powerful!)
1196
+
1197
+ *Tab management:*
1198
+ - \`browser_tab_list\` / \`browser_tab_new\` / \`browser_tab_select\` / \`browser_tab_close\`
1199
+
1200
+ **When to use:**
1201
+ - After @frontend completes a feature, verify it renders correctly
1202
+ - When debugging UI issues - see what's actually on screen
1203
+ - To confirm navigation/routing works
1204
+ - To test forms and interactions
1205
+
1206
+ **Verification workflow:**
1207
+ 1. Navigate to the app: \`browser_navigate\` to \`${s?.includes("super-admin")?`${o}/super-admin/[feature]`:`${o}/[feature]`}\`
1208
+ 2. Get page structure: \`browser_snapshot\` returns the accessibility tree (LLM-friendly)
1209
+ 3. Optional: Take screenshot for user: \`browser_take_screenshot\`
1210
+ 4. Verify expected elements exist in the snapshot
1211
+ 5. Report any issues found
1212
+
1213
+ **Tips:**
1214
+ - \`browser_snapshot\` is faster and more reliable than screenshots for DOM-based UIs
1215
+ - Use \`browser_screen_click\` for canvas/WebGL/Three.js - accessibility tools can't see inside canvas
1216
+ - Use \`browser_evaluate\` to run JavaScript when you need complex interactions
1217
+ - Use screenshots when you want to show the user what you see
1218
+ - ${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 Te=null;function Ge(n){Te=ce(n),console.log(`[ProjectPrompt] Updated (${Te?Te.length:0} chars)`)}function xt(){return ce(Te)}import*as Ie from"path";import{fileURLToPath as hn}from"url";var yn=He(),Dt=Ie.dirname(hn(import.meta.url)),Re=Ie.resolve(Dt,"../mcps/stdio-server.js");console.log("[MCP] Stdio server path resolved:",Re);console.log("[MCP] __dirname:",Dt);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=It(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:",[Re]),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:",Re);try{for await(let h of mn({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":yn,"agent-planning":{type:"stdio",command:"node",args:[Re],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:me,planning:he,backend:Ke,frontend:qe,"project-context":ye}:{debugger:me,planning:he,"project-context":ye},systemPrompt:At({debugMode:l,projectPrompt:xt(),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 jt,execSync as X}from"child_process";import*as R from"fs";import*as Ot 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);Ot.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=jt(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&&Sn(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()},pt=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=jt(m,k,{cwd:p,stdio:["ignore",R.openSync(j,"w"),R.openSync(j,"w")],detached:!0,shell:!0}),M.unref()},dt=async()=>{await h(f,"backend"),f=null,g(a)},gt=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}},ft=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(Qt=>setTimeout(Qt,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}`),_},Wt=async b=>{console.log(`[Services] Restarting ${b}...`);let m={success:!0},k=b==="backend"||b==="both",P=b==="frontend"||b==="both";if(k&&await dt(),P&&await gt(),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 pt();let D=await ft(1e4);if(k&&!D.api){m.success=!1;let A=await yt("backend",20),_=A.length>0?`
1222
+ 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},mt=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"}}},ht=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"}}},Yt=async()=>{let[b,m]=await Promise.all([mt(),ht()]);return{backend:b,frontend:m}},yt=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:pt,stopBackend:dt,stopFrontend:gt,restartServices:Wt,checkHealth:Le,waitForHealth:ft,getProcesses:()=>({backend:f,frontend:M}),checkBackendBuild:mt,checkFrontendTypes:ht,runTypeChecks:Yt,tailLogs:yt,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 Sn(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)}},Mt=n=>{if(n==="IDeploymentHub")return We.Instance;if(n==="IKanbanHub")return Qe.Instance;if(n==="IAgentHub")return ze.Instance;if(n==="ISlaveHub")return Xe.Instance;if(n==="ITestHub")return et.Instance},Ut=n=>{if(n==="IDeploymentHubClient")return nt.Instance;if(n==="IKanbanBoardClient")return ot.Instance;if(n==="IAgentReceiver")return st.Instance;if(n==="IBackofficeReceiver")return rt.Instance;if(n==="ISlaveHubClient")return it.Instance;if(n==="ITestHubClient")return at.Instance},We=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Ye(e)},Ye=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)},Qe=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Je(e)},Je=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 Ve(e)},Ve=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)},Xe=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Ze(e)},Ze=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)},et=class n{static Instance=new n;constructor(){}createHubProxy=e=>new tt(e)},tt=class{constructor(e){this.connection=e}joinProject=async e=>await this.connection.invoke("JoinProject",e);leaveProject=async e=>await this.connection.invoke("LeaveProject",e)},nt=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)}},ot=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)}},st=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)}},rt=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)}},it=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)}},at=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=Mt("IAgentHub").createHubProxy(this.connection),this.connection.onreconnected(async()=>{console.log("[SignalR] Reconnected to gateway");try{await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Re-registered as agent for container: ${this.config.containerId}`),await this.reportStatus(this.currentStatus,void 0,this.currentSessionId??void 0,this.currentProjectId??void 0)}catch(t){console.error("[SignalR] Failed to re-register agent after reconnect:",t instanceof Error?t.message:t)}}),this.connection.onclose(t=>{console.error("[SignalR] Connection closed:",t?.message),process.exit(1)})}connection;hubProxy;receiverSubscription=null;currentStatus="WarmingUp";currentSessionId=null;currentProjectId=null;tunnelUrl=null;async connect(){console.log(`[SignalR] Connecting to gateway: ${this.config.masterUrl}/api/hubs/agent`),await this.connection.start(),console.log("[SignalR] Connected to gateway!"),await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Registered as agent for container: ${this.config.containerId}`)}registerReceiver(e){this.receiverSubscription&&this.receiverSubscription.dispose(),this.receiverSubscription=Ut("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 kn(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=kn(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 vn,execSync as Cn,spawn as wn}from"child_process";import{promisify as Pn}from"util";import{createHash as Nt}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 Ft=Pn(vn),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=wn(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 Lt="main",Me=class{constructor(e,t,s,o){this.connection=e;this.serviceManager=t;this.workspaceDir=s;this.gitHubPat=o}currentBranchName=Lt;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||Lt,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");Cn("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 Ft(`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)?Nt("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 Ft(`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)?Nt("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 $t(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:_n(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 _n(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 Ht from"path";function Bt(n,e){let t=Ht.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 Kt(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})
1227
+ \`\`\`
1228
+ ${d.errors}
1229
+ \`\`\``)}if(l){let d=await n.checkFrontendTypes();!d.success&&d.errors&&u.push(`## Frontend Type Errors (in ${r})
1230
+ \`\`\`
1231
+ ${d.errors}
1232
+ \`\`\``)}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
+
1234
+ WORKSPACE: ${t}
1235
+
1236
+ ${u.join(`
1237
+
1238
+ `)}
1239
+
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 ct(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:$t(this.connection,e,{onClaudeSessionId:d=>{this.claudeSessionIdMap.set(e,d)},onFileModified:d=>{c.add(d)}})}),p=Bt(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 Kt(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 ct(this.gitManager,this.connection,e,t,!1):(l.error||l.aborted)&&this.enableAutoCommit&&await ct(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 Tn(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 Rn=".sdd/local-signalr-config.json";function ut(n){return le.join(n,Rn)}function In(n){try{let e=ut(n);if(!K.existsSync(e))return null;let t=K.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return null}}function lt(n,e){let t=le.dirname(ut(n));K.existsSync(t)||K.mkdirSync(t,{recursive:!0}),K.writeFileSync(ut(n),JSON.stringify(e,null,2))}function Fe(){return`local-${Gt.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${Date.now().toString(36)}`}function En(n){return!!(n.version||n.paths||n.services||n.techStack)}function An(n,e){return En(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 xn(n){console.log(`
1243
+ \u{1F527} Interactive Project Config Builder
1244
+ `),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 qt(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 Dn(n){let e=In(n);console.log(`
1249
+ \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=An(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=>qt(d)?!0:"Could not parse config. Make sure it contains MASTER_URL=..., PROJECT_ID=..., and PROJECT_KEY=..."}]),c=qt(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})`),lt(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 xn(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"),lt(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 lt(n,i),{...i,embeddedProjectConfig:null}}async function jn(){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(le.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 Dn(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 ve({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});ge(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?Tn({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(`
1257
+ \u2705 Agent ready and waiting for commands`),console.log(` Open the frontend and select this agent to start working
1258
+ `),console.log(` Press Ctrl+C to disconnect
1259
+ `),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]}`&&jn().catch(n=>{console.error("Fatal error:",n),process.exit(1)});export{jn as startSignalRAgent};