Skip to content

Commit 00fda68

Browse files
feat: add tool-type scaffold generator and registration records
1 parent f294fb9 commit 00fda68

7 files changed

Lines changed: 1245 additions & 20 deletions

File tree

CONTRIBUTING.md

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,15 +143,29 @@ See [`docs/architecture.md`](docs/architecture.md) for data flow, export state m
143143

144144
## Adding a new tool type
145145

146-
Claude Code assistant `tool_use` blocks carry a `name` string (e.g. `"Read"`, `"Bash"`). The browser coordinates that name across four sites; drift is caught by `tests/test_tool_dispatch_sync.py`.
147-
148-
1. **`utils/tool_dispatch.py`** — add the name to `_FILE_ACTIVITY_HANDLERS` (`None` if no file/bash/web side effects); `KNOWN_TOOL_TYPES` is derived from its keys. If the tool has a distinct `toolUseResult` JSON shape, add a `ToolResultDispatchEntry` to `_TOOL_RESULT_DISPATCH` (set `priority` when overlapping another predicate — see module docstring and `tests/test_tool_dispatch_ordering.py`).
149-
2. **`models/tool_results.py`** — add the name to `ToolNameLiteral` and, when the tool has a distinct result payload, add the TypedDict, type guard (`is_*_tool_result`), and union member on `ToolResultUnion`.
150-
3. **`utils/md_exporter.py`** — add an `elif name == "…"` branch in `_render_tool_use` (sync test parses these branches).
151-
4. **`static/js/render/registry.js`** — add a `TOOL_USE_RENDERERS` entry (and a `tool_use/*.js` renderer module).
152-
5. Regenerate **`static/tool_types.json`**: `python scripts/gen_tool_types_manifest.py`
153-
6. **Optional result UI** — if the backend emits a new `result_type`, add `TOOL_RESULT_RENDERERS` and a `tool_result/*.js` module.
154-
7. Run `pytest tests/test_tool_dispatch_sync.py -v` — failure names the site missing the new type.
146+
Claude Code assistant `tool_use` blocks carry a `name` string (e.g. `"Read"`, `"Bash"`). The browser coordinates that name across Python dispatch, Markdown export, and the SPA registry; drift is caught by `tests/test_tool_dispatch_sync.py`.
147+
148+
### Before (manual checklist — 7 coordinated sites)
149+
150+
1. `utils/tool_dispatch.py``_FILE_ACTIVITY_HANDLERS` + `_TOOL_RESULT_DISPATCH`
151+
2. `models/tool_results.py``ToolNameLiteral`, TypedDict, `is_*` guard, union member
152+
3. `utils/md_exporter.py``_render_tool_use` / `_render_tool_result` branches
153+
4. `static/js/render/registry.js` — imports + `TOOL_USE_RENDERERS` + `TOOL_RESULT_RENDERERS`
154+
5. `static/js/render/tool_use/*.js` — renderer module
155+
6. `static/js/render/tool_result/*.js` — result renderer module (when applicable)
156+
7. `static/tool_types.json` — regenerate via `scripts/gen_tool_types_manifest.py`
157+
158+
### After (registration record + generator)
159+
160+
1. **One edit:** create or update a JSON record under `tool_types/` (see `tool_types/README.md`).
161+
2. **One command:** `python scripts/scaffold_tool_type.py --record tool_types/<name>.json`
162+
Or from scratch: `python scripts/scaffold_tool_type.py --name my_tool`
163+
3. **Complete stubs:** field mapping in the dispatch builder, render HTML in JS modules, overlap fixtures when `priority > 0`.
164+
4. **Verify:** `pytest tests/test_tool_dispatch_sync.py tests/test_tool_dispatch_ordering.py tests/test_tool_dispatch_adversarial.py -q`
165+
166+
The generator emits coordinated stubs across all seven sites and writes the registration record. Hand-editing drops from seven sites to the record plus finishing TODO stubs (typically 2–3 files).
167+
168+
Dry-run preview: `python scripts/scaffold_tool_type.py --name example_tool --dry-run`
155169

156170
## Getting help
157171

docs/architecture.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,10 @@ In `utils/tool_dispatch.py`, tool results are classified through `_parse_tool_re
7575

7676
When adding a new tool renderer:
7777

78-
1. Add a `ToolResultDispatchEntry` to `_TOOL_RESULT_DISPATCH` in `utils/tool_dispatch.py`. Set `priority` higher than any overlapping predicate it must beat without relying on registration order, and add a row to `ORDERING_INVARIANTS` in `tests/test_tool_dispatch_ordering.py` when overlaps exist. Documented exception: `plan` (priority 1) over `file_write` (0). Broad predicates like `task_message` use registration order instead of elevated priority.
79-
2. Add or extend a JSONL fixture under `tests/fixtures/` (especially for overlaps with existing predicates).
80-
3. Run `pytest tests/test_tool_dispatch_ordering.py tests/test_tool_dispatch_adversarial.py tests/test_jsonl_parser.py tests/test_real_session_fixtures.py -v`.
78+
1. **Preferred:** add a registration record under `tool_types/` and run `python scripts/scaffold_tool_type.py --record tool_types/<name>.json`. The generator emits `_TOOL_RESULT_DISPATCH` entries, TypedDict/guards, Markdown branches, JS module stubs, `registry.js` map entries, a parser fixture, and regenerates `static/tool_types.json`. See `CONTRIBUTING.md` § "Adding a new tool type" for the before/after workflow.
79+
2. **Manual (legacy):** add a `ToolResultDispatchEntry` to `_TOOL_RESULT_DISPATCH` in `utils/tool_dispatch.py`. Set `priority` higher than any overlapping predicate it must beat without relying on registration order, and add a row to `ORDERING_INVARIANTS` in `tests/test_tool_dispatch_ordering.py` when overlaps exist. Documented exception: `plan` (priority 1) over `file_write` (0). Broad predicates like `task_message` use registration order instead of elevated priority.
80+
3. Add or extend a JSONL fixture under `tests/fixtures/` (especially for overlaps with existing predicates).
81+
4. Run `pytest tests/test_tool_dispatch_ordering.py tests/test_tool_dispatch_adversarial.py tests/test_jsonl_parser.py tests/test_real_session_fixtures.py tests/test_scaffold_tool_type.py -v`.
8182

8283
## Export state machine
8384

0 commit comments

Comments
 (0)