@zibby/cli 0.4.18 → 0.4.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/workflows/deploy.js +37 -37
- package/dist/commands/workflows/run-local.js +15 -15
- package/dist/commands/workflows/run.js +10 -10
- package/dist/commands/workflows/validate-helpers.js +1 -1
- package/dist/commands/workflows/validate.js +4 -4
- package/dist/package.json +1 -1
- package/dist/templates/.claude/CLAUDE.md +164 -48
- package/dist/templates/.claude/commands/add-skill.md +19 -7
- package/dist/utils/session-sync.js +1 -0
- package/package.json +1 -1
- package/templates/.claude/CLAUDE.md +164 -48
- package/templates/.claude/commands/add-skill.md +19 -7
|
@@ -13,20 +13,31 @@ the intent.
|
|
|
13
13
|
## 0. The 30-second tour
|
|
14
14
|
|
|
15
15
|
```
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
├──
|
|
16
|
+
workflows/<name>/ # default. Override via paths.workflows
|
|
17
|
+
# in .zibby.config.mjs (legacy projects
|
|
18
|
+
# may have .zibby/workflows/).
|
|
19
|
+
├── workflow.json # name, description, entryClass, triggers, defaultAgent
|
|
20
|
+
├── graph.mjs # WorkflowAgent class — buildGraph() + onComplete()
|
|
21
|
+
├── state.js # Zod schema for caller-provided inputs (-p key=value)
|
|
22
|
+
├── nodes/ # one file per node — prompt|execute + outputSchema
|
|
23
|
+
│ ├── index.mjs # OPTIONAL barrel — re-exports keep graph.mjs imports
|
|
24
|
+
│ │ # tidy ("import { fooNode, barNode } from './nodes/'")
|
|
25
|
+
│ │ # but a graph that imports each file directly is
|
|
26
|
+
│ │ # equally valid. Pick whichever you prefer.
|
|
20
27
|
│ ├── plan.mjs
|
|
21
28
|
│ ├── implement.mjs
|
|
22
29
|
│ └── verify.mjs
|
|
23
|
-
└── package.json # @zibby/
|
|
30
|
+
└── package.json # @zibby/core + zod (core re-exports WorkflowGraph,
|
|
31
|
+
# WorkflowAgent, z, skills, agent strategies).
|
|
24
32
|
```
|
|
25
33
|
|
|
26
34
|
Lifecycle:
|
|
27
35
|
|
|
28
36
|
```bash
|
|
29
|
-
zibby workflow new <name> # scaffold (creates
|
|
37
|
+
zibby workflow new <name> # scaffold (creates workflows/<name>/ by default;
|
|
38
|
+
# also writes a starter nodes/example.mjs that
|
|
39
|
+
# you can replace/delete once your real nodes
|
|
40
|
+
# are in place)
|
|
30
41
|
zibby workflow run <name> -p key=val # run locally — one-shot, no server
|
|
31
42
|
zibby workflow start <name> # run locally with hot-reload (server)
|
|
32
43
|
zibby workflow validate <name> # static check (graph topology, schemas, skills)
|
|
@@ -49,44 +60,76 @@ start; cloud is ~60s. Iterating in cloud is 12× slower.
|
|
|
49
60
|
{
|
|
50
61
|
"name": "code-review",
|
|
51
62
|
"description": "Review a git diff and return structured findings",
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"diff": "string",
|
|
56
|
-
"findings": "array"
|
|
57
|
-
}
|
|
63
|
+
"entryClass": "CodeReviewWorkflow",
|
|
64
|
+
"triggers": { "api": true },
|
|
65
|
+
"defaultAgent": "claude"
|
|
58
66
|
}
|
|
59
67
|
```
|
|
60
68
|
|
|
61
|
-
`
|
|
62
|
-
|
|
69
|
+
- `name` — kebab-case slug, ≤24 chars
|
|
70
|
+
- `entryClass` — the class exported from `graph.mjs` (CLI uses this to
|
|
71
|
+
pick the right export when there are multiple)
|
|
72
|
+
- `triggers.api` — `true` exposes a webhook URL after `zibby workflow
|
|
73
|
+
deploy`; `false` hides it (cron-only or internal)
|
|
74
|
+
- `defaultAgent` — one of `claude`, `cursor`, `codex`, `gemini`. Any
|
|
75
|
+
node overrides with its own `agent: 'cursor'` field.
|
|
63
76
|
|
|
64
|
-
### `graph.mjs`
|
|
77
|
+
### `graph.mjs` (class form — what production runtime expects)
|
|
65
78
|
|
|
66
79
|
```js
|
|
67
|
-
import { WorkflowGraph } from '@zibby/
|
|
68
|
-
import { z } from 'zod';
|
|
80
|
+
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
|
|
69
81
|
import { planNode } from './nodes/plan.mjs';
|
|
70
82
|
import { implementNode } from './nodes/implement.mjs';
|
|
71
83
|
import { verifyNode } from './nodes/verify.mjs';
|
|
84
|
+
import { codeReviewStateSchema } from './state.js';
|
|
72
85
|
|
|
73
|
-
export
|
|
74
|
-
|
|
86
|
+
export class CodeReviewWorkflow extends WorkflowAgent {
|
|
87
|
+
buildGraph() {
|
|
88
|
+
const graph = new WorkflowGraph();
|
|
89
|
+
graph.setStateSchema(codeReviewStateSchema);
|
|
75
90
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
91
|
+
graph.addNode('plan', planNode);
|
|
92
|
+
graph.addNode('implement', implementNode);
|
|
93
|
+
graph.addNode('verify', verifyNode);
|
|
79
94
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
95
|
+
graph.addEdge('plan', 'implement');
|
|
96
|
+
graph.addEdge('implement', 'verify');
|
|
97
|
+
graph.addEdge('verify', 'END'); // 'END' is the terminal sentinel
|
|
83
98
|
|
|
84
|
-
|
|
99
|
+
graph.setEntryPoint('plan');
|
|
100
|
+
return graph;
|
|
101
|
+
}
|
|
85
102
|
|
|
86
|
-
|
|
103
|
+
async onComplete(result) {
|
|
104
|
+
// Optional — runs after the graph finishes. Useful for posting a
|
|
105
|
+
// summary somewhere or transforming `result` before the runner
|
|
106
|
+
// logs it.
|
|
107
|
+
console.log(`[code-review] done — success=${result.success !== false}`);
|
|
108
|
+
}
|
|
87
109
|
}
|
|
88
110
|
```
|
|
89
111
|
|
|
112
|
+
The class name MUST match `entryClass` in workflow.json. The CLI
|
|
113
|
+
instantiates it (`new CodeReviewWorkflow()`), calls `.buildGraph()`,
|
|
114
|
+
runs the graph, then invokes `.onComplete(result)`.
|
|
115
|
+
|
|
116
|
+
### `state.js`
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
import { z } from 'zod';
|
|
120
|
+
|
|
121
|
+
export const codeReviewStateSchema = z.object({
|
|
122
|
+
diff: z.string().describe('Staged git diff to review'),
|
|
123
|
+
strict: z.boolean().optional().describe('Treat warnings as errors'),
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Declares which user-input fields callers can pass via `-p key=value`
|
|
128
|
+
or `--input '{"key":"value"}'`. The runner validates inputs against
|
|
129
|
+
this schema at run time — invalid inputs fail fast with a Zod error.
|
|
130
|
+
Read fields in nodes via `state.diff`, `state.strict`, etc. Optional
|
|
131
|
+
in v1 — graphs without `state.js` work but lose input validation.
|
|
132
|
+
|
|
90
133
|
### A node — `nodes/plan.mjs`
|
|
91
134
|
|
|
92
135
|
```js
|
|
@@ -159,6 +202,13 @@ the first node sees `state.userRequest = "fix login"`.
|
|
|
159
202
|
are internal to the graph runtime. Just `return` a plain object that
|
|
160
203
|
matches your `outputSchema`. The runtime puts it under `state[nodeName]`.
|
|
161
204
|
|
|
205
|
+
**Execute nodes can ONLY write under `state[nodeName]`.** There's no
|
|
206
|
+
mechanism to mutate a top-level state key from inside `execute()`. If
|
|
207
|
+
you need a counter that survives across loop iterations (retries,
|
|
208
|
+
attempts, accumulator), put it inside the node's own output schema —
|
|
209
|
+
read the prior value via `state.<nodeName>?.<field>`, return the new
|
|
210
|
+
value as part of `execute()`'s output. See §3 for the loop pattern.
|
|
211
|
+
|
|
162
212
|
---
|
|
163
213
|
|
|
164
214
|
## 3. Conditional routing
|
|
@@ -173,7 +223,47 @@ graph.addConditionalEdges('verify', (state) => {
|
|
|
173
223
|
|
|
174
224
|
The router function receives the full state, returns the name of the
|
|
175
225
|
next node (or `'END'`). All possible target nodes must also be declared
|
|
176
|
-
elsewhere via `addNode
|
|
226
|
+
elsewhere via `addNode` — a router returning an unregistered name will
|
|
227
|
+
fail at runtime (validate also catches it as `graph-edge-to-unknown`).
|
|
228
|
+
|
|
229
|
+
### Loops with a retry counter
|
|
230
|
+
|
|
231
|
+
Graphs aren't DAGs — you can route back to an earlier node. The
|
|
232
|
+
counter pattern lives **inside the looping node's own output**, since
|
|
233
|
+
`execute()` can only write to `state[nodeName]`. Example: a
|
|
234
|
+
`generate → check` loop that retries up to 2 times on failure.
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
// nodes/check.mjs
|
|
238
|
+
export const checkNode = {
|
|
239
|
+
name: 'check',
|
|
240
|
+
outputSchema: z.object({
|
|
241
|
+
passed: z.boolean(),
|
|
242
|
+
attempts: z.number(),
|
|
243
|
+
}),
|
|
244
|
+
execute: async (state) => {
|
|
245
|
+
const priorAttempts = state.check?.attempts ?? 0;
|
|
246
|
+
const passed = /* … your check logic … */ false;
|
|
247
|
+
return { passed, attempts: priorAttempts + 1 };
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// graph.mjs (inside buildGraph)
|
|
252
|
+
graph.addNode('generate', generateNode);
|
|
253
|
+
graph.addNode('check', checkNode);
|
|
254
|
+
graph.addEdge('generate', 'check');
|
|
255
|
+
graph.addConditionalEdges('check', (state) => {
|
|
256
|
+
if (state.check.passed) return 'END';
|
|
257
|
+
if (state.check.attempts < 2) return 'generate'; // loop back
|
|
258
|
+
return 'END'; // give up
|
|
259
|
+
});
|
|
260
|
+
graph.setEntryPoint('generate');
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Each iteration through `check`, `priorAttempts` reads the value the
|
|
264
|
+
previous iteration wrote, and the new value is what the next router
|
|
265
|
+
call sees as `state.check.attempts`. `'END'` is the only sentinel —
|
|
266
|
+
any other return value must be an `addNode`'d name.
|
|
177
267
|
|
|
178
268
|
---
|
|
179
269
|
|
|
@@ -202,7 +292,7 @@ don't list each tool.
|
|
|
202
292
|
|
|
203
293
|
```js
|
|
204
294
|
// .zibby/workflows/<name>/skills/slack.mjs
|
|
205
|
-
import { registerSkill } from '@zibby/
|
|
295
|
+
import { registerSkill } from '@zibby/core';
|
|
206
296
|
|
|
207
297
|
registerSkill({
|
|
208
298
|
id: 'slack', // referenced by `skills: ['slack']`
|
|
@@ -219,7 +309,7 @@ Then import it from your `graph.mjs` BEFORE building the graph:
|
|
|
219
309
|
|
|
220
310
|
```js
|
|
221
311
|
import './skills/slack.mjs'; // side-effect: registers the skill
|
|
222
|
-
import { WorkflowGraph } from '@zibby/
|
|
312
|
+
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
|
|
223
313
|
// ...
|
|
224
314
|
```
|
|
225
315
|
|
|
@@ -361,6 +451,7 @@ cleaned up too.
|
|
|
361
451
|
|
|
362
452
|
| Symptom | Fix |
|
|
363
453
|
|--------------------------------------------------|------------------------------------------------|
|
|
454
|
+
| `No WorkflowAgent class found in graph.mjs` | Class name in `graph.mjs` must match `entryClass` in `workflow.json`. Or export a class that `extends WorkflowAgent` from `@zibby/core`. |
|
|
364
455
|
| `Node 'X' must define outputSchema` | Add `outputSchema: z.object({...})` to the node config |
|
|
365
456
|
| `Skill 'foo' not registered` | Import the skill file in graph.mjs BEFORE `new WorkflowGraph()` |
|
|
366
457
|
| `state.previousNode is undefined` in a prompt | Wrong order — add `graph.addEdge('previousNode', 'thisNode')` |
|
|
@@ -369,6 +460,9 @@ cleaned up too.
|
|
|
369
460
|
| Zod error: "Expected string, received undefined" | The previous node's outputSchema doesn't match its return — fix the producer, not the consumer |
|
|
370
461
|
| `workflow trigger` works but `run` doesn't | Local run reads env from `.env` / shell; cloud reads from `zibby workflow env`. Set both. |
|
|
371
462
|
| Hangs forever on a node | Add `retries: 0` to fail fast while debugging; check the prompt isn't asking the agent to wait |
|
|
463
|
+
| `Workflow "<name>" not found.` | Check `paths.workflows` in `.zibby.config.mjs` matches where you scaffolded. Default is `workflows/` at repo root. |
|
|
464
|
+
| Router returns a string that's not a registered node name | All possible return values must be either `'END'` or a name passed to `graph.addNode(...)` elsewhere. `validate` flags this as `graph-edge-to-unknown`. |
|
|
465
|
+
| Need a counter / accumulator across loop iterations | Put it in the looping node's own outputSchema. Read prior value via `state.<nodeName>?.<field>` inside `execute()`, return new value. See §3 retry-loop example. |
|
|
372
466
|
|
|
373
467
|
---
|
|
374
468
|
|
|
@@ -399,27 +493,49 @@ Read `.claude/commands/` for slash commands the user can invoke:
|
|
|
399
493
|
## 9. Quick reference
|
|
400
494
|
|
|
401
495
|
```js
|
|
402
|
-
|
|
403
|
-
import {
|
|
404
|
-
|
|
405
|
-
//
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
//
|
|
416
|
-
|
|
417
|
-
|
|
496
|
+
// Imports — @zibby/core re-exports everything you need.
|
|
497
|
+
import {
|
|
498
|
+
WorkflowAgent, // base class your workflow extends
|
|
499
|
+
WorkflowGraph, // construct + wire nodes inside buildGraph()
|
|
500
|
+
registerSkill, // for custom MCP tool bundles
|
|
501
|
+
registerStrategy, AgentStrategy, // for custom LLM strategies (rare)
|
|
502
|
+
z, // re-exported Zod for schemas
|
|
503
|
+
} from '@zibby/core';
|
|
504
|
+
|
|
505
|
+
// Workflow shell (production form — what run/start/deploy expect):
|
|
506
|
+
export class MyWorkflow extends WorkflowAgent {
|
|
507
|
+
buildGraph() {
|
|
508
|
+
const graph = new WorkflowGraph();
|
|
509
|
+
graph.setStateSchema(myStateSchema); // from ./state.js (optional)
|
|
510
|
+
graph.addNode(name, { prompt, outputSchema, execute, skills, agent, retries });
|
|
511
|
+
graph.addEdge(from, to);
|
|
512
|
+
graph.addConditionalEdges(from, (state) =>
|
|
513
|
+
state.cond ? 'nextNodeName' : 'END' // 'END' = terminal sentinel
|
|
514
|
+
);
|
|
515
|
+
graph.setEntryPoint(name);
|
|
516
|
+
return graph;
|
|
517
|
+
}
|
|
518
|
+
async onComplete(result) { /* optional post-processing */ }
|
|
519
|
+
}
|
|
418
520
|
|
|
419
521
|
// State (inside execute / prompt fns)
|
|
420
522
|
// READ: state.someKey or state.previousNode.field
|
|
421
523
|
// WRITE: return { ... } from execute() — runtime puts it at state[nodeName]
|
|
422
524
|
```
|
|
423
525
|
|
|
424
|
-
|
|
425
|
-
|
|
526
|
+
**Two API surfaces** — when in doubt, use the class form:
|
|
527
|
+
|
|
528
|
+
- **Class form** (above) — what `zibby workflow new` scaffolds and what
|
|
529
|
+
`zibby workflow run/start/deploy` execute. Required for cloud
|
|
530
|
+
deployment. Use this for anything you might trigger remotely.
|
|
531
|
+
|
|
532
|
+
- **Function form** — `export default function buildGraph() { return new
|
|
533
|
+
WorkflowGraph()... }`. Works for local `validate` + the standalone
|
|
534
|
+
`@zibby/agent-workflow` library (the underlying graph runtime), but
|
|
535
|
+
NOT for `run`/`deploy` (they look for the class). Useful for one-off
|
|
536
|
+
local scripts that import the graph runtime directly. **For Zibby
|
|
537
|
+
workflows, always use the class form** — import from `@zibby/core`,
|
|
538
|
+
which re-exports everything you need.
|
|
539
|
+
|
|
540
|
+
`zibby workflow validate` accepts both shapes. Other commands need the
|
|
541
|
+
class.
|
|
@@ -18,31 +18,43 @@ The user wants to add a custom skill (MCP tool bundle) to a workflow.
|
|
|
18
18
|
- If unsure, ask: "Which MCP server should this skill wrap, or
|
|
19
19
|
should it be a JS-only middleware?"
|
|
20
20
|
|
|
21
|
-
2. **Find the workflow**
|
|
22
|
-
`
|
|
21
|
+
2. **Find the workflow** under your project's workflows path —
|
|
22
|
+
`workflows/<name>/` by default, or whatever `paths.workflows` is
|
|
23
|
+
set to in `.zibby.config.mjs`. (Legacy projects may still use
|
|
24
|
+
`.zibby/workflows/`.) Create a `skills/` subfolder if it doesn't
|
|
25
|
+
exist.
|
|
23
26
|
|
|
24
27
|
3. **Write `skills/<id>.mjs`:**
|
|
25
28
|
|
|
26
29
|
```js
|
|
27
|
-
import { registerSkill } from '@zibby/
|
|
30
|
+
import { registerSkill } from '@zibby/core';
|
|
28
31
|
|
|
29
32
|
registerSkill({
|
|
30
33
|
id: 'slack', // referenced by node `skills: ['slack']`
|
|
31
34
|
serverName: 'slack-mcp',
|
|
32
35
|
command: 'npx',
|
|
33
36
|
args: ['-y', '@modelcontextprotocol/server-slack'],
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
// Convention: 'mcp__<short-server-name>__*' matches every tool the
|
|
38
|
+
// MCP server exposes. Use the short name from the npm package
|
|
39
|
+
// (e.g. server-filesystem → 'mcp__filesystem__*').
|
|
40
|
+
allowedTools: ['mcp__slack__*'],
|
|
41
|
+
envKeys: ['SLACK_BOT_TOKEN'], // optional — env required to run
|
|
42
|
+
description: 'Read channels, post messages, search history', // optional
|
|
37
43
|
});
|
|
38
44
|
```
|
|
39
45
|
|
|
46
|
+
**MCP-server-specific args:** some servers need extra positional
|
|
47
|
+
arguments. `@modelcontextprotocol/server-filesystem` requires the
|
|
48
|
+
allowed paths as args, e.g.
|
|
49
|
+
`args: ['-y', '@modelcontextprotocol/server-filesystem', process.cwd()]`.
|
|
50
|
+
Check the server's npm page for required args.
|
|
51
|
+
|
|
40
52
|
4. **Import the skill file from `graph.mjs`** at the TOP, before
|
|
41
53
|
`new WorkflowGraph()`:
|
|
42
54
|
|
|
43
55
|
```js
|
|
44
56
|
import './skills/slack.mjs'; // side-effect: registers the skill
|
|
45
|
-
import { WorkflowGraph } from '@zibby/
|
|
57
|
+
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
|
|
46
58
|
// ...
|
|
47
59
|
```
|
|
48
60
|
|