@zibby/skills 0.1.23 → 0.1.25
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/github.js +3 -3
- package/dist/index.js +59 -59
- package/dist/integrations.js +1 -0
- package/dist/jira.js +6 -6
- package/dist/lark.js +2 -2
- package/dist/package.json +3 -2
- package/dist/sentry.js +2 -2
- package/dist/slack.js +2 -2
- package/docs/concepts/skills.md +2 -0
- package/docs/packages/skills.md +2 -0
- package/docs/skills/browser.md +97 -0
- package/docs/skills/chat-memory.md +93 -0
- package/docs/skills/core-tools.md +80 -0
- package/docs/skills/function-skill.md +93 -0
- package/docs/skills/github.md +91 -0
- package/docs/skills/index.md +42 -0
- package/docs/skills/jira.md +99 -0
- package/docs/skills/lark.md +85 -0
- package/docs/skills/memory.md +92 -0
- package/docs/skills/sentry.md +80 -0
- package/docs/skills/slack.md +84 -0
- package/package.json +3 -2
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 10
|
|
3
|
+
title: Function skill
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Function skill
|
|
7
|
+
|
|
8
|
+
Author a custom skill as a single async function. No MCP server to write, no spawn config, no glue — just a `handler({ args })` that returns a JSON-serializable result. Zibby auto-bridges it through MCP at runtime so any Claude/Cursor/Codex node can call it.
|
|
9
|
+
|
|
10
|
+
For wrapping a full external MCP server, use the same `skill()` factory with a `resolve()` instead of a `handler` — see the MCP skill example at the bottom.
|
|
11
|
+
|
|
12
|
+
## API
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import { skill } from '@zibby/skills';
|
|
16
|
+
|
|
17
|
+
export const myTool = skill('my_tool', {
|
|
18
|
+
description: 'One-line description shown to the model',
|
|
19
|
+
input: {
|
|
20
|
+
foo: 'string',
|
|
21
|
+
bar: { type: 'number', description: 'Optional knob', required: false },
|
|
22
|
+
},
|
|
23
|
+
handler: async ({ foo, bar = 0 }) => {
|
|
24
|
+
return { result: foo.repeat(bar) };
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
| Field | Type | Notes |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `description` | `string` | Shown to the LLM as the tool description |
|
|
32
|
+
| `input` | `Record<string, string \| { type, description?, required? }>` | Shorthand schema. String values mean `{ type: <string>, required: true }` |
|
|
33
|
+
| `handler` | `async (args) => any` | Return value is `JSON.stringify`'d back to the model |
|
|
34
|
+
|
|
35
|
+
Calling `skill()` both creates and **registers** the skill in the global registry, so just importing the module is enough to make it available.
|
|
36
|
+
|
|
37
|
+
## Use in a workflow
|
|
38
|
+
|
|
39
|
+
Once registered, reference by id:
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
|
|
43
|
+
import './skills/my-tool.js'; // import for the side effect
|
|
44
|
+
|
|
45
|
+
graph.addNode('process', {
|
|
46
|
+
agent: 'claude',
|
|
47
|
+
skills: ['my_tool'],
|
|
48
|
+
prompt: (state) => `Call my_tool with foo="hello", bar=3 and report what you got back.`,
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The tool surfaces to the model as `my_tool` (and to MCP-aware strategies as `mcp__my_tool__my_tool`).
|
|
53
|
+
|
|
54
|
+
## Output example
|
|
55
|
+
|
|
56
|
+
For the example above:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{ "result": "hellohellohello" }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
If `handler` throws, the error message is returned to the model as the tool result so it can recover or retry.
|
|
63
|
+
|
|
64
|
+
## Wrapping an external MCP server
|
|
65
|
+
|
|
66
|
+
Same factory, different shape — provide `resolve()` instead of `handler`:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
import { skill } from '@zibby/skills';
|
|
70
|
+
|
|
71
|
+
export const linear = skill('linear', {
|
|
72
|
+
description: 'Linear issue tracker',
|
|
73
|
+
serverName: 'linear',
|
|
74
|
+
allowedTools: ['mcp__linear__*'],
|
|
75
|
+
envKeys: ['LINEAR_API_KEY'],
|
|
76
|
+
resolve() {
|
|
77
|
+
if (!process.env.LINEAR_API_KEY) return null;
|
|
78
|
+
return {
|
|
79
|
+
command: 'npx',
|
|
80
|
+
args: ['-y', '@anthropic/linear-mcp-server'],
|
|
81
|
+
env: { LINEAR_API_KEY: process.env.LINEAR_API_KEY },
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Return `null` from `resolve()` to silently disable the skill when its prerequisites aren't met (no env var, no bin, etc.) — the node still runs, the model just doesn't see those tools.
|
|
88
|
+
|
|
89
|
+
## Implementation notes
|
|
90
|
+
|
|
91
|
+
Function skills route through a tiny stdio MCP bridge (`@zibby/core/function-bridge.js`) that the strategy spawns. The bridge imports your skill module, runs the handler in-process, and proxies the result back via MCP — so the agent SDK sees a real MCP server even though you didn't write one.
|
|
92
|
+
|
|
93
|
+
When you ship a skill in a published package, derive bin/module paths from `import.meta.url`, **not** `require.resolve('@your/pkg/...')`. esbuild emits a `dist/package.json` that makes package self-references resolve to `dist/...` instead of the package root, which silently breaks the lookup. Every Zibby-shipped skill (sentry, lark, jira) uses the `import.meta.url` pattern for this reason.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 4
|
|
3
|
+
title: GitHub
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GitHub skill
|
|
7
|
+
|
|
8
|
+
Read repos, list and inspect PRs, search code and issues, read files, clone repositories, create issues.
|
|
9
|
+
|
|
10
|
+
- **ID:** `github`
|
|
11
|
+
- **MCP server:** `github` (tools exposed as `mcp__github__*`)
|
|
12
|
+
- **Underlying server:** `npx @modelcontextprotocol/server-github@latest`
|
|
13
|
+
|
|
14
|
+
## Tools provided
|
|
15
|
+
|
|
16
|
+
| Tool | What it does |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `github_list_repos` | List accessible repos (private + public). Defaults to all installation repos when no `owner` given |
|
|
19
|
+
| `github_search_repos` | Substring search across accessible repo names and descriptions |
|
|
20
|
+
| `github_get_user` | Authenticated user (or GitHub App installation owner) |
|
|
21
|
+
| `github_list_orgs` | Organizations with accessible repos |
|
|
22
|
+
| `github_clone` | `git clone` a repo locally, defaulting to `~/zibby-repos/<repo>`; accepts `destination` |
|
|
23
|
+
| `github_get_file` | Read a file's content (or list a directory) at a `ref` |
|
|
24
|
+
| `github_list_commits` | Recent commits on a branch, optionally filtered by `path` |
|
|
25
|
+
| `github_get_commit` | Full commit details + per-file patches |
|
|
26
|
+
| `github_search_issues` | GitHub search-syntax across issues and PRs |
|
|
27
|
+
| `github_search_code` | Code search, optionally scoped to `repo` or `language` |
|
|
28
|
+
| `github_get_pr` | PR metadata (title, branch, stats) |
|
|
29
|
+
| `github_get_pr_diff` | Unified diff (capped at 15 KB) |
|
|
30
|
+
| `github_list_pr_files` | Files changed in a PR with per-file patches |
|
|
31
|
+
| `github_list_pr_comments` | Review + issue comments combined, sorted ascending |
|
|
32
|
+
| `github_create_issue` | Create a new issue |
|
|
33
|
+
|
|
34
|
+
## Setup
|
|
35
|
+
|
|
36
|
+
Two options:
|
|
37
|
+
|
|
38
|
+
**GitHub App (recommended).** In **Settings → Integrations**, click **Connect GitHub**, install the Zibby GitHub App on the orgs/repos you want, and authorize. Tokens auto-refresh; you don't manage them. See the [GitHub Integration page](../integrations/github.md) for the full app permissions list.
|
|
39
|
+
|
|
40
|
+
**Personal access token.** Set `GITHUB_TOKEN` in your workflow's env (locally) or via **Cloud → Env vars** (cloud runs). Scope `repo` for private read+write, `public_repo` for public-only.
|
|
41
|
+
|
|
42
|
+
The skill reads `envKeys: ['GITHUB_TOKEN']` and the official `@modelcontextprotocol/server-github` consumes it directly.
|
|
43
|
+
|
|
44
|
+
## Use in a workflow
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
|
|
48
|
+
import { SKILLS } from '@zibby/skills';
|
|
49
|
+
|
|
50
|
+
export class PrSummarizer extends WorkflowAgent {
|
|
51
|
+
buildGraph() {
|
|
52
|
+
const graph = new WorkflowGraph();
|
|
53
|
+
graph.addNode('summarize_pr', {
|
|
54
|
+
agent: 'claude',
|
|
55
|
+
skills: [SKILLS.GITHUB],
|
|
56
|
+
prompt: (state) => `Get PR #${state.prNumber} on ${state.owner}/${state.repo}.
|
|
57
|
+
Read the diff with github_get_pr_diff and list reviewer comments with
|
|
58
|
+
github_list_pr_comments. Produce a 4-bullet summary of what changed and any
|
|
59
|
+
open review threads.`,
|
|
60
|
+
});
|
|
61
|
+
return graph;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Output example
|
|
67
|
+
|
|
68
|
+
`github_get_pr`:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"number": 142,
|
|
73
|
+
"title": "fix(auth): refresh token on 401",
|
|
74
|
+
"state": "open",
|
|
75
|
+
"merged": false,
|
|
76
|
+
"user": "alice",
|
|
77
|
+
"branch": "fix-auth-refresh",
|
|
78
|
+
"base": "main",
|
|
79
|
+
"changedFiles": 3,
|
|
80
|
+
"additions": 47,
|
|
81
|
+
"deletions": 12,
|
|
82
|
+
"url": "https://github.com/acme/app/pull/142",
|
|
83
|
+
"labels": ["bug", "auth"]
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Implementation notes
|
|
88
|
+
|
|
89
|
+
`skill.resolve()` returns `{ command: 'npx', args: ['-y', '@modelcontextprotocol/server-github@latest'], env }` — the official upstream MCP server. The skill's `handleToolCall` implementation (with GitHub App-aware behavior for `/user` and `/user/orgs`) is used by the `assistant` agent strategy and by any caller that bypasses MCP.
|
|
90
|
+
|
|
91
|
+
For GitHub App installation tokens, `/user` and `/user/orgs` are server-to-server-forbidden; the in-process handlers transparently fall back to `/installation/repositories` and derive the owner/orgs from there.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 0
|
|
3
|
+
title: Skills reference
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skills reference
|
|
7
|
+
|
|
8
|
+
Per-skill reference for everything shipped in `@zibby/skills`. For the mental model and how to attach skills to nodes, see [Concepts → Skills](../concepts/skills.md). For the package-level API (`skill()` factory, `registerSkill`, middleware), see [`@zibby/skills`](../packages/skills.md).
|
|
9
|
+
|
|
10
|
+
## Built-in skills
|
|
11
|
+
|
|
12
|
+
| Skill | ID | Tools | Setup |
|
|
13
|
+
|---|---|---|---|
|
|
14
|
+
| [Browser](./browser.md) | `browser` | Playwright (navigate, click, type, snapshot, video) | None — bundled with `@zibby/cli` |
|
|
15
|
+
| [Sentry](./sentry.md) | `sentry` | `sentry_list_projects`, `sentry_list_issues`, `sentry_get_issue` | OAuth (PKCE) |
|
|
16
|
+
| [Lark](./lark.md) | `lark` | `lark_send_message`, `lark_reply`, `lark_list_chats`, `lark_get_chat_history` | App ID + App Secret |
|
|
17
|
+
| [GitHub](./github.md) | `github` | Repos, PRs, issues, commits, file reads, clone | GitHub App or `GITHUB_TOKEN` |
|
|
18
|
+
| [Slack](./slack.md) | `slack` | Channels, post/reply, reactions, history, users | Bot token + team ID |
|
|
19
|
+
| [Jira](./jira.md) | `jira` | Issues, sprints, comments, transitions | Atlassian OAuth |
|
|
20
|
+
| [Memory](./memory.md) | `memory` | Test history, selectors, page model (Dolt) | `zibby init --mem` |
|
|
21
|
+
| [Chat memory](./chat-memory.md) | `chat-memory` | `memory_store`, `memory_recall`, `memory_brief`, `task_log`, `task_history` | None (Dolt or mem0) |
|
|
22
|
+
| [Core tools](./core-tools.md) | `core-tools` | `read_file`, `write_file`, `list_directory`, `run_command`, `open_url`, `wait` | None |
|
|
23
|
+
| [Function skill](./function-skill.md) | n/a | Author-defined | Define with `skill()` |
|
|
24
|
+
|
|
25
|
+
## Attaching a skill
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
|
|
29
|
+
import { SKILLS } from '@zibby/skills';
|
|
30
|
+
|
|
31
|
+
graph.addNode('triage', {
|
|
32
|
+
agent: 'claude',
|
|
33
|
+
skills: [SKILLS.SENTRY, SKILLS.SLACK],
|
|
34
|
+
prompt: (state) => `Find unresolved Sentry issues and post a summary to #alerts.`,
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## MCP architecture in one paragraph
|
|
39
|
+
|
|
40
|
+
When a node executes, the strategy reads its `skills` array. For each skill it calls `skill.resolve(...)`, which returns either a stdio spawn spec (`{ command, args, env }`) or `null` for in-process skills. The Agent SDK launches each spec as an MCP server and exposes its tools to the LLM under the prefix `mcp__<serverName>__<tool>`. The skill's `promptFragment` is appended to the system prompt so the model knows the tools exist. In-process skills (`core-tools`, `chat-memory`, function skills with a `handler`) skip the spawn and the strategy dispatches tool calls directly via `handleToolCall`.
|
|
41
|
+
|
|
42
|
+
See [`@zibby/skills`](../packages/skills.md) for the full skill object shape and how to author your own.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 6
|
|
3
|
+
title: Jira
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Jira skill
|
|
7
|
+
|
|
8
|
+
Read/write Jira Cloud — search issues, manage sprints, transition tickets, manage comments. Backed by a custom Zibby MCP server (`@zibby/mcp-jira`).
|
|
9
|
+
|
|
10
|
+
- **ID:** `jira`
|
|
11
|
+
- **MCP server:** `jira` (tools exposed as `mcp__jira__*`)
|
|
12
|
+
|
|
13
|
+
## Tools provided
|
|
14
|
+
|
|
15
|
+
| Tool | What it does |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `jira_list_projects` | List all accessible Jira projects |
|
|
18
|
+
| `jira_list_statuses` | Status catalog. Pass `projectKey` for the project workflow's statuses; omit for the global catalog |
|
|
19
|
+
| `jira_list_issue_types` | Issue types allowed for issue creation in a `projectKey` |
|
|
20
|
+
| `jira_search` | JQL search. Auto-bounds queries with `created >= -365d` if no `ORDER BY` is given |
|
|
21
|
+
| `jira_get_issue` | Full details for an `issueKey` |
|
|
22
|
+
| `jira_create_issue` | Create an issue. Supports `moveToSprint` + `sprintId`/`sprintName`/`target` for atomic "create and place in sprint" |
|
|
23
|
+
| `jira_list_sprints` | Sprints for a project, optionally filtered by `state` (`active`/`closed`/`future`) |
|
|
24
|
+
| `jira_get_sprint_issues` | Issues in a sprint, optionally filtered by status — returns status breakdown |
|
|
25
|
+
| `jira_move_issue_to_sprint` | Move an issue to a sprint by id, name, or target (`current`/`active`/`latest`) with membership verification |
|
|
26
|
+
| `jira_get_comments` | Comments on an issue (newest first), ADF flattened to markdown |
|
|
27
|
+
| `jira_add_comment` | Add a plain-text comment |
|
|
28
|
+
| `jira_edit_issue` | PUT arbitrary fields (summary, labels, priority, story points, custom fields) |
|
|
29
|
+
| `jira_transition_issue` | Move to another status by `transitionId` or `toStatus`; lists available transitions when neither is provided |
|
|
30
|
+
|
|
31
|
+
## Setup
|
|
32
|
+
|
|
33
|
+
Jira uses Atlassian OAuth 2.0 (3LO).
|
|
34
|
+
|
|
35
|
+
1. In the Zibby dashboard, **Settings → Integrations → Connect Jira**.
|
|
36
|
+
2. Authorize Zibby for your Atlassian site.
|
|
37
|
+
3. In **Settings → Jira Configuration**, pick the project/space to use as the default.
|
|
38
|
+
|
|
39
|
+
See the [Jira Integration page](../integrations/jira.md) for the user-facing setup. Tokens auto-refresh; reconnect from the same panel if refresh fails.
|
|
40
|
+
|
|
41
|
+
Under the hood the bin reads `ATLASSIAN_ACCESS_TOKEN` + `ATLASSIAN_CLOUD_ID` (and optional `ATLASSIAN_INSTANCE_URL`), which the backend supplies through `resolveIntegrationToken('jira')`.
|
|
42
|
+
|
|
43
|
+
## Use in a workflow
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
|
|
47
|
+
import { SKILLS } from '@zibby/skills';
|
|
48
|
+
|
|
49
|
+
export class StandupBot extends WorkflowAgent {
|
|
50
|
+
buildGraph() {
|
|
51
|
+
const graph = new WorkflowGraph();
|
|
52
|
+
graph.addNode('standup', {
|
|
53
|
+
agent: 'claude',
|
|
54
|
+
skills: [SKILLS.JIRA],
|
|
55
|
+
prompt: (state) => `List active sprints for project ${state.projectKey}.
|
|
56
|
+
For the current sprint, call jira_get_sprint_issues to get a status breakdown,
|
|
57
|
+
then produce a standup-style summary grouped by assignee.`,
|
|
58
|
+
});
|
|
59
|
+
return graph;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Transitioning issues
|
|
65
|
+
|
|
66
|
+
The prompt fragment instructs the model to call `jira_transition_issue({ issueKey, toStatus })` directly when the user gives an explicit target; only when ambiguous should it call without `transitionId`/`toStatus` to list options. Status matching is normalized (whitespace, punctuation, case), then alias-matched, then dice-coefficient fuzzy-matched at a 0.45 threshold with a 0.12 gap.
|
|
67
|
+
|
|
68
|
+
### Sprint membership
|
|
69
|
+
|
|
70
|
+
To move an issue to a sprint, use `jira_move_issue_to_sprint` — it picks the right sprint (id/name/target), writes `customfield_10020`, then verifies membership via JQL and a second issue read before reporting success.
|
|
71
|
+
|
|
72
|
+
## Output example
|
|
73
|
+
|
|
74
|
+
`jira_get_sprint_issues`:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"count": 12,
|
|
79
|
+
"total": 12,
|
|
80
|
+
"statusCounts": { "To Do": 4, "In Progress": 5, "Done": 3 },
|
|
81
|
+
"issues": [
|
|
82
|
+
{
|
|
83
|
+
"key": "SCRUM-123",
|
|
84
|
+
"project": "SCRUM",
|
|
85
|
+
"summary": "Refresh token on 401",
|
|
86
|
+
"status": "In Progress",
|
|
87
|
+
"assignee": "Alice",
|
|
88
|
+
"priority": "High",
|
|
89
|
+
"type": "Story"
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Implementation notes
|
|
96
|
+
|
|
97
|
+
`resolve()` spawns `@zibby/mcp-jira` (`node @zibby/mcp-jira/index.js`) with the OAuth bearer token and cloud id in env. If the bin can't be resolved, `resolve()` returns `null` and the strategy falls back to in-process tool dispatch via `handleToolCall` (used by the `assistant` agent strategy).
|
|
98
|
+
|
|
99
|
+
`jiraFetch` clears the integration-token cache and retries once on auth-looking errors — the token endpoint can return a malformed payload mid-rotation. ADF (Atlassian Document Format) bodies are flattened to markdown in `jira_get_comments` so the model sees readable text instead of nested JSON.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 3
|
|
3
|
+
title: Lark
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Lark skill
|
|
7
|
+
|
|
8
|
+
Send messages, reply in threads, list chats, and read chat history on Lark / Feishu.
|
|
9
|
+
|
|
10
|
+
- **ID:** `lark`
|
|
11
|
+
- **MCP server:** `lark` (tools exposed as `mcp__lark__*`)
|
|
12
|
+
|
|
13
|
+
## Tools provided
|
|
14
|
+
|
|
15
|
+
| Tool | What it does |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `lark_send_message` | Send a text message. `receive_id` accepts chat_id (`oc_*`), open_id (`ou_*`), union_id (`on_*`), or email — the receive id type is inferred from the prefix |
|
|
18
|
+
| `lark_reply` | Reply to a specific `message_id` (creates a thread) |
|
|
19
|
+
| `lark_list_chats` | List chats the bot is a member of |
|
|
20
|
+
| `lark_get_chat_history` | Fetch recent messages in a `chat_id` (newest first), default `page_size: 20` |
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
Lark bots authenticate with App ID + App Secret. The bot needs `im:message` and `im:chat:readonly` (or wider) permissions, and must be added to any chat it should read or post into.
|
|
25
|
+
|
|
26
|
+
1. Create a custom app at [open.feishu.cn](https://open.feishu.cn/) (or Lark's open platform).
|
|
27
|
+
2. Enable **Bot** capability and grant the IM permissions above.
|
|
28
|
+
3. In the Zibby dashboard, **Settings → Integrations → Connect Lark**, paste your `App ID` and `App Secret`.
|
|
29
|
+
4. The backend exchanges them for a `tenant_access_token` on demand and caches it (TTL ~100 min, just under Lark's 2h).
|
|
30
|
+
|
|
31
|
+
For self-hosted Lark deployments, set `host` on the integration config; the default is the standard Feishu host.
|
|
32
|
+
|
|
33
|
+
## Use in a workflow
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
|
|
37
|
+
import { SKILLS } from '@zibby/skills';
|
|
38
|
+
|
|
39
|
+
export class IncidentNotifier extends WorkflowAgent {
|
|
40
|
+
buildGraph() {
|
|
41
|
+
const graph = new WorkflowGraph();
|
|
42
|
+
graph.addNode('notify', {
|
|
43
|
+
agent: 'claude',
|
|
44
|
+
skills: [SKILLS.LARK],
|
|
45
|
+
prompt: (state) => `Post a summary of incident ${state.incidentId} to the on-call
|
|
46
|
+
chat (chat_id: oc_xxxxx). If a previous thread exists in state.threadMessageId,
|
|
47
|
+
use lark_reply instead of lark_send_message.`,
|
|
48
|
+
});
|
|
49
|
+
return graph;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Output example
|
|
55
|
+
|
|
56
|
+
`lark_send_message`:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{ "ok": true, "message_id": "om_d2e6e3a1f24c9d3..." }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`lark_get_chat_history`:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"messages": [
|
|
67
|
+
{
|
|
68
|
+
"message_id": "om_abc...",
|
|
69
|
+
"sender_id": "ou_xyz...",
|
|
70
|
+
"sender_type": "user",
|
|
71
|
+
"msg_type": "text",
|
|
72
|
+
"content": "{\"text\":\"deploy is green\"}",
|
|
73
|
+
"create_time": "1715839203000"
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Implementation notes
|
|
80
|
+
|
|
81
|
+
Spawns `packages/skills/bin/mcp-lark.mjs`. Like Sentry, auth is fetched through the backend's `resolveIntegrationToken('lark')` endpoint and the tenant access token is cached in-process.
|
|
82
|
+
|
|
83
|
+
`receive_id_type` is required by Lark and is inferred from the id prefix (`oc_` → chat_id, `ou_` → open_id, `on_` → union_id, `cli_` → app_id, anything with `@` → email). Callers pass whichever id they have.
|
|
84
|
+
|
|
85
|
+
The MCP server uses `alwaysLoad: true` so tools land in the system prompt — same fix as Sentry. The `assistant` agent strategy can call the in-process `handleToolCall` for OpenAI Assistant API runs.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 7
|
|
3
|
+
title: Memory (test)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory skill
|
|
7
|
+
|
|
8
|
+
Query the test memory database — prior test runs, known selectors with stability metrics, page models, navigation transitions — and save insights for future runs. Dolt-backed (git-style versioned SQL).
|
|
9
|
+
|
|
10
|
+
- **ID:** `memory`
|
|
11
|
+
- **MCP server:** `memory` (tools exposed as `mcp__memory__*`)
|
|
12
|
+
- **Underlying server:** `@zibby/ui-memory/mcp-server`
|
|
13
|
+
|
|
14
|
+
For persistent **chat** memory (not test history) see [Chat memory](./chat-memory.md).
|
|
15
|
+
|
|
16
|
+
## Tools provided
|
|
17
|
+
|
|
18
|
+
| Tool | What it does |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `memory_get_test_history` | Recent test runs with pass/fail + timing. Filter by `specPath` substring |
|
|
21
|
+
| `memory_get_selectors` | Known selectors for a page with stability metrics. Filter by `pageUrl` substring |
|
|
22
|
+
| `memory_get_page_model` | Page structure — elements, roles, selectors. Filter by `url` substring |
|
|
23
|
+
| `memory_get_navigation` | Known page-to-page transitions. Filter by `fromUrl` substring |
|
|
24
|
+
| `memory_save_insight` | Save an observation. Categories: `selector_tip`, `timing`, `navigation`, `workaround`, `flaky`, `general` |
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
Requires [Dolt](https://docs.dolthub.com/introduction/installation) and an initialized memory database in your workspace.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# macOS
|
|
32
|
+
brew install dolt
|
|
33
|
+
# then, in your workflow workspace:
|
|
34
|
+
zibby init --mem
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`zibby init --mem` creates `.zibby/memory/` (a Dolt repo). The memory tools only activate after at least one completed test run — until then the skill returns `null` from `resolve()` and is skipped.
|
|
38
|
+
|
|
39
|
+
Override the bin location for development with `MCP_MEMORY_PATH`.
|
|
40
|
+
|
|
41
|
+
See [Tests → Memory](../tests/memory.md) for the full memory lifecycle.
|
|
42
|
+
|
|
43
|
+
## Use in a workflow
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
|
|
47
|
+
import { SKILLS } from '@zibby/skills';
|
|
48
|
+
|
|
49
|
+
export class FlakyTestInvestigator extends WorkflowAgent {
|
|
50
|
+
buildGraph() {
|
|
51
|
+
const graph = new WorkflowGraph();
|
|
52
|
+
graph.addNode('investigate', {
|
|
53
|
+
agent: 'claude',
|
|
54
|
+
skills: [SKILLS.MEMORY, SKILLS.BROWSER],
|
|
55
|
+
prompt: (state) => `Before running the test:
|
|
56
|
+
1. Call memory_get_test_history with specPath="${state.specPath}" — review prior failures.
|
|
57
|
+
2. Call memory_get_selectors with pageUrl matching the target page.
|
|
58
|
+
Then run the test using the browser tools. When done, save a memory_save_insight
|
|
59
|
+
capturing any selectors that worked when an older one failed.`,
|
|
60
|
+
});
|
|
61
|
+
return graph;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The skill's prompt fragment auto-instructs the agent to consult prior runs and call `memory_save_insight` mid-run when a fallback selector works, and at minimum once at end-of-run.
|
|
67
|
+
|
|
68
|
+
## Output example
|
|
69
|
+
|
|
70
|
+
`memory_get_selectors`:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"selectors": [
|
|
75
|
+
{
|
|
76
|
+
"pageUrl": "/dashboard",
|
|
77
|
+
"role": "button",
|
|
78
|
+
"stableId": "e7a1",
|
|
79
|
+
"selector": "[data-testid='new-project']",
|
|
80
|
+
"successCount": 42,
|
|
81
|
+
"failureCount": 1,
|
|
82
|
+
"lastSeen": "2026-05-15T14:00:00Z"
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Implementation notes
|
|
89
|
+
|
|
90
|
+
`resolve({ workspace })` resolves `@zibby/ui-memory/mcp-server` and spawns it with `--db-path .zibby/memory`. Before spawning it sanity-checks Dolt is on the PATH and the database has at least one `test_runs` row; if not, returns `null` (skill skipped silently) or throws a clear "install Dolt" error.
|
|
91
|
+
|
|
92
|
+
The skill also exports `middleware()` that loads `createMemoryMiddleware()` from `@zibby/ui-memory` when the package is present — this runs before/after each node to inject test history into the prompt and persist new insights.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 2
|
|
3
|
+
title: Sentry
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Sentry skill
|
|
7
|
+
|
|
8
|
+
Read-only access to a connected Sentry organization — list projects, list issues, fetch issue details.
|
|
9
|
+
|
|
10
|
+
- **ID:** `sentry`
|
|
11
|
+
- **MCP server:** `sentry` (tools exposed as `mcp__sentry__*`)
|
|
12
|
+
|
|
13
|
+
## Tools provided
|
|
14
|
+
|
|
15
|
+
| Tool | What it does |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `sentry_list_projects` | List projects in the connected organization (slug, name, platform) |
|
|
18
|
+
| `sentry_list_issues` | List issues. Supports `project`, `query` (Sentry search syntax, default `is:unresolved`), `sort` (`date`/`new`/`priority`/`freq`/`user`), `limit` |
|
|
19
|
+
| `sentry_get_issue` | Detailed info for one issue by `issueId` — title, culprit, counts, first/last seen, level, status |
|
|
20
|
+
|
|
21
|
+
## Setup
|
|
22
|
+
|
|
23
|
+
Sentry uses OAuth 2.0 with PKCE (public client — no client secret).
|
|
24
|
+
|
|
25
|
+
1. In the Zibby dashboard, go to **Settings → Integrations**.
|
|
26
|
+
2. Click **Connect Sentry**.
|
|
27
|
+
3. Approve the organization in the Sentry consent screen.
|
|
28
|
+
4. You're redirected back; the access token + organization slug are stored encrypted on the project.
|
|
29
|
+
|
|
30
|
+
The backend (`/integrations/sentry/connect` and `/integrations/sentry/callback`) handles the PKCE exchange. Tokens auto-refresh on use; if refresh fails, click **Reconnect** in the same settings panel.
|
|
31
|
+
|
|
32
|
+
## Use in a workflow
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
|
|
36
|
+
import { SKILLS } from '@zibby/skills';
|
|
37
|
+
|
|
38
|
+
export class SentryTriage extends WorkflowAgent {
|
|
39
|
+
buildGraph() {
|
|
40
|
+
const graph = new WorkflowGraph();
|
|
41
|
+
graph.addNode('triage', {
|
|
42
|
+
agent: 'claude',
|
|
43
|
+
skills: [SKILLS.SENTRY],
|
|
44
|
+
prompt: () => `Use sentry_list_issues with query "is:unresolved level:error" to find the
|
|
45
|
+
top 10 unresolved errors. Then call sentry_get_issue for the highest-frequency one
|
|
46
|
+
and summarize what's failing.`,
|
|
47
|
+
});
|
|
48
|
+
return graph;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Output example
|
|
54
|
+
|
|
55
|
+
`sentry_list_issues`:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"issues": [
|
|
60
|
+
{
|
|
61
|
+
"id": "5712345678",
|
|
62
|
+
"title": "TypeError: Cannot read properties of undefined (reading 'id')",
|
|
63
|
+
"culprit": "src/handlers/checkout.ts in handleSubmit",
|
|
64
|
+
"count": 1247,
|
|
65
|
+
"firstSeen": "2026-05-01T14:22:10Z",
|
|
66
|
+
"lastSeen": "2026-05-16T09:11:03Z",
|
|
67
|
+
"level": "error",
|
|
68
|
+
"status": "unresolved"
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Implementation notes
|
|
75
|
+
|
|
76
|
+
Spawns `packages/skills/bin/mcp-sentry.mjs` via `node`. Auth is delegated: the bin calls the Zibby backend's `resolveIntegrationToken('sentry')` endpoint using `PROJECT_API_TOKEN` + `PROGRESS_API_URL` (passed through from the Fargate environment).
|
|
77
|
+
|
|
78
|
+
`alwaysLoad: true` is set on the MCP server config so Sentry tools land in the initial system prompt instead of behind the Claude SDK's lazy `ToolSearch` (which misses MCP-served tools by keyword).
|
|
79
|
+
|
|
80
|
+
The skill also implements `handleToolCall` for the `assistant` agent strategy (OpenAI Assistant API), which doesn't speak MCP — same Sentry API, dispatched in-process.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 5
|
|
3
|
+
title: Slack
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Slack skill
|
|
7
|
+
|
|
8
|
+
Read/write a Slack workspace — list channels, post messages, reply in threads, react, read history, list users.
|
|
9
|
+
|
|
10
|
+
- **ID:** `slack`
|
|
11
|
+
- **MCP server:** `slack` (tools exposed as `mcp__slack__*`)
|
|
12
|
+
- **Underlying server:** `npx @modelcontextprotocol/server-slack@latest`
|
|
13
|
+
|
|
14
|
+
## Tools provided
|
|
15
|
+
|
|
16
|
+
| Tool | What it does |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `slack_list_channels` | List public channels (id, name, topic) |
|
|
19
|
+
| `slack_post_message` | Post a top-level message. Requires `channel` and `text` |
|
|
20
|
+
| `slack_reply_to_thread` | Post a reply with `thread_ts` |
|
|
21
|
+
| `slack_add_reaction` | Add an emoji reaction to a message |
|
|
22
|
+
| `slack_get_channel_history` | Recent messages in a channel (`limit` default 20) |
|
|
23
|
+
| `slack_get_thread_replies` | All replies in a thread |
|
|
24
|
+
| `slack_get_users` | Workspace members (excludes bots and deleted) |
|
|
25
|
+
| `slack_get_user_profile` | Detailed profile for a `user_id` |
|
|
26
|
+
|
|
27
|
+
## Setup
|
|
28
|
+
|
|
29
|
+
Slack uses a bot token + workspace team id.
|
|
30
|
+
|
|
31
|
+
1. Create (or reuse) a Slack app at [api.slack.com/apps](https://api.slack.com/apps).
|
|
32
|
+
2. Add Bot Token Scopes: `chat:write`, `channels:read`, `channels:history`, `groups:read`, `users:read`, `reactions:write` (minimum).
|
|
33
|
+
3. Install the app to your workspace; copy the **Bot User OAuth Token** (`xoxb-…`) and **Team ID**.
|
|
34
|
+
4. Set them on the workflow's env (locally) or via **Cloud → Env vars**:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
SLACK_BOT_TOKEN=xoxb-...
|
|
38
|
+
SLACK_TEAM_ID=T01234567
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The skill declares `envKeys: ['SLACK_BOT_TOKEN', 'SLACK_TEAM_ID']` and the official `@modelcontextprotocol/server-slack` consumes them.
|
|
42
|
+
|
|
43
|
+
## Use in a workflow
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
|
|
47
|
+
import { SKILLS } from '@zibby/skills';
|
|
48
|
+
|
|
49
|
+
export class DeployAnnouncer extends WorkflowAgent {
|
|
50
|
+
buildGraph() {
|
|
51
|
+
const graph = new WorkflowGraph();
|
|
52
|
+
graph.addNode('announce', {
|
|
53
|
+
agent: 'claude',
|
|
54
|
+
skills: [SKILLS.SLACK],
|
|
55
|
+
prompt: (state) => `Post to #deploys: "v${state.version} deployed to ${state.env}".
|
|
56
|
+
If state.threadTs is set, use slack_reply_to_thread instead of slack_post_message.`,
|
|
57
|
+
});
|
|
58
|
+
return graph;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Output example
|
|
64
|
+
|
|
65
|
+
`slack_post_message`:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{ "ok": true, "ts": "1715839203.000400", "channel": "C0123ABC" }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`slack_list_channels`:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"channels": [
|
|
76
|
+
{ "id": "C0123ABC", "name": "deploys", "topic": "Release announcements" },
|
|
77
|
+
{ "id": "C0456DEF", "name": "incidents", "topic": "P0/P1 coordination" }
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Implementation notes
|
|
83
|
+
|
|
84
|
+
`resolve()` spawns the official `@modelcontextprotocol/server-slack` via `npx` and passes through `SLACK_BOT_TOKEN` + `SLACK_TEAM_ID`. The in-process `handleToolCall` (used by the `assistant` strategy) talks directly to `https://slack.com/api/<method>` with the same bot token resolved via the backend's integration token service.
|