import{existsSync as u,readFileSync as y,readdirSync as N,mkdirSync as J,writeFileSync as m,statSync as _}from"fs";import{join as l,resolve as j,relative as C,dirname as x}from"path";import{fileURLToPath as R}from"url";import{createRequire as P}from"module";var F=P(import.meta.url),T=`## Workflow Builder

You can help users build custom AI workflows using the Zibby workflow framework.

### What makes Zibby workflows different
Each node invokes a **real AI agent** (Cursor, Claude, Codex, or Gemini) \u2014 not a thin LLM API wrapper.
That means every node has full agent capabilities: tool use, MCP servers (browser, GitHub, Jira, Slack),
multi-turn reasoning, and structured output validation via Zod schemas.

Key differentiators:
- **Agent-powered nodes** \u2014 each step runs a full AI agent (cursor-agent, claude, codex, gemini CLI) with tool access and MCP skills, not a simple chat completion call.
- **Structured output** \u2014 every node declares a Zod schema; the framework validates and parses the agent's response automatically.
- **Conditional routing** \u2014 edges can branch on agent-produced fields (e.g., \`state.triage.priority === 'critical'\`), enabling intelligent decision graphs.
- **MCP skill injection** \u2014 nodes declare \`skills: [SKILLS.BROWSER, SKILLS.GITHUB]\` and the framework spins up the right MCP servers automatically.
- **Deploy anywhere** \u2014 \`zibby deploy\` pushes to Zibby Cloud with an API trigger; or self-host with \`zibby start\`.
- **State accumulation** \u2014 each node's validated output is stored under its name in \`state\` (e.g., \`state.classify_ticket\`), so downstream nodes can reference upstream results.

### What is a workflow?
A directed graph of nodes (AI agent steps) connected by edges. Each node has:
- \`name\` \u2014 unique identifier (snake_case)
- \`prompt\` \u2014 function that receives state and returns the prompt string sent to the agent
- \`outputSchema\` \u2014 Zod schema defining the structured output the agent must return
- \`skills\` (optional) \u2014 array of MCP skill IDs the node needs (e.g., \`SKILLS.BROWSER\`, \`SKILLS.GITHUB\`)
- \`timeout\` (optional) \u2014 max execution time in ms (default: 300000)
- \`model\` (optional) \u2014 override the model for this node (e.g., \`'claude-opus-4'\`)

### File structure
\`\`\`
.zibby/workflows/<name>/
\u251C\u2500\u2500 graph.mjs          \u2014 WorkflowAgent subclass with buildGraph()
\u251C\u2500\u2500 nodes/
\u2502   \u251C\u2500\u2500 index.mjs      \u2014 barrel export for all nodes
\u2502   \u2514\u2500\u2500 <node>.mjs     \u2014 one file per node
\u2514\u2500\u2500 workflow.json       \u2014 manifest (name, description, triggers)
\`\`\`

### Node pattern
\`\`\`javascript
import { z, SKILLS } from '@zibby/core';

const OutputSchema = z.object({
  summary: z.string().describe('Brief summary'),
  items: z.array(z.string()).describe('List of extracted items'),
  needsReview: z.boolean().describe('Whether a human should review this'),
});

export const myNode = {
  name: 'my_node',
  skills: [SKILLS.GITHUB],  // optional \u2014 framework injects MCP servers
  timeout: 120000,           // optional \u2014 2 min timeout
  prompt: (state) => \\\`You are analyzing a pull request.

Input:
\\\${JSON.stringify(state.input || {}, null, 2)}

Return a JSON object matching the schema.\\\`,
  outputSchema: OutputSchema,
};
\`\`\`

### Graph pattern
\`\`\`javascript
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
import { classifyNode, routeNode } from './nodes/index.mjs';

export class MyWorkflow extends WorkflowAgent {
  buildGraph() {
    const graph = new WorkflowGraph();
    graph.addNode('classify', classifyNode);
    graph.addNode('route', routeNode);
    graph.setEntryPoint('classify');
    graph.addEdge('classify', 'route');
    graph.addEdge('route', 'END');
    return graph;
  }

  async onComplete(result) {
    // Post-execution hook \u2014 save artifacts, notify, etc.
    console.log('Workflow complete:', result.success);
  }
}
\`\`\`

Conditional edges: \`graph.addConditionalEdges('node', (state) => state.node.priority === 'high' ? 'escalate' : 'notify')\`

### Available SKILLS constants
Import from \`@zibby/core\`: \`SKILLS.BROWSER\`, \`SKILLS.MEMORY\`, \`SKILLS.GITHUB\`, \`SKILLS.JIRA\`, \`SKILLS.SLACK\`, \`SKILLS.RUNNER\`

### Deep documentation
Call \`explore_framework_docs\` to read detailed framework docs on demand. Use it for:
- Advanced patterns (middleware, parallel nodes, state schemas)
- Deployment & cloud triggers
- CLI commands reference
- Integration details (Jira, GitHub, etc.)
Call with no arguments to see all available topics.

### How to use the builder tools
1. For complex workflows, call \`explore_framework_docs("custom-workflows")\` first to learn advanced patterns.
2. Ask the user what their workflow should do, what input it receives, and what steps are needed.
3. Call \`design_workflow\` with the structured spec for the user to review.
4. Once approved, call \`build_workflow\` to generate real code on disk (uses the configured agent for high-quality code generation).
5. Remind the user: \`zibby start <name>\` to test locally, \`zibby deploy <name> --project <id>\` to deploy to cloud, \`zibby logs --workflow <name>\` to tail logs.

### Important
- Each node prompt should be detailed and specific \u2014 tell the AI agent exactly what to do and what format to return.
- Zod schemas MUST use .describe() on every field so the agent knows what each field means.
- Node names must be snake_case (e.g., classify_ticket, generate_report).
- Workflow names must be kebab-case (e.g., ticket-triage, pr-review).
- State flows through: each node's validated output is stored under its name in state (e.g., state.classify_ticket).
- Downstream nodes reference upstream outputs in their prompt function (e.g., \\\`\\\${JSON.stringify(state.classify_ticket, null, 2)}\\\`).
- Nodes can declare skills to get MCP tool access \u2014 the framework handles server lifecycle automatically.`,O=/^[a-z][a-z0-9-]{0,62}[a-z0-9]$/;function W(r){return`${r.split("-").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join("")}Workflow`}function h(r){return`${r.replace(/_([a-z])/g,(e,n)=>n.toUpperCase())}Node`}function K(r){let e=r?.agent;return e?e.provider?e.provider:e.gemini?"gemini":e.codex?"codex":e.claude?"claude":e.cursor?"cursor":process.env.AGENT_TYPE||"cursor":process.env.AGENT_TYPE||"cursor"}async function G(r){let e=j(r,".zibby.config.mjs");if(!u(e))return{};try{return(await import(e)).default||{}}catch{return{}}}function M(){try{let r=x(F.resolve("@zibby/core/package.json")),e=l(r,"templates","browser-test-automation"),n=y(l(e,"nodes","preflight.mjs"),"utf-8"),i=y(l(e,"graph.mjs"),"utf-8");return{preflight:n,graph:i}}catch{return null}}var v=x(R(import.meta.url));function z(){let r=j(v,"..","..","..","docsite","docs");if(u(r))return r;let e=j(v,"..","docs");return u(e)?e:null}function I(){let r=z();if(!r)return[];try{let e=(n,i="")=>{let o=[];for(let s of N(n)){let t=l(n,s);try{if(_(t).isDirectory())o=o.concat(e(t,`${i}${s}/`));else if(s.endsWith(".md")){let d=`${i}${s.replace(/\.md$/,"")}`;o.push(d)}}catch{}}return o};return e(r)}catch{return[]}}function A(r){let e=z();if(!e)return null;let n=l(e,`${r}.md`);if(!u(n))return null;try{return y(n,"utf-8")}catch{return null}}function U(r){let e=r.nodes.map(t=>{let d=t.inputFields?.length?`Input fields: ${t.inputFields.join(", ")}`:"Input: receives full state",a=t.outputFields?.length?`Output fields: ${t.outputFields.join(", ")}`:"Output: determined by task",p=t.skills?.length?`Skills: ${t.skills.join(", ")}`:"";return`- ${t.name}: ${t.description}. ${d}. ${a}.${p?` ${p}`:""}`}).join(`
`),n=r.edges.map(t=>t.condition?`- ${t.from} \u2192 ${t.to} (conditional: ${t.condition})`:`- ${t.from} \u2192 ${t.to}`).join(`
`),i=M(),o=A("custom-workflows"),s="";return i&&(s+=`
## Real working examples from the Zibby framework

### Example node (preflight.mjs) \u2014 a prompt-only node with Zod schema and onComplete hook:
\`\`\`javascript
${i.preflight}
\`\`\`

### Example graph (graph.mjs) \u2014 WorkflowAgent subclass with conditional routing:
\`\`\`javascript
${i.graph}
\`\`\`

Study these examples carefully. Your generated code must follow the same patterns exactly.
`),o&&(s+=`
## Full framework documentation (Custom Workflows)
${o}
`),`Generate the code for a Zibby workflow called "${r.name}".

## Zibby Workflow Framework Reference

Zibby workflows are directed graphs where each node invokes a **real AI agent** (Cursor, Claude, Codex, or Gemini)
with full tool access, MCP server integration, and Zod-validated structured output.
This is NOT a simple LLM API wrapper \u2014 each node runs a full agent with tool-calling capabilities.

### Architecture
- The framework calls the configured AI agent for each node.
- Each node's \`prompt\` function receives the accumulated \`state\` object and returns a prompt string.
- The agent's response is parsed and validated against the node's \`outputSchema\` (Zod).
- The validated output is stored in \`state\` under the node's name (e.g., \`state.classify_ticket\`).
- Downstream nodes access upstream results via \`state.<upstream_node_name>\`.

### Node properties
- \`name\` (string, required) \u2014 snake_case identifier
- \`prompt\` (function, required) \u2014 \`(state) => \\\`...\\\`\` returns the prompt string
- \`outputSchema\` (Zod schema, required) \u2014 every field MUST have \`.describe()\`
- \`skills\` (array, optional) \u2014 MCP skills: \`[SKILLS.BROWSER]\`, \`[SKILLS.GITHUB]\`, etc.
- \`timeout\` (number, optional) \u2014 ms, default 300000
- \`onComplete\` (async function, optional) \u2014 \`(state, result) => {}\` post-processing hook

### Available SKILLS constants (import from '@zibby/core')
SKILLS.BROWSER, SKILLS.MEMORY, SKILLS.GITHUB, SKILLS.JIRA, SKILLS.SLACK, SKILLS.RUNNER

### Graph API
- \`graph.addNode(name, nodeObject)\` \u2014 register a node
- \`graph.setEntryPoint(name)\` \u2014 set the first node
- \`graph.addEdge(from, to)\` \u2014 connect nodes (use \`'END'\` to terminate)
- \`graph.addConditionalEdges(from, (state) => 'nextNode' | 'END')\` \u2014 conditional routing

### Rules
- Import: \`import { z } from '@zibby/core';\` (add \`SKILLS\` only if the node uses skills)
- Export name: camelCase + "Node" (e.g., \`classifyTicketNode\` for name \`classify_ticket\`)
- Prompt function: template literal referencing \`state.input\` and upstream \`state.<node_name>\`
- Prompts must be detailed \u2014 tell the agent exactly what to analyze/produce
${s}
## Workflow to generate: "${r.name}"

### Description
${r.description}

### Nodes
${e}

### Edges (flow)
${n}

## Output format

Return a JSON object with this exact structure:
{
  "nodes": {
    "<node_name>": {
      "code": "// complete ESM module code as a string"
    }
  }
}

IMPORTANT: Return ONLY valid JSON. No markdown fences, no explanation outside the JSON.`}async function D(r,e){let n=await G(e),i=K(n);try{let{invokeAgent:o}=await import("@zibby/core"),s=U(r),t=await o(s,{state:{agentType:i,config:n,cwd:e,workspace:e}},{model:n?.agent?.[i]?.model||"auto",workspace:e,config:n,timeout:12e4}),a=(typeof t=="string"?t:t?.raw||JSON.stringify(t?.structured||t)).match(/\{[\s\S]*\}/);if(!a)throw new Error("Agent did not return valid JSON");return JSON.parse(a[0])}catch(o){return console.warn(`Agent code generation failed (${o.message}), using templates`),q(r)}}function q(r){let e={};for(let n of r.nodes){let i=h(n.name),o=`${n.name.split("_").map(a=>a.charAt(0).toUpperCase()+a.slice(1)).join("")}OutputSchema`,s=n.outputFields?.length?n.outputFields.map(a=>`  ${a}: z.string().describe('${a}'),`).join(`
`):`  summary: z.string().describe('Summary of the result'),
  status: z.enum(['ok', 'warn', 'error']).describe('Overall status'),`,t=r.edges.filter(a=>a.to===n.name&&a.from!=="START").map(a=>`Previous step (${a.from}): \${JSON.stringify(state.${a.from} || {}, null, 2)}`).join(`
`),d=t?`${n.description}

Input:
\${JSON.stringify(state.input || {}, null, 2)}

${t}`:`${n.description}

Input:
\${JSON.stringify(state.input || {}, null, 2)}`;e[n.name]={code:`import { z } from '@zibby/core';

const ${o} = z.object({
${s}
});

export const ${i} = {
  name: '${n.name}',
  prompt: (state) => \`${d}\`,
  outputSchema: ${o},
};
`}}return{nodes:e}}function B(r,e,n,i){let o=e.toLowerCase(),s=W(o),t=l(r,".zibby","workflows",o),d=l(t,"nodes");J(d,{recursive:!0});let a=n.nodes.map(c=>c.name);for(let c of n.nodes){let f=i.nodes?.[c.name]?.code;f&&m(l(d,`${c.name.replace(/_/g,"-")}.mjs`),f,"utf-8")}let p=a.map(c=>{let f=h(c),E=c.replace(/_/g,"-");return`export { ${f} } from './${E}.mjs';`});m(l(d,"index.mjs"),`${p.join(`
`)}
`,"utf-8");let $=a[0],L=a.map(c=>h(c)).join(", "),k=a.map(c=>`    graph.addNode('${c}', ${h(c)});`).join(`
`),b=n.edges.map(c=>c.condition?`    graph.addConditionalEdges('${c.from}', (state) => {
      ${c.condition}
    });`:`    graph.addEdge('${c.from}', '${c.to}');`).join(`
`),S=`import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
import { ${L} } from './nodes/index.mjs';

export class ${s} extends WorkflowAgent {
  buildGraph() {
    const graph = new WorkflowGraph();

${k}

    graph.setEntryPoint('${$}');
${b}

    return graph;
  }

  async onComplete(result) {
    console.log(\`[${o}] workflow complete \u2014 success: \${result.success !== false}\`);
  }
}
`;m(l(t,"graph.mjs"),S,"utf-8");let g={name:o,description:n.description||`${s} workflow`,entryClass:s,triggers:{api:!0}};m(l(t,"workflow.json"),`${JSON.stringify(g,null,2)}
`,"utf-8");let w=["graph.mjs","workflow.json","nodes/index.mjs",...a.map(c=>`nodes/${c.replace(/_/g,"-")}.mjs`)];return{workflowDir:C(r,t),files:w,className:s,slug:o}}async function Z(r){let{name:e,description:n,nodes:i,edges:o}=r;if(!e||!O.test(e.toLowerCase()))return JSON.stringify({error:`Invalid workflow name "${e}". Must be kebab-case, 2-64 chars, lowercase letters/numbers/hyphens.`});if(!i||i.length===0)return JSON.stringify({error:"At least one node is required."});let s={name:e.toLowerCase(),description:n||`${W(e.toLowerCase())} workflow`,nodes:i.map(t=>({name:t.name.replace(/-/g,"_"),description:t.description||`Process ${t.name}`,inputFields:t.inputFields||[],outputFields:t.outputFields||[]})),edges:o||[]};if(s.edges.length===0&&s.nodes.length>0){for(let t=0;t<s.nodes.length-1;t++)s.edges.push({from:s.nodes[t].name,to:s.nodes[t+1].name});s.edges.push({from:s.nodes[s.nodes.length-1].name,to:"END"})}return JSON.stringify({ok:!0,spec:s,message:`Workflow "${s.name}" designed with ${s.nodes.length} node(s). Call build_workflow to generate the code.`,preview:{nodes:s.nodes.map(t=>t.name),flow:s.edges.map(t=>t.condition?`${t.from} \u2192(if ${t.condition})\u2192 ${t.to}`:`${t.from} \u2192 ${t.to}`)}})}async function H(r,e){let{name:n,spec:i}=r,o=(n||i?.name||"").toLowerCase();if(!o||!O.test(o))return JSON.stringify({error:`Invalid workflow name "${o}".`});if(!i||!i.nodes||i.nodes.length===0)return JSON.stringify({error:"spec with nodes is required. Call design_workflow first."});let s=l(e,".zibby","workflows",o);if(u(s))return JSON.stringify({error:`Workflow "${o}" already exists at .zibby/workflows/${o}/. Delete it first or choose a different name.`});let t=await D(i,e),d=B(e,o,i,t);return JSON.stringify({ok:!0,...d,message:`Workflow "${o}" created at ${d.workflowDir}/`,nextSteps:[`Test locally: zibby start ${o}`,`Deploy to cloud: zibby deploy ${o} --project <project-id>`,`Tail logs: zibby logs --workflow ${o} --project <project-id>`]})}async function Y(r,e){let{workflowName:n,nodeName:i,description:o,inputFields:s,outputFields:t}=r,d=(n||"").toLowerCase(),a=(i||"").replace(/-/g,"_"),p=l(e,".zibby","workflows",d);if(!u(p))return JSON.stringify({error:`Workflow "${d}" not found. Create it first with build_workflow.`});let $={name:d,description:"",nodes:[{name:a,description:o||`Process ${a}`,inputFields:s||[],outputFields:t||[]}],edges:[]},k=(await D($,e)).nodes?.[a]?.code;if(!k)return JSON.stringify({error:"Failed to generate node code."});let b=l(p,"nodes"),S=`${a.replace(/_/g,"-")}.mjs`;m(l(b,S),k,"utf-8");let g=l(b,"index.mjs"),w=h(a),c=`export { ${w} } from './${a.replace(/_/g,"-")}.mjs';
`,f=u(g)?y(g,"utf-8"):"";return f.includes(w)||m(g,f+c,"utf-8"),JSON.stringify({ok:!0,file:`nodes/${S}`,exportName:w,message:`Node "${a}" added. Update graph.mjs to wire it into the graph.`})}async function Q(r,e){let{name:n,projectId:i}=r,o=(n||"").toLowerCase();if(!o)return JSON.stringify({error:"Workflow name is required."});if(!i)return JSON.stringify({error:"projectId is required."});let s=l(e,".zibby","workflows",o);if(!u(s))return JSON.stringify({error:`Workflow "${o}" not found at .zibby/workflows/${o}/`});try{let{execSync:t}=await import("child_process"),d=t(`node "${l(e,"packages/cli/bin/zibby.js")}" deploy ${o} --project ${i}`,{cwd:e,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]});return JSON.stringify({ok:!0,output:d.trim()})}catch{try{let{execSync:d}=await import("child_process"),a=d(`npx zibby deploy ${o} --project ${i}`,{cwd:e,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]});return JSON.stringify({ok:!0,output:a.trim()})}catch(d){return JSON.stringify({error:`Deploy failed: ${d.message}`})}}}function V(r){let e=l(r,".zibby","workflows");if(!u(e))return JSON.stringify({workflows:[],message:"No workflows found. Use build_workflow to create one."});let i=N(e).filter(o=>{try{return _(l(e,o)).isDirectory()}catch{return!1}}).map(o=>{let s=l(e,o,"workflow.json"),t={};try{t=JSON.parse(y(s,"utf-8"))}catch{}let d=l(e,o,"nodes"),a=0;try{a=N(d).filter(p=>p.endsWith(".mjs")&&p!=="index.mjs").length}catch{}return{name:o,description:t.description||"",nodeCount:a,path:C(r,l(e,o))}});return JSON.stringify({workflows:i})}var re={id:"workflow-builder",description:"Build, scaffold, and deploy custom AI workflows via conversation",envKeys:[],promptFragment:T,tools:[{name:"design_workflow",description:"Design a workflow spec (nodes, edges, descriptions) for the user to review before building. Call this after understanding requirements.",input_schema:{type:"object",properties:{name:{type:"string",description:"Workflow name in kebab-case (e.g., ticket-triage)"},description:{type:"string",description:"What the workflow does"},nodes:{type:"array",items:{type:"object",properties:{name:{type:"string",description:"Node name in snake_case (e.g., classify_ticket)"},description:{type:"string",description:"What this node does \u2014 be specific about input/output"},inputFields:{type:"array",items:{type:"string"},description:"Key fields this node reads from state"},outputFields:{type:"array",items:{type:"string"},description:"Key fields this node produces"}},required:["name","description"]},description:"Workflow nodes (processing steps)"},edges:{type:"array",items:{type:"object",properties:{from:{type:"string",description:"Source node name"},to:{type:"string",description:'Target node name (or "END")'},condition:{type:"string",description:"JS expression for conditional routing (optional)"}},required:["from","to"]},description:"Edges connecting nodes. If omitted, nodes are wired linearly."}},required:["name","description","nodes"]}},{name:"build_workflow",description:"Generate real workflow code on disk from a design spec. Uses the configured AI agent for high-quality code generation.",input_schema:{type:"object",properties:{name:{type:"string",description:"Workflow name (from design_workflow)"},spec:{type:"object",description:"The full spec object returned by design_workflow",properties:{name:{type:"string"},description:{type:"string"},nodes:{type:"array",items:{type:"object"}},edges:{type:"array",items:{type:"object"}}}}},required:["name","spec"]}},{name:"add_node",description:"Add a new node to an existing workflow. Generates the node file and updates the barrel export.",input_schema:{type:"object",properties:{workflowName:{type:"string",description:"Existing workflow name (kebab-case)"},nodeName:{type:"string",description:"New node name (snake_case)"},description:{type:"string",description:"What this node does"},inputFields:{type:"array",items:{type:"string"},description:"Fields read from state"},outputFields:{type:"array",items:{type:"string"},description:"Fields produced"}},required:["workflowName","nodeName","description"]}},{name:"deploy_workflow",description:"Deploy a workflow to Zibby Cloud. Returns the trigger URL.",input_schema:{type:"object",properties:{name:{type:"string",description:"Workflow name to deploy"},projectId:{type:"string",description:"Target project ID"}},required:["name","projectId"]}},{name:"list_workflows",description:"List all local workflows in .zibby/workflows/.",input_schema:{type:"object",properties:{}}},{name:"explore_framework_docs",description:"Read Zibby framework documentation on demand. Call this before building complex workflows or when you need details on advanced patterns (middleware, conditional routing, skills, deployment, CLI commands).",input_schema:{type:"object",properties:{topic:{type:"string",description:'Doc topic to read (e.g., "workflow", "custom-workflows", "cli-reference", "packages/core", "packages/skills", "integrations/jira"). Call with no topic to list all available docs.'}}}}],async handleToolCall(r,e,n){let i=n?.options?.workspace||process.cwd();try{switch(r){case"design_workflow":return await Z(e);case"build_workflow":return await H(e,i);case"add_node":return await Y(e,i);case"deploy_workflow":return await Q(e,i);case"list_workflows":return V(i);case"explore_framework_docs":{let o=(e.topic||"").trim();if(!o){let t=I();return JSON.stringify({available:t,hint:"Call again with a topic to read its content."})}let s=A(o);if(!s){let t=I();return JSON.stringify({error:`Doc "${o}" not found.`,available:t})}return JSON.stringify({topic:o,content:s})}default:return JSON.stringify({error:`Unknown tool: ${r}`})}}catch(o){return JSON.stringify({error:o.message})}},resolve(){return null}};export{re as workflowBuilderSkill};
