@mthanh/vespercli 2.0.1 → 2.1.0
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/README.md +34 -9
- package/assets/claude/.claude-plugin/marketplace.json +8 -8
- package/assets/claude/.claude-plugin/plugin.json +1 -1
- package/assets/claude/packs/backend/.claude-plugin/plugin.json +1 -1
- package/assets/claude/packs/docs/.claude-plugin/plugin.json +1 -1
- package/assets/claude/packs/frontend/.claude-plugin/plugin.json +1 -1
- package/assets/claude/packs/integrations/.claude-plugin/plugin.json +1 -1
- package/assets/claude/packs/playwright/.claude-plugin/plugin.json +1 -1
- package/assets/claude/packs/polyglot/.claude-plugin/plugin.json +1 -1
- package/assets/claude/packs/styles/.claude-plugin/plugin.json +1 -1
- package/assets/claude/vesper-orchestrator.md +1 -1
- package/assets/hooks/pre-commit +1 -1
- package/assets/opencode/vesper-plugin.js +120 -0
- package/dist/cli.min.js +119 -99
- package/dist/opencode-render.js +254 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Vesper Agent Suite
|
|
2
2
|
|
|
3
|
-
A CLI that provisions an AI agent suite into your project for **Claude Code** or **GitHub Copilot Chat (VS Code)**: an orchestrator persona, specialist subagents, skills, and maintenance workflows.
|
|
3
|
+
A CLI that provisions an AI agent suite into your project for **Claude Code + OpenCode** or **GitHub Copilot Chat (VS Code)**: an orchestrator persona, specialist subagents, skills, and maintenance workflows.
|
|
4
4
|
|
|
5
|
-
**Vesper** gives your AI coding assistant specialized agents, reusable skills, and structured workflows. Each workspace runs **exactly one host**, Claude Code or GitHub Copilot, and you pick it with `vespercli init --host claude|copilot`. The project's workflow state (`.vesper/.tracking/`, including the run ledger, and `.vesper/memory.jsonl`) is host-neutral and survives a host switch.
|
|
5
|
+
**Vesper** gives your AI coding assistant specialized agents, reusable skills, and structured workflows. Each workspace runs **exactly one host**, Claude Code (which always prepares [OpenCode](#opencode) as well) or GitHub Copilot, and you pick it with `vespercli init --host claude|copilot`. The project's workflow state (`.vesper/.tracking/`, including the run ledger, and `.vesper/memory.jsonl`) is host-neutral and survives a host switch.
|
|
6
6
|
|
|
7
7
|
> **Upgrading from 1.x?** Vesper 2.0 is a breaking release. Running `vespercli init` once migrates a 1.x workspace in place and backs up everything it removes. See [Upgrading from 1.x](#upgrading-from-1x) and [`CHANGELOG.md`](CHANGELOG.md).
|
|
8
8
|
|
|
@@ -70,7 +70,7 @@ The design optimizes for compounding: mistakes convert to rules, workflows conve
|
|
|
70
70
|
|
|
71
71
|
- Node.js >= 22.13.0 (the `vesper-indexing` MCP server uses the built-in `node:sqlite` module)
|
|
72
72
|
- git (worktree bootstrap, hooks and host resolution read the repository)
|
|
73
|
-
- One host: Claude Code (CLI, VS Code extension, JetBrains, or desktop app)
|
|
73
|
+
- One host: Claude Code (CLI, VS Code extension, JetBrains, or desktop app) — [OpenCode](#opencode) opens the same workspace as-is — or VS Code with GitHub Copilot Chat
|
|
74
74
|
|
|
75
75
|
## Installation
|
|
76
76
|
|
|
@@ -84,7 +84,7 @@ The session-start triggers and git hooks call `vespercli` from `PATH`. They do n
|
|
|
84
84
|
## Quick start
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
|
-
vespercli init --host claude # or: --host copilot
|
|
87
|
+
vespercli init --host claude # Claude Code + OpenCode; or: --host copilot
|
|
88
88
|
vespercli init # interactive: asks "Which AI host is this workspace for?"
|
|
89
89
|
```
|
|
90
90
|
|
|
@@ -93,6 +93,7 @@ A non-interactive run (no TTY) never guesses. It re-uses the host the checked-ou
|
|
|
93
93
|
Then say hello:
|
|
94
94
|
|
|
95
95
|
- **Claude Code**: start a session in the project. It opens as the `vesper-orchestrator` agent. The first time, Claude Code asks you to trust the workspace, which loads the `vesper` plugin. Prompt `init`, and Vesper detects the missing Team Roster and runs the workspace setup.
|
|
96
|
+
- **OpenCode**: open the same checkout. `opencode.json` makes `vesper-orchestrator` the default agent and loads the always-on rules; the Vesper plugin under `.opencode/plugins/` registers the specialists (see [OpenCode](#opencode)).
|
|
96
97
|
- **GitHub Copilot**: reload VS Code (`Developer: Reload Window`), open Copilot Chat, pick **vesper-orchestrator** in the agent dropdown, and prompt `Vesperize`.
|
|
97
98
|
|
|
98
99
|
Commit what `init` wrote (see the tables below). The committed files are what make the project a Vesper project for every clone, worktree, and teammate.
|
|
@@ -101,7 +102,7 @@ Commit what `init` wrote (see the tables below). The committed files are what ma
|
|
|
101
102
|
|
|
102
103
|
Vesper writes only inside the project, and every file it writes is recorded in an ownership ledger (see [Ownership and removal](#ownership-and-removal)).
|
|
103
104
|
|
|
104
|
-
### Claude Code (`--host claude`)
|
|
105
|
+
### Claude Code + OpenCode (`--host claude`)
|
|
105
106
|
|
|
106
107
|
The specialist catalog is **not copied** into the project. It ships inside the npm package as a Claude Code **marketplace** (`vesper-marketplace`) holding a **core plugin** (`vesper`) and the stack **packs** (`vesper-frontend`, `vesper-playwright`, `vesper-backend`, `vesper-polyglot`, `vesper-docs`, `vesper-integrations`, and the opt-in `vesper-styles`), all referenced in place. Core specialists resolve as `vesper:<name>` and core skills as `/vesper:<name>`; a packed one as `vesper-<pack>:<name>` / `/vesper-<pack>:<name>`. Core is always enabled; a pack is enabled per project — see [Packs](#packs).
|
|
107
108
|
|
|
@@ -118,6 +119,16 @@ The specialist catalog is **not copied** into the project. It ships inside the n
|
|
|
118
119
|
| `CLAUDE.md` | yes | A short guarded prose pointer (`<!-- vesper:start -->…<!-- vesper:end -->`). No `@import`: the rules load natively. |
|
|
119
120
|
| `.mcp.json` | no (gitignored) | The three core MCP servers plus any you added. Regenerated at every session start. |
|
|
120
121
|
|
|
122
|
+
#### OpenCode (always written with `--host claude`)
|
|
123
|
+
|
|
124
|
+
The Claude host always prepares OpenCode too (there is no separate OpenCode host and no flag): three small committed files, recorded in the ledger under host `claude`, removed by `reset --host claude` and by a switch to Copilot, re-provisioned by a switch back.
|
|
125
|
+
|
|
126
|
+
| Path | Committed? | What it is |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| `opencode.json` | yes | Vesper's keys only, merged into your file (your keys and array entries are kept): `default_agent: "vesper-orchestrator"`, `instructions` (the three `.claude/rules/vesper-*.md` files, append-only), `mcp` (the same servers `.mcp.json` carries, as `["vespercli", "mcp", "serve", "<name>"]` — no machine path, so the file commits), `plugin: ["./.opencode/plugins/vesper.js"]` (append-only), and a Vesper-owned `vesper` object (`packs`: the enabled packs, mirrored by `packs enable/disable/sync`; `agentsMode`: `plugin` or `files`). A fresh file also gets `$schema`. |
|
|
129
|
+
| `.opencode/agents/vesper-orchestrator.md` | yes | The primary OpenCode agent (`mode: primary`) — the same pointer to the rules as the committed Claude persona. |
|
|
130
|
+
| `.opencode/plugins/vesper.js` | yes | A byte-exact copy of the shipped Vesper plugin. Refreshed on `init`/`update` when the shipped file changed; an edited copy is kept and reported. |
|
|
131
|
+
|
|
121
132
|
### GitHub Copilot (`--host copilot`)
|
|
122
133
|
|
|
123
134
|
The generated Copilot catalog stays **inside the npm package** (`assets/copilot/`). Vesper points VS Code at it through workspace settings. Every package agent is generated with `user-invocable: false`, so the agent picker shows the built-in agents plus one Vesper agent.
|
|
@@ -176,9 +187,22 @@ and then works as a single generalist, following the memory and run-ledger conve
|
|
|
176
187
|
|
|
177
188
|
Session-start work also needs `vespercli`: the `SessionStart` hook is `command -v vespercli >/dev/null 2>&1 && vespercli guard --running-host claude || true`. Where `vespercli` isn't installed, nothing runs, so `.mcp.json` and the code index are not generated.
|
|
178
189
|
|
|
190
|
+
## OpenCode
|
|
191
|
+
|
|
192
|
+
OpenCode ([sst/opencode](https://github.com/sst/opencode)) reads a Claude workspace natively (`CLAUDE.md`, `.claude/skills/`), but not the always-on `.claude/rules/*`. The Claude host therefore always writes the overlay in the table above, and no `reinit` is needed between the two tools — both run on the same checkout. Open the repository in OpenCode:
|
|
193
|
+
|
|
194
|
+
- `vesper-orchestrator` is the primary agent (`default_agent`), and the playbook, Team Roster and whitepaper are loaded through `instructions`.
|
|
195
|
+
- The three core MCP servers (and any you added with `vespercli mcp add`) run through `vespercli mcp serve <name>`, so `opencode.json` carries no machine path.
|
|
196
|
+
- The specialists come from the npm package through the Vesper plugin, under the same names the roster carries (`vesper:<name>`, `vesper-<pack>:<name>`); the four maintenance skills resolve from `.claude/skills/`. `vesper.packs` in `opencode.json` mirrors the enabled packs.
|
|
197
|
+
- Fallback: set `vesper.agentsMode` to `"files"` in `opencode.json` and run `vespercli opencode render-agents` — the specialists are then generated as `.opencode/agents/<name>.md` files (re-rendered by `packs enable/disable/sync`).
|
|
198
|
+
|
|
199
|
+
OpenCode ignores unknown top-level keys in `opencode.json` (its config decoder drops excess properties), so the `vesper` object is harmless at runtime; an editor validating against `opencode.ai/config.json` may flag it as unknown.
|
|
200
|
+
|
|
201
|
+
Verify on your install (the story's open questions): **OQ-1** the plugin registers agents at runtime (otherwise use the `files` fallback); **OQ-2** `instructions` accepts the three rule files as-is; **OQ-3** the `session.created` hook's guard output reaches the model's context; **OQ-4** `vesper:<name>` names with a `:` are accepted as agent identifiers by the Task tool.
|
|
202
|
+
|
|
179
203
|
## Which host is this?
|
|
180
204
|
|
|
181
|
-
**The checked-out tree decides.** Vesper resolves the host from Vesper-only keys in files git can see, as they stand in the working tree: the plugin and `agent` keys in `.claude/settings.json` for Claude; the `.vesper/agents` settings entry and the Copilot guard task for Copilot. Generic files such as `.mcp.json` never decide the host. Every path uses this rule: the guard, bootstrap, `doctor`, `init`, `reset`, `list`, `add`, `update` and `mcp`.
|
|
205
|
+
**The checked-out tree decides.** Vesper resolves the host from Vesper-only keys in files git can see, as they stand in the working tree: the plugin and `agent` keys in `.claude/settings.json`, or `default_agent: "vesper-orchestrator"` in `opencode.json` (the OpenCode overlay's key — a second Claude fingerprint, so both present is the normal state, and `opencode.json` alone still resolves Claude), for Claude; the `.vesper/agents` settings entry and the Copilot guard task for Copilot. Generic files such as `.mcp.json` never decide the host. Every path uses this rule: the guard, bootstrap, `doctor`, `init`, `reset`, `list`, `add`, `update` and `mcp`.
|
|
182
206
|
|
|
183
207
|
Consequences:
|
|
184
208
|
|
|
@@ -199,6 +223,7 @@ Caveats:
|
|
|
199
223
|
| Trigger | Command | Where it comes from |
|
|
200
224
|
|---|---|---|
|
|
201
225
|
| Claude Code opens the project | `vespercli guard --running-host claude` | `SessionStart` hook in `.claude/settings.json` |
|
|
226
|
+
| OpenCode opens the project | `vespercli guard --running-host opencode` | the Vesper plugin (`.opencode/plugins/vesper.js`, `session.created`) — `opencode` is the Claude host's identity: a Claude workspace is healthy, a Copilot one a mismatch |
|
|
202
227
|
| VS Code opens the folder | `vespercli guard --running-host copilot` | folder-open task in `.vscode/tasks.json` |
|
|
203
228
|
| `git checkout`/`git switch` of a branch | `vespercli guard` (no host) | `post-checkout` git hook |
|
|
204
229
|
|
|
@@ -210,7 +235,7 @@ VS Code runs folder-open tasks only when automatic tasks are allowed in the fold
|
|
|
210
235
|
- **Copilot direction, Claude workspace**: VS Code opens Claude workspaces too (a Claude Code user editing in VS Code), so that task's message is advisory: it names `vespercli init --host copilot` for Copilot users, says it removes the Claude Code setup, and tells Claude Code users to ignore it. Nothing is written.
|
|
211
236
|
- **Copilot**: the folder-open task prints the same token and fix in the terminal, and the Copilot orchestrator playbook carries the same banner-and-continue rule. There is no sentinel file and no always-on refusal rule; a `.vesper/instructions/vesper-host-guard.instructions.md` left by an earlier 2.0 build is removed by the next `init`, `update` or `reset` when it is byte-identical to what Vesper wrote (an edited copy is kept and reported).
|
|
212
237
|
|
|
213
|
-
**Plugin absence (Claude Code only).** On the healthy path of `vespercli guard --running-host claude`, after bootstrap has regenerated `.claude/settings.local.json`, the guard checks the conditions the plugin needs and writes `VESPER_PLUGIN_ABSENT` to stdout when one is verifiably missing (see [degraded mode](#claude-code-plugin-trust-and-degraded-mode)). It never writes a "present" token. At most one host token and one plugin token appear per run; a mismatch suppresses the plugin token, and the plugin token leaves the exit code unchanged.
|
|
238
|
+
**Plugin absence (Claude Code only, never for `--running-host opencode` — the OpenCode plugin carries the specialists itself).** On the healthy path of `vespercli guard --running-host claude`, after bootstrap has regenerated `.claude/settings.local.json`, the guard checks the conditions the plugin needs and writes `VESPER_PLUGIN_ABSENT` to stdout when one is verifiably missing (see [degraded mode](#claude-code-plugin-trust-and-degraded-mode)). It never writes a "present" token. At most one host token and one plugin token appear per run; a mismatch suppresses the plugin token, and the plugin token leaves the exit code unchanged.
|
|
214
239
|
|
|
215
240
|
**Without `--running-host`** (the git hook or a manual run) there is no identity claim: no mismatch check and no marker of either kind. The guard writes nothing unless the **committed** tree at `HEAD` carries the resolved host's keys and the working tree still agrees.
|
|
216
241
|
|
|
@@ -218,7 +243,7 @@ VS Code runs folder-open tasks only when automatic tasks are allowed in the fold
|
|
|
218
243
|
|
|
219
244
|
## `vespercli doctor`
|
|
220
245
|
|
|
221
|
-
A **read-only** validator. It never writes, deletes, or backs anything up. It checks host resolution, the ownership ledger against disk, MCP config drift, tracking shape, the framework version, git hooks, 1.x leftovers,
|
|
246
|
+
A **read-only** validator. It never writes, deletes, or backs anything up. It checks host resolution, the ownership ledger against disk, MCP config drift, tracking shape, the framework version, git hooks, 1.x leftovers, user-level residue, and (INFO `opencode-overlay-missing`) a Claude workspace that lacks one of the three OpenCode overlay files — `vespercli init` adds them. Each finding is `ERROR`, `WARN` or `INFO` and names its fix: `vespercli init`, `vespercli init --host <host>`, `vespercli reset --host <host>`, or `vespercli worktree bootstrap` for local-only state such as a missing git hook block. A fresh worktree's ledger carries no framework version until `init` runs there once; `doctor` reports that as `ledger-unstamped` (INFO), not as a stale framework.
|
|
222
247
|
|
|
223
248
|
```bash
|
|
224
249
|
vespercli doctor # human-readable report
|
|
@@ -398,7 +423,7 @@ A newer upstream reaches your project with the next Vesper release. `vespercli p
|
|
|
398
423
|
|---|---|
|
|
399
424
|
| `vespercli init [--host claude\|copilot] [-y] [--force-default] [--path <dir>]` | Provision, switch host, re-provision, or migrate a 1.x workspace. `--target` is a deprecated alias of `--host`; `all` and comma lists are rejected. |
|
|
400
425
|
| `vespercli doctor [--quiet] [--running-host <host>]` | Read-only validation; exit `0`/`1`/`2`. |
|
|
401
|
-
| `vespercli guard [--running-host
|
|
426
|
+
| `vespercli guard [--running-host claude\|copilot\|opencode]` | Session-start guard (installed as the hook, the task and the OpenCode plugin's `session.created` hook); `opencode` stands for the Claude host. |
|
|
402
427
|
| `vespercli gate [--plan <file>] [--base <ref>] [--json]` | Run the committed gate policy (`.vesper/gates.json`): checks, plan conformance, diff-size cap, protected paths. Exit `0` pass, `1` fail, `2` no config. See [Gates](#gates). |
|
|
403
428
|
| `vespercli log <started\|completed\|note\|delegated\|returned> --pipeline <p> [--step <s>] [--agent <a>] [--goal <g>] [--author <a>] [--status DONE\|NEED_HELP\|BLOCKED] [--slug <slug>] [--note <text>] [--stdin] [--json]` | Append one row to the run ledger `.vesper/.tracking/runs/YYYYMMDD-<slug>.jsonl` (atomic append; `--slug` defaults to `adhoc`). `--stdin` reads a Claude Code hook's JSON. Exit `2` on a missing required field. |
|
|
404
429
|
| `vespercli log tail [--slug <slug>] [-n 5] [--json]` | Print the last run-ledger rows — what the orchestrator reads on resumption. |
|
|
@@ -7,49 +7,49 @@
|
|
|
7
7
|
{
|
|
8
8
|
"name": "vesper",
|
|
9
9
|
"source": "./",
|
|
10
|
-
"version": "2.0
|
|
10
|
+
"version": "2.1.0",
|
|
11
11
|
"description": "Vesper core: the support, review and generic developer/tester specialists plus the catalog skills every project uses. Always enabled."
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
"name": "vesper-frontend",
|
|
15
15
|
"source": "./packs/frontend",
|
|
16
|
-
"version": "2.0
|
|
16
|
+
"version": "2.1.0",
|
|
17
17
|
"description": "Vesper frontend pack — React, Next.js, Tailwind and Jest/RTL specialists and skills."
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
"name": "vesper-playwright",
|
|
21
21
|
"source": "./packs/playwright",
|
|
22
|
-
"version": "2.0
|
|
22
|
+
"version": "2.1.0",
|
|
23
23
|
"description": "Vesper playwright pack — Playwright end-to-end testing specialists and skills."
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
"name": "vesper-backend",
|
|
27
27
|
"source": "./packs/backend",
|
|
28
|
-
"version": "2.0
|
|
28
|
+
"version": "2.1.0",
|
|
29
29
|
"description": "Vesper backend pack — Node.js/Express, Firebase, NestJS, databases, auth and DevOps specialists and skills."
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
"name": "vesper-polyglot",
|
|
33
33
|
"source": "./packs/polyglot",
|
|
34
|
-
"version": "2.0
|
|
34
|
+
"version": "2.1.0",
|
|
35
35
|
"description": "Vesper polyglot pack — Java, Flutter and Google ADK (Python) skills."
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
"name": "vesper-docs",
|
|
39
39
|
"source": "./packs/docs",
|
|
40
|
-
"version": "2.0
|
|
40
|
+
"version": "2.1.0",
|
|
41
41
|
"description": "Vesper docs pack — Document processing (docx/pptx/pdf/xlsx) and multimodal (Gemini) skills."
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
"name": "vesper-integrations",
|
|
45
45
|
"source": "./packs/integrations",
|
|
46
|
-
"version": "2.0
|
|
46
|
+
"version": "2.1.0",
|
|
47
47
|
"description": "Vesper integrations pack — ClickUp task-board specialist, the GitLab MR guardrails skill, and the third-party GitLab CI skills (gitlab-ci-skill, github-actions-to-gitlab-ci)."
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"name": "vesper-styles",
|
|
51
51
|
"source": "./packs/styles",
|
|
52
|
-
"version": "2.0
|
|
52
|
+
"version": "2.1.0",
|
|
53
53
|
"description": "Vesper styles pack — opt-in output styles: i-have-adhd, ponytail (third-party, MIT). Enabled by `vespercli packs enable styles`, which writes the always-on pointer rule that applies them."
|
|
54
54
|
}
|
|
55
55
|
]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vesper",
|
|
3
3
|
"displayName": "Vesper",
|
|
4
|
-
"version": "2.0
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"description": "Vesper specialist subagent catalog + catalog skills for Claude Code. The orchestrator playbook, team roster, slim project whitepaper, the committed main-thread orchestrator agent, and the four maintenance skills are project-committed (see .claude/rules/, .claude/agents/, .claude/skills/) and are deliberately NOT shipped by this plugin.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Vesper Maintainers"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vesper-backend",
|
|
3
3
|
"displayName": "Vesper backend pack",
|
|
4
|
-
"version": "2.0
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"description": "Vesper backend pack — Node.js/Express, Firebase, NestJS, databases, auth and DevOps specialists and skills. Enabled per project by `vespercli packs sync` when the Team Roster names one of its assets.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Vesper Maintainers"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vesper-docs",
|
|
3
3
|
"displayName": "Vesper docs pack",
|
|
4
|
-
"version": "2.0
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"description": "Vesper docs pack — Document processing (docx/pptx/pdf/xlsx) and multimodal (Gemini) skills. Enabled per project by `vespercli packs sync` when the Team Roster names one of its assets.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Vesper Maintainers"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vesper-frontend",
|
|
3
3
|
"displayName": "Vesper frontend pack",
|
|
4
|
-
"version": "2.0
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"description": "Vesper frontend pack — React, Next.js, Tailwind and Jest/RTL specialists and skills. Enabled per project by `vespercli packs sync` when the Team Roster names one of its assets.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Vesper Maintainers"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vesper-integrations",
|
|
3
3
|
"displayName": "Vesper integrations pack",
|
|
4
|
-
"version": "2.0
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"description": "Vesper integrations pack — ClickUp task-board specialist, the GitLab MR guardrails skill, and the third-party GitLab CI skills (gitlab-ci-skill, github-actions-to-gitlab-ci; UPSTREAM.json beside each). Enabled per project by `vespercli packs sync` when the Team Roster names one of its assets.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Vesper Maintainers"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vesper-playwright",
|
|
3
3
|
"displayName": "Vesper playwright pack",
|
|
4
|
-
"version": "2.0
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"description": "Vesper playwright pack — Playwright end-to-end testing specialists and skills. Enabled per project by `vespercli packs sync` when the Team Roster names one of its assets.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Vesper Maintainers"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vesper-polyglot",
|
|
3
3
|
"displayName": "Vesper polyglot pack",
|
|
4
|
-
"version": "2.0
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"description": "Vesper polyglot pack — Java, Flutter and Google ADK (Python) skills. Enabled per project by `vespercli packs sync` when the Team Roster names one of its assets.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Vesper Maintainers"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vesper-styles",
|
|
3
3
|
"displayName": "Vesper styles pack",
|
|
4
|
-
"version": "2.0
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"description": "opt-in output styles: i-have-adhd, ponytail. Third-party skills (MIT) shipped as pack content, never written into a project; `vespercli packs enable styles` is the consent and writes the always-on pointer rule that applies them.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Vesper Maintainers"
|
|
@@ -56,7 +56,7 @@ For verified trivial edits, Vesper MAY perform the change directly instead of de
|
|
|
56
56
|
- If no roster yet, or no role matches a task → delegate to `vesper:workspace-learner`.
|
|
57
57
|
- **Namespaced delegation names (correctness invariant, not style):** every specialist is a plugin agent exposed under the `vesper:` namespace. Delegate to them by their **`vesper:<name>`** form (e.g. `vesper:code-reviewer`, `vesper:task-researcher`, `vesper:principal-software-engineer`). The Team Roster's "Agent Name" column carries these namespaced names verbatim — use them exactly as written. If a roster entry names a catalog specialist **without** the namespace (an older roster not yet revalidated), delegate to `vesper:<name>` anyway — unless that name is a project agent in `.claude/agents/`, which resolves bare — and suggest `/revalidate-team`. The ONE non-namespaced name is the committed main-thread persona `vesper-orchestrator` (the `.claude/settings.json` `agent` key value); it is a project agent, not a plugin agent, so it carries no namespace. The four maintenance skills are committed project skills invoked **unqualified** (`/reinit`, `/compact-knowledge`, `/revalidate-knowledge`, `/revalidate-team`) — never `/vesper:reinit`.
|
|
58
58
|
|
|
59
|
-
- **Packs (core + packs):** the catalog is a **core** plugin (`vesper`) plus stack **packs** — separate plugins in the same marketplace (`vesper-frontend`, `vesper-playwright`, `vesper-backend`, `vesper-polyglot`, `vesper-docs`, `vesper-integrations`, the opt-in `vesper-styles`), enabled per project when the Team Roster names one of their assets. A packed specialist resolves as **`vesper-<pack>:<name>`** (e.g. `vesper-frontend:expert-react-frontend-engineer`, `vesper-playwright:playwright-tester`) and the roster carries that namespaced name verbatim; a packed skill is `/vesper-<pack>:<name>`. A roster row naming an asset of a pack that is **not enabled** here is a `vespercli doctor` finding (`pack-not-enabled`) — the fix is `vespercli packs sync`, which enables every pack the roster needs (it never disables). `init` and `update` run the same sync; `vespercli packs` lists what is shipped, enabled and needed.
|
|
59
|
+
- **Packs (core + packs):** the catalog is a **core** plugin (`vesper`) plus stack **packs** — separate plugins in the same marketplace (`vesper-frontend`, `vesper-playwright`, `vesper-backend`, `vesper-polyglot`, `vesper-docs`, `vesper-integrations`, the opt-in `vesper-styles`), enabled per project when the Team Roster names one of their assets. A packed specialist resolves as **`vesper-<pack>:<name>`** (e.g. `vesper-frontend:expert-react-frontend-engineer`, `vesper-playwright:playwright-tester`) and the roster carries that namespaced name verbatim; a packed skill is `/vesper-<pack>:<name>`. A roster row naming an asset of a pack that is **not enabled** here is a `vespercli doctor` finding (`pack-not-enabled`) — the fix is `vespercli packs sync`, which enables every pack the roster needs (it never disables). `init` and `update` run the same sync; `vespercli packs` lists what is shipped, enabled and needed. On OpenCode the same namespaced names resolve through the Vesper plugin.
|
|
60
60
|
|
|
61
61
|
**What you MAY read** (routing files only — to decide WHO to delegate to, never to do the work yourself):
|
|
62
62
|
|
package/assets/hooks/pre-commit
CHANGED
|
@@ -28,7 +28,7 @@ if [ "$(git config --type=bool --get vesper.hooks 2>/dev/null)" != "false" ]; th
|
|
|
28
28
|
# No `exit 0` inside this block: it may sit inside a user's or a hook manager's
|
|
29
29
|
# hook with lines after it, and those must always run. Only the mr.json refusal
|
|
30
30
|
# exits (non-zero, to abort the commit on purpose).
|
|
31
|
-
vesper_git_paths=".claude/vesper/context .claude/agents .claude/commands .claude/skills .claude/rules .vesper/skills .vesper/instructions .vesper/agents .vesper/context .vesper/.tracking .vesper/memory.jsonl .vesper/gates.json CLAUDE.md"
|
|
31
|
+
vesper_git_paths=".claude/vesper/context .claude/agents .claude/commands .claude/skills .claude/rules .vesper/skills .vesper/instructions .vesper/agents .vesper/context .vesper/.tracking .vesper/memory.jsonl .vesper/gates.json CLAUDE.md opencode.json .opencode"
|
|
32
32
|
|
|
33
33
|
vesper_staged=$(git diff --cached --name-only)
|
|
34
34
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vesper OpenCode plugin — TEMPLATE. `vespercli init --host claude` copies it to
|
|
3
|
+
* `.opencode/plugins/vesper.js` (committed). Plain ESM, no dependencies, runs under Bun.
|
|
4
|
+
*
|
|
5
|
+
* What it does (every `vespercli` call is guarded; nothing here ever throws into OpenCode):
|
|
6
|
+
* 1. Locates the installed package: `vespercli --print-assets-dir` (fallback: `VESPER_ASSETS_DIR`).
|
|
7
|
+
* 2. Reads Vesper's keys in `opencode.json`: `vesper.packs` (the enabled packs) and
|
|
8
|
+
* `vesper.agentsMode` (`"plugin"` default | `"files"`).
|
|
9
|
+
* 3. `config` hook (plugin mode): registers every core + enabled-pack specialist as an OpenCode
|
|
10
|
+
* subagent (`vesper:<name>` / `vesper-<pack>:<name>`) by adding them to the loaded config's
|
|
11
|
+
* `agent` map — converted from the Claude catalog by the package's own renderer
|
|
12
|
+
* (`dist/opencode-render.js`), never a second converter here.
|
|
13
|
+
* 4. `session.created` → `vespercli guard --running-host opencode`; a `VESPER_HOST_MISMATCH` on
|
|
14
|
+
* its stdout is shown as a TUI toast and appended to the session's first message so the
|
|
15
|
+
* playbook's host-mismatch warning fires.
|
|
16
|
+
* 5. `tool.execute.after` on the `task` tool → `vespercli log delegated` (run-ledger enrichment).
|
|
17
|
+
*
|
|
18
|
+
* OpenCode loads every export of this module as a plugin function, so this file exports exactly one.
|
|
19
|
+
*/
|
|
20
|
+
import { execFileSync } from 'node:child_process';
|
|
21
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
22
|
+
import { join, resolve } from 'node:path';
|
|
23
|
+
import { pathToFileURL } from 'node:url';
|
|
24
|
+
|
|
25
|
+
const TOKEN = 'VESPER_HOST_MISMATCH';
|
|
26
|
+
|
|
27
|
+
function vespercli(args, cwd) {
|
|
28
|
+
try {
|
|
29
|
+
const stdout = execFileSync('vespercli', args, { cwd, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 60000 });
|
|
30
|
+
return { ok: true, stdout: String(stdout) };
|
|
31
|
+
} catch (err) {
|
|
32
|
+
return { ok: false, stdout: String(err?.stdout || ''), stderr: String(err?.stderr || err?.message || '') };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readVesperSettings(dir) {
|
|
37
|
+
const out = { packs: [], agentsMode: 'plugin' };
|
|
38
|
+
try {
|
|
39
|
+
const v = JSON.parse(readFileSync(join(dir, 'opencode.json'), 'utf-8'))?.vesper;
|
|
40
|
+
if (Array.isArray(v?.packs)) out.packs = v.packs.filter((p) => typeof p === 'string');
|
|
41
|
+
if (v?.agentsMode === 'files') out.agentsMode = 'files';
|
|
42
|
+
} catch { /* no opencode.json / no vesper key → defaults */ }
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function agentFiles(dir, plugin) {
|
|
47
|
+
try {
|
|
48
|
+
return readdirSync(dir).filter((f) => f.endsWith('.md')).sort().map((f) => ({ name: f.slice(0, -3), plugin, path: join(dir, f) }));
|
|
49
|
+
} catch { return []; }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function loadRenderer(assetsDir) {
|
|
53
|
+
const root = resolve(assetsDir, '..');
|
|
54
|
+
for (const rel of ['dist/opencode-render.js', 'src/cli/utils/opencode-render.js']) {
|
|
55
|
+
const abs = join(root, rel);
|
|
56
|
+
if (existsSync(abs)) return import(pathToFileURL(abs).href);
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const VesperPlugin = async ({ client, directory, worktree }) => {
|
|
62
|
+
const dir = directory || worktree || process.cwd();
|
|
63
|
+
const settings = readVesperSettings(dir);
|
|
64
|
+
const log = async (level, message) => {
|
|
65
|
+
try { await client?.app?.log?.({ body: { service: 'vesper', level, message } }); } catch { /* best effort */ }
|
|
66
|
+
};
|
|
67
|
+
const available = vespercli(['--version'], dir).ok;
|
|
68
|
+
const located = available ? vespercli(['--print-assets-dir'], dir) : { ok: false };
|
|
69
|
+
const assetsDir = located.ok ? located.stdout.trim() : (process.env.VESPER_ASSETS_DIR || '');
|
|
70
|
+
const render = assetsDir ? await loadRenderer(assetsDir).catch(() => null) : null;
|
|
71
|
+
if (settings.agentsMode === 'plugin' && !render) {
|
|
72
|
+
await log('warn', 'Vesper: the @mthanh/vespercli package was not found (vespercli on PATH, or VESPER_ASSETS_DIR) — specialists not registered. Install it, or run `vespercli opencode render-agents` and set `vesper.agentsMode: "files"` in opencode.json.');
|
|
73
|
+
}
|
|
74
|
+
const pending = new Map(); // sessionID → the mismatch banner to append once
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
config: async (config) => {
|
|
78
|
+
if (settings.agentsMode !== 'plugin' || !render) return;
|
|
79
|
+
config.agent ??= {};
|
|
80
|
+
const sources = agentFiles(join(assetsDir, 'claude', 'agents'), 'vesper');
|
|
81
|
+
for (const pack of settings.packs) sources.push(...agentFiles(join(assetsDir, 'claude', 'packs', pack, 'agents'), `vesper-${pack}`));
|
|
82
|
+
for (const src of sources) {
|
|
83
|
+
try {
|
|
84
|
+
const { frontmatter, body } = render.parseAgentMarkdown(readFileSync(src.path, 'utf-8'));
|
|
85
|
+
const { id, config: agent } = render.convertClaudeAgent({ name: src.name, frontmatter, body, plugin: src.plugin });
|
|
86
|
+
if (!config.agent[id]) config.agent[id] = agent;
|
|
87
|
+
} catch (err) { await log('warn', `Vesper: could not register ${src.plugin}:${src.name}: ${err?.message || err}`); }
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
event: async ({ event }) => {
|
|
92
|
+
if (event?.type !== 'session.created' || !available) return;
|
|
93
|
+
const info = event.properties?.info;
|
|
94
|
+
if (info?.parentID) return; // a subagent session — the guard ran for its parent
|
|
95
|
+
const res = vespercli(['guard', '--running-host', 'opencode', '--path', dir], dir);
|
|
96
|
+
const lines = res.stdout.split('\n');
|
|
97
|
+
const at = lines.indexOf(TOKEN);
|
|
98
|
+
if (at === -1) return;
|
|
99
|
+
const banner = lines.slice(at).filter(Boolean).join('\n');
|
|
100
|
+
pending.set(info?.id ?? '*', banner);
|
|
101
|
+
try { await client?.tui?.showToast?.({ body: { title: 'Vesper', message: lines[at + 1] || TOKEN, variant: 'warning' } }); } catch { /* headless */ }
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
'chat.message': async (input, output) => {
|
|
105
|
+
const banner = pending.get(input?.sessionID) ?? pending.get('*');
|
|
106
|
+
if (!banner) return;
|
|
107
|
+
pending.delete(input?.sessionID); pending.delete('*');
|
|
108
|
+
const part = (output?.parts || []).find((p) => p?.type === 'text' && typeof p.text === 'string');
|
|
109
|
+
if (part) part.text = `${part.text}\n\n${banner}`;
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
'tool.execute.after': async (input) => {
|
|
113
|
+
if (input?.tool !== 'task' || !available) return;
|
|
114
|
+
const agent = input.args?.subagent_type || input.args?.agent || 'unknown';
|
|
115
|
+
vespercli(['log', 'delegated', '--pipeline', 'hook', '--agent', String(agent), '--note', 'opencode', '--session', String(input.sessionID || ''), '--path', dir], dir);
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export default VesperPlugin;
|