Skip to content

[Plugins]: Add entry_command field to PluginManifest (to support Initial Prompts) #2229

@jpshackelford

Description

@jpshackelford

Summary

Add an optional entry_command field to PluginManifest that allows plugin authors to specify which command should be invoked by default when launching their plugin.

Background

When launching a plugin via the OpenHands plugin directory or /launch route, the system needs to know what slash command to invoke. Currently, this is hard-coded by the caller (e.g., /city-weather:now Tokyo).

By adding entry_command to the plugin manifest, plugin authors can declare their intended entry point, and consumers (plugin-directory, future OpenHands plugin registration system) can read this value to construct the appropriate initial message.

Proposed Changes

1. Add entry_command to PluginManifest (in types.py):

class PluginManifest(BaseModel):
    name: str = Field(description="Plugin name")
    version: str = Field(default="1.0.0", description="Plugin version")
    description: str = Field(default="", description="Plugin description")
    author: PluginAuthor | None = Field(default=None, description="Plugin author")
    
    # NEW FIELD
    entry_command: str | None = Field(
        default=None,
        description=(
            "Default command to invoke when launching this plugin. "
            "Should match a command name from the commands/ directory. "
            "Example: 'now' for a command defined in commands/now.md"
        ),
    )

    model_config = {"extra": "allow"}

2. Add convenience property to Plugin (in plugin.py):

@property
def entry_slash_command(self) -> str | None:
    """Get the full slash command for the entry point, if defined.
    
    Returns the slash command in format /<plugin-name>:<command-name>,
    or None if no entry_command is defined in the manifest.
    
    Example:
        >>> plugin = Plugin.load(path)
        >>> plugin.entry_slash_command
        '/city-weather:now'
    """
    if not self.manifest.entry_command:
        return None
    return f"/{self.name}:{self.manifest.entry_command}"

Example Usage

plugin.json:

{
  "name": "city-weather",
  "version": "1.0.0",
  "description": "Get current weather for any city",
  "entry_command": "now"
}

Consumer code:

plugin = Plugin.load(path)

if plugin.entry_slash_command:
    # Construct initial message: "/city-weather:now Tokyo"
    initial_message = f"{plugin.entry_slash_command} {user_arguments}"

Claude Code Compatibility

This is an OpenHands extension to the Claude Code plugin format. The entry_command field is not part of the official Claude Code schema.

However, this should not break compatibility because:

  • JSON parsers typically ignore unknown fields
  • Our PluginManifest already uses extra="allow" which preserves unknown fields
  • Claude Code should similarly ignore fields it doesn't recognize

Non-Goals

This issue does not change conversation flow or add auto-launch behavior. The SDK remains message-agnostic - it simply exposes the entry_command data for callers to use as they see fit.

Related

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestpluginsAbout plugins and their contents

Projects

Status

In Progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions