Skip to content

feat: Integrate plugin commands from upstream PR #4411 #132

@shuv1337

Description

@shuv1337

Summary

Integrate the plugin commands feature from upstream PR sst/opencode#4411 which allows plugins to register custom slash commands that execute code.

Context

The upstream PR adds a powerful new plugin.command hook that enables plugins to register their own /commands in the TUI. This unlocks use cases like:

  • /prune - prune tool outputs from session
  • Toggling plugin features on/off
  • Creating composable workflows
  • Custom session manipulation commands

Acceptance Criteria

  • Plugin commands appear in autocomplete when typing /
  • Plugin commands with sessionOnly: true only show when a session exists
  • Command aliases work (e.g., /hi resolves to /hello)
  • Plugin command execution works with error handling
  • SDK types are updated with sessionOnly and aliases fields
  • SDK rebuild works (cd packages/sdk/js && bun run script/build.ts)

Implementation Details

Files to Update

File Changes
packages/plugin/src/index.ts:145-192 Add plugin.command hook interface to Hooks
packages/opencode/src/plugin/index.ts:53-76 Exclude plugin.command from trigger, add client() export
packages/opencode/src/command/index.ts:23-79 Add sessionOnly/aliases to schema, load plugin commands, support alias lookup
packages/opencode/src/session/prompt.ts:1413-1498 Execute plugin commands with error handling
packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx:207-222 Filter sessionOnly commands, add aliases to options
packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx:506-518 Resolve command aliases
packages/sdk/js/src/gen/types.gen.ts Add sessionOnly to Command type
packages/sdk/js/src/v2/gen/types.gen.ts Add sessionOnly and aliases to Command type

Key Changes

1. New Plugin Hook Interface (packages/plugin/src/index.ts)

"plugin.command"?: {
  [key: string]: {
    description: string
    aliases?: string[]
    sessionOnly?: boolean
    execute(input: { sessionID?: string; client: ReturnType<typeof createOpencodeClient> }): Promise<void>
  }
}

2. Command Schema Updates (packages/opencode/src/command/index.ts)

// Add to Command.Info schema
sessionOnly: z.boolean().optional(),
aliases: z.array(z.string()).optional(),

3. Plugin Command Loading (packages/opencode/src/command/index.ts)

const plugins = await Plugin.list()
for (const plugin of plugins) {
  const commands = plugin["plugin.command"]
  if (!commands) continue
  for (const [name, cmd] of Object.entries(commands)) {
    if (result[name]) continue
    result[name] = {
      name,
      description: cmd.description,
      template: "",
      sessionOnly: cmd.sessionOnly,
      aliases: cmd.aliases,
    }
  }
}

4. Plugin Command Execution (packages/opencode/src/session/prompt.ts)

  • Check for plugin commands before built-in commands
  • Execute with { sessionID, client } context
  • Handle errors gracefully with user feedback

Example Plugin Command

import type { Plugin } from "@opencode-ai/plugin"

export const CommandsPlugin: Plugin = async (ctx) => {
  return {
    "plugin.command": {
      hello: {
        description: "Say hello",
        aliases: ["hi"],
        sessionOnly: true,
        async execute({ sessionID, client }) {
          await client.session.sendMessage({
            path: { id: sessionID! },
            body: { content: "Hello from plugin command!" },
          })
        },
      },
    },
  }
}

Tasks

  • Cherry-pick or manually apply changes from upstream PR
  • Verify no conflicts with fork-specific changes
  • Rebuild SDK: cd packages/sdk/js && bun run script/build.ts
  • Test plugin command registration
  • Test autocomplete with sessionOnly filtering
  • Test alias resolution
  • Test error handling for failed plugin commands
  • Run full test suite

References

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions