Skip to content

AlexanderGolys/codex.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

54 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

codex-ide.nvim

codex-ide.nvim is a new standalone Neovim plugin aimed at becoming the closest practical equivalent of the official Codex IDE extensions for VS Code-compatible editors, but adapted to Neovim.

This project is intentionally not a continuation of clodex.nvim's original product shape. clodex.nvim grew around terminal/session orchestration, prompt queues, and project-local workflow management. This new plugin has a different center:

  • reproduce the official Codex IDE extension model as closely as practical
  • use Codex-native concepts such as thread, turn, item, review, and approval
  • make Neovim act like a real Codex IDE client
  • accept a temporarily weak chat surface if needed, as long as the underlying Codex-facing interface is correct

Product Intent

The goal is to build a real Codex IDE client for Neovim, not just a smarter terminal wrapper.

That means the plugin should eventually provide:

  • native Neovim thread and history management
  • native editor actions for attaching current file, visual selection, diagnostics, and other context
  • native review/diff entry points
  • native approval-related flows where practical
  • alignment with the official Codex IDE extension's commands, settings, and overall model

The conversation/chat surface is important, but not the first architectural priority. The first priority is matching the same Codex-facing interface family and interaction model that the official extensions use.

What This Should Realize

Based on the design discussion that led to this repo, this project should realize the following:

  1. Neovim should become a first-class Codex client.
  2. The official Codex IDE extension for VS Code should be treated as the main reference implementation and product spec.
  3. snacks.nvim should be the required UI foundation because this plugin needs more than just pickers:
    • pickers
    • floating windows and panels
    • terminal-adjacent UX
    • notifications
    • diagnostics/status-facing surfaces
  4. Generic UI infrastructure should not be rewritten from scratch.
  5. Codex-specific state and workflow should remain project-owned:
    • Codex transport/client integration
    • thread/turn/item/review/approval model
    • context attachment semantics
    • high-level workflow coordination
  6. Chat polish should come later. A basic chat surface is acceptable in early versions if the architecture and product model are correct.
  7. The plugin should stay close enough to OpenAI's IDE-extension vision that it would be recognizable as a serious Neovim Codex IDE effort.

Architectural Direction

This repo should be developed around these principles:

  • Follow the official Codex IDE extension definition as closely as possible.
  • Prefer Codex-native concepts over wrapper-era terminology such as workspace.
  • Build a fresh architecture instead of forcing the old clodex.nvim structure to evolve into something it was not designed to be.
  • Use snacks.nvim as infrastructure, not as the owner of product semantics.
  • Delay the hardest conversation/composer UX problem until the rest of the client model is correct.

Dependency Philosophy

Dependencies are welcome when they massively reduce work on solved Neovim UI problems.

Good dependency targets:

  • pickers/selectors
  • floating windows and layout primitives
  • terminal presentation helpers
  • notifications
  • diagnostics-oriented UI
  • protocol/job/plumbing helpers

Areas that should stay owned by this project:

  • Codex-facing model and semantics
  • thread, turn, item, review, and approval state
  • editor-to-Codex context attachment behavior
  • high-level IDE workflow

Initial Required Dependency

The working assumption is that one strong UI foundation is better than stitching together several overlapping picker/window/notification dependencies.

Configuration

Call setup with only the options you want to override:

require("codex_ide").setup({
    codex_cmd = "codex",
    notify = true,
    thread_list_limit = 50,
    project = {
        -- Optional custom resolver. Return an absolute root path.
        -- resolver = function(bufnr) ... end,
        root_markers = { ".git" },
    },
    integrations = {
        lualine = false,
    },
    chat_window = {
        position = "left",
        width = 0.3,
        height = 1.0,
        border = "single",
        enter = true,
        input_placeholder = "Message Codex",
        status = {
            mode = "chat_footer", -- "chat_footer" | false
        },
        style = {
            background = "NONE",
            user_message_background = "#243145",
            item_background = "#202735",
            item_title = "#b7c0d8",
            item_muted = "#7d8597",
            input_background = "#1f2430",
            input_placeholder = "#7d8597",
            winblend = 0,
            winhighlight = {
                Normal = "CodexIdeChatNormal",
                NormalFloat = "CodexIdeChatNormal",
                FloatBorder = "CodexIdeFloatBorder",
                FloatTitle = "CodexIdeFloatTitle",
            },
        },
    },
    keymaps = {
        toggle_chat = "<leader>kk",
        chat = {
            close = "q",
            send = "<CR>",
            interrupt = "x",
        },
    },
})

chat_window.position accepts "left", "right", "top", "bottom", "float", or "floating". Width and height may be fractions of the editor size or absolute cell counts, and visible chat windows reapply those dimensions when Neovim or the chat split is resized. Anchored positions open as real Neovim splits, while "float" and "floating" use a floating window. By default, chat_window.style gives the chat surface no custom background color while leaving border and title highlights on the active theme defaults. Set chat_window.style = false, override style.background, or provide a custom style.winhighlight map to change that behavior. All highlight group names and highlight-definition behavior used by the plugin live in codex_ide.highlights. UI modules consume those constants instead of hardcoding group names locally, and every exported Codex highlight group is defined with either configured colors or a link to an existing editor highlight group.

Statusline plugins can skip Codex chat through the stable codex_ide_chat filetype. For lualine, wrap setup options with the integration helper:

require("lualine").setup(require("codex_ide").lualine().extend_config({
    options = {
        -- existing lualine options
    },
}))

If lualine is configured before codex-ide.nvim loads, call require("codex_ide").lualine().apply() after both plugins are available to reapply the current lualine config with Codex chat disabled for winbar and, when lualine is not using global statusline mode, statusline. Alternatively, set integrations.lualine = true in codex_ide.setup(...) when lualine is already loaded before Codex IDE setup runs.

Set keymaps = false, keymaps.chat = false, or an individual chat mapping to false to disable those defaults. keymaps.toggle_chat is registered in normal, insert, and terminal mode so the same mapping can toggle chat from editor and terminal contexts.

Project detection defaults to: current buffer LSP root, then git root, then configured project.root_markers, then process cwd as a final fallback. Thread context is project-scoped, so one Neovim instance can keep separate active Codex threads for multiple projects at once. Thread project-root normalization is now resolved through an internal helper in codex_ide.thread, so chat open and thread setup remain stable even if external module state mutates exported function fields at runtime. Opening chat creates the UI immediately, then ensures a selected thread in the background: it reuses the current project thread when present, otherwise resumes the newest matching project thread, falls back to a global thread when no project root is available, and creates a new thread only when none of those exist. While bootstrap is pending, the chat window shows a preparing state and blocks sending; if bootstrap fails, the window stays open with the thread/list or thread/start error visible. Thread root metadata from the backend is normalized defensively, including file:// URI-shaped objects, so resumed-thread selection does not crash on non-string cwd payloads. Thread bootstrap uses the app-server cwd filter for project thread lookup and tolerates backend payload variants for both start and resume paths, including embedded thread objects, id-only threadId responses, thread_id responses, result/data/payload wrappers, and direct thread objects. The bootstrapped thread is re-selected for the active project root before chat continues; if no thread id can be derived, the thread layer reports the backend response shape instead of letting chat continue with no selected thread. Sending the first chat message resolves the active thread id defensively before calling turn/start. Internal thread-state helpers are documented and called as module-level dot functions, which keeps LuaLS annotations aligned with the actual helper signatures and avoids treating the module table as a hidden method argument. The thread layer uses concrete LuaLS classes for thread records, turn records, item records, partial item seeds, and backend response wrappers so editor diagnostics can distinguish cached state from transport payloads.

The chat surface renders conversation history with wrapping enabled, bottom-aligns short histories, and keeps a padded input box anchored to the bottom of the chat window so new messages appear above the composer. User messages render as unlabeled turn-separating blocks, normal Codex text stays unboxed and unlabeled, and non-text activity such as reasoning, commands, file changes, tool calls, web search, images, review mode, and context compaction renders as boxed feed items. Boxed feed text keeps its own indentation without rewriting every feed line or changing the input composer padding. Chat buffers use the codex_ide_chat filetype with Markdown syntax so statusline plugins can skip them cleanly. Chat window wrapping, linebreak, breakindent, disabled cursor-line highlighting, hidden line-number and fold columns, blank local statuscolumn, blank local statusline, blank local winbar, and one-cell left/right chat margins are owned by the chat surface and recalculated when the window is opened, reused, refreshed, or resized. Multiline backend strings are normalized into separate Neovim buffer lines before rendering, so inspected tool payloads and command-style output cannot crash refresh. The input box uses chat_window.input_placeholder when empty, removes the placeholder as soon as the input line has text, uses chat_window.style.input_background for its box background, and renders typed text with the editor Normal highlight. While typing in insert mode, cursor movement is constrained to the input line and edits outside that prompt line are restored, so conversation history and the input frame stay read-only. Press the configured keymaps.chat.send mapping to submit the input line. When scrolling, the chat viewport still allows upward movement through history, but clamps downward wheel/key jumps once the input box lower border is already the final visible line.

chat_window.status.mode controls status placement. Use "chat_footer" to render status as the last metadata line in the chat feed, or false to disable it. The chat window does not render a local Neovim statusline. Current status shows model, cwd, and context token size when rendered in the chat footer.

Detailed Implementation Plan

Summary

Create a new standalone Neovim plugin that aims to be the closest practical equivalent of the official Codex IDE extensions, with the official VS Code extension used as the primary product and interface reference.

Core decisions already chosen:

  • new repo/project, not a continuation of clodex.nvim
  • snacks.nvim as the required UI foundation
  • follow the official Codex IDE extension model as closely as possible
  • prioritize matching the same Codex-facing interfaces and concepts
  • defer “great chat UX” until later; initial chat can be basic if the architecture is correct

Product Direction

The plugin should be a real Codex IDE client for Neovim, not a smarter terminal wrapper.

That means v1 should focus on:

  • Codex-native concepts such as thread, turn, item, review, and approval
  • native Neovim actions for attaching context from files and selections
  • thread/history/status management from Neovim UI
  • review/diff/approval entry points from Neovim
  • preserving Codex product identity and alignment with the official IDE vision

Temporary acceptable compromise:

  • the conversation/chat surface may be basic at first
  • chat polish is a later milestone, not the architectural driver

Architecture

Foundation

  • Start from a fresh architecture centered on Codex IDE parity.
  • Do not center the design on old clodex concepts like queue workspaces or custom project/session wrappers.
  • Use the official VS Code extension package as the canonical implementation reference before major design decisions are finalized.

UI

  • Use snacks.nvim as the main required dependency for:
    • pickers
    • floating panels/windows
    • layout and interaction scaffolding
    • related generic UI building blocks
  • Do not add Telescope as a base dependency unless a concrete missing capability later forces it.

Ownership boundaries

Own internally:

  • Codex transport/client integration
  • thread/turn/review/approval model
  • context attachment semantics
  • high-level workflow and state coordination

Depend on snacks.nvim for:

  • picker/select UI
  • float/panel primitives
  • general editor UX infrastructure

Chat strategy

  • Implement a minimal native chat/conversation surface first.
  • Do not let chat quality block the rest of the architecture.
  • Revisit the chat layer at the end, once the Codex-facing integration is correct.
  • Hybrid or terminal-backed chat remains an end-stage option only if needed.

Implementation Order

  1. Create the new repo and plugin skeleton.
  2. Inspect the official VS Code Codex extension package and document:
    • commands
    • settings
    • views
    • runtime interface
    • core model concepts
  3. Define the Neovim architecture around the same Codex-facing model.
  4. Implement the transport/client layer.
  5. Build native IDE surfaces with snacks.nvim:
    • thread picker/history
    • context attach actions
    • review/diff entry points
    • status/metadata surfaces
  6. Add a basic conversation UI sufficient for end-to-end use.
  7. Revisit and redesign the chat UX once the rest of the system is correct.

Test Plan

  • Produce an “official extension parity memo” from package inspection.
  • Produce a Codex interface map documenting the client-side concepts the plugin mirrors.
  • Validate these end-to-end flows:
    • create/open thread
    • attach current file
    • attach visual selection
    • continue conversation
    • inspect review/diff flow
    • handle approval-related flow
  • Validate that snacks.nvim cleanly supports the required UI surfaces without introducing extra UI dependencies.

Assumptions

  • The official VS Code extension is the best available reference for what this plugin should become.
  • snacks.nvim is the correct required UI dependency because the plugin needs a broad UI foundation, not just a picker.
  • Rewriting generic picker/window infrastructure would be wasted effort.
  • Chat quality matters, but getting the Codex-facing interface and product shape right matters more in the first implementation phase.

About

[WIP] Neovim native Codex integration - it's app-server based and aims to be a fully featured IDE Extension. The choice between a chat CLI with no editor and nice official plugin, but only working in some forked VS Code would gain a lot from some third option, so that the first two may both stay not picked

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages