@zibby/skills 0.1.8 → 0.1.10

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.
@@ -1,13 +1,28 @@
1
- import{existsSync as f,readFileSync as j,readdirSync as S,mkdirSync as W,writeFileSync as m,statSync as J}from"fs";import{join as p,resolve as F,relative as _}from"path";const v=`## Workflow Builder
1
+ 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
2
2
 
3
3
  You can help users build custom AI workflows using the Zibby workflow framework.
4
4
 
5
+ ### What makes Zibby workflows different
6
+ Each node invokes a **real AI agent** (Cursor, Claude, Codex, or Gemini) \u2014 not a thin LLM API wrapper.
7
+ That means every node has full agent capabilities: tool use, MCP servers (browser, GitHub, Jira, Slack),
8
+ multi-turn reasoning, and structured output validation via Zod schemas.
9
+
10
+ Key differentiators:
11
+ - **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.
12
+ - **Structured output** \u2014 every node declares a Zod schema; the framework validates and parses the agent's response automatically.
13
+ - **Conditional routing** \u2014 edges can branch on agent-produced fields (e.g., \`state.triage.priority === 'critical'\`), enabling intelligent decision graphs.
14
+ - **MCP skill injection** \u2014 nodes declare \`skills: [SKILLS.BROWSER, SKILLS.GITHUB]\` and the framework spins up the right MCP servers automatically.
15
+ - **Deploy anywhere** \u2014 \`zibby deploy\` pushes to Zibby Cloud with an API trigger; or self-host with \`zibby start\`.
16
+ - **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.
17
+
5
18
  ### What is a workflow?
6
- A directed graph of nodes (AI steps) connected by edges. Each node has:
19
+ A directed graph of nodes (AI agent steps) connected by edges. Each node has:
7
20
  - \`name\` \u2014 unique identifier (snake_case)
8
- - \`prompt\` \u2014 function that receives state and returns the LLM prompt string
9
- - \`outputSchema\` \u2014 Zod schema defining the structured output
10
- - \`skills\` (optional) \u2014 array of MCP skill IDs the node needs (e.g., 'browser', 'github')
21
+ - \`prompt\` \u2014 function that receives state and returns the prompt string sent to the agent
22
+ - \`outputSchema\` \u2014 Zod schema defining the structured output the agent must return
23
+ - \`skills\` (optional) \u2014 array of MCP skill IDs the node needs (e.g., \`SKILLS.BROWSER\`, \`SKILLS.GITHUB\`)
24
+ - \`timeout\` (optional) \u2014 max execution time in ms (default: 300000)
25
+ - \`model\` (optional) \u2014 override the model for this node (e.g., \`'claude-opus-4'\`)
11
26
 
12
27
  ### File structure
13
28
  \`\`\`
@@ -21,17 +36,24 @@ A directed graph of nodes (AI steps) connected by edges. Each node has:
21
36
 
22
37
  ### Node pattern
23
38
  \`\`\`javascript
24
- import { z } from '@zibby/core';
39
+ import { z, SKILLS } from '@zibby/core';
25
40
 
26
41
  const OutputSchema = z.object({
27
42
  summary: z.string().describe('Brief summary'),
28
- items: z.array(z.string()).describe('List of items'),
43
+ items: z.array(z.string()).describe('List of extracted items'),
44
+ needsReview: z.boolean().describe('Whether a human should review this'),
29
45
  });
30
46
 
31
47
  export const myNode = {
32
48
  name: 'my_node',
33
- prompt: (state) => \\\`Analyze the input and return structured results.
34
- Input: \\\${JSON.stringify(state.input || {}, null, 2)}\\\`,
49
+ skills: [SKILLS.GITHUB], // optional \u2014 framework injects MCP servers
50
+ timeout: 120000, // optional \u2014 2 min timeout
51
+ prompt: (state) => \\\`You are analyzing a pull request.
52
+
53
+ Input:
54
+ \\\${JSON.stringify(state.input || {}, null, 2)}
55
+
56
+ Return a JSON object matching the schema.\\\`,
35
57
  outputSchema: OutputSchema,
36
58
  };
37
59
  \`\`\`
@@ -51,41 +73,110 @@ export class MyWorkflow extends WorkflowAgent {
51
73
  graph.addEdge('route', 'END');
52
74
  return graph;
53
75
  }
76
+
77
+ async onComplete(result) {
78
+ // Post-execution hook \u2014 save artifacts, notify, etc.
79
+ console.log('Workflow complete:', result.success);
80
+ }
54
81
  }
55
82
  \`\`\`
56
83
 
57
84
  Conditional edges: \`graph.addConditionalEdges('node', (state) => state.node.priority === 'high' ? 'escalate' : 'notify')\`
58
85
 
86
+ ### Available SKILLS constants
87
+ Import from \`@zibby/core\`: \`SKILLS.BROWSER\`, \`SKILLS.MEMORY\`, \`SKILLS.GITHUB\`, \`SKILLS.JIRA\`, \`SKILLS.SLACK\`, \`SKILLS.RUNNER\`
88
+
89
+ ### Deep documentation
90
+ Call \`explore_framework_docs\` to read detailed framework docs on demand. Use it for:
91
+ - Advanced patterns (middleware, parallel nodes, state schemas)
92
+ - Deployment & cloud triggers
93
+ - CLI commands reference
94
+ - Integration details (Jira, GitHub, etc.)
95
+ Call with no arguments to see all available topics.
96
+
59
97
  ### How to use the builder tools
60
- 1. Ask the user what their workflow should do, what input it receives, and what steps are needed.
61
- 2. Call \`design_workflow\` with the structured spec for the user to review.
62
- 3. Once approved, call \`build_workflow\` to generate real code on disk.
63
- 4. Remind the user: \`zibby start <name>\` to test, \`zibby deploy <name> --project <id>\` to deploy, \`zibby logs --workflow <name>\` to tail logs.
98
+ 1. For complex workflows, call \`explore_framework_docs("custom-workflows")\` first to learn advanced patterns.
99
+ 2. Ask the user what their workflow should do, what input it receives, and what steps are needed.
100
+ 3. Call \`design_workflow\` with the structured spec for the user to review.
101
+ 4. Once approved, call \`build_workflow\` to generate real code on disk (uses the configured agent for high-quality code generation).
102
+ 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.
64
103
 
65
104
  ### Important
66
- - Each node prompt should be detailed and specific \u2014 tell the AI exactly what to do and what format to return.
67
- - Zod schemas should use .describe() on every field so the AI knows what each field means.
105
+ - Each node prompt should be detailed and specific \u2014 tell the AI agent exactly what to do and what format to return.
106
+ - Zod schemas MUST use .describe() on every field so the agent knows what each field means.
68
107
  - Node names must be snake_case (e.g., classify_ticket, generate_report).
69
108
  - Workflow names must be kebab-case (e.g., ticket-triage, pr-review).
70
- - State flows through: each node's output is stored under its name in state (e.g., state.classify_ticket).
71
- - Downstream nodes can reference upstream outputs in their prompt function.`,O=/^[a-z][a-z0-9-]{0,62}[a-z0-9]$/;function z(n){return`${n.split("-").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join("")}Workflow`}function y(n){return`${n.replace(/_([a-z])/g,(e,s)=>s.toUpperCase())}Node`}function A(n){const e=n?.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 E(n){const e=F(n,".zibby.config.mjs");if(!f(e))return{};try{return(await import(e)).default||{}}catch{return{}}}function D(n){const e=n.nodes.map(o=>{const t=o.inputFields?.length?`Input fields: ${o.inputFields.join(", ")}`:"Input: receives full state",i=o.outputFields?.length?`Output fields: ${o.outputFields.join(", ")}`:"Output: determined by task";return`- ${o.name}: ${o.description}. ${t}. ${i}.`}).join(`
72
- `),s=n.edges.map(o=>o.condition?`- ${o.from} \u2192 ${o.to} (conditional: ${o.condition})`:`- ${o.from} \u2192 ${o.to}`).join(`
73
- `);return`Generate the code for a Zibby workflow called "${n.name}".
109
+ - State flows through: each node's validated output is stored under its name in state (e.g., state.classify_ticket).
110
+ - Downstream nodes reference upstream outputs in their prompt function (e.g., \\\`\\\${JSON.stringify(state.classify_ticket, null, 2)}\\\`).
111
+ - 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(`
112
+ `),n=r.edges.map(t=>t.condition?`- ${t.from} \u2192 ${t.to} (conditional: ${t.condition})`:`- ${t.from} \u2192 ${t.to}`).join(`
113
+ `),i=M(),o=A("custom-workflows"),s="";return i&&(s+=`
114
+ ## Real working examples from the Zibby framework
115
+
116
+ ### Example node (preflight.mjs) \u2014 a prompt-only node with Zod schema and onComplete hook:
117
+ \`\`\`javascript
118
+ ${i.preflight}
119
+ \`\`\`
120
+
121
+ ### Example graph (graph.mjs) \u2014 WorkflowAgent subclass with conditional routing:
122
+ \`\`\`javascript
123
+ ${i.graph}
124
+ \`\`\`
125
+
126
+ Study these examples carefully. Your generated code must follow the same patterns exactly.
127
+ `),o&&(s+=`
128
+ ## Full framework documentation (Custom Workflows)
129
+ ${o}
130
+ `),`Generate the code for a Zibby workflow called "${r.name}".
131
+
132
+ ## Zibby Workflow Framework Reference
133
+
134
+ Zibby workflows are directed graphs where each node invokes a **real AI agent** (Cursor, Claude, Codex, or Gemini)
135
+ with full tool access, MCP server integration, and Zod-validated structured output.
136
+ This is NOT a simple LLM API wrapper \u2014 each node runs a full agent with tool-calling capabilities.
137
+
138
+ ### Architecture
139
+ - The framework calls the configured AI agent for each node.
140
+ - Each node's \`prompt\` function receives the accumulated \`state\` object and returns a prompt string.
141
+ - The agent's response is parsed and validated against the node's \`outputSchema\` (Zod).
142
+ - The validated output is stored in \`state\` under the node's name (e.g., \`state.classify_ticket\`).
143
+ - Downstream nodes access upstream results via \`state.<upstream_node_name>\`.
144
+
145
+ ### Node properties
146
+ - \`name\` (string, required) \u2014 snake_case identifier
147
+ - \`prompt\` (function, required) \u2014 \`(state) => \\\`...\\\`\` returns the prompt string
148
+ - \`outputSchema\` (Zod schema, required) \u2014 every field MUST have \`.describe()\`
149
+ - \`skills\` (array, optional) \u2014 MCP skills: \`[SKILLS.BROWSER]\`, \`[SKILLS.GITHUB]\`, etc.
150
+ - \`timeout\` (number, optional) \u2014 ms, default 300000
151
+ - \`onComplete\` (async function, optional) \u2014 \`(state, result) => {}\` post-processing hook
152
+
153
+ ### Available SKILLS constants (import from '@zibby/core')
154
+ SKILLS.BROWSER, SKILLS.MEMORY, SKILLS.GITHUB, SKILLS.JIRA, SKILLS.SLACK, SKILLS.RUNNER
155
+
156
+ ### Graph API
157
+ - \`graph.addNode(name, nodeObject)\` \u2014 register a node
158
+ - \`graph.setEntryPoint(name)\` \u2014 set the first node
159
+ - \`graph.addEdge(from, to)\` \u2014 connect nodes (use \`'END'\` to terminate)
160
+ - \`graph.addConditionalEdges(from, (state) => 'nextNode' | 'END')\` \u2014 conditional routing
161
+
162
+ ### Rules
163
+ - Import: \`import { z } from '@zibby/core';\` (add \`SKILLS\` only if the node uses skills)
164
+ - Export name: camelCase + "Node" (e.g., \`classifyTicketNode\` for name \`classify_ticket\`)
165
+ - Prompt function: template literal referencing \`state.input\` and upstream \`state.<node_name>\`
166
+ - Prompts must be detailed \u2014 tell the agent exactly what to analyze/produce
167
+ ${s}
168
+ ## Workflow to generate: "${r.name}"
74
169
 
75
- ## Workflow description
76
- ${n.description}
170
+ ### Description
171
+ ${r.description}
77
172
 
78
- ## Nodes
173
+ ### Nodes
79
174
  ${e}
80
175
 
81
- ## Edges (flow)
82
- ${s}
176
+ ### Edges (flow)
177
+ ${n}
83
178
 
84
- ## Requirements
85
- For EACH node, generate a complete ESM module with:
86
- 1. A Zod output schema with .describe() on every field
87
- 2. A prompt function that takes state and returns a detailed, specific prompt string
88
- 3. The prompt should reference upstream node outputs from state when applicable
179
+ ## Output format
89
180
 
90
181
  Return a JSON object with this exact structure:
91
182
  {
@@ -96,80 +187,53 @@ Return a JSON object with this exact structure:
96
187
  }
97
188
  }
98
189
 
99
- Each node's code must:
100
- - Start with: import { z } from '@zibby/core';
101
- - Define a const schema (e.g., ClassifyOutputSchema)
102
- - Export a const node object with name, prompt (function), and outputSchema
103
- - The export name should be camelCase + "Node" (e.g., classifyTicketNode)
104
-
105
- Example node code:
106
- \`\`\`
107
- import { z } from '@zibby/core';
108
-
109
- const ClassifyOutputSchema = z.object({
110
- priority: z.enum(['critical', 'high', 'medium', 'low']).describe('Ticket priority level'),
111
- category: z.string().describe('Issue category'),
112
- });
113
-
114
- export const classifyTicketNode = {
115
- name: 'classify_ticket',
116
- prompt: (state) => \`You are a ticket classifier.
117
-
118
- Input ticket:
119
- \${JSON.stringify(state.input || {}, null, 2)}
120
-
121
- Classify this ticket by priority and category.\`,
122
- outputSchema: ClassifyOutputSchema,
123
- };
124
- \`\`\`
125
-
126
- IMPORTANT: Return ONLY valid JSON. No markdown fences, no explanation outside the JSON.`}async function x(n,e){const s=await E(e),o=A(s);try{const{invokeAgent:t}=await import("@zibby/core"),i=D(n),r=await t(i,{state:{agentType:o,config:s,cwd:e,workspace:e}},{model:s?.agent?.[o]?.model||"auto",workspace:e,config:s,timeout:12e4}),a=(typeof r=="string"?r:r?.raw||JSON.stringify(r?.structured||r)).match(/\{[\s\S]*\}/);if(!a)throw new Error("Agent did not return valid JSON");return JSON.parse(a[0])}catch(t){return console.warn(`Agent code generation failed (${t.message}), using templates`),I(n)}}function I(n){const e={};for(const s of n.nodes){const o=y(s.name),t=`${s.name.split("_").map(a=>a.charAt(0).toUpperCase()+a.slice(1)).join("")}OutputSchema`,i=s.outputFields?.length?s.outputFields.map(a=>` ${a}: z.string().describe('${a}'),`).join(`
190
+ 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(`
127
191
  `):` summary: z.string().describe('Summary of the result'),
128
- status: z.enum(['ok', 'warn', 'error']).describe('Overall status'),`,r=n.edges.filter(a=>a.to===s.name&&a.from!=="START").map(a=>`Previous step (${a.from}): \${JSON.stringify(state.${a.from} || {}, null, 2)}`).join(`
129
- `),c=r?`${s.description}
192
+ 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(`
193
+ `),d=t?`${n.description}
130
194
 
131
195
  Input:
132
196
  \${JSON.stringify(state.input || {}, null, 2)}
133
197
 
134
- ${r}`:`${s.description}
198
+ ${t}`:`${n.description}
135
199
 
136
200
  Input:
137
- \${JSON.stringify(state.input || {}, null, 2)}`;e[s.name]={code:`import { z } from '@zibby/core';
201
+ \${JSON.stringify(state.input || {}, null, 2)}`;e[n.name]={code:`import { z } from '@zibby/core';
138
202
 
139
- const ${t} = z.object({
140
- ${i}
203
+ const ${o} = z.object({
204
+ ${s}
141
205
  });
142
206
 
143
- export const ${o} = {
144
- name: '${s.name}',
145
- prompt: (state) => \`${c}\`,
146
- outputSchema: ${t},
207
+ export const ${i} = {
208
+ name: '${n.name}',
209
+ prompt: (state) => \`${d}\`,
210
+ outputSchema: ${o},
147
211
  };
148
- `}}return{nodes:e}}function T(n,e,s,o){const t=e.toLowerCase(),i=z(t),r=p(n,".zibby","workflows",t),c=p(r,"nodes");W(c,{recursive:!0});const a=s.nodes.map(d=>d.name);for(const d of s.nodes){const u=o.nodes?.[d.name]?.code;u&&m(p(c,`${d.name.replace(/_/g,"-")}.mjs`),u,"utf-8")}const l=a.map(d=>{const u=y(d),C=d.replace(/_/g,"-");return`export { ${u} } from './${C}.mjs';`});m(p(c,"index.mjs"),`${l.join(`
212
+ `}}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(`
149
213
  `)}
150
- `,"utf-8");const $=a[0],N=a.map(d=>y(d)).join(", "),h=a.map(d=>` graph.addNode('${d}', ${y(d)});`).join(`
151
- `),k=s.edges.map(d=>d.condition?` graph.addConditionalEdges('${d.from}', (state) => {
152
- ${d.condition}
153
- });`:` graph.addEdge('${d.from}', '${d.to}');`).join(`
154
- `),b=`import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
155
- import { ${N} } from './nodes/index.mjs';
156
-
157
- export class ${i} extends WorkflowAgent {
214
+ `,"utf-8");let $=a[0],L=a.map(c=>h(c)).join(", "),k=a.map(c=>` graph.addNode('${c}', ${h(c)});`).join(`
215
+ `),b=n.edges.map(c=>c.condition?` graph.addConditionalEdges('${c.from}', (state) => {
216
+ ${c.condition}
217
+ });`:` graph.addEdge('${c.from}', '${c.to}');`).join(`
218
+ `),S=`import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
219
+ import { ${L} } from './nodes/index.mjs';
220
+
221
+ export class ${s} extends WorkflowAgent {
158
222
  buildGraph() {
159
223
  const graph = new WorkflowGraph();
160
224
 
161
- ${h}
225
+ ${k}
162
226
 
163
227
  graph.setEntryPoint('${$}');
164
- ${k}
228
+ ${b}
165
229
 
166
230
  return graph;
167
231
  }
168
232
 
169
233
  async onComplete(result) {
170
- console.log(\`[${t}] workflow complete \u2014 success: \${result.success !== false}\`);
234
+ console.log(\`[${o}] workflow complete \u2014 success: \${result.success !== false}\`);
171
235
  }
172
236
  }
173
- `;m(p(r,"graph.mjs"),b,"utf-8");const g={name:t,description:s.description||`${i} workflow`,entryClass:i,triggers:{api:!0}};m(p(r,"workflow.json"),`${JSON.stringify(g,null,2)}
174
- `,"utf-8");const w=["graph.mjs","workflow.json","nodes/index.mjs",...a.map(d=>`nodes/${d.replace(/_/g,"-")}.mjs`)];return{workflowDir:_(n,r),files:w,className:i,slug:t}}async function L(n){const{name:e,description:s,nodes:o,edges:t}=n;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(!o||o.length===0)return JSON.stringify({error:"At least one node is required."});const i={name:e.toLowerCase(),description:s||`${z(e.toLowerCase())} workflow`,nodes:o.map(r=>({name:r.name.replace(/-/g,"_"),description:r.description||`Process ${r.name}`,inputFields:r.inputFields||[],outputFields:r.outputFields||[]})),edges:t||[]};if(i.edges.length===0&&i.nodes.length>0){for(let r=0;r<i.nodes.length-1;r++)i.edges.push({from:i.nodes[r].name,to:i.nodes[r+1].name});i.edges.push({from:i.nodes[i.nodes.length-1].name,to:"END"})}return JSON.stringify({ok:!0,spec:i,message:`Workflow "${i.name}" designed with ${i.nodes.length} node(s). Call build_workflow to generate the code.`,preview:{nodes:i.nodes.map(r=>r.name),flow:i.edges.map(r=>r.condition?`${r.from} \u2192(if ${r.condition})\u2192 ${r.to}`:`${r.from} \u2192 ${r.to}`)}})}async function G(n,e){const{name:s,spec:o}=n,t=(s||o?.name||"").toLowerCase();if(!t||!O.test(t))return JSON.stringify({error:`Invalid workflow name "${t}".`});if(!o||!o.nodes||o.nodes.length===0)return JSON.stringify({error:"spec with nodes is required. Call design_workflow first."});const i=p(e,".zibby","workflows",t);if(f(i))return JSON.stringify({error:`Workflow "${t}" already exists at .zibby/workflows/${t}/. Delete it first or choose a different name.`});const r=await x(o,e),c=T(e,t,o,r);return JSON.stringify({ok:!0,...c,message:`Workflow "${t}" created at ${c.workflowDir}/`,nextSteps:[`Test locally: zibby start ${t}`,`Deploy to cloud: zibby deploy ${t} --project <project-id>`,`Tail logs: zibby logs --workflow ${t} --project <project-id>`]})}async function P(n,e){const{workflowName:s,nodeName:o,description:t,inputFields:i,outputFields:r}=n,c=(s||"").toLowerCase(),a=(o||"").replace(/-/g,"_"),l=p(e,".zibby","workflows",c);if(!f(l))return JSON.stringify({error:`Workflow "${c}" not found. Create it first with build_workflow.`});const $={name:c,description:"",nodes:[{name:a,description:t||`Process ${a}`,inputFields:i||[],outputFields:r||[]}],edges:[]},h=(await x($,e)).nodes?.[a]?.code;if(!h)return JSON.stringify({error:"Failed to generate node code."});const k=p(l,"nodes"),b=`${a.replace(/_/g,"-")}.mjs`;m(p(k,b),h,"utf-8");const g=p(k,"index.mjs"),w=y(a),d=`export { ${w} } from './${a.replace(/_/g,"-")}.mjs';
175
- `,u=f(g)?j(g,"utf-8"):"";return u.includes(w)||m(g,u+d,"utf-8"),JSON.stringify({ok:!0,file:`nodes/${b}`,exportName:w,message:`Node "${a}" added. Update graph.mjs to wire it into the graph.`})}async function q(n,e){const{name:s,projectId:o}=n,t=(s||"").toLowerCase();if(!t)return JSON.stringify({error:"Workflow name is required."});if(!o)return JSON.stringify({error:"projectId is required."});const i=p(e,".zibby","workflows",t);if(!f(i))return JSON.stringify({error:`Workflow "${t}" not found at .zibby/workflows/${t}/`});try{const{execSync:r}=await import("child_process"),c=r(`node "${p(e,"packages/cli/bin/zibby.js")}" deploy ${t} --project ${o}`,{cwd:e,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]});return JSON.stringify({ok:!0,output:c.trim()})}catch{try{const{execSync:c}=await import("child_process"),a=c(`npx zibby deploy ${t} --project ${o}`,{cwd:e,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]});return JSON.stringify({ok:!0,output:a.trim()})}catch(c){return JSON.stringify({error:`Deploy failed: ${c.message}`})}}}function R(n){const e=p(n,".zibby","workflows");if(!f(e))return JSON.stringify({workflows:[],message:"No workflows found. Use build_workflow to create one."});const o=S(e).filter(t=>{try{return J(p(e,t)).isDirectory()}catch{return!1}}).map(t=>{const i=p(e,t,"workflow.json");let r={};try{r=JSON.parse(j(i,"utf-8"))}catch{}const c=p(e,t,"nodes");let a=0;try{a=S(c).filter(l=>l.endsWith(".mjs")&&l!=="index.mjs").length}catch{}return{name:t,description:r.description||"",nodeCount:a,path:_(n,p(e,t))}});return JSON.stringify({workflows:o})}const B={id:"workflow-builder",description:"Build, scaffold, and deploy custom AI workflows via conversation",envKeys:[],promptFragment:v,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:{}}}],async handleToolCall(n,e,s){const o=s?.options?.workspace||process.cwd();try{switch(n){case"design_workflow":return await L(e);case"build_workflow":return await G(e,o);case"add_node":return await P(e,o);case"deploy_workflow":return await q(e,o);case"list_workflows":return R(o);default:return JSON.stringify({error:`Unknown tool: ${n}`})}}catch(t){return JSON.stringify({error:t.message})}},resolve(){return null}};export{B as workflowBuilderSkill};
237
+ `;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)}
238
+ `,"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';
239
+ `,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};
@@ -0,0 +1,109 @@
1
+ ---
2
+ sidebar_position: 4
3
+ title: Running Analysis
4
+ ---
5
+
6
+ # Running Analysis
7
+
8
+ Analysis is where Zibby's AI reads your Jira ticket, studies your codebase, and produces code changes and test cases.
9
+
10
+ ## Starting an Analysis
11
+
12
+ 1. Go to your project's **Tickets** page
13
+ 2. Click on a ticket to open its details
14
+ 3. Click **Analyze**
15
+ 4. Optionally add **additional context** or select an AI model
16
+ 5. The analysis runs in the cloud (AWS Fargate)
17
+
18
+ ## Analysis Pipeline
19
+
20
+ The AI runs through these steps automatically:
21
+
22
+ ### 1. Setup
23
+
24
+ - Clones your GitHub repository into a secure container
25
+ - Creates a git baseline for tracking changes
26
+
27
+ ### 2. Analyze Ticket
28
+
29
+ The AI reads:
30
+ - The Jira ticket summary and description
31
+ - Acceptance criteria and requirements
32
+ - Your codebase structure and existing code
33
+
34
+ It produces a **structured analysis** with:
35
+ - Understanding of the requirement
36
+ - Affected files and components
37
+ - Implementation approach
38
+ - Edge cases and considerations
39
+
40
+ If the ticket lacks sufficient detail, the analysis may stop here with a **"needs clarification"** status and explain what information is missing.
41
+
42
+ ### 3. Generate Code
43
+
44
+ Based on the analysis, the AI:
45
+ - Writes or modifies code to implement the ticket
46
+ - Captures all changes as a git diff
47
+ - Lists affected files
48
+
49
+ You can review the diff side-by-side in the dashboard and create a **Pull Request** directly.
50
+
51
+ ### 4. Generate Test Cases
52
+
53
+ The AI produces structured test cases including:
54
+ - Test case title and description
55
+ - Step-by-step instructions
56
+ - Test data and credentials
57
+ - Expected outcomes
58
+ - Preconditions and postconditions
59
+
60
+ ### 5. Finalize
61
+
62
+ - Compiles the final report
63
+ - Uploads all artifacts to the dashboard
64
+ - Updates the execution status
65
+
66
+ ## Custom Instructions
67
+
68
+ You can add custom prompt instructions for each AI node in **Project Settings > Node Configuration**:
69
+
70
+ - **analyze_ticket** — additional context about your architecture
71
+ - **generate_code** — coding standards, framework preferences
72
+ - **generate_test_cases** — test credentials, login flows, specific data to use
73
+
74
+ For example, to ensure tests use specific credentials:
75
+
76
+ ```
77
+ Use test account for login:
78
+ Email: test@example.com
79
+ Password: TestPass123
80
+ ```
81
+
82
+ ## Analysis Statuses
83
+
84
+ | Status | Meaning |
85
+ |---|---|
86
+ | **running** | Analysis is in progress |
87
+ | **completed** | All steps finished successfully |
88
+ | **failed** | A step encountered an error |
89
+ | **blocked** | Ticket lacks information to proceed |
90
+
91
+ ## Reviewing Analysis Results
92
+
93
+ After completion, the analysis page shows:
94
+
95
+ - **Pipeline progress** — status of each step with logs
96
+ - **Code Changes** — side-by-side diff with file tree
97
+ - **Test Cases** — editable test cases you can refine
98
+ - **Create PR** — one-click pull request creation
99
+ - **Suggested Changes** — AI suggestions for the ticket itself
100
+
101
+ ## Running Tests from Analysis
102
+
103
+ Once test cases are generated, you can run them locally using the CLI:
104
+
105
+ ```bash
106
+ zibby test --sources TC001,TC002 --execution <execution-id> --sync
107
+ ```
108
+
109
+ Or copy the full command from the **Code** button in the dashboard.