feat(plugin): add lifecycle hooks API (onStartup, onBeforeExecute, onAfterExecute)#376
Merged
feat(plugin): add lifecycle hooks API (onStartup, onBeforeExecute, onAfterExecute)#376
Conversation
…AfterExecute) Introduce a hooks system that allows plugins to tap into opencli's execution lifecycle without modifying core code. New files: - src/hooks.ts: hook registration, emission, and globalThis singleton - src/hooks.test.ts: 10 unit tests covering registration, ordering, error isolation, async support, and globalThis sharing Modified files: - src/execution.ts: emit onBeforeExecute/onAfterExecute around command execution - src/main.ts: emit onStartup after discoverPlugins() - src/registry-api.ts: export hooks API for plugin consumption Example plugin: https://github.com/ByteYue/opencli-plugin-audit-log
The isCliModule() check only matched files containing 'cli(' calls,
silently skipping hook-only files like audit-hooks.ts that register
onBeforeExecute/onAfterExecute without any cli() command registration.
Renamed CLI_MODULE_PATTERN → PLUGIN_MODULE_PATTERN and extended the
regex to also match onStartup(, onBeforeExecute(, onAfterExecute(.
cf2ce99 to
2bb3fc6
Compare
jackwener
approved these changes
Mar 24, 2026
Owner
jackwener
left a comment
There was a problem hiding this comment.
结论:方向对,而且我已经在当前分支补了两个关键行为修正;现在这版可以合。
深度 review 结论:
- ✅
hooks.ts这层抽象本身是成立的:global singleton、ordered execution、error isolation、plugin-facing API 都合理 - ✅
discovery.ts补 hook-only plugin file 的加载条件也是必要修复,否则只注册 hooks 的插件会被静默跳过 - P1(已修):原实现里
onAfterExecute只会在命令成功时触发;命令失败时,plugin 无法做 audit / metrics / error reporting - P1(已修):
onStartup原来放在 completion fast-path 之前,意味着 shell completion 这种高频路径也会触发 plugin startup side effects,不合适
我直接推到分支上的 follow-up:
2bb3fc6—fix(plugin): tighten lifecycle hook semantics
具体改动:
executeCommand()现在在失败路径也会触发onAfterExecute,并在HookContext里带上error/finishedAtonStartup移到--get-completionsfast-path 之后,避免 completion 时误触发 plugin startup hooks- 补了回归测试,覆盖 failed execution 也会触发
onAfterExecute
我认为这也是更好的方案:
- 保持现有三种 hook API 不变,不额外引入
onError之类新 surface area - 但把 after-hook 的语义收紧成“命令结束后都会触发”,这样插件作者更容易依赖
本地验证已过:
npx vitest run src/hooks.test.ts src/engine.test.tsnpm run typechecknpm testnpm run build
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add a lifecycle hooks system that allows plugins to tap into opencli's execution lifecycle without modifying core code. This is a key step toward evolving the plugin system from "command extensions" to a full "plugin ecosystem platform."
Changes
New files
src/hooks.ts— Core hooks module withglobalThis.__opencli_hooks__singleton (consistent with registry pattern), 3 hook registration functions, and safeemitHook()that catches individual handler errorssrc/hooks.test.ts— 10 unit tests covering registration, ordered execution, async support, error isolation, and globalThis singleton sharingModified files
src/execution.ts— EmitonBeforeExecute/onAfterExecutearound command execution inexecuteCommand(). Refactored to captureresultfrom both browser and non-browser paths.src/main.ts— EmitonStartupafterdiscoverPlugins(), before CLI execution beginssrc/registry-api.ts— Export hooks API (onStartup,onBeforeExecute,onAfterExecute) via@jackwener/opencli/registryHook Types
onStartuponBeforeExecuteonAfterExecuteDesign Decisions
__opencli_registry__— guarantees plugin hooks work across npm link / symlink boundariesExample Plugins
4 example plugins demonstrate the hooks API in action:
~/.opencli/audit.jsonlonBeforeExecute+onAfterExecuteonAfterExecuteonAfterExecuteonAfterExecuteInstall any of them to try:
Test Results