refactor(ai): single-source tool registration via register_tool() (#56) #68

Merged
claude-code merged 1 commit from refactor/issue-56-tool-registry into main 2026-04-11 10:19:07 -06:00
Collaborator

Closes #56.

Adding a tool used to require updating two parallel structures in ai.py: a name→handler entry in _TOOL_DISPATCH and a schema dict in _DIR_TOOLS (or _SYNTHESIS_TOOLS or _SURVEY_TOOLS). Forgetting one half was silent. Internals.md §9.1 documented this as a 5-step process.

The new shape

A single register_tool() call per (tool, scope):

register_tool(
    name="read_file",
    description="...",
    schema={...},
    scopes=["dir"],
    handler=_tool_read_file,
)

The function appends the schema to one or more scope lists (_DIR_TOOLS / _SYNTHESIS_TOOLS / _SURVEY_TOOLS) and lands the handler in _TOOL_DISPATCH. Tools intercepted by the loop body (submit_report, submit_survey) register schema only with handler=None.

Tools whose schema differs by scope (submit_report has different shapes in dir vs synthesis loops) get one register_tool() call per scope. flag is also registered twice because it appears in dir + synthesis at different positions in each list — the order is preserved with two calls rather than reordered.

Verification

  • _DIR_TOOLS, _SYNTHESIS_TOOLS, _SURVEY_TOOLS contain the same names in the same order as before. Verified via runtime introspection:
    • dir: read_file, list_directory, run_command, parse_structure, write_cache, think, checkpoint, flag, submit_report
    • synthesis: read_cache, list_cache, flag, submit_report
    • survey: submit_survey
  • _TOOL_DISPATCH contains the same 10 handlers as before.
  • 164/164 tests pass.

Internals.md updates

  • §4.2 (Tool dispatch) — describes the new single-registration model.
  • §9.1 (Add a new tool) — collapses from a 5-step process with a "don't forget the second half" warning to a 4-step process with one obvious place to look. (Wiki repo, separate commit and push.)

Out of scope

  • Phase 3.5 (#39) MCP backend will eventually replace this with dynamic discovery from the connected MCP server, at which point register_tool() collapses to a one-line forward. The structure introduced here is naturally MCP-shaped: a single registry of (name, schema, scopes, handler) is exactly what an MCP tool list looks like.
Closes #56. Adding a tool used to require updating two parallel structures in `ai.py`: a name→handler entry in `_TOOL_DISPATCH` and a schema dict in `_DIR_TOOLS` (or `_SYNTHESIS_TOOLS` or `_SURVEY_TOOLS`). Forgetting one half was silent. Internals.md §9.1 documented this as a 5-step process. ## The new shape A single `register_tool()` call per (tool, scope): ```python register_tool( name="read_file", description="...", schema={...}, scopes=["dir"], handler=_tool_read_file, ) ``` The function appends the schema to one or more scope lists (`_DIR_TOOLS` / `_SYNTHESIS_TOOLS` / `_SURVEY_TOOLS`) and lands the handler in `_TOOL_DISPATCH`. Tools intercepted by the loop body (`submit_report`, `submit_survey`) register schema only with `handler=None`. Tools whose schema differs by scope (`submit_report` has different shapes in dir vs synthesis loops) get one `register_tool()` call per scope. `flag` is also registered twice because it appears in dir + synthesis at different positions in each list — the order is preserved with two calls rather than reordered. ## Verification - `_DIR_TOOLS`, `_SYNTHESIS_TOOLS`, `_SURVEY_TOOLS` contain the same names in the same order as before. Verified via runtime introspection: - dir: `read_file, list_directory, run_command, parse_structure, write_cache, think, checkpoint, flag, submit_report` - synthesis: `read_cache, list_cache, flag, submit_report` - survey: `submit_survey` - `_TOOL_DISPATCH` contains the same 10 handlers as before. - 164/164 tests pass. ## Internals.md updates - §4.2 (Tool dispatch) — describes the new single-registration model. - §9.1 (Add a new tool) — collapses from a 5-step process with a "don't forget the second half" warning to a 4-step process with one obvious place to look. (Wiki repo, separate commit and push.) ## Out of scope - Phase 3.5 (#39) MCP backend will eventually replace this with dynamic discovery from the connected MCP server, at which point `register_tool()` collapses to a one-line forward. The structure introduced here is naturally MCP-shaped: a single registry of (name, schema, scopes, handler) is exactly what an MCP tool list looks like.
claude-code added 1 commit 2026-04-11 10:19:01 -06:00
Adding a tool used to require updating two parallel structures in
ai.py: a name->handler entry in _TOOL_DISPATCH and a schema dict in
_DIR_TOOLS (or _SYNTHESIS_TOOLS or _SURVEY_TOOLS). Forgetting one half
was silent. Internals.md §9.1 documented this as a 5-step process.

Replaced both with a single register_tool() call per (tool, scope):

    register_tool(
        name="read_file",
        description="...",
        schema={...},
        scopes=["dir"],
        handler=_tool_read_file,
    )

The function appends the schema to one or more scope lists
(_DIR_TOOLS / _SYNTHESIS_TOOLS / _SURVEY_TOOLS) and lands the handler
in _TOOL_DISPATCH. Tools intercepted by the loop body (submit_report,
submit_survey) register schema only with handler=None.

Tools whose schema differs by scope (submit_report has different shapes
in dir vs synthesis loops) get one register_tool() call per scope.
flag is also registered twice because it appears in dir + synthesis at
different positions in each list — the order is preserved with two
calls rather than reordered for fewer calls.

Verification:
- _DIR_TOOLS, _SYNTHESIS_TOOLS, _SURVEY_TOOLS contain the same names
  in the same order as before.
- _TOOL_DISPATCH contains the same 10 handlers as before.
- 164 tests pass.

No behavior change. Phase 3.5 (#39) MCP backend will eventually replace
this with dynamic discovery from the connected MCP server, at which
point register_tool() collapses to a one-line forward.
claude-code merged commit f72dc7a0fd into main 2026-04-11 10:19:07 -06:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: archeious/luminos#68
No description provided.