glenn-code 1.0.17 → 1.0.18
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/adapters/signalr/index.js +163 -86
- package/dist/cli.js +163 -86
- package/dist/core.js +167 -90
- package/dist/index.js +168 -91
- package/package.json +1 -1
package/dist/core.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Glenn Code - Bundled and minified
|
|
2
2
|
// (c) DNM Lab - All rights reserved
|
|
3
3
|
|
|
4
|
-
import{createSdkMcpServer as pt}from"@anthropic-ai/claude-agent-sdk";import{tool as
|
|
4
|
+
import{createSdkMcpServer as pt}from"@anthropic-ai/claude-agent-sdk";import{tool as re}from"@anthropic-ai/claude-agent-sdk";import{z as se}from"zod";import*as oe from"fs";import*as De from"path";var D=null;function Oe(e){D=e}function me(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}var It=re("save_specification",`Save a specification to the database.
|
|
5
5
|
|
|
6
6
|
IMPORTANT: First write the specification content to a file using the Write tool, then call this with the file path.
|
|
7
7
|
|
|
@@ -12,30 +12,30 @@ Workflow:
|
|
|
12
12
|
|
|
13
13
|
Example:
|
|
14
14
|
1. Write tool \u2192 save to /tmp/user-auth-spec.md
|
|
15
|
-
2. save_specification(name: "User Authentication", filePath: "/tmp/user-auth-spec.md")`,{name:
|
|
15
|
+
2. save_specification(name: "User Authentication", filePath: "/tmp/user-auth-spec.md")`,{name:se.string().describe("Specification name (e.g., 'User Authentication')"),filePath:se.string().describe("Path to the file containing the specification content (written via Write tool)")},async e=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=De.resolve(e.filePath);if(!oe.existsSync(t))return{content:[{type:"text",text:`\u274C File not found: ${e.filePath}. Use Write tool first to create the file.`}]};let n=oe.readFileSync(t,"utf-8"),a=me(e.name);return await D.saveSpecification(a,n),{content:[{type:"text",text:`\u2705 Saved specification: ${a}.spec.md`}]}}),Dt=re("read_specification","Read the content of a specification.",{name:se.string().describe("Name of the specification to read")},async e=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=me(e.name),n=await D.getSpecification(t);return n?{content:[{type:"text",text:n}]}:{content:[{type:"text",text:`\u274C Specification "${e.name}" not found.`}]}}),Ot=re("list_specifications","List all specifications.",{},async()=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let e=await D.listSpecifications();return e.length===0?{content:[{type:"text",text:"\u{1F4CB} No specifications found."}]}:{content:[{type:"text",text:`\u{1F4CB} Specifications:
|
|
16
16
|
|
|
17
17
|
${e.map(n=>`- ${n.name} (${n.slug}.spec.md)`).join(`
|
|
18
|
-
`)}`}]}}),Ft=
|
|
18
|
+
`)}`}]}}),Ft=re("delete_specification","Delete a specification.",{name:se.string().describe("Name of the specification to delete")},async e=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=me(e.name);return await D.deleteSpecification(t)?{content:[{type:"text",text:`\u{1F5D1}\uFE0F Deleted: ${t}.spec.md`}]}:{content:[{type:"text",text:`\u274C Specification "${e.name}" not found.`}]}});import{tool as M}from"@anthropic-ai/claude-agent-sdk";import{z as Q}from"zod";var y=null;function Fe(e){y=e}var Ne=M("restart_services",`Rebuild and restart services. Call this after making backend changes (.cs files) to apply them.
|
|
19
19
|
|
|
20
20
|
Parameters:
|
|
21
21
|
- target: "backend" (default) | "frontend" | "both"
|
|
22
22
|
|
|
23
23
|
Use target="backend" after .cs file changes (faster, only restarts .NET).
|
|
24
24
|
Use target="frontend" if Vite is stuck (rare, HMR usually works).
|
|
25
|
-
Use target="both" if unsure or both need restart.`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to restart")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] restart_services called (target: ${t})`);let n=await y.restartServices(t);if(!n.success){let
|
|
26
|
-
${
|
|
25
|
+
Use target="both" if unsure or both need restart.`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to restart")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] restart_services called (target: ${t})`);let n=await y.restartServices(t);if(!n.success){let s=[];return n.backendError&&s.push(`Backend: ${n.backendError}`),n.frontendError&&s.push(`Frontend: ${n.frontendError}`),{content:[{type:"text",text:`\u274C Restart failed:
|
|
26
|
+
${s.join(`
|
|
27
27
|
|
|
28
28
|
`)}`}]}}return{content:[{type:"text",text:`\u2705 ${t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend"} restarted successfully.`}]}}),Ue=M("stop_services",`Stop running services. Call this BEFORE running scaffold to prevent port conflicts and ensure clean migrations.
|
|
29
29
|
|
|
30
30
|
Parameters:
|
|
31
31
|
- target: "backend" (default) | "frontend" | "both"
|
|
32
32
|
|
|
33
|
-
Use target="backend" before running scaffold (required for migrations).`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] stop_services called (target: ${t})`);let n=t==="backend"||t==="both",a=t==="frontend"||t==="both";n&&await y.stopBackend(),a&&await y.stopFrontend();let
|
|
33
|
+
Use target="backend" before running scaffold (required for migrations).`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] stop_services called (target: ${t})`);let n=t==="backend"||t==="both",a=t==="frontend"||t==="both";n&&await y.stopBackend(),a&&await y.stopFrontend();let s=5e3,o=Date.now(),r=!n,g=!a;for(;Date.now()-o<s;){let u=await y.checkHealth();if(n&&!u.api&&(r=!0),a&&!u.web&&(g=!0),r&&g)break;await new Promise(C=>setTimeout(C,500))}let _=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";return{content:[{type:"text",text:r&&g?`\u2705 ${_} stopped successfully.`:`\u26A0\uFE0F ${_} stop requested but health check still shows running. Proceeding anyway.`}]}}),Le=M("start_services",`Start services. Call this AFTER running scaffold to bring services back up.
|
|
34
34
|
|
|
35
35
|
Parameters:
|
|
36
36
|
- target: "backend" (default) | "frontend" | "both"
|
|
37
37
|
|
|
38
|
-
Use target="backend" after running scaffold.`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to start")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] start_services called (target: ${t})`);let n=t==="backend"||t==="both",a=t==="frontend"||t==="both";n&&await y.startBackend(),a&&await y.startFrontend();let
|
|
38
|
+
Use target="backend" after running scaffold.`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to start")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] start_services called (target: ${t})`);let n=t==="backend"||t==="both",a=t==="frontend"||t==="both";n&&await y.startBackend(),a&&await y.startFrontend();let s=await y.waitForHealth(15e3),o=!n||s.api,r=!a||s.web,g=o&&r,_=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";if(!g){let f=[];return n&&!s.api&&f.push("Backend failed to start"),a&&!s.web&&f.push("Frontend failed to start"),{content:[{type:"text",text:`\u274C ${_} failed to start: ${f.join(", ")}`}]}}return{content:[{type:"text",text:`\u2705 ${_} started successfully.`}]}}),Me=M("check_service_health",`Check if services are running and healthy. Returns current status of backend API and frontend.
|
|
39
39
|
Note: This only checks if ports are responding, NOT build/type errors. Use check_backend_build or check_frontend_types for that.`,{},async()=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=await y.checkHealth();return{content:[{type:"text",text:`Service Health:
|
|
40
40
|
\u{1F5A5}\uFE0F Backend API: ${e.api?"\u2705 running":"\u274C not running"}
|
|
41
41
|
\u{1F310} Frontend: ${e.web?"\u2705 running":"\u274C not running"}`}]}}),je=M("check_backend_build","Run dotnet build to check for C# compilation errors. Use this after fixing backend code to verify the fix works.",{},async()=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};console.log("[MCP] check_backend_build called");let e=await y.checkBackendBuild();return e.success?{content:[{type:"text",text:"\u2705 Backend build succeeded - no errors"}]}:{content:[{type:"text",text:`\u274C Backend build failed:
|
|
@@ -55,38 +55,61 @@ Parameters:
|
|
|
55
55
|
- pattern: string (e.g., "users", "api/reports")`,{pattern:Q.string().describe("Text to search for in endpoint paths")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=await y.checkSwaggerEndpoints(e.pattern);return t.foundEndpoints.length===0?{content:[{type:"text",text:`\u274C No endpoints found matching "${e.pattern}" (Total endpoints: ${t.totalEndpoints})`}]}:{content:[{type:"text",text:`\u2705 Found ${t.foundEndpoints.length} matching endpoints:
|
|
56
56
|
|
|
57
57
|
${t.foundEndpoints.join(`
|
|
58
|
-
`)}`}]}});import*as P from"fs";import*as V from"path";var
|
|
59
|
-
`,"utf-8")},async reportProgress(n,a,
|
|
60
|
-
`,"utf-8")}}}function ye(e){let t=V.join(
|
|
61
|
-
1.
|
|
62
|
-
2.
|
|
63
|
-
3.
|
|
64
|
-
4.
|
|
65
|
-
5.
|
|
58
|
+
`)}`}]}});import*as P from"fs";import*as V from"path";var qe="/tmp";function he(e){let t=e?V.join(e,".sdd","insights"):V.join(process.cwd(),".sdd","insights");return{async reportInsight(n,a){P.mkdirSync(t,{recursive:!0});let s=a.type==="learning"?"learnings.jsonl":"bugs.jsonl",o=V.join(t,s);P.appendFileSync(o,JSON.stringify(a)+`
|
|
59
|
+
`,"utf-8")},async reportProgress(n,a,s){let o=V.join(qe,`agent-progress-${n}.json`),r={timestamp:new Date().toISOString(),event:a,data:s};P.appendFileSync(o,JSON.stringify(r)+`
|
|
60
|
+
`,"utf-8")}}}function ye(e){let t=V.join(qe,`agent-questions-${e}.json`);if(!P.existsSync(t))return null;try{let n=P.readFileSync(t,"utf-8");return P.unlinkSync(t),JSON.parse(n)}catch{return null}}import*as w from"fs";import*as ae from"path";function be(e){let t=ae.join(e,".sdd","specifications");function n(){w.existsSync(t)||w.mkdirSync(t,{recursive:!0})}function a(s){return ae.join(t,`${s}.spec.md`)}return{async saveSpecification(s,o){n(),w.writeFileSync(a(s),o,"utf-8")},async getSpecification(s){n();let o=a(s);return w.existsSync(o)?w.readFileSync(o,"utf-8"):null},async listSpecifications(){return n(),w.readdirSync(t).filter(o=>o.endsWith(".spec.md")).map(o=>{let r=w.readFileSync(ae.join(t,o),"utf-8"),g=r.match(/name: "([^"]+)"/),_=r.match(/status: (\w+)/),f=r.match(/version: "([^"]+)"/);return{slug:o.replace(".spec.md",""),name:g?.[1]||o,status:_?.[1]||"unknown",version:f?.[1]||"1.0.0"}})},async deleteSpecification(s){n();let o=a(s);return w.existsSync(o)?(w.unlinkSync(o),!0):!1},async specificationExists(s){return n(),w.existsSync(a(s))}}}function _e(){return pt({name:"agent-insights",version:"1.0.0",tools:[Ne,Ue,Le,Me,je,Be]})}import{query as Tt}from"@anthropic-ai/claude-agent-sdk";var ke={description:"Delegate to this agent when scaffolding new entities or creating CRUD features. This agent handles entity generation using the scaffold CLI with multi-entity support.",model:"inherit",prompt:`You are a scaffolding specialist. Your ONLY job is:
|
|
61
|
+
1. Read the spec if referenced in the card description
|
|
62
|
+
2. Check existing features (quick ls)
|
|
63
|
+
3. Write ALL entities to ONE /tmp/scaffold.json (big bang, no phasing!)
|
|
64
|
+
4. Run scaffold CLI ONCE
|
|
65
|
+
5. Restart backend
|
|
66
|
+
6. STOP IMMEDIATELY
|
|
66
67
|
|
|
67
68
|
**\u26A1 ONE-SHOT RULE: Put ALL entities in ONE JSON. The CLI handles dependencies.**
|
|
68
69
|
|
|
70
|
+
## \u26A0\uFE0F SPEC AWARENESS (READ THIS FIRST!)
|
|
71
|
+
|
|
72
|
+
**If the card description contains "\u{1F4CB} Spec:" or mentions a specification:**
|
|
73
|
+
1. Extract the spec name/slug from the card description
|
|
74
|
+
2. Call \`read_specification("{spec-slug}")\` to get the FULL specification
|
|
75
|
+
3. Use BOTH the card description AND the spec to understand what to build
|
|
76
|
+
4. The spec contains the complete context - don't miss any fields or relations!
|
|
77
|
+
|
|
78
|
+
**Example:** If card says "\u{1F4CB} Spec: time-off-management", run:
|
|
79
|
+
\`\`\`
|
|
80
|
+
read_specification("time-off-management")
|
|
81
|
+
\`\`\`
|
|
82
|
+
|
|
69
83
|
## WORKFLOW
|
|
70
84
|
|
|
71
|
-
### Step 0:
|
|
85
|
+
### Step 0: Read Spec (IF REFERENCED)
|
|
86
|
+
If the card mentions a spec, read it first to get full context.
|
|
87
|
+
|
|
88
|
+
### Step 1: Check Existing Features (ALWAYS DO THIS)
|
|
72
89
|
\`\`\`bash
|
|
73
90
|
ls /workspace/packages/dotnet-api/Source/Features/
|
|
74
91
|
\`\`\`
|
|
75
92
|
This shows what already exists. **DO NOT scaffold entities that already exist!**
|
|
76
93
|
Common existing features: Users (auth), Authentication, KanbanBoard, Document, etc.
|
|
77
94
|
|
|
78
|
-
### Step
|
|
95
|
+
### Step 2: Write Schema
|
|
79
96
|
\`\`\`bash
|
|
80
97
|
# Use Write tool to create /tmp/scaffold.json
|
|
81
98
|
\`\`\`
|
|
82
99
|
|
|
83
|
-
### Step
|
|
100
|
+
### Step 3: Run Scaffold
|
|
84
101
|
\`\`\`bash
|
|
85
102
|
scaffold --schema /tmp/scaffold.json --output /workspace --force --full
|
|
86
103
|
\`\`\`
|
|
87
104
|
|
|
88
|
-
### Step
|
|
89
|
-
|
|
105
|
+
### Step 4: Restart Backend
|
|
106
|
+
\`\`\`bash
|
|
107
|
+
mcp__agent-insights__restart_services
|
|
108
|
+
\`\`\`
|
|
109
|
+
This regenerates swagger and applies migrations.
|
|
110
|
+
|
|
111
|
+
### Step 5: Report & STOP
|
|
112
|
+
If everything succeeded, respond with:
|
|
90
113
|
- Entities created: [names from output]
|
|
91
114
|
- Routes: /super-admin/[plural] for each
|
|
92
115
|
- DONE
|
|
@@ -241,7 +264,7 @@ DO NOT phase/split into multiple runs - that's slower and error-prone.
|
|
|
241
264
|
- Enrollment depends on Student + CourseOffering - CLI handles it
|
|
242
265
|
- ONE scaffold run creates ALL 6 entities with correct FK relationships
|
|
243
266
|
|
|
244
|
-
REMEMBER: ls \u2192 Write \u2192 Run \u2192 Restart \u2192 STOP. No verification needed.`};var ie={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.
|
|
267
|
+
REMEMBER: Read spec (if referenced) \u2192 ls \u2192 Write \u2192 Run \u2192 Restart \u2192 STOP. No verification needed.`};var ie={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.
|
|
245
268
|
|
|
246
269
|
## Process
|
|
247
270
|
1. Read the error message carefully - understand WHAT and WHERE
|
|
@@ -439,7 +462,27 @@ Write custom backend code that goes BEYOND what scaffold generates:
|
|
|
439
462
|
- Business logic
|
|
440
463
|
- Lookup queries for dropdowns
|
|
441
464
|
|
|
465
|
+
## \u26A0\uFE0F SPEC AWARENESS (READ THIS FIRST!)
|
|
466
|
+
|
|
467
|
+
**If the card description contains "\u{1F4CB} Spec:" or mentions a specification:**
|
|
468
|
+
1. Extract the spec name/slug from the card description
|
|
469
|
+
2. Call \`read_specification("{spec-slug}")\` to get the FULL specification
|
|
470
|
+
3. Use BOTH the card description AND the spec to understand:
|
|
471
|
+
- Business logic rules
|
|
472
|
+
- Validation requirements
|
|
473
|
+
- Edge cases and special considerations
|
|
474
|
+
- How this feature fits into the bigger picture
|
|
475
|
+
|
|
476
|
+
**Example:** If card says "\u{1F4CB} Spec: time-off-management", run:
|
|
477
|
+
\`\`\`
|
|
478
|
+
read_specification("time-off-management")
|
|
479
|
+
\`\`\`
|
|
480
|
+
|
|
481
|
+
The spec often contains crucial details about business rules that may not fit in the card description!
|
|
482
|
+
|
|
442
483
|
## BEFORE YOU START
|
|
484
|
+
1. **Read the spec** (if referenced in card)
|
|
485
|
+
2. **Check existing features:**
|
|
443
486
|
\`\`\`bash
|
|
444
487
|
ls /workspace/packages/dotnet-api/Source/Features/
|
|
445
488
|
\`\`\`
|
|
@@ -510,28 +553,46 @@ Write frontend code using GENERATED API hooks (never manual fetch):
|
|
|
510
553
|
- Custom hooks for state management
|
|
511
554
|
- Forms with validation
|
|
512
555
|
|
|
556
|
+
## \u26A0\uFE0F SPEC AWARENESS (READ THIS FIRST!)
|
|
557
|
+
|
|
558
|
+
**If the card description contains "\u{1F4CB} Spec:" or mentions a specification:**
|
|
559
|
+
1. Extract the spec name/slug from the card description
|
|
560
|
+
2. Call \`read_specification("{spec-slug}")\` to get the FULL specification
|
|
561
|
+
3. Use BOTH the card description AND the spec to understand:
|
|
562
|
+
- User workflows and interactions
|
|
563
|
+
- UI/UX requirements
|
|
564
|
+
- What data needs to be displayed
|
|
565
|
+
- How components should behave
|
|
566
|
+
|
|
567
|
+
**Example:** If card says "\u{1F4CB} Spec: time-off-management", run:
|
|
568
|
+
\`\`\`
|
|
569
|
+
read_specification("time-off-management")
|
|
570
|
+
\`\`\`
|
|
571
|
+
|
|
572
|
+
The spec contains the full user story and UX requirements that help you build the right UI!
|
|
573
|
+
|
|
513
574
|
## BEFORE YOU START
|
|
514
|
-
1.
|
|
575
|
+
1. **Read the spec** (if referenced in card)
|
|
576
|
+
|
|
577
|
+
2. Check existing features for patterns:
|
|
515
578
|
\`\`\`bash
|
|
516
579
|
ls /workspace/packages/backoffice-web/src/applications/super-admin/features/
|
|
517
580
|
\`\`\`
|
|
518
581
|
|
|
519
|
-
|
|
582
|
+
3. Check generated API hooks exist:
|
|
520
583
|
\`\`\`bash
|
|
521
584
|
grep -l "{EntityName}" /workspace/packages/backoffice-web/src/generated/queries-commands.ts
|
|
522
585
|
\`\`\`
|
|
523
586
|
|
|
524
|
-
|
|
525
|
-
Scaffold output includes
|
|
587
|
+
4. MAKE SURE FUNCTIONALITY YOU ARE ABOUT TO BUILD DOES NOT ALREADY EXIST!
|
|
588
|
+
Scaffold output includes:
|
|
526
589
|
- {Entity}Page (full CRUD list view)
|
|
527
590
|
- {Entity}Table (table component)
|
|
528
591
|
- {Entity}CreateDialog (add form)
|
|
529
592
|
- {Entity}DetailsDialog (edit/detail view)
|
|
530
593
|
- use{Entity}Management hook (custom logic)
|
|
531
594
|
\u2192 These are COMPLETE and reusable. Never rebuild
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
.
|
|
595
|
+
Check /workspace/packages/backoffice-web/src/applications/super-admin/features/{entity}/
|
|
535
596
|
|
|
536
597
|
**\u26A0\uFE0F If hooks don't exist, STOP and tell the main agent to run backend first!**
|
|
537
598
|
|
|
@@ -771,21 +832,21 @@ Before saving, verify your context:
|
|
|
771
832
|
3. **Show patterns** - Include small code examples for common tasks
|
|
772
833
|
4. **Prioritize** - Put most critical info in <critical_rules>
|
|
773
834
|
5. **Keep it scannable** - Use bullets, clear sections
|
|
774
|
-
6. **Update, don't replace** - If context exists, improve it rather than starting fresh`};import*as z from"fs";import*as
|
|
835
|
+
6. **Update, don't replace** - If context exists, improve it rather than starting fresh`};import*as z from"fs";import*as $e from"path";var de=class{buffer=[];flushInterval=null;sessionId=null;outputDir;flushIntervalMs;constructor(t){this.outputDir=t.outputDir||"/tmp/agent-debug",this.flushIntervalMs=t.flushIntervalMs||3e3,t.enabled&&(this.ensureOutputDir(),this.startFlushInterval())}ensureOutputDir(){z.existsSync(this.outputDir)||z.mkdirSync(this.outputDir,{recursive:!0})}startFlushInterval(){this.flushInterval=setInterval(()=>{this.flush()},this.flushIntervalMs)}setSessionId(t){this.sessionId=t}log(t){let a={timestamp:new Date().toISOString(),message:t};this.buffer.push(JSON.stringify(a,null,2)+`
|
|
775
836
|
---
|
|
776
|
-
`)}getLogFilePath(){let n=(this.sessionId||"unknown").replace(/[^a-zA-Z0-9-]/g,"_").slice(0,50),a=new Date().toISOString().split("T")[0];return
|
|
837
|
+
`)}getLogFilePath(){let n=(this.sessionId||"unknown").replace(/[^a-zA-Z0-9-]/g,"_").slice(0,50),a=new Date().toISOString().split("T")[0];return $e.join(this.outputDir,`session-${a}-${n}.log`)}flush(){if(this.buffer.length===0)return;let t=this.buffer.join("");this.buffer=[];let n=this.getLogFilePath();z.appendFileSync(n,t)}stop(){this.flushInterval&&(clearInterval(this.flushInterval),this.flushInterval=null),this.flush()}};function We(e){return e?typeof e=="boolean"?e?new de({enabled:!0}):null:e.enabled?new de(e):null:null}import{z as c}from"zod";import*as J from"fs/promises";import*as E from"path";var Ge=c.object({port:c.number(),startCommand:c.string(),buildCommand:c.string().optional(),typecheckCommand:c.string().optional(),logFile:c.string().optional(),healthEndpoint:c.string().optional(),extensions:c.array(c.string())}),ut=c.object({port:c.number(),type:c.enum(["postgres","mysql","sqlite","mongodb"])}),ft=c.object({command:c.string(),schemaPath:c.string().optional()}),gt=c.object({email:c.string().optional(),name:c.string().optional(),commitPrefix:c.string().optional()}),mt=c.object({enabled:c.boolean(),port:c.number().optional()}),ee=c.object({version:c.literal("1.0"),paths:c.object({workspace:c.string(),backend:c.string().optional(),frontend:c.string().optional(),features:c.object({backend:c.string().optional(),frontend:c.string().optional()}).optional()}),services:c.object({backend:Ge.optional(),frontend:Ge.optional(),database:ut.optional()}).optional(),scaffold:ft.optional(),git:gt.optional(),techStack:c.object({backend:c.array(c.string()).optional(),frontend:c.array(c.string()).optional(),patterns:c.array(c.string()).optional()}).optional(),tunnel:mt.optional()});function O(e){return{version:"1.0",paths:{workspace:e||process.env.WORKSPACE_DIR||"/workspace",backend:"packages/dotnet-api",frontend:"packages/backoffice-web",features:{backend:"Source/Features",frontend:"src/applications/super-admin/features"}},services:{backend:{port:5338,startCommand:"dotnet run",buildCommand:"dotnet build",typecheckCommand:"dotnet build --no-restore",logFile:"/tmp/api.log",healthEndpoint:"http://localhost:5338",extensions:[".cs",".csproj"]},frontend:{port:5173,startCommand:"npm run dev",typecheckCommand:"npx tsc -p tsconfig.app.json --noEmit",logFile:"/tmp/web.log",healthEndpoint:"http://localhost:5173",extensions:[".ts",".tsx",".json"]},database:{port:43594,type:"postgres"}},scaffold:{command:"scaffold --schema /tmp/scaffold.json --output /workspace --force --full",schemaPath:"/tmp/scaffold.json"},git:{email:"agent@dotnetmentor.se",name:"Agent",commitPrefix:"[agent]"},techStack:{backend:[".NET 9","PostgreSQL","MediatR (CQRS)","EF Core"],frontend:["React 19","TypeScript","Vite","MUI","TanStack Query"],patterns:["Vertical slices","CQRS","Soft delete","Timestamps"]},tunnel:{enabled:!0,port:5173}}}var He="project-config.json",Ye=".sdd";async function Ke(e){let{workspaceDir:t,providedConfig:n,requireConfig:a}=e;if(n)return{config:ee.parse(n),source:"provided"};let s=E.join(t,Ye,He);try{let o=await J.readFile(s,"utf-8"),r=JSON.parse(o);return{config:ee.parse(r),source:"file",configPath:s}}catch(o){o instanceof Error&&"code"in o&&o.code!=="ENOENT"&&console.warn(`[Config] Warning: Invalid project-config.json at ${s}:`,o instanceof c.ZodError?o.errors:o.message)}if(a)throw new Error(`No project configuration found at ${s} and requireConfig is true`);return{config:O(t),source:"default"}}async function Qe(e,t){let n=E.join(e,Ye),a=E.join(n,He);await J.mkdir(n,{recursive:!0});let s=ee.parse(t);return await J.writeFile(a,JSON.stringify(s,null,2),"utf-8"),a}function pe(e,t){if(t)return E.isAbsolute(t)?t:E.join(e.paths.workspace,t)}function F(e){return pe(e,e.paths.backend)}function N(e){return pe(e,e.paths.frontend)}function te(e){let t=F(e);if(!(!t||!e.paths.features?.backend))return E.join(t,e.paths.features.backend)}function X(e){let t=N(e);if(!(!t||!e.paths.features?.frontend))return E.join(t,e.paths.features.frontend)}function Ve(e,t){if(!e.services?.backend?.extensions)return!1;let n=E.extname(t).toLowerCase();return e.services.backend.extensions.includes(n)}function ze(e,t){if(!e.services?.frontend?.extensions)return!1;let n=E.extname(t).toLowerCase();return e.services.frontend.extensions.includes(n)}function ue(e){let t=[];return e.techStack?.backend?.length&&t.push(`**Backend:** ${e.techStack.backend.join(", ")}`),e.techStack?.frontend?.length&&t.push(`**Frontend:** ${e.techStack.frontend.join(", ")}`),e.techStack?.patterns?.length&&t.push(`**Patterns:** ${e.techStack.patterns.join(", ")}`),t.join(`
|
|
777
838
|
`)}function Je(e){let t=[];return e.paths.backend&&t.push(`- Backend: ${F(e)}`),e.paths.frontend&&t.push(`- Frontend: ${N(e)}`),e.paths.features?.backend&&t.push(`- Backend Features: ${te(e)}`),e.paths.features?.frontend&&t.push(`- Frontend Features: ${X(e)}`),t.join(`
|
|
778
|
-
`)}function
|
|
839
|
+
`)}function fe(e){let t=[];return e.services?.backend&&t.push(`| Backend | ${F(e)} | ${e.services.backend.port} | ${e.services.backend.logFile||"N/A"} |`),e.services?.frontend&&t.push(`| Frontend | ${N(e)} | ${e.services.frontend.port} | ${e.services.frontend.logFile||"N/A"} |`),e.services?.database&&t.push(`| Database (${e.services.database.type}) | localhost | ${e.services.database.port} | - |`),t.length===0?"":`| Service | Location | Port | Logs |
|
|
779
840
|
|---------|----------|------|------|
|
|
780
841
|
${t.join(`
|
|
781
|
-
`)}`}function Xe(e={}){let{debugMode:t=!1,projectPrompt:n=null,isLocal:a=!1,projectConfig:
|
|
842
|
+
`)}`}function Xe(e={}){let{debugMode:t=!1,projectPrompt:n=null,isLocal:a=!1,projectConfig:s=O(),useDefaultStack:o=!0}=e,r=n?`
|
|
782
843
|
<project_context>
|
|
783
844
|
${n}
|
|
784
845
|
</project_context>
|
|
785
846
|
|
|
786
847
|
---
|
|
787
848
|
|
|
788
|
-
`:"",
|
|
849
|
+
`:"",g=t?`
|
|
789
850
|
## \u{1F6D1} DEBUG MODE ACTIVE - HIGHEST PRIORITY
|
|
790
851
|
|
|
791
852
|
**You are in DEBUG mode. This instruction block OVERRIDES ALL OTHER INSTRUCTIONS.**
|
|
@@ -823,7 +884,7 @@ Then STOP. Do not continue. Do not try to fix it. Wait for user.
|
|
|
823
884
|
|
|
824
885
|
---
|
|
825
886
|
|
|
826
|
-
`:"",_=ht(
|
|
887
|
+
`:"",_=ht(s),f=yt(s,a),u=kt(s,o),C=bt(o),R=o?wt(s):"",B=o?St(s,a):"",q=_t(o);return`${r}${g}# Agent Instructions
|
|
827
888
|
|
|
828
889
|
## \u26D4 CRITICAL: EVERYTHING IS A KANBAN TASK
|
|
829
890
|
|
|
@@ -947,7 +1008,7 @@ When user asks to build ANY feature, your FIRST action is to delegate to @planni
|
|
|
947
1008
|
- Use EnterPlanMode or any built-in planning
|
|
948
1009
|
- Skip the @planning step
|
|
949
1010
|
|
|
950
|
-
${
|
|
1011
|
+
${C}
|
|
951
1012
|
|
|
952
1013
|
---
|
|
953
1014
|
|
|
@@ -958,27 +1019,27 @@ You know that overengineering is the enemy of good software. You are pragmatic a
|
|
|
958
1019
|
|
|
959
1020
|
${_}
|
|
960
1021
|
|
|
961
|
-
${
|
|
1022
|
+
${f}
|
|
962
1023
|
|
|
963
1024
|
${u}
|
|
964
1025
|
|
|
965
|
-
${
|
|
1026
|
+
${q}
|
|
966
1027
|
|
|
967
1028
|
${R}
|
|
968
1029
|
|
|
969
1030
|
${B}
|
|
970
|
-
`}function ht(e){let t=ue(e),n=F(e),a=N(e),
|
|
1031
|
+
`}function ht(e){let t=ue(e),n=F(e),a=N(e),s=te(e),o=X(e),r=[];s&&r.push(`- Backend: "${s}/{Entity}/"`),o&&r.push(`- Frontend: "${o}/{entity}/"`);let g=e.techStack?.patterns?.length?`
|
|
971
1032
|
**Key patterns:**
|
|
972
1033
|
${e.techStack.patterns.map(u=>`- ${u}`).join(`
|
|
973
|
-
`)}`:"",
|
|
1034
|
+
`)}`:"",f=e.techStack?.backend?.some(u=>u.includes(".NET")||u.includes("C#"))??!1?`
|
|
974
1035
|
|
|
975
1036
|
**\u26A0\uFE0F User entity exists!** ASP.NET Identity "ApplicationUser" at "Features/Users/" - don't scaffold User, just relate to it.`:"";return`<tech-stack>
|
|
976
1037
|
${t}
|
|
977
|
-
${
|
|
1038
|
+
${r.length>0?`
|
|
978
1039
|
**Architecture:** Feature folders
|
|
979
|
-
${
|
|
1040
|
+
${r.join(`
|
|
980
1041
|
`)}`:""}
|
|
981
|
-
${
|
|
1042
|
+
${g}${f}
|
|
982
1043
|
</tech-stack>`}function yt(e,t){return t?`<environment>
|
|
983
1044
|
**LOCAL MODE - User manages their own services.**
|
|
984
1045
|
|
|
@@ -1000,7 +1061,7 @@ The user is responsible for:
|
|
|
1000
1061
|
</environment>`:`<environment>
|
|
1001
1062
|
Docker container with pre-started services:
|
|
1002
1063
|
|
|
1003
|
-
${
|
|
1064
|
+
${fe(e)}
|
|
1004
1065
|
${e.tunnel?.enabled?"| Tunnel | - | - | Exposes frontend to user |":""}
|
|
1005
1066
|
|
|
1006
1067
|
User sees app through **Cloudflare tunnel** (live preview in browser).
|
|
@@ -1009,19 +1070,21 @@ User sees app through **Cloudflare tunnel** (live preview in browser).
|
|
|
1009
1070
|
| Trigger | Subagent | Example |
|
|
1010
1071
|
|---------|----------|---------|
|
|
1011
1072
|
| Any new feature request | @planning | "build a golf app", "add tasks" |
|
|
1073
|
+
| Create tasks from spec | @kanban | "create tasks for time-off spec" |
|
|
1012
1074
|
| Create entity, CRUD, scaffold | @scaffolding | "add task management" |
|
|
1013
1075
|
| Custom backend code | @backend | "add lookup query" |
|
|
1014
1076
|
| Frontend UI code | @frontend | "create the UI" |
|
|
1015
1077
|
| Build error, bug, fix | @debugger | "error CS0246" |
|
|
1016
1078
|
|
|
1017
|
-
**Flow:** @planning \u2192
|
|
1079
|
+
**Flow:** @planning \u2192 @kanban (creates detailed tasks) \u2192 @scaffolding \u2192 @backend \u2192 @frontend \u2192 @debugger`:`**\u2705 ALWAYS delegate to these subagents:**
|
|
1018
1080
|
|
|
1019
1081
|
| Trigger | Subagent | Example |
|
|
1020
1082
|
|---------|----------|---------|
|
|
1021
1083
|
| Any new feature request | @planning | "build a feature", "add tasks" |
|
|
1084
|
+
| Create tasks from spec | @kanban | "create tasks for the spec" |
|
|
1022
1085
|
| Build error, bug, fix | @debugger | "fix this error" |
|
|
1023
1086
|
|
|
1024
|
-
**Flow:** @planning \u2192
|
|
1087
|
+
**Flow:** @planning \u2192 @kanban (creates detailed tasks) \u2192 Execute work \u2192 @debugger (if issues)
|
|
1025
1088
|
|
|
1026
1089
|
Note: For this project, you implement the code directly after planning (no stack-specific subagents).
|
|
1027
1090
|
Check \`.claude/skills/\` for project-specific patterns and conventions.`}function _t(e){return e?`<workflow>
|
|
@@ -1029,42 +1092,42 @@ Check \`.claude/skills/\` for project-specific patterns and conventions.`}functi
|
|
|
1029
1092
|
1. **@planning** - Gather requirements, design spec, saves it.
|
|
1030
1093
|
2. **\u23F8\uFE0F STOP & WAIT** - Tell user: "Spec is ready! Review it and tell me when to implement."
|
|
1031
1094
|
3. **User approves** - User says "looks good", "let's build".
|
|
1032
|
-
4. **KANBAN CREATION** -
|
|
1095
|
+
4. **KANBAN CREATION** - Delegate to @kanban agent OR create cards yourself:
|
|
1033
1096
|
|
|
1034
|
-
|
|
1097
|
+
**Option A: Delegate to @kanban** (recommended)
|
|
1098
|
+
- "@kanban, create tasks for the {spec-name} specification"
|
|
1099
|
+
- The kanban agent reads the spec and creates detailed cards automatically
|
|
1035
1100
|
|
|
1036
|
-
**
|
|
1101
|
+
**Option B: Create cards yourself** (if @kanban unavailable)
|
|
1102
|
+
- Read the spec with \`read_specification("{spec-slug}")\`
|
|
1103
|
+
- Create detailed cards following the format below
|
|
1037
1104
|
|
|
1038
|
-
|
|
1105
|
+
**\u26A0\uFE0F CRITICAL: Cards MUST include spec details AND a spec reference!**
|
|
1039
1106
|
|
|
1040
|
-
|
|
1041
|
-
- Entity name and all its fields/properties from the spec
|
|
1042
|
-
- Relationships to other entities (e.g., "belongs to Project", "has many Tasks")
|
|
1043
|
-
- Any enum types or special field types
|
|
1107
|
+
**WHY THIS MATTERS:** Subagents (@scaffolding, @backend, @frontend) receive ONLY the card title and description as context. They do NOT automatically see the original specification.
|
|
1044
1108
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
- Business rules or validation requirements
|
|
1049
|
-
- Example: "Implement GetTasksByStatus query that filters by status enum and orders by dueDate"
|
|
1109
|
+
**CARD DESCRIPTION FORMAT:**
|
|
1110
|
+
\`\`\`
|
|
1111
|
+
\u{1F4CB} Spec: {spec-slug}
|
|
1050
1112
|
|
|
1051
|
-
|
|
1052
|
-
- Specific UI components needed from the spec
|
|
1053
|
-
- User interactions and workflows
|
|
1054
|
-
- Which data needs to be displayed and how
|
|
1055
|
-
- Example: "Create TaskBoard component with drag-drop between status columns"
|
|
1113
|
+
{Specific requirements extracted from the spec}
|
|
1056
1114
|
|
|
1057
|
-
|
|
1115
|
+
---
|
|
1116
|
+
\u{1F4A1} Read full spec: read_specification("{spec-slug}")
|
|
1117
|
+
\`\`\`
|
|
1058
1118
|
|
|
1059
|
-
**
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1119
|
+
**This format ensures:**
|
|
1120
|
+
1. Subagents have immediate context (extracted details)
|
|
1121
|
+
2. Subagents can read the full spec if they need more detail
|
|
1122
|
+
3. There's a single source of truth (the spec)
|
|
1123
|
+
|
|
1124
|
+
**For Scaffold cards** - include: entity name, ALL fields with types, ALL relations, features
|
|
1125
|
+
**For Backend cards** - include: endpoints, business rules, validation, inputs/outputs
|
|
1126
|
+
**For Frontend cards** - include: components, user workflows, data to display
|
|
1063
1127
|
|
|
1064
|
-
**GOOD example
|
|
1128
|
+
**GOOD example:**
|
|
1065
1129
|
- Title: "Scaffold Task Feature"
|
|
1066
|
-
- Description: "
|
|
1067
|
-
- Subtasks: "Add title field", "Add status enum", "Add Project relationship", etc.
|
|
1130
|
+
- Description: "\u{1F4CB} Spec: task-management\\n\\nCreate 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.\\n\\n---\\n\u{1F4A1} Read full spec: read_specification(\\"task-management\\")"
|
|
1068
1131
|
|
|
1069
1132
|
5. **EXECUTION LOOP**:
|
|
1070
1133
|
- "get_kanban_board"
|
|
@@ -1085,18 +1148,26 @@ Check \`.claude/skills/\` for project-specific patterns and conventions.`}functi
|
|
|
1085
1148
|
1. **@planning** - Gather requirements, design spec, saves it.
|
|
1086
1149
|
2. **\u23F8\uFE0F STOP & WAIT** - Tell user: "Spec is ready! Review it and tell me when to implement."
|
|
1087
1150
|
3. **User approves** - User says "looks good", "let's build".
|
|
1088
|
-
4. **KANBAN CREATION** -
|
|
1151
|
+
4. **KANBAN CREATION** - Delegate to @kanban OR create cards yourself:
|
|
1152
|
+
|
|
1153
|
+
**\u26A0\uFE0F CRITICAL: Cards MUST include spec reference AND extracted details!**
|
|
1154
|
+
|
|
1155
|
+
**CARD DESCRIPTION FORMAT:**
|
|
1156
|
+
\`\`\`
|
|
1157
|
+
\u{1F4CB} Spec: {spec-slug}
|
|
1089
1158
|
|
|
1090
|
-
|
|
1159
|
+
{Specific requirements extracted from the spec}
|
|
1091
1160
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1161
|
+
---
|
|
1162
|
+
\u{1F4A1} Read full spec: read_specification("{spec-slug}")
|
|
1163
|
+
\`\`\`
|
|
1164
|
+
|
|
1165
|
+
This ensures you can read the full spec for context when picking up the task.
|
|
1096
1166
|
|
|
1097
1167
|
5. **EXECUTION LOOP**:
|
|
1098
1168
|
- "get_kanban_board"
|
|
1099
1169
|
- Pick top "Todo" card \u2192 Move to "In Progress"
|
|
1170
|
+
- **If card references a spec, read it first** for full context
|
|
1100
1171
|
- Implement the feature (check \`.claude/skills/\` for project patterns)
|
|
1101
1172
|
- Move card to "Done"
|
|
1102
1173
|
- Repeat until board is empty OR if you need user input.
|
|
@@ -1105,6 +1176,7 @@ Check \`.claude/skills/\` for project-specific patterns and conventions.`}functi
|
|
|
1105
1176
|
|
|
1106
1177
|
**Rules:**
|
|
1107
1178
|
- After @planning \u2192 STOP and wait for user to approve the spec
|
|
1179
|
+
- Always read the referenced spec before starting work on a card
|
|
1108
1180
|
- Check project's existing code for patterns before implementing
|
|
1109
1181
|
- Use @debugger if you encounter build errors or bugs
|
|
1110
1182
|
</workflow>`}function kt(e,t){let n=`**@planning** - Design before building:
|
|
@@ -1112,18 +1184,23 @@ Check \`.claude/skills/\` for project-specific patterns and conventions.`}functi
|
|
|
1112
1184
|
- Asks questions, designs spec
|
|
1113
1185
|
- **Saves spec using save_specification MCP tool**
|
|
1114
1186
|
- **After @planning completes: STOP and wait for user approval!**
|
|
1115
|
-
|
|
1187
|
+
|
|
1188
|
+
**@kanban** - Break down specs into tasks:
|
|
1189
|
+
- Reads the specification and creates detailed Kanban cards
|
|
1190
|
+
- Cards include spec reference + extracted details
|
|
1191
|
+
- Subagents can work independently with full context
|
|
1192
|
+
`;if(t){let a=!!e.scaffold?.command,s=!!e.services?.backend,o=!!e.services?.frontend;a&&(n+=`
|
|
1116
1193
|
**@scaffolding** - Create CRUD entities, database, backend & frontend:
|
|
1117
1194
|
- "Add X management" \u2192 one-shot all entities, everything from entity creation to frontend hooks and pages!
|
|
1118
1195
|
- NEVER write scaffold JSON yourself
|
|
1119
|
-
`),
|
|
1196
|
+
`),s&&(n+=`
|
|
1120
1197
|
**@backend** - Custom backend code (non-scaffold):
|
|
1121
1198
|
- Custom queries, endpoints, lookups
|
|
1122
1199
|
- Runs build + swagger generation before done
|
|
1123
1200
|
- **Run \`./scripts/generate-swagger.sh\` after adding/changing endpoints**
|
|
1124
1201
|
- **Run \`./scripts/generate-signalr.sh\` after adding/changing SignalR hubs**
|
|
1125
|
-
${
|
|
1126
|
-
`),
|
|
1202
|
+
${o?"- **Must complete before @frontend starts!**":""}
|
|
1203
|
+
`),o&&(n+=`
|
|
1127
1204
|
**@frontend** - UI components:
|
|
1128
1205
|
- Uses GENERATED API hooks only
|
|
1129
1206
|
- Never writes manual fetch calls
|
|
@@ -1178,7 +1255,7 @@ queryClient.invalidateQueries({ queryKey: getGetApiPeopleQueryKey() })
|
|
|
1178
1255
|
|
|
1179
1256
|
Check the project's existing patterns for API calls and data fetching.
|
|
1180
1257
|
Look for existing feature code to understand the correct patterns before writing new API calls.
|
|
1181
|
-
</frontend-api>`:""}function St(e,t){if(!e.services?.frontend)return"";let n=e.services.frontend.port,a=X(e),
|
|
1258
|
+
</frontend-api>`:""}function St(e,t){if(!e.services?.frontend)return"";let n=e.services.frontend.port,a=X(e),s=`http://localhost:${n}`;return`<ui-verification>
|
|
1182
1259
|
## \u{1F50D} UI Verification with Playwright
|
|
1183
1260
|
|
|
1184
1261
|
You have access to **Playwright MCP** tools to verify the UI works correctly after making changes.
|
|
@@ -1236,7 +1313,7 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
|
|
|
1236
1313
|
- To test forms and interactions
|
|
1237
1314
|
|
|
1238
1315
|
**Verification workflow:**
|
|
1239
|
-
1. Navigate to the app: \`browser_navigate\` to \`${a?.includes("super-admin")?`${
|
|
1316
|
+
1. Navigate to the app: \`browser_navigate\` to \`${a?.includes("super-admin")?`${s}/super-admin/[feature]`:`${s}/[feature]`}\`
|
|
1240
1317
|
2. Get page structure: \`browser_snapshot\` returns the accessibility tree (LLM-friendly)
|
|
1241
1318
|
3. Optional: Take screenshot for user: \`browser_take_screenshot\`
|
|
1242
1319
|
4. Verify expected elements exist in the snapshot
|
|
@@ -1260,13 +1337,13 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
|
|
|
1260
1337
|
- **Multi-tab apps**: Use \`browser_tabs\` to manage multiple windows/tabs in legacy applications
|
|
1261
1338
|
- **Large snapshots**: For pages with huge dropdowns (e.g., country lists), use \`browser_evaluate\` to collapse them first
|
|
1262
1339
|
- ${t?"In local mode, the browser window will be visible to you":"In container mode, browser runs headless"}
|
|
1263
|
-
</ui-verification>`}function Ze(e){return e&&e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var vt=null;function et(){return Ze(vt)}import*as W from"path";import*as
|
|
1340
|
+
</ui-verification>`}function Ze(e){return e&&e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var vt=null;function et(){return Ze(vt)}import*as W from"path";import*as Te from"fs";import{fileURLToPath as Ct}from"url";var Et=_e(),ve=W.dirname(Ct(import.meta.url)),tt=[W.resolve(ve,"../mcps/stdio-server.js"),W.resolve(ve,"./adapters/mcps/stdio-server.js")],ne=tt.find(e=>Te.existsSync(e))||tt[0];console.log("[MCP] Stdio server path resolved:",ne);console.log("[MCP] __dirname:",ve);console.log("[MCP] File exists:",Te.existsSync(ne));async function nt(e,t={}){let{model:n,sessionId:a=null,projectId:s=null,apiUrl:o=null,callbacks:r={},debugLog:g,abortController:_,debugMode:f=!1,isLocal:u=process.env.LOCAL_MODE==="true",projectConfig:C=O(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:R=!0}=t,B=We(g),q=new Set,Y=a||"",K,$,T,U=!1,Z=!1,I=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[ne]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",s||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",I),console.log("[MCP] File exists check:",ne);try{for await(let d of Tt({prompt:e,options:{abortController:_,cwd:I,resume:a??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","Skill","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...u?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__agent-planning__reference_projects_list","mcp__agent-planning__reference_project_tree","mcp__agent-planning__reference_project_read_file","mcp__agent-planning__reference_project_download","mcp__agent-planning__reference_project_update_status","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_fill_form","mcp__playwright__browser_select_option","mcp__playwright__browser_press_key","mcp__playwright__browser_hover","mcp__playwright__browser_drag","mcp__playwright__browser_file_upload","mcp__playwright__browser_handle_dialog","mcp__playwright__browser_close","mcp__playwright__browser_resize","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_screen_capture","mcp__playwright__browser_screen_move_mouse","mcp__playwright__browser_screen_click","mcp__playwright__browser_screen_drag","mcp__playwright__browser_screen_type","mcp__playwright__browser_evaluate","mcp__playwright__browser_run_code","mcp__playwright__browser_tabs","mcp__playwright__browser_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:n,mcpServers:{"agent-insights":Et,"agent-planning":{type:"stdio",command:"node",args:[ne],env:{WORKSPACE_DIR:I,API_URL:o||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:s||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${W.join(I,".playwright-data")}`,...u?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:R?{scaffolding:ke,debugger:ie,planning:ce,backend:we,frontend:Se,"project-context":le}:{debugger:ie,planning:ce,"project-context":le},systemPrompt:Xe({debugMode:f,projectPrompt:et(),isLocal:u,projectConfig:C,useDefaultStack:R})}}))if(!(!d.uuid||q.has(d.uuid))){switch(q.add(d.uuid),B?.log(d),r.onRawMessage?.(d),d.type){case"assistant":if(d.message?.content)for(let S of d.message.content)S.type==="text"?r.onAssistantText?.(S.text):S.type==="tool_use"?r.onAssistantToolUse?.(S.name,S.input):S.type==="tool_result"&&r.onAssistantToolResult?.(S.tool_use_id,S.content);break;case"user":r.onUserMessage?.(d);break;case"result":if(d.subtype==="success")K=d.result,$=d.total_cost_usd,r.onResult?.(d.result,d.total_cost_usd);else{let S=`Agent error: ${d.subtype}`;T=S,r.onError?.(S)}Z=!0;break;case"system":switch(d.subtype){case"init":Y=d.session_id,B?.setSessionId(d.session_id),r.onSystemInit?.(d.session_id);break;case"status":r.onSystemStatus?.(JSON.stringify(d));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":r.onStreamEvent?.(d);break;case"tool_progress":r.onToolProgress?.(d);break;case"auth_status":r.onAuthStatus?.(d);break}if(Z)break}}catch(d){d instanceof Error&&d.name==="AbortError"||d instanceof Error&&d.message.includes("aborted by user")?(U=!0,r.onAborted?.()):(T=d instanceof Error?d.message:String(d),r.onError?.(T))}finally{B?.stop()}return console.log("after try"),{sessionId:Y,result:K,cost:$,error:T,aborted:U}}function st(e){return`User answered the questions:
|
|
1264
1341
|
|
|
1265
|
-
${Object.entries(e).map(([n,a])=>{let
|
|
1266
|
-
`)}`}import{spawn as
|
|
1267
|
-
`).filter(Boolean);console.log(`[Services] Killing ${p.length} process(es) on port ${l}: ${p.join(", ")}`);for(let m of p)try{process.kill(parseInt(m,10),"SIGKILL")}catch{}}}catch{try{G(`fuser -k ${l}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},d=(l,i)=>new Promise(p=>{if(!l||!l.pid){p();return}console.log(`[Services] Stopping ${i} (PID: ${l.pid})...`);try{process.kill(-l.pid,"SIGTERM")}catch{try{l.kill("SIGTERM")}catch{}}setTimeout(p,500)}),S=async()=>{if(!
|
|
1268
|
-
`);v=A.pop()||"";for(let x of A)x&&Pt(x)&&
|
|
1342
|
+
${Object.entries(e).map(([n,a])=>{let s=Array.isArray(a)?a.join(", "):a;return`${n}: ${s}`}).join(`
|
|
1343
|
+
`)}`}import{spawn as rt,execSync as G}from"child_process";import*as b from"fs";import*as ot from"http";import*as j from"path";function at(e){let{workspaceDir:t,apiPort:n,webPort:a,onBackendError:s,onFrontendError:o,projectConfig:r=O(t)}=e,g=n??r.services?.backend?.port??5338,_=a??r.services?.frontend?.port??5173,f=F(r),u=N(r),C=r.services?.backend?.startCommand??"dotnet run",R=r.services?.backend?.buildCommand??"dotnet build",B=r.services?.backend?.typecheckCommand??"dotnet build --no-restore",q=r.services?.frontend?.startCommand??"npm run dev",Y=r.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",K=r.services?.backend?.logFile??"/tmp/api.log",$=r.services?.frontend?.logFile??"/tmp/web.log",T=null,U=null,Z=l=>new Promise(i=>{let p=setTimeout(()=>i(!1),3e3);ot.get(`http://localhost:${l}`,v=>{clearTimeout(p),i(v.statusCode!==void 0&&v.statusCode<500)}).on("error",()=>{clearTimeout(p),i(!1)})}),I=l=>{try{let i=G(`lsof -ti:${l} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(i){let p=i.split(`
|
|
1344
|
+
`).filter(Boolean);console.log(`[Services] Killing ${p.length} process(es) on port ${l}: ${p.join(", ")}`);for(let m of p)try{process.kill(parseInt(m,10),"SIGKILL")}catch{}}}catch{try{G(`fuser -k ${l}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},d=(l,i)=>new Promise(p=>{if(!l||!l.pid){p();return}console.log(`[Services] Stopping ${i} (PID: ${l.pid})...`);try{process.kill(-l.pid,"SIGTERM")}catch{try{l.kill("SIGTERM")}catch{}}setTimeout(p,500)}),S=async()=>{if(!f||!b.existsSync(f)){console.log("[Services] No backend found, skipping backend start");return}I(g),console.log(`[Services] Starting backend with: ${C}...`);let l=j.dirname(K);b.existsSync(l)||b.mkdirSync(l,{recursive:!0});let i=b.createWriteStream(K,{flags:"a"}),[p,...m]=C.split(" ");T=rt(p,m,{cwd:f,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let v="",k=h=>{let L=h.toString();i.write(L);let A=(v+L).split(`
|
|
1345
|
+
`);v=A.pop()||"";for(let x of A)x&&Pt(x)&&s&&s(x)};T.stdout?.on("data",k),T.stderr?.on("data",k),T.on("exit",h=>{i.end(),h!==0&&h!==null&&s&&s(`Process exited with code ${h}`)}),T.unref()},Ce=async()=>{if(!u||!b.existsSync(j.join(u,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}I(_),console.log(`[Services] Starting frontend with: ${q}...`);let l=j.dirname($);b.existsSync(l)||b.mkdirSync(l,{recursive:!0});let[i,...p]=q.split(" ");U=rt(i,p,{cwd:u,stdio:["ignore",b.openSync($,"w"),b.openSync($,"w")],detached:!0,shell:!0}),U.unref()},Ee=async()=>{await d(T,"backend"),T=null,I(g)},Pe=async()=>{await d(U,"frontend"),U=null,I(_)},ge=async()=>{let[l,i]=await Promise.all([Z(g),Z(_)]);return{api:l,web:i}},Ae=async(l=15e3)=>{let i=Date.now(),p=1e3,m=0,v=null,k=null;for(console.log(`[Services] Waiting for health (timeout: ${l}ms)...`);Date.now()-i<l;){m++;let A=await ge(),x=Date.now()-i;if(A.web&&k===null&&(k=x,console.log(`[Services] \u2713 Frontend (Vite) ready after ${x}ms`)),A.api&&v===null&&(v=x,console.log(`[Services] \u2713 Backend (dotnet) ready after ${x}ms`)),console.log(`[Services] Health poll #${m} (${x}ms): api=${A.api}, web=${A.web}`),A.api&&A.web)return console.log(`[Services] \u2713 Both services healthy after ${x}ms (${m} polls)`),A;await new Promise(dt=>setTimeout(dt,p))}let h=await ge(),L=Date.now()-i;return h.web&&k===null&&console.log(`[Services] \u2713 Frontend (Vite) ready after ${L}ms`),h.api&&v===null&&console.log(`[Services] \u2713 Backend (dotnet) ready after ${L}ms`),console.log(`[Services] \u26A0 Health timeout after ${L}ms: api=${h.api}, web=${h.web}`),h},ct=async l=>{console.log(`[Services] Restarting ${l}...`);let i={success:!0},p=l==="backend"||l==="both",m=l==="frontend"||l==="both";if(p&&await Ee(),m&&await Pe(),p&&f&&b.existsSync(f)){console.log(`[Services] Building backend with: ${R}...`);try{G(R,{cwd:f,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),console.log("[Services] \u2713 Backend build succeeded")}catch(k){let h=k;i.success=!1,i.backendError=h.stderr||h.stdout||"Build failed",console.error("[Services] \u2717 Backend build failed")}}if(m&&u&&b.existsSync(j.join(u,"package.json"))){console.log(`[Services] Type-checking frontend with: ${Y}...`);try{G(Y,{cwd:u,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),console.log("[Services] \u2713 Frontend types OK")}catch(k){let h=k;i.success=!1,i.frontendError=h.stderr||h.stdout||"Type check failed",console.error("[Services] \u2717 Frontend type check failed")}}console.log(`[Services] Starting ${l}...`),p&&await S(),m&&await Ce();let v=await Ae(1e4);if(p&&!v.api){i.success=!1;let k=await Ie("backend",20),h=k.length>0?`
|
|
1269
1346
|
Recent logs:
|
|
1270
1347
|
${k.join(`
|
|
1271
|
-
`)}`:"";i.backendError||(i.backendError=`Backend failed to start.${h}`)}return m&&!v.web&&(i.success=!1,i.frontendError||(i.frontendError="Frontend failed to start")),i},xe=async()=>{if(!
|
|
1272
|
-
`).filter(Boolean)}catch(m){return[`Error reading logs: ${m instanceof Error?m.message:String(m)}`]}};return{startBackend:S,startFrontend:
|
|
1348
|
+
`)}`:"";i.backendError||(i.backendError=`Backend failed to start.${h}`)}return m&&!v.web&&(i.success=!1,i.frontendError||(i.frontendError="Frontend failed to start")),i},xe=async()=>{if(!f||!b.existsSync(f))return{success:!0};try{return G(B,{cwd:f,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),{success:!0}}catch(l){let i=l;return{success:!1,errors:i.stdout||i.stderr||"Build failed"}}},Re=async()=>{if(!u||!b.existsSync(j.join(u,"package.json")))return{success:!0};try{return G(Y,{cwd:u,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),{success:!0}}catch(l){let i=l;return{success:!1,errors:i.stdout||i.stderr||"Type check failed"}}},lt=async()=>{let[l,i]=await Promise.all([xe(),Re()]);return{backend:l,frontend:i}},Ie=async(l,i)=>{let p=l==="backend"?K:$;if(!b.existsSync(p))return[`Log file not found: ${p}`];try{return G(`tail -n ${i} ${p}`,{encoding:"utf-8"}).split(`
|
|
1349
|
+
`).filter(Boolean)}catch(m){return[`Error reading logs: ${m instanceof Error?m.message:String(m)}`]}};return{startBackend:S,startFrontend:Ce,stopBackend:Ee,stopFrontend:Pe,restartServices:ct,checkHealth:ge,waitForHealth:Ae,getProcesses:()=>({backend:T,frontend:U}),checkBackendBuild:xe,checkFrontendTypes:Re,runTypeChecks:lt,tailLogs:Ie,checkSwaggerEndpoints:async l=>{let i=j.join(t,"swagger.json");if(!b.existsSync(i))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let p=JSON.parse(b.readFileSync(i,"utf-8")),m=Object.keys(p.paths||{}),v=[];for(let k of m)if(k.toLowerCase().includes(l.toLowerCase())){let h=Object.keys(p.paths[k]);for(let L of h)v.push(`${L.toUpperCase()} ${k}`)}return{foundEndpoints:v,totalEndpoints:m.length}}catch(p){return{foundEndpoints:[`Error parsing swagger.json: ${p instanceof Error?p.message:String(p)}`],totalEndpoints:0}}}}}function Pt(e){let t=e.toLowerCase();return t.includes("exception")||t.includes("fail:")||t.includes("[err]")||t.includes("[error]")}import{execSync as H}from"child_process";function it(e){let t=async()=>{try{return H("git status --porcelain",{cwd:e,encoding:"utf-8"}).trim().length>0}catch{return!1}},n=async()=>{try{return H("git branch --show-current",{cwd:e,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:t,commitAndPush:async(s,o)=>{try{if(!await t())return{success:!0,noChanges:!0};let r=s.length>60?s.substring(0,60)+"...":s,_=`${o?"[agent] (partial)":"[agent]"} ${r}`;H("git add -A",{cwd:e}),H(`git commit -m "${_.replace(/"/g,'\\"')}"`,{cwd:e,encoding:"utf-8"});let f=H("git rev-parse --short HEAD",{cwd:e,encoding:"utf-8"}).trim();try{H("git push",{cwd:e,stdio:"pipe"})}catch(u){let C=u instanceof Error?u.message:String(u);if(C.includes("no upstream branch")){let R=await n();H(`git push --set-upstream origin ${R}`,{cwd:e,stdio:"pipe"})}else return console.error("[Git] Push failed:",C),{success:!0,commitHash:f,error:`Committed but push failed: ${C}`}}return{success:!0,commitHash:f}}catch(r){let g=r instanceof Error?r.message:String(r);return console.error("[Git] Error:",g),{success:!1,error:g}}},getCurrentBranch:n}}export{ee as ProjectConfigSchema,be as createFilePlanningTransport,he as createFileTransport,it as createLocalGitManager,at as createLocalServiceManager,_e as createMcpServer,st as formatAnswersAsPrompt,te as getBackendFeaturesPath,F as getBackendPath,O as getDefaultConfig,X as getFrontendFeaturesPath,N as getFrontendPath,Je as getPathsDescription,fe as getServicesDescription,ue as getTechStackDescription,Ve as isBackendFile,ze as isFrontendFile,Ke as loadProjectConfig,ye as readPendingQuestions,pe as resolveConfigPath,nt as runAgent,Qe as saveProjectConfig,Oe as setPlanningTransport,Fe as setServiceManager};
|