Problem
Adding a tool requires modifying `tool-registry.ts` in two places:
- Runtime: add entry to `TOOLKIT_REGISTRY` array
- Compile-time: extend the `RegisteredToolkit` intersection type
The intersection type is load-bearing for downstream type-checking. This makes the registry a mandatory touch-point for every new toolkit — a structural seam, not an extension point. There is currently only one adapter (in-process toolkits), so the seam provides no real extensibility.
Proposed solution
Make toolkits self-registering at composition time. Each toolkit module exports a descriptor:
```ts
export const descriptor = { id: "file", createToolkit };
```
The registry is assembled at the composition root (`server-chat-runtime.ts`, `cli-chat.ts`) by combining descriptors. The `RegisteredToolkit` type becomes an explicit contract defined by the registry rather than an inferred intersection. New toolkits are added at the composition root, not inside `tool-registry.ts`.
Benefits
- Locality — `tool-registry.ts` becomes stable; composition roots absorb new toolkits
- Leverage — daemon and CLI composition roots can carry different tool sets without forking the registry
- Eliminates the dual-edit requirement (runtime array + compile-time type)
Files involved
- `tool-registry.ts` (184 LOC, 22 imports)
- All 13 `*-toolkit.ts` files
- `lifecycle-generate.ts`, `agent-instructions.ts` (downstream consumers)
Problem
Adding a tool requires modifying `tool-registry.ts` in two places:
The intersection type is load-bearing for downstream type-checking. This makes the registry a mandatory touch-point for every new toolkit — a structural seam, not an extension point. There is currently only one adapter (in-process toolkits), so the seam provides no real extensibility.
Proposed solution
Make toolkits self-registering at composition time. Each toolkit module exports a descriptor:
```ts
export const descriptor = { id: "file", createToolkit };
```
The registry is assembled at the composition root (`server-chat-runtime.ts`, `cli-chat.ts`) by combining descriptors. The `RegisteredToolkit` type becomes an explicit contract defined by the registry rather than an inferred intersection. New toolkits are added at the composition root, not inside `tool-registry.ts`.
Benefits
Files involved