opencode icon indicating copy to clipboard operation
opencode copied to clipboard

[FEATURE]: SessionStart hook for session lifecycle events

Open simonwjackson opened this issue 3 weeks ago • 2 comments

Feature hasn't been suggested before.

  • [x] I have verified this feature I'm about to request hasn't been suggested before.

Request

Add a SessionStart hook (similar to Claude Code) that runs at key session lifecycle points.

Demo

POC: https://github.com/simonwjackson/opencode/pull/new/feature/session-start-hook

Made with VHS

Use Cases

  • Load development context (existing issues, recent codebase changes)
  • Install dependencies or run setup scripts
  • Initialize project-specific tooling
  • Run validation checks before a session begins

Motivation

I have a custom tool that gathers project context (git status, package.json, tsconfig, directory tree) to augment AGENTS.md.

The problem: Manually calling it adds ~12 seconds before I can start working.

What I want: This should run automatically in the background before the first message is sent. Subagents would load this automatically as well.

Why This Can't Be Done With Plugins Today

I investigated the current plugin system and found:

What works:

  • Plugin init function runs once at OpenCode bootstrap (approximates startup)
  • event hook can react to session.created and session.compacted

What's missing:

  • resume - No event fires when resuming via --continue/--session (it just navigates to an existing session)
  • No way to distinguish why a session started

The core limitation is that resume is a UI navigation operation that doesn't fire a bus event.

Proposed Solution

A new plugin hook that fires at session boundaries:

"session.start"?: (input: {
  /** What triggered this hook */
  trigger: "startup" | "resume" | "compact"
  /** Session ID (when resuming an existing session) */
  sessionID?: string
}) => Promise<void>

Triggers

Trigger When it fires
startup Fresh OpenCode start
resume --resume, --continue, or /resume
compact Auto or manual compaction

Example

  export const TestSessionStartPlugin = async ({ $ }) => ({
    "session.start": async (input, output) => {
      const gitBranch = await $`git branch --show-current 2>/dev/null`.text().catch(() => "unknown")                                                                                                                                            
      const gitStatus = await $`git status --short 2>/dev/null`.text().catch(() => "")                                                                                                                                                          
      const recentCommits = await $`git log --oneline -3 2>/dev/null`.text().catch(() => "")                                                                                                                                                    
                                                                                                                                                                                                                                                
      output.additionalContext = `<project-context>                                                                                                                                                                                             
  ## Current Branch: ${gitBranch.trim()}                                                                                                                                                                                                        
                                                                                                                                                                                                                                                
  ## Uncommitted Changes                                                                                                                                                                                                                        
  ${gitStatus.trim() || '(none)'}                                                                                                                                                                                                               
                                                                                                                                                                                                                                                
  ## Recent Commits                                                                                                                                                                                                                             
  ${recentCommits.trim() || '(none)'}                                                                                                                                                                                                           
  </project-context>`                                                                                                                                                                                                                           
    }                                                                                                                                                                                                                                           
  })   

References

simonwjackson avatar Dec 11 '25 23:12 simonwjackson

This feature request appears to be unique and doesn't duplicate existing issues. However, it's related to several ongoing plugin system improvements:

  • #5148 - Comprehensive Plugin Pipeline (middleware-style data flow control)
  • #4726 - Custom tools install hook (initialization hooks for tools)
  • #5305 - Plugin Hook for Instant TUI Commands (expanding plugin hook system)

These issues are complementary to your SessionStart hook proposal and might benefit from coordinated implementation.

github-actions[bot] avatar Dec 11 '25 23:12 github-actions[bot]

Tried the POC branch - the session.start hook works for startup (new sessions) but fails on resume when using --continue. Getting "No context found for instance" error because Plugin.triggerSessionStart is called during route navigation before the plugin context is fully initialized. The hook fires (confirmed via logging) but the plugin system isn't ready yet at that point in the --continue bootstrap sequence.

Brandtweary avatar Dec 14 '25 04:12 Brandtweary