Skip to content

Add OpenCode and Codex CLI agent support#257

Open
Avyukth wants to merge 8 commits intoentireio:mainfrom
Avyukth:add-opencode-codex-agent-support
Open

Add OpenCode and Codex CLI agent support#257
Avyukth wants to merge 8 commits intoentireio:mainfrom
Avyukth:add-opencode-codex-agent-support

Conversation

@Avyukth
Copy link

@Avyukth Avyukth commented Feb 11, 2026

Closes #245
Closes #266
Relates to #281

Summary

Adds two new AI coding agent integrations to the Entire CLI: OpenCode and Codex CLI. Both agents support full session checkpointing via hooks — capturing transcripts, file changes, prompts, and metadata on shadow branches.

As part of this work, duplicated framework code was extracted into a shared commit pipeline ("$(hooks_common_session.go)"), directly addressing issue #281's goal of separating framework from agent-specific logic. The three agent handlers (Codex, OpenCode, Gemini) that previously duplicated ~250 lines of commit logic are now thin wrappers calling a shared "$(commitAgentSession())" function.

~3,000 lines across 16 files (all new code from main's perspective).


Framework Extraction (addresses #281)

The shared "$(hooks_common_session.go)" consolidates duplicated logic from Codex, OpenCode, and Gemini handlers:

Extracted Function Replaces Purpose
"$(commitAgentSession())" "$(commitCodexSession)", "$(commitOpenCodeSession)", "$(commitGeminiSession)" Core commit pipeline: load state → detect changes → save via strategy
"$(createContextFile())" "$(createContextFileForCodex)", "$(createContextFileForOpenCode)", "$(createContextFileForGemini)" Unified context.md generation (single + multi-prompt)
"$(transitionSessionTurnEnd())" Was in Claude Code handlers, used by all agents Session phase state machine transition
"$(logFileChanges())" Was in Gemini handlers, used by all agents Stderr logging of modified/new/deleted files

Each agent handler is now a thin wrapper that populates an "$(agentCommitContext)" struct:

  • Codex: provides transcript-extracted modified files, single prompt
  • OpenCode: sets "$(useGitStatusForModified: true)" (no transcript parsing), single prompt
  • Gemini: adds token usage calculation, multi-prompt array, transcript step tracking

This reduces the barrier for adding new agents — a new handler only needs to populate the context struct and call "$(commitAgentSession())".

Not yet extracted (future #281 work): Claude Code's "$(commitWithMetadata)" has unique subagent/summarization/incremental-checkpoint logic that requires its own pattern. The "$(Agent)" interface itself is also unchanged — broader interface refactoring is deferred to the core team per #281.


OpenCode Agent

  • TypeScript plugin system: generates "$(.opencode/plugins/entire.ts)" that subscribes to "$(session.created)" and "$(session.status)" (busy/idle) events via BunShell
  • 3 hooks: "$(session-created)" (SessionStart), "$(session-busy)" (per-turn state capture), "$(session-idle)" (Stop/checkpoint)
  • Per-turn pre-prompt state capture via "$(session-busy)" hook — captures untracked files before agent starts each turn for accurate new-file detection
  • File change detection via git status with pre-prompt untracked baseline (OpenCode sessions are opaque JSON — no transcript-based file extraction possible)
  • Session storage: reads from XDG data directory ("$(~/.local/share/opencode/storage/session/)")
  • Recursive session file discovery via "$(filepath.WalkDir)" (OpenCode nests sessions under "$(/)" subdirectories)

Codex CLI Agent

  • TOML-based notify hook: installs "$(notify = ["entire", "hooks", "codex", "agent-turn-complete"])" in "$(.codex/config.toml)"
  • Parses Codex JSONL rollout transcripts for file change extraction ("$(file_change)", "$(function_call)", "$(event_msg/patch_apply_begin)" items)
  • Codex sends notify payload via argv (not stdin) — handler reads from last positional argument via "$(GetCurrentHookArgs())"
  • Post-turn state capture — captures untracked files after each turn completes as baseline for next turn's file detection (Codex has no before-turn hook in codex-rs)
  • Recursive session file discovery via "$(filepath.WalkDir)" (Codex uses date-based "$(YYYY/MM/DD/)" directory structure)
  • Session storage: reads from "$(~/.codex/sessions/)" (respects "$(CODEX_HOME)" env var)
  • Local dev mode resolves project path at write-time via "$(paths.RepoRoot())" (Codex executes notify as direct argv without shell expansion)

Shared Infrastructure

  • "$(hooks_common_session.go)" — shared commit pipeline with "$(agentCommitContext)" struct
  • Hook registry entries for both agents in "$(hook_registry.go)"
  • "$(currentHookArgs)" + "$(GetCurrentHookArgs())" for agents that pass payloads via positional args
  • "$(fileExistsAndIsRegular())" helper to distinguish files from directories

Key Implementation Details

Codex argv payload handling:
Codex's Rust source ("$(user_notification.rs)") calls "$(command.arg(notify_payload))" with "$(stdin(Stdio::null()))". The hook registry captures positional args via "$(cobra.ArbitraryArgs)" and stores them in "$(currentHookArgs)". The handler reads from "$(args[len(args)-1])" when available, falling back to stdin.

OpenCode session-busy hook:
OpenCode fires "$(session.status)" with "$(type: "busy")" when the agent starts processing each turn. The TypeScript plugin pipes this to "$(entire hooks opencode session-busy)", which calls "$(CapturePrePromptState(sessionID, ""))" to snapshot untracked files before the agent modifies anything. The subsequent "$(session-idle)" handler uses "$(commitAgentSession())" with "$(useGitStatusForModified: true)".

Codex post-turn state capture (workaround):
Codex only exposes "$(AfterAgent)" and "$(AfterToolUse)" hooks — no "before turn" event exists. As a workaround, "$(handleCodexAgentTurnComplete)" captures state after each turn completes ("$(CapturePrePromptState)" at the end), which serves as the baseline for the next turn's "$(DetectFileChanges)".

File existence checks:
"$(fileExistsAndIsRegular())" replaces "$(fileExists())" for transcript path validation. Codex's "$(ParseHookInput)" sets "$(SessionRef)" to "$(~/.codex/sessions)" (a directory). Without the regular-file check, "$(copyFile(dir, ...))" would fail with "is a directory".

Codex JSONL file extraction:
Supports three event formats from Codex rollout transcripts:

  • "$(response_item)" with "$(file_change)" items (direct file path)
  • "$(response_item)" with "$(function_call)" items (write_file, apply_diff, edit_file, create_file)
  • "$(event_msg)" with "$(patch_apply_begin)" payload (HashMap keys are file paths — actual production format from "$(exec_events.rs)")

Uses "$(bufio.NewReader)" + "$(ReadBytes('\n'))" instead of "$(bufio.Scanner)" to handle JSONL lines exceeding 64KB.

TOML hook installation (Codex):
"$(InstallHooks)" parses "$(.codex/config.toml)" line-by-line, preserving user content and comments. When a user-configured "$(notify)" line exists and "$(--force)" isn't set, installation is skipped to avoid duplicate TOML keys.


Feature Comparison: Claude Code vs OpenCode vs Codex

Hook Coverage

Hook Event Claude Code OpenCode Codex
Session Start "$(session-start)" "$(session-created)"
Session End "$(session-end)"
Per-Turn State Capture "$(user-prompt-submit)" "$(session-busy)" post-turn (workaround)
Turn Complete / Checkpoint "$(stop)" "$(session-idle)" "$(agent-turn-complete)"
Pre-Task (subagent) "$(pre-task)"
Post-Task (subagent) "$(post-task)"
Post-Todo (incremental) "$(post-todo)"
Total 7 3 1

File Change Detection

Method Claude Code Codex OpenCode
Transcript-based (modified) Write/Edit/NotebookEdit tool calls file_change, function_call, patch_apply_begin None (opaque JSON)
Git-status-based (new/deleted) With pre-prompt baseline With pre-prompt baseline With pre-prompt baseline
Git-status-based (modified) No No Yes (primary method)

Agent Interfaces Implemented

Interface Claude Code Codex OpenCode
"$(Agent)" Yes Yes Yes
"$(HookSupport)" Yes Yes Yes
"$(HookHandler)" Yes Yes Yes
"$(TranscriptAnalyzer)" Yes No No
"$(TranscriptChunker)" Yes Yes No

Remaining Gaps (Agent Limitations)

Gap Reason
Codex: no true before-turn hook Only "$(AfterAgent)" and "$(AfterToolUse)" exist in codex-rs
OpenCode: no transcript parsing Sessions stored as opaque JSON, no tool-call data accessible
Codex/OpenCode: no token tracking Neither agent exposes token usage in payloads or transcripts
Codex/OpenCode: no subagent support Neither has a subagent/task architecture
OpenCode: no session-end hook No event fires when session is explicitly closed

Test Plan

  • "$(mise run fmt)" — no formatting issues
  • "$(mise run lint)" — 0 new issues
  • "$(mise run test)" — all unit tests pass
  • "$(mise run test:ci)" — full CI suite with race detection passes
  • "$(mise run test:integration)" — integration tests pass
  • Manual: "$(entire enable --agent opencode)" installs "$(.opencode/plugins/entire.ts)"
  • Manual: "$(entire enable --agent codex)" installs notify hook in "$(.codex/config.toml)"
  • Manual: idempotent re-enable ("$(entire enable --agent opencode)" twice)
  • Manual: "$(entire hooks codex agent-turn-complete '{...}')" reads from argv
  • Manual: "$(entire hooks opencode session-idle)" reads from stdin pipe
  • OpenCode tests verify 3 hooks, session-busy parsing, plugin content includes busy handler
  • Codex tests verify argv payload reading, JSONL event extraction, TOML hook installation

🤖 Generated with Claude Code

@Avyukth Avyukth requested a review from a team as a code owner February 11, 2026 13:51
@Avyukth Avyukth force-pushed the add-opencode-codex-agent-support branch from 1416df8 to 6347727 Compare February 12, 2026 01:51
Avyukth and others added 8 commits February 12, 2026 17:01
Add two new AI coding agent integrations to the plugin-style agent
architecture:

- OpenCode: open-source AI coding CLI with .opencode/ config, no
  lifecycle hooks, XDG-based session storage, JSON sessions
- Codex CLI: OpenAI's CLI agent with .codex/config.toml (TOML),
  notify hook (agent-turn-complete), JSONL session transcripts in
  ~/.codex/sessions/

Both agents follow existing patterns: init() self-registration,
Agent interface implementation, comprehensive tests. Codex also
implements HookSupport, HookHandler, TranscriptAnalyzer, and
TranscriptChunker interfaces.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 0930c3197f05
… issues

OpenCode hooks:
- Implement HookSupport/HookHandler via TypeScript plugin at .opencode/plugins/entire.ts
- Plugin subscribes to session.created and session.status (idle) events
- Add hook handlers (session-created, session-idle) with full session commit flow
- Register OpenCode handlers in hook registry

CodeRabbit fixes:
- Add parent dir creation in WriteSession for both Codex and OpenCode agents
- Fix GetTranscriptPosition off-by-one for files without trailing newline
- Replace filepath.Glob with filepath.WalkDir for recursive session file search
- Fix force=true creating duplicate TOML notify keys in Codex hooks
- Set userPrompt from hook input in Codex handler context
- Remove redundant DetectPresence check in Codex agent
- Extract shared extractFilePathFromLine helper to deduplicate extraction logic
- Fix isEntireNotifyLine false positives matching "entire" as substring
- Change FileModificationTools from mutable var to function (immutability)
- Update Codex rollout types to match actual format (response_item, function_call)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: b0b842285f30
…env var

- Add explicit nil guard before calling preState.PreUntrackedFiles() in both
  codex and opencode handlers (PreUntrackedFiles has a nil receiver check but
  being explicit is better defensive coding)
- Resolve CODEX_PROJECT_DIR env var at write-time in buildLocalDevNotifyCommand
  since Codex executes notify commands as direct argv without shell expansion
- Add test for resolved path detection in isEntireNotifyLine
- Add test verifying no unresolved ${...} templates in local dev config

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 1e577052cd36
…, scanner limit, TOML keys

- (CRITICAL) Codex sends notify payload via argv not stdin; capture positional
  args in hook_registry.go and read -r from last arg in Codex handler
- (HIGH) fileExists returns true for directories; add fileExistsAndIsRegular
  helper and use for transcript path checks in both handlers
- (HIGH) File extraction misses actual Codex event_msg/patch_apply_begin format
  where file paths are HashMap keys; add extractFilePathsFromEventMsg
- (LOW) Replace bufio.Scanner (64KB limit) with bufio.NewReader in
  ExtractModifiedFiles to handle large JSONL lines
- (LOW) Fix duplicate TOML notify keys when Entire line found alongside user
  notify line; simplify guard to always respect user-configured notify commands

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 662e9607cc62
…, test coverage

- OpenCode ResolveSessionFile now walks directory tree to find session
  files nested under projectID subdirectories (matching actual storage layout)
- Codex buildLocalDevNotifyCommand uses paths.RepoRoot() instead of
  os.Getwd() to avoid baking subdirectory paths into notify command
- Add tests for null/missing last-assistant-message in Codex ParseHookInput
- Add tests for OpenCode recursive session file resolution with projectID nesting
- Extract testThreadID constant to fix goconst lint warning

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: d60164698930
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 2638743a8f84
OpenCode: Add session-busy hook that fires on session.status busy event,
capturing pre-prompt state (untracked files) at the start of each turn so
session-idle can accurately detect new files created during the turn.

Codex: Capture post-turn state after each agent-turn-complete as baseline
for next turn's file change detection (Codex has no before-turn hook).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 97aad7243b13
Consolidate duplicated commit logic from Codex, OpenCode, and Gemini
handlers into a shared commitAgentSession() function. Each agent handler
is now a thin wrapper that populates an agentCommitContext struct.

Also moves transitionSessionTurnEnd() and logFileChanges() to the shared
file since they are used by all agents. Removes per-agent createContextFile
variants in favor of a unified createContextFile() that supports both
single and multi-prompt formats.

Net reduction: ~160 lines of duplicated code removed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 2b7d7a03ca93
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Opencode support Codex support

1 participant