glenn-code 1.0.4 → 1.0.5
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 +47 -13
- package/dist/cli.js +47 -13
- package/dist/core.js +39 -5
- package/dist/index.js +63 -29
- package/package.json +1 -1
package/dist/index.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 Bt}from"@anthropic-ai/claude-agent-sdk";import{tool as pe}from"@anthropic-ai/claude-agent-sdk";import{z as le}from"zod";import*as de from"fs";import*as Ze from"path";var
|
|
4
|
+
import{createSdkMcpServer as Bt}from"@anthropic-ai/claude-agent-sdk";import{tool as pe}from"@anthropic-ai/claude-agent-sdk";import{z as le}from"zod";import*as de from"fs";import*as Ze from"path";var N=null;function ue(e){N=e}function Fe(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}var Rn=pe("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:le.string().describe("Specification name (e.g., 'User Authentication')"),filePath:le.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:le.string().describe("Specification name (e.g., 'User Authentication')"),filePath:le.string().describe("Path to the file containing the specification content (written via Write tool)")},async e=>{if(!N)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=Ze.resolve(e.filePath);if(!de.existsSync(t))return{content:[{type:"text",text:`\u274C File not found: ${e.filePath}. Use Write tool first to create the file.`}]};let n=de.readFileSync(t,"utf-8"),s=Fe(e.name);return await N.saveSpecification(s,n),{content:[{type:"text",text:`\u2705 Saved specification: ${s}.spec.md`}]}}),On=pe("read_specification","Read the content of a specification.",{name:le.string().describe("Name of the specification to read")},async e=>{if(!N)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=Fe(e.name),n=await N.getSpecification(t);return n?{content:[{type:"text",text:n}]}:{content:[{type:"text",text:`\u274C Specification "${e.name}" not found.`}]}}),Dn=pe("list_specifications","List all specifications.",{},async()=>{if(!N)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let e=await N.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
|
-
`)}`}]}}),Fn=pe("delete_specification","Delete a specification.",{name:le.string().describe("Name of the specification to delete")},async e=>{if(!
|
|
18
|
+
`)}`}]}}),Fn=pe("delete_specification","Delete a specification.",{name:le.string().describe("Name of the specification to delete")},async e=>{if(!N)return{content:[{type:"text",text:"\u274C Planning transport not initialized"}]};let t=Fe(e.name);return await N.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 q}from"@anthropic-ai/claude-agent-sdk";import{z as X}from"zod";var k=null;function ge(e){k=e}var et=q("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:X.enum(["backend","frontend","both"]).default("backend").describe("Which service to restart")},async e=>{if(!
|
|
25
|
+
Use target="both" if unsure or both need restart.`,{target:X.enum(["backend","frontend","both"]).default("backend").describe("Which service to restart")},async e=>{if(!k)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 k.restartServices(t);if(!n.success){let o=[];return n.backendError&&o.push(`Backend: ${n.backendError}`),n.frontendError&&o.push(`Frontend: ${n.frontendError}`),{content:[{type:"text",text:`\u274C Restart failed:
|
|
26
26
|
${o.join(`
|
|
27
27
|
|
|
28
28
|
`)}`}]}}return{content:[{type:"text",text:`\u2705 ${t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend"} restarted successfully.`}]}}),tt=q("stop_services",`Stop running services. Call this BEFORE running scaffold to prevent port conflicts and ensure clean migrations.
|
|
@@ -30,32 +30,32 @@ ${o.join(`
|
|
|
30
30
|
Parameters:
|
|
31
31
|
- target: "backend" (default) | "frontend" | "both"
|
|
32
32
|
|
|
33
|
-
Use target="backend" before running scaffold (required for migrations).`,{target:X.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async e=>{if(!
|
|
33
|
+
Use target="backend" before running scaffold (required for migrations).`,{target:X.enum(["backend","frontend","both"]).default("backend").describe("Which service to stop")},async e=>{if(!k)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",s=t==="frontend"||t==="both";n&&await k.stopBackend(),s&&await k.stopFrontend();let o=5e3,r=Date.now(),i=!n,l=!s;for(;Date.now()-r<o;){let m=await k.checkHealth();if(n&&!m.api&&(i=!0),s&&!m.web&&(l=!0),i&&l)break;await new Promise(v=>setTimeout(v,500))}let h=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";return{content:[{type:"text",text:i&&l?`\u2705 ${h} stopped successfully.`:`\u26A0\uFE0F ${h} stop requested but health check still shows running. Proceeding anyway.`}]}}),nt=q("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:X.enum(["backend","frontend","both"]).default("backend").describe("Which service to start")},async e=>{if(!
|
|
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(!
|
|
38
|
+
Use target="backend" after running scaffold.`,{target:X.enum(["backend","frontend","both"]).default("backend").describe("Which service to start")},async e=>{if(!k)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",s=t==="frontend"||t==="both";n&&await k.startBackend(),s&&await k.startFrontend();let o=await k.waitForHealth(15e3),r=!n||o.api,i=!s||o.web,l=r&&i,h=t==="both"?"Backend and frontend":t==="backend"?"Backend":"Frontend";if(!l){let f=[];return n&&!o.api&&f.push("Backend failed to start"),s&&!o.web&&f.push("Frontend failed to start"),{content:[{type:"text",text:`\u274C ${h} failed to start: ${f.join(", ")}`}]}}return{content:[{type:"text",text:`\u2705 ${h} started successfully.`}]}}),ot=q("check_service_health",`Check if services are running and healthy. Returns current status of backend API and frontend.
|
|
39
|
+
Note: This only checks if ports are responding, NOT build/type errors. Use check_backend_build or check_frontend_types for that.`,{},async()=>{if(!k)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let e=await k.checkHealth();return{content:[{type:"text",text:`Service Health:
|
|
40
40
|
\u{1F5A5}\uFE0F Backend API: ${e.api?"\u2705 running":"\u274C not running"}
|
|
41
|
-
\u{1F310} Frontend: ${e.web?"\u2705 running":"\u274C not running"}`}]}}),st=q("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(!
|
|
41
|
+
\u{1F310} Frontend: ${e.web?"\u2705 running":"\u274C not running"}`}]}}),st=q("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(!k)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};console.log("[MCP] check_backend_build called");let e=await k.checkBackendBuild();return e.success?{content:[{type:"text",text:"\u2705 Backend build succeeded - no errors"}]}:{content:[{type:"text",text:`\u274C Backend build failed:
|
|
42
42
|
|
|
43
|
-
${e.errors}`}]}}),rt=q("check_frontend_types","Run TypeScript type check (tsc) on frontend code. Use this after fixing frontend code to verify the fix works.",{},async()=>{if(!
|
|
43
|
+
${e.errors}`}]}}),rt=q("check_frontend_types","Run TypeScript type check (tsc) on frontend code. Use this after fixing frontend code to verify the fix works.",{},async()=>{if(!k)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};console.log("[MCP] check_frontend_types called");let e=await k.checkFrontendTypes();return e.success?{content:[{type:"text",text:"\u2705 Frontend type check passed - no errors"}]}:{content:[{type:"text",text:`\u274C Frontend type errors:
|
|
44
44
|
|
|
45
45
|
${e.errors}`}]}}),Ln=q("tail_service_log",`Get the last N lines of service logs. Use this to debug startup failures or verify that the application is running correctly.
|
|
46
46
|
|
|
47
47
|
Parameters:
|
|
48
48
|
- target: "backend" | "frontend"
|
|
49
|
-
- lines: number (default 50)`,{target:X.enum(["backend","frontend"]).describe("Which log to read"),lines:X.number().default(50).describe("Number of lines to read")},async e=>{if(!
|
|
49
|
+
- lines: number (default 50)`,{target:X.enum(["backend","frontend"]).describe("Which log to read"),lines:X.number().default(50).describe("Number of lines to read")},async e=>{if(!k)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=await k.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
|
`)}`}]}}),jn=q("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:X.string().describe("Text to search for in endpoint paths")},async e=>{if(!
|
|
55
|
+
- pattern: string (e.g., "users", "api/reports")`,{pattern:X.string().describe("Text to search for in endpoint paths")},async e=>{if(!k)return{content:[{type:"text",text:"\u274C Service manager not initialized"}]};let t=await k.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 W from"fs";import*as it from"path";var $t="/tmp";function te(e){let t=it.join($t,`agent-questions-${e}.json`);if(!W.existsSync(t))return null;try{let n=W.readFileSync(t,"utf-8");return W.unlinkSync(t),JSON.parse(n)}catch{return null}}import*as P from"fs";import*as fe from"path";function ne(e){let t=fe.join(e,".sdd","specifications");function n(){P.existsSync(t)||P.mkdirSync(t,{recursive:!0})}function s(o){return fe.join(t,`${o}.spec.md`)}return{async saveSpecification(o,r){n(),P.writeFileSync(s(o),r,"utf-8")},async getSpecification(o){n();let r=s(o);return P.existsSync(r)?P.readFileSync(r,"utf-8"):null},async listSpecifications(){return n(),P.readdirSync(t).filter(r=>r.endsWith(".spec.md")).map(r=>{let i=P.readFileSync(fe.join(t,r),"utf-8"),l=i.match(/name: "([^"]+)"/),h=i.match(/status: (\w+)/),f=i.match(/version: "([^"]+)"/);return{slug:r.replace(".spec.md",""),name:l?.[1]||r,status:h?.[1]||"unknown",version:f?.[1]||"1.0.0"}})},async deleteSpecification(o){n();let r=s(o);return P.existsSync(r)?(P.unlinkSync(r),!0):!1},async specificationExists(o){return n(),P.existsSync(s(o))}}}function Ue(){return Bt({name:"agent-insights",version:"1.0.0",tools:[et,tt,nt,ot,st,rt]})}import{query as rn}from"@anthropic-ai/claude-agent-sdk";var
|
|
58
|
+
`)}`}]}});import*as W from"fs";import*as it from"path";var $t="/tmp";function te(e){let t=it.join($t,`agent-questions-${e}.json`);if(!W.existsSync(t))return null;try{let n=W.readFileSync(t,"utf-8");return W.unlinkSync(t),JSON.parse(n)}catch{return null}}import*as P from"fs";import*as fe from"path";function ne(e){let t=fe.join(e,".sdd","specifications");function n(){P.existsSync(t)||P.mkdirSync(t,{recursive:!0})}function s(o){return fe.join(t,`${o}.spec.md`)}return{async saveSpecification(o,r){n(),P.writeFileSync(s(o),r,"utf-8")},async getSpecification(o){n();let r=s(o);return P.existsSync(r)?P.readFileSync(r,"utf-8"):null},async listSpecifications(){return n(),P.readdirSync(t).filter(r=>r.endsWith(".spec.md")).map(r=>{let i=P.readFileSync(fe.join(t,r),"utf-8"),l=i.match(/name: "([^"]+)"/),h=i.match(/status: (\w+)/),f=i.match(/version: "([^"]+)"/);return{slug:r.replace(".spec.md",""),name:l?.[1]||r,status:h?.[1]||"unknown",version:f?.[1]||"1.0.0"}})},async deleteSpecification(o){n();let r=s(o);return P.existsSync(r)?(P.unlinkSync(r),!0):!1},async specificationExists(o){return n(),P.existsSync(s(o))}}}function Ue(){return Bt({name:"agent-insights",version:"1.0.0",tools:[et,tt,nt,ot,st,rt]})}import{query as rn}from"@anthropic-ai/claude-agent-sdk";var Me={description:"Delegate to this agent when scaffolding new entities or creating CRUD features. This agent handles entity generation using the scaffold CLI with multi-entity support.",model:"inherit",prompt:`You are a scaffolding specialist. Your ONLY job is:
|
|
59
59
|
1. Check existing features (quick ls)
|
|
60
60
|
2. Write ALL entities to ONE /tmp/scaffold.json (big bang, no phasing!)
|
|
61
61
|
3. Run scaffold CLI ONCE
|
|
@@ -428,7 +428,7 @@ Save your spec. The spec will appear in the user's UI.
|
|
|
428
428
|
- Ask ONE question at a time (not all at once!)
|
|
429
429
|
- STOP asking if user says "start implementing"
|
|
430
430
|
- NEVER output the spec as text - use save_specification
|
|
431
|
-
- Maximum 7 questions - don't exhaust the user!`};var
|
|
431
|
+
- Maximum 7 questions - don't exhaust the user!`};var Ne={description:"Delegate to this agent for writing new backend features (non-scaffold). Custom endpoints, queries, commands, business logic.",model:"inherit",prompt:`You are a backend specialist for .NET 9 / ASP.NET Core.
|
|
432
432
|
|
|
433
433
|
## YOUR ROLE
|
|
434
434
|
Write custom backend code that goes BEYOND what scaffold generates:
|
|
@@ -771,8 +771,8 @@ Before saving, verify your context:
|
|
|
771
771
|
5. **Keep it scannable** - Use bullets, clear sections
|
|
772
772
|
6. **Update, don't replace** - If context exists, improve it rather than starting fresh`};import*as Z from"fs";import*as ct from"path";var be=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 s={timestamp:new Date().toISOString(),message:t};this.buffer.push(JSON.stringify(s,null,2)+`
|
|
773
773
|
---
|
|
774
|
-
`)}getLogFilePath(){let n=(this.sessionId||"unknown").replace(/[^a-zA-Z0-9-]/g,"_").slice(0,50),s=new Date().toISOString().split("T")[0];return ct.join(this.outputDir,`session-${s}-${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 lt(e){return e?typeof e=="boolean"?e?new be({enabled:!0}):null:e.enabled?new be(e):null:null}import{z as d}from"zod";import*as je from"fs/promises";import*as K from"path";var pt=d.object({port:d.number(),startCommand:d.string(),buildCommand:d.string().optional(),typecheckCommand:d.string().optional(),logFile:d.string().optional(),healthEndpoint:d.string().optional(),extensions:d.array(d.string())}),qt=d.object({port:d.number(),type:d.enum(["postgres","mysql","sqlite","mongodb"])}),Wt=d.object({command:d.string(),schemaPath:d.string().optional()}),Gt=d.object({email:d.string().optional(),name:d.string().optional(),commitPrefix:d.string().optional()}),
|
|
775
|
-
`)}function Se(e){let t=[];return e.services?.backend&&t.push(`| Backend | ${
|
|
774
|
+
`)}getLogFilePath(){let n=(this.sessionId||"unknown").replace(/[^a-zA-Z0-9-]/g,"_").slice(0,50),s=new Date().toISOString().split("T")[0];return ct.join(this.outputDir,`session-${s}-${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 lt(e){return e?typeof e=="boolean"?e?new be({enabled:!0}):null:e.enabled?new be(e):null:null}import{z as d}from"zod";import*as je from"fs/promises";import*as K from"path";var pt=d.object({port:d.number(),startCommand:d.string(),buildCommand:d.string().optional(),typecheckCommand:d.string().optional(),logFile:d.string().optional(),healthEndpoint:d.string().optional(),extensions:d.array(d.string())}),qt=d.object({port:d.number(),type:d.enum(["postgres","mysql","sqlite","mongodb"])}),Wt=d.object({command:d.string(),schemaPath:d.string().optional()}),Gt=d.object({email:d.string().optional(),name:d.string().optional(),commitPrefix:d.string().optional()}),Ht=d.object({enabled:d.boolean(),port:d.number().optional()}),dt=d.object({version:d.literal("1.0"),paths:d.object({workspace:d.string(),backend:d.string().optional(),frontend:d.string().optional(),features:d.object({backend:d.string().optional(),frontend:d.string().optional()}).optional()}),services:d.object({backend:pt.optional(),frontend:pt.optional(),database:qt.optional()}).optional(),scaffold:Wt.optional(),git:Gt.optional(),techStack:d.object({backend:d.array(d.string()).optional(),frontend:d.array(d.string()).optional(),patterns:d.array(d.string()).optional()}).optional(),tunnel:Ht.optional()});function G(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}}}function we(e,t){if(t)return K.isAbsolute(t)?t:K.join(e.paths.workspace,t)}function H(e){return we(e,e.paths.backend)}function Y(e){return we(e,e.paths.frontend)}function ke(e){let t=H(e);if(!(!t||!e.paths.features?.backend))return K.join(t,e.paths.features.backend)}function oe(e){let t=Y(e);if(!(!t||!e.paths.features?.frontend))return K.join(t,e.paths.features.frontend)}function _e(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(`
|
|
775
|
+
`)}function Se(e){let t=[];return e.services?.backend&&t.push(`| Backend | ${H(e)} | ${e.services.backend.port} | ${e.services.backend.logFile||"N/A"} |`),e.services?.frontend&&t.push(`| Frontend | ${Y(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 |
|
|
776
776
|
|---------|----------|------|------|
|
|
777
777
|
${t.join(`
|
|
778
778
|
`)}`}function ut(e={}){let{debugMode:t=!1,projectPrompt:n=null,isLocal:s=!1,projectConfig:o=G(),useDefaultStack:r=!0}=e,i=n?`
|
|
@@ -964,7 +964,7 @@ ${O}
|
|
|
964
964
|
${T}
|
|
965
965
|
|
|
966
966
|
${S}
|
|
967
|
-
`}function Jt(e){let t=_e(e),n=
|
|
967
|
+
`}function Jt(e){let t=_e(e),n=H(e),s=Y(e),o=ke(e),r=oe(e),i=[];o&&i.push(`- Backend: "${o}/{Entity}/"`),r&&i.push(`- Frontend: "${r}/{entity}/"`);let l=e.techStack?.patterns?.length?`
|
|
968
968
|
**Key patterns:**
|
|
969
969
|
${e.techStack.patterns.map(m=>`- ${m}`).join(`
|
|
970
970
|
`)}`:"",f=e.techStack?.backend?.some(m=>m.includes(".NET")||m.includes("C#"))??!1?`
|
|
@@ -1218,11 +1218,13 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
|
|
|
1218
1218
|
- \`browser_screen_drag\` - Drag from one coordinate to another
|
|
1219
1219
|
- \`browser_screen_type\` - Type at current position
|
|
1220
1220
|
|
|
1221
|
-
*JavaScript execution:*
|
|
1222
|
-
- \`browser_evaluate\` - Run JavaScript code on the page
|
|
1221
|
+
*JavaScript execution (for complex interactions):*
|
|
1222
|
+
- \`browser_evaluate\` - Run JavaScript code on the page
|
|
1223
|
+
- \`browser_run_code\` - Run custom Playwright code snippets (powerful for complex scenarios!)
|
|
1223
1224
|
|
|
1224
|
-
*Tab management:*
|
|
1225
|
-
- \`
|
|
1225
|
+
*Tab management (for multi-tab workflows):*
|
|
1226
|
+
- \`browser_tabs\` - List, create, close, or switch between tabs (combined tool)
|
|
1227
|
+
- \`browser_tab_list\` / \`browser_tab_new\` / \`browser_tab_select\` / \`browser_tab_close\` (individual tools)
|
|
1226
1228
|
|
|
1227
1229
|
**When to use:**
|
|
1228
1230
|
- After @frontend completes a feature, verify it renders correctly
|
|
@@ -1245,17 +1247,49 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
|
|
|
1245
1247
|
- **Dialog handling**: If an alert/confirm/prompt appears, use \`browser_handle_dialog\` immediately to dismiss it
|
|
1246
1248
|
- **Browser lifecycle**: YOU control when to close the browser with \`browser_close\`. Keep it open if you need to do more interactions.
|
|
1247
1249
|
- Use \`browser_console_messages\` to check for JavaScript errors when debugging
|
|
1250
|
+
- **Popups auto-hidden**: Common feedback widgets (Wootric, Intercom, HubSpot, Drift, Zendesk, Hotjar) are automatically hidden
|
|
1251
|
+
- **iframes**: Use \`browser_evaluate\` or \`browser_run_code\` to interact with iframe content:
|
|
1252
|
+
\`\`\`javascript
|
|
1253
|
+
// Focus iframe and get content
|
|
1254
|
+
const iframe = document.querySelector('iframe[name="main"]');
|
|
1255
|
+
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
|
1256
|
+
iframeDoc.querySelector('input').value = 'text';
|
|
1257
|
+
\`\`\`
|
|
1258
|
+
- **Multi-tab apps**: Use \`browser_tabs\` to manage multiple windows/tabs in legacy applications
|
|
1259
|
+
- **Large snapshots**: For pages with huge dropdowns (e.g., country lists), use \`browser_evaluate\` to collapse them first
|
|
1248
1260
|
- ${t?"In local mode, the browser window will be visible to you":"In container mode, browser runs headless"}
|
|
1249
|
-
</ui-verification>`}function gt(e){return e&&e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var sn=null;function ft(){return gt(sn)}import*as V from"path";import*as Be from"fs";import{fileURLToPath as an}from"url";var cn=Ue(),$e=V.dirname(an(import.meta.url)),mt=[V.resolve($e,"../mcps/stdio-server.js"),V.resolve($e,"./adapters/mcps/stdio-server.js")],se=mt.find(e=>Be.existsSync(e))||mt[0];console.log("[MCP] Stdio server path resolved:",se);console.log("[MCP] __dirname:",$e);console.log("[MCP] File exists:",Be.existsSync(se));async function ve(e,t={}){let{model:n,sessionId:s=null,projectId:o=null,apiUrl:r=null,callbacks:i={},debugLog:l,abortController:h,debugMode:f=!1,isLocal:m=process.env.LOCAL_MODE==="true",projectConfig:v=G(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:T=!0}=t,S=lt(l),O=new Set,y=s||"",R,U,x,$=!1,ee=!1,
|
|
1261
|
+
</ui-verification>`}function gt(e){return e&&e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var sn=null;function ft(){return gt(sn)}import*as V from"path";import*as Be from"fs";import{fileURLToPath as an}from"url";var cn=Ue(),$e=V.dirname(an(import.meta.url)),mt=[V.resolve($e,"../mcps/stdio-server.js"),V.resolve($e,"./adapters/mcps/stdio-server.js")],se=mt.find(e=>Be.existsSync(e))||mt[0];console.log("[MCP] Stdio server path resolved:",se);console.log("[MCP] __dirname:",$e);console.log("[MCP] File exists:",Be.existsSync(se));async function ve(e,t={}){let{model:n,sessionId:s=null,projectId:o=null,apiUrl:r=null,callbacks:i={},debugLog:l,abortController:h,debugMode:f=!1,isLocal:m=process.env.LOCAL_MODE==="true",projectConfig:v=G(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:T=!0}=t,S=lt(l),O=new Set,y=s||"",R,U,x,$=!1,ee=!1,M=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[se]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",o||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",M),console.log("[MCP] File exists check:",se);try{for await(let u of rn({prompt:e,options:{abortController:h,cwd:M,resume:s??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...m?[]:["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_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":cn,"agent-planning":{type:"stdio",command:"node",args:[se],env:{WORKSPACE_DIR:M,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:o||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${V.join(M,".playwright-data")}`,"--init-script",`(function() {
|
|
1262
|
+
const popupSelectors = [
|
|
1263
|
+
'[class*="wootric"]', '[id*="wootric"]',
|
|
1264
|
+
'[class*="intercom"]', '[id*="intercom"]',
|
|
1265
|
+
'[class*="hubspot"]', '[id*="hubspot"]',
|
|
1266
|
+
'[class*="drift"]', '[id*="drift"]',
|
|
1267
|
+
'[class*="zendesk"]', '[id*="zendesk"]',
|
|
1268
|
+
'[class*="hotjar"]', '[id*="hotjar"]',
|
|
1269
|
+
'[class*="feedback"]', '[class*="survey"]',
|
|
1270
|
+
'[class*="nps-"]', '[id*="nps-"]',
|
|
1271
|
+
'.grecaptcha-badge'
|
|
1272
|
+
];
|
|
1273
|
+
const hidePopups = () => {
|
|
1274
|
+
popupSelectors.forEach(sel => {
|
|
1275
|
+
document.querySelectorAll(sel).forEach(el => {
|
|
1276
|
+
if (el instanceof HTMLElement) el.style.display = 'none';
|
|
1277
|
+
});
|
|
1278
|
+
});
|
|
1279
|
+
};
|
|
1280
|
+
hidePopups();
|
|
1281
|
+
const observer = new MutationObserver(hidePopups);
|
|
1282
|
+
observer.observe(document.body || document.documentElement, { childList: true, subtree: true });
|
|
1283
|
+
})();`,...m?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:T?{scaffolding:Me,debugger:me,planning:he,backend:Ne,frontend:Le,"project-context":ye}:{debugger:me,planning:he,"project-context":ye},systemPrompt:ut({debugMode:f,projectPrompt:ft(),isLocal:m,projectConfig:v,useDefaultStack:T})}}))if(!(!u.uuid||O.has(u.uuid))){switch(O.add(u.uuid),S?.log(u),i.onRawMessage?.(u),u.type){case"assistant":if(u.message?.content)for(let E of u.message.content)E.type==="text"?i.onAssistantText?.(E.text):E.type==="tool_use"?i.onAssistantToolUse?.(E.name,E.input):E.type==="tool_result"&&i.onAssistantToolResult?.(E.tool_use_id,E.content);break;case"user":i.onUserMessage?.(u);break;case"result":if(u.subtype==="success")R=u.result,U=u.total_cost_usd,i.onResult?.(u.result,u.total_cost_usd);else{let E=`Agent error: ${u.subtype}`;x=E,i.onError?.(E)}ee=!0;break;case"system":switch(u.subtype){case"init":y=u.session_id,S?.setSessionId(u.session_id),i.onSystemInit?.(u.session_id);break;case"status":i.onSystemStatus?.(JSON.stringify(u));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":i.onStreamEvent?.(u);break;case"tool_progress":i.onToolProgress?.(u);break;case"auth_status":i.onAuthStatus?.(u);break}if(ee)break}}catch(u){u instanceof Error&&u.name==="AbortError"||u instanceof Error&&u.message.includes("aborted by user")?($=!0,i.onAborted?.()):(x=u instanceof Error?u.message:String(u),i.onError?.(x))}finally{S?.stop()}return console.log("after try"),{sessionId:y,result:R,cost:U,error:x,aborted:$}}function Ce(e){return`User answered the questions:
|
|
1250
1284
|
|
|
1251
1285
|
${Object.entries(e).map(([n,s])=>{let o=Array.isArray(s)?s.join(", "):s;return`${n}: ${o}`}).join(`
|
|
1252
|
-
`)}`}import{spawn as ht,execSync as z}from"child_process";import*as _ from"fs";import*as yt from"http";import*as Q from"path";function Te(e){let{workspaceDir:t,apiPort:n,webPort:s,onBackendError:o,onFrontendError:r,projectConfig:i=G(t)}=e,l=n??i.services?.backend?.port??5338,h=s??i.services?.frontend?.port??5173,f=
|
|
1253
|
-
`).filter(Boolean);console.log(`[Services] Killing ${g.length} process(es) on port ${p}: ${g.join(", ")}`);for(let b of g)try{process.kill(parseInt(b,10),"SIGKILL")}catch{}}}catch{try{z(`fuser -k ${p}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},u=(p,c)=>new Promise(g=>{if(!p||!p.pid){g();return}console.log(`[Services] Stopping ${c} (PID: ${p.pid})...`);try{process.kill(-p.pid,"SIGTERM")}catch{try{p.kill("SIGTERM")}catch{}}setTimeout(g,500)}),E=async()=>{if(!f||!_.existsSync(f)){console.log("[Services] No backend found, skipping backend start");return}
|
|
1254
|
-
`);A=D.pop()||"";for(let F of D)F&&ln(F)&&o&&o(F)};x.stdout?.on("data",C),x.stderr?.on("data",C),x.on("exit",
|
|
1286
|
+
`)}`}import{spawn as ht,execSync as z}from"child_process";import*as _ from"fs";import*as yt from"http";import*as Q from"path";function Te(e){let{workspaceDir:t,apiPort:n,webPort:s,onBackendError:o,onFrontendError:r,projectConfig:i=G(t)}=e,l=n??i.services?.backend?.port??5338,h=s??i.services?.frontend?.port??5173,f=H(i),m=Y(i),v=i.services?.backend?.startCommand??"dotnet run",T=i.services?.backend?.buildCommand??"dotnet build",S=i.services?.backend?.typecheckCommand??"dotnet build --no-restore",O=i.services?.frontend?.startCommand??"npm run dev",y=i.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",R=i.services?.backend?.logFile??"/tmp/api.log",U=i.services?.frontend?.logFile??"/tmp/web.log",x=null,$=null,ee=p=>new Promise(c=>{let g=setTimeout(()=>c(!1),3e3);yt.get(`http://localhost:${p}`,A=>{clearTimeout(g),c(A.statusCode!==void 0&&A.statusCode<500)}).on("error",()=>{clearTimeout(g),c(!1)})}),M=p=>{try{let c=z(`lsof -ti:${p} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(c){let g=c.split(`
|
|
1287
|
+
`).filter(Boolean);console.log(`[Services] Killing ${g.length} process(es) on port ${p}: ${g.join(", ")}`);for(let b of g)try{process.kill(parseInt(b,10),"SIGKILL")}catch{}}}catch{try{z(`fuser -k ${p}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},u=(p,c)=>new Promise(g=>{if(!p||!p.pid){g();return}console.log(`[Services] Stopping ${c} (PID: ${p.pid})...`);try{process.kill(-p.pid,"SIGTERM")}catch{try{p.kill("SIGTERM")}catch{}}setTimeout(g,500)}),E=async()=>{if(!f||!_.existsSync(f)){console.log("[Services] No backend found, skipping backend start");return}M(l),console.log(`[Services] Starting backend with: ${v}...`);let p=Q.dirname(R);_.existsSync(p)||_.mkdirSync(p,{recursive:!0});let c=_.createWriteStream(R,{flags:"a"}),[g,...b]=v.split(" ");x=ht(g,b,{cwd:f,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let A="",C=w=>{let B=w.toString();c.write(B);let D=(A+B).split(`
|
|
1288
|
+
`);A=D.pop()||"";for(let F of D)F&&ln(F)&&o&&o(F)};x.stdout?.on("data",C),x.stderr?.on("data",C),x.on("exit",w=>{c.end(),w!==0&&w!==null&&o&&o(`Process exited with code ${w}`)}),x.unref()},Ye=async()=>{if(!m||!_.existsSync(Q.join(m,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}M(h),console.log(`[Services] Starting frontend with: ${O}...`);let p=Q.dirname(U);_.existsSync(p)||_.mkdirSync(p,{recursive:!0});let[c,...g]=O.split(" ");$=ht(c,g,{cwd:m,stdio:["ignore",_.openSync(U,"w"),_.openSync(U,"w")],detached:!0,shell:!0}),$.unref()},Qe=async()=>{await u(x,"backend"),x=null,M(l)},Ke=async()=>{await u($,"frontend"),$=null,M(h)},De=async()=>{let[p,c]=await Promise.all([ee(l),ee(h)]);return{api:p,web:c}},Ve=async(p=15e3)=>{let c=Date.now(),g=1e3,b=0,A=null,C=null;for(console.log(`[Services] Waiting for health (timeout: ${p}ms)...`);Date.now()-c<p;){b++;let D=await De(),F=Date.now()-c;if(D.web&&C===null&&(C=F,console.log(`[Services] \u2713 Frontend (Vite) ready after ${F}ms`)),D.api&&A===null&&(A=F,console.log(`[Services] \u2713 Backend (dotnet) ready after ${F}ms`)),console.log(`[Services] Health poll #${b} (${F}ms): api=${D.api}, web=${D.web}`),D.api&&D.web)return console.log(`[Services] \u2713 Both services healthy after ${F}ms (${b} polls)`),D;await new Promise(jt=>setTimeout(jt,g))}let w=await De(),B=Date.now()-c;return w.web&&C===null&&console.log(`[Services] \u2713 Frontend (Vite) ready after ${B}ms`),w.api&&A===null&&console.log(`[Services] \u2713 Backend (dotnet) ready after ${B}ms`),console.log(`[Services] \u26A0 Health timeout after ${B}ms: api=${w.api}, web=${w.web}`),w},Nt=async p=>{console.log(`[Services] Restarting ${p}...`);let c={success:!0},g=p==="backend"||p==="both",b=p==="frontend"||p==="both";if(g&&await Qe(),b&&await Ke(),g&&f&&_.existsSync(f)){console.log(`[Services] Building backend with: ${T}...`);try{z(T,{cwd:f,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),console.log("[Services] \u2713 Backend build succeeded")}catch(C){let w=C;c.success=!1,c.backendError=w.stderr||w.stdout||"Build failed",console.error("[Services] \u2717 Backend build failed")}}if(b&&m&&_.existsSync(Q.join(m,"package.json"))){console.log(`[Services] Type-checking frontend with: ${y}...`);try{z(y,{cwd:m,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),console.log("[Services] \u2713 Frontend types OK")}catch(C){let w=C;c.success=!1,c.frontendError=w.stderr||w.stdout||"Type check failed",console.error("[Services] \u2717 Frontend type check failed")}}console.log(`[Services] Starting ${p}...`),g&&await E(),b&&await Ye();let A=await Ve(1e4);if(g&&!A.api){c.success=!1;let C=await Xe("backend",20),w=C.length>0?`
|
|
1255
1289
|
Recent logs:
|
|
1256
1290
|
${C.join(`
|
|
1257
|
-
`)}`:"";c.backendError||(c.backendError=`Backend failed to start.${
|
|
1258
|
-
`).filter(Boolean)}catch(b){return[`Error reading logs: ${b instanceof Error?b.message:String(b)}`]}};return{startBackend:E,startFrontend:
|
|
1291
|
+
`)}`:"";c.backendError||(c.backendError=`Backend failed to start.${w}`)}return b&&!A.web&&(c.success=!1,c.frontendError||(c.frontendError="Frontend failed to start")),c},ze=async()=>{if(!f||!_.existsSync(f))return{success:!0};try{return z(S,{cwd:f,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),{success:!0}}catch(p){let c=p;return{success:!1,errors:c.stdout||c.stderr||"Build failed"}}},Je=async()=>{if(!m||!_.existsSync(Q.join(m,"package.json")))return{success:!0};try{return z(y,{cwd:m,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),{success:!0}}catch(p){let c=p;return{success:!1,errors:c.stdout||c.stderr||"Type check failed"}}},Lt=async()=>{let[p,c]=await Promise.all([ze(),Je()]);return{backend:p,frontend:c}},Xe=async(p,c)=>{let g=p==="backend"?R:U;if(!_.existsSync(g))return[`Log file not found: ${g}`];try{return z(`tail -n ${c} ${g}`,{encoding:"utf-8"}).split(`
|
|
1292
|
+
`).filter(Boolean)}catch(b){return[`Error reading logs: ${b instanceof Error?b.message:String(b)}`]}};return{startBackend:E,startFrontend:Ye,stopBackend:Qe,stopFrontend:Ke,restartServices:Nt,checkHealth:De,waitForHealth:Ve,getProcesses:()=>({backend:x,frontend:$}),checkBackendBuild:ze,checkFrontendTypes:Je,runTypeChecks:Lt,tailLogs:Xe,checkSwaggerEndpoints:async p=>{let c=Q.join(t,"swagger.json");if(!_.existsSync(c))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let g=JSON.parse(_.readFileSync(c,"utf-8")),b=Object.keys(g.paths||{}),A=[];for(let C of b)if(C.toLowerCase().includes(p.toLowerCase())){let w=Object.keys(g.paths[C]);for(let B of w)A.push(`${B.toUpperCase()} ${C}`)}return{foundEndpoints:A,totalEndpoints:b.length}}catch(g){return{foundEndpoints:[`Error parsing swagger.json: ${g instanceof Error?g.message:String(g)}`],totalEndpoints:0}}}}}function ln(e){let t=e.toLowerCase();return t.includes("exception")||t.includes("fail:")||t.includes("[err]")||t.includes("[error]")}import{execSync as J}from"child_process";function Pe(e){let t=async()=>{try{return J("git status --porcelain",{cwd:e,encoding:"utf-8"}).trim().length>0}catch{return!1}},n=async()=>{try{return J("git branch --show-current",{cwd:e,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:t,commitAndPush:async(o,r)=>{try{if(!await t())return{success:!0,noChanges:!0};let i=o.length>60?o.substring(0,60)+"...":o,h=`${r?"[agent] (partial)":"[agent]"} ${i}`;J("git add -A",{cwd:e}),J(`git commit -m "${h.replace(/"/g,'\\"')}"`,{cwd:e,encoding:"utf-8"});let f=J("git rev-parse --short HEAD",{cwd:e,encoding:"utf-8"}).trim();try{J("git push",{cwd:e,stdio:"pipe"})}catch(m){let v=m instanceof Error?m.message:String(m);if(v.includes("no upstream branch")){let T=await n();J(`git push --set-upstream origin ${T}`,{cwd:e,stdio:"pipe"})}else return console.error("[Git] Push failed:",v),{success:!0,commitHash:f,error:`Committed but push failed: ${v}`}}return{success:!0,commitHash:f}}catch(i){let l=i instanceof Error?i.message:String(i);return console.error("[Git] Error:",l),{success:!1,error:l}}},getCurrentBranch:n}}import ie from"inquirer";var re="\u2192 Other (type custom answer)";async function Ee(e){let t={};for(let n of e){let s=await pn(n);t[n.id]=s}return t}async function pn(e){return e.multiSelect&&e.options.length>0?dn(e):e.options.length>0?un(e):gn(e)}async function dn(e){let t=[...e.options];e.allowFreeText&&t.push(re);let{selection:n}=await ie.prompt([{type:"checkbox",name:"selection",message:e.question,choices:t}]);if(n.includes(re)){let{customAnswer:s}=await ie.prompt([{type:"input",name:"customAnswer",message:"Enter your custom answer:"}]);return[...n.filter(r=>r!==re),s]}return n}async function un(e){let t=[...e.options];e.allowFreeText&&t.push(re);let{selection:n}=await ie.prompt([{type:"select",name:"selection",message:e.question,choices:t}]);if(n===re){let{customAnswer:s}=await ie.prompt([{type:"input",name:"customAnswer",message:"Enter your custom answer:"}]);return s}return n}async function gn(e){let{answer:t}=await ie.prompt([{type:"input",name:"answer",message:e.question}]);return t}import*as L from"fs";import*as Ae from"path";var fn=".sdd/cli-sessions.json";function qe(){return Ae.join(process.cwd(),fn)}function mn(){let e=Ae.dirname(qe());L.existsSync(e)||L.mkdirSync(e,{recursive:!0})}function We(){try{let e=qe();if(!L.existsSync(e))return[];let t=L.readFileSync(e,"utf-8");return JSON.parse(t).sort((s,o)=>new Date(o.lastUsedAt).getTime()-new Date(s.lastUsedAt).getTime())}catch{return[]}}function bt(e){mn();let t=We(),n=t.findIndex(o=>o.sessionId===e.sessionId);n>=0?t[n]={...t[n],lastUsedAt:e.lastUsedAt,turnCount:e.turnCount}:t.unshift(e);let s=t.slice(0,20);L.writeFileSync(qe(),JSON.stringify(s,null,2))}function wt(e){let t=e.firstPrompt.length>50?e.firstPrompt.slice(0,50)+"...":e.firstPrompt,n=new Date(e.lastUsedAt),s=hn(n);return`${t} (${s}, ${e.turnCount} turns)`}function hn(e){let n=new Date().getTime()-e.getTime(),s=Math.floor(n/6e4),o=Math.floor(n/36e5),r=Math.floor(n/864e5);return s<1?"just now":s<60?`${s}m ago`:o<24?`${o}h ago`:r<7?`${r}d ago`:e.toLocaleDateString()}var kt={reset:"\x1B[0m",dim:"\x1B[2m",bold:"\x1B[1m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",red:"\x1B[31m",gray:"\x1B[90m"};function a(e,t){return`${kt[e]}${t}${kt.reset}`}var yn={Bash:"\u26A1",Read:"\u{1F4D6}",Write:"\u270F\uFE0F",Edit:"\u{1F527}",MultiEdit:"\u{1F527}",Glob:"\u{1F50D}",Grep:"\u{1F50E}",LS:"\u{1F4C1}",WebFetch:"\u{1F310}",NotebookRead:"\u{1F4D3}",NotebookEdit:"\u{1F4DD}",Task:"\u{1F4CB}",ask_questions:"\u2753",save_specification:"\u{1F4C4}",list_specifications:"\u{1F4CB}",read_specification:"\u{1F4D6}",delete_specification:"\u{1F5D1}\uFE0F",scaffold_entity:"\u{1F3D7}\uFE0F"};function bn(e){return yn[e]||"\u{1F527}"}function ae(e,t){return e.length<=t?e:e.slice(0,t-3)+"..."}function _t(e){let t=e.split("/");return t.length<=3?e:".../"+t.slice(-2).join("/")}function Ge(e){console.log(`
|
|
1259
1293
|
${a("bold",a("cyan","\u2550".repeat(60)))}`),console.log(a("bold",` ${e}`)),console.log(`${a("bold",a("cyan","\u2550".repeat(60)))}
|
|
1260
1294
|
`)}function St(e){let t=e.split(`
|
|
1261
1295
|
`);for(let n of t)n.trim()?console.log(` ${n}`):console.log()}function vt(e,t){if(t&&typeof t=="object"){let s=t;if(e==="Task"&&s.subagent_type){let o=String(s.subagent_type);if(console.log(`
|
|
@@ -1270,7 +1304,7 @@ ${a("dim","\u2192 Answers sent, agent continuing...")}
|
|
|
1270
1304
|
`)}function xe(){console.log(a("dim",`
|
|
1271
1305
|
`+"\u2500".repeat(50)+`
|
|
1272
1306
|
`))}function xt(e,t){let n="";if(typeof t=="string")n=ae(t,100);else if(Array.isArray(t)){let s=t.find(o=>typeof o=="object"&&o!==null&&o.type==="text");if(s&&typeof s=="object"){let o=s.text;if(typeof o=="string"){let r=o.split(`
|
|
1273
|
-
`).filter(i=>i.trim());r.length>3?n=r.slice(0,3).join(" \u21B5 ")+` (+${r.length-3} more lines)`:n=r.join(" \u21B5 "),n=ae(n,100)}}}console.log(n?` ${a("dim","\u2190")} ${a("gray",n)}`:` ${a("dim","\u2190")} ${a("green","done")}`)}function It(e){return{onAssistantText:St,onAssistantToolUse:(t,n)=>{if(vt(t,n),["Write","Edit","MultiEdit"].includes(t)){let s=n,o=s.file_path||s.target_file;o&&e.onFileModified(o)}},onAssistantToolResult:xt,onResult:(t,n)=>{e.onCostUpdate(n),Ct(t,n)},onError:Tt,onAborted:Pt,onSystemInit:t=>{e.sessionId||(e.onSessionInit(t),console.log(` Session: ${t.slice(0,12)}...`))}}}import I from"inquirer";import*as j from"fs";import*as Ie from"path";var{Separator:
|
|
1307
|
+
`).filter(i=>i.trim());r.length>3?n=r.slice(0,3).join(" \u21B5 ")+` (+${r.length-3} more lines)`:n=r.join(" \u21B5 "),n=ae(n,100)}}}console.log(n?` ${a("dim","\u2190")} ${a("gray",n)}`:` ${a("dim","\u2190")} ${a("green","done")}`)}function It(e){return{onAssistantText:St,onAssistantToolUse:(t,n)=>{if(vt(t,n),["Write","Edit","MultiEdit"].includes(t)){let s=n,o=s.file_path||s.target_file;o&&e.onFileModified(o)}},onAssistantToolResult:xt,onResult:(t,n)=>{e.onCostUpdate(n),Ct(t,n)},onError:Tt,onAborted:Pt,onSystemInit:t=>{e.sessionId||(e.onSessionInit(t),console.log(` Session: ${t.slice(0,12)}...`))}}}import I from"inquirer";import*as j from"fs";import*as Ie from"path";var{Separator:wn}=I,kn=".sdd/api-config.json";function He(){return Ie.join(process.cwd(),kn)}function _n(){let e=Ie.dirname(He());j.existsSync(e)||j.mkdirSync(e,{recursive:!0})}function Sn(){try{let e=He();if(!j.existsSync(e))return null;let t=j.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return null}}function vn(e){_n();let t={...e,lastUsedAt:new Date().toISOString()};j.writeFileSync(He(),JSON.stringify(t,null,2))}async function Rt(){let{options:e}=await I.prompt([{type:"checkbox",name:"options",message:"Session options:",choices:[{name:"\u{1F50D} Auto-typecheck (self-correcting mode)",value:"typecheck"},{name:"\u{1F4E6} Auto-commit & push",value:"autoCommit"}]}]),{model:t}=await I.prompt([{type:"list",name:"model",message:"Select Claude model:",choices:[{name:"\u{1F916} Auto (let Claude choose)",value:"auto"},{name:"\u{1F9E0} Opus 4.5 (most capable)",value:"claude-opus-4-5-20251101"},{name:"\u26A1 Sonnet 4.5 (fast & capable)",value:"claude-sonnet-4-5-20250929"},{name:"\u{1F4A8} Haiku 4.5 (fastest)",value:"claude-haiku-4-5-20251001"}],default:"auto"}]);return{typecheck:e.includes("typecheck"),autoCommit:e.includes("autoCommit"),model:t}}async function Ot(){let e=We();if(e.length===0){let{prompt:r}=await I.prompt([{type:"input",name:"prompt",message:"What would you like to build?"}]);return{sessionId:null,firstPrompt:r,isResume:!1,existingTurnCount:0}}let t=[{name:"\u{1F195} Start new session",value:"new"},new wn("\u2500\u2500\u2500 Previous Sessions \u2500\u2500\u2500"),...e.map(r=>({name:wt(r),value:r.sessionId}))],{selection:n}=await I.prompt([{type:"select",name:"selection",message:"Select a session:",choices:t}]);if(n==="new"){let{prompt:r}=await I.prompt([{type:"input",name:"prompt",message:"What would you like to build?"}]);return{sessionId:null,firstPrompt:r,isResume:!1,existingTurnCount:0}}let s=e.find(r=>r.sessionId===n);console.log(`
|
|
1274
1308
|
\u{1F4C2} Resuming session: ${s.firstPrompt.slice(0,50)}...`);let{prompt:o}=await I.prompt([{type:"input",name:"prompt",message:"Continue with:"}]);return{sessionId:s.sessionId,firstPrompt:s.firstPrompt,isResume:!0,existingTurnCount:s.turnCount||0}}async function Re(){let{action:e}=await I.prompt([{type:"select",name:"action",message:"What would you like to do?",choices:[{name:"\u{1F4DD} Send follow-up message",value:"followup"},{name:"\u{1F6AA} Exit",value:"exit"}]}]);if(e==="exit")return null;let{prompt:t}=await I.prompt([{type:"input",name:"prompt",message:"Your message:"}]);return t}async function Dt(){let{action:e}=await I.prompt([{type:"select",name:"action",message:"What would you like to do?",choices:[{name:"Continue with a new prompt",value:"continue"},{name:"Exit (session saved)",value:"exit"}]}]);return e}async function Ft(){let e=process.env.API_URL||process.env.MASTER_URL,t=process.env.PROJECT_ID,n=Sn();if(n&&!e&&!t){console.log(`
|
|
1275
1309
|
\u{1F4CB} Previous API Configuration Found
|
|
1276
1310
|
`),console.log(` \u{1F517} API URL: ${n.apiUrl}`),console.log(` \u{1F194} Project ID: ${n.projectId}`);let{action:r}=await I.prompt([{type:"select",name:"action",message:"What would you like to do?",choices:[{name:"\u2705 Proceed with saved configuration",value:"proceed"},{name:"\u{1F504} Override with new configuration",value:"override"}]}]);if(r==="proceed")return{apiUrl:n.apiUrl,projectId:n.projectId}}let s=await I.prompt([{type:"input",name:"apiUrl",message:"API URL (backend endpoint):",default:e||n?.apiUrl||"http://localhost:5338",validate:r=>{if(!r.trim())return"API URL is required";try{return new URL(r),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:t||n?.projectId,validate:r=>r.trim()?!0:"Project ID is required"}]),o={apiUrl:s.apiUrl,projectId:s.projectId};return vn(o),o}async function Ut(e,t){if(t.size===0)return console.log(`
|
|
@@ -1291,14 +1325,14 @@ ${i.join(`
|
|
|
1291
1325
|
|
|
1292
1326
|
`)}
|
|
1293
1327
|
|
|
1294
|
-
File paths like "src/..." are relative to the package directory shown in parentheses.`}):(console.log(` ${a("green","\u2713 All type checks passed")}`),{passed:!0})}async function
|
|
1328
|
+
File paths like "src/..." are relative to the package directory shown in parentheses.`}):(console.log(` ${a("green","\u2713 All type checks passed")}`),{passed:!0})}async function Mt(e,t,n){if(!await e.hasChanges())return;console.log(`
|
|
1295
1329
|
${a("dim","\u{1F4E6} Committing changes...")}`);let o=await e.commitAndPush(t,n);if(o.noChanges)console.log(` ${a("dim","No changes to commit")}`);else if(o.success){let r=await e.getCurrentBranch();console.log(` ${a("green","\u2713 Committed")} ${a("dim",o.commitHash||"")}`),console.log(` ${a("dim",` \u2192 Pushed to ${r}`)}`),o.error&&console.log(` ${a("yellow",`\u26A0\uFE0F ${o.error}`)}`)}else console.log(` ${a("red",`\u274C Git error: ${o.error}`)}`)}var ce=null;process.stdout.on("error",e=>{e.code!=="EPIPE"&&console.error("stdout error:",e)});process.stderr.on("error",e=>{e.code!=="EPIPE"&&console.error("stderr error:",e)});process.on("SIGINT",()=>{ce?(console.log(`
|
|
1296
1330
|
|
|
1297
1331
|
\u23F9\uFE0F Stopping agent...
|
|
1298
1332
|
`),ce.abort()):(console.log(`
|
|
1299
1333
|
`),process.exit(0))});async function Oe(e,t={}){let{serviceManager:n,gitManager:s}=t,o=null,r,i,l=0,h=0,f=t.typecheck??!1,m=t.autoCommit??!1,v=t.model;if(e)r=e,i=e;else{let T=await Rt();n&&(f=T.typecheck),s&&(m=T.autoCommit),t.skipModelPrompt||(v=T.model);let S=await Ot();o=S.sessionId,r=S.firstPrompt,l=S.existingTurnCount,i=S.isResume&&await Re()||r}for(Cn(f,m,!!n,!!s,v);;){l++,console.log(`
|
|
1300
1334
|
Turn ${l}`),xe();let T=new Set;ce=new AbortController;let S=await ve(i,{model:v==="auto"?void 0:v,sessionId:o,projectId:process.env.PROJECT_ID||null,debugLog:!0,abortController:ce,callbacks:It({sessionId:o,onSessionInit:y=>{o=y},onCostUpdate:y=>{h+=y},onFileModified:y=>{T.add(y)}})});if(console.log("after runAgent"),ce=null,S.sessionId&&(o=S.sessionId),S.cost&&(h=S.cost),console.log("after result"),Pn(o,r,l),console.log("after saveSessionIfNeeded"),S.aborted){if(await new Promise(U=>setTimeout(U,100)),xe(),h>0&&console.log(` \u{1F4B0} Session cost so far: $${h.toFixed(4)}
|
|
1301
|
-
`),await Dt()==="exit")break;let R=await Re();if(!R)break;i=R;continue}if(console.log("after abort"),o){let y=te(o);if(y&&y.length>0){Et();let R=await Ee(y);i=Ce(R),At();continue}}if(console.log("after pending questions"),f&&n){let y=await Ut(n,T);if(!y.passed&&y.errorPrompt){i=y.errorPrompt;continue}}if(console.log("after typecheck"),m&&s){let y=S.aborted||!!S.error;await
|
|
1335
|
+
`),await Dt()==="exit")break;let R=await Re();if(!R)break;i=R;continue}if(console.log("after abort"),o){let y=te(o);if(y&&y.length>0){Et();let R=await Ee(y);i=Ce(R),At();continue}}if(console.log("after pending questions"),f&&n){let y=await Ut(n,T);if(!y.passed&&y.errorPrompt){i=y.errorPrompt;continue}}if(console.log("after typecheck"),m&&s){let y=S.aborted||!!S.error;await Mt(s,r,y)}console.log("after auto-commit"),xe();let O=await Re();if(!O)break;i=O}Tn(o,h)}function Cn(e,t,n,s,o){Ge("\u{1F680} Agent Started"),console.log(" \u{1F4DD} Debug logs \u2192 /tmp/agent-debug/"),o&&console.log(` \u{1F916} Model: ${o==="auto"?"Auto":o==="claude-opus-4-5-20251101"?"Opus 4.5":o==="claude-sonnet-4-5-20250929"?"Sonnet 4.5":o==="claude-haiku-4-5-20251001"?"Haiku 4.5":"Unknown"}`),e&&n&&console.log(" \u{1F50D} Auto-typecheck enabled (self-correcting mode)"),t&&s&&console.log(" \u{1F4E6} Auto-commit enabled"),console.log(` \u{1F4A1} Press Ctrl+C to stop the agent
|
|
1302
1336
|
`)}function Tn(e,t){Ge("\u2705 Session Complete"),e&&console.log(" \u{1F4BE} Session saved - you can resume it next time"),t>0&&console.log(` \u{1F4B0} Total cost: $${t.toFixed(4)}`),console.log()}function Pn(e,t,n){e&&bt({sessionId:e,firstPrompt:t,createdAt:new Date().toISOString(),lastUsedAt:new Date().toISOString(),turnCount:n})}process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT=process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT||"60000";async function En(){let e=process.env.API_URL||process.env.MASTER_URL,t=process.env.PROJECT_ID,n=e,s=t;if(!e||!t){console.log(`
|
|
1303
1337
|
\u{1F4CB} API Configuration Required
|
|
1304
1338
|
`);let f=await Ft();n=f.apiUrl,s=f.projectId}process.env.API_URL=n,process.env.PROJECT_ID=s;let o=process.env.WORKSPACE_DIR||process.cwd(),r=Te({workspaceDir:o}),i=Pe(o),l=ne(o);ge(r),ue(l),await Oe(void 0,{serviceManager:r,gitManager:i})}En().catch(e=>{console.error("Fatal error:",e),process.exit(1)});
|