Skip to content

[FEATURE]: SessionStart hook for session lifecycle events #5409

@simonwjackson

Description

@simonwjackson

Feature hasn't been suggested before.

  • 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    discussionUsed for feature requests, proposals, ideas, etc. Open discussion

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions