Skip to content

[refactor] : clean code for api.lua with handlers#337

Draft
jensenojs wants to merge 2 commits intosudo-tee:mainfrom
jensenojs:refactor/command-boundary-a-clean
Draft

[refactor] : clean code for api.lua with handlers#337
jensenojs wants to merge 2 commits intosudo-tee:mainfrom
jensenojs:refactor/command-boundary-a-clean

Conversation

@jensenojs
Copy link
Contributor

@jensenojs jensenojs commented Mar 20, 2026

Summary

for #322

This PR makes the command path explicit and predictable.

entry (command/slash/keymap) -> router -> parse -> dispatch -> handlers
So instead of spreading command behavior across API shortcuts and implicit fallbacks, we now have one clear path to read, debug, and review.

  • router only decides where to go
  • parse only validates intent
  • dispatch owns execution lifecycle + normalized errors
  • handlers own command semantics
  • api.lua is just a thin mapping layer

Intentional behavior tightening

This is not a pure no-op move. A few behaviors are intentionally stricter now:

  • keymap no longer does arbitrary dynamic api[func] dispatch
  • unsupported legacy-style API methods now fail with deterministic invalid_command
  • legacy usecases -> handlers compatibility is kept at loader edge, not inside registry core flow

oujinsai added 2 commits March 20, 2026 23:10
Move command execution into a single handlers layer and keep api.lua as a thin action map. This removes the split handlers/usecases execution path so command parsing, dispatch, and execution boundaries are explicit and reviewable.
Codify command-layer invariants so the refactor is reviewable and regression-safe. The tests lock parser/dispatcher behavior, handler boundaries, keymap routing, and registry contracts while tightening shared types used by those checks.
@jensenojs
Copy link
Contributor Author

What comes next

This refactor is intended as groundwork for a longer direction: moving feature work toward recipe-style extension, instead of repeatedly editing core files.

  • PR-B focuses on clarifying the registry contract (what an extension can declare, and how conflicts are handled), so feature additions rely less on core-file edits.
  • PR-C focuses on wiring runtime consumers (completion, formatter lookup, context injection, events, hooks) to that contract, so recipe-style capabilities can run on a more stable interface.

@jensenojs
Copy link
Contributor Author

My modifications here were a bit rushed, but I need to get some sleep (and unfortunately, I had to submit another large PR...). @sudo-tee

@sudo-tee
Copy link
Owner

Thanks for the PR.

I will have a look

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors Opencode’s command execution into an explicit pipeline (entry -> router -> parse -> dispatch -> handlers) and introduces an extension-backed registry so command/slash definitions and handlers are centrally defined and validated.

Changes:

  • Added a registry + loader layer for builtin and user extensions (commands, slash commands, handlers, hooks, etc.) with conflict policies.
  • Implemented the new command stack (router/parse/dispatch/handlers) and updated entry points (Neovim :Opencode, slash commands, keymaps) to route through it.
  • Slimmed api.lua into a public “actions surface” that composes handler action modules, and added extensive unit coverage/guards for dependency boundaries and error normalization.

Reviewed changes

Copilot reviewed 32 out of 32 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/unit/registry_loader_spec.lua Validates extension loading, isolation, conflict policy, and legacy usecases adaptation.
tests/unit/keymap_spec.lua Updates keymap tests for router-based dispatch and legacy alias behavior.
tests/unit/entry_dependency_guard_spec.lua Adds dependency boundary guards (router boundary, no dynamic api[...] access).
tests/unit/commands_router_spec.lua Covers API-method-to-command argv routing and invalid_command behavior.
tests/unit/commands_parse_spec.lua Covers parse results and stable error shapes (unknown/missing/invalid subcommand).
tests/unit/commands_handlers_spec.lua Validates handler isolation, duplicate detection, and error normalization via executor.
tests/unit/commands_dispatch_spec.lua Covers lifecycle ordering, hook isolation, registry hook pipeline, and error normalization.
tests/unit/api_spec.lua Updates expectations for registry-backed command defs and public API boundary.
lua/opencode/types.lua Adds/updates types for command registry, lifecycle hooks, dispatch results, and extension config.
lua/opencode/state/init.lua Updates state type annotations to include common top-level fields.
lua/opencode/registry/loader.lua Adds extension loader with builtin/user extensions and legacy usecases warning/adaptation.
lua/opencode/registry/init.lua Implements registry capabilities, conflict policy handling, and lazy setup.
lua/opencode/registry/extensions/slash.lua Provides builtin slash-command specs mapped to API methods.
lua/opencode/registry/extensions/commands.lua Exposes builtin command definitions via builtin_commands.get().
lua/opencode/registry/builtin_commands.lua Defines builtin command schema (handler_id, completions, nested validation, etc.).
lua/opencode/keymap.lua Refactors keymap binding to router + explicit non-command whitelist callbacks and legacy aliases.
lua/opencode/init.lua Initializes registry and registers :Opencode via opencode.commands.
lua/opencode/config.lua Adds extensions configuration defaults (builtin toggles + conflict policy).
lua/opencode/commands/slash.lua Builds runtime slash commands from registry + user commands, routes via router.
lua/opencode/commands/router.lua Routes entry invocations to parse+dispatch, provides API-method-to-command argv mapping.
lua/opencode/commands/parse.lua Parses argv/range into intent and returns stable parse errors.
lua/opencode/commands/init.lua Registers :Opencode and provides completion via registry + completion providers.
lua/opencode/commands/handlers/*.lua Implements command semantics in handler modules (window/session/diff/workflow/agent/permission).
lua/opencode/commands/handlers.lua Central handler executor, duplicate detection, and extension handler integration.
lua/opencode/commands/dispatch.lua Dispatch lifecycle (before/after/error/finally), hook pipeline, normalized errors.
lua/opencode/commands/completion_providers.lua Adds completion provider registry (e.g., user_commands).
lua/opencode/api.lua Replaces monolithic API with composed action tables from handler modules.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jensenojs jensenojs marked this pull request as draft March 21, 2026 05:09
@jensenojs
Copy link
Contributor Author

registry part should remove in this pr

@sudo-tee
Copy link
Owner

sudo-tee commented Mar 21, 2026

I was wondering. If the plan is to move to a registry. The core should also use it all the way. I agree that this could be a seperate PR

@jensenojs
Copy link
Contributor Author

That was my local exploratory code, and I still believe some kind of registration mechanism is necessary. It would unify built-in features with those added in the future via recipes or plugins, but it isn't a problem with an immediately obvious, elegant design. I initially wanted to find the answer to this before starting changes on api.lua, but I failed. The time might not be right to solve this problem yet (or perhaps my expectations for it were too high).

@sudo-tee
Copy link
Owner

No worries, take your time, there is no rush

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants