@zibby/skills 0.2.23 → 0.2.24

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.
Files changed (92) hide show
  1. package/dist/browser.d.ts +0 -17
  2. package/dist/browser.js +3 -3
  3. package/dist/code-scan.d.ts +1 -1
  4. package/dist/index.d.ts +0 -1
  5. package/dist/index.js +97 -97
  6. package/dist/jira.d.ts +75 -9
  7. package/dist/jira.js +4 -4
  8. package/dist/package.json +1 -1
  9. package/dist/report.d.ts +51 -276
  10. package/dist/trackers/index.js +12 -12
  11. package/dist/trackers/jira-adapter.js +11 -11
  12. package/package.json +1 -1
  13. package/docs/analysis.md +0 -109
  14. package/docs/apps/agent-ops.md +0 -130
  15. package/docs/apps/auth.md +0 -158
  16. package/docs/apps/deploy.md +0 -207
  17. package/docs/apps/goal-mode.md +0 -175
  18. package/docs/apps/index.md +0 -149
  19. package/docs/apps/managing.md +0 -121
  20. package/docs/cli-reference.md +0 -494
  21. package/docs/cloning-repositories.md +0 -285
  22. package/docs/cloud/bundles.md +0 -92
  23. package/docs/cloud/dedicated-egress.md +0 -140
  24. package/docs/cloud/editing-prompts.md +0 -79
  25. package/docs/cloud/env-vars.md +0 -144
  26. package/docs/cloud/limits.md +0 -81
  27. package/docs/cloud/logs.md +0 -104
  28. package/docs/cloud/triggering.md +0 -128
  29. package/docs/concepts/agents.md +0 -112
  30. package/docs/concepts/designing-agents.md +0 -247
  31. package/docs/concepts/graph.md +0 -83
  32. package/docs/concepts/sessions.md +0 -70
  33. package/docs/concepts/skills.md +0 -86
  34. package/docs/concepts/state.md +0 -106
  35. package/docs/concepts/sub-graphs.md +0 -227
  36. package/docs/custom-workflows.md +0 -358
  37. package/docs/get-started/deploy.md +0 -75
  38. package/docs/get-started/install.md +0 -60
  39. package/docs/get-started/run-locally.md +0 -94
  40. package/docs/get-started/trigger-and-logs.md +0 -90
  41. package/docs/get-started/use-from-agents.md +0 -153
  42. package/docs/get-started/your-first-workflow.md +0 -69
  43. package/docs/getting-started.md +0 -108
  44. package/docs/installation.md +0 -127
  45. package/docs/integrations/github.md +0 -73
  46. package/docs/integrations/gitlab.md +0 -43
  47. package/docs/integrations/jira.md +0 -71
  48. package/docs/integrations/lark.md +0 -41
  49. package/docs/integrations/linear.md +0 -43
  50. package/docs/integrations/notion.md +0 -33
  51. package/docs/integrations/plane.md +0 -46
  52. package/docs/integrations/sentry.md +0 -42
  53. package/docs/integrations/slack.md +0 -33
  54. package/docs/intro.md +0 -86
  55. package/docs/legacy/test-automation.md +0 -111
  56. package/docs/packages/agent-workflow.md +0 -88
  57. package/docs/packages/cli.md +0 -73
  58. package/docs/packages/core.md +0 -72
  59. package/docs/packages/mcp-browser.md +0 -110
  60. package/docs/packages/mcp-cli.md +0 -176
  61. package/docs/packages/memory.md +0 -223
  62. package/docs/packages/skills.md +0 -218
  63. package/docs/packages/ui-memory.md +0 -245
  64. package/docs/recipes/bug-autofix.md +0 -85
  65. package/docs/recipes/github-ai-scout.md +0 -61
  66. package/docs/recipes/index.md +0 -68
  67. package/docs/recipes/pipeline-supervisor.md +0 -57
  68. package/docs/recipes/sentry-triage.md +0 -93
  69. package/docs/recipes/test.md +0 -190
  70. package/docs/reviewing-results.md +0 -114
  71. package/docs/running-tests.md +0 -134
  72. package/docs/self-host/backup-restore.md +0 -53
  73. package/docs/self-host/custom-sidecars.md +0 -136
  74. package/docs/self-host/index.md +0 -75
  75. package/docs/self-host/storage.md +0 -50
  76. package/docs/self-host/troubleshooting.md +0 -48
  77. package/docs/self-host/upgrade.md +0 -81
  78. package/docs/skills/browser.md +0 -97
  79. package/docs/skills/chat-memory.md +0 -122
  80. package/docs/skills/core-tools.md +0 -80
  81. package/docs/skills/function-skill.md +0 -93
  82. package/docs/skills/github.md +0 -91
  83. package/docs/skills/index.md +0 -46
  84. package/docs/skills/jira.md +0 -99
  85. package/docs/skills/lark.md +0 -85
  86. package/docs/skills/memory.md +0 -92
  87. package/docs/skills/sentry.md +0 -80
  88. package/docs/skills/slack.md +0 -89
  89. package/docs/tests/memory.md +0 -131
  90. package/docs/triggering-workflows.md +0 -552
  91. package/docs/workflow-artifact-layout-evaluation.md +0 -119
  92. package/docs/workflow.md +0 -558
@@ -1,247 +0,0 @@
1
- ---
2
- sidebar_position: 7
3
- title: Designing agents
4
- ---
5
-
6
- # Designing agents
7
-
8
- Before you build an agent, learn the three patterns that make Zibby agents
9
- powerful. Almost every good agent is some combination of them:
10
-
11
- 1. **One agent, many entry points** — a single deployed agent that behaves
12
- differently depending on *how* it was triggered (cron vs. a human vs. a
13
- webhook), selected by a code **router** node.
14
- 2. **Zero-LLM code nodes that read & write Stores** — deterministic work
15
- (fetch, ETL, aggregate, persist) runs as plain code, no model call, and can
16
- read/write a Store (SQLite / file / dataset) directly.
17
- 3. **Event-driven agents** — one agent subscribes to several events (PR opened,
18
- comment, `@mention`) and routes each to the right branch.
19
-
20
- These are not exotic. The shipped **sentry-triage** and **engineering-insights**
21
- agents use all three at once. Study them — they are the canonical reference for
22
- "how an agent should be designed."
23
-
24
- :::tip Design rule
25
- **Don't reach for an LLM node when plain code will do.** A model call costs
26
- tokens, adds latency, and is non-deterministic. Fetching data, transforming
27
- JSON, running a query, writing to a Store, calling an API — all of that belongs
28
- in a **code node**. Reserve LLM nodes for judgment: classify, summarize, decide.
29
- :::
30
-
31
- ---
32
-
33
- ## Pattern 1 — one agent, many entry points
34
-
35
- The trap beginners fall into is building *two* agents: a "collector" that runs on
36
- a schedule and a "reporter" a human triggers. Don't. Build **one** agent whose
37
- **first node is a code router** that inspects the trigger and sends the run down
38
- the right branch. One bundle, one deployment, one set of env/Store bindings.
39
-
40
- ```js
41
- import { WorkflowGraph } from '@zibby/agent-workflow';
42
- import { z } from '@zibby/core';
43
-
44
- // The router is PURE CODE — no model call. It reads the trigger input off state
45
- // and returns the name of the next node.
46
- function route(state) {
47
- // state.trigger / state.mode / state.action come from the trigger payload,
48
- // spread onto the initial state by the runner.
49
- return state?.trigger === 'fix' ? 'fix_intake' : 'fetch_issues';
50
- }
51
-
52
- const graph = new WorkflowGraph()
53
- .addNode('route', {
54
- _isCustomCode: true,
55
- description: 'Routes on the trigger: "fix" runs the single-issue auto-fix branch, anything else runs the scheduled triage branch.',
56
- outputSchema: z.object({}).passthrough(),
57
- execute: async (ctx) => ctx.state.getAll(), // router only decides an edge; it forwards state
58
- })
59
- // …branch nodes…
60
- .setEntryPoint('route')
61
- // A conditional edge: run `route`, then take the edge its return value names.
62
- .addConditionalEdges('route', route, {
63
- labels: { fix_intake: 'auto-fix one issue', fetch_issues: 'scheduled triage' },
64
- });
65
- ```
66
-
67
- The **same agent** then gets three trigger sources, all landing on `route`:
68
-
69
- | Trigger | Payload | Branch it takes |
70
- |---|---|---|
71
- | Cron (nightly) | `{ mode: 'collect' }` | collect → persist to Store |
72
- | Human `zibby agent trigger` | `{ action: 'report' }` | read Store → generate → deliver |
73
- | Webhook / Lark `@bot` | `{ trigger: 'fix', issueId }` | single-item fix branch |
74
-
75
- > **Real reference:** `sentry-triage` routes `state.trigger === 'fix'` to an
76
- > auto-fix branch and everything else (incl. an absent trigger — the nightly
77
- > cron) to scheduled triage. `engineering-insights` routes `state.action`
78
- > between `collect` (meter commits → SQLite) and `report` (aggregate → charts).
79
-
80
- ---
81
-
82
- ## Pattern 2 — code nodes read & write Stores (no LLM)
83
-
84
- A **code node** (`_isCustomCode: true` + an `execute` function) is a first-class
85
- citizen. It runs in the same container as the rest of the graph and has the same
86
- environment — including any **Store** bound to the node. So a deterministic node
87
- can fetch from a Store, transform, and write results back, entirely without a
88
- model.
89
-
90
- ### How a node reaches a Store
91
-
92
- When you bind a Store to a node (via `zibby_set_node_stores`, the deploy modal,
93
- or the CLI), the runtime injects three env vars into that node's container:
94
-
95
- | Env var | What it is |
96
- |---|---|
97
- | `ZIBBY_STORE__<name>` | the **storeId** of the Store you bound as `<name>` |
98
- | `ZIBBY_ACCOUNT_API_URL` | the base URL of the datasets API |
99
- | `PROJECT_API_TOKEN` | a project-scoped bearer token |
100
-
101
- The node just `fetch`es the datasets API. No SDK required.
102
-
103
- ```js
104
- // A pure-code node: read from a SQLite Store, write to a file Store. Zero LLM.
105
- const BASE = process.env.ZIBBY_ACCOUNT_API_URL;
106
- const TOKEN = process.env.PROJECT_API_TOKEN;
107
-
108
- async function ds(storeId, action, body) {
109
- const r = await fetch(`${BASE}/datasets/stores/${encodeURIComponent(storeId)}/${action}`, {
110
- method: 'POST',
111
- headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${TOKEN}` },
112
- body: JSON.stringify(body),
113
- });
114
- if (!r.ok) throw new Error(`${action} ${r.status}: ${await r.text()}`);
115
- return r.json();
116
- }
117
-
118
- graph.addNode('build_report', {
119
- _isCustomCode: true,
120
- description: 'Reads metrics from a SQLite Store, writes the rendered report to a file Store. Pure code — no model call.',
121
- outputSchema: z.object({ topDevs: z.array(z.any()), wrote: z.boolean() }),
122
- execute: async (ctx) => {
123
- const state = ctx.state.getAll();
124
- const metricsDb = process.env.ZIBBY_STORE__metrics_db; // a sqlite Store
125
- const reportFile = process.env.ZIBBY_STORE__report_files; // a file Store
126
-
127
- // ── SQLite READ ── (readOnly:true → read-only guard)
128
- const q = await ds(metricsDb, 'sql', {
129
- sql: 'SELECT dev, added FROM commits WHERE added > ? ORDER BY added DESC LIMIT 10',
130
- params: [100],
131
- readOnly: true,
132
- });
133
- // q = { columns: ['dev','added'], rows: [['alice', 420], …] }
134
-
135
- // ── SQLite WRITE ── (bound params; a brand-new table is created STRICT)
136
- await ds(metricsDb, 'sql', {
137
- sql: 'INSERT INTO runs (ran_at, top_dev) VALUES (?, ?)',
138
- params: [state.ranAt, q.rows[0]?.[0] ?? null],
139
- });
140
-
141
- // ── file WRITE / READ ──
142
- await ds(reportFile, 'put', { path: 'report.html', content: state.html });
143
- const tpl = await ds(reportFile, 'get', { path: 'template.html' });
144
-
145
- return { topDevs: q.rows, wrote: true };
146
- },
147
- });
148
- ```
149
-
150
- ### The Store data actions
151
-
152
- | Store type | Actions | Notes |
153
- |---|---|---|
154
- | `sqlite` | `sql` `{ sql, params?, readOnly? }` | a real relational DB — tables, joins, UPDATEs. `readOnly: true` refuses any write. New tables are created **STRICT** so a column's declared type is enforced. |
155
- | `file` | `put` / `get` / `list` / `delete` `{ path, content? }` | arbitrary blobs by relative path — good for whole-JSON inputs and rendered outputs. |
156
- | `dataset` | `append` `{ record }` / `query` `{ select?, where?, … }` | append-only records + SQL-style aggregation — good for raw event streams you later `count`/`sum`/`group`. |
157
-
158
- :::tip Choosing a Store type by data shape
159
- - **Whole-blob** (a JSON file a script reads with `readFileSync`, a rendered
160
- report) → **file**.
161
- - **Rows you dedupe / update / join** (git commits keyed by SHA) → **sqlite**.
162
- - **Append-only events you aggregate later** (MRs, worklogs) → **dataset**.
163
- - **A tiny cursor** ("where did I get to last time") → **kv-memory** (built into
164
- every agent, not a registry Store).
165
- :::
166
-
167
- ### File-store size limit — and how to handle big files
168
-
169
- A `file` store's per-file cap is **4 MiB of raw bytes** (the content travels
170
- base64-inside-JSON through the API). Two ways past it:
171
-
172
- - **A relational/append-only shape? Use `sqlite`/`dataset`, not `file`.** Those
173
- aren't whole-file uploads — they take rows/records, so the 4 MiB blob cap
174
- doesn't apply. This is the right move for raw event data (commits, worklogs).
175
- - **A genuinely large blob** (a multi-MB `.jsonl` a script must read whole)?
176
- **gzip it, transparently.** Text compresses well; store the compressed bytes
177
- and decompress on read, so the generating script still sees the original file:
178
-
179
- ```js
180
- // write node: gzip + base64 → store as <name>.gz.b64
181
- import { gzipSync } from 'node:zlib';
182
- const packed = gzipSync(Buffer.from(rawText)).toString('base64'); // 5.6MB → ~0.8MB
183
- await ds(fileStore, 'put', { path: 'big.jsonl.gz.b64', content: packed });
184
-
185
- // read node: fetch → base64-decode → gunzip → original file on disk
186
- import { gunzipSync } from 'node:zlib';
187
- const got = await ds(fileStore, 'get', { path: 'big.jsonl.gz.b64' });
188
- const raw = gunzipSync(Buffer.from(got.content, 'base64')).toString('utf8');
189
- ```
190
-
191
- Keep the list of "which files are compressed" as data (one array), so adding a
192
- big file later is a one-line change — the read/write nodes stay generic.
193
-
194
- :::caution Check every dependency's SIZE up front
195
- When you list a store's inputs, don't glob one extension and assume — a single
196
- oversized dependency (e.g. a 5.6 MB `.jsonl` hiding among small `.json`s) is what
197
- trips the cap. Enumerate the real files + their bytes before wiring the store.
198
- File paths accept Unicode (`报告.html` is fine); only `/ \ : ? # [ ] " < > | *`
199
- and control characters are rejected.
200
- :::
201
-
202
- ### Debugging a self-host run when logs look truncated
203
-
204
- Self-host truncates long per-node step logs, so a node can report `success`
205
- while a downstream step silently failed (you see all-green but empty output).
206
- The robust pattern: **have each node persist its own full result JSON into an
207
- output store** (e.g. `diag/<node>.json`) — best-effort, never throwing, never
208
- changing the node's return value. Then read the diagnostics back with
209
- `zibby_store_peek` to see exactly what each node saw (staged files + sizes,
210
- stderr, the real error). This turns a silent all-green failure into a precise
211
- root cause.
212
-
213
- ---
214
-
215
- ## Pattern 3 — event-driven agents
216
-
217
- A single agent can subscribe to **several** events and handle each differently.
218
- The shipped **github-code-review** / **gitlab-code-review** agents are one agent
219
- that reacts to *PR opened*, *new commits pushed*, *a comment reply*, and an
220
- *`@mention`* — the entry router branches on which event fired. The owner even
221
- chooses **which** events trigger a review (cost control), because the whole thing
222
- is just an event-subscription subset feeding the same router.
223
-
224
- The takeaway: **you rarely need a second agent for a new trigger.** Add an event
225
- to the subscription, add a branch, extend the router.
226
-
227
- ---
228
-
229
- ## Putting it together — a design checklist
230
-
231
- When you design a new agent, ask:
232
-
233
- 1. **How many ways will this be triggered?** More than one → **Pattern 1**: a
234
- code `route` node at the entry, one agent, branches per scenario.
235
- 2. **What here is deterministic?** Fetch / transform / query / persist → **code
236
- nodes** (Pattern 2), not LLM nodes. Bind the Stores those nodes need.
237
- 3. **What actually needs judgment?** Only *those* steps get an LLM node
238
- (`agent: 'claude' | 'codex' | …` + a `prompt` + an `outputSchema`).
239
- 4. **Where does the data live?** Pick a Store type per data shape (blob → file,
240
- relational → sqlite, append-only → dataset; cursor → kv-memory).
241
- 5. **Is the split write-side vs. read-side?** Keep it **one agent** — a nightly
242
- `collect` branch that writes the Store and an on-demand `report` branch that
243
- reads it, selected by the router. Not two agents.
244
-
245
- Then read the source of **sentry-triage** and **engineering-insights** and map
246
- each pattern onto what you see. That is the fastest way to learn what Zibby can
247
- do.
@@ -1,83 +0,0 @@
1
- ---
2
- sidebar_position: 1
3
- title: Graph & nodes
4
- ---
5
-
6
- # Graph & nodes
7
-
8
- A **graph** is a directed graph of agent invocations. You declare it in code:
9
-
10
- ```js
11
- import { WorkflowGraph } from '@zibby/agent-workflow';
12
- import { z } from '@zibby/core';
13
-
14
- const graph = new WorkflowGraph()
15
- .addNode('plan', { prompt: 'List 3 tasks for: {{goal}}', outputSchema: Plan, agent: 'claude' })
16
- .addNode('execute', { prompt: 'Do task: {{task}}', outputSchema: Done, agent: 'cursor' })
17
- .addNode('verify', { prompt: 'Verify: {{result}}', outputSchema: Status, agent: 'codex' })
18
- .addEdge('plan', 'execute')
19
- .addEdge('execute', 'verify')
20
- .setEntryPoint('plan');
21
- ```
22
-
23
- ## Node config
24
-
25
- Every node accepts:
26
-
27
- | Field | Required | Description |
28
- |---|---|---|
29
- | `prompt` | yes | Either a string template (`{{state.X}}` interpolated) or a function `({ input, state }) => string`. |
30
- | `outputSchema` | yes | A Zod schema. The node's output is validated against this before downstream nodes see it. Validation failure = node failure. |
31
- | `agent` | no | Agent strategy override: `'cursor' | 'claude' | 'codex' | 'gemini' | 'assistant'`. Falls back to project default if omitted. |
32
- | `retries` | no | Number of times to retry on failure (default: 0). |
33
- | `skills` | no | Array of skill IDs to enable for this node — see [Skills](./skills). |
34
- | `onComplete` | no | Async callback invoked with the node's validated output. |
35
-
36
- ## Edges
37
-
38
- Three forms:
39
-
40
- ```js
41
- // Linear: A → B
42
- graph.addEdge('A', 'B');
43
-
44
- // Conditional branching: state-driven routing
45
- graph.addConditionalEdges('classify', (state) => {
46
- if (state.classify.severity === 'critical') return 'pageOncall';
47
- if (state.classify.severity === 'high') return 'createIncident';
48
- return 'logAndExit';
49
- });
50
-
51
- // Multi-conditional with named labels (cleaner for many branches)
52
- graph.addConditionalEdges('classify', {
53
- routes: (state) => state.classify.severity,
54
- labels: { critical: 'pageOncall', high: 'createIncident', _default: 'logAndExit' },
55
- });
56
- ```
57
-
58
- ## Entry point
59
-
60
- Exactly one node is the entry point:
61
-
62
- ```js
63
- graph.setEntryPoint('plan');
64
- ```
65
-
66
- This is the node that runs first when `graph.run(initialState)` is called.
67
-
68
- ## Running
69
-
70
- ```js
71
- const { state } = await graph.run(agent, {
72
- input: { goal: 'add a dark-mode toggle' },
73
- agentType: 'cursor',
74
- });
75
-
76
- console.log(state.verify.status);
77
- ```
78
-
79
- Each node's output lands at `state[nodeName]`, so downstream prompts can reference `{{state.plan.tasks}}` or `state.plan.tasks` from a function prompt.
80
-
81
- ## State immutability
82
-
83
- State is append-only — every node-completion creates a new state object. Earlier values are preserved in the state's `_history` array, accessible via `state.rollback(N)` for retries that need to drop the latest N writes.
@@ -1,70 +0,0 @@
1
- ---
2
- sidebar_position: 5
3
- title: Sessions & artifacts
4
- ---
5
-
6
- # Sessions & artifacts
7
-
8
- Every agent run — local or cloud — produces a **session folder** under `.zibby/output/sessions/<sessionId>/`. The folder is the canonical record of what happened.
9
-
10
- ## What's inside
11
-
12
- ```
13
- .zibby/output/sessions/1777678254943_ymcw/
14
- ├── result.json # final structured output (Zod-validated)
15
- ├── raw_stream_output.txt # every byte the agent emitted, raw
16
- ├── events.json # JSONL execution log: per-node start/end, validation, retries
17
- ├── .session-info.json # session metadata (start time, agent, model, workflow type)
18
- └── nodes/
19
- ├── plan/
20
- │ ├── prompt.txt # exact prompt sent to the agent
21
- │ ├── raw_output.txt # what the agent returned, pre-validation
22
- │ └── result.json # the validated output
23
- ├── implement/...
24
- └── verify/...
25
- ```
26
-
27
- ## Session IDs
28
-
29
- Format: `<unix_ms>_<random>`. Generated when the graph starts running.
30
-
31
- You can override via:
32
-
33
- ```bash
34
- zibby agent run my-agent --session 1777678254943_ymcw
35
- ```
36
-
37
- Useful for replay — re-run from a saved input/state without re-paying earlier nodes.
38
-
39
- ## Replay a session
40
-
41
- ```bash
42
- zibby agent run my-agent \
43
- --session 1777678254943_ymcw \
44
- --node verify # only re-run the 'verify' node
45
- ```
46
-
47
- The graph reads `state.plan` and `state.implement` from the saved session and only invokes `verify` again. Cheap iteration on the last node when debugging.
48
-
49
- ## Cloud sessions
50
-
51
- Cloud runs land in the same `.zibby/output/sessions/` layout, just inside the ECS container's `/workspace/`. The session folder is uploaded to S3 at the end of each run; `zibby agent logs <uuid> -t` streams CloudWatch logs in real time.
52
-
53
- To download a finished cloud session locally:
54
-
55
- ```bash
56
- zibby agent download <uuid>
57
- ```
58
-
59
- This pulls the agent source (so you can edit and redeploy) plus the most recent execution's session folder.
60
-
61
- ## Studio integration
62
-
63
- [Zibby Studio](https://zibby.app) is a desktop UI that watches `.zibby/output/sessions/`. Anything that writes a session folder shows up in Studio automatically — pin a session, watch state evolve live, or stop an agent from the Stop button.
64
-
65
- The protocol is documented and stable:
66
-
67
- - `__WORKFLOW_GRAPH_LOG__` markers in stdout signal node begin/end events
68
- - `.zibby-studio-stop` file is the kill switch — Studio writes it, the runtime checks for it between nodes
69
- - `ZIBBY_RUN_SOURCE=studio` env var tells the runtime "you were spawned by Studio"
70
- - `stoppedByStudio: true` returned from `graph.run()` when the kill switch fired
@@ -1,86 +0,0 @@
1
- ---
2
- sidebar_position: 4
3
- title: Skills
4
- ---
5
-
6
- # Skills
7
-
8
- A **skill** is a named bundle of MCP tools (and optional prompt fragments) that a node can opt into. Skills let you compose tool access per-node without giving every node every tool.
9
-
10
- See also: [Skills reference](../skills/index.md) for per-skill docs (tools, setup, code samples).
11
-
12
- ## Built-in skills
13
-
14
- `@zibby/skills` ships these:
15
-
16
- | Skill ID | What it adds |
17
- |---|---|
18
- | `browser` | Playwright MCP — browse, click, fill, screenshot |
19
- | `github` | GitHub MCP — issues, PRs, file edits, branches |
20
- | `jira` | Jira MCP — tickets, comments, transitions |
21
- | `slack` | Slack MCP — read/write channels, threads, DMs |
22
- | `memory` | Test memory database — version-controlled (Dolt) knowledge from prior runs |
23
-
24
- ## Enabling on a node
25
-
26
- ```js
27
- import { registerSkill } from '@zibby/agent-workflow';
28
- import { browserSkill } from '@zibby/skills';
29
-
30
- registerSkill(browserSkill);
31
-
32
- graph.addNode('research', {
33
- prompt: 'Find pricing for {{input.product}}',
34
- outputSchema: Price,
35
- agent: 'cursor',
36
- skills: ['browser'],
37
- });
38
- ```
39
-
40
- Two effects:
41
- 1. The agent gets the Browser MCP tools at this node only.
42
- 2. The skill's prompt fragment (telling the agent how to use the tools) is appended to the prompt.
43
-
44
- ## Custom skills
45
-
46
- Implement the `Skill` shape:
47
-
48
- ```js
49
- import { registerSkill } from '@zibby/agent-workflow';
50
-
51
- registerSkill({
52
- id: 'pdf',
53
- serverName: 'pdf-mcp',
54
- tools: ['pdfExtract', 'pdfRender'],
55
- promptFragment: 'When working with PDFs, use the pdfExtract tool first.',
56
- });
57
- ```
58
-
59
- Now any node can opt in via `skills: ['pdf']`.
60
-
61
- ## Why per-node, not per-graph?
62
-
63
- Two reasons:
64
-
65
- - **Tool scoping** — a node that's planning shouldn't have Slack write access; a node that's posting status shouldn't have file-write tools. Per-node skills give you least-privilege automatically.
66
- - **Prompt size** — every enabled skill adds prompt fragments. Don't pay for tools you won't use.
67
-
68
- ## Function skills (no MCP server needed)
69
-
70
- For lightweight cases, register a function skill:
71
-
72
- ```js
73
- import { registerSkill } from '@zibby/agent-workflow';
74
-
75
- registerSkill({
76
- id: 'pricing',
77
- type: 'function',
78
- fn: async ({ product }) => {
79
- const r = await fetch(`https://my.api/price?p=${product}`);
80
- return r.json();
81
- },
82
- description: 'Look up product pricing.',
83
- });
84
- ```
85
-
86
- The agent sees `pricing` as a callable tool, gets your function's return value back as a tool result. No MCP server to spin up.
@@ -1,106 +0,0 @@
1
- ---
2
- sidebar_position: 3
3
- title: State & schema
4
- ---
5
-
6
- # State & schema
7
-
8
- Every agent run carries one **state** object that flows from node to node. State is the only handoff mechanism — there's no shared globals, no hidden context.
9
-
10
- ## Shape
11
-
12
- ```js
13
- state = {
14
- input: { /* whatever the trigger passed */ },
15
- plan: { tasks: ['t1', 't2'] }, // ← from node 'plan'
16
- implement: { diff: '...' }, // ← from node 'implement'
17
- verify: { status: 'ok' }, // ← from node 'verify'
18
- }
19
- ```
20
-
21
- When a node completes, its validated output lands at `state[nodeName]`.
22
-
23
- ## Schema-validated handoff
24
-
25
- Every node's `outputSchema` is a Zod schema. It runs *before* downstream nodes see the output:
26
-
27
- ```js
28
- import { z } from '@zibby/core';
29
-
30
- const Plan = z.object({
31
- tasks: z.array(z.string()).min(1),
32
- priority: z.enum(['low', 'normal', 'high']),
33
- });
34
-
35
- graph.addNode('plan', {
36
- prompt: 'Triage this ticket.',
37
- outputSchema: Plan,
38
- agent: 'claude',
39
- });
40
- ```
41
-
42
- If the agent returns malformed output, the node fails. Combined with `retries: N`, you get cheap automatic recovery from one-off LLM hallucinations.
43
-
44
- Downstream nodes can reference plan output directly:
45
-
46
- ```js
47
- graph.addNode('implement', {
48
- prompt: ({ state }) => `Implement these tasks:\n${state.plan.tasks.map(t => `- ${t}`).join('\n')}`,
49
- outputSchema: Implementation,
50
- agent: 'cursor',
51
- });
52
- ```
53
-
54
- ## Function vs. template prompts
55
-
56
- Two prompt forms:
57
-
58
- ```js
59
- // String template — variables interpolated from state.
60
- graph.addNode('plan', { prompt: 'Plan: {{input.goal}}', ... });
61
-
62
- // Function — full programmatic control.
63
- graph.addNode('plan', {
64
- prompt: ({ input, state }) => {
65
- const ctx = state.context?.summary ?? '';
66
- return `Plan: ${input.goal}\n\nContext:\n${ctx}`;
67
- },
68
- ...
69
- });
70
- ```
71
-
72
- Functions get `{ input, state, getAll, get }` so they can introspect state without throwing on missing keys.
73
-
74
- ## Skill hints
75
-
76
- If a node opts into [skills](./skills), the framework appends prompt fragments that tell the agent how to use those tools. You don't write that boilerplate — register the skill once, list it on the node:
77
-
78
- ```js
79
- graph.addNode('search', {
80
- prompt: 'Find info about {{input.query}}',
81
- outputSchema: Results,
82
- agent: 'cursor',
83
- skills: ['browser'], // appends Browser MCP usage instructions to the prompt
84
- });
85
- ```
86
-
87
- ## Rollback
88
-
89
- State is history-tracked. To revert the last N writes (typical use case: a node validation passes Zod but the *content* is wrong, and you want to retry with extra instructions):
90
-
91
- ```js
92
- const recovered = state.rollback(2);
93
- ```
94
-
95
- `rollback` returns a fresh state with the last 2 writes dropped. Use it inside `onComplete` callbacks or custom retry logic.
96
-
97
- ## Reading state in templates
98
-
99
- Inside the `prompt` string, `{{x}}` resolves first against `state.x`, then against `input.x`. Dotted paths work:
100
-
101
- ```
102
- {{state.plan.tasks.length}}
103
- {{input.ticket}}
104
- ```
105
-
106
- Function prompts give you the same access without the templating layer.