Skip to content

feat(cli): show BitFun's own Hooks under /hooks - #1814

Merged
bobleer merged 1 commit into
GCWing:mainfrom
bobleer:feat/cli-native-hooks-command
Jul 28, 2026
Merged

feat(cli): show BitFun's own Hooks under /hooks#1814
bobleer merged 1 commit into
GCWing:mainfrom
bobleer:feat/cli-native-hooks-command

Conversation

@bobleer

@bobleer bobleer commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

CLI /hooks now reports BitFun's own agent lifecycle hooks. The read-only catalog of hooks configured for other AI applications moves to /hooks_external (alias /hooks-external), with its behavior unchanged.

Fixes #

Type and Areas

Type: Feature (plus a UX fix for a command-name collision)

Areas: CLI/TUI, Rust core (bitfun-core::native_hooks), docs

Motivation / Impact

/hooks was introduced by the static external Hook catalog (#b770f03a), before BitFun had native hooks. Since #1785 added Codex-compatible hooks that actually execute, /hooks named the one thing it did not show:

  • /hooks listed hooks belonging to Claude Code / Codex / OpenCode, which BitFun never runs.
  • BitFun's own hooks — the ones that do run — had no CLI surface at all. Checking them meant opening hooks.json by hand and raising the backend log level.
  • For users arriving from Claude Code, whose /hooks manages their own hooks, the meaning was inverted.

After this change each view has its own command and each help text points at the other, so neither can be mistaken for the other.

/hooks output:

Hooks (BitFun)
Commands BitFun runs at agent lifecycle events. Nothing was executed to build this view.

Hooks: enabled (app.hooks.enabled)
Project hooks: disabled (app.hooks.project_hooks_enabled)

Configuration:
  user [loaded; present]: ~/.config/bitfun/config/hooks.json
  project [not loaded; present]: /ws/.bitfun/config/hooks.json

1 matcher group, 1 handler:

PreToolUse
  matcher: Bash [user; 1 handler]
    - jq -r '.tool_input.command' >> ~/log [timeout 600s]

Configuration issues:
  ! Hook event 'PreTool' is not a supported event name: …

Edit hooks.json to change this. Hooks configured for other AI applications: /hooks_external. Help: /help hooks, /hooks -h, or /hooks --help

Breaking for anyone typing /hooks expecting the external catalog; they need /hooks_external. Both help texts and the design docs were updated accordingly.

Reviewer Notes

  • bitfun-core::native_hooks::overview is a new read-only snapshot API: both gates, every candidate configuration file with whether it exists and whether a dispatch would load it, matcher groups per event with handlers and effective timeouts, and parse issues. It re-reads the files rather than consulting the dispatch cache, so it always reflects what is on disk. Nothing in this path executes a handler.
  • The design goal was that the view never reads as "nothing is configured" when something is configured but inert: a gated-off project file is still listed, a disabled master switch is stated outright, and skipped oversized files, unknown event names, and never-matching matchers surface as issues.
  • One subtlety worth a look: a matcher whose pattern fails to compile parses into AgentHookMatcher::Pattern with no compiled regex, not Invalid. Reporting only the latter would miss the common case, so both report as invalid — they never match either way.
  • ActionHandler::Hooks splits into NativeHooks / ExternalHooks. Both stay outside the Shared TUI preview slice, matching how the other management surfaces are gated today. Happy to let /hooks into that slice if you'd prefer — it is a local read-only report — but I kept the existing posture.
  • /hooks-external is registered alongside /hooks_external because the repo's existing multiword command is kebab-case (/reload-skills); the underscore form is the primary name.

Verification

cargo test -p bitfun-cli --bin bitfun          # 360 passed
cargo test -p bitfun-core --lib native_hooks   # 13 passed
cargo clippy -p bitfun-cli --bins --all-targets   # no new warnings in touched files
cargo clippy -p bitfun-core --lib                 # no new warnings in touched files
cargo fmt --check -p bitfun-cli -p bitfun-core    # touched files clean

New tests: 3 in native_hooks_tests.rs covering layer gating, issue reporting, and the oversized-file case; 4 in the CLI covering rendering, the empty/disabled states, the never-matching matcher, and the command registration for both aliases.

Not verified: the rendered view was checked through its unit tests, not by driving a live TUI session interactively.

Checklist

  • This PR is focused and does not include secrets, temporary prompts, generated scratch files, or unrelated artifacts.
  • Relevant verification is recorded above, or skipped checks are explained.
  • User-facing strings, docs, and locales are updated where applicable.

AI-assisted (Claude Code). Testing level: fully tested at the unit level, not exercised interactively in a live TUI.

The CLI `/hooks` command showed the read-only catalog of Hooks configured
for other AI applications, which predates BitFun having native hooks. Once
BitFun could run a user's own commands at lifecycle events, `/hooks` named
the one thing it did not show, and BitFun's own hooks had no CLI surface at
all — the only way to check them was reading hooks.json and the backend log.

Give the two views their own commands. `/hooks` now reports BitFun's native
hooks; the external catalog moves to `/hooks_external` (alias
`/hooks-external`), unchanged in behavior. Both help texts point at the
other, so neither view can be mistaken for the other.

- `bitfun-core::native_hooks::overview` returns a read-only snapshot: both
  gates, every candidate configuration file with whether it exists and
  whether a dispatch would load it, the matcher groups per event with their
  handlers and effective timeouts, and any parse issues. It re-reads the
  files instead of consulting the dispatch cache, so it always reflects
  what is on disk.
- The view never reads as "nothing is configured" when something is
  configured but inert: a gated-off project file is still listed, a
  disabled master switch is stated, and skipped oversized files, unknown
  event names, and never-matching matchers are reported as issues.
- A matcher whose pattern fails to compile parses into `Pattern` with no
  regex rather than `Invalid`; both report as invalid, since both never
  match.
- Native hooks stay outside the Shared TUI preview slice, matching the
  other management surfaces.

Documented in docs/features/agent-hooks.md and its Chinese translation.

AI-assisted; fully tested at the unit level (`cargo test -p bitfun-cli --bin
bitfun`, `cargo test -p bitfun-core --lib native_hooks`), not exercised
interactively in a live TUI session.
@bobleer
bobleer merged commit 7523266 into GCWing:main Jul 28, 2026
7 checks passed
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.

1 participant