Skip to content
Merged
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
58 changes: 41 additions & 17 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# AGENTS GUIDE

## Positioning

Two layers, one mental model:

- **`devframe`** — *the container for one devtool integration, portable across viewers.* Build a single tool (its RPC, its SPA, its diagnostics, its CLI/build/spa/embedded outputs) without caring how it'll be displayed. A devframe app runs standalone (CLI, static deploy, embedded SPA) just as well as it mounts inside a hub.
- **`@vitejs/devtools-kit`** — *the hub that unites many devtools integrations.* Owns docking, the command palette, toasts, terminal sessions — anything that only makes sense when more than one tool shares a UI. Provides `createPluginFromDevframe(devtoolApp)` so a portable devframe definition drops into Vite DevTools as a Vite plugin, with the dock entry auto-derived from the definition's metadata.

When deciding where something belongs: if a single-app standalone CLI would still need it, it lives in devframe; if it only matters once you have multiple integrations or a host UI, it lives in the kit.

## Stack & Structure

Monorepo (`pnpm` workspaces + `turbo`). ESM TypeScript; bundled with `tsdown`. Path aliases in `alias.ts` (propagated to `tsconfig.base.json` — do not edit manually).
Expand All @@ -8,14 +17,14 @@ Monorepo (`pnpm` workspaces + `turbo`). ESM TypeScript; bundled with `tsdown`. P

| Package | npm | Description |
|---------|-----|-------------|
| `packages/devframe` | `devframe` | Framework-neutral foundation — RPC layer (birpc + valibot + WS presets), host classes, createHostContext, six adapters at `devframe/adapters/*` (cli/build/spa/vite/kit/embedded), connectDevtool client |
| `packages/core` | `@vitejs/devtools` | Vite plugin, CLI, standalone/webcomponents client. Wraps devframe's createHostContext with the Vite plugin scan |
| `packages/kit` | `@vitejs/devtools-kit` | Vite-specific superset of devframe — adds PluginWithDevTools, ViteDevToolsNodeContext, and re-exports devframe's public types |
| `packages/ui` | `@vitejs/devtools-ui` | Shared UI components, composables, and UnoCSS preset (`presetDevToolsUI`). Private, not published |
| `packages/rolldown` | `@vitejs/devtools-rolldown` | Nuxt UI for Rolldown build data. Serves at `/__devtools-rolldown/` |
| `packages/vite` | `@vitejs/devtools-vite` | Nuxt UI for Vite DevTools (WIP). Serves at `/__devtools-vite/` |
| `packages/self-inspect` | `@vitejs/devtools-self-inspect` | Meta-introspection — DevTools for the DevTools. Serves at `/__devtools-self-inspect/` |
| `packages/webext` | — | Browser extension scaffolding (ancillary) |
| `devframe/packages/devframe` | `devframe` | Framework-neutral container for one devtool integration. RPC layer (birpc + valibot + WS presets), `createHostContext`, six deployment adapters at `devframe/adapters/*` (cli/dev/build/vite/embedded/mcp), `connectDevtool` client. No docks, no terminals, no command palette — those are hub concerns. |
| `packages/kit` | `@vitejs/devtools-kit` | The hub. `createKitContext` wraps devframe's context with `docks` / `terminals` / `messages` / `commands` host subsystems plus the Vite-augmented context type. `createPluginFromDevframe` bridges a portable devframe app into a `Plugin.devtools.setup` Vite plugin, auto-deriving its iframe dock entry from the definition. |
| `packages/core` | `@vitejs/devtools` | Vite plugin + CLI + standalone/webcomponents client for Vite DevTools itself. Calls kit's `createKitContext`, scans Vite plugins for `.devtools.setup`, and serves the dock UI. |
| `packages/ui` | `@vitejs/devtools-ui` | Shared UI components, composables, and UnoCSS preset (`presetDevToolsUI`). Private, not published. |
| `packages/rolldown` | `@vitejs/devtools-rolldown` | Nuxt UI for Rolldown build data. Hub-mounted via `Plugin.devtools.setup`. Serves at `/__devtools-rolldown/`. |
| `packages/vite` | `@vitejs/devtools-vite` | Nuxt UI for Vite DevTools (WIP). Hub-mounted via `Plugin.devtools.setup`. Serves at `/__devtools-vite/`. |
| `packages/self-inspect` | `@vitejs/devtools-self-inspect` | Meta-introspection — DevTools for the DevTools. Hub-mounted via `Plugin.devtools.setup`. Serves at `/__devtools-self-inspect/`. |
| `packages/webext` | — | Browser extension scaffolding (ancillary). |

Other top-level directories:
- `docs/` — VitePress docs; guides in `docs/guide/`
Expand All @@ -34,15 +43,19 @@ flowchart TD

## Dep Boundary

`packages/devframe` is the lowest-level package in this monorepo and is positioned to be extracted into its own repo. It MUST NOT import from `vite` or any `@vitejs/*` package — not as a `dependencies` entry, not as an inlined dep, not as a source import. `packages/kit` and above build on top of devframe, never the reverse.
`devframe/packages/devframe` is the lowest-level package in this monorepo and is positioned to be extracted into its own repo. It MUST NOT import from `vite`, `@vitejs/*`, or any hub-only concept (docks, terminals, messages, commands) — not as a `dependencies` entry, not as an inlined dep, not as a source import. `packages/kit` and above build on top of devframe; never the reverse. If a feature requires multi-integration awareness, it goes in kit.

`devframe/internal` is a marked-internal subpath that exposes a small set of helpers (`getInternalContext`, `resolveBasePath`) for first-party adapters that need to reach into devframe's private machinery — kit's relocated `DocksHost` uses it for remote-dock token allocation. End users should not import it.

## Architecture

- **Entry**: `createDevToolsContext` (`packages/core/src/node/context.ts`) builds `DevToolsNodeContext` with hosts for RPC, docks, views, terminals. Invokes `plugin.devtools.setup` hooks.
- **Node context**: server-side (cwd, vite config, mode, hosts, auth storage at `node_modules/.vite/devtools/auth.json`).
- **Devframe context** (`devframe/packages/devframe/src/node/context.ts`): `createHostContext` returns a `DevToolsNodeContext` carrying `rpc`, `views` (HTTP file-serving via `hostStatic`), `diagnostics`, `agent`, plus `cwd`/`workspaceRoot`/`mode`/`host`/`createJsonRenderer`. No docks, no terminals.
- **Kit context** (`packages/kit/src/node/context.ts`): `createKitContext` wraps `createHostContext` and attaches the four hub hosts — `docks`, `terminals`, `messages`, `commands`. Optionally surfaces `viteConfig`/`viteServer` when mounted inside Vite DevTools. Wires the `'devframe:docks'` / `'devframe:commands'` shared-state sync.
- **Bridge** (`packages/kit/src/node/create-plugin-from-devframe.ts`): `createPluginFromDevframe(d, opts?)` returns `PluginWithDevTools`; in its `setup`, mounts the SPA via `views.hostStatic`, auto-registers an iframe dock entry from `id`/`name`/`icon`/`basePath`, runs `d.setup(ctx)` for the devframe-level wiring, then runs `opts.setup?.(ctx)` for kit-only extensions.
- **Vite DevTools entry** (`packages/core/src/node/context.ts`): `createDevToolsContext` calls `createKitContext`, registers Vite-specific commands (`vite:open-in-editor`, `vite:open-in-finder`), then scans Vite plugins for `.devtools.setup` hooks (which now receive the kit-augmented context).
- **Client context**: webcomponents/Nuxt UI state (`packages/core/src/client/webcomponents/state/*`) — dock entries, panels, RPC client. Two modes: `embedded` (overlay in host app) and `standalone` (independent page).
- **WS server** (`packages/core/src/node/ws.ts`): RPC via `devframe/rpc/transports/ws-server`. Auth skipped in build mode or when `devtools.clientAuth` is `false`.
- **Nuxt UI plugins** (rolldown, vite, self-inspect): each registers RPC functions and hosts static Nuxt SPA at its own base path.
- **Hub-mounted Nuxt UI plugins** (rolldown, vite, self-inspect): each implements `Plugin.devtools.setup`, receives a `KitNodeContext`, registers RPC functions, hosts a static Nuxt SPA, and registers its dock entry.

## Development

Expand All @@ -69,14 +82,23 @@ pnpm -C docs run docs # docs dev server

### Devframe design principles

These apply to everything inside `packages/devframe` and to how host packages (`kit`, `core`, etc.) layer on top. When in doubt, err on the side of "devframe provides hooks, the app decides UX".
These apply to everything inside `devframe/packages/devframe` and reinforce its positioning as "the container for one devtool integration, portable to multiple viewers". When in doubt, err on the side of "devframe provides primitives, the hub provides UX".

- **Single-integration scope.** Devframe describes one tool. If a feature only makes sense when multiple tools share a UI — docking, a unified command palette, cross-tool toasts, terminal aggregation — it lives in `@vitejs/devtools-kit`, not here.
- **Headless by default.** No default startup banners, no opinionated logging to stdout, no default styling. Provide hooks (`onReady`, `cli.configure`, etc.); let the application print its own branding. Structured diagnostics via `logs-sdk` are fine — ad-hoc `console.log`s baked into adapters are not.
- **File watching is the app's job, not devframe's.** Don't add a generic watcher primitive. Authors wire chokidar / fs.watch / watchman themselves and signal change via `ctx.rpc.sharedState.set(...)` or event-type RPCs. devframe stays out of the filesystem-observation business.
- **Mount path depends on adapter context.** Given `id: 'foo'`, the default mount path is `/__foo/` for *hosted* adapters (`vite`, `kit`, `embedded`) and `/` for *standalone* adapters (`cli`, `spa`, `build`). Authors override via `DevtoolDefinition.basePath`. Don't hardcode `DEVTOOLS_MOUNT_PATH` in adapter code paths that may run standalone.
- **SPAs own their basePath at runtime.** Build SPAs with relative asset paths (`vite.base: './'`); discover the effective base in the browser from the executing script's location / `document.baseURI`. `createBuild` / `createSpa` copy SPA output verbatim — no HTML rewriting, no build-time `--base` injection. The client (`connectDevtool`) resolves `__connection.json` relative to the runtime base automatically.
- **Mount path depends on adapter context.** Given `id: 'foo'`, the default mount path is `/__foo/` for *hosted* adapters (`vite`, `embedded`, kit's `createPluginFromDevframe`) and `/` for *standalone* adapters (`cli`, `spa`, `build`). Authors override via `DevtoolDefinition.basePath`. Don't hardcode `DEVTOOLS_MOUNT_PATH` in adapter code paths that may run standalone.
- **SPAs own their basePath at runtime.** Build SPAs with relative asset paths (`vite.base: './'`); discover the effective base in the browser from the executing script's location / `document.baseURI`. `createBuild` / `createSpa` copy SPA output verbatim — no HTML rewriting, no build-time `--base` injection. The client (`connectDevtool`) resolves `.connection.json` relative to the runtime base automatically.
- **CLI flags compose from both sides.** The `cac` instance backing `createCli` is exposed both to the `DevtoolDefinition` (`cli.configure(cli)`) — for capabilities contributed by the tool itself — and to the `createCli` caller — for flags added at the final assembly stage. Parsed flag values are forwarded to `setup(ctx, { flags })`. Never hardcode domain-specific flags into `createCli`.

### Kit design principles

The kit is the integration hub. When adding to it, the question is "does this help unify multiple devtools?" — not "is this useful in general?".

- **Hub-only features.** `docks`, `terminals`, `messages`, `commands`, the auto-derived dock entry in `createPluginFromDevframe`, the unified user-settings shared state — these only have meaning across integrations and stay kit-side.
- **Devframe definitions stay portable.** `createPluginFromDevframe(devtoolApp, opts?)` is the bridge. The devtool's own `setup(ctx)` should not assume kit context; if it needs hub features, contribute them via `opts.setup` or via a kit-only Vite plugin that augments the same context.
- **Auto-derive what you can, override via options.** `createPluginFromDevframe` synthesizes the iframe dock entry from `id`/`name`/`icon`/`basePath`. Callers customise via `opts.dock` (category, when-clause, custom icon override) or `opts.setup` (terminals, additional dock entries). Don't push these into the portable `DevtoolDefinition`.

## Structured Diagnostics (Error Codes)

All node-side warnings and errors use structured diagnostics via [`logs-sdk`](https://github.com/vercel-labs/logs-sdk). Never use raw `console.warn`, `console.error`, or `throw new Error` with ad-hoc messages in node-side code — always define a coded diagnostic.
Expand All @@ -85,11 +107,13 @@ All node-side warnings and errors use structured diagnostics via [`logs-sdk`](ht

| Prefix | Package(s) | Diagnostics file |
|--------|-----------|-----------------|
| `DF` | `packages/devframe` | `packages/devframe/src/node/diagnostics.ts`, `packages/devframe/src/rpc/diagnostics.ts` |
| `DTK` | `packages/core` (Vite-specific remainder) | `packages/core/src/node/diagnostics.ts` |
| `DF` | `devframe/packages/devframe` | `devframe/packages/devframe/src/node/diagnostics.ts`, `devframe/packages/devframe/src/rpc/diagnostics.ts` |
| `DTK` | `packages/kit` + `packages/core` (shared codespace, hub-side) | `packages/kit/src/node/diagnostics.ts`, `packages/core/src/node/diagnostics.ts` |
| `RDDT` | `packages/rolldown` | `packages/rolldown/src/node/diagnostics.ts` |
| `VDT` | `packages/vite` (reserved) | — |

`DTK` is shared between core and kit because they're sibling layers of the Vite DevTools hub. Coordinate code numbers across both files: kit currently reserves `DTK0050+`; core's existing codes top out below that.

Codes are sequential 4-digit numbers per prefix (e.g. `DTK0033`, `RDDT0003`). Check the existing diagnostics file to find the next available number.

### Adding a new error
Expand Down
2 changes: 1 addition & 1 deletion alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const alias = {
'devframe/types': df('devframe/src/types/index.ts'),
'devframe/node': df('devframe/src/node/index.ts'),
'devframe/constants': df('devframe/src/constants.ts'),
'devframe/internal': df('devframe/src/internal/index.ts'),
'devframe/utils/events': df('devframe/src/utils/events.ts'),
'devframe/utils/human-id': df('devframe/src/utils/human-id.ts'),
'devframe/utils/nanoid': df('devframe/src/utils/nanoid.ts'),
Expand All @@ -27,7 +28,6 @@ export const alias = {
'devframe/adapters/dev': df('devframe/src/adapters/dev.ts'),
'devframe/adapters/build': df('devframe/src/adapters/build.ts'),
'devframe/adapters/vite': df('devframe/src/adapters/vite.ts'),
'devframe/adapters/kit': df('devframe/src/adapters/kit.ts'),
'devframe/adapters/embedded': df('devframe/src/adapters/embedded.ts'),
'devframe/adapters/mcp': df('devframe/src/adapters/mcp.ts'),
'@devframes/nuxt/runtime/plugin.client': df('nuxt/src/runtime/plugin.client.ts'),
Expand Down
4 changes: 0 additions & 4 deletions devframe/docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,8 @@ function guideItems(prefix: string): DefaultTheme.NavItemWithLink[] {
{ text: 'RPC', link: `${prefix}/guide/rpc` },
{ text: 'Shared State', link: `${prefix}/guide/shared-state` },
{ text: 'Streaming', link: `${prefix}/guide/streaming` },
{ text: 'Dock System', link: `${prefix}/guide/dock-system` },
{ text: 'Commands', link: `${prefix}/guide/commands` },
{ text: 'When Clauses', link: `${prefix}/guide/when-clauses` },
{ text: 'Messages & Notifications', link: `${prefix}/guide/messages` },
{ text: 'Structured Diagnostics', link: `${prefix}/guide/diagnostics` },
{ text: 'Terminals', link: `${prefix}/guide/terminals` },
{ text: 'Client', link: `${prefix}/guide/client` },
{ text: 'Standalone CLI', link: `${prefix}/guide/standalone-cli` },
{ text: 'Nuxt Helper', link: `${prefix}/guide/nuxt` },
Expand Down
21 changes: 0 additions & 21 deletions devframe/docs/errors/DF0001.md

This file was deleted.

21 changes: 0 additions & 21 deletions devframe/docs/errors/DF0002.md

This file was deleted.

21 changes: 0 additions & 21 deletions devframe/docs/errors/DF0003.md

This file was deleted.

21 changes: 0 additions & 21 deletions devframe/docs/errors/DF0004.md

This file was deleted.

21 changes: 0 additions & 21 deletions devframe/docs/errors/DF0005.md

This file was deleted.

21 changes: 0 additions & 21 deletions devframe/docs/errors/DF0009.md

This file was deleted.

21 changes: 0 additions & 21 deletions devframe/docs/errors/DF0010.md

This file was deleted.

Loading
Loading