55codex.nvim is a Neovim plugin that orchestrates an embedded terminal session for
66the Codex CLI. The architecture centres on a ** pluggable provider** abstraction:
77all terminal management (opening, closing, sending text, focus, toggling) is
8- delegated to a provider that satisfies a 9-method interface contract, while the
9- core module (` init.lua ` ) owns session lifecycle, dependency wiring, and the
10- public Lua API.
8+ delegated to a provider that satisfies a 9-method interface contract. The core
9+ module (` init.lua ` ) acts as a thin ** facade** -- it owns dependency wiring and
10+ the public Lua API, while delegating session lifecycle, send dispatch, and
11+ mention orchestration to dedicated modules.
1112
1213```
1314plugin/codex.lua (entry point, version guard, load guard)
1415 │
1516 ▼
16- lua/codex/init.lua (public API, session lifecycle , DI container)
17+ lua/codex/init.lua (public API facade , DI container, setup wiring )
1718 │
1819 ├──► config.lua (defaults, validation, deep merge)
1920 ├──► logger.lua (level-gated vim.notify wrapper)
@@ -24,12 +25,16 @@ lua/codex/init.lua (public API, session lifecycle, DI container)
2425 │ └── snacks.lua
2526 ├──► context/
2627 │ ├── formatter.lua (selection + mention payload formatting)
28+ │ ├── mention.lua (mention orchestration, prompt capture/restore)
2729 │ ├── path.lua (CWD-relative path normalization)
2830 │ └── selection.lua (visual selection extraction)
2931 ├──► runtime/
30- │ └── send_queue.lua (FIFO queue + retry timer for startup readiness)
32+ │ ├── send_dispatch.lua (send pipeline, queue/retry orchestration)
33+ │ ├── send_queue.lua (FIFO queue + retry timer for startup readiness)
34+ │ └── terminal_io.lua (terminal I/O utilities + constants)
3135 └──► state/
32- └── session_store.lua (session registry, alive/dead tracking)
36+ ├── session_lifecycle.lua (session open/close/toggle/focus operations)
37+ └── session_store.lua (session registry, alive/dead tracking)
3338```
3439
3540## Directory Layout
@@ -40,9 +45,9 @@ codex.nvim/
4045│ └── codex.lua # Entry point. Guards Neovim >= 0.11.0 and
4146│ # prevents double-loading via vim.g.loaded_codex.
4247├── lua/codex/
43- │ ├── init.lua # Public API surface (setup, open, close, toggle,
48+ │ ├── init.lua # Public API facade (setup, open, close, toggle,
4449│ │ # send, send_selection, mention_file, mention_directory, resume, etc.).
45- │ │ # Owns the DI container and session lifecycle .
50+ │ │ # Owns the DI container, setup wiring, and thin delegates .
4651│ ├── config.lua # Default config table, vim.validate-based
4752│ │ # validation, and deep-merge with user options.
4853│ ├── types.lua # EmmyLua annotations only (no runtime code).
@@ -66,15 +71,28 @@ codex.nvim/
6671│ │ ├── formatter.lua # Formats selection payloads (fenced code blocks with
6772│ │ │ # adaptive backtick fencing) and /mention payloads
6873│ │ │ # (auto-quoting paths with special characters).
74+ │ │ ├── mention.lua # Mention orchestration: captures terminal prompt input,
75+ │ │ │ # dispatches /mention payloads, auto-submits, and restores
76+ │ │ │ # previously typed text. Uses create(opts) constructor.
6977│ │ ├── path.lua # Normalizes file paths to CWD-relative form via
7078│ │ │ # fnamemodify(":."). Falls back to the original path
7179│ │ │ # on error.
7280│ │ └── selection.lua # Extracts visual selection from the current buffer.
7381│ │ # Resolves range via command args or visual marks.
7482│ ├── runtime/
75- │ │ └── send_queue.lua # Startup-readiness send queue. Owns retry scheduling
76- │ │ # and FIFO flushing for deferred payload dispatch.
83+ │ │ ├── send_dispatch.lua # Send pipeline with startup/retry orchestration.
84+ │ │ │ # Builds PendingSend items, submits to send_queue,
85+ │ │ │ # handles session readiness checks and timeout logic.
86+ │ │ │ # Uses create(opts) constructor.
87+ │ │ ├── send_queue.lua # Startup-readiness send queue. Owns retry scheduling
88+ │ │ │ # and FIFO flushing for deferred payload dispatch.
89+ │ │ └── terminal_io.lua # Pure/near-pure terminal I/O utilities: bracketed paste
90+ │ │ # encoding, termcode expansion, prompt line parsing,
91+ │ │ # ANSI stripping, and shared constants.
7792│ └── state/
93+ │ ├── session_lifecycle.lua # Session lifecycle operations: open, close, toggle,
94+ │ │ # focus, alive/ready checks, post-send refocus.
95+ │ │ # All functions take (deps, config) explicitly.
7896│ └── session_store.lua # In-memory session registry. Tracks sessions by ID
7997│ # with alive/dead lifecycle, active session pointer,
8098│ # and monotonic counter for ID generation.
@@ -98,9 +116,11 @@ codex.nvim/
98116
99117## Key Design Patterns
100118
101- ### Module Pattern
119+ ### Module Patterns
102120
103- Every Lua module follows the standard Neovim module pattern:
121+ #### Stateless modules
122+
123+ Most modules follow the standard Neovim module pattern:
104124
105125``` lua
106126local M = {}
@@ -115,7 +135,36 @@ return M
115135```
116136
117137Functions on ` M ` are the public API; bare ` local function ` declarations are
118- private. This convention is consistent across every file in ` lua/codex/ ` .
138+ private. Examples: ` terminal_io.lua ` , ` session_lifecycle.lua ` , ` config.lua ` .
139+
140+ #### Constructor modules
141+
142+ Modules that need runtime accessors (closures over ` get_deps ` , ` get_config ` ,
143+ etc.) use a ` create(opts) ` constructor that returns a table of bound functions:
144+
145+ ``` lua
146+ local M = {}
147+
148+ function M .create (opts )
149+ local get_deps = opts .get_deps
150+
151+ local function private_helper () ... end
152+
153+ local function public_method ()
154+ local deps = get_deps ()
155+ ...
156+ end
157+
158+ return { public_method = public_method }
159+ end
160+
161+ return M
162+ ```
163+
164+ ` init.lua ` calls ` create() ` during ` setup() ` and stores the returned instance
165+ in ` state ` . This pattern avoids circular dependencies -- extracted modules never
166+ ` require("codex") ` back. Examples: ` send_dispatch.lua ` , ` mention.lua ` ,
167+ ` send_queue.lua ` .
119168
120169### Provider Abstraction
121170
@@ -154,8 +203,13 @@ tracks:
154203
155204The store exposes ` create ` , ` get ` , ` get_active ` , ` mark_dead ` , ` remove ` , ` list ` ,
156205and ` reset ` . Only one session is "active" at a time. When a provider fires its
157- ` on_exit ` callback, ` init.lua ` walks the session list to find the matching
158- handle and calls ` mark_dead ` .
206+ ` on_exit ` callback, ` session_lifecycle.mark_session_dead_by_handle() ` walks the
207+ session list to find the matching handle and calls ` mark_dead ` .
208+
209+ ` state/session_lifecycle.lua ` builds on the session store with higher-level
210+ operations: ` open_session ` , ` close_session ` , ` toggle_session ` , ` focus_session ` ,
211+ and alive/ready checks. All functions take ` (deps, config) ` explicitly, making
212+ them stateless and testable without the full ` init.lua ` setup.
159213
160214### Dependency Injection
161215
@@ -182,6 +236,18 @@ override the corresponding defaults. The `_deps` key is then stripped before
182236config validation. This enables full isolation in unit tests: every collaborator
183237(including ` vim ` itself) can be replaced with a mock.
184238
239+ After resolving deps and config, ` setup() ` wires the constructor modules:
240+
241+ 1 . ` send_dispatch.create() ` -- receives ` get_deps ` , ` get_config ` ,
242+ ` get_send_queue ` , and an ` open_session ` closure.
243+ 2 . ` send_queue.new() ` -- receives the send dispatch's
244+ ` process_pending_send_item ` as its process callback.
245+ 3 . ` mention.create() ` -- receives ` get_deps ` , ` get_config ` , and a
246+ ` dispatch_send ` closure that delegates to the send dispatch instance.
247+
248+ This wiring order allows ` send_dispatch ` to reference the send queue (via
249+ closure) even though the queue is created after the dispatch instance.
250+
185251### Error Handling
186252
187253The codebase uses two error-reporting strategies:
@@ -202,15 +268,16 @@ failures downstream.
202268### Auto-Open
203269
204270APIs that need an active session (` send ` , ` send_command ` , ` focus ` ,
205- ` send_selection ` , ` mention_file ` , ` mention_directory ` ) automatically open one when needed. The
206- lower-level ` send ` API opens without focus, while command-facing flows
207- (` :CodexSend ` , ` :CodexAdd ` ) ensure the terminal is opened with focus before
208- payload dispatch. If the provider handle is not yet ready, payloads are queued
209- and retried on a timer (` terminal.startup.retry_interval_ms ` ) until ready or
210- timeout (` terminal.startup.timeout_ms ` ). Queueing/scheduling is implemented in
211- ` runtime/send_queue.lua ` , while ` init.lua ` owns session/open/reopen decisions.
212- Providers apply a startup grace delay via ` terminal.startup.grace_ms ` before
213- reporting readiness.
271+ ` send_selection ` , ` mention_file ` , ` mention_directory ` ) automatically open one
272+ when needed. The lower-level ` send ` API opens without focus, while
273+ command-facing flows (` :CodexSend ` , ` :CodexMentionFile ` ) ensure the terminal is
274+ opened with focus before payload dispatch. If the provider handle is not yet
275+ ready, payloads are queued and retried on a timer
276+ (` terminal.startup.retry_interval_ms ` ) until ready or timeout
277+ (` terminal.startup.timeout_ms ` ). Queueing/scheduling is implemented in
278+ ` runtime/send_queue.lua ` , while ` runtime/send_dispatch.lua ` owns
279+ session/open/reopen decisions. Providers apply a startup grace delay via
280+ ` terminal.startup.grace_ms ` before reporting readiness.
214281
215282## Component Interaction
216283
@@ -243,6 +310,8 @@ Key types:
243310| ` codex.SelectionSpec ` | class | Visual selection data (defined in ` formatter.lua ` ) |
244311| ` codex.SelectionOpts ` | class | Options for selection extraction (defined in ` selection.lua ` ) |
245312| ` codex.UserCommandOpts ` | class | Neovim user command callback argument shape |
313+ | ` codex.PendingSend ` | class | Queued send item (defined in ` send_dispatch.lua ` ) |
314+ | ` codex.DispatchSendOpts ` | class | Options for dispatch_send (defined in ` send_dispatch.lua ` ) |
246315| ` codex.ResumeOpts ` | class | Options for the resume API (defined in ` init.lua ` ) |
247316| ` codex.SendResult ` | alias | Boolean result alias (defined in ` init.lua ` ) |
248317
0 commit comments