Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# agent-bundle

agent-bundle compiles an agent pluginskills, hooks, MCP servers, and scripts, described by one typed config into installable output for Claude Code, Codex, Cursor, portable Agent Plugins clients, and experimental Amp. You write the plugin once; the compiler emits each selected host's entry, manifests, and wrappers into one composite root. Amp is contract-verified against the pinned PluginAPI and install layout; live activation, tools, and events are runtime-unverified (no Amp account in CI).
agent-bundle compiles an agent plugin, skills, hooks, MCP servers, and scripts, described by one typed config, into installable output for Claude Code, Codex, Cursor, portable Agent Plugins clients, and experimental Amp. You write the plugin once; the compiler emits each selected host's entry, manifests, and wrappers into one composite root. Amp is contract-verified against the pinned PluginAPI and install layout; live activation, tools, and events are runtime-unverified (no Amp account in CI).

Documentation: [scriptedalchemy.github.io/agent-bundle](https://scriptedalchemy.github.io/agent-bundle/) (English and 简体中文; guide, reference, generated type API, host capability matrices).

Expand All @@ -19,7 +19,7 @@ Use a PR number or the SHA of a commit whose package-preview run succeeded (ever

## Quick start

The fastest start is the scaffolder — it prompts for a name, a template
The fastest start is the scaffolder. It prompts for a name, a template
(`minimal`, `mcp-server`, or `cli-tool`), and the host targets, then emits a
project that already passes its own `check`:

Expand Down Expand Up @@ -53,25 +53,25 @@ npx agent-bundle dev --root . # local workbench with live rebu

`agent-bundle build` writes one composite root (`artifact/` by default; `--output` or `output.distPath` relocates it), and `targets` selects which host projections it carries: Amp's `.amp/plugins/<name>/index.js`, the `.claude-plugin/`, `.codex-plugin/`, and `.cursor-plugin/` manifests, and the portable `plugin.json`. Amp installs its nested directory; the other hosts install the composite root, and the generated `INSTALL.md` explains how. Omitting `targets` emits only the `portable` projection. The `amp` target is experimental: contract-verified, runtime-unverified.

The `portable` target is the [Agent Plugins open standard](https://agent-plugins.org/specification) (specification 1.0.0) adapter — the default projection, and the layout Cursor loads natively (Claude Code consumes it only through CLI translation). Every other client that reads this artifact is recorded with its tier, install command, and dated evidence in the `clients` section of that same capability table, rendered as the [hosts reference](https://scriptedalchemy.github.io/agent-bundle/reference/hosts). It emits the closed root `plugin.json` (canonical `$schema`, `name`, `version`, `description`, plus `author`, `homepage`, `repository`, `license`, `keywords`, and reverse-domain `extensions` authored under the `portable` config key), `skills/<name>/SKILL.md`, and `mcp.json` with stdio and Streamable HTTP servers whose `args`, `env` values, and `cwd` use the standard's `${PLUGIN_ROOT}`/`${PLUGIN_DATA}` placeholders. Rules, commands, hooks, marketplaces, and client extension directories are honestly unavailable there because the v1 standard packages only skills and MCP servers. Both documents are validated against the vendored, hash-pinned 1.0.0 schemas and the normative text at plan time (`portable.mcp.*.standard`), after every ordinary build and `validate --artifact` (`AB6011`/`AB6012` plus the Agent Plugins byte lane `AB6035`–`AB6037`), under `validate --artifact --host-validation` (same lane with the `AB6038` provenance note), and by `agent-bundle doctor` for installed Cursor local plugins that declare the standard's `$schema` (`AB7320`); see [Diagnostics](docs/diagnostics.md#agent-plugins-portable-validation-ab6035ab6038). Pins live in `packages/agent-bundle/src/adapters/schemas/portable/PROVENANCE.json`; the capability table `packages/agent-bundle/src/adapters/capabilities/portable-1.0.0.json` carries a dated row for every standard feature.
The `portable` target is the [Agent Plugins open standard](https://agent-plugins.org/specification) (specification 1.0.0) adapter, the default projection, and the layout Cursor loads natively (Claude Code consumes it only through CLI translation). Every other client that reads this artifact is recorded with its tier, install command, and dated evidence in the `clients` section of that same capability table, rendered as the [hosts reference](https://scriptedalchemy.github.io/agent-bundle/reference/hosts). It emits the closed root `plugin.json` (canonical `$schema`, `name`, `version`, `description`, plus `author`, `homepage`, `repository`, `license`, `keywords`, and reverse-domain `extensions` authored under the `portable` config key), `skills/<name>/SKILL.md`, and `mcp.json` with stdio and Streamable HTTP servers whose `args`, `env` values, and `cwd` use the standard's `${PLUGIN_ROOT}`/`${PLUGIN_DATA}` placeholders. Rules, commands, hooks, marketplaces, and client extension directories are honestly unavailable there because the v1 standard packages only skills and MCP servers. Both documents are validated against the vendored, hash-pinned 1.0.0 schemas and the normative text at plan time (`portable.mcp.*.standard`), after every ordinary build and `validate --artifact` (`AB6011`/`AB6012` plus the Agent Plugins byte lane `AB6035`–`AB6037`), under `validate --artifact --host-validation` (same lane with the `AB6038` provenance note), and by `agent-bundle doctor` for installed Cursor local plugins that declare the standard's `$schema` (`AB7320`); see [Diagnostics](docs/diagnostics.md#agent-plugins-portable-validation-ab6035ab6038). Pins live in `packages/agent-bundle/src/adapters/schemas/portable/PROVENANCE.json`; the capability table `packages/agent-bundle/src/adapters/capabilities/portable-1.0.0.json` carries a dated row for every standard feature.

Claude Code language servers are declared under `claude.lspServers`; the `claude` projection emits the record as plugin-root `.lsp.json`. Agent Bundle expands path tokens only in `command`, `args`, `env`, and `workspaceFolder`, and it does not include the language-server binary install that separately so the declared command is available on `PATH`. Codex, Cursor, and the portable format do not currently receive this host-scoped configuration.
Claude Code language servers are declared under `claude.lspServers`; the `claude` projection emits the record as plugin-root `.lsp.json`. Agent Bundle expands path tokens only in `command`, `args`, `env`, and `workspaceFolder`, and it does not include the language-server binary, install that separately so the declared command is available on `PATH`. Codex, Cursor, and the portable format do not currently receive this host-scoped configuration.

Claude Code plugin defaults are declared under `claude.settings` and emitted as plugin-root `settings.json`, which Claude Code applies when the plugin is enabled. The pinned contract supports only `agent` and `subagentStatusLine`; Agent Bundle rejects any other key rather than shipping a default Claude Code would silently ignore, and it expands no path tokens here because `settings.json` is absent from the host's placeholder-substitution table. Because the plugin `agents/` component is still deferred, declaring `agent` also raises a warning: the referenced agent has to reach the plugin root some other way, such as a prebuilt payload.

Cursor Plugin manifest metadata is declared under `cursor.*` and emitted verbatim into `.cursor-plugin/plugin.json` by the `cursor` projection: `author` (`name`, optional `email`), `homepage`, `repository`, `license`, `keywords`, plus the schema-admitted `publisher`, `category`, `tags`, and `minClientVersions` (for example `{ cursor: '3.13.0' }`). Every field is validated against the pinned `cursor/plugins` manifest schema before emission — `author.url`, non-HTTP URLs, empty strings, and loose semver are rejected with `cursor.manifest.*` errors, and an invalid block emits no partial metadata. The Cursor Plugin documents never mix Agent Plugin (`plugin.json`, `${PLUGIN_ROOT}`) paths or tokens into the Cursor Plugin format; the portable projection owns that format, even when both share one root. The full documented-surface contract matrix (every Cursor hook event, cloud availability, hook options, marketplace and team-distribution surfaces, canvases, agents) lives as dated `supported` / `unavailable` rows in `packages/agent-bundle/src/adapters/capabilities/cursor-2026-08-28.json`.
Cursor Plugin manifest metadata is declared under `cursor.*` and emitted verbatim into `.cursor-plugin/plugin.json` by the `cursor` projection: `author` (`name`, optional `email`), `homepage`, `repository`, `license`, `keywords`, plus the schema-admitted `publisher`, `category`, `tags`, and `minClientVersions` (for example `{ cursor: '3.13.0' }`). Every field is validated against the pinned `cursor/plugins` manifest schema before emission, `author.url`, non-HTTP URLs, empty strings, and loose semver are rejected with `cursor.manifest.*` errors, and an invalid block emits no partial metadata. The Cursor Plugin documents never mix Agent Plugin (`plugin.json`, `${PLUGIN_ROOT}`) paths or tokens into the Cursor Plugin format; the portable projection owns that format, even when both share one root. The full documented-surface contract matrix (every Cursor hook event, cloud availability, hook options, marketplace and team-distribution surfaces, canvases, agents) lives as dated `supported` / `unavailable` rows in `packages/agent-bundle/src/adapters/capabilities/cursor-2026-08-28.json`.

The same config also owns the npm package build no second bundler config, bin shims, or hand-rolled stdio lifecycles. `bin` and `lib` entries (or the conventions `src/cli.ts`, `src/index.ts`, and `src/mcp/<server-id>.ts`) emit executable `dist/bin/<name>.js` bundles and a library output alongside the plugin root; an MCP entry that default-exports a server factory runs under a framework-owned stdio lifecycle; `tools.rsbuild` / `tools.rspack` is the one bundler escape hatch. [Entry conventions](docs/entry-conventions.md) is the full contract, and [Framework mode](docs/framework-mode.md) is the whole authoring model on one screen: structure in config and conventions (`src/skills/<name>/SKILL.md` ships with no declaration at all), JSX only where something is rendered.
The same config also owns the npm package build, no second bundler config, bin shims, or hand-rolled stdio lifecycles. `bin` and `lib` entries (or the conventions `src/cli.ts`, `src/index.ts`, and `src/mcp/<server-id>.ts`) emit executable `dist/bin/<name>.js` bundles and a library output alongside the plugin root; an MCP entry that default-exports a server factory runs under a framework-owned stdio lifecycle; `tools.rsbuild` / `tools.rspack` is the one bundler escape hatch. [Entry conventions](docs/entry-conventions.md) is the full contract, and [Framework mode](docs/framework-mode.md) is the whole authoring model on one screen: structure in config and conventions (`src/skills/<name>/SKILL.md` ships with no declaration at all), JSX only where something is rendered.

## Commands

- `build` validate the project and write the plugin root (plus the `bin`/`lib` package build when declared)
- `validate` check project source, or a built artifact with `--artifact <dir>`
- `inspect` show the normalized configuration and each selected host's projection plan; `--bundler` dumps the lowered Rspack config of every compiled output (post-`tools`-hatch merge, as Rslib/Rsbuild hand it to the compiler)
- `dev` serve the local development workbench and rebuild the `dist/` package build when its inputs change; `--install-host <claude|codex|cursor>` installs a development variant whose stable `dev proxy` MCP command hot-swaps epochs behind the host's open connection and re-syncs hooks and Skills on every adopted rebuild (see [Framework mode › Live development into hosts](docs/framework-mode.md#live-development-into-hosts))
- `mcp list` / `mcp invoke` / `mcp run` list, invoke, or run an artifact's MCP servers locally
- `hooks list` / `hooks simulate` inspect and simulate generated hooks
- `eval` run eval suites against a built artifact
- `build`: validate the project and write the plugin root (plus the `bin`/`lib` package build when declared)
- `validate`: check project source, or a built artifact with `--artifact <dir>`
- `inspect`: show the normalized configuration and each selected host's projection plan; `--bundler` dumps the lowered Rspack config of every compiled output (post-`tools`-hatch merge, as Rslib/Rsbuild hand it to the compiler)
- `dev`: serve the local development workbench and rebuild the `dist/` package build when its inputs change; `--install-host <claude|codex|cursor>` installs a development variant whose stable `dev proxy` MCP command hot-swaps epochs behind the host's open connection and re-syncs hooks and Skills on every adopted rebuild (see [Framework mode › Live development into hosts](docs/framework-mode.md#live-development-into-hosts))
- `mcp list` / `mcp invoke` / `mcp run`: list, invoke, or run an artifact's MCP servers locally
- `hooks list` / `hooks simulate`: inspect and simulate generated hooks
- `eval`: run eval suites against a built artifact

When the `claude` projection is selected, validating the built root also runs the installed
Claude Code developer toolchain in addition to the pinned schemas. Use
Expand All @@ -96,7 +96,7 @@ Run these from the repository root. `pnpm examples:check` validates and builds e

## Development

`pnpm check` runs the local delivery gate (build, unit and integration tests, lint, typecheck); `pnpm check:release` adds the packaging gates. `pnpm check:local-ci` mirrors the full hosted CI gatethe three-Node verify matrix plus the examples, release, and micro-eval jobs in parallel local worktrees, and is the merge gate for the local-merge workflow described in [docs/local-ci.md](docs/local-ci.md). Versioning goes through Changesets. Native Claude/Codex host smokes are opt-in and intentionally skipped in CI. The Workbench architecture and the optional RSC runtime are documented in [docs/architecture/rsc-runtime-workbench.md](docs/architecture/rsc-runtime-workbench.md).
`pnpm check` runs the local delivery gate (build, unit and integration tests, lint, typecheck); `pnpm check:release` adds the packaging gates. `pnpm check:local-ci` mirrors the full hosted CI gate, the three-Node verify matrix plus the examples, release, and micro-eval jobs, in parallel local worktrees, and is the merge gate for the local-merge workflow described in [docs/local-ci.md](docs/local-ci.md). Versioning goes through Changesets. Native Claude/Codex host smokes are opt-in and intentionally skipped in CI. The Workbench architecture and the optional RSC runtime are documented in [docs/architecture/rsc-runtime-workbench.md](docs/architecture/rsc-runtime-workbench.md).

## Status

Expand Down
14 changes: 7 additions & 7 deletions agent-patterns/effect-concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ Source: `repos/effect/packages/effect/src/Fiber.ts`, `Semaphore.ts`,
`Latch.ts`, `Queue.ts`, `PubSub.ts`, `Deferred.ts`, and `Effect.ts`
(`forkChild`, `forEach`, `all`, `raceFirst`). Refresh when the subtree moves.

Waves 46 grow the biggest concurrency surfaces (hook thin-clients under
Waves 4 to 6 grow the biggest concurrency surfaces (hook thin-clients under
host deadlines, MCP progress projector, notices ledger, warm-runtime
lifecycle). Build those Effect-native from day one behind Promise edges.

## Fibers

- `yield* Effect.forkChild(effect)` structured child of the current fiber.
- `Fiber.await` / `Fiber.join` / `Fiber.interrupt` observe or cancel.
- `yield* Effect.forkChild(effect)`: structured child of the current fiber.
- `Fiber.await` / `Fiber.join` / `Fiber.interrupt`: observe or cancel.
- Prefer exported Fiber functions over `interruptUnsafe` / `pollUnsafe`.
- Forked work that owns a resource must be forked *into a scope*
(`forkScoped` / `Layer.scoped`) so interruption closes the resource.
Expand All @@ -20,7 +20,7 @@ Host `AbortSignal` still exists at the edges (`dispatch()` / `stream()`).
Do not thread extra internal signals once the program is an Effect; interrupt
the fiber (`Stream.interruptWhen` + `abortToInterrupt`). `Latch.makeUnsafe`
is the legal sync bridge when a web `ReadableStream.pull` must open demand
for an Effect stream do not invent a second AbortSignal for that.
for an Effect stream, do not invent a second AbortSignal for that.

## Bounded work

Expand All @@ -37,8 +37,8 @@ for an Effect stream — do not invent a second AbortSignal for that.

## Racing and cancellation

- `Effect.raceFirst(a, b)` first to complete wins; the loser is interrupted.
- `interruptWhenAborted(effect, signal)` host deadline into a race with
- `Effect.raceFirst(a, b)`: first to complete wins; the loser is interrupted.
- `interruptWhenAborted(effect, signal)`: host deadline into a race with
`Effect.interrupt`.
- Do not `Promise.race` around Effects. Race inside Effect, `runPromise` once.

Expand All @@ -50,5 +50,5 @@ for an Effect stream — do not invent a second AbortSignal for that.
`Fiber` you join/interrupt.
- Using `PubSub` when consumers should compete (that's a `Queue`).
- Using `Queue` when every subscriber needs every event (that's `PubSub`).
- Coalescing rebuilds with debounce timers (`setTimeout`) `Latch` +
- Coalescing rebuilds with debounce timers (`setTimeout`): `Latch` +
fiber, not host timers (`globalTimersInEffect`).
Loading
Loading