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

Open
opened 2026-02-16 17:40:18 -05:00 by yindo · 2 comments
Owner

Originally created by @simonwjackson on GitHub (Dec 11, 2025).

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

Originally created by @simonwjackson on GitHub (Dec 11, 2025). ### 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](https://docs.anthropic.com/en/docs/claude-code/hooks#sessionstart)) that runs at key session lifecycle points. ### Demo POC: https://github.com/simonwjackson/opencode/pull/new/feature/session-start-hook ![Made with VHS](https://vhs.charm.sh/vhs-7mRJcIEKMuhwjcWZZAPvwg.gif) ## 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: ```typescript "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 ```typescript 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 - [Claude Code SessionStart Hook](https://docs.anthropic.com/en/docs/claude-code/hooks#sessionstart) - Prior art for this feature
yindo added the discussion label 2026-02-16 17:40:18 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Dec 11, 2025):

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] commented on GitHub (Dec 11, 2025): 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.
Author
Owner

@Brandtweary commented on GitHub (Dec 13, 2025):

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 commented on GitHub (Dec 13, 2025): 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.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#3484