glenn-code 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/signalr/index.js +97 -67
- package/dist/cli.js +97 -67
- package/dist/core.js +69 -39
- package/dist/index.js +67 -37
- 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
|
|
4
|
+
import{createSdkMcpServer as pt}from"@anthropic-ai/claude-agent-sdk";import{tool as oe}from"@anthropic-ai/claude-agent-sdk";import{z as se}from"zod";import*as re from"fs";import*as De from"path";var D=null;function Oe(e){D=e}function me(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}var It=oe("save_specification",`Save a specification to the database.
|
|
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,17 +12,17 @@ Workflow:
|
|
|
12
12
|
|
|
13
13
|
Example:
|
|
14
14
|
1. Write tool \u2192 save to /tmp/user-auth-spec.md
|
|
15
|
-
2. save_specification(name: "User Authentication", filePath: "/tmp/user-auth-spec.md")`,{name:se.string().describe("Specification name (e.g., 'User Authentication')"),filePath:se.string().describe("Path to the file containing the specification content (written via Write tool)")},async e=>{if(!
|
|
15
|
+
2. save_specification(name: "User Authentication", filePath: "/tmp/user-auth-spec.md")`,{name:se.string().describe("Specification name (e.g., 'User Authentication')"),filePath:se.string().describe("Path to the file containing the specification content (written via Write tool)")},async e=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=De.resolve(e.filePath);if(!re.existsSync(t))return{content:[{type:"text",text:`\u274C File not found: ${e.filePath}. Use Write tool first to create the file.`}]};let n=re.readFileSync(t,"utf-8"),a=me(e.name);return await D.saveSpecification(a,n),{content:[{type:"text",text:`\u2705 Saved specification: ${a}.spec.md`}]}}),Dt=oe("read_specification","Read the content of a specification.",{name:se.string().describe("Name of the specification to read")},async e=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=me(e.name),n=await D.getSpecification(t);return n?{content:[{type:"text",text:n}]}:{content:[{type:"text",text:`\u274C Specification "${e.name}" not found.`}]}}),Ot=oe("list_specifications","List all specifications.",{},async()=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let e=await D.listSpecifications();return e.length===0?{content:[{type:"text",text:"\u{1F4CB} No specifications found."}]}:{content:[{type:"text",text:`\u{1F4CB} Specifications:
|
|
16
16
|
|
|
17
17
|
${e.map(n=>`- ${n.name} (${n.slug}.spec.md)`).join(`
|
|
18
|
-
`)}`}]}}),Ft=oe("delete_specification","Delete a specification.",{name:se.string().describe("Name of the specification to delete")},async e=>{if(!
|
|
18
|
+
`)}`}]}}),Ft=oe("delete_specification","Delete a specification.",{name:se.string().describe("Name of the specification to delete")},async e=>{if(!D)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=me(e.name);return await D.deleteSpecification(t)?{content:[{type:"text",text:`\u{1F5D1}\uFE0F Deleted: ${t}.spec.md`}]}:{content:[{type:"text",text:`\u274C Specification "${e.name}" not found.`}]}});import{tool as M}from"@anthropic-ai/claude-agent-sdk";import{z as Q}from"zod";var y=null;function Fe(e){y=e}var Ne=M("restart_services",`Rebuild and restart services. Call this after making backend changes (.cs files) to apply them.
|
|
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:
|
|
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
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.
|
|
@@ -30,12 +30,12 @@ ${s.join(`
|
|
|
30
30
|
Parameters:
|
|
31
31
|
- target: "backend" (default) | "frontend" | "both"
|
|
32
32
|
|
|
33
|
-
Use target="backend" before running scaffold (required for migrations).`,{target:
|
|
33
|
+
Use target="backend" before running scaffold (required for migrations).`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] stop_services called (target: ${t})`);let n=t==="backend"||t==="both",a=t==="frontend"||t==="both";n&&await y.stopBackend(),a&&await y.stopFrontend();let s=5e3,r=Date.now(),o=!n,f=!a;for(;Date.now()-r<s;){let u=await y.checkHealth();if(n&&!u.api&&(o=!0),a&&!u.web&&(f=!0),o&&f)break;await new Promise(T=>setTimeout(T,500))}let _=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";return{content:[{type:"text",text:o&&f?`\u2705 ${_} stopped successfully.`:`\u26A0\uFE0F ${_} stop requested but health check still shows running. Proceeding anyway.`}]}}),Le=M("start_services",`Start services. Call this AFTER running scaffold to bring services back up.
|
|
34
34
|
|
|
35
35
|
Parameters:
|
|
36
36
|
- target: "backend" (default) | "frontend" | "both"
|
|
37
37
|
|
|
38
|
-
Use target="backend" after running scaffold.`,{target:
|
|
38
|
+
Use target="backend" after running scaffold.`,{target:Q.enum(["backend","frontend","both"]).default("backend").describe("Which service to start")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=e.target||"backend";console.log(`[MCP] start_services called (target: ${t})`);let n=t==="backend"||t==="both",a=t==="frontend"||t==="both";n&&await y.startBackend(),a&&await y.startFrontend();let s=await y.waitForHealth(15e3),r=!n||s.api,o=!a||s.web,f=r&&o,_=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";if(!f){let g=[];return n&&!s.api&&g.push("Backend failed to start"),a&&!s.web&&g.push("Frontend failed to start"),{content:[{type:"text",text:`\u274C ${_} failed to start: ${g.join(", ")}`}]}}return{content:[{type:"text",text:`\u2705 ${_} started successfully.`}]}}),Me=M("check_service_health",`Check if services are running and healthy. Returns current status of backend API and frontend.
|
|
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:
|
|
@@ -46,18 +46,18 @@ ${e.errors}`}]}}),Mt=M("tail_service_log",`Get the last N lines of service logs.
|
|
|
46
46
|
|
|
47
47
|
Parameters:
|
|
48
48
|
- target: "backend" | "frontend"
|
|
49
|
-
- lines: number (default 50)`,{target:
|
|
49
|
+
- lines: number (default 50)`,{target:Q.enum(["backend","frontend"]).describe("Which log to read"),lines:Q.number().default(50).describe("Number of lines to read")},async e=>{if(!y)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=await y.tailLogs(e.target,e.lines);return{content:[{type:"text",text:`Last ${e.lines} lines of ${e.target} log:
|
|
50
50
|
|
|
51
51
|
${t.join(`
|
|
52
52
|
`)}`}]}}),jt=M("check_swagger_endpoints",`Search for endpoints in the generated swagger.json. Use this to verify that new backend endpoints are correctly exposed.
|
|
53
53
|
|
|
54
54
|
Parameters:
|
|
55
|
-
- pattern: string (e.g., "users", "api/reports")`,{pattern:
|
|
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
|
|
59
|
-
`,"utf-8")},async reportProgress(n,a,s){let r=
|
|
60
|
-
`,"utf-8")}}}function ye(e){let t=
|
|
58
|
+
`)}`}]}});import*as P from"fs";import*as V from"path";var $e="/tmp";function he(e){let t=e?V.join(e,".sdd","insights"):V.join(process.cwd(),".sdd","insights");return{async reportInsight(n,a){P.mkdirSync(t,{recursive:!0});let s=a.type==="learning"?"learnings.jsonl":"bugs.jsonl",r=V.join(t,s);P.appendFileSync(r,JSON.stringify(a)+`
|
|
59
|
+
`,"utf-8")},async reportProgress(n,a,s){let r=V.join($e,`agent-progress-${n}.json`),o={timestamp:new Date().toISOString(),event:a,data:s};P.appendFileSync(r,JSON.stringify(o)+`
|
|
60
|
+
`,"utf-8")}}}function ye(e){let t=V.join($e,`agent-questions-${e}.json`);if(!P.existsSync(t))return null;try{let n=P.readFileSync(t,"utf-8");return P.unlinkSync(t),JSON.parse(n)}catch{return null}}import*as w from"fs";import*as ae from"path";function be(e){let t=ae.join(e,".sdd","specifications");function n(){w.existsSync(t)||w.mkdirSync(t,{recursive:!0})}function a(s){return ae.join(t,`${s}.spec.md`)}return{async saveSpecification(s,r){n(),w.writeFileSync(a(s),r,"utf-8")},async getSpecification(s){n();let r=a(s);return w.existsSync(r)?w.readFileSync(r,"utf-8"):null},async listSpecifications(){return n(),w.readdirSync(t).filter(r=>r.endsWith(".spec.md")).map(r=>{let o=w.readFileSync(ae.join(t,r),"utf-8"),f=o.match(/name: "([^"]+)"/),_=o.match(/status: (\w+)/),g=o.match(/version: "([^"]+)"/);return{slug:r.replace(".spec.md",""),name:f?.[1]||r,status:_?.[1]||"unknown",version:g?.[1]||"1.0.0"}})},async deleteSpecification(s){n();let r=a(s);return w.existsSync(r)?(w.unlinkSync(r),!0):!1},async specificationExists(s){return n(),w.existsSync(a(s))}}}function _e(){return pt({name:"agent-insights",version:"1.0.0",tools:[Ne,Ue,Le,Me,je,Be]})}import{query as Ct}from"@anthropic-ai/claude-agent-sdk";var ke={description:"Delegate to this agent when scaffolding new entities or creating CRUD features. This agent handles entity generation using the scaffold CLI with multi-entity support.",model:"inherit",prompt:`You are a scaffolding specialist. Your ONLY job is:
|
|
61
61
|
1. Check existing features (quick ls)
|
|
62
62
|
2. Write ALL entities to ONE /tmp/scaffold.json (big bang, no phasing!)
|
|
63
63
|
3. Run scaffold CLI ONCE
|
|
@@ -430,7 +430,7 @@ Save your spec. The spec will appear in the user's UI.
|
|
|
430
430
|
- Ask ONE question at a time (not all at once!)
|
|
431
431
|
- STOP asking if user says "start implementing"
|
|
432
432
|
- NEVER output the spec as text - use save_specification
|
|
433
|
-
- Maximum 7 questions - don't exhaust the user!`};var
|
|
433
|
+
- Maximum 7 questions - don't exhaust the user!`};var we={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.
|
|
434
434
|
|
|
435
435
|
## YOUR ROLE
|
|
436
436
|
Write custom backend code that goes BEYOND what scaffold generates:
|
|
@@ -502,7 +502,7 @@ public class Get{EntityB}LookupHandler : IQueryHandler<Get{EntityB}LookupQuery,
|
|
|
502
502
|
2. If build succeeds, generate swagger: \`mcp__agent-insights__restart_services\` (this regenerates swagger)
|
|
503
503
|
3. Report what you created and STOP
|
|
504
504
|
|
|
505
|
-
**\u26A0\uFE0F CRITICAL:** Always generate swagger before frontend work begins!`};var
|
|
505
|
+
**\u26A0\uFE0F CRITICAL:** Always generate swagger before frontend work begins!`};var Se={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.
|
|
506
506
|
|
|
507
507
|
## YOUR ROLE
|
|
508
508
|
Write frontend code using GENERATED API hooks (never manual fetch):
|
|
@@ -771,21 +771,21 @@ Before saving, verify your context:
|
|
|
771
771
|
3. **Show patterns** - Include small code examples for common tasks
|
|
772
772
|
4. **Prioritize** - Put most critical info in <critical_rules>
|
|
773
773
|
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
|
|
774
|
+
6. **Update, don't replace** - If context exists, improve it rather than starting fresh`};import*as z from"fs";import*as qe from"path";var de=class{buffer=[];flushInterval=null;sessionId=null;outputDir;flushIntervalMs;constructor(t){this.outputDir=t.outputDir||"/tmp/agent-debug",this.flushIntervalMs=t.flushIntervalMs||3e3,t.enabled&&(this.ensureOutputDir(),this.startFlushInterval())}ensureOutputDir(){z.existsSync(this.outputDir)||z.mkdirSync(this.outputDir,{recursive:!0})}startFlushInterval(){this.flushInterval=setInterval(()=>{this.flush()},this.flushIntervalMs)}setSessionId(t){this.sessionId=t}log(t){let a={timestamp:new Date().toISOString(),message:t};this.buffer.push(JSON.stringify(a,null,2)+`
|
|
775
775
|
---
|
|
776
|
-
`)}getLogFilePath(){let n=(this.sessionId||"unknown").replace(/[^a-zA-Z0-9-]/g,"_").slice(0,50),a=new Date().toISOString().split("T")[0];return qe.join(this.outputDir,`session-${a}-${n}.log`)}flush(){if(this.buffer.length===0)return;let t=this.buffer.join("");this.buffer=[];let n=this.getLogFilePath();
|
|
777
|
-
`)}function Je(e){let t=[];return e.paths.backend&&t.push(`- Backend: ${
|
|
778
|
-
`)}function
|
|
776
|
+
`)}getLogFilePath(){let n=(this.sessionId||"unknown").replace(/[^a-zA-Z0-9-]/g,"_").slice(0,50),a=new Date().toISOString().split("T")[0];return qe.join(this.outputDir,`session-${a}-${n}.log`)}flush(){if(this.buffer.length===0)return;let t=this.buffer.join("");this.buffer=[];let n=this.getLogFilePath();z.appendFileSync(n,t)}stop(){this.flushInterval&&(clearInterval(this.flushInterval),this.flushInterval=null),this.flush()}};function We(e){return e?typeof e=="boolean"?e?new de({enabled:!0}):null:e.enabled?new de(e):null:null}import{z as c}from"zod";import*as J from"fs/promises";import*as E from"path";var Ge=c.object({port:c.number(),startCommand:c.string(),buildCommand:c.string().optional(),typecheckCommand:c.string().optional(),logFile:c.string().optional(),healthEndpoint:c.string().optional(),extensions:c.array(c.string())}),ut=c.object({port:c.number(),type:c.enum(["postgres","mysql","sqlite","mongodb"])}),gt=c.object({command:c.string(),schemaPath:c.string().optional()}),ft=c.object({email:c.string().optional(),name:c.string().optional(),commitPrefix:c.string().optional()}),mt=c.object({enabled:c.boolean(),port:c.number().optional()}),ee=c.object({version:c.literal("1.0"),paths:c.object({workspace:c.string(),backend:c.string().optional(),frontend:c.string().optional(),features:c.object({backend:c.string().optional(),frontend:c.string().optional()}).optional()}),services:c.object({backend:Ge.optional(),frontend:Ge.optional(),database:ut.optional()}).optional(),scaffold:gt.optional(),git:ft.optional(),techStack:c.object({backend:c.array(c.string()).optional(),frontend:c.array(c.string()).optional(),patterns:c.array(c.string()).optional()}).optional(),tunnel:mt.optional()});function O(e){return{version:"1.0",paths:{workspace:e||process.env.WORKSPACE_DIR||"/workspace",backend:"packages/dotnet-api",frontend:"packages/backoffice-web",features:{backend:"Source/Features",frontend:"src/applications/super-admin/features"}},services:{backend:{port:5338,startCommand:"dotnet run",buildCommand:"dotnet build",typecheckCommand:"dotnet build --no-restore",logFile:"/tmp/api.log",healthEndpoint:"http://localhost:5338",extensions:[".cs",".csproj"]},frontend:{port:5173,startCommand:"npm run dev",typecheckCommand:"npx tsc -p tsconfig.app.json --noEmit",logFile:"/tmp/web.log",healthEndpoint:"http://localhost:5173",extensions:[".ts",".tsx",".json"]},database:{port:43594,type:"postgres"}},scaffold:{command:"scaffold --schema /tmp/scaffold.json --output /workspace --force --full",schemaPath:"/tmp/scaffold.json"},git:{email:"agent@dotnetmentor.se",name:"Agent",commitPrefix:"[agent]"},techStack:{backend:[".NET 9","PostgreSQL","MediatR (CQRS)","EF Core"],frontend:["React 19","TypeScript","Vite","MUI","TanStack Query"],patterns:["Vertical slices","CQRS","Soft delete","Timestamps"]},tunnel:{enabled:!0,port:5173}}}var Ye="project-config.json",He=".sdd";async function Ke(e){let{workspaceDir:t,providedConfig:n,requireConfig:a}=e;if(n)return{config:ee.parse(n),source:"provided"};let s=E.join(t,He,Ye);try{let r=await J.readFile(s,"utf-8"),o=JSON.parse(r);return{config:ee.parse(o),source:"file",configPath:s}}catch(r){r instanceof Error&&"code"in r&&r.code!=="ENOENT"&&console.warn(`[Config] Warning: Invalid project-config.json at ${s}:`,r instanceof c.ZodError?r.errors:r.message)}if(a)throw new Error(`No project configuration found at ${s} and requireConfig is true`);return{config:O(t),source:"default"}}async function Qe(e,t){let n=E.join(e,He),a=E.join(n,Ye);await J.mkdir(n,{recursive:!0});let s=ee.parse(t);return await J.writeFile(a,JSON.stringify(s,null,2),"utf-8"),a}function pe(e,t){if(t)return E.isAbsolute(t)?t:E.join(e.paths.workspace,t)}function F(e){return pe(e,e.paths.backend)}function N(e){return pe(e,e.paths.frontend)}function te(e){let t=F(e);if(!(!t||!e.paths.features?.backend))return E.join(t,e.paths.features.backend)}function X(e){let t=N(e);if(!(!t||!e.paths.features?.frontend))return E.join(t,e.paths.features.frontend)}function Ve(e,t){if(!e.services?.backend?.extensions)return!1;let n=E.extname(t).toLowerCase();return e.services.backend.extensions.includes(n)}function ze(e,t){if(!e.services?.frontend?.extensions)return!1;let n=E.extname(t).toLowerCase();return e.services.frontend.extensions.includes(n)}function ue(e){let t=[];return e.techStack?.backend?.length&&t.push(`**Backend:** ${e.techStack.backend.join(", ")}`),e.techStack?.frontend?.length&&t.push(`**Frontend:** ${e.techStack.frontend.join(", ")}`),e.techStack?.patterns?.length&&t.push(`**Patterns:** ${e.techStack.patterns.join(", ")}`),t.join(`
|
|
777
|
+
`)}function Je(e){let t=[];return e.paths.backend&&t.push(`- Backend: ${F(e)}`),e.paths.frontend&&t.push(`- Frontend: ${N(e)}`),e.paths.features?.backend&&t.push(`- Backend Features: ${te(e)}`),e.paths.features?.frontend&&t.push(`- Frontend Features: ${X(e)}`),t.join(`
|
|
778
|
+
`)}function ge(e){let t=[];return e.services?.backend&&t.push(`| Backend | ${F(e)} | ${e.services.backend.port} | ${e.services.backend.logFile||"N/A"} |`),e.services?.frontend&&t.push(`| Frontend | ${N(e)} | ${e.services.frontend.port} | ${e.services.frontend.logFile||"N/A"} |`),e.services?.database&&t.push(`| Database (${e.services.database.type}) | localhost | ${e.services.database.port} | - |`),t.length===0?"":`| Service | Location | Port | Logs |
|
|
779
779
|
|---------|----------|------|------|
|
|
780
780
|
${t.join(`
|
|
781
|
-
`)}`}function Xe(e={}){let{debugMode:t=!1,projectPrompt:n=null,isLocal:a=!1,projectConfig:s=
|
|
781
|
+
`)}`}function Xe(e={}){let{debugMode:t=!1,projectPrompt:n=null,isLocal:a=!1,projectConfig:s=O(),useDefaultStack:r=!0}=e,o=n?`
|
|
782
782
|
<project_context>
|
|
783
783
|
${n}
|
|
784
784
|
</project_context>
|
|
785
785
|
|
|
786
786
|
---
|
|
787
787
|
|
|
788
|
-
`:"",
|
|
788
|
+
`:"",f=t?`
|
|
789
789
|
## \u{1F6D1} DEBUG MODE ACTIVE - HIGHEST PRIORITY
|
|
790
790
|
|
|
791
791
|
**You are in DEBUG mode. This instruction block OVERRIDES ALL OTHER INSTRUCTIONS.**
|
|
@@ -823,7 +823,7 @@ Then STOP. Do not continue. Do not try to fix it. Wait for user.
|
|
|
823
823
|
|
|
824
824
|
---
|
|
825
825
|
|
|
826
|
-
`:"",
|
|
826
|
+
`:"",_=ht(s),g=yt(s,a),u=kt(s,r),T=bt(r),R=r?wt(s):"",B=r?St(s,a):"",$=_t(r);return`${o}${f}# Agent Instructions
|
|
827
827
|
|
|
828
828
|
## \u26D4 CRITICAL: EVERYTHING IS A KANBAN TASK
|
|
829
829
|
|
|
@@ -956,21 +956,21 @@ You know that overengineering is the enemy of good software. You are pragmatic a
|
|
|
956
956
|
|
|
957
957
|
**Your users may be non-technical.** They have ideas, not code. You are their bridge to magic.
|
|
958
958
|
|
|
959
|
-
${
|
|
959
|
+
${_}
|
|
960
960
|
|
|
961
|
-
${
|
|
961
|
+
${g}
|
|
962
962
|
|
|
963
|
-
${
|
|
963
|
+
${u}
|
|
964
964
|
|
|
965
965
|
${$}
|
|
966
966
|
|
|
967
967
|
${R}
|
|
968
968
|
|
|
969
969
|
${B}
|
|
970
|
-
`}function ht(e){let t=
|
|
970
|
+
`}function ht(e){let t=ue(e),n=F(e),a=N(e),s=te(e),r=X(e),o=[];s&&o.push(`- Backend: "${s}/{Entity}/"`),r&&o.push(`- Frontend: "${r}/{entity}/"`);let f=e.techStack?.patterns?.length?`
|
|
971
971
|
**Key patterns:**
|
|
972
|
-
${e.techStack.patterns.map(
|
|
973
|
-
`)}`:"",
|
|
972
|
+
${e.techStack.patterns.map(u=>`- ${u}`).join(`
|
|
973
|
+
`)}`:"",g=e.techStack?.backend?.some(u=>u.includes(".NET")||u.includes("C#"))??!1?`
|
|
974
974
|
|
|
975
975
|
**\u26A0\uFE0F User entity exists!** ASP.NET Identity "ApplicationUser" at "Features/Users/" - don't scaffold User, just relate to it.`:"";return`<tech-stack>
|
|
976
976
|
${t}
|
|
@@ -978,7 +978,7 @@ ${o.length>0?`
|
|
|
978
978
|
**Architecture:** Feature folders
|
|
979
979
|
${o.join(`
|
|
980
980
|
`)}`:""}
|
|
981
|
-
${
|
|
981
|
+
${f}${g}
|
|
982
982
|
</tech-stack>`}function yt(e,t){return t?`<environment>
|
|
983
983
|
**LOCAL MODE - User manages their own services.**
|
|
984
984
|
|
|
@@ -1000,7 +1000,7 @@ The user is responsible for:
|
|
|
1000
1000
|
</environment>`:`<environment>
|
|
1001
1001
|
Docker container with pre-started services:
|
|
1002
1002
|
|
|
1003
|
-
${
|
|
1003
|
+
${ge(e)}
|
|
1004
1004
|
${e.tunnel?.enabled?"| Tunnel | - | - | Exposes frontend to user |":""}
|
|
1005
1005
|
|
|
1006
1006
|
User sees app through **Cloudflare tunnel** (live preview in browser).
|
|
@@ -1024,7 +1024,7 @@ User sees app through **Cloudflare tunnel** (live preview in browser).
|
|
|
1024
1024
|
**Flow:** @planning \u2192 **[Create Kanban Cards]** \u2192 Execute work \u2192 @debugger (if issues)
|
|
1025
1025
|
|
|
1026
1026
|
Note: For this project, you implement the code directly after planning (no stack-specific subagents).
|
|
1027
|
-
Check \`.claude/skills/\` for project-specific patterns and conventions.`}function
|
|
1027
|
+
Check \`.claude/skills/\` for project-specific patterns and conventions.`}function _t(e){return e?`<workflow>
|
|
1028
1028
|
**Standard feature flow:**
|
|
1029
1029
|
1. **@planning** - Gather requirements, design spec, saves it.
|
|
1030
1030
|
2. **\u23F8\uFE0F STOP & WAIT** - Tell user: "Spec is ready! Review it and tell me when to implement."
|
|
@@ -1107,7 +1107,7 @@ Check \`.claude/skills/\` for project-specific patterns and conventions.`}functi
|
|
|
1107
1107
|
- After @planning \u2192 STOP and wait for user to approve the spec
|
|
1108
1108
|
- Check project's existing code for patterns before implementing
|
|
1109
1109
|
- Use @debugger if you encounter build errors or bugs
|
|
1110
|
-
</workflow>`}function
|
|
1110
|
+
</workflow>`}function kt(e,t){let n=`**@planning** - Design before building:
|
|
1111
1111
|
- Complex features \u2192 plan first
|
|
1112
1112
|
- Asks questions, designs spec
|
|
1113
1113
|
- **Saves spec using save_specification MCP tool**
|
|
@@ -1120,6 +1120,8 @@ Check \`.claude/skills/\` for project-specific patterns and conventions.`}functi
|
|
|
1120
1120
|
**@backend** - Custom backend code (non-scaffold):
|
|
1121
1121
|
- Custom queries, endpoints, lookups
|
|
1122
1122
|
- Runs build + swagger generation before done
|
|
1123
|
+
- **Run \`./scripts/generate-swagger.sh\` after adding/changing endpoints**
|
|
1124
|
+
- **Run \`./scripts/generate-signalr.sh\` after adding/changing SignalR hubs**
|
|
1123
1125
|
${r?"- **Must complete before @frontend starts!**":""}
|
|
1124
1126
|
`),r&&(n+=`
|
|
1125
1127
|
**@frontend** - UI components:
|
|
@@ -1138,7 +1140,7 @@ ${r?"- **Must complete before @frontend starts!**":""}
|
|
|
1138
1140
|
**\u26A0\uFE0F MANDATORY DELEGATION - You MUST use subagents:**
|
|
1139
1141
|
|
|
1140
1142
|
${n}
|
|
1141
|
-
</subagents>`}function
|
|
1143
|
+
</subagents>`}function wt(e){return e.services?.frontend?e.techStack?.frontend?.some(n=>n.includes("TanStack")||n.includes("React Query")||n.includes("MUI"))??!1?`<frontend-api>
|
|
1142
1144
|
## Orval-Generated API Hooks
|
|
1143
1145
|
|
|
1144
1146
|
Frontend uses **Orval** to auto-generate React Query hooks from Swagger. Import from 'api/queries-commands.ts'.
|
|
@@ -1167,12 +1169,16 @@ queryClient.invalidateQueries({ queryKey: getGetApiPeopleQueryKey() })
|
|
|
1167
1169
|
'''
|
|
1168
1170
|
|
|
1169
1171
|
**Important:** Check existing feature code (e.g., 'features/person/') for correct patterns before writing new API calls.
|
|
1172
|
+
|
|
1173
|
+
**If hooks are missing or stale:**
|
|
1174
|
+
- Run \`./scripts/generate-swagger.sh\` to regenerate API hooks from backend swagger
|
|
1175
|
+
- Run \`./scripts/generate-signalr.sh\` to regenerate SignalR hub client code
|
|
1170
1176
|
</frontend-api>`:`<frontend-api>
|
|
1171
1177
|
## API Integration
|
|
1172
1178
|
|
|
1173
1179
|
Check the project's existing patterns for API calls and data fetching.
|
|
1174
1180
|
Look for existing feature code to understand the correct patterns before writing new API calls.
|
|
1175
|
-
</frontend-api>`:""}function
|
|
1181
|
+
</frontend-api>`:""}function St(e,t){if(!e.services?.frontend)return"";let n=e.services.frontend.port,a=X(e),s=`http://localhost:${n}`;return`<ui-verification>
|
|
1176
1182
|
## \u{1F50D} UI Verification with Playwright
|
|
1177
1183
|
|
|
1178
1184
|
You have access to **Playwright MCP** tools to verify the UI works correctly after making changes.
|
|
@@ -1188,6 +1194,27 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
|
|
|
1188
1194
|
- \`browser_scroll_down/up\` - Scroll the page
|
|
1189
1195
|
- \`browser_wait\` - Wait for page to settle
|
|
1190
1196
|
|
|
1197
|
+
*Forms and input:*
|
|
1198
|
+
- \`browser_fill_form\` - Fill multiple form fields at once
|
|
1199
|
+
- \`browser_select_option\` - Select dropdown options
|
|
1200
|
+
- \`browser_press_key\` - Press keyboard keys (Enter, Escape, Tab, etc.)
|
|
1201
|
+
- \`browser_hover\` - Hover over elements
|
|
1202
|
+
- \`browser_drag\` - Drag and drop between elements
|
|
1203
|
+
- \`browser_file_upload\` - Upload files to file inputs
|
|
1204
|
+
|
|
1205
|
+
*Dialog handling (IMPORTANT for alerts/confirms/prompts):*
|
|
1206
|
+
- \`browser_handle_dialog\` - Accept or dismiss alert, confirm, and prompt dialogs
|
|
1207
|
+
- Use \`accept: true\` to click OK/Yes, \`accept: false\` to click Cancel/No
|
|
1208
|
+
- For prompt dialogs, provide \`promptText\` with the text to enter
|
|
1209
|
+
|
|
1210
|
+
*Browser lifecycle (YOU control when browser closes):*
|
|
1211
|
+
- \`browser_close\` - Explicitly close the browser when you're done
|
|
1212
|
+
- \`browser_resize\` - Resize the browser viewport
|
|
1213
|
+
|
|
1214
|
+
*Debugging:*
|
|
1215
|
+
- \`browser_console_messages\` - Get browser console output (errors, warnings, logs)
|
|
1216
|
+
- \`browser_network_requests\` - See network activity (API calls, failed requests)
|
|
1217
|
+
|
|
1191
1218
|
*Vision/coordinate-based (for canvas, WebGL, Three.js, games):*
|
|
1192
1219
|
- \`browser_screen_click\` - Click at specific x,y coordinates
|
|
1193
1220
|
- \`browser_screen_move_mouse\` - Move mouse to x,y position
|
|
@@ -1218,14 +1245,17 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
|
|
|
1218
1245
|
- Use \`browser_screen_click\` for canvas/WebGL/Three.js - accessibility tools can't see inside canvas
|
|
1219
1246
|
- Use \`browser_evaluate\` to run JavaScript when you need complex interactions
|
|
1220
1247
|
- Use screenshots when you want to show the user what you see
|
|
1248
|
+
- **Dialog handling**: If an alert/confirm/prompt appears, use \`browser_handle_dialog\` immediately to dismiss it
|
|
1249
|
+
- **Browser lifecycle**: YOU control when to close the browser with \`browser_close\`. Keep it open if you need to do more interactions.
|
|
1250
|
+
- Use \`browser_console_messages\` to check for JavaScript errors when debugging
|
|
1221
1251
|
- ${t?"In local mode, the browser window will be visible to you":"In container mode, browser runs headless"}
|
|
1222
|
-
</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
|
|
1252
|
+
</ui-verification>`}function Ze(e){return e&&e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var vt=null;function et(){return Ze(vt)}import*as W from"path";import*as Ce from"fs";import{fileURLToPath as Tt}from"url";var Et=_e(),ve=W.dirname(Tt(import.meta.url)),tt=[W.resolve(ve,"../mcps/stdio-server.js"),W.resolve(ve,"./adapters/mcps/stdio-server.js")],ne=tt.find(e=>Ce.existsSync(e))||tt[0];console.log("[MCP] Stdio server path resolved:",ne);console.log("[MCP] __dirname:",ve);console.log("[MCP] File exists:",Ce.existsSync(ne));async function nt(e,t={}){let{model:n,sessionId:a=null,projectId:s=null,apiUrl:r=null,callbacks:o={},debugLog:f,abortController:_,debugMode:g=!1,isLocal:u=process.env.LOCAL_MODE==="true",projectConfig:T=O(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:R=!0}=t,B=We(f),$=new Set,H=a||"",K,q,C,U=!1,Z=!1,I=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[ne]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",s||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",I),console.log("[MCP] File exists check:",ne);try{for await(let d of Ct({prompt:e,options:{abortController:_,cwd:I,resume:a??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...u?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_fill_form","mcp__playwright__browser_select_option","mcp__playwright__browser_press_key","mcp__playwright__browser_hover","mcp__playwright__browser_drag","mcp__playwright__browser_file_upload","mcp__playwright__browser_handle_dialog","mcp__playwright__browser_close","mcp__playwright__browser_resize","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_screen_capture","mcp__playwright__browser_screen_move_mouse","mcp__playwright__browser_screen_click","mcp__playwright__browser_screen_drag","mcp__playwright__browser_screen_type","mcp__playwright__browser_evaluate","mcp__playwright__browser_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:n,mcpServers:{"agent-insights":Et,"agent-planning":{type:"stdio",command:"node",args:[ne],env:{WORKSPACE_DIR:I,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:s||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${W.join(I,".playwright-data")}`,...u?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:R?{scaffolding:ke,debugger:ie,planning:ce,backend:we,frontend:Se,"project-context":le}:{debugger:ie,planning:ce,"project-context":le},systemPrompt:Xe({debugMode:g,projectPrompt:et(),isLocal:u,projectConfig:T,useDefaultStack:R})}}))if(!(!d.uuid||$.has(d.uuid))){switch($.add(d.uuid),B?.log(d),o.onRawMessage?.(d),d.type){case"assistant":if(d.message?.content)for(let S of d.message.content)S.type==="text"?o.onAssistantText?.(S.text):S.type==="tool_use"?o.onAssistantToolUse?.(S.name,S.input):S.type==="tool_result"&&o.onAssistantToolResult?.(S.tool_use_id,S.content);break;case"user":o.onUserMessage?.(d);break;case"result":if(d.subtype==="success")K=d.result,q=d.total_cost_usd,o.onResult?.(d.result,d.total_cost_usd);else{let S=`Agent error: ${d.subtype}`;C=S,o.onError?.(S)}Z=!0;break;case"system":switch(d.subtype){case"init":H=d.session_id,B?.setSessionId(d.session_id),o.onSystemInit?.(d.session_id);break;case"status":o.onSystemStatus?.(JSON.stringify(d));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":o.onStreamEvent?.(d);break;case"tool_progress":o.onToolProgress?.(d);break;case"auth_status":o.onAuthStatus?.(d);break}if(Z)break}}catch(d){d instanceof Error&&d.name==="AbortError"||d instanceof Error&&d.message.includes("aborted by user")?(U=!0,o.onAborted?.()):(C=d instanceof Error?d.message:String(d),o.onError?.(C))}finally{B?.stop()}return console.log("after try"),{sessionId:H,result:K,cost:q,error:C,aborted:U}}function st(e){return`User answered the questions:
|
|
1223
1253
|
|
|
1224
1254
|
${Object.entries(e).map(([n,a])=>{let s=Array.isArray(a)?a.join(", "):a;return`${n}: ${s}`}).join(`
|
|
1225
|
-
`)}`}import{spawn as ot,execSync as
|
|
1226
|
-
`).filter(Boolean);console.log(`[Services] Killing ${
|
|
1227
|
-
`);v=A.pop()||"";for(let x of A)x&&Pt(x)&&s&&s(x)};C.stdout?.on("data",
|
|
1255
|
+
`)}`}import{spawn as ot,execSync as G}from"child_process";import*as b from"fs";import*as rt from"http";import*as j from"path";function at(e){let{workspaceDir:t,apiPort:n,webPort:a,onBackendError:s,onFrontendError:r,projectConfig:o=O(t)}=e,f=n??o.services?.backend?.port??5338,_=a??o.services?.frontend?.port??5173,g=F(o),u=N(o),T=o.services?.backend?.startCommand??"dotnet run",R=o.services?.backend?.buildCommand??"dotnet build",B=o.services?.backend?.typecheckCommand??"dotnet build --no-restore",$=o.services?.frontend?.startCommand??"npm run dev",H=o.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",K=o.services?.backend?.logFile??"/tmp/api.log",q=o.services?.frontend?.logFile??"/tmp/web.log",C=null,U=null,Z=l=>new Promise(i=>{let p=setTimeout(()=>i(!1),3e3);rt.get(`http://localhost:${l}`,v=>{clearTimeout(p),i(v.statusCode!==void 0&&v.statusCode<500)}).on("error",()=>{clearTimeout(p),i(!1)})}),I=l=>{try{let i=G(`lsof -ti:${l} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(i){let p=i.split(`
|
|
1256
|
+
`).filter(Boolean);console.log(`[Services] Killing ${p.length} process(es) on port ${l}: ${p.join(", ")}`);for(let m of p)try{process.kill(parseInt(m,10),"SIGKILL")}catch{}}}catch{try{G(`fuser -k ${l}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},d=(l,i)=>new Promise(p=>{if(!l||!l.pid){p();return}console.log(`[Services] Stopping ${i} (PID: ${l.pid})...`);try{process.kill(-l.pid,"SIGTERM")}catch{try{l.kill("SIGTERM")}catch{}}setTimeout(p,500)}),S=async()=>{if(!g||!b.existsSync(g)){console.log("[Services] No backend found, skipping backend start");return}I(f),console.log(`[Services] Starting backend with: ${T}...`);let l=j.dirname(K);b.existsSync(l)||b.mkdirSync(l,{recursive:!0});let i=b.createWriteStream(K,{flags:"a"}),[p,...m]=T.split(" ");C=ot(p,m,{cwd:g,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let v="",k=h=>{let L=h.toString();i.write(L);let A=(v+L).split(`
|
|
1257
|
+
`);v=A.pop()||"";for(let x of A)x&&Pt(x)&&s&&s(x)};C.stdout?.on("data",k),C.stderr?.on("data",k),C.on("exit",h=>{i.end(),h!==0&&h!==null&&s&&s(`Process exited with code ${h}`)}),C.unref()},Te=async()=>{if(!u||!b.existsSync(j.join(u,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}I(_),console.log(`[Services] Starting frontend with: ${$}...`);let l=j.dirname(q);b.existsSync(l)||b.mkdirSync(l,{recursive:!0});let[i,...p]=$.split(" ");U=ot(i,p,{cwd:u,stdio:["ignore",b.openSync(q,"w"),b.openSync(q,"w")],detached:!0,shell:!0}),U.unref()},Ee=async()=>{await d(C,"backend"),C=null,I(f)},Pe=async()=>{await d(U,"frontend"),U=null,I(_)},fe=async()=>{let[l,i]=await Promise.all([Z(f),Z(_)]);return{api:l,web:i}},Ae=async(l=15e3)=>{let i=Date.now(),p=1e3,m=0,v=null,k=null;for(console.log(`[Services] Waiting for health (timeout: ${l}ms)...`);Date.now()-i<l;){m++;let A=await fe(),x=Date.now()-i;if(A.web&&k===null&&(k=x,console.log(`[Services] \u2713 Frontend (Vite) ready after ${x}ms`)),A.api&&v===null&&(v=x,console.log(`[Services] \u2713 Backend (dotnet) ready after ${x}ms`)),console.log(`[Services] Health poll #${m} (${x}ms): api=${A.api}, web=${A.web}`),A.api&&A.web)return console.log(`[Services] \u2713 Both services healthy after ${x}ms (${m} polls)`),A;await new Promise(dt=>setTimeout(dt,p))}let h=await fe(),L=Date.now()-i;return h.web&&k===null&&console.log(`[Services] \u2713 Frontend (Vite) ready after ${L}ms`),h.api&&v===null&&console.log(`[Services] \u2713 Backend (dotnet) ready after ${L}ms`),console.log(`[Services] \u26A0 Health timeout after ${L}ms: api=${h.api}, web=${h.web}`),h},ct=async l=>{console.log(`[Services] Restarting ${l}...`);let i={success:!0},p=l==="backend"||l==="both",m=l==="frontend"||l==="both";if(p&&await Ee(),m&&await Pe(),p&&g&&b.existsSync(g)){console.log(`[Services] Building backend with: ${R}...`);try{G(R,{cwd:g,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),console.log("[Services] \u2713 Backend build succeeded")}catch(k){let h=k;i.success=!1,i.backendError=h.stderr||h.stdout||"Build failed",console.error("[Services] \u2717 Backend build failed")}}if(m&&u&&b.existsSync(j.join(u,"package.json"))){console.log(`[Services] Type-checking frontend with: ${H}...`);try{G(H,{cwd:u,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),console.log("[Services] \u2713 Frontend types OK")}catch(k){let h=k;i.success=!1,i.frontendError=h.stderr||h.stdout||"Type check failed",console.error("[Services] \u2717 Frontend type check failed")}}console.log(`[Services] Starting ${l}...`),p&&await S(),m&&await Te();let v=await Ae(1e4);if(p&&!v.api){i.success=!1;let k=await Ie("backend",20),h=k.length>0?`
|
|
1228
1258
|
Recent logs:
|
|
1229
|
-
${
|
|
1230
|
-
`)}`:"";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(!
|
|
1231
|
-
`).filter(Boolean)}catch(m){return[`Error reading logs: ${m instanceof Error?m.message:String(m)}`]}};return{startBackend:
|
|
1259
|
+
${k.join(`
|
|
1260
|
+
`)}`:"";i.backendError||(i.backendError=`Backend failed to start.${h}`)}return m&&!v.web&&(i.success=!1,i.frontendError||(i.frontendError="Frontend failed to start")),i},xe=async()=>{if(!g||!b.existsSync(g))return{success:!0};try{return G(B,{cwd:g,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),{success:!0}}catch(l){let i=l;return{success:!1,errors:i.stdout||i.stderr||"Build failed"}}},Re=async()=>{if(!u||!b.existsSync(j.join(u,"package.json")))return{success:!0};try{return G(H,{cwd:u,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),{success:!0}}catch(l){let i=l;return{success:!1,errors:i.stdout||i.stderr||"Type check failed"}}},lt=async()=>{let[l,i]=await Promise.all([xe(),Re()]);return{backend:l,frontend:i}},Ie=async(l,i)=>{let p=l==="backend"?K:q;if(!b.existsSync(p))return[`Log file not found: ${p}`];try{return G(`tail -n ${i} ${p}`,{encoding:"utf-8"}).split(`
|
|
1261
|
+
`).filter(Boolean)}catch(m){return[`Error reading logs: ${m instanceof Error?m.message:String(m)}`]}};return{startBackend:S,startFrontend:Te,stopBackend:Ee,stopFrontend:Pe,restartServices:ct,checkHealth:fe,waitForHealth:Ae,getProcesses:()=>({backend:C,frontend:U}),checkBackendBuild:xe,checkFrontendTypes:Re,runTypeChecks:lt,tailLogs:Ie,checkSwaggerEndpoints:async l=>{let i=j.join(t,"swagger.json");if(!b.existsSync(i))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let p=JSON.parse(b.readFileSync(i,"utf-8")),m=Object.keys(p.paths||{}),v=[];for(let k of m)if(k.toLowerCase().includes(l.toLowerCase())){let h=Object.keys(p.paths[k]);for(let L of h)v.push(`${L.toUpperCase()} ${k}`)}return{foundEndpoints:v,totalEndpoints:m.length}}catch(p){return{foundEndpoints:[`Error parsing swagger.json: ${p instanceof Error?p.message:String(p)}`],totalEndpoints:0}}}}}function Pt(e){let t=e.toLowerCase();return t.includes("exception")||t.includes("fail:")||t.includes("[err]")||t.includes("[error]")}import{execSync as Y}from"child_process";function it(e){let t=async()=>{try{return Y("git status --porcelain",{cwd:e,encoding:"utf-8"}).trim().length>0}catch{return!1}},n=async()=>{try{return Y("git branch --show-current",{cwd:e,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:t,commitAndPush:async(s,r)=>{try{if(!await t())return{success:!0,noChanges:!0};let o=s.length>60?s.substring(0,60)+"...":s,_=`${r?"[agent] (partial)":"[agent]"} ${o}`;Y("git add -A",{cwd:e}),Y(`git commit -m "${_.replace(/"/g,'\\"')}"`,{cwd:e,encoding:"utf-8"});let g=Y("git rev-parse --short HEAD",{cwd:e,encoding:"utf-8"}).trim();try{Y("git push",{cwd:e,stdio:"pipe"})}catch(u){let T=u instanceof Error?u.message:String(u);if(T.includes("no upstream branch")){let R=await n();Y(`git push --set-upstream origin ${R}`,{cwd:e,stdio:"pipe"})}else return console.error("[Git] Push failed:",T),{success:!0,commitHash:g,error:`Committed but push failed: ${T}`}}return{success:!0,commitHash:g}}catch(o){let f=o instanceof Error?o.message:String(o);return console.error("[Git] Error:",f),{success:!1,error:f}}},getCurrentBranch:n}}export{ee as ProjectConfigSchema,be as createFilePlanningTransport,he as createFileTransport,it as createLocalGitManager,at as createLocalServiceManager,_e as createMcpServer,st as formatAnswersAsPrompt,te as getBackendFeaturesPath,F as getBackendPath,O as getDefaultConfig,X as getFrontendFeaturesPath,N as getFrontendPath,Je as getPathsDescription,ge as getServicesDescription,ue as getTechStackDescription,Ve as isBackendFile,ze as isFrontendFile,Ke as loadProjectConfig,ye as readPendingQuestions,pe as resolveConfigPath,nt as runAgent,Qe as saveProjectConfig,Oe as setPlanningTransport,Fe as setServiceManager};
|