-
Notifications
You must be signed in to change notification settings - Fork 4
feat: plugin command execution hooks #207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
7 files reviewed, 1 comment
| // Emit event if plugin created a message | ||
| const last = await Session.messages({ sessionID: input.sessionID, limit: 1 }) | ||
| if (last.length > 0) { | ||
| Bus.publish(Command.Event.Executed, { | ||
| name: command.name, | ||
| sessionID: input.sessionID, | ||
| arguments: input.arguments, | ||
| messageID: last[0].info.id, | ||
| }) | ||
| return last[0] | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: event emission may incorrectly trigger if session already has messages
the logic Session.messages({ sessionID, limit: 1 }) returns the most recent message in the session, but doesn't verify it was created by this plugin command execution. if the session already had messages before this command ran, the check last.length > 0 will be true even if the plugin didn't create any new message.
to fix, capture the message count or last message ID before execution, then compare after:
// Before execution
const messagesBefore = await Session.messages({ sessionID: input.sessionID, limit: 1 })
const lastMessageIDBefore = messagesBefore[0]?.info.id
// After execution
const messagesAfter = await Session.messages({ sessionID: input.sessionID, limit: 1 })
if (messagesAfter.length > 0 && messagesAfter[0].info.id !== lastMessageIDBefore) {
// New message was created
Bus.publish(Command.Event.Executed, { ... })
}Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/opencode/src/session/prompt.ts
Line: 1337:1347
Comment:
**logic:** event emission may incorrectly trigger if session already has messages
the logic `Session.messages({ sessionID, limit: 1 })` returns the most recent message in the session, but doesn't verify it was created by this plugin command execution. if the session already had messages before this command ran, the check `last.length > 0` will be true even if the plugin didn't create any new message.
to fix, capture the message count or last message ID before execution, then compare after:
```typescript
// Before execution
const messagesBefore = await Session.messages({ sessionID: input.sessionID, limit: 1 })
const lastMessageIDBefore = messagesBefore[0]?.info.id
// After execution
const messagesAfter = await Session.messages({ sessionID: input.sessionID, limit: 1 })
if (messagesAfter.length > 0 && messagesAfter[0].info.id !== lastMessageIDBefore) {
// New message was created
Bus.publish(Command.Event.Executed, { ... })
}
```
How can I resolve this? If you propose a fix, please make it concise.
Summary
Plugin Command Example
Here's a complete example plugin that registers custom slash commands:
Features
descriptionaliases/hi→/hello)sessionOnlytrue, command requires an existing sessionexecute{ sessionID, arguments, client }Test Coverage
/hiresolves tohelloviaCommand.get/hello worldcalls the pluginexecutehandler with argumentsSession.Event.ErrorsessionOnly: truereject when session doesn't existGreptile Summary
This PR implements server-side plugin slash command execution without requiring client changes. Plugin commands are loaded into the command registry with support for aliases and sessionOnly enforcement.
Key changes:
plugin.commandhook type to enable custom slash commands via pluginsCommand.get()to resolve command aliases server-sideSessionPrompt.command()with error handling viaSession.Event.ErrorIssues found:
Command.Event.Executedwhen the plugin didn't create a message (see inline comment)Confidence Score: 4/5
packages/opencode/src/session/prompt.ts- the event emission logic needs to be fixed before mergeImportant Files Changed
plugin.commandhook type definition to enable custom slash commandsclient()export, type guard for plugin functions, and excludedplugin.commandfrom trigger mechanismSequence Diagram
sequenceDiagram participant Client as Client (TUI/Web) participant API as session.command API participant Command as Command.get() participant Plugin as Plugin.list() participant Hook as plugin.command.execute() participant Session as Session participant Bus as Event Bus Client->>API: /hello world API->>Command: Command.get("hello") Command->>Plugin: Load plugin commands Plugin-->>Command: Plugin commands + aliases Command-->>API: Resolve alias → canonical name alt sessionOnly command API->>Session: Session.get(sessionID) alt session not found Session-->>API: Error API->>Bus: Publish Session.Event.Error API-->>Client: Throw error end end alt plugin command API->>Plugin: Plugin.list() Plugin-->>API: Plugin hooks API->>Hook: execute({ sessionID, arguments, client }) alt command succeeds Hook->>Session: Create message (optional) Hook-->>API: Success API->>Session: Session.messages(limit: 1) Session-->>API: Last message alt message was created API->>Bus: Publish Command.Event.Executed end API-->>Client: Return message else command fails Hook-->>API: Throw error API->>Bus: Publish Session.Event.Error API-->>Client: Throw error end else template command API->>Session: Execute template command Session-->>API: Result API-->>Client: Return result end