glenn-code 1.0.12 → 1.0.14

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.
@@ -2,19 +2,19 @@
2
2
  // Glenn Code - Bundled and minified
3
3
  // (c) DNM Lab - All rights reserved
4
4
 
5
- import{McpServer as E}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as I}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as a}from"zod";import*as f from"fs";import*as b from"path";var k=process.env.API_URL||"http://localhost:5338",i=process.env.PROJECT_ID,_=process.env.PROJECT_KEY,D=process.env.WORKSPACE_DIR||process.cwd(),w="/tmp/agent-logs",S=b.join(w,"stdio-mcp-server.log");try{f.existsSync(w)||f.mkdirSync(w,{recursive:!0})}catch{}function n(e,...t){let s=`[${new Date().toISOString()}] ${e} ${t.map(o=>typeof o=="object"?JSON.stringify(o):String(o)).join(" ")}
6
- `;console.error(`[Stdio MCP Server] ${e}`,...t);try{f.appendFileSync(S,s,"utf8")}catch{}}n("Starting...");n("API_URL:",k);n("PROJECT_ID:",i||"(not set)");n("PROJECT_KEY:",_?`${_.slice(0,12)}...`:"(not set)");n("WORKSPACE_DIR:",D);n("Process cwd:",process.cwd());n("__filename equivalent:",import.meta.url);n("Log file:",S);function $(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function m(e){return e&&e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}function h(e){if(typeof e=="string")return m(e);if(Array.isArray(e))return e.map(t=>h(t));if(e&&typeof e=="object"){let t={};for(let[r,s]of Object.entries(e))t[r]=h(s);return t}return e}async function p(e,t){let r=`${k}${e}`,s={"Content-Type":"application/json"};return _&&(s["X-Project-Key"]=_),fetch(r,{...t,headers:{...s,...t?.headers}})}var u=new E({name:"agent-planning",version:"1.0.0"});u.tool("save_specification","Save a specification. First write content to a temp file with Write tool, then call this with the file path.",{name:a.string().describe("Specification name"),filePath:a.string().describe("Path to file containing the spec content")},async({name:e,filePath:t})=>{if(n("save_specification called",{name:e,filePath:t,projectId:i||"(not set)"}),!i)return n("save_specification failed: PROJECT_ID not set"),{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};let r=b.resolve(t);if(!f.existsSync(r))return n("save_specification failed: file not found",{resolvedPath:r}),{content:[{type:"text",text:`\u274C File not found: ${t}. Use Write tool first to create the file.`}]};let s=f.readFileSync(r,"utf-8"),o=$(e);n("save_specification reading file",{resolvedPath:r,contentLength:s.length,slug:o});try{let c=await p(`/api/projects/${i}/specifications`,{method:"POST",body:JSON.stringify({slug:o,name:e,content:s})});if(!c.ok){let l=await c.text();return n("save_specification failed:",{status:c.status,error:l}),{content:[{type:"text",text:`\u274C Failed to save: ${l}`}]}}let d=await c.json();return d.success?(n("save_specification succeeded:",{name:e,slug:o}),{content:[{type:"text",text:`\u2705 Saved specification: ${o}.spec.md`}]}):(n("save_specification failed:",{result:d}),{content:[{type:"text",text:`\u274C ${d.error||"Failed to save"}`}]})}catch(c){return n("save_specification error:",c instanceof Error?c.message:String(c),c),{content:[{type:"text",text:`\u274C Error: ${c instanceof Error?c.message:String(c)}`}]}}});u.tool("read_specification","Read a specification's content",{name:a.string().describe("Specification name")},async({name:e})=>{if(!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};let t=$(e);try{let r=await p(`/api/projects/${i}/specifications/${t}`);if(!r.ok)return{content:[{type:"text",text:`\u274C Specification "${e}" not found.`}]};let s=await r.json();return!s.success||!s.content?{content:[{type:"text",text:`\u274C ${s.error||"Specification not found"}`}]}:{content:[{type:"text",text:m(s.content)}]}}catch(r){return{content:[{type:"text",text:`\u274C Error: ${r instanceof Error?r.message:String(r)}`}]}}});u.tool("list_specifications","List all specifications",{},async()=>{if(!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await p(`/api/projects/${i}/specifications`);if(!e.ok)return{content:[{type:"text",text:"\u{1F4CB} No specifications found."}]};let t=await e.json();return!t.success||!t.specifications||t.specifications.length===0?{content:[{type:"text",text:"\u{1F4CB} No specifications found."}]}:{content:[{type:"text",text:`\u{1F4CB} Specifications:
5
+ import{McpServer as v}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as O}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as d}from"zod";import*as g from"fs";import*as _ from"path";import{execFileSync as D}from"child_process";var E=process.env.API_URL||"http://localhost:5338",i=process.env.PROJECT_ID,b=process.env.PROJECT_KEY,T=process.env.WORKSPACE_DIR||process.cwd(),k="/tmp/agent-logs",C=_.join(k,"stdio-mcp-server.log");try{g.existsSync(k)||g.mkdirSync(k,{recursive:!0})}catch{}function s(t,...e){let r=`[${new Date().toISOString()}] ${t} ${e.map(o=>typeof o=="object"?JSON.stringify(o):String(o)).join(" ")}
6
+ `;console.error(`[Stdio MCP Server] ${t}`,...e);try{g.appendFileSync(C,r,"utf8")}catch{}}s("Starting...");s("API_URL:",E);s("PROJECT_ID:",i||"(not set)");s("PROJECT_KEY:",b?`${b.slice(0,12)}...`:"(not set)");s("WORKSPACE_DIR:",T);s("Process cwd:",process.cwd());s("__filename equivalent:",import.meta.url);s("Log file:",C);function I(t){return t.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function h(t){return t&&t.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}function $(t){if(typeof t=="string")return h(t);if(Array.isArray(t))return t.map(e=>$(e));if(t&&typeof t=="object"){let e={};for(let[n,r]of Object.entries(t))e[n]=$(r);return e}return t}async function l(t,e){let n=`${E}${t}`,r={"Content-Type":"application/json"};return b&&(r["X-Project-Key"]=b),fetch(n,{...e,headers:{...r,...e?.headers}})}var p=new v({name:"agent-planning",version:"1.0.0"});p.tool("save_specification","Save a specification. First write content to a temp file with Write tool, then call this with the file path.",{name:d.string().describe("Specification name"),filePath:d.string().describe("Path to file containing the spec content")},async({name:t,filePath:e})=>{if(s("save_specification called",{name:t,filePath:e,projectId:i||"(not set)"}),!i)return s("save_specification failed: PROJECT_ID not set"),{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};let n=_.resolve(e);if(!g.existsSync(n))return s("save_specification failed: file not found",{resolvedPath:n}),{content:[{type:"text",text:`\u274C File not found: ${e}. Use Write tool first to create the file.`}]};let r=g.readFileSync(n,"utf-8"),o=I(t);s("save_specification reading file",{resolvedPath:n,contentLength:r.length,slug:o});try{let c=await l(`/api/projects/${i}/specifications`,{method:"POST",body:JSON.stringify({slug:o,name:t,content:r})});if(!c.ok){let u=await c.text();return s("save_specification failed:",{status:c.status,error:u}),{content:[{type:"text",text:`\u274C Failed to save: ${u}`}]}}let a=await c.json();return a.success?(s("save_specification succeeded:",{name:t,slug:o}),{content:[{type:"text",text:`\u2705 Saved specification: ${o}.spec.md`}]}):(s("save_specification failed:",{result:a}),{content:[{type:"text",text:`\u274C ${a.error||"Failed to save"}`}]})}catch(c){return s("save_specification error:",c instanceof Error?c.message:String(c),c),{content:[{type:"text",text:`\u274C Error: ${c instanceof Error?c.message:String(c)}`}]}}});p.tool("read_specification","Read a specification's content",{name:d.string().describe("Specification name")},async({name:t})=>{if(!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};let e=I(t);try{let n=await l(`/api/projects/${i}/specifications/${e}`);if(!n.ok)return{content:[{type:"text",text:`\u274C Specification "${t}" not found.`}]};let r=await n.json();return!r.success||!r.content?{content:[{type:"text",text:`\u274C ${r.error||"Specification not found"}`}]}:{content:[{type:"text",text:h(r.content)}]}}catch(n){return{content:[{type:"text",text:`\u274C Error: ${n instanceof Error?n.message:String(n)}`}]}}});p.tool("list_specifications","List all specifications",{},async()=>{if(!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await l(`/api/projects/${i}/specifications`);if(!t.ok)return{content:[{type:"text",text:"\u{1F4CB} No specifications found."}]};let e=await t.json();return!e.success||!e.specifications||e.specifications.length===0?{content:[{type:"text",text:"\u{1F4CB} No specifications found."}]}:{content:[{type:"text",text:`\u{1F4CB} Specifications:
7
7
 
8
- ${t.specifications.map(s=>`- ${s.name} (${s.slug}.spec.md)`).join(`
9
- `)}`}]}}catch(e){return{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});u.tool("delete_specification","Delete a specification",{name:a.string().describe("Specification name")},async({name:e})=>{if(!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};let t=$(e);try{let r=await p(`/api/projects/${i}/specifications/${t}`,{method:"DELETE"});if(!r.ok)return{content:[{type:"text",text:`\u274C Specification "${e}" not found.`}]};let s=await r.json();return s.success?{content:[{type:"text",text:`\u{1F5D1}\uFE0F Deleted: ${t}.spec.md`}]}:{content:[{type:"text",text:`\u274C ${s.error||"Failed to delete"}`}]}}catch(r){return{content:[{type:"text",text:`\u274C Error: ${r instanceof Error?r.message:String(r)}`}]}}});var O=a.object({id:a.string().describe("Unique identifier for the question"),question:a.string().describe("The question to ask the user"),options:a.array(a.string()).optional().describe("Predefined options the user can choose from"),allowFreeText:a.boolean().optional().default(!0).describe("Allow user to type a custom answer"),multiSelect:a.boolean().optional().default(!1).describe("Allow selecting multiple options")}),x=null;u.tool("start_question_session",`Start a conversational question session to gather requirements from the user.
8
+ ${e.specifications.map(r=>`- ${r.name} (${r.slug}.spec.md)`).join(`
9
+ `)}`}]}}catch(t){return{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});p.tool("delete_specification","Delete a specification",{name:d.string().describe("Specification name")},async({name:t})=>{if(!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};let e=I(t);try{let n=await l(`/api/projects/${i}/specifications/${e}`,{method:"DELETE"});if(!n.ok)return{content:[{type:"text",text:`\u274C Specification "${t}" not found.`}]};let r=await n.json();return r.success?{content:[{type:"text",text:`\u{1F5D1}\uFE0F Deleted: ${e}.spec.md`}]}:{content:[{type:"text",text:`\u274C ${r.error||"Failed to delete"}`}]}}catch(n){return{content:[{type:"text",text:`\u274C Error: ${n instanceof Error?n.message:String(n)}`}]}}});var N=d.object({id:d.string().describe("Unique identifier for the question"),question:d.string().describe("The question to ask the user"),options:d.array(d.string()).optional().describe("Predefined options the user can choose from"),allowFreeText:d.boolean().optional().default(!0).describe("Allow user to type a custom answer"),multiSelect:d.boolean().optional().default(!1).describe("Allow selecting multiple options")}),j=null;p.tool("start_question_session",`Start a conversational question session to gather requirements from the user.
10
10
 
11
11
  Call this BEFORE asking any questions. It initializes the session context.
12
12
 
13
13
  After calling this, use ask_question to ask individual questions one at a time.
14
- Each question should build on the previous answer (leading questions approach).`,{purpose:a.string().describe("What you're trying to understand/gather (e.g., 'understand feature requirements')")},async({purpose:e})=>(n("start_question_session called",{purpose:e,projectId:i||"(not set)"}),x=e,{content:[{type:"text",text:`\u2705 Question session started.
15
- Purpose: ${e}
14
+ Each question should build on the previous answer (leading questions approach).`,{purpose:d.string().describe("What you're trying to understand/gather (e.g., 'understand feature requirements')")},async({purpose:t})=>(s("start_question_session called",{purpose:t,projectId:i||"(not set)"}),j=t,{content:[{type:"text",text:`\u2705 Question session started.
15
+ Purpose: ${t}
16
16
 
17
- Now call ask_question to ask your first question. Ask ONE question at a time, and let the user's answer guide your next question.`}]}));u.tool("ask_question",`Ask a SINGLE question to the user and wait for their answer.
17
+ Now call ask_question to ask your first question. Ask ONE question at a time, and let the user's answer guide your next question.`}]}));p.tool("ask_question",`Ask a SINGLE question to the user and wait for their answer.
18
18
 
19
19
  This tool will:
20
20
  1. Display the question in the UI
@@ -31,33 +31,33 @@ Parameters:
31
31
  - question: The question text
32
32
  - options: Optional predefined choices (user can still type custom answer)
33
33
  - allowFreeText: Let user type custom answer (default: true)
34
- - multiSelect: Allow multiple selections (default: false)`,{question:a.string().describe("The question to ask"),options:a.array(a.string()).optional().describe("Predefined answer options"),allowFreeText:a.boolean().optional().default(!0),multiSelect:a.boolean().optional().default(!1)},async({question:e,options:t,allowFreeText:r,multiSelect:s})=>{if(n("ask_question called",{question:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};let c={id:`q_${Date.now()}`,question:e,options:t||[],allowFreeText:r??!0,multiSelect:s??!1};try{let d=await p(`/api/projects/${i}/questions/ask`,{method:"POST",body:JSON.stringify({question:c,sessionContext:x?{purpose:x}:null})});if(!d.ok){let l=await d.text();return n("ask_question failed:",{status:d.status,error:l}),{content:[{type:"text",text:`\u274C Failed to ask question: ${l}`}]}}return n("ask_question succeeded"),{content:[{type:"text",text:`\u2705 Question sent to user. STOP HERE and wait for their answer.
34
+ - multiSelect: Allow multiple selections (default: false)`,{question:d.string().describe("The question to ask"),options:d.array(d.string()).optional().describe("Predefined answer options"),allowFreeText:d.boolean().optional().default(!0),multiSelect:d.boolean().optional().default(!1)},async({question:t,options:e,allowFreeText:n,multiSelect:r})=>{if(s("ask_question called",{question:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};let c={id:`q_${Date.now()}`,question:t,options:e||[],allowFreeText:n??!0,multiSelect:r??!1};try{let a=await l(`/api/projects/${i}/questions/ask`,{method:"POST",body:JSON.stringify({question:c,sessionContext:j?{purpose:j}:null})});if(!a.ok){let u=await a.text();return s("ask_question failed:",{status:a.status,error:u}),{content:[{type:"text",text:`\u274C Failed to ask question: ${u}`}]}}return s("ask_question succeeded"),{content:[{type:"text",text:`\u2705 Question sent to user. STOP HERE and wait for their answer.
35
35
 
36
- The user's answer will be provided in the next message. Based on their answer, decide what to ask next (or proceed with implementation if you have enough info).`}]}}catch(d){return n("ask_question error:",d instanceof Error?d.message:String(d)),{content:[{type:"text",text:`\u274C Error: ${d instanceof Error?d.message:String(d)}`}]}}});u.tool("end_question_session",`End the current question session and summarize what you learned.
36
+ The user's answer will be provided in the next message. Based on their answer, decide what to ask next (or proceed with implementation if you have enough info).`}]}}catch(a){return s("ask_question error:",a instanceof Error?a.message:String(a)),{content:[{type:"text",text:`\u274C Error: ${a instanceof Error?a.message:String(a)}`}]}}});p.tool("end_question_session",`End the current question session and summarize what you learned.
37
37
 
38
38
  Call this when:
39
39
  1. You have gathered enough information to proceed
40
40
  2. The user said "start implementing", "that's enough", "let's go", etc.
41
41
  3. You've asked enough questions (aim for 3-7, don't exhaust the user!)
42
42
 
43
- After ending the session, you can proceed with implementation.`,{summary:a.string().describe("Brief summary of what you learned from the questions")},async({summary:e})=>{if(n("end_question_session called",{summary:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{await p(`/api/projects/${i}/questions/end-session`,{method:"POST",body:JSON.stringify({summary:e})})}catch(t){n("end_question_session error (non-fatal):",t instanceof Error?t.message:String(t))}return x=null,{content:[{type:"text",text:`\u2705 Question session ended.
43
+ After ending the session, you can proceed with implementation.`,{summary:d.string().describe("Brief summary of what you learned from the questions")},async({summary:t})=>{if(s("end_question_session called",{summary:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{await l(`/api/projects/${i}/questions/end-session`,{method:"POST",body:JSON.stringify({summary:t})})}catch(e){s("end_question_session error (non-fatal):",e instanceof Error?e.message:String(e))}return j=null,{content:[{type:"text",text:`\u2705 Question session ended.
44
44
 
45
- Summary: ${e}
45
+ Summary: ${t}
46
46
 
47
- You may now proceed with implementation based on what you learned.`}]}});u.tool("report_learning",`Report a critical insight that would save significant time if added to CLAUDE.md.
47
+ You may now proceed with implementation based on what you learned.`}]}});p.tool("report_learning",`Report a critical insight that would save significant time if added to CLAUDE.md.
48
48
  Use this when you discover something important about the codebase or workflow.
49
49
 
50
50
  Categories:
51
51
  - missing_instruction: Something that should be documented in CLAUDE.md
52
52
  - time_saver: Workflow tip that saves significant time
53
53
  - gotcha: Common mistake or pitfall to avoid
54
- - workflow_tip: Best practice you discovered`,{category:a.enum(["missing_instruction","time_saver","gotcha","workflow_tip"]).describe("Category of the learning"),description:a.string().describe("The insight - write it as you'd want it in CLAUDE.md"),file:a.string().optional().describe("Relevant file path if applicable")},async({category:e,description:t,file:r})=>{if(n("report_learning called",{category:e,projectId:i||"(not set)"}),!i)return n("report_learning failed: PROJECT_ID not set"),{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let s=await p(`/api/projects/${i}/insights`,{method:"POST",body:JSON.stringify({type:"Learning",category:e,description:t,file:r})});if(!s.ok){let o=await s.text();return n("report_learning failed:",{status:s.status,error:o}),{content:[{type:"text",text:`\u26A0\uFE0F Learning noted locally (API error): ${e}`}]}}return n("report_learning succeeded:",{category:e}),{content:[{type:"text",text:`\u2705 Learning recorded: ${e}`}]}}catch(s){return n("report_learning error:",s instanceof Error?s.message:String(s)),{content:[{type:"text",text:`\u26A0\uFE0F Learning noted locally (error): ${e}`}]}}});u.tool("report_bug",`Report a bug, issue, or enhancement that needs human attention.
54
+ - workflow_tip: Best practice you discovered`,{category:d.enum(["missing_instruction","time_saver","gotcha","workflow_tip"]).describe("Category of the learning"),description:d.string().describe("The insight - write it as you'd want it in CLAUDE.md"),file:d.string().optional().describe("Relevant file path if applicable")},async({category:t,description:e,file:n})=>{if(s("report_learning called",{category:t,projectId:i||"(not set)"}),!i)return s("report_learning failed: PROJECT_ID not set"),{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let r=await l(`/api/projects/${i}/insights`,{method:"POST",body:JSON.stringify({type:"Learning",category:t,description:e,file:n})});if(!r.ok){let o=await r.text();return s("report_learning failed:",{status:r.status,error:o}),{content:[{type:"text",text:`\u26A0\uFE0F Learning noted locally (API error): ${t}`}]}}return s("report_learning succeeded:",{category:t}),{content:[{type:"text",text:`\u2705 Learning recorded: ${t}`}]}}catch(r){return s("report_learning error:",r instanceof Error?r.message:String(r)),{content:[{type:"text",text:`\u26A0\uFE0F Learning noted locally (error): ${t}`}]}}});p.tool("report_bug",`Report a bug, issue, or enhancement that needs human attention.
55
55
 
56
56
  Categories:
57
57
  - bug: Code bug found in the application
58
58
  - scaffold_improvement: Enhancement needed for scaffold tool
59
59
  - agent_env: Issue with agent environment or setup
60
- - code_generation: Problem with generated code patterns`,{category:a.enum(["bug","scaffold_improvement","agent_env","code_generation"]).describe("Category of the bug/issue"),description:a.string().describe("Clear description of the issue"),file:a.string().optional().describe("File where the issue was found")},async({category:e,description:t,file:r})=>{if(n("report_bug called",{category:e,projectId:i||"(not set)"}),!i)return n("report_bug failed: PROJECT_ID not set"),{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let s=await p(`/api/projects/${i}/insights`,{method:"POST",body:JSON.stringify({type:"Bug",category:e,description:t,file:r})});if(!s.ok){let o=await s.text();return n("report_bug failed:",{status:s.status,error:o}),{content:[{type:"text",text:`\u26A0\uFE0F Bug noted locally (API error): ${e}`}]}}return n("report_bug succeeded:",{category:e}),{content:[{type:"text",text:`\u{1F41B} Bug reported: ${e}`}]}}catch(s){return n("report_bug error:",s instanceof Error?s.message:String(s)),{content:[{type:"text",text:`\u26A0\uFE0F Bug noted locally (error): ${e}`}]}}});u.tool("report_debug_insight",`\u{1F6D1} DEBUG MODE TOOL: Report unexpected behavior and signal that you're stopping.
60
+ - code_generation: Problem with generated code patterns`,{category:d.enum(["bug","scaffold_improvement","agent_env","code_generation"]).describe("Category of the bug/issue"),description:d.string().describe("Clear description of the issue"),file:d.string().optional().describe("File where the issue was found")},async({category:t,description:e,file:n})=>{if(s("report_bug called",{category:t,projectId:i||"(not set)"}),!i)return s("report_bug failed: PROJECT_ID not set"),{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let r=await l(`/api/projects/${i}/insights`,{method:"POST",body:JSON.stringify({type:"Bug",category:t,description:e,file:n})});if(!r.ok){let o=await r.text();return s("report_bug failed:",{status:r.status,error:o}),{content:[{type:"text",text:`\u26A0\uFE0F Bug noted locally (API error): ${t}`}]}}return s("report_bug succeeded:",{category:t}),{content:[{type:"text",text:`\u{1F41B} Bug reported: ${t}`}]}}catch(r){return s("report_bug error:",r instanceof Error?r.message:String(r)),{content:[{type:"text",text:`\u26A0\uFE0F Bug noted locally (error): ${t}`}]}}});p.tool("report_debug_insight",`\u{1F6D1} DEBUG MODE TOOL: Report unexpected behavior and signal that you're stopping.
61
61
 
62
62
  Use this when DEBUG MODE is active and you encounter:
63
63
  - Scaffold failures or unexpected output
@@ -66,97 +66,97 @@ Use this when DEBUG MODE is active and you encounter:
66
66
  - Anything that deviates from expected flow
67
67
  - Errors you don't understand
68
68
 
69
- After calling this, you MUST stop and wait for user feedback.`,{category:a.enum(["scaffold_failure","build_error","unexpected_behavior","weird_error"]).describe("Category of the debug issue"),description:a.string().describe("What went wrong - be specific"),context:a.string().optional().describe("What you were trying to do when this happened"),errorOutput:a.string().optional().describe("The actual error message or output")},async({category:e,description:t,context:r,errorOutput:s})=>{if(n("report_debug_insight called",{category:e,projectId:i||"(not set)"}),!i)return n("report_debug_insight failed: PROJECT_ID not set"),{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let o=[t,r?`
69
+ After calling this, you MUST stop and wait for user feedback.`,{category:d.enum(["scaffold_failure","build_error","unexpected_behavior","weird_error"]).describe("Category of the debug issue"),description:d.string().describe("What went wrong - be specific"),context:d.string().optional().describe("What you were trying to do when this happened"),errorOutput:d.string().optional().describe("The actual error message or output")},async({category:t,description:e,context:n,errorOutput:r})=>{if(s("report_debug_insight called",{category:t,projectId:i||"(not set)"}),!i)return s("report_debug_insight failed: PROJECT_ID not set"),{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let o=[e,n?`
70
70
 
71
- **Context:** ${r}`:"",s?`
71
+ **Context:** ${n}`:"",r?`
72
72
 
73
73
  **Error Output:**
74
74
  \`\`\`
75
- ${s}
76
- \`\`\``:""].join(""),c=await p(`/api/projects/${i}/insights`,{method:"POST",body:JSON.stringify({type:"DebugInsight",category:e,description:o})});if(!c.ok){let d=await c.text();return n("report_debug_insight failed:",{status:c.status,error:d}),{content:[{type:"text",text:"\u{1F6D1} Debug insight recorded locally (API error). STOP and wait for user."}]}}return n("report_debug_insight succeeded:",{category:e}),{content:[{type:"text",text:`\u{1F6D1} Debug insight reported: ${e}
77
-
78
- You MUST now stop and say: "\u{1F6D1} DEBUG: I've hit an issue and reported it. Please review and tell me how to proceed."`}]}}catch(o){return n("report_debug_insight error:",o instanceof Error?o.message:String(o)),{content:[{type:"text",text:"\u{1F6D1} Debug insight recorded locally (error). STOP and wait for user."}]}}});u.tool("get_kanban_board",`Get kanban board overview - columns with card counts only.
79
- Use get_column_cards to fetch cards for a specific column.`,{},async()=>{if(n("get_kanban_board called",{projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await p(`/api/kanban-boards/by-project/${i}/full`);if(!e.ok)return e.status===404?{content:[{type:"text",text:"\u{1F4CB} No kanban board found for this project."}]}:{content:[{type:"text",text:`\u274C Failed to get board: ${await e.text()}`}]};let t=await e.json(),r=h(t),s=`\u{1F4CB} **${r.title}**
80
-
81
- `;for(let o of r.columns)s+=`- **${o.title}**: ${o.cards.length} cards (id: ${o.id})
82
- `;return s+="\nUse `get_column_cards` with column name to see cards.",n("get_kanban_board succeeded"),{content:[{type:"text",text:s}]}}catch(e){return n("get_kanban_board error:",e instanceof Error?e.message:String(e)),{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});u.tool("get_column_cards",`Get cards in a specific column. Returns card titles, priorities, and IDs.
83
- Use get_card_details for full card info including description and subtasks.`,{column:a.string().describe("Column name (e.g., 'Todo', 'In Progress', 'Done')")},async({column:e})=>{if(n("get_column_cards called",{column:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await p(`/api/kanban-boards/by-project/${i}/full`);if(!t.ok)return{content:[{type:"text",text:"\u274C Failed to get board"}]};let r=await t.json(),s=h(r),o=s.columns.find(d=>d.title.toLowerCase()===e.toLowerCase());if(!o){let d=s.columns.map(l=>l.title).join(", ");return{content:[{type:"text",text:`\u274C Column "${e}" not found. Available: ${d}`}]}}if(o.cards.length===0)return{content:[{type:"text",text:`\u{1F4CB} **${o.title}**: No cards`}]};let c=`\u{1F4CB} **${o.title}** (${o.cards.length} cards)
84
-
85
- `;for(let d of o.cards){let l=d.priority!=="Medium"?` [${d.priority}]`:"",g=d.subtasks?.length||0,y=g>0?` (${g} subtasks)`:"";c+=`- **${m(d.title)}**${l}${y}
86
- id: ${d.id}
87
- `}return c+="\nUse `get_card_details` with card ID for full info.",n("get_column_cards succeeded",{column:o.title,cardCount:o.cards.length}),{content:[{type:"text",text:c}]}}catch(t){return n("get_column_cards error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});u.tool("get_card_details","Get full details of a specific card including description and subtasks.",{cardId:a.string().describe("Card ID (GUID)")},async({cardId:e})=>{if(n("get_card_details called",{cardId:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await p(`/api/kanban-boards/by-project/${i}/full`);if(!t.ok)return{content:[{type:"text",text:"\u274C Failed to get board"}]};let r=await t.json(),s=h(r),o=null,c="";for(let l of s.columns){let g=l.cards.find(y=>y.id===e);if(g){o=g,c=l.title;break}}if(!o)return{content:[{type:"text",text:`\u274C Card not found: ${e}`}]};let d=`\u{1F4CB} **${m(o.title)}**
88
- `;if(d+=`Column: ${c}
89
- `,d+=`Priority: ${o.priority}
90
- `,o.dueDate&&(d+=`Due: ${new Date(o.dueDate).toLocaleDateString()}
91
- `),d+=`ID: ${o.id}
92
-
93
- `,o.description&&(d+=`**Description:**
94
- ${m(o.description)}
95
-
96
- `),o.subtasks&&o.subtasks.length>0){d+=`**Subtasks:**
97
- `;for(let l of o.subtasks){let g=l.isCompleted?"\u2705":"\u2B1C";d+=`${g} ${m(l.title)} (id: ${l.id})
98
- `}}return n("get_card_details succeeded",{cardId:e}),{content:[{type:"text",text:d}]}}catch(t){return n("get_card_details error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});u.tool("create_kanban_card",`Create a new card on the kanban board.
75
+ ${r}
76
+ \`\`\``:""].join(""),c=await l(`/api/projects/${i}/insights`,{method:"POST",body:JSON.stringify({type:"DebugInsight",category:t,description:o})});if(!c.ok){let a=await c.text();return s("report_debug_insight failed:",{status:c.status,error:a}),{content:[{type:"text",text:"\u{1F6D1} Debug insight recorded locally (API error). STOP and wait for user."}]}}return s("report_debug_insight succeeded:",{category:t}),{content:[{type:"text",text:`\u{1F6D1} Debug insight reported: ${t}
77
+
78
+ You MUST now stop and say: "\u{1F6D1} DEBUG: I've hit an issue and reported it. Please review and tell me how to proceed."`}]}}catch(o){return s("report_debug_insight error:",o instanceof Error?o.message:String(o)),{content:[{type:"text",text:"\u{1F6D1} Debug insight recorded locally (error). STOP and wait for user."}]}}});p.tool("get_kanban_board",`Get kanban board overview - columns with card counts only.
79
+ Use get_column_cards to fetch cards for a specific column.`,{},async()=>{if(s("get_kanban_board called",{projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await l(`/api/kanban-boards/by-project/${i}/full`);if(!t.ok)return t.status===404?{content:[{type:"text",text:"\u{1F4CB} No kanban board found for this project."}]}:{content:[{type:"text",text:`\u274C Failed to get board: ${await t.text()}`}]};let e=await t.json(),n=$(e),r=`\u{1F4CB} **${n.title}**
80
+
81
+ `;for(let o of n.columns)r+=`- **${o.title}**: ${o.cards.length} cards (id: ${o.id})
82
+ `;return r+="\nUse `get_column_cards` with column name to see cards.",s("get_kanban_board succeeded"),{content:[{type:"text",text:r}]}}catch(t){return s("get_kanban_board error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});p.tool("get_column_cards",`Get cards in a specific column. Returns card titles, priorities, and IDs.
83
+ Use get_card_details for full card info including description and subtasks.`,{column:d.string().describe("Column name (e.g., 'Todo', 'In Progress', 'Done')")},async({column:t})=>{if(s("get_column_cards called",{column:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await l(`/api/kanban-boards/by-project/${i}/full`);if(!e.ok)return{content:[{type:"text",text:"\u274C Failed to get board"}]};let n=await e.json(),r=$(n),o=r.columns.find(a=>a.title.toLowerCase()===t.toLowerCase());if(!o){let a=r.columns.map(u=>u.title).join(", ");return{content:[{type:"text",text:`\u274C Column "${t}" not found. Available: ${a}`}]}}if(o.cards.length===0)return{content:[{type:"text",text:`\u{1F4CB} **${o.title}**: No cards`}]};let c=`\u{1F4CB} **${o.title}** (${o.cards.length} cards)
84
+
85
+ `;for(let a of o.cards){let u=a.priority!=="Medium"?` [${a.priority}]`:"",f=a.subtasks?.length||0,m=f>0?` (${f} subtasks)`:"";c+=`- **${h(a.title)}**${u}${m}
86
+ id: ${a.id}
87
+ `}return c+="\nUse `get_card_details` with card ID for full info.",s("get_column_cards succeeded",{column:o.title,cardCount:o.cards.length}),{content:[{type:"text",text:c}]}}catch(e){return s("get_column_cards error:",e instanceof Error?e.message:String(e)),{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});p.tool("get_card_details","Get full details of a specific card including description and subtasks.",{cardId:d.string().describe("Card ID (GUID)")},async({cardId:t})=>{if(s("get_card_details called",{cardId:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await l(`/api/kanban-boards/by-project/${i}/full`);if(!e.ok)return{content:[{type:"text",text:"\u274C Failed to get board"}]};let n=await e.json(),r=$(n),o=null,c="";for(let u of r.columns){let f=u.cards.find(m=>m.id===t);if(f){o=f,c=u.title;break}}if(!o)return{content:[{type:"text",text:`\u274C Card not found: ${t}`}]};let a=`\u{1F4CB} **${h(o.title)}**
88
+ `;if(a+=`Column: ${c}
89
+ `,a+=`Priority: ${o.priority}
90
+ `,o.dueDate&&(a+=`Due: ${new Date(o.dueDate).toLocaleDateString()}
91
+ `),a+=`ID: ${o.id}
92
+
93
+ `,o.description&&(a+=`**Description:**
94
+ ${h(o.description)}
95
+
96
+ `),o.subtasks&&o.subtasks.length>0){a+=`**Subtasks:**
97
+ `;for(let u of o.subtasks){let f=u.isCompleted?"\u2705":"\u2B1C";a+=`${f} ${h(u.title)} (id: ${u.id})
98
+ `}}return s("get_card_details succeeded",{cardId:t}),{content:[{type:"text",text:a}]}}catch(e){return s("get_card_details error:",e instanceof Error?e.message:String(e)),{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});p.tool("create_kanban_card",`Create a new card on the kanban board.
99
99
  Column names are typically: Backlog, Todo, In Progress, Done
100
- The tool will match column names case-insensitively.`,{title:a.string().describe("Card title (max 200 chars)"),column:a.string().describe("Column name (e.g., 'Backlog', 'Todo', 'In Progress', 'Done')"),description:a.string().optional().describe("Card description (max 2000 chars)"),priority:a.enum(["Low","Medium","High","Urgent"]).optional().describe("Priority level (default: Medium)"),dueDate:a.string().optional().describe("Due date in ISO format (e.g., '2024-12-31')")},async({title:e,column:t,description:r,priority:s,dueDate:o})=>{if(n("create_kanban_card called",{title:e,column:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let c=await p(`/api/projects/${i}/kanban/cards`,{method:"POST",body:JSON.stringify({title:e,column:t,description:r,priority:s||"Medium",dueDate:o?new Date(o).toISOString():void 0})}),d=await c.json();return!c.ok||!d.success?(n("create_kanban_card failed:",{result:d}),{content:[{type:"text",text:`\u274C ${d.error||"Failed to create card"}`}]}):(n("create_kanban_card succeeded:",{id:d.card.id}),{content:[{type:"text",text:`\u2705 Created card "${d.card.title}" in ${d.card.columnName} (id: ${d.card.id})`}]})}catch(c){return n("create_kanban_card error:",c instanceof Error?c.message:String(c)),{content:[{type:"text",text:`\u274C Error: ${c instanceof Error?c.message:String(c)}`}]}}});u.tool("update_kanban_card",`Update an existing kanban card's properties.
101
- Use get_kanban_board first to get the card ID.`,{cardId:a.string().describe("Card ID (GUID from get_kanban_board)"),title:a.string().optional().describe("New title"),description:a.string().optional().describe("New description"),priority:a.enum(["Low","Medium","High","Urgent"]).optional().describe("New priority"),dueDate:a.string().optional().describe("New due date in ISO format")},async({cardId:e,title:t,description:r,priority:s,dueDate:o})=>{if(n("update_kanban_card called",{cardId:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let c=await p(`/api/projects/${i}/kanban/cards/${e}`,{method:"PUT",body:JSON.stringify({title:t,description:r,priority:s,dueDate:o?new Date(o).toISOString():void 0})}),d=await c.json();return!c.ok||!d.success?(n("update_kanban_card failed:",{result:d}),{content:[{type:"text",text:`\u274C ${d.error||"Failed to update card"}`}]}):(n("update_kanban_card succeeded:",{id:d.card.id}),{content:[{type:"text",text:`\u2705 Updated card "${d.card.title}"`}]})}catch(c){return n("update_kanban_card error:",c instanceof Error?c.message:String(c)),{content:[{type:"text",text:`\u274C Error: ${c instanceof Error?c.message:String(c)}`}]}}});u.tool("move_kanban_card",`Move a card to a different column.
100
+ The tool will match column names case-insensitively.`,{title:d.string().describe("Card title (max 200 chars)"),column:d.string().describe("Column name (e.g., 'Backlog', 'Todo', 'In Progress', 'Done')"),description:d.string().optional().describe("Card description (max 2000 chars)"),priority:d.enum(["Low","Medium","High","Urgent"]).optional().describe("Priority level (default: Medium)"),dueDate:d.string().optional().describe("Due date in ISO format (e.g., '2024-12-31')")},async({title:t,column:e,description:n,priority:r,dueDate:o})=>{if(s("create_kanban_card called",{title:t,column:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let c=await l(`/api/projects/${i}/kanban/cards`,{method:"POST",body:JSON.stringify({title:t,column:e,description:n,priority:r||"Medium",dueDate:o?new Date(o).toISOString():void 0})}),a=await c.json();return!c.ok||!a.success?(s("create_kanban_card failed:",{result:a}),{content:[{type:"text",text:`\u274C ${a.error||"Failed to create card"}`}]}):(s("create_kanban_card succeeded:",{id:a.card.id}),{content:[{type:"text",text:`\u2705 Created card "${a.card.title}" in ${a.card.columnName} (id: ${a.card.id})`}]})}catch(c){return s("create_kanban_card error:",c instanceof Error?c.message:String(c)),{content:[{type:"text",text:`\u274C Error: ${c instanceof Error?c.message:String(c)}`}]}}});p.tool("update_kanban_card",`Update an existing kanban card's properties.
101
+ Use get_kanban_board first to get the card ID.`,{cardId:d.string().describe("Card ID (GUID from get_kanban_board)"),title:d.string().optional().describe("New title"),description:d.string().optional().describe("New description"),priority:d.enum(["Low","Medium","High","Urgent"]).optional().describe("New priority"),dueDate:d.string().optional().describe("New due date in ISO format")},async({cardId:t,title:e,description:n,priority:r,dueDate:o})=>{if(s("update_kanban_card called",{cardId:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let c=await l(`/api/projects/${i}/kanban/cards/${t}`,{method:"PUT",body:JSON.stringify({title:e,description:n,priority:r,dueDate:o?new Date(o).toISOString():void 0})}),a=await c.json();return!c.ok||!a.success?(s("update_kanban_card failed:",{result:a}),{content:[{type:"text",text:`\u274C ${a.error||"Failed to update card"}`}]}):(s("update_kanban_card succeeded:",{id:a.card.id}),{content:[{type:"text",text:`\u2705 Updated card "${a.card.title}"`}]})}catch(c){return s("update_kanban_card error:",c instanceof Error?c.message:String(c)),{content:[{type:"text",text:`\u274C Error: ${c instanceof Error?c.message:String(c)}`}]}}});p.tool("move_kanban_card",`Move a card to a different column.
102
102
  Use get_kanban_board first to get the card ID.
103
- Column names (EXACT, case-sensitive): "Backlog", "Todo", "In Progress", "Done"`,{cardId:a.string().describe("Card ID (GUID from get_kanban_board)"),column:a.string().describe("Target column name"),position:a.number().optional().describe("Position in column (0 = top, omit for bottom)")},async({cardId:e,column:t,position:r})=>{if(n("move_kanban_card called",{cardId:e,column:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let s=await p(`/api/projects/${i}/kanban/cards/${e}/move`,{method:"PUT",body:JSON.stringify({column:t,position:r})}),o=await s.json();return!s.ok||!o.success?(n("move_kanban_card failed:",{result:o}),{content:[{type:"text",text:`\u274C ${o.error||"Failed to move card"}`}]}):(n("move_kanban_card succeeded:",{id:o.card.id,column:o.card.columnName}),{content:[{type:"text",text:`\u2705 Moved card "${o.card.title}" to ${o.card.columnName}`}]})}catch(s){return n("move_kanban_card error:",s instanceof Error?s.message:String(s)),{content:[{type:"text",text:`\u274C Error: ${s instanceof Error?s.message:String(s)}`}]}}});u.tool("delete_kanban_card",`Delete a kanban card.
104
- Use get_kanban_board first to get the card ID.`,{cardId:a.string().describe("Card ID (GUID from get_kanban_board)")},async({cardId:e})=>{if(n("delete_kanban_card called",{cardId:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await p(`/api/projects/${i}/kanban/cards/${e}`,{method:"DELETE"}),r=await t.json();return!t.ok||!r.success?(n("delete_kanban_card failed:",{result:r}),{content:[{type:"text",text:`\u274C ${r.error||"Failed to delete card"}`}]}):(n("delete_kanban_card succeeded:",{cardId:e}),{content:[{type:"text",text:"\u{1F5D1}\uFE0F Deleted card"}]})}catch(t){return n("delete_kanban_card error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});u.tool("create_subtask",`Add a subtask to a kanban card.
105
- Use get_kanban_board first to get the card ID.`,{cardId:a.string().describe("Parent card ID (GUID from get_kanban_board)"),title:a.string().describe("Subtask title (max 200 chars)")},async({cardId:e,title:t})=>{if(n("create_subtask called",{cardId:e,title:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let r=await p(`/api/projects/${i}/kanban/cards/${e}/subtasks`,{method:"POST",body:JSON.stringify({title:t})}),s=await r.json();return!r.ok||!s.success?(n("create_subtask failed:",{result:s}),{content:[{type:"text",text:`\u274C ${s.error||"Failed to create subtask"}`}]}):(n("create_subtask succeeded:",{id:s.subtask.id}),{content:[{type:"text",text:`\u2705 Created subtask "${s.subtask.title}" (id: ${s.subtask.id})`}]})}catch(r){return n("create_subtask error:",r instanceof Error?r.message:String(r)),{content:[{type:"text",text:`\u274C Error: ${r instanceof Error?r.message:String(r)}`}]}}});u.tool("toggle_subtask",`Toggle a subtask's completion status.
106
- Use get_kanban_board first to get the subtask ID.`,{subtaskId:a.string().describe("Subtask ID (GUID from get_kanban_board)")},async({subtaskId:e})=>{if(n("toggle_subtask called",{subtaskId:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await p(`/api/projects/${i}/kanban/subtasks/${e}/toggle`,{method:"PUT"}),r=await t.json();if(!t.ok||!r.success)return n("toggle_subtask failed:",{result:r}),{content:[{type:"text",text:`\u274C ${r.error||"Failed to toggle subtask"}`}]};let s=r.subtask.isCompleted?"completed \u2705":"uncompleted \u2B1C";return n("toggle_subtask succeeded:",{id:r.subtask.id,isCompleted:r.subtask.isCompleted}),{content:[{type:"text",text:`\u2705 Subtask "${r.subtask.title}" marked as ${s}`}]}}catch(t){return n("toggle_subtask error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});u.tool("delete_subtask",`Delete a subtask.
107
- Use get_kanban_board first to get the subtask ID.`,{subtaskId:a.string().describe("Subtask ID (GUID from get_kanban_board)")},async({subtaskId:e})=>{if(n("delete_subtask called",{subtaskId:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await p(`/api/projects/${i}/kanban/subtasks/${e}`,{method:"DELETE"}),r=await t.json();return!t.ok||!r.success?(n("delete_subtask failed:",{result:r}),{content:[{type:"text",text:`\u274C ${r.error||"Failed to delete subtask"}`}]}):(n("delete_subtask succeeded:",{subtaskId:e}),{content:[{type:"text",text:"\u{1F5D1}\uFE0F Deleted subtask"}]})}catch(t){return n("delete_subtask error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});u.tool("get_test_suites",`Get all test suites for the current project.
103
+ Column names (EXACT, case-sensitive): "Backlog", "Todo", "In Progress", "Done"`,{cardId:d.string().describe("Card ID (GUID from get_kanban_board)"),column:d.string().describe("Target column name"),position:d.number().optional().describe("Position in column (0 = top, omit for bottom)")},async({cardId:t,column:e,position:n})=>{if(s("move_kanban_card called",{cardId:t,column:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let r=await l(`/api/projects/${i}/kanban/cards/${t}/move`,{method:"PUT",body:JSON.stringify({column:e,position:n})}),o=await r.json();return!r.ok||!o.success?(s("move_kanban_card failed:",{result:o}),{content:[{type:"text",text:`\u274C ${o.error||"Failed to move card"}`}]}):(s("move_kanban_card succeeded:",{id:o.card.id,column:o.card.columnName}),{content:[{type:"text",text:`\u2705 Moved card "${o.card.title}" to ${o.card.columnName}`}]})}catch(r){return s("move_kanban_card error:",r instanceof Error?r.message:String(r)),{content:[{type:"text",text:`\u274C Error: ${r instanceof Error?r.message:String(r)}`}]}}});p.tool("delete_kanban_card",`Delete a kanban card.
104
+ Use get_kanban_board first to get the card ID.`,{cardId:d.string().describe("Card ID (GUID from get_kanban_board)")},async({cardId:t})=>{if(s("delete_kanban_card called",{cardId:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await l(`/api/projects/${i}/kanban/cards/${t}`,{method:"DELETE"}),n=await e.json();return!e.ok||!n.success?(s("delete_kanban_card failed:",{result:n}),{content:[{type:"text",text:`\u274C ${n.error||"Failed to delete card"}`}]}):(s("delete_kanban_card succeeded:",{cardId:t}),{content:[{type:"text",text:"\u{1F5D1}\uFE0F Deleted card"}]})}catch(e){return s("delete_kanban_card error:",e instanceof Error?e.message:String(e)),{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});p.tool("create_subtask",`Add a subtask to a kanban card.
105
+ Use get_kanban_board first to get the card ID.`,{cardId:d.string().describe("Parent card ID (GUID from get_kanban_board)"),title:d.string().describe("Subtask title (max 200 chars)")},async({cardId:t,title:e})=>{if(s("create_subtask called",{cardId:t,title:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let n=await l(`/api/projects/${i}/kanban/cards/${t}/subtasks`,{method:"POST",body:JSON.stringify({title:e})}),r=await n.json();return!n.ok||!r.success?(s("create_subtask failed:",{result:r}),{content:[{type:"text",text:`\u274C ${r.error||"Failed to create subtask"}`}]}):(s("create_subtask succeeded:",{id:r.subtask.id}),{content:[{type:"text",text:`\u2705 Created subtask "${r.subtask.title}" (id: ${r.subtask.id})`}]})}catch(n){return s("create_subtask error:",n instanceof Error?n.message:String(n)),{content:[{type:"text",text:`\u274C Error: ${n instanceof Error?n.message:String(n)}`}]}}});p.tool("toggle_subtask",`Toggle a subtask's completion status.
106
+ Use get_kanban_board first to get the subtask ID.`,{subtaskId:d.string().describe("Subtask ID (GUID from get_kanban_board)")},async({subtaskId:t})=>{if(s("toggle_subtask called",{subtaskId:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await l(`/api/projects/${i}/kanban/subtasks/${t}/toggle`,{method:"PUT"}),n=await e.json();if(!e.ok||!n.success)return s("toggle_subtask failed:",{result:n}),{content:[{type:"text",text:`\u274C ${n.error||"Failed to toggle subtask"}`}]};let r=n.subtask.isCompleted?"completed \u2705":"uncompleted \u2B1C";return s("toggle_subtask succeeded:",{id:n.subtask.id,isCompleted:n.subtask.isCompleted}),{content:[{type:"text",text:`\u2705 Subtask "${n.subtask.title}" marked as ${r}`}]}}catch(e){return s("toggle_subtask error:",e instanceof Error?e.message:String(e)),{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});p.tool("delete_subtask",`Delete a subtask.
107
+ Use get_kanban_board first to get the subtask ID.`,{subtaskId:d.string().describe("Subtask ID (GUID from get_kanban_board)")},async({subtaskId:t})=>{if(s("delete_subtask called",{subtaskId:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await l(`/api/projects/${i}/kanban/subtasks/${t}`,{method:"DELETE"}),n=await e.json();return!e.ok||!n.success?(s("delete_subtask failed:",{result:n}),{content:[{type:"text",text:`\u274C ${n.error||"Failed to delete subtask"}`}]}):(s("delete_subtask succeeded:",{subtaskId:t}),{content:[{type:"text",text:"\u{1F5D1}\uFE0F Deleted subtask"}]})}catch(e){return s("delete_subtask error:",e instanceof Error?e.message:String(e)),{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});p.tool("get_test_suites",`Get all test suites for the current project.
108
108
  Returns a list of test suites with their names, descriptions, and test counts.
109
- Use this to find test suites before running them.`,{},async()=>{if(n("get_test_suites called",{projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await p(`/api/projects/${i}/tests/suites`),t=await e.json();if(!e.ok||!t.success)return n("get_test_suites failed:",{result:t}),{content:[{type:"text",text:`\u274C ${t.error||"Failed to get test suites"}`}]};let r=t.suites||[];return r.length===0?{content:[{type:"text",text:"\u{1F4CB} No test suites found for this project."}]}:{content:[{type:"text",text:`\u{1F4CB} Test Suites:
109
+ Use this to find test suites before running them.`,{},async()=>{if(s("get_test_suites called",{projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await l(`/api/projects/${i}/tests/suites`),e=await t.json();if(!t.ok||!e.success)return s("get_test_suites failed:",{result:e}),{content:[{type:"text",text:`\u274C ${e.error||"Failed to get test suites"}`}]};let n=e.suites||[];return n.length===0?{content:[{type:"text",text:"\u{1F4CB} No test suites found for this project."}]}:{content:[{type:"text",text:`\u{1F4CB} Test Suites:
110
110
 
111
- ${r.map(o=>`\u2022 ${o.name} (${o.testCount} tests) - ID: ${o.id}${o.lastRunStatus?` [Last: ${o.lastRunStatus}]`:""}${o.description?`
111
+ ${n.map(o=>`\u2022 ${o.name} (${o.testCount} tests) - ID: ${o.id}${o.lastRunStatus?` [Last: ${o.lastRunStatus}]`:""}${o.description?`
112
112
  ${o.description}`:""}`).join(`
113
- `)}`}]}}catch(e){return n("get_test_suites error:",e instanceof Error?e.message:String(e)),{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});u.tool("get_test_suite_details",`Get detailed information about a test suite including all its tests.
113
+ `)}`}]}}catch(t){return s("get_test_suites error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});p.tool("get_test_suite_details",`Get detailed information about a test suite including all its tests.
114
114
  Returns the suite with all test definitions ordered by position.
115
- Use this to get the tests before running a suite.`,{suiteId:a.string().describe("Test suite ID (GUID)")},async({suiteId:e})=>{if(n("get_test_suite_details called",{suiteId:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await p(`/api/projects/${i}/tests/suites/${e}`),r=await t.json();if(!t.ok||!r.success)return n("get_test_suite_details failed:",{result:r}),{content:[{type:"text",text:`\u274C ${r.error||"Failed to get test suite"}`}]};let s=r.suite,o=s.tests||[],c=o.map((d,l)=>`${l+1}. ${d.name}${d.lastRunStatus?` [${d.lastRunStatus}]`:""}
116
- ID: ${d.id}
117
- Instructions: ${d.instructions.substring(0,200)}${d.instructions.length>200?"...":""}`).join(`
115
+ Use this to get the tests before running a suite.`,{suiteId:d.string().describe("Test suite ID (GUID)")},async({suiteId:t})=>{if(s("get_test_suite_details called",{suiteId:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await l(`/api/projects/${i}/tests/suites/${t}`),n=await e.json();if(!e.ok||!n.success)return s("get_test_suite_details failed:",{result:n}),{content:[{type:"text",text:`\u274C ${n.error||"Failed to get test suite"}`}]};let r=n.suite,o=r.tests||[],c=o.map((a,u)=>`${u+1}. ${a.name}${a.lastRunStatus?` [${a.lastRunStatus}]`:""}
116
+ ID: ${a.id}
117
+ Instructions: ${a.instructions.substring(0,200)}${a.instructions.length>200?"...":""}`).join(`
118
118
 
119
- `);return{content:[{type:"text",text:`\u{1F4CB} Test Suite: ${s.name}
120
- ${s.description?`Description: ${s.description}
119
+ `);return{content:[{type:"text",text:`\u{1F4CB} Test Suite: ${r.name}
120
+ ${r.description?`Description: ${r.description}
121
121
  `:""}
122
122
  Tests (${o.length}):
123
123
 
124
- ${c}`}]}}catch(t){return n("get_test_suite_details error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});u.tool("get_test_details",`Get detailed information about a single test.
125
- Returns the test name and full instructions for the AI to execute.`,{testId:a.string().describe("Test ID (GUID)")},async({testId:e})=>{if(n("get_test_details called",{testId:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await p(`/api/projects/${i}/tests/${e}`),r=await t.json();if(!t.ok||!r.success)return n("get_test_details failed:",{result:r}),{content:[{type:"text",text:`\u274C ${r.error||"Failed to get test"}`}]};let s=r.test;return{content:[{type:"text",text:`\u{1F4DD} Test: ${s.name}
126
- Suite: ${s.testSuiteName}
124
+ ${c}`}]}}catch(e){return s("get_test_suite_details error:",e instanceof Error?e.message:String(e)),{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});p.tool("get_test_details",`Get detailed information about a single test.
125
+ Returns the test name and full instructions for the AI to execute.`,{testId:d.string().describe("Test ID (GUID)")},async({testId:t})=>{if(s("get_test_details called",{testId:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await l(`/api/projects/${i}/tests/${t}`),n=await e.json();if(!e.ok||!n.success)return s("get_test_details failed:",{result:n}),{content:[{type:"text",text:`\u274C ${n.error||"Failed to get test"}`}]};let r=n.test;return{content:[{type:"text",text:`\u{1F4DD} Test: ${r.name}
126
+ Suite: ${r.testSuiteName}
127
127
 
128
128
  Instructions:
129
- ${s.instructions}`}]}}catch(t){return n("get_test_details error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});u.tool("run_test",`Start a test run for a single test.
129
+ ${r.instructions}`}]}}catch(e){return s("get_test_details error:",e instanceof Error?e.message:String(e)),{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});p.tool("run_test",`Start a test run for a single test.
130
130
  Creates a new test run record and returns the test details for execution.
131
131
  After calling this, execute the test using Playwright MCP tools, then call report_test_status to update the result.
132
132
 
133
133
  The test instructions contain human-readable steps that you should follow using
134
- Playwright MCP tools (browser_navigate, browser_click, browser_type, browser_snapshot, etc.).`,{testId:a.string().describe("Test ID to run (GUID)")},async({testId:e})=>{if(n("run_test called",{testId:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await p(`/api/projects/${i}/tests/runs`,{method:"POST",body:JSON.stringify({testId:e})}),r=await t.json();if(!t.ok||!r.success)return n("run_test failed:",{result:r}),{content:[{type:"text",text:`\u274C ${r.error||"Failed to start test run"}`}]};let s=r.test;return{content:[{type:"text",text:`\u{1F680} Started test run!
134
+ Playwright MCP tools (browser_navigate, browser_click, browser_type, browser_snapshot, etc.).`,{testId:d.string().describe("Test ID to run (GUID)")},async({testId:t})=>{if(s("run_test called",{testId:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await l(`/api/projects/${i}/tests/runs`,{method:"POST",body:JSON.stringify({testId:t})}),n=await e.json();if(!e.ok||!n.success)return s("run_test failed:",{result:n}),{content:[{type:"text",text:`\u274C ${n.error||"Failed to start test run"}`}]};let r=n.test;return{content:[{type:"text",text:`\u{1F680} Started test run!
135
135
 
136
- Run ID: ${r.runId}
137
- Test: ${s.name}
136
+ Run ID: ${n.runId}
137
+ Test: ${r.name}
138
138
 
139
139
  Instructions to execute:
140
- ${s.instructions}
140
+ ${r.instructions}
141
141
 
142
- \u26A0\uFE0F Remember to call report_test_status with run ID "${r.runId}" when done.`}]}}catch(t){return n("run_test error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});u.tool("run_test_suite",`Start a test run for an entire test suite.
142
+ \u26A0\uFE0F Remember to call report_test_status with run ID "${n.runId}" when done.`}]}}catch(e){return s("run_test error:",e instanceof Error?e.message:String(e)),{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});p.tool("run_test_suite",`Start a test run for an entire test suite.
143
143
  Creates a suite-level test run record and returns all tests to execute in order.
144
144
  Execute each test using Playwright MCP tools, call report_test_status for each test result,
145
- then call report_test_status with the suite summary at the end.`,{suiteId:a.string().describe("Test suite ID to run (GUID)")},async({suiteId:e})=>{if(n("run_test_suite called",{suiteId:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await p(`/api/projects/${i}/tests/runs`,{method:"POST",body:JSON.stringify({testSuiteId:e})}),r=await t.json();if(!t.ok||!r.success)return n("run_test_suite failed:",{result:r}),{content:[{type:"text",text:`\u274C ${r.error||"Failed to start test suite run"}`}]};let o=(r.tests||[]).map((c,d)=>`${d+1}. ${c.name}
145
+ then call report_test_status with the suite summary at the end.`,{suiteId:d.string().describe("Test suite ID to run (GUID)")},async({suiteId:t})=>{if(s("run_test_suite called",{suiteId:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await l(`/api/projects/${i}/tests/runs`,{method:"POST",body:JSON.stringify({testSuiteId:t})}),n=await e.json();if(!e.ok||!n.success)return s("run_test_suite failed:",{result:n}),{content:[{type:"text",text:`\u274C ${n.error||"Failed to start test suite run"}`}]};let o=(n.tests||[]).map((c,a)=>`${a+1}. ${c.name}
146
146
  ID: ${c.id}
147
147
  Instructions: ${c.instructions}`).join(`
148
148
 
149
149
  `);return{content:[{type:"text",text:`\u{1F680} Started test suite run!
150
150
 
151
- Run ID: ${r.runId}
152
- Suite: ${r.suiteName}
153
- Total Tests: ${r.totalTests}
151
+ Run ID: ${n.runId}
152
+ Suite: ${n.suiteName}
153
+ Total Tests: ${n.totalTests}
154
154
 
155
155
  Tests to execute:
156
156
 
157
157
  ${o}
158
158
 
159
- \u26A0\uFE0F Execute each test in order, then call report_test_status with run ID "${r.runId}" to update progress.`}]}}catch(t){return n("run_test_suite error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});u.tool("report_test_status",`Report the status of a test run.
159
+ \u26A0\uFE0F Execute each test in order, then call report_test_status with run ID "${n.runId}" to update progress.`}]}}catch(e){return s("run_test_suite error:",e instanceof Error?e.message:String(e)),{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});p.tool("report_test_status",`Report the status of a test run.
160
160
  Call this after executing a test to update its status.
161
161
  For suite runs, call with passed/failed counts as you complete each test.
162
162
 
@@ -164,15 +164,15 @@ Status values:
164
164
  - "running": Test is in progress
165
165
  - "passed": Test completed successfully
166
166
  - "failed": Test failed
167
- - "cancelled": Test was cancelled`,{runId:a.string().describe("Test run ID (GUID from run_test or run_test_suite)"),status:a.enum(["running","passed","failed","cancelled"]).describe("New status"),resultSummary:a.string().optional().describe("Summary of test results (what was verified, any issues)"),errorMessage:a.string().optional().describe("Short error message if test failed"),errorDetails:a.string().optional().describe("Full error details/stack trace if failed"),passedTests:a.number().optional().describe("For suite runs: count of passed tests so far"),failedTests:a.number().optional().describe("For suite runs: count of failed tests so far")},async({runId:e,status:t,resultSummary:r,errorMessage:s,errorDetails:o,passedTests:c,failedTests:d})=>{if(n("report_test_status called",{runId:e,status:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let l=await p(`/api/projects/${i}/tests/runs/${e}`,{method:"PUT",body:JSON.stringify({status:t,resultSummary:r,errorMessage:s,errorDetails:o,passedTests:c,failedTests:d})}),g=await l.json();return!l.ok||!g.success?(n("report_test_status failed:",{result:g}),{content:[{type:"text",text:`\u274C ${g.error||"Failed to update test status"}`}]}):{content:[{type:"text",text:`${t==="passed"?"\u2705":t==="failed"?"\u274C":t==="cancelled"?"\u{1F6AB}":"\u{1F504}"} Test run updated: ${t.toUpperCase()}${g.endedAt?`
168
- Completed at: ${g.endedAt}`:""}`}]}}catch(l){return n("report_test_status error:",l instanceof Error?l.message:String(l)),{content:[{type:"text",text:`\u274C Error: ${l instanceof Error?l.message:String(l)}`}]}}});u.tool("create_test_suite",`Create a new test suite for organizing tests.
167
+ - "cancelled": Test was cancelled`,{runId:d.string().describe("Test run ID (GUID from run_test or run_test_suite)"),status:d.enum(["running","passed","failed","cancelled"]).describe("New status"),resultSummary:d.string().optional().describe("Summary of test results (what was verified, any issues)"),errorMessage:d.string().optional().describe("Short error message if test failed"),errorDetails:d.string().optional().describe("Full error details/stack trace if failed"),passedTests:d.number().optional().describe("For suite runs: count of passed tests so far"),failedTests:d.number().optional().describe("For suite runs: count of failed tests so far")},async({runId:t,status:e,resultSummary:n,errorMessage:r,errorDetails:o,passedTests:c,failedTests:a})=>{if(s("report_test_status called",{runId:t,status:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let u=await l(`/api/projects/${i}/tests/runs/${t}`,{method:"PUT",body:JSON.stringify({status:e,resultSummary:n,errorMessage:r,errorDetails:o,passedTests:c,failedTests:a})}),f=await u.json();return!u.ok||!f.success?(s("report_test_status failed:",{result:f}),{content:[{type:"text",text:`\u274C ${f.error||"Failed to update test status"}`}]}):{content:[{type:"text",text:`${e==="passed"?"\u2705":e==="failed"?"\u274C":e==="cancelled"?"\u{1F6AB}":"\u{1F504}"} Test run updated: ${e.toUpperCase()}${f.endedAt?`
168
+ Completed at: ${f.endedAt}`:""}`}]}}catch(u){return s("report_test_status error:",u instanceof Error?u.message:String(u)),{content:[{type:"text",text:`\u274C Error: ${u instanceof Error?u.message:String(u)}`}]}}});p.tool("create_test_suite",`Create a new test suite for organizing tests.
169
169
  A test suite is a logical grouping of related tests (e.g., "Login Tests", "Checkout Flow").
170
170
 
171
- Use this to create a container for related tests before adding individual tests to it.`,{name:a.string().describe("Name of the test suite (e.g., 'Login Tests', 'Checkout Flow')"),description:a.string().optional().describe("Optional description of what this suite tests")},async({name:e,description:t})=>{if(n("create_test_suite called",{name:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let r=await p(`/api/projects/${i}/tests/suites`,{method:"POST",body:JSON.stringify({name:e,description:t})}),s=await r.json();return!r.ok||!s.success?(n("create_test_suite failed:",{result:s}),{content:[{type:"text",text:`\u274C ${s.error||"Failed to create test suite"}`}]}):{content:[{type:"text",text:`\u2705 Created test suite "${s.name}"
171
+ Use this to create a container for related tests before adding individual tests to it.`,{name:d.string().describe("Name of the test suite (e.g., 'Login Tests', 'Checkout Flow')"),description:d.string().optional().describe("Optional description of what this suite tests")},async({name:t,description:e})=>{if(s("create_test_suite called",{name:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let n=await l(`/api/projects/${i}/tests/suites`,{method:"POST",body:JSON.stringify({name:t,description:e})}),r=await n.json();return!n.ok||!r.success?(s("create_test_suite failed:",{result:r}),{content:[{type:"text",text:`\u274C ${r.error||"Failed to create test suite"}`}]}):{content:[{type:"text",text:`\u2705 Created test suite "${r.name}"
172
172
 
173
- Suite ID: ${s.suiteId}
173
+ Suite ID: ${r.suiteId}
174
174
 
175
- You can now add tests to this suite using create_test.`}]}}catch(r){return n("create_test_suite error:",r instanceof Error?r.message:String(r)),{content:[{type:"text",text:`\u274C Error: ${r instanceof Error?r.message:String(r)}`}]}}});u.tool("create_test",`Create a new test within a test suite.
175
+ You can now add tests to this suite using create_test.`}]}}catch(n){return s("create_test_suite error:",n instanceof Error?n.message:String(n)),{content:[{type:"text",text:`\u274C Error: ${n instanceof Error?n.message:String(n)}`}]}}});p.tool("create_test",`Create a new test within a test suite.
176
176
  The test should have clear, step-by-step instructions that can be executed using Playwright.
177
177
 
178
178
  Write instructions that are:
@@ -186,19 +186,19 @@ Example instructions:
186
186
  3. Enter "password123" in the password field
187
187
  4. Click the "Sign In" button
188
188
  5. Verify the URL changes to /dashboard
189
- 6. Verify a welcome message is displayed`,{suiteId:a.string().describe("Test suite ID to add the test to (GUID)"),name:a.string().describe("Name of the test (e.g., 'Login with valid credentials')"),instructions:a.string().describe("Step-by-step instructions for the AI to execute this test using Playwright")},async({suiteId:e,name:t,instructions:r})=>{if(n("create_test called",{suiteId:e,name:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let s=await p(`/api/projects/${i}/tests/suites/${e}/tests`,{method:"POST",body:JSON.stringify({name:t,instructions:r})}),o=await s.json();return!s.ok||!o.success?(n("create_test failed:",{result:o}),{content:[{type:"text",text:`\u274C ${o.error||"Failed to create test"}`}]}):{content:[{type:"text",text:`\u2705 Created test "${o.name}" in suite "${o.suiteName}"
189
+ 6. Verify a welcome message is displayed`,{suiteId:d.string().describe("Test suite ID to add the test to (GUID)"),name:d.string().describe("Name of the test (e.g., 'Login with valid credentials')"),instructions:d.string().describe("Step-by-step instructions for the AI to execute this test using Playwright")},async({suiteId:t,name:e,instructions:n})=>{if(s("create_test called",{suiteId:t,name:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let r=await l(`/api/projects/${i}/tests/suites/${t}/tests`,{method:"POST",body:JSON.stringify({name:e,instructions:n})}),o=await r.json();return!r.ok||!o.success?(s("create_test failed:",{result:o}),{content:[{type:"text",text:`\u274C ${o.error||"Failed to create test"}`}]}):{content:[{type:"text",text:`\u2705 Created test "${o.name}" in suite "${o.suiteName}"
190
190
 
191
191
  Test ID: ${o.testId}
192
192
 
193
- You can run this test using run_test with the test ID.`}]}}catch(s){return n("create_test error:",s instanceof Error?s.message:String(s)),{content:[{type:"text",text:`\u274C Error: ${s instanceof Error?s.message:String(s)}`}]}}});u.tool("update_test",`Update an existing test's name or instructions.
193
+ You can run this test using run_test with the test ID.`}]}}catch(r){return s("create_test error:",r instanceof Error?r.message:String(r)),{content:[{type:"text",text:`\u274C Error: ${r instanceof Error?r.message:String(r)}`}]}}});p.tool("update_test",`Update an existing test's name or instructions.
194
194
  Use this to fix test instructions that are outdated or incorrect.
195
195
 
196
196
  Common reasons to update:
197
197
  - Selector changed (element moved or renamed)
198
198
  - Flow changed (new steps added, steps removed)
199
- - Clarify ambiguous instructions`,{testId:a.string().describe("Test ID to update (GUID)"),name:a.string().optional().describe("New name for the test (optional)"),instructions:a.string().optional().describe("New instructions for the test (optional)")},async({testId:e,name:t,instructions:r})=>{if(n("update_test called",{testId:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};if(!t&&!r)return{content:[{type:"text",text:"\u274C Provide at least name or instructions to update"}]};try{let s=await p(`/api/projects/${i}/tests/${e}`,{method:"PUT",body:JSON.stringify({name:t,instructions:r})}),o=await s.json();return!s.ok||!o.success?(n("update_test failed:",{result:o}),{content:[{type:"text",text:`\u274C ${o.error||"Failed to update test"}`}]}):{content:[{type:"text",text:`\u2705 Updated test "${o.name}"
199
+ - Clarify ambiguous instructions`,{testId:d.string().describe("Test ID to update (GUID)"),name:d.string().optional().describe("New name for the test (optional)"),instructions:d.string().optional().describe("New instructions for the test (optional)")},async({testId:t,name:e,instructions:n})=>{if(s("update_test called",{testId:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};if(!e&&!n)return{content:[{type:"text",text:"\u274C Provide at least name or instructions to update"}]};try{let r=await l(`/api/projects/${i}/tests/${t}`,{method:"PUT",body:JSON.stringify({name:e,instructions:n})}),o=await r.json();return!r.ok||!o.success?(s("update_test failed:",{result:o}),{content:[{type:"text",text:`\u274C ${o.error||"Failed to update test"}`}]}):{content:[{type:"text",text:`\u2705 Updated test "${o.name}"
200
200
 
201
- Test ID: ${o.testId}`}]}}catch(s){return n("update_test error:",s instanceof Error?s.message:String(s)),{content:[{type:"text",text:`\u274C Error: ${s instanceof Error?s.message:String(s)}`}]}}});u.tool("create_command",`Create a reusable command/prompt that can be saved to the user's command library.
201
+ Test ID: ${o.testId}`}]}}catch(r){return s("update_test error:",r instanceof Error?r.message:String(r)),{content:[{type:"text",text:`\u274C Error: ${r instanceof Error?r.message:String(r)}`}]}}});p.tool("create_command",`Create a reusable command/prompt that can be saved to the user's command library.
202
202
  Commands are reusable prompts or instructions that users can quickly insert into chat.
203
203
 
204
204
  Use this when the user asks you to:
@@ -206,13 +206,13 @@ Use this when the user asks you to:
206
206
  - Save a prompt as a command
207
207
  - Add a reusable instruction to their library
208
208
 
209
- The command will be saved to the user's personal command library.`,{name:a.string().describe("Command name (short, descriptive)"),content:a.string().describe("The actual prompt/instruction content"),description:a.string().optional().describe("Brief description of what the command does"),category:a.string().optional().describe("Category (e.g., 'Code Review', 'Refactoring', 'Testing')"),tags:a.string().optional().describe("Comma-separated tags for searchability")},async({name:e,content:t,description:r,category:s,tags:o})=>{n("create_command called",{name:e,projectId:i||"(not set)"});try{let c=await p("/api/commands",{method:"POST",body:JSON.stringify({name:e,content:t,description:r,category:s,tags:o})});if(!c.ok){let l=await c.text();return n("create_command failed:",{status:c.status,error:l}),{content:[{type:"text",text:`\u274C Failed to create command: ${l}`}]}}let d=await c.json();return n("create_command succeeded:",{id:d.id,name:d.name}),{content:[{type:"text",text:`\u2705 Created command "${d.name}" (id: ${d.id})
209
+ The command will be saved to the user's personal command library.`,{name:d.string().describe("Command name (short, descriptive)"),content:d.string().describe("The actual prompt/instruction content"),description:d.string().optional().describe("Brief description of what the command does"),category:d.string().optional().describe("Category (e.g., 'Code Review', 'Refactoring', 'Testing')"),tags:d.string().optional().describe("Comma-separated tags for searchability")},async({name:t,content:e,description:n,category:r,tags:o})=>{s("create_command called",{name:t,projectId:i||"(not set)"});try{let c=await l("/api/commands",{method:"POST",body:JSON.stringify({name:t,content:e,description:n,category:r,tags:o})});if(!c.ok){let u=await c.text();return s("create_command failed:",{status:c.status,error:u}),{content:[{type:"text",text:`\u274C Failed to create command: ${u}`}]}}let a=await c.json();return s("create_command succeeded:",{id:a.id,name:a.name}),{content:[{type:"text",text:`\u2705 Created command "${a.name}" (id: ${a.id})
210
210
 
211
- You can find it in the Commands section or use "/" in chat to access it.`}]}}catch(c){return n("create_command error:",c instanceof Error?c.message:String(c)),{content:[{type:"text",text:`\u274C Error: ${c instanceof Error?c.message:String(c)}`}]}}});u.tool("get_project_prompt",`Get the current project prompt/description that defines this project's context.
211
+ You can find it in the Commands section or use "/" in chat to access it.`}]}}catch(c){return s("create_command error:",c instanceof Error?c.message:String(c)),{content:[{type:"text",text:`\u274C Error: ${c instanceof Error?c.message:String(c)}`}]}}});p.tool("get_project_prompt",`Get the current project prompt/description that defines this project's context.
212
212
  Returns the project-specific prompt that gets injected into your system prompt.
213
- This describes what the project is about, its tech stack, patterns, and key rules.`,{},async()=>{if(n("get_project_prompt called",{projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await p(`/api/projects/${i}/prompt`,{method:"GET"}),t=await e.json();return e.ok?(n("get_project_prompt succeeded:",{hasPrompt:!!t.prompt}),t.prompt?{content:[{type:"text",text:`\u{1F4CB} **Project Prompt:**
213
+ This describes what the project is about, its tech stack, patterns, and key rules.`,{},async()=>{if(s("get_project_prompt called",{projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await l(`/api/projects/${i}/prompt`,{method:"GET"}),e=await t.json();return t.ok?(s("get_project_prompt succeeded:",{hasPrompt:!!e.prompt}),e.prompt?{content:[{type:"text",text:`\u{1F4CB} **Project Prompt:**
214
214
 
215
- ${m(t.prompt)}`}]}:{content:[{type:"text",text:"\u{1F4DD} No project prompt set. Use update_project_prompt to set one."}]}):(n("get_project_prompt failed:",{result:t}),{content:[{type:"text",text:`\u274C ${t.error||"Failed to get project prompt"}`}]})}catch(e){return n("get_project_prompt error:",e instanceof Error?e.message:String(e)),{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});u.tool("update_project_prompt",`Update the project prompt/description. Use this to define or refine the project context.
215
+ ${h(e.prompt)}`}]}:{content:[{type:"text",text:"\u{1F4DD} No project prompt set. Use update_project_prompt to set one."}]}):(s("get_project_prompt failed:",{result:e}),{content:[{type:"text",text:`\u274C ${e.error||"Failed to get project prompt"}`}]})}catch(t){return s("get_project_prompt error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});p.tool("update_project_prompt",`Update the project prompt/description. Use this to define or refine the project context.
216
216
  The prompt should describe:
217
217
  - What the project is about (overview)
218
218
  - Technology stack
@@ -221,4 +221,52 @@ The prompt should describe:
221
221
  - Critical rules and gotchas
222
222
 
223
223
  This prompt will be injected into your system prompt for all future conversations.
224
- Changes are synced in real-time via SignalR.`,{prompt:a.string().describe("The new project prompt content. Use XML-style sections for organization.")},async({prompt:e})=>{if(n("update_project_prompt called",{projectId:i||"(not set)",promptLength:e.length}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await p(`/api/projects/${i}/prompt`,{method:"PUT",body:JSON.stringify({prompt:e})}),r=await t.json();return t.ok?(n("update_project_prompt succeeded:",{updatedAt:r.updatedAt}),{content:[{type:"text",text:`\u2705 Project prompt updated (${e.length} chars). It will be used in your next conversation.`}]}):(n("update_project_prompt failed:",{result:r}),{content:[{type:"text",text:`\u274C ${r.error||"Failed to update project prompt"}`}]})}catch(t){return n("update_project_prompt error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});async function T(){try{n("Initializing transport...");let e=new I;n("Connecting to transport..."),await u.connect(e),n("Started successfully")}catch(e){n("Fatal error during startup:",e instanceof Error?e.message:String(e),e),process.exit(1)}}T().catch(e=>{n("Unhandled error:",e instanceof Error?e.message:String(e),e),process.exit(1)});
224
+ Changes are synced in real-time via SignalR.`,{prompt:d.string().describe("The new project prompt content. Use XML-style sections for organization.")},async({prompt:t})=>{if(s("update_project_prompt called",{projectId:i||"(not set)",promptLength:t.length}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let e=await l(`/api/projects/${i}/prompt`,{method:"PUT",body:JSON.stringify({prompt:t})}),n=await e.json();return e.ok?(s("update_project_prompt succeeded:",{updatedAt:n.updatedAt}),{content:[{type:"text",text:`\u2705 Project prompt updated (${t.length} chars). It will be used in your next conversation.`}]}):(s("update_project_prompt failed:",{result:n}),{content:[{type:"text",text:`\u274C ${n.error||"Failed to update project prompt"}`}]})}catch(e){return s("update_project_prompt error:",e instanceof Error?e.message:String(e)),{content:[{type:"text",text:`\u274C Error: ${e instanceof Error?e.message:String(e)}`}]}}});p.tool("reference_projects_list",`List all imported reference projects for the current project.
225
+ Reference projects are customer prototypes (Lovable, Bolt, etc.) imported via zip upload.
226
+ Use this to discover available reference projects before exploring their contents.`,{},async()=>{if(s("reference_projects_list called",{projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let t=await l(`/api/projects/${i}/reference-projects`);if(!t.ok){let r=await t.text();return s("reference_projects_list failed:",{status:t.status,error:r}),{content:[{type:"text",text:`\u274C Failed to list reference projects: ${r}`}]}}let e=await t.json();if(!e||e.length===0)return{content:[{type:"text",text:"\u{1F4C1} No reference projects found. Upload a zip via the UI first."}]};let n=`\u{1F4C1} **Reference Projects:**
227
+
228
+ `;for(let r of e)n+=`\u2022 **${h(r.name)}**
229
+ `,n+=` ID: ${r.id}
230
+ `,n+=` Status: ${r.status}
231
+ `,n+=` Files: ${r.fileCount}
232
+ `,r.techStack&&(n+=` Tech: ${r.techStack}
233
+ `),r.specificationSlug&&(n+=` Spec: ${r.specificationSlug}
234
+ `),r.notes&&(n+=` Notes: ${h(r.notes).substring(0,100)}${r.notes.length>100?"...":""}
235
+ `),n+=`
236
+ `;return n+="Use `reference_project_tree` with an ID to explore file structure.",s("reference_projects_list succeeded",{count:e.length}),{content:[{type:"text",text:n}]}}catch(t){return s("reference_projects_list error:",t instanceof Error?t.message:String(t)),{content:[{type:"text",text:`\u274C Error: ${t instanceof Error?t.message:String(t)}`}]}}});p.tool("reference_project_tree",`Get the file tree structure of a reference project.
237
+ Returns a hierarchical view of all files and directories.
238
+ Use this to understand the project structure before reading specific files.`,{referenceProjectId:d.string().describe("Reference project ID (GUID)")},async({referenceProjectId:t})=>{if(s("reference_project_tree called",{referenceProjectId:t,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let c=function(f,m=""){let x="",y=f.type==="directory"?"\u{1F4C1}":"\u{1F4C4}",S=f.size?` (${a(f.size)})`:"";if(x+=`${m}${y} ${f.name}${S}
239
+ `,f.children&&f.children.length>0)for(let w of f.children)x+=c(w,m+" ");return x},a=function(f){return f<1024?`${f}B`:f<1024*1024?`${(f/1024).toFixed(1)}KB`:`${(f/1024/1024).toFixed(1)}MB`};var e=c,n=a;let r=await l(`/api/projects/${i}/reference-projects/${t}/tree`);if(!r.ok){if(r.status===404)return{content:[{type:"text",text:"\u274C Reference project not found"}]};let f=await r.text();return s("reference_project_tree failed:",{status:r.status,error:f}),{content:[{type:"text",text:`\u274C Failed to get file tree: ${f}`}]}}let o=await r.json(),u=c(o.root);return s("reference_project_tree succeeded",{referenceProjectId:t}),{content:[{type:"text",text:`\u{1F4C1} **File Tree:**
240
+
241
+ ${u}
242
+ Use \`reference_project_read_file\` with a path to read file contents.`}]}}catch(r){return s("reference_project_tree error:",r instanceof Error?r.message:String(r)),{content:[{type:"text",text:`\u274C Error: ${r instanceof Error?r.message:String(r)}`}]}}});p.tool("reference_project_read_file",`Read the content of a specific file from a reference project.
243
+ Use reference_project_tree first to find the file path.
244
+ Binary files will show a placeholder instead of content.
245
+ Maximum file size: 1MB.`,{referenceProjectId:d.string().describe("Reference project ID (GUID)"),path:d.string().describe("Relative file path within the project (e.g., 'src/App.tsx', 'package.json')")},async({referenceProjectId:t,path:e})=>{if(s("reference_project_read_file called",{referenceProjectId:t,filePath:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};try{let c=function(a){return a<1024?`${a}B`:a<1024*1024?`${(a/1024).toFixed(1)}KB`:`${(a/1024/1024).toFixed(1)}MB`};var n=c;let r=await l(`/api/projects/${i}/reference-projects/${t}/file?path=${encodeURIComponent(e)}`);if(!r.ok){if(r.status===404)return{content:[{type:"text",text:`\u274C File not found: ${e}`}]};if(r.status===400)return{content:[{type:"text",text:`\u274C ${(await r.json()).error||"Invalid file path"}`}]};let a=await r.text();return s("reference_project_read_file failed:",{status:r.status,error:a}),{content:[{type:"text",text:`\u274C Failed to read file: ${a}`}]}}let o=await r.json();return o.isBinary?{content:[{type:"text",text:`\u{1F4C4} **${e}** (binary file, ${c(o.size)})
246
+
247
+ [Binary content not displayed]`}]}:(s("reference_project_read_file succeeded",{referenceProjectId:t,filePath:e}),{content:[{type:"text",text:`\u{1F4C4} **${e}** (${c(o.size)})
248
+
249
+ \`\`\`
250
+ ${h(o.content)}
251
+ \`\`\``}]})}catch(r){return s("reference_project_read_file error:",r instanceof Error?r.message:String(r)),{content:[{type:"text",text:`\u274C Error: ${r instanceof Error?r.message:String(r)}`}]}}});p.tool("reference_project_update_status",`Update a reference project's status and/or specification slug.
252
+ Use this after completing analysis to mark progress.
253
+
254
+ Status values:
255
+ - Imported: Initial state after upload
256
+ - Analyzing: Analysis in progress
257
+ - Analyzed: Analysis complete, spec saved
258
+ - Migrating: Migration in progress
259
+ - Done: All work complete
260
+
261
+ The specificationSlug links to a saved specification (use save_specification first).`,{referenceProjectId:d.string().describe("Reference project ID (GUID)"),status:d.enum(["Imported","Analyzing","Analyzed","Migrating","Done"]).optional().describe("New status"),specificationSlug:d.string().optional().describe("Slug of the linked specification (e.g., 'ref-lovable-prototype')"),notes:d.string().optional().describe("Optional notes about the project")},async({referenceProjectId:t,status:e,specificationSlug:n,notes:r})=>{if(s("reference_project_update_status called",{referenceProjectId:t,status:e,specificationSlug:n,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};if(!e&&!n&&r===void 0)return{content:[{type:"text",text:"\u274C Provide at least status, specificationSlug, or notes to update"}]};try{let o={};e&&(o.status=e),n&&(o.specificationSlug=n),r!==void 0&&(o.notes=r);let c=await l(`/api/projects/${i}/reference-projects/${t}`,{method:"PATCH",body:JSON.stringify(o)});if(!c.ok){if(c.status===404)return{content:[{type:"text",text:"\u274C Reference project not found"}]};let u=await c.text();return s("reference_project_update_status failed:",{status:c.status,error:u}),{content:[{type:"text",text:`\u274C Failed to update: ${u}`}]}}let a=await c.json();return s("reference_project_update_status succeeded",{referenceProjectId:t,status:a.status}),{content:[{type:"text",text:`\u2705 Updated reference project "${a.name}"
262
+
263
+ Status: ${a.status}${a.specificationSlug?`
264
+ Spec: ${a.specificationSlug}`:""}`}]}}catch(o){return s("reference_project_update_status error:",o instanceof Error?o.message:String(o)),{content:[{type:"text",text:`\u274C Error: ${o instanceof Error?o.message:String(o)}`}]}}});p.tool("reference_project_download",`Download a reference project to the local filesystem for exploration.
265
+ This downloads and extracts the project files to a local directory.
266
+ After downloading, you can use Read, Glob, Grep tools to explore the codebase freely.
267
+
268
+ Returns the local path where files are extracted.`,{referenceProjectId:d.string().describe("Reference project ID (GUID)"),targetDir:d.string().optional().describe("Optional target directory (defaults to ./ref-projects/{id})")},async({referenceProjectId:t,targetDir:e})=>{if(s("reference_project_download called",{referenceProjectId:t,targetDir:e,projectId:i||"(not set)"}),!i)return{content:[{type:"text",text:"\u274C PROJECT_ID not set"}]};if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(t))return{content:[{type:"text",text:"\u274C Invalid reference project ID format (expected GUID)"}]};try{let r;if(e){if(r=_.resolve(e),r.includes(".."))return{content:[{type:"text",text:"\u274C Invalid target directory (path traversal not allowed)"}]}}else r=_.join(T,"ref-projects",t);g.mkdirSync(r,{recursive:!0});let o=`${E}/api/projects/${i}/reference-projects/${t}/download`,c={};b&&(c["X-Project-Key"]=b),s("reference_project_download fetching",{url:o});let a=await fetch(o,{headers:c});if(!a.ok){if(a.status===404)return{content:[{type:"text",text:"\u274C Reference project not found"}]};let y=await a.text();return s("reference_project_download failed:",{status:a.status,error:y}),{content:[{type:"text",text:`\u274C Failed to download: ${y}`}]}}let u=_.join("/tmp",`ref-project-${t}.zip`),f=await a.arrayBuffer();g.writeFileSync(u,Buffer.from(f)),s("reference_project_download extracting",{zipPath:u,extractPath:r});try{D("unzip",["-o","-q",u,"-d",r],{stdio:"pipe"})}catch(y){try{D("ditto",["-x","-k",u,r],{stdio:"pipe"})}catch{return s("reference_project_download extract failed:",y),{content:[{type:"text",text:"\u274C Failed to extract zip. Make sure 'unzip' is installed."}]}}}try{g.unlinkSync(u)}catch{}let m=0,x=y=>{let S=g.readdirSync(y,{withFileTypes:!0});for(let w of S)w.isDirectory()?x(_.join(y,w.name)):m++};return x(r),s("reference_project_download succeeded",{extractPath:r,fileCount:m}),{content:[{type:"text",text:`\u2705 Downloaded reference project to: ${r}
269
+
270
+ ${m} files extracted.
271
+
272
+ You can now use Read, Glob, Grep to explore the codebase freely.`}]}}catch(r){return s("reference_project_download error:",r instanceof Error?r.message:String(r)),{content:[{type:"text",text:`\u274C Error: ${r instanceof Error?r.message:String(r)}`}]}}});async function P(){try{s("Initializing transport...");let t=new O;s("Connecting to transport..."),await p.connect(t),s("Started successfully")}catch(t){s("Fatal error during startup:",t instanceof Error?t.message:String(t),t),process.exit(1)}}P().catch(t=>{s("Unhandled error:",t instanceof Error?t.message:String(t),t),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glenn-code",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Glenn Code - Connect your local development environment to DNM Lab",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",