Skip to content

Instantly share code, notes, and snippets.

@johnlindquist
Created January 12, 2026 02:20
Show Gist options
  • Select an option

  • Save johnlindquist/cfa254df98601a9de7e14e443356c204 to your computer and use it in GitHub Desktop.

Select an option

Save johnlindquist/cfa254df98601a9de7e14e443356c204 to your computer and use it in GitHub Desktop.
OpenCode Plugin Logging Infrastructure - What we learned

OpenCode Plugin Logging Infrastructure

What We Did

Added comprehensive session-based file logging to all 8 OpenCode plugins in .opencode/plugin/. Each plugin now logs every hook invocation, whether it triggered an action or was skipped.

Files Created/Modified

  1. .opencode/lib/logger.ts - Shared logging utility
  2. All 8 plugins - Added logTriggered() and logSkipped() calls to every hook

Log Output

Logs are written to .opencode/logs/<session-id>.log as JSON lines:

{"timestamp":"2026-01-12T02:18:59.890Z","plugin":"conventional-commit-reminder","hook":"tool.execute.after","sessionId":"abc-123","triggered":true,"message":"Tracked: code file modified: src/main.rs","details":{"tool":"edit","totalModifiedFiles":3}}
{"timestamp":"2026-01-12T02:19:01.123Z","plugin":"verification-gate","hook":"tool.execute.before","sessionId":"abc-123","triggered":false,"message":"Not a commit command","details":{"command":"cargo check"}}

Log Fields

Field Description
timestamp ISO 8601 timestamp
plugin Plugin name (e.g., "conventional-commit-reminder")
hook Hook name (e.g., "tool.execute.after", "stop", "event")
sessionId OpenCode session ID for correlation
triggered true if action was taken, false if skipped
message Human-readable description
details Optional object with additional context

Key Learning: OpenCode Plugin Loading

OpenCode loads ALL .ts files in the plugin/ directory as plugins.

This means:

  • Every .ts file must export a valid Plugin as its default export
  • Utility modules (like our logger) must live in a different directory
  • We moved logger.ts to .opencode/lib/ to fix this

The Error We Hit

TypeError: null is not an object (evaluating 'hook.config')

This cryptic error occurred because OpenCode tried to load logger.ts as a plugin, but it exported utility functions instead of a Plugin function.

Directory Structure

.opencode/
├── lib/
│   └── logger.ts          # Utility module (NOT a plugin)
├── logs/
│   └── <session-id>.log   # Log files (gitignored)
└── plugin/
    ├── cargo-fmt-check.ts           # Plugin
    ├── conventional-commit-reminder.ts
    ├── guideline-enforcer.ts
    ├── project-reminders.ts
    ├── screenshot-enforcer.ts
    ├── thoroughness-enforcer.ts
    ├── verification-gate.ts
    └── visual-testing-reminder.ts

Why This Matters

  1. Post-session debugging - Review what plugins did without watching console
  2. Verify enforcement - Confirm plugins are triggering when expected
  3. Understand behavior - See why a plugin did or didn't take action
  4. Session correlation - All logs for a session in one file

Usage

After an OpenCode session completes, check:

# List all session logs
ls -la .opencode/logs/

# View a specific session
cat .opencode/logs/<session-id>.log | jq .

# Find all triggered actions
grep '"triggered":true' .opencode/logs/*.log | jq .

# Find all commit-related logs
grep 'commit' .opencode/logs/*.log
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment