Skip to content

customizations: redesign as a typed two-level tree#152

Merged
connor4312 merged 6 commits into
mainfrom
customizations-revamp
May 27, 2026
Merged

customizations: redesign as a typed two-level tree#152
connor4312 merged 6 commits into
mainfrom
customizations-revamp

Conversation

@connor4312
Copy link
Copy Markdown
Member

Replace the opaque, plugin-only customization model with a discriminated union that distinguishes containers from the components inside them. This addresses three concrete problems with the previous design.

  1. Constituent parts were a one-off. The old model bolted an agents array on the side of each customization to expose contributed agents; skills, prompts, rules, hooks, and MCP servers had no equivalent. Agents are no longer special — agent hosts publish every contributed component (under either a plugin or a directory) as a typed child of its container.

  2. Non-plugin customizations didn't fit. The protocol assumed every customization was an Open Plugins package, so workspace sources like .github/skills/ had nowhere to live. Top-level entries are now Plugin or Directory. A Directory is just a folder the host watches, which is how a host can surface things like a host-global instructions folder or workspace skills without pretending they're plugins. Clients can tell a directory's "scope" from whether its URI sits inside the workspace, so no separate scope flag is added.

  3. Saving customizations had no shape. A writable Directory doubles as a save target: clients persist new customizations with the existing resourceWrite command, and the host re-emits the directory with updated children.

Clients continue to publish customizations in Open Plugins format (ClientPluginCustomization, with an opaque nonce). They don't know how a given harness consumes plugins, so the host is free to re-interpret what they publish.

The new shape:

  • Containers (Plugin / Directory) carry id, uri, name, load state, optional clientId, and a children array.
  • Children (Agent, Skill, Prompt, Rule, Hook, McpServer) are typed leaves with optional metadata sourced from the Open Plugins component spec (descriptions, globs, hook events, etc.). Host-internal execution details (commands, env, args) stay off the wire.
  • id is the protocol handle for every action; uri is the source location. Inline declarations (e.g. an MCP server embedded in a plugins.json manifest) use the containing file's uri plus a new optional range.

Actions collapse to four: customizationsChanged (full replace), customizationToggled (by id, walks both levels, client-dispatchable), customizationUpdated (upsert one container, children included), and customizationRemoved (by id, cascades). Children are always updated through their container.

Touches the type definitions, the session reducer and its fixtures, the version registry, the Swift and Rust generators (plus the hand-written reducer branches in each), and the customizations and actions guides.

Replace the opaque, plugin-only customization model with a discriminated
union that distinguishes containers from the components inside them.
This addresses three concrete problems with the previous design.

1. Constituent parts were a one-off. The old model bolted an `agents`
   array on the side of each customization to expose contributed
   agents; skills, prompts, rules, hooks, and MCP servers had no
   equivalent. Agents are no longer special — agent hosts publish
   every contributed component (under either a plugin or a directory)
   as a typed child of its container.

2. Non-plugin customizations didn't fit. The protocol assumed every
   customization was an Open Plugins package, so workspace sources
   like `.github/skills/` had nowhere to live. Top-level entries are
   now Plugin or Directory. A Directory is just a folder the host
   watches, which is how a host can surface things like a
   host-global instructions folder or workspace skills without
   pretending they're plugins. Clients can tell a directory's "scope"
   from whether its URI sits inside the workspace, so no separate
   scope flag is added.

3. Saving customizations had no shape. A writable Directory doubles
   as a save target: clients persist new customizations with the
   existing `resourceWrite` command, and the host re-emits the
   directory with updated children.

Clients continue to publish customizations in Open Plugins format
(ClientPluginCustomization, with an opaque nonce). They don't know
how a given harness consumes plugins, so the host is free to
re-interpret what they publish.

The new shape:

- Containers (Plugin / Directory) carry id, uri, name, load state,
  optional clientId, and a children array.
- Children (Agent, Skill, Prompt, Rule, Hook, McpServer) are typed
  leaves with optional metadata sourced from the Open Plugins
  component spec (descriptions, globs, hook events, etc.).
  Host-internal execution details (commands, env, args) stay off the
  wire.
- `id` is the protocol handle for every action; `uri` is the source
  location. Inline declarations (e.g. an MCP server embedded in a
  plugins.json manifest) use the containing file's uri plus a new
  optional range.

Actions collapse to four: customizationsChanged (full replace),
customizationToggled (by id, walks both levels, client-dispatchable),
customizationUpdated (upsert one container, children included), and
customizationRemoved (by id, cascades). Children are always updated
through their container.

Touches the type definitions, the session reducer and its fixtures,
the version registry, the Swift and Rust generators (plus the
hand-written reducer branches in each), and the customizations and
actions guides.
@connor4312 connor4312 marked this pull request as ready for review May 27, 2026 19:32
DonJayamanne
DonJayamanne previously approved these changes May 27, 2026
TylerLeonhardt
TylerLeonhardt previously approved these changes May 27, 2026
@connor4312 connor4312 merged commit b735d8f into main May 27, 2026
7 checks passed
@connor4312 connor4312 deleted the customizations-revamp branch May 27, 2026 21:59
connor4312 pushed a commit that referenced this pull request May 28, 2026
The Deploy Documentation workflow has been failing since PR #152
because two markdown files reference `/reference/commands#resourcewrite`,
which is not a real page in the generated reference. The
`resourceWrite` command is defined on the `common` channel
(`docs/reference/common.md`), so the correct link is
`/reference/common#resourcewrite`.

Updated the JSDoc on `DirectoryCustomization` in
`types/channels-session/state.ts` (which propagates to the generated
schema and Rust outputs) and the guide page
`docs/guide/customizations.md`, then re-ran `npm run generate` to
refresh derived artifacts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants