diff --git a/docs/app/app.config.ts b/docs/app/app.config.ts index 242dbdbe..caa9c447 100644 --- a/docs/app/app.config.ts +++ b/docs/app/app.config.ts @@ -19,9 +19,9 @@ export default defineAppConfig({ }, { label: 'Adapters', - sections: ['adapters', 'frameworks', 'helpers'], + sections: ['adapters', 'frameworks'], }, - { label: 'Plugins', sections: ['plugins'], link: 'section' as const }, + { label: 'Add-ons', sections: ['add-ons'], link: 'section' as const }, { label: 'Reference', sections: ['references'], link: 'section' as const }, { label: 'Errors', sections: ['errors'], link: 'section' as const }, // { label: 'Blog', sections: ['posts'], link: 'section' as const }, diff --git a/docs/app/components/global/GettingStartedWizard.vue b/docs/app/components/global/GettingStartedWizard.vue index 2eb8ecbf..9058d790 100644 --- a/docs/app/components/global/GettingStartedWizard.vue +++ b/docs/app/components/global/GettingStartedWizard.vue @@ -140,8 +140,8 @@ const DOC_CATALOG: Record = { '/guide/build-your-own-json-render-frontend': { title: 'Build Your Own JSON-Render Frontend', description: 'Implement the renderer contract in your own framework instead of the reference one.', icon: 'i-lucide-component' }, '/guide/build-your-own-hub-ui': { title: 'Build Your Own Hub UI', description: 'The two contracts a hub UI provider implements — node side and browser side.', icon: 'i-lucide-layout-panel-left' }, '/guide/standalone-cli': { title: 'Standalone CLI with Devframe', description: 'npx my-tool starts a dev server serving your SPA over type-safe RPC.', icon: 'i-lucide-terminal' }, - '/helpers/interactive-auth': { title: 'Interactive Auth', description: 'An OTP auth layer over devframe\'s node-side primitives.', icon: 'i-lucide-key-round' }, - '/helpers/utilities': { title: 'Utilities', description: 'Small, stable helpers bundled into devframe — no npm install.', icon: 'i-lucide-wrench' }, + '/references/interactive-auth': { title: 'Interactive Auth', description: 'An OTP auth layer over devframe\'s node-side primitives.', icon: 'i-lucide-key-round' }, + '/references/utilities': { title: 'Utilities', description: 'Small, stable helpers bundled into devframe — no npm install.', icon: 'i-lucide-wrench' }, '/adapters': { title: 'Adapters', description: 'Every path from a DevframeDefinition to a running devframe.', icon: 'i-lucide-shuffle' }, '/adapters/initiate': { title: 'The Standard Handler', description: 'initDevframe() turns a definition into a Web Standard Request → Response handler.', icon: 'i-lucide-server' }, '/adapters/cac': { title: 'CLI (cac)', description: 'A cac CLI around a DevframeDefinition with dev, build, and mcp commands.', icon: 'i-lucide-square-terminal' }, @@ -153,8 +153,8 @@ const DOC_CATALOG: Record = { '/frameworks/vite': { title: 'Vite', description: 'Author one devframe\'s SPA, or mount a whole hub, from a Vite plugin.', icon: 'i-simple-icons-vite' }, '/frameworks/next': { title: 'Next', description: 'Host devframes from a Next.js App Router app via a route handler.', icon: 'i-simple-icons-nextdotjs' }, '/frameworks/nuxt': { title: 'Nuxt', description: 'A Nuxt module split into authoring one devframe or mounting a hub.', icon: 'i-simple-icons-nuxtdotjs' }, - '/plugins/a11y': { title: 'Accessibility Inspector', description: 'Runs axe-core against the user app and highlights violations in the page.', icon: 'i-lucide-accessibility' }, - '/plugins/terminals': { title: 'Terminals', description: 'A terminal panel built on xterm.js.', icon: 'i-lucide-square-terminal' }, + '/add-ons/devframes/a11y': { title: 'Accessibility Inspector', description: 'Runs axe-core against the user app and highlights violations in the page.', icon: 'i-lucide-accessibility' }, + '/add-ons/devframes/terminals': { title: 'Terminals', description: 'A terminal panel built on xterm.js.', icon: 'i-lucide-square-terminal' }, } /** Always worth reading, regardless of what's checked above. */ @@ -162,8 +162,8 @@ const BASE_DOCS = ['/guide', '/guide/devframe-definition', '/guide/tutorial-serv /** `${section.key}:${item.value}` -> doc routes that answer is worth reading. */ const RECOMMENDATIONS: Record = { - 'dataSource:node': ['/guide/rpc', '/guide/shared-state', '/helpers/utilities'], - 'dataSource:browser': ['/guide/client-context', '/guide/deep-linking', '/plugins/a11y'], + 'dataSource:node': ['/guide/rpc', '/guide/shared-state', '/references/utilities'], + 'dataSource:browser': ['/guide/client-context', '/guide/deep-linking', '/add-ons/devframes/a11y'], 'environments:standalone': ['/guide/standalone-cli', '/adapters/cac', '/adapters/build'], 'environments:framework': ['/adapters'], @@ -171,7 +171,7 @@ const RECOMMENDATIONS: Record = { 'availability:dev': ['/guide/rpc', '/guide/transports'], 'availability:build': ['/adapters/build', '/guide/client-assets'], - 'availability:static': ['/adapters/build', '/helpers/utilities'], + 'availability:static': ['/adapters/build', '/references/utilities'], 'availability:remote': ['/guide/transports', '/guide/security'], 'frontend:framework': ['/guide/client-assets', '/guide/client'], @@ -179,10 +179,10 @@ const RECOMMENDATIONS: Record = { 'frontend:nodeside': ['/guide/json-render', '/guide/build-your-own-json-render-frontend'], 'requirements:agent': ['/guide/agent-native', '/adapters/mcp'], - 'requirements:terminal': ['/plugins/terminals'], + 'requirements:terminal': ['/add-ons/devframes/terminals'], 'requirements:streaming': ['/guide/streaming'], 'requirements:deep-linking': ['/guide/deep-linking'], - 'requirements:overlay': ['/guide/client-context', '/plugins/a11y'], + 'requirements:overlay': ['/guide/client-context', '/add-ons/devframes/a11y'], } const selections = reactive>( diff --git a/docs/content/1.guide/1.tutorial-server-data-inspector.md b/docs/content/1.guide/1.tutorial-server-data-inspector.md index 262477f7..6e75b06f 100644 --- a/docs/content/1.guide/1.tutorial-server-data-inspector.md +++ b/docs/content/1.guide/1.tutorial-server-data-inspector.md @@ -245,7 +245,7 @@ export default defineConfig({ npx vite --config vite.hub.config.ts ``` -Your inspector now sits in the hub's dock rail as a dock entry. Add more to `devframes: [...]` — your own or the [built-in devframes](/plugins) — and each gets its own. (The hub prints a code to authorize on first connect.) +Your inspector now sits in the hub's dock rail as a dock entry. Add more to `devframes: [...]` — your own or the [built-in devframes](/add-ons) — and each gets its own. (The hub prints a code to authorize on first connect.) ## Step 5 — Build a static version @@ -317,7 +317,7 @@ node bin.mjs mcp # expose the tool to a coding agent over MCP You can also assemble your own CLI from the adapter functions used above. -That's it for this tutorial. For a full-featured version, there's a ready-to-use [Data Inspector built-in devframe](/plugins/data-inspector) to use or read for reference. +That's it for this tutorial. For a full-featured version, there's a ready-to-use [Data Inspector built-in devframe](/add-ons/devframes/data-inspector) to use or read for reference. ## What's next diff --git a/docs/content/1.guide/10.standalone-cli.md b/docs/content/1.guide/10.standalone-cli.md index bd8f8a96..bd6625f8 100644 --- a/docs/content/1.guide/10.standalone-cli.md +++ b/docs/content/1.guide/10.standalone-cli.md @@ -188,7 +188,7 @@ Booleans become `--verbose` / `--no-verbose`, else `--depth `; keys are c ## Common RPC functions -Recipes for opening files in the editor or OS explorer live in `devframe/recipes/common-rpc-functions` ([Common RPC Functions](/helpers/common-rpc-functions)). +Recipes for opening files in the editor or OS explorer live in `devframe/recipes/common-rpc-functions` ([Common RPC Functions](/references/common-rpc-functions)). ## Snapshot queries for static builds diff --git a/docs/content/1.guide/12.in-page-channel.md b/docs/content/1.guide/12.in-page-channel.md index 0f43b138..debc019c 100644 --- a/docs/content/1.guide/12.in-page-channel.md +++ b/docs/content/1.guide/12.in-page-channel.md @@ -5,7 +5,7 @@ navigation: description: 'The in-page channel connects a devframe''s page script to its panels entirely in the browser — typed events, calls, and page-script-authoritative shared state, with no server involved.' --- -The in-page channel (`devframe/in-page-channel`) connects a devframe's page script to its panels entirely in the browser — typed events, calls, and page-script-authoritative shared state, with no server involved. It is how a live inspect-the-page loop (like the [a11y inspector](/plugins/a11y)'s scan/highlight cycle) works identically in dev and in a static build. +The in-page channel (`devframe/in-page-channel`) connects a devframe's page script to its panels entirely in the browser — typed events, calls, and page-script-authoritative shared state, with no server involved. It is how a live inspect-the-page loop (like the [a11y inspector](/add-ons/devframes/a11y)'s scan/highlight cycle) works identically in dev and in a static build. ## Overview diff --git a/docs/content/1.guide/16.hub.md b/docs/content/1.guide/16.hub.md index 2bd175e9..6f274c17 100644 --- a/docs/content/1.guide/16.hub.md +++ b/docs/content/1.guide/16.hub.md @@ -13,7 +13,7 @@ _Orchestrating multiple devtools (from [A Playground](https://github.com/devfram ## What the hub adds -`DevframeHubContext` adds four subsystems to `DevframeNodeContext`: `ctx.docks` registers dock entries and groups and [activates docks](#cross-iframe-dock-activation); `ctx.terminals` aggregates terminal sessions with streaming output ([Terminals](/plugins/terminals#hub-aggregation)); `ctx.messages` is the server-side toast/notification queue; `ctx.commands` is the hierarchical command palette with keybindings and `when` clauses. Each subsystem's API is in the [Hub API reference](/references/hub-api#hub-subsystems). +`DevframeHubContext` adds four subsystems to `DevframeNodeContext`: `ctx.docks` registers dock entries and groups and [activates docks](#cross-iframe-dock-activation); `ctx.terminals` aggregates terminal sessions with streaming output ([Terminals](/add-ons/devframes/terminals#hub-aggregation)); `ctx.messages` is the server-side toast/notification queue; `ctx.commands` is the hierarchical command palette with keybindings and `when` clauses. Each subsystem's API is in the [Hub API reference](/references/hub-api#hub-subsystems). Data-driven UI panels are an opt-in [JSON-Render](/guide/json-render) package (a `json-render` dock type). @@ -58,7 +58,7 @@ await rpc.call('hub:docks:activate', { }) ``` -It mirrors into the `devframe:docks:active` shared-state slot; the [terminals dock](/plugins/terminals#focusing-a-session) reads `params.sessionId`, unknown ids no-op ([DF8107](/errors/DF8107)). Server-side: `ctx.docks.activate(dockId, params?)`. +It mirrors into the `devframe:docks:active` shared-state slot; the [terminals dock](/add-ons/devframes/terminals#focusing-a-session) reads `params.sessionId`, unknown ids no-op ([DF8107](/errors/DF8107)). Server-side: `ctx.docks.activate(dockId, params?)`. ## Process-control launchers diff --git a/docs/content/1.guide/17.client-context.md b/docs/content/1.guide/17.client-context.md index 7a73b7cb..815c8078 100644 --- a/docs/content/1.guide/17.client-context.md +++ b/docs/content/1.guide/17.client-context.md @@ -71,7 +71,7 @@ Client scripts execute in the user app's page realm (`window`); anchor shared st ### Dual boots -One bundle can serve as both a client script (default export) and, via a globally-guarded self-boot, a standalone page script ([a11y inspector](/plugins/a11y)). +One bundle can serve as both a client script (default export) and, via a globally-guarded self-boot, a standalone page script ([a11y inspector](/add-ons/devframes/a11y)). ## Iframe panels diff --git a/docs/content/1.guide/19.services.md b/docs/content/1.guide/19.services.md index 27a04f5a..174c390d 100644 --- a/docs/content/1.guide/19.services.md +++ b/docs/content/1.guide/19.services.md @@ -7,6 +7,8 @@ description: 'ctx.services lets one devframe expose a typed, namespaced capabili `ctx.services` lets one devframe expose a typed, namespaced capability visible to every devframe. Two tiers: in-process services (`provide`/`get`) share live objects between devframes; [wire services](#wire-services) also register RPC and advertise to RPC clients. +The [Services reference](/references/services) collects the host methods, the definition/descriptor fields, and the built-in services as lookup tables. + ## Providing a service Augment `DevframeServicesRegistry` with your id and type, then provide in setup: @@ -113,11 +115,11 @@ A reactive UI subscribes via `rpc.services.state()`. `has()`/`get()`/`keys()` ar ### Built-in services -**`@devframes/service-open`** (`devframes:service:open`) opens files in an editor (`open-in-editor`, optional `line`/`column`) or OS explorer (`open-in-finder`), refusing paths outside the workspace root plus extra `roots` (`DS_OPEN_0002`), gating editors to `KNOWN_EDITORS`. Options `{ editor?, roots? }` (later wins; dirs union-merged). - -**`@devframes/service-git`** (`devframes:service:git`) runs typed git ops — `status`, `log`, `show`, `readFile`, `diff`, `branches`, `tags`, `stage`, `unstage`, `commit` — on one repo fixed at install (`{ cwd? }`). Write ops are exposed; authorization is the host framework's boundary. +Three first-party wire services ship ready to install, each with its own page under [Add-ons › Services](/add-ons/services): -**`@devframes/service-shiki`** (`devframes:service:shiki`) renders [Shiki](https://shiki.style) highlighting on the node side via three RPC queries — `highlight` (dual-theme HTML), `code-to-hast`, `code-to-tokens` — all client-`cacheable`, LRU-cached per `(code, lang, themes)`. Options `{ themes?, langs? }` — light/dark pair (defaults `vitesse-light`/`vitesse-dark`; later wins) and preloaded languages (union-merged). +- **[`@devframes/service-open`](/add-ons/services/open)** (`devframes:service:open`) — open files in an editor or OS explorer, refusing paths outside the workspace root. +- **[`@devframes/service-git`](/add-ons/services/git)** (`devframes:service:git`) — typed read/write git operations on one repo. +- **[`@devframes/service-shiki`](/add-ons/services/shiki)** (`devframes:service:shiki`) — node-side [Shiki](https://shiki.style) highlighting, LRU-cached and dual-theme. ## Services, RPC, or shared state? diff --git a/docs/content/1.guide/20.deep-linking.md b/docs/content/1.guide/20.deep-linking.md index a0fcac33..c33eec97 100644 --- a/docs/content/1.guide/20.deep-linking.md +++ b/docs/content/1.guide/20.deep-linking.md @@ -20,7 +20,7 @@ await rpc.call('hub:docks:activate', { The hub broadcasts the request and mirrors it into the [`devframe:docks:active`](/guide/shared-state) slot, so a dock mounting *because* of the switch converges on it. The target subscribes, filters on its `dockId`, and reads `params` — see [Cross-iframe dock activation](/guide/hub#cross-iframe-dock-activation). -Focus is one-shot: the [terminals dock](/plugins/terminals#focusing-a-session) reads `params.sessionId`, the [Data Inspector](/plugins/data-inspector#deep-linking) `params.sourceId`; a target naming something unregistered waits, then fires once. An id that never arrives is a no-op; an unknown `dockId` warns ([DF8107](/errors/DF8107)). +Focus is one-shot: the [terminals dock](/add-ons/devframes/terminals#focusing-a-session) reads `params.sessionId`, the [Data Inspector](/add-ons/devframes/data-inspector#deep-linking) `params.sourceId`; a target naming something unregistered waits, then fires once. An id that never arrives is a no-op; an unknown `dockId` warns ([DF8107](/errors/DF8107)). ## Standalone URL deep links @@ -38,6 +38,6 @@ history.replaceState(history.state, '', `#${params.toString()}`) window.addEventListener('hashchange', applyState) ``` -The [terminals dock](/plugins/terminals#deep-linking) keys a selection as `#id=`; the [Data Inspector](/plugins/data-inspector#deep-linking) encodes its workbench (`#source=…&query=…` plus filter/auto-rerun flags). `replaceState` writes never fire `hashchange`, so boot read, live listener, and write-back don't loop. +The [terminals dock](/add-ons/devframes/terminals#deep-linking) keys a selection as `#id=`; the [Data Inspector](/add-ons/devframes/data-inspector#deep-linking) encodes its workbench (`#source=…&query=…` plus filter/auto-rerun flags). `replaceState` writes never fire `hashchange`, so boot read, live listener, and write-back don't loop. Keep credentials out of anything shareable: a handshake token belongs in the query string, scrubbed once read (as the Data Inspector does), never in a copyable hash. diff --git a/docs/content/1.guide/23.built-with.md b/docs/content/1.guide/23.built-with.md index 23b7d74b..88548964 100644 --- a/docs/content/1.guide/23.built-with.md +++ b/docs/content/1.guide/23.built-with.md @@ -13,18 +13,18 @@ description: 'Real-world devtools and hub UI providers built on devframe — fro ## Built-in Devframes -The [built-in devframes](/plugins) are real tools built on Devframe, each in a different UI framework: +The [built-in devframes](/add-ons) are real tools built on Devframe, each in a different UI framework: | Devframe | UI framework | What it does | |--------|--------------|--------------| -| [Data Inspector](/plugins/data-inspector) | Vue | Query live server-side objects with jora. | -| [Devframe Inspector](/plugins/inspect) | Vue | Browse RPC, shared state, and the agent-consumable API. | -| [Open Graph Viewer](/plugins/og) | Vue | Inspect Open Graph / Twitter metadata and card previews. | -| [Accessibility Inspector](/plugins/a11y) | Solid | Run axe-core; list WCAG violations. | -| [Git](/plugins/git) | React (Next.js) | Repository dashboard: status, graph, branches, diffs. | -| [Terminals](/plugins/terminals) | Svelte | Stream output and run interactive PTY shells. | -| [Code Server](/plugins/code-server) | Vue | Run VS Code in the browser. | -| [Assets](/plugins/assets) | Vue | Browse, preview, upload, rename, and delete files. | +| [Data Inspector](/add-ons/devframes/data-inspector) | Vue | Query live server-side objects with jora. | +| [Devframe Inspector](/add-ons/devframes/inspect) | Vue | Browse RPC, shared state, and the agent-consumable API. | +| [Open Graph Viewer](/add-ons/devframes/og) | Vue | Inspect Open Graph / Twitter metadata and card previews. | +| [Accessibility Inspector](/add-ons/devframes/a11y) | Solid | Run axe-core; list WCAG violations. | +| [Git](/add-ons/devframes/git) | React (Next.js) | Repository dashboard: status, graph, branches, diffs. | +| [Terminals](/add-ons/devframes/terminals) | Svelte | Stream output and run interactive PTY shells. | +| [Code Server](/add-ons/devframes/code-server) | Vue | Run VS Code in the browser. | +| [Assets](/add-ons/devframes/assets) | Vue | Browse, preview, upload, rename, and delete files. | ## Playable Examples diff --git a/docs/content/4.helpers/.navigation.yml b/docs/content/4.helpers/.navigation.yml deleted file mode 100644 index 37165d43..00000000 --- a/docs/content/4.helpers/.navigation.yml +++ /dev/null @@ -1 +0,0 @@ -title: Helpers diff --git a/docs/content/4.helpers/index.md b/docs/content/4.helpers/index.md deleted file mode 100644 index d7839072..00000000 --- a/docs/content/4.helpers/index.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: 'Helpers' -navigation: - icon: i-lucide-wrench -description: 'Helpers are the optional layer around defineDevframe: prebuilt RPC recipes and low-level utilities from the devframe package.' ---- - -Helpers are the optional layer around `defineDevframe`: prebuilt RPC recipes and low-level utilities from the `devframe` package. - -| Helper | Entry | What it does | -|--------|-------|--------------| -| [Utilities](/helpers/utilities) | `devframe/utils/*` | Colors, hashing, editor launch, structured-clone, etc. | -| [Common RPC Functions](/helpers/common-rpc-functions) | `devframe/recipes/common-rpc-functions` | "Open in editor" and "reveal in Finder" actions. | -| [Interactive Auth](/helpers/interactive-auth) | `devframe/recipes/interactive-auth` | OTP auth layer: handshake, resolver gate, connect-time trust, banner. | - -Unlike [adapters](/adapters), which deploy a `DevframeDefinition` as a runnable deployment (CLI, dev server, build, MCP), a helper is a recipe or utility composed with one. - -To integrate a devframe or hub with a meta-framework, see the [framework kits](/frameworks). diff --git a/docs/content/5.add-ons/.navigation.yml b/docs/content/5.add-ons/.navigation.yml new file mode 100644 index 00000000..68b971f4 --- /dev/null +++ b/docs/content/5.add-ons/.navigation.yml @@ -0,0 +1 @@ +title: Add-ons diff --git a/docs/content/5.plugins/1.data-inspector.md b/docs/content/5.add-ons/1.devframes/1.data-inspector.md similarity index 100% rename from docs/content/5.plugins/1.data-inspector.md rename to docs/content/5.add-ons/1.devframes/1.data-inspector.md diff --git a/docs/content/5.plugins/2.inspect.md b/docs/content/5.add-ons/1.devframes/2.inspect.md similarity index 100% rename from docs/content/5.plugins/2.inspect.md rename to docs/content/5.add-ons/1.devframes/2.inspect.md diff --git a/docs/content/5.plugins/3.og.md b/docs/content/5.add-ons/1.devframes/3.og.md similarity index 100% rename from docs/content/5.plugins/3.og.md rename to docs/content/5.add-ons/1.devframes/3.og.md diff --git a/docs/content/5.plugins/4.a11y.md b/docs/content/5.add-ons/1.devframes/4.a11y.md similarity index 100% rename from docs/content/5.plugins/4.a11y.md rename to docs/content/5.add-ons/1.devframes/4.a11y.md diff --git a/docs/content/5.plugins/5.git.md b/docs/content/5.add-ons/1.devframes/5.git.md similarity index 91% rename from docs/content/5.plugins/5.git.md rename to docs/content/5.add-ons/1.devframes/5.git.md index c36aeac2..865c0fc1 100644 --- a/docs/content/5.plugins/5.git.md +++ b/docs/content/5.add-ons/1.devframes/5.git.md @@ -46,6 +46,8 @@ await createCac(createGitDevframe({ repoRoot: process.cwd() })).parse() Namespaced `devframes:plugin:git:*`. Reads — `status`, `log` (+ parent hashes), `branches` (ahead / behind), `diff` (unified patch) — are `query` (`snapshot: true`): live in dev, baked static, `isRepo: false` outside a repo. Write `stage` / `unstage` / `commit` are write-mode `action`s. +For a repo capability shared across devframes rather than this dashboard's own RPC, see the [`@devframes/service-git`](/add-ons/services/git) wire service. + ## Source [`plugins/git`](https://github.com/devframes/devframe/tree/main/plugins/git) diff --git a/docs/content/5.plugins/6.terminals.md b/docs/content/5.add-ons/1.devframes/6.terminals.md similarity index 100% rename from docs/content/5.plugins/6.terminals.md rename to docs/content/5.add-ons/1.devframes/6.terminals.md diff --git a/docs/content/5.plugins/7.code-server.md b/docs/content/5.add-ons/1.devframes/7.code-server.md similarity index 100% rename from docs/content/5.plugins/7.code-server.md rename to docs/content/5.add-ons/1.devframes/7.code-server.md diff --git a/docs/content/5.plugins/8.assets.md b/docs/content/5.add-ons/1.devframes/8.assets.md similarity index 94% rename from docs/content/5.plugins/8.assets.md rename to docs/content/5.add-ons/1.devframes/8.assets.md index 1bf10ef3..ea13916d 100644 --- a/docs/content/5.plugins/8.assets.md +++ b/docs/content/5.add-ons/1.devframes/8.assets.md @@ -82,12 +82,12 @@ Namespaced `devframes:plugin:assets:*`: | `list` | `query`, `snapshot: true` | Every file: type, size, mtime. | | `capabilities` | `query`, `snapshot: true` | Write-enabled flag; the allow-list. | | `read-image-meta` | `query` | Width/height/orientation. | -| `read-text` | `query` | Truncated preview text; server-highlighted via [`@devframes/service-shiki`](/guide/services#built-in-services), else plain `
`. |
+| `read-text` | `query` | Truncated preview text; server-highlighted via [`@devframes/service-shiki`](/add-ons/services/shiki), else plain `
`. |
 | `upload` | `action` | Streaming upload slot; the SPA pipes bytes. |
 | `rename` | `action` | Renames one (keeps extension). |
 | `delete` | `action` | Deletes one or more. |
 | `mkdir` | `action` | Creates a folder + parents. |
-| `open-in-editor` / `reveal-in-folder` | `action` | Open/reveal via [`@devframes/service-open`](/guide/services#built-in-services); always registered. |
+| `open-in-editor` / `reveal-in-folder` | `action` | Open/reveal via [`@devframes/service-open`](/add-ons/services/open); always registered. |
 
 `upload` / `rename` / `delete` / `mkdir` register only with `write`.
 
diff --git a/docs/content/5.add-ons/1.devframes/index.md b/docs/content/5.add-ons/1.devframes/index.md
new file mode 100644
index 00000000..41689f9b
--- /dev/null
+++ b/docs/content/5.add-ons/1.devframes/index.md
@@ -0,0 +1,36 @@
+---
+title: 'Devframes'
+navigation:
+  icon: i-lucide-palette
+description: 'Ready-to-run built-in example devframes. Compose your own DevTools solution from them, run one standalone, or read one as a reference for building your own.'
+---
+
+Ready-to-run built-in example devframes (`@devframes/plugin-*`). Compose your own DevTools solution from them, run one standalone, or read one as a reference for building your own.
+
+| Devframe | UI framework | What it does |
+|--------|--------------|--------------|
+| [Data Inspector](/add-ons/devframes/data-inspector) | Vue | Query live server-side objects with jora. |
+| [Devframe Inspector](/add-ons/devframes/inspect) | Vue | Browse RPC, shared state, and agent exposure. |
+| [Open Graph Viewer](/add-ons/devframes/og) | Vue | Inspect Open Graph / Twitter metadata and card previews. |
+| [Accessibility Inspector](/add-ons/devframes/a11y) | Solid | Run axe-core; list WCAG violations. |
+| [Git](/add-ons/devframes/git) | React (Next.js) | Repository dashboard: status, graph, branches, diffs. |
+| [Terminals](/add-ons/devframes/terminals) | Svelte | Stream output and run interactive PTY shells. |
+| [Code Server](/add-ons/devframes/code-server) | Vue | Run VS Code in the browser. |
+| [Assets](/add-ons/devframes/assets) | Vue | Browse, preview, upload, rename, and delete files. |
+
+## One RPC client, any framework
+
+Each devframe picks its own UI framework yet shares one node-side API — [RPC](/guide/rpc), [shared state](/guide/shared-state), and `connectDevframe`.
+
+## Running a built-in devframe
+
+Most built-in devframes publish a `bin`:
+
+```sh
+pnpx @devframes/plugin-inspect      # the Devframe Inspector, standalone
+pnpx @devframes/plugin-og           # inspect Open Graph metadata and social cards
+pnpx @devframes/plugin-git          # the Git dashboard against the current repo
+pnpx @devframes/plugin-assets       # manage the files under /public
+```
+
+Each also exports a `create…Devframe` factory; see each page for options.
diff --git a/docs/content/5.add-ons/2.services/1.open.md b/docs/content/5.add-ons/2.services/1.open.md
new file mode 100644
index 00000000..f23724a9
--- /dev/null
+++ b/docs/content/5.add-ons/2.services/1.open.md
@@ -0,0 +1,61 @@
+---
+title: 'Open'
+navigation:
+  icon: i-ph:file-arrow-up-duotone
+description: 'The @devframes/service-open wire service: open files in an editor or reveal them in the OS explorer over RPC, with workspace-root path containment and editor gating.'
+---
+
+Open files in an editor or reveal them in the OS explorer, shared over RPC by every devframe on the host. Replaces per-devframe registration of the (deprecated) [`common-rpc-functions`](/references/common-rpc-functions) recipe with one install, feature-detectable from RPC clients, adding workspace-root path containment on top of editor gating.
+
+Package: `@devframes/service-open` · Scope: `devframes:service:open`
+
+## Installing
+
+```ts
+defineDevframe({
+  importMetaUrl: import.meta.url,
+  services: [
+    { package: '@devframes/service-open', options: { editor: 'code' } },
+  ],
+})
+```
+
+A hub installs a shared instance with `initHub({ services: [createOpenService(options)] })`.
+
+## Options
+
+`OpenServiceOptions` — later installer wins on `editor`; `roots` union-merged.
+
+| Option | Type | Description |
+|--------|------|-------------|
+| `editor` | `KnownEditor` | Preferred editor command (one of `KNOWN_EDITORS`). Auto-detected via `LAUNCH_EDITOR` and common defaults when omitted. |
+| `roots` | `string[]` | Extra directories files may be opened from, on top of the context's `workspaceRoot` (e.g. a managed storage dir outside the workspace). |
+
+## RPC functions
+
+Registered under `devframes:service:open:*`, both `action`s. Paths may be absolute or relative to the `workspaceRoot`; the service refuses paths outside the workspace root plus the configured `roots` (`DS_OPEN_0002`), and gates `editor` to the `KNOWN_EDITORS` picklist so the surface can't spawn an arbitrary command.
+
+| Function | Args | Purpose |
+|----------|------|---------|
+| `open-in-editor` | `{ path, line?, column?, editor? }` | Open a file, optionally at a line/column, in the user's editor. |
+| `open-in-finder` | `{ path }` | Reveal a path in the OS file explorer. |
+
+## Node API
+
+`ctx.services.get('@devframes/service-open')` returns the in-process `OpenServiceApi` (no RPC hop server-side): `openInEditor(input)` and `openInFinder({ path })`.
+
+## On the RPC client
+
+```ts
+const rpc = await connectDevframe()
+
+if (rpc.services.has('@devframes/service-open')) {
+  const open = rpc.services.get('@devframes/service-open')!
+  await open.rpc.call('open-in-editor', { path: 'src/main.ts', line: 42, column: 7 })
+  await open.rpc.call('open-in-finder', { path: 'public' })
+}
+```
+
+## Source
+
+[`services/open`](https://github.com/devframes/devframe/tree/main/services/open)
diff --git a/docs/content/5.add-ons/2.services/2.git.md b/docs/content/5.add-ons/2.services/2.git.md
new file mode 100644
index 00000000..e04d487c
--- /dev/null
+++ b/docs/content/5.add-ons/2.services/2.git.md
@@ -0,0 +1,69 @@
+---
+title: 'Git'
+navigation:
+  icon: i-ph:git-branch-duotone
+description: 'The @devframes/service-git wire service: typed read/write git operations over RPC on one repository, shared by every devframe on the host.'
+---
+
+Typed read/write git operations shared over RPC by every devframe on the host, generalizing the utilities that used to live inside the [Git devframe](/add-ons/devframes/git). The exec wrapper and output parsers stay internal; consumers get the typed `GitServiceApi` in-process (`ctx.services.get`) and the same ops over `devframes:service:git:*` RPC.
+
+Package: `@devframes/service-git` · Scope: `devframes:service:git`
+
+## Installing
+
+```ts
+defineDevframe({
+  importMetaUrl: import.meta.url,
+  services: [
+    { package: '@devframes/service-git', options: { cwd: process.cwd() } },
+  ],
+})
+```
+
+A hub installs a shared instance with `initHub({ services: [createGitService(options)] })`.
+
+## Options
+
+`GitServiceOptions` — `cwd` merges as a scalar (later installer wins).
+
+| Option | Type | Description |
+|--------|------|-------------|
+| `cwd` | `string` | Repository directory to operate on. Defaults to the context's `cwd`; the repo root is discovered once (`rev-parse --show-toplevel`) and memoized. |
+
+## RPC functions
+
+Registered under `devframes:service:git:*`. Reads are `query`; writes are `action`. Write ops are **always exposed** — authorization is the host's connection-trust boundary. The service defines no `dump`/`snapshot`; a devframe bakes what it needs via `snapshotRpc`.
+
+| Function | Type | Args | Returns |
+|----------|------|------|---------|
+| `status` | `query` | — | Working-tree status: branch, ahead/behind, staged/unstaged/untracked files. |
+| `log` | `query` | `{ limit?, skip?, ref?, paths? }` | Commit history, newest first (paginated). |
+| `show` | `query` | `{ hash, patch? }` | Full detail of one commit: metadata, files, unified patch. |
+| `readFile` | `query` | `{ path, ref? }` | Contents of a file at a commit-ish (default HEAD). |
+| `diff` | `query` | `{ path?, staged? }` | Unified diff of uncommitted changes. |
+| `branches` | `query` | — | Local branches with tracking state and the current branch. |
+| `tags` | `query` | — | Tags (newest first) with target SHA, date, and subject. |
+| `stage` | `action` | `{ … }` | Stage paths; returns the new status. |
+| `unstage` | `action` | `{ … }` | Unstage paths; returns the new status. |
+| `commit` | `action` | `{ … }` | Create a commit; returns the result. |
+
+The read `query` functions carry agent metadata, so they surface as tools to a [coding agent](/guide/agent-native).
+
+## Node API
+
+`ctx.services.get('@devframes/service-git')` returns the in-process `GitServiceApi` — the same operations without an RPC hop.
+
+## On the RPC client
+
+```ts
+const rpc = await connectDevframe()
+
+if (rpc.services.has('@devframes/service-git')) {
+  const git = rpc.services.get('@devframes/service-git')!
+  const status = await git.rpc.call('status')
+}
+```
+
+## Source
+
+[`services/git`](https://github.com/devframes/devframe/tree/main/services/git)
diff --git a/docs/content/5.add-ons/2.services/3.shiki.md b/docs/content/5.add-ons/2.services/3.shiki.md
new file mode 100644
index 00000000..73245a44
--- /dev/null
+++ b/docs/content/5.add-ons/2.services/3.shiki.md
@@ -0,0 +1,61 @@
+---
+title: 'Shiki'
+navigation:
+  icon: i-ph:paint-brush-duotone
+description: 'The @devframes/service-shiki wire service: node-side Shiki syntax highlighting shared over RPC, LRU-cached and dual-theme, so client bundles stop shipping their own grammars and themes.'
+---
+
+Server-side [Shiki](https://shiki.style) syntax highlighting shared over RPC by every devframe on the host, so client bundles stop shipping their own grammars and themes. Shiki itself loads lazily on first use; results are LRU-cached per `(code, lang, themes)` and every RPC function is `cacheable` on the client too.
+
+Package: `@devframes/service-shiki` · Scope: `devframes:service:shiki`
+
+## Installing
+
+```ts
+defineDevframe({
+  importMetaUrl: import.meta.url,
+  services: [
+    { package: '@devframes/service-shiki', options: { langs: ['vue', 'ts'] } },
+  ],
+})
+```
+
+A hub installs a shared instance with `initHub({ services: [createShikiService(options)] })`.
+
+## Options
+
+`ShikiServiceOptions` — later installer wins on `themes` (deep-merged per key); `langs` union-merged.
+
+| Option | Type | Description |
+|--------|------|-------------|
+| `themes` | `{ light, dark }` | Dual light/dark theme pair every request uses unless it carries its own. Defaults to `vitesse-light` / `vitesse-dark`. |
+| `langs` | `string[]` | Languages to eagerly load at setup. Others load on demand per request; unknown ids degrade to plain text instead of throwing. |
+
+## RPC functions
+
+Registered under `devframes:service:shiki:*`, all `query` and `cacheable`. Each takes `{ code, lang?, themes? }`.
+
+| Function | Returns |
+|----------|---------|
+| `highlight` | `{ html }` — dual-theme HTML (light values inline, dark via `--shiki-dark` vars). |
+| `code-to-hast` | A HAST tree, for surfaces that render their own DOM. |
+| `code-to-tokens` | Themed tokens, for line-oriented renderers (e.g. diff views). |
+
+## Node API
+
+`ctx.services.get('@devframes/service-shiki')` returns the in-process `ShikiServiceApi`: `highlight`, `codeToHast`, and `codeToTokens`.
+
+## On the RPC client
+
+```ts
+const rpc = await connectDevframe()
+
+if (rpc.services.has('@devframes/service-shiki')) {
+  const shiki = rpc.services.get('@devframes/service-shiki')!
+  const { html } = await shiki.rpc.call('highlight', { code, lang: 'ts' })
+}
+```
+
+## Source
+
+[`services/shiki`](https://github.com/devframes/devframe/tree/main/services/shiki)
diff --git a/docs/content/5.add-ons/2.services/index.md b/docs/content/5.add-ons/2.services/index.md
new file mode 100644
index 00000000..ed271be4
--- /dev/null
+++ b/docs/content/5.add-ons/2.services/index.md
@@ -0,0 +1,30 @@
+---
+title: 'Services'
+navigation:
+  icon: i-lucide-share-2
+description: 'Built-in wire services (@devframes/service-*): one node-side capability installed once per host and consumed by every devframe and RPC client, without re-implementing or re-bundling it.'
+---
+
+Built-in [wire services](/guide/services#wire-services) (`@devframes/service-*`) — one node-side capability installed once per host and consumed by every devframe and RPC client, without re-implementing or re-bundling it. See [Cross-Devframe Services](/guide/services) for the mechanism and the [Services reference](/references/services) for the host API.
+
+| Service | Scope | RPC functions | What it does |
+|---------|-------|---------------|--------------|
+| [Open](/add-ons/services/open) | `devframes:service:open` | `open-in-editor`, `open-in-finder` | Open files in an editor or reveal them in the OS explorer, refusing paths outside the workspace. |
+| [Git](/add-ons/services/git) | `devframes:service:git` | `status`, `log`, `show`, `readFile`, `diff`, `branches`, `tags`, `stage`, `unstage`, `commit` | Typed read/write git operations over RPC on one repo. |
+| [Shiki](/add-ons/services/shiki) | `devframes:service:shiki` | `highlight`, `code-to-hast`, `code-to-tokens` | Node-side [Shiki](https://shiki.style) syntax highlighting, LRU-cached and dual-theme. |
+
+## Installing a service
+
+Services are **declarative** — a devframe lists what it consumes on its definition; a hub lists shared ones on `initHub`:
+
+```ts
+defineDevframe({
+  importMetaUrl: import.meta.url, // resolution base for the declared packages
+  services: [
+    { package: '@devframes/service-open' },
+    { package: '@devframes/service-shiki', version: '^1', options: { langs: ['vue'] } },
+  ],
+})
+```
+
+The host constructs each declared service **once**, before any `setup(ctx)` runs, so setup consumes it synchronously via `ctx.services.get(pkg)`. On the RPC client, feature-detect with `rpc.services.has(pkg)`.
diff --git a/docs/content/5.add-ons/index.md b/docs/content/5.add-ons/index.md
new file mode 100644
index 00000000..e6ea90d6
--- /dev/null
+++ b/docs/content/5.add-ons/index.md
@@ -0,0 +1,49 @@
+---
+title: 'Add-ons'
+navigation:
+  icon: i-lucide-blocks
+description: 'Ready-to-run packages built on Devframe: built-in devframes you can run, compose, or learn from, and wire services that share one node-side capability across every devframe on a host.'
+---
+
+Ready-to-run packages built on Devframe, in two families:
+
+- **[Devframes](/add-ons/devframes)** — complete built-in devtools (`@devframes/plugin-*`). Run one standalone, compose several into your own DevTools, or read one as a reference for building your own.
+- **[Services](/add-ons/services)** — wire services (`@devframes/service-*`): one node-side capability installed once per host and consumed by every devframe and RPC client, without re-bundling it.
+
+## Devframes
+
+Complete tools, each picking its own UI framework yet sharing one node-side API — [RPC](/guide/rpc), [shared state](/guide/shared-state), and `connectDevframe`.
+
+| Devframe | UI framework | What it does |
+|--------|--------------|--------------|
+| [Data Inspector](/add-ons/devframes/data-inspector) | Vue | Query live server-side objects with jora. |
+| [Devframe Inspector](/add-ons/devframes/inspect) | Vue | Browse RPC, shared state, and agent exposure. |
+| [Open Graph Viewer](/add-ons/devframes/og) | Vue | Inspect Open Graph / Twitter metadata and card previews. |
+| [Accessibility Inspector](/add-ons/devframes/a11y) | Solid | Run axe-core; list WCAG violations. |
+| [Git](/add-ons/devframes/git) | React (Next.js) | Repository dashboard: status, graph, branches, diffs. |
+| [Terminals](/add-ons/devframes/terminals) | Svelte | Stream output and run interactive PTY shells. |
+| [Code Server](/add-ons/devframes/code-server) | Vue | Run VS Code in the browser. |
+| [Assets](/add-ons/devframes/assets) | Vue | Browse, preview, upload, rename, and delete files. |
+
+## Services
+
+Shared node-side capabilities other devframes install and consume — see [Cross-Devframe Services](/guide/services) for the mechanism, and the [Services reference](/references/services) for the host API.
+
+| Service | Scope | What it does |
+|---------|-------|--------------|
+| [Open](/add-ons/services/open) (`@devframes/service-open`) | `devframes:service:open` | Open files in an editor or the OS explorer, with workspace-root containment. |
+| [Git](/add-ons/services/git) (`@devframes/service-git`) | `devframes:service:git` | Typed read/write git operations over RPC on one repo. |
+| [Shiki](/add-ons/services/shiki) (`@devframes/service-shiki`) | `devframes:service:shiki` | Node-side [Shiki](https://shiki.style) syntax highlighting, cached and dual-theme. |
+
+## Running a built-in devframe
+
+Most built-in devframes publish a `bin`:
+
+```sh
+pnpx @devframes/plugin-inspect      # the Devframe Inspector, standalone
+pnpx @devframes/plugin-og           # inspect Open Graph metadata and social cards
+pnpx @devframes/plugin-git          # the Git dashboard against the current repo
+pnpx @devframes/plugin-assets       # manage the files under /public
+```
+
+Each also exports a `create…Devframe` factory; see each page for options.
diff --git a/docs/content/5.plugins/.navigation.yml b/docs/content/5.plugins/.navigation.yml
deleted file mode 100644
index c5953d19..00000000
--- a/docs/content/5.plugins/.navigation.yml
+++ /dev/null
@@ -1 +0,0 @@
-title: Example Devframes
diff --git a/docs/content/5.plugins/index.md b/docs/content/5.plugins/index.md
deleted file mode 100644
index 46fd267a..00000000
--- a/docs/content/5.plugins/index.md
+++ /dev/null
@@ -1,36 +0,0 @@
----
-title: 'Built-in Devframes'
-navigation:
-  icon: i-lucide-palette
-description: 'Devframe also provides ready-to-run built-in example devframes. You can use them to compose your own DevTools solution, use them standalone, or as a reference for building your own devframe.'
----
-
-Devframe also provides ready-to-run built-in example devframes. You can use them to compose your own DevTools solution, use them standalone, or as a reference for building your own devframe.
-
-| Devframe | UI framework | What it does |
-|--------|--------------|--------------|
-| [Data Inspector](/plugins/data-inspector) | Vue | Query live server-side objects with jora. |
-| [Devframe Inspector](/plugins/inspect) | Vue | Browse RPC, shared state, and agent exposure. |
-| [Open Graph Viewer](/plugins/og) | Vue | Inspect Open Graph / Twitter metadata and card previews. |
-| [Accessibility Inspector](/plugins/a11y) | Solid | Run axe-core; list WCAG violations. |
-| [Git](/plugins/git) | React (Next.js) | Repository dashboard: status, graph, branches, diffs. |
-| [Terminals](/plugins/terminals) | Svelte | Stream output and run interactive PTY shells. |
-| [Code Server](/plugins/code-server) | Vue | Run VS Code in the browser. |
-| [Assets](/plugins/assets) | Vue | Browse, preview, upload, rename, and delete files. |
-
-## One RPC client, any framework
-
-Each devframe picks its own UI framework yet shares one node-side API — [RPC](/guide/rpc), [shared state](/guide/shared-state), and `connectDevframe`.
-
-## Running a built-in devframe
-
-Most built-in devframes publish a `bin`:
-
-```sh
-pnpx @devframes/plugin-inspect      # the Devframe Inspector, standalone
-pnpx @devframes/plugin-og           # inspect Open Graph metadata and social cards
-pnpx @devframes/plugin-git          # the Git dashboard against the current repo
-pnpx @devframes/plugin-assets       # manage the files under /public
-```
-
-Each also exports a `create…Devframe` factory; see each page for options.
diff --git a/docs/content/7.migrations/4.migration-0.6.md b/docs/content/7.migrations/4.migration-0.6.md
index 457be055..fb835365 100644
--- a/docs/content/7.migrations/4.migration-0.6.md
+++ b/docs/content/7.migrations/4.migration-0.6.md
@@ -52,7 +52,7 @@ Two gates landed on the RPC socket:
 
   Pass `allowedOrigins: false` to disable the check (not recommended).
 
-- **Real authorization enforcement, opt-in.** `auth: true` (default) stays permissive. A `DevframeAuthHandler` turns on the [pre-trust gate](/guide/security#the-pre-trust-gate): untrusted callers reach only `anonymous:` methods, else [`DF0036`](/errors/DF0036) throws. The [Interactive Auth](/helpers/interactive-auth) recipe builds one:
+- **Real authorization enforcement, opt-in.** `auth: true` (default) stays permissive. A `DevframeAuthHandler` turns on the [pre-trust gate](/guide/security#the-pre-trust-gate): untrusted callers reach only `anonymous:` methods, else [`DF0036`](/errors/DF0036) throws. The [Interactive Auth](/references/interactive-auth) recipe builds one:
 
   ```ts
   import { startHttpAndWs } from 'devframe/node'
diff --git a/docs/content/4.helpers/3.interactive-auth.md b/docs/content/8.references/10.interactive-auth.md
similarity index 100%
rename from docs/content/4.helpers/3.interactive-auth.md
rename to docs/content/8.references/10.interactive-auth.md
diff --git a/docs/content/8.references/11.services.md b/docs/content/8.references/11.services.md
new file mode 100644
index 00000000..34edf1ed
--- /dev/null
+++ b/docs/content/8.references/11.services.md
@@ -0,0 +1,77 @@
+---
+title: 'Services'
+navigation:
+  icon: i-lucide-share-2
+description: 'Lookup tables for ctx.services: the DevframeServicesHost methods, the wire-service definition and descriptor fields, the advertised meta shape, and the first-party built-in services.'
+---
+
+Lookup tables for `ctx.services`, the cross-devframe capability registry. Each section links the [Cross-Devframe Services](/guide/services) guide that teaches it.
+
+## `DevframeServicesHost`
+
+The methods on `ctx.services` — [Cross-Devframe Services](/guide/services#the-devframeserviceshost-api).
+
+| Method | Signature | Role |
+|--------|-----------|------|
+| `provide` | `(id, service) => revoke` | Publish an in-process service under a namespaced id. Throws [`DF0037`](/errors/DF0037) if the id is taken. |
+| `get` | `(id) => service \| undefined` | The service currently provided under `id` (augmented type, else `unknown`). |
+| `has` | `(id) => boolean` | Whether a service is provided under `id`. |
+| `whenAvailable` | `(id, cb) => unsubscribe` | Run `cb` as soon as the service exists — now if provided, else on `provide` — and re-fire on revoke/re-provide. |
+| `keys` | `() => string[]` | Ids of every currently-provided service. |
+| `install` | `(input, options?) => Promise` | Install a [wire service](#wire-service-definition-fields) at runtime (the dynamic escape hatch; the common path is declarative). `options.resolveFrom` is the descriptor's resolution base. |
+| `ready` | `() => Promise` | **Internal.** Construct every queued wire service before any `setup` runs. Adapters call it; application code uses declarative `services`. |
+
+## Service tiers
+
+The two tiers a service can take — [Cross-Devframe Services](/guide/services).
+
+| Tier | Shared how | Registers RPC | Advertised to clients |
+|------|-----------|---------------|-----------------------|
+| In-process service (`provide`/`get`) | live object, node side only | No | No |
+| [Wire service](/guide/services#wire-services) (`install` / declarative `services`) | npm package, node API + RPC | Yes, under its `scope` | Yes, via `devframe:services` shared state |
+
+## Wire-service definition fields
+
+The fields of a `DevframeServiceDefinition` returned by a service package's `createService` factory — [Shipping a wire service](/guide/services#shipping-one).
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `package` | `string` | **Required.** npm package name — also its registry key (`ctx.services.has(pkg)`). |
+| `version` | `string` | **Required.** Semver; advertised to clients, checked against declared ranges. |
+| `scope` | `string` | **Required.** RPC namespace its functions register under (e.g. `devframes:service:open`); `setup` gets a context pre-scoped to it. |
+| `meta` | `Record` | Extra advertised metadata (feature flags, defaults). Must be JSON-serializable. |
+| `options` | `Options` | This instance's own option set, baked in by its factory; joins the merge. |
+| `mergeOptions` | `(sets: Options[]) => Options` | Merge multiple installers' option sets. Default: shallow, later wins. |
+| `setup` | `(ctx, info) => api` | **Required.** Register RPC on the pre-scoped context; return the node API served from `ctx.services.get(package)`. |
+
+## Wire-service descriptor fields
+
+The declarative reference form on `DevframeDefinition.services` / `initHub({ services })` — [Declaring services](/guide/services#declaring).
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `package` | `string` | **Required.** npm package name; its default export is the factory the host imports. |
+| `version` | `string` | Accepted semver range. Unsatisfied warns ([`DF0069`](/errors/DF0069)), or throws ([`DF0068`](/errors/DF0068)) when `required`. |
+| `required` | `boolean` | Fail hard on a missing package ([`DF0067`](/errors/DF0067)) or unsatisfied range. Default `false` — a missing service is skipped and clients see `has() === false`. |
+| `options` | `Options` | Option set this installer contributes to the merge. |
+
+## Advertised service meta
+
+Each installed service's entry in the `devframe:services` [shared state](/guide/shared-state), mirrored to RPC clients as `rpc.services` — [Feature-detecting on the RPC client](/guide/services#feature-detecting-on-the-rpc-client).
+
+| Field | Description |
+|-------|-------------|
+| `package` | npm package name — the registry key. |
+| `version` | Installed version of the service. |
+| `scope` | RPC namespace its functions live under. |
+| `meta` | Extra service-declared metadata. |
+
+## Built-in services
+
+The first-party wire services (`@devframes/service-*`), each installed once per host and consumed by any devframe or RPC client. Each has its own page under [Add-ons › Services](/add-ons/services).
+
+| Package | Scope | RPC functions | Page |
+|---------|-------|---------------|------|
+| `@devframes/service-open` | `devframes:service:open` | `open-in-editor`, `open-in-finder` | [Open](/add-ons/services/open) |
+| `@devframes/service-git` | `devframes:service:git` | `status`, `log`, `show`, `readFile`, `diff`, `branches`, `tags`, `stage`, `unstage`, `commit` | [Git](/add-ons/services/git) |
+| `@devframes/service-shiki` | `devframes:service:shiki` | `highlight`, `code-to-hast`, `code-to-tokens` | [Shiki](/add-ons/services/shiki) |
diff --git a/docs/content/8.references/6.hub-api.md b/docs/content/8.references/6.hub-api.md
index 397c4941..abb6972a 100644
--- a/docs/content/8.references/6.hub-api.md
+++ b/docs/content/8.references/6.hub-api.md
@@ -14,7 +14,7 @@ What `DevframeHubContext` adds to `DevframeNodeContext` — [Hub](/guide/hub).
 | Subsystem | API | Purpose |
 |---|---|---|
 | `ctx.docks` | `register / update / values / activate` | Dock entries (iframes, launchers, custom-render) and groups; `activate(dockId, params?)` sets the active dock ([Cross-iframe dock activation](/guide/hub#cross-iframe-dock-activation)). |
-| `ctx.terminals` | `register / startChildProcess` | Aggregate terminal sessions, streaming output ([Terminals](/plugins/terminals#hub-aggregation)). |
+| `ctx.terminals` | `register / startChildProcess` | Aggregate terminal sessions, streaming output ([Terminals](/add-ons/devframes/terminals#hub-aggregation)). |
 | `ctx.messages` | `add / update / remove / clear` | Server-side toast/notification queue (FIFO, capped at 1000). |
 | `ctx.commands` | `register / execute / list` | Hierarchical command palette with keybindings and `when` clauses. |
 
diff --git a/docs/content/8.references/7.helpers.md b/docs/content/8.references/7.helpers.md
new file mode 100644
index 00000000..7906e13c
--- /dev/null
+++ b/docs/content/8.references/7.helpers.md
@@ -0,0 +1,18 @@
+---
+title: 'Helpers'
+navigation:
+  icon: i-lucide-wrench
+description: 'The optional layer around defineDevframe: prebuilt RPC recipes and low-level utilities from the devframe package, each on its own reference page.'
+---
+
+Helpers are the optional layer around `defineDevframe`: prebuilt RPC recipes and low-level utilities from the `devframe` package.
+
+| Helper | Entry | What it does |
+|--------|-------|--------------|
+| [Utilities](/references/utilities) | `devframe/utils/*` | Colors, hashing, editor launch, structured-clone, etc. |
+| [Common RPC Functions](/references/common-rpc-functions) | `devframe/recipes/common-rpc-functions` | "Open in editor" and "reveal in Finder" actions. |
+| [Interactive Auth](/references/interactive-auth) | `devframe/recipes/interactive-auth` | OTP auth layer: handshake, resolver gate, connect-time trust, banner. |
+
+Unlike [adapters](/adapters), which deploy a `DevframeDefinition` as a runnable deployment (CLI, dev server, build, MCP), a helper is a recipe or utility composed with one.
+
+To integrate a devframe or hub with a meta-framework, see the [framework kits](/frameworks).
diff --git a/docs/content/4.helpers/1.utilities.md b/docs/content/8.references/8.utilities.md
similarity index 97%
rename from docs/content/4.helpers/1.utilities.md
rename to docs/content/8.references/8.utilities.md
index 5132ac46..16ca68b7 100644
--- a/docs/content/4.helpers/1.utilities.md
+++ b/docs/content/8.references/8.utilities.md
@@ -45,7 +45,7 @@ launchEditor('src/main.ts:42:7')
 launchEditor('src/main.ts:42:7', 'code')
 ```
 
-Auto-detection reads `LAUNCH_EDITOR`, else defaults; most use the `openInEditor` recipe ([Common RPC Functions](/helpers/common-rpc-functions)).
+Auto-detection reads `LAUNCH_EDITOR`, else defaults; most use the `openInEditor` recipe ([Common RPC Functions](/references/common-rpc-functions)).
 
 ### `devframe/utils/hash`
 
diff --git a/docs/content/4.helpers/2.common-rpc-functions.md b/docs/content/8.references/9.common-rpc-functions.md
similarity index 77%
rename from docs/content/4.helpers/2.common-rpc-functions.md
rename to docs/content/8.references/9.common-rpc-functions.md
index 6a6235cd..0790ac2d 100644
--- a/docs/content/4.helpers/2.common-rpc-functions.md
+++ b/docs/content/8.references/9.common-rpc-functions.md
@@ -6,7 +6,7 @@ description: 'Prebuilt RPC actions: open a file in an editor, reveal a path in t
 ---
 
 > [!WARNING]
-> Deprecated for the [`@devframes/service-open` wire service](/guide/services#built-in-services) — one node-side install shared by every devframe, feature-detectable from RPC clients, with workspace-root path containment atop the editor gating. The recipe keeps working; removal in a future major.
+> Deprecated for the [`@devframes/service-open` wire service](/add-ons/services/open) — one node-side install shared by every devframe, feature-detectable from RPC clients, with workspace-root path containment atop the editor gating. The recipe keeps working; removal in a future major.
 
 Prebuilt RPC actions: open a file in an editor, reveal a path in the OS.
 
@@ -26,8 +26,8 @@ defineDevframe({
 
 | Export | Registered name | Type | Args | Purpose |
 |--------|------------------|------|------|---------|
-| `openInEditor` | `devframe:open-in-editor` | `action` | `[filename: string, editor?: KnownEditor]` | Open via [`launchEditor`](/helpers/utilities#devframe-utils-launch-editor); `filename` = `file`/`file:line`/`file:line:column`. |
-| `openInFinder` | `devframe:open-in-finder` | `action` | `[path: string]` | Reveal via [`open`](/helpers/utilities#devframe-utils-open) |
+| `openInEditor` | `devframe:open-in-editor` | `action` | `[filename: string, editor?: KnownEditor]` | Open via [`launchEditor`](/references/utilities#devframe-utils-launch-editor); `filename` = `file`/`file:line`/`file:line:column`. |
+| `openInFinder` | `devframe:open-in-finder` | `action` | `[path: string]` | Reveal via [`open`](/references/utilities#devframe-utils-open) |
 | `commonRpcFunctions` | — | `readonly [openInEditor, openInFinder]` | — | Batch-registration array. |
 | `KNOWN_EDITORS` | — | `readonly string[]` | — | Accepted commands (`code`, `vim`, …). |
 | `KnownEditor` | — | type | — | Union of `KNOWN_EDITORS`. |
diff --git a/docs/content/8.references/index.md b/docs/content/8.references/index.md
index 21e866a9..6709c93e 100644
--- a/docs/content/8.references/index.md
+++ b/docs/content/8.references/index.md
@@ -2,7 +2,7 @@
 title: 'References'
 navigation:
   icon: i-lucide-book-marked
-description: 'Lookup pages the guides link into: the canonical terms, the when-clause contexts, every event name on the wire, and the API tables for the node side, the browser side, and the hub.'
+description: 'Lookup pages the guides link into: the canonical terms, the when-clause contexts, every event name on the wire, the API tables for the node side, the browser side, and the hub, and the helpers, utilities, and services surfaces.'
 ---
 
 Lookup pages the guides link into:
@@ -13,5 +13,7 @@ Lookup pages the guides link into:
 - [Node-Side API](/references/node-api) — `DevframeDefinition` fields, CLI options, storage scopes, RPC function types, broadcast options, streaming lifecycle, remote assets, diagnostics prefixes, and the auth surface.
 - [Browser-Side API](/references/browser-api) — `connectDevframe` options, RPC client events, connection statuses, and in-page channel error codes.
 - [Hub API](/references/hub-api) — hub subsystems, launcher fields, duplication strategies, dock categories, the hub UI protocol, the namespace routes, the client runtime, the client context, and dock entry types.
+- [Helpers](/references/helpers) — the optional layer around `defineDevframe`: the [utilities](/references/utilities), the [common RPC functions](/references/common-rpc-functions), and the [interactive auth](/references/interactive-auth) recipe.
+- [Services](/references/services) — the `DevframeServicesHost` methods, the wire-service definition and descriptor fields, and the advertised meta shape (the built-in services live under [Add-ons](/add-ons/services)).
 
 The [error reference](/errors) documents each `DF*` diagnostic code, and [migrations](/migrations) each version step. The adapter, framework-kit, helper, and built-in-devframe pages each carry their own package's options and RPC tables.
diff --git a/docs/content/9.posts/1.pluggable-extensible-playful-devtools.md b/docs/content/9.posts/1.pluggable-extensible-playful-devtools.md
index d4e88976..55775f4a 100644
--- a/docs/content/9.posts/1.pluggable-extensible-playful-devtools.md
+++ b/docs/content/9.posts/1.pluggable-extensible-playful-devtools.md
@@ -140,13 +140,13 @@ We are still exploring the APIs and practices around discoverability, permission
 
 ### Built-in Devframes
 
-Real tools make the abstraction convincing. To test Devframe's capabilities and framework-neutral design, we ship a few [built-in devframes](/plugins) as reusable working examples. They intentionally use **different UI frameworks**, and each can run through a standalone adapter or mount into a supported host framework.
+Real tools make the abstraction convincing. To test Devframe's capabilities and framework-neutral design, we ship a few [built-in devframes](/add-ons) as reusable working examples. They intentionally use **different UI frameworks**, and each can run through a standalone adapter or mount into a supported host framework.
 
 Here are a few examples:
 
 #### Data Inspector
 
-[`@devframes/plugin-data-inspector`](/plugins/data-inspector) is built with Vue and provides an interactive workbench for live node-side objects. A tool can register an object as a data source, then explore and query it with [Jora](https://discoveryjs.github.io/jora/) inside the process that owns it.
+[`@devframes/plugin-data-inspector`](/add-ons/devframes/data-inspector) is built with Vue and provides an interactive workbench for live node-side objects. A tool can register an object as a data source, then explore and query it with [Jora](https://discoveryjs.github.io/jora/) inside the process that owns it.
 
 Through its standalone adapters, it can inspect JSON or JSONL files, build a self-contained report, or attach to a running Node.js process. This is useful for inspecting stores, caches, framework contexts, build metadata, or other states that usually require custom logging.
 
@@ -165,7 +165,7 @@ When mounted, other tools contribute data sources. A Vite plugin could expose it
 
 #### Terminals
 
-[`@devframes/plugin-terminals`](/plugins/terminals) is built with Svelte and provides a browser-based terminal panel supporting read-only process output and interactive PTY sessions.
+[`@devframes/plugin-terminals`](/add-ons/devframes/terminals) is built with Svelte and provides a browser-based terminal panel supporting read-only process output and interactive PTY sessions.
 
 This separates the process-running capability from the tool that renders it. A DevTools host can give multiple tools a consistent place for subprocess output and interactive commands while keeping the user's main terminal focused.
 
@@ -189,7 +189,7 @@ This opens the interactive terminal directly in your browser. You can use it to
 
 #### Accessibility Inspector
 
-[`@devframes/plugin-a11y`](/plugins/a11y) is built with Solid. Its page script scans the user app with [axe-core](https://github.com/dequelabs/axe-core), lists WCAG violations, and highlights the corresponding elements on the page. It can also turn the findings into fix prompts for coding agents, connecting visual inspection with a coding-agent workflow.
+[`@devframes/plugin-a11y`](/add-ons/devframes/a11y) is built with Solid. Its page script scans the user app with [axe-core](https://github.com/dequelabs/axe-core), lists WCAG violations, and highlights the corresponding elements on the page. It can also turn the findings into fix prompts for coding agents, connecting visual inspection with a coding-agent workflow.
 
 With a standalone adapter, its panel and page script can inspect any page. Inside a DevTools host, the same findings can also be mirrored into the shared message feed.
 
@@ -202,7 +202,7 @@ It is heavily inspired by [`@nuxt/a11y`](https://github.com/nuxt/a11y), which br
 
 #### More Built-in Devframes
 
-Other built-in devframes cover a [VS Code editor on the web](/plugins/code-server), [asset management](/plugins/assets), a [Git panel](/plugins/git), [Open Graph previews](/plugins/og), and [Devframe's own RPC and state inspector](/plugins/inspect). They share the devframe definition and protocol while each chooses its own UI framework.
+Other built-in devframes cover a [VS Code editor on the web](/add-ons/devframes/code-server), [asset management](/add-ons/devframes/assets), a [Git panel](/add-ons/devframes/git), [Open Graph previews](/add-ons/devframes/og), and [Devframe's own RPC and state inspector](/add-ons/devframes/inspect). They share the devframe definition and protocol while each chooses its own UI framework.
 
 These built-in devframes show what Devframe can support and offer starting points for communities to build their own. We believe many more interesting DevTools will emerge over time. You can follow the growing list on [Built with Devframe](/guide/built-with).
 
diff --git a/docs/content/index.md b/docs/content/index.md
index 6c2b881c..6c7b313f 100644
--- a/docs/content/index.md
+++ b/docs/content/index.md
@@ -119,7 +119,7 @@ One definition, every entry point
   `@devframes/hub` composes many devframes behind one handler with docks, commands, terminals, and messages.
   :::
 
-  :::landing-feature-card{icon="i-lucide-palette" to="/plugins"}
+  :::landing-feature-card{icon="i-lucide-palette" to="/add-ons"}
   #title
   Built-in Devframes, Any Framework
 
diff --git a/docs/nuxt.config.ts b/docs/nuxt.config.ts
index 6ae6ac21..a3483adb 100644
--- a/docs/nuxt.config.ts
+++ b/docs/nuxt.config.ts
@@ -25,7 +25,7 @@ export default defineNuxtConfig({
     full: {
       title: 'Devframe Documentation',
       description:
-        'Complete Devframe documentation as plain markdown — guide, adapters, frameworks, helpers, plugins, references, and the error reference.',
+        'Complete Devframe documentation as plain markdown — guide, adapters, frameworks, add-ons, references, and the error reference.',
     },
   },
 
diff --git a/docs/vercel.json b/docs/vercel.json
index 80282db6..deb41bef 100644
--- a/docs/vercel.json
+++ b/docs/vercel.json
@@ -7,6 +7,19 @@
   "redirects": [
     { "source": "/guide/terms", "destination": "/references/terms", "permanent": true },
     { "source": "/guide/when-clauses", "destination": "/references/when-clauses", "permanent": true },
-    { "source": "/guide/events", "destination": "/references/events", "permanent": true }
+    { "source": "/guide/events", "destination": "/references/events", "permanent": true },
+    { "source": "/plugins", "destination": "/add-ons", "permanent": true },
+    { "source": "/plugins/data-inspector", "destination": "/add-ons/devframes/data-inspector", "permanent": true },
+    { "source": "/plugins/inspect", "destination": "/add-ons/devframes/inspect", "permanent": true },
+    { "source": "/plugins/og", "destination": "/add-ons/devframes/og", "permanent": true },
+    { "source": "/plugins/a11y", "destination": "/add-ons/devframes/a11y", "permanent": true },
+    { "source": "/plugins/git", "destination": "/add-ons/devframes/git", "permanent": true },
+    { "source": "/plugins/terminals", "destination": "/add-ons/devframes/terminals", "permanent": true },
+    { "source": "/plugins/code-server", "destination": "/add-ons/devframes/code-server", "permanent": true },
+    { "source": "/plugins/assets", "destination": "/add-ons/devframes/assets", "permanent": true },
+    { "source": "/helpers", "destination": "/references/helpers", "permanent": true },
+    { "source": "/helpers/utilities", "destination": "/references/utilities", "permanent": true },
+    { "source": "/helpers/common-rpc-functions", "destination": "/references/common-rpc-functions", "permanent": true },
+    { "source": "/helpers/interactive-auth", "destination": "/references/interactive-auth", "permanent": true }
   ]
 }
diff --git a/knip.jsonc b/knip.jsonc
index d8f926c6..51200959 100644
--- a/knip.jsonc
+++ b/knip.jsonc
@@ -51,7 +51,12 @@
       // component and mirrors its imports verbatim (see the file header). These
       // packages are provided transitively by the `comark-docs` layer, not
       // declared in `docs/package.json`, so knip sees them as unlisted.
-      "ignoreDependencies": ["ai", "@ai-sdk/vue", "@comark/nuxt.*", "@nuxt/ui.*"]
+      //
+      // `@shikijs/magic-move` is declared in `docs/package.json` but only ever
+      // referenced as the `@shikijs/magic-move/style.css` subpath in the
+      // `nuxt.config.ts` `css` array; knip can't map that CSS-only subpath back
+      // to the package, so it mis-reports the (genuinely-listed) dep as unlisted.
+      "ignoreDependencies": ["ai", "@ai-sdk/vue", "@comark/nuxt.*", "@nuxt/ui.*", "@shikijs/magic-move"]
     },
     "examples/hub-next": {
       // The React client build's root sits at `src/client` (`next dev