Skip to content

feat(installer): support project-local Codex installs - #1551

Open
maxmilian wants to merge 1 commit into
colbymchenry:mainfrom
maxmilian:fix/1531-codex-project-local
Open

feat(installer): support project-local Codex installs#1551
maxmilian wants to merge 1 commit into
colbymchenry:mainfrom
maxmilian:fix/1531-codex-project-local

Conversation

@maxmilian

Copy link
Copy Markdown
Contributor

Closes #1531.

What this changes

CodexTarget was the one target that forced a machine-wide install: supportsLocation('local') returned false, so picking --location=local skipped Codex with a note saying it has no project-local config. This makes both locations work.

config instructions
--location=global (unchanged) ~/.codex/config.toml ~/.codex/AGENTS.md
--location=local (new) <cwd>/.codex/config.toml <cwd>/AGENTS.md

Why the old assumption was wrong

The file carried this comment:

Codex CLI as of 2026-05 has no project-local config concept — everything lives under ~/.codex/.

Checked against openai/codex rather than assumed:

  1. .codex/config.toml is a first-class layer. codex-rs/config/src/loader/README.md documents the stack, precedence top-over-bottom:

    3. SessionFlags (CLI overrides)
    4. Project config (.codex/config.toml)
    5. User profile config, when present
    6. User config (config.toml)
    

    loader/mod.rs lists three discovery routes for it — ${PWD}/config.toml, a walk up parent directories for ./.codex/config.toml, and $(git rev-parse --show-toplevel)/.codex/config.toml — implemented in load_project_layers() / discover_project_layers().

  2. mcp_servers is honored from that layer. The project layer is sanitized against a denylist (PROJECT_LOCAL_CONFIG_DENYLIST in loader/mod.rs) covering settings repo contents shouldn't get to choose — base URLs, model_provider/model_providers, notify, profile/profiles, otel. mcp_servers is not on it, so a project-scoped [mcp_servers.codegraph] survives into the merged config that core/src/mcp.rs (runtime_config(&Config)configured_mcp_servers / effective_mcp_servers) consumes.

  3. It is not a recent addition. In-repo .codex/config.toml landed in feat: support in-repo .codex/config.toml entries as sources of config info openai/codex#8354 on 2025-12-22 — before the comment was written. So this is a correction, not a catch-up: the commit message and the new file comment both say "was never accurate" rather than "has changed."

The one caveat, surfaced instead of hidden

All three project routes are documented as "loaded but disabled when untrusted." A local install can therefore be written perfectly and still do nothing until the user trusts the project. Rather than report a silent success, install('local', …) returns a note:

Codex applies <cwd>/.codex/config.toml only in a project marked trusted — otherwise the layer is loaded but disabled. Trust this project in Codex to activate it.

Why AGENTS.md sits at the project root, not under .codex/

That's the file Codex reads for repo instructions, and it matches the local layout already used by the two other dual-location targets: opencode writes <cwd>/AGENTS.md, and gemini writes <cwd>/.gemini/settings.json plus a project-root <cwd>/GEMINI.md. Same asymmetry, same reasoning.

What I deliberately did not do

The issue suggested optional version-gating (supportsLocation('local') true only for Codex builds that ship the project layer, falling back to global otherwise). I left it out: the layer has existed since 2025-12, and an older Codex simply doesn't read the extra file — nothing breaks. Gating would mean shelling out to codex --version and parsing it, i.e. error handling for a case that no longer occurs. Happy to add it if you'd rather have the belt and braces.

Tests

Two new cases in __tests__/installer-targets.test.ts, both red against the previous implementation (expected false to be true) and green with this change:

  • codex: local install writes ./.codex/config.toml and the project-root ./AGENTS.md block (#1531) — asserts the write layout, [mcp_servers.codegraph] in the local TOML, the CodeGraph block in the project-root AGENTS.md, the trust note, and that ~/.codex/config.toml is not created.
  • codex: local uninstall reverses the local install and leaves the global entry alone (#1531) — installs both locations, uninstalls local, asserts the global entry survives.

No existing assertions were changed. The generic per-target contract suite (install / idempotent re-install / sibling preservation / uninstall / non-writing printConfig) now picks up codex at location=local automatically, because it iterates supportsLocation.

npm run build && npm test
Test Files  163 passed | 15 skipped (178)
     Tests  2908 passed | 178 skipped (3086)

Also updated

Two doc comments that used Codex as the example of a global-only target (targets/types.ts, installer/index.ts) now name the Copilot CLI instead, so the stale claim doesn't survive in prose. Plus a user-facing CHANGELOG.md entry under [Unreleased] → New Features, per the src/installer/ rule in CLAUDE.md.

)

Codex CLI has a first-class project config layer — `.codex/config.toml`
is layer 4 of the loader stack, above the user config at layer 6
(`codex-rs/config/src/loader/README.md` in openai/codex), and it landed
in openai/codex#8354 on 2025-12-22. The CodexTarget's "Codex has no
project-local config concept" note was therefore never accurate, and
`supportsLocation('local') === false` made Codex the one agent that
forces a machine-wide MCP install.

`mcp_servers` is not on the project layer's denylist (which strips base
URLs, model providers, `notify`, profiles and otel — settings repo
contents shouldn't choose), so a project-scoped `[mcp_servers.codegraph]`
is honored.

- Path helpers take a `Location`: global keeps `~/.codex/config.toml` +
  `~/.codex/AGENTS.md`; local writes `<cwd>/.codex/config.toml` and the
  project-root `<cwd>/AGENTS.md` — the same split the gemini and
  opencode targets already use for their local layout.
- Drops the five `loc !== 'global'` early returns from detect, install,
  uninstall, printConfig and describePaths.
- Local install returns a note that Codex only applies a project layer
  in a project marked trusted; untrusted projects load the layer but
  leave it disabled, so a silent success would be misleading.
- Refreshes the two doc comments that used Codex as the example of a
  global-only target (now the Copilot CLI).

Tests: two new cases covering the local write layout, the trust note,
global config staying untouched, and local uninstall leaving the global
entry intact. Both fail against the previous implementation. The generic
per-target contract suite now also exercises codex at location=local.

@codegraph-impact codegraph-impact Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeGraph review

Overall risk: 🟡 Low — Codex CLI now supports project-local install/uninstall; new tests directly cover file creation, reversal, and global-config isolation.

Verify in the running product (3 checks)

  • cli — In a fresh project directory, run codegraph install --location=local and select only Codex CLI. Confirm it creates ./.codex/config.toml with [mcp_servers.codegraph] and adds a CodeGraph block to the project's AGENTS.md, and that it prints a note that the config only activates once the project is trusted in Codex.
  • cli — After the local install above, run codegraph uninstall --location=local. Confirm the local .codex/config.toml entry and AGENTS.md block are removed, while a separately-run global install (~/.codex/config.toml) is left untouched.
  • cli — Run the interactive codegraph install and select only Codex CLI as the target. Confirm the 'All projects vs just this project' location prompt now appears.

Worth double-checking

  • Upstream Codex trust-gating claim
  • Installer contract idempotency/recoverable coverage for codex-local
  • CHANGELOG entry placement
What to look for in each
  • Upstream Codex trust-gating claim — The header comment in codex.ts asserts Codex's project config layer (openai/codex#8354), the denylist that excludes mcp_servers, and the 'disabled until trusted' behavior. This is unverifiable from this repo's source — confirm it against the actual Codex CLI version being targeted, since the whole local-install feature's correctness rests on it.
  • Installer contract idempotency/recoverable coverage for codex-local — The two new tests (#1531) explicitly assert reversibility and that global config stays untouched (sibling-preserving), but don't directly assert that a second local install is byte-identical to the first (idempotent) or that install recovers correctly from a partial prior local state — confirm the shared parameterized suite exercises codex+local for those two invariants too.
  • CHANGELOG entry placement — The diff hunk doesn't show the enclosing '### New Features' / '## [Unreleased]' headers directly — confirm the new Codex entry actually landed under those, per the changelog rule.
Business rules — 3 honored · 3 not applicable
Status Rule Note
✔ Honored Surfaces The change extends the documented installer install/uninstall CLI surface for Codex without altering or breaking any other listed surface.
✔ Honored Changelog and release contract The new CHANGELOG entry uses user-facing prose, real commands, and the agent name (Codex CLI), and no version number is pre-staged.
✔ Honored Installer contract Two new parameterized tests (#1531) directly cover local install creation, the trust note, local uninstall reversing install, and global config being left untouched.
— Not applicable Test selection (affected tests) src/bin/codegraph.ts (the affected-test-selection command) is only listed as a downstream dependent, not modified by this diff, so test-selection logic is unaffected.
— Not applicable Source strings must exclude interpolated template literals The src/installer/index.ts changes in this diff are comment-text edits only and do not touch extractSourceStrings or any template-literal indexing logic.
— Not applicable Runtime compatibility and releases This change doesn't alter runtime/Node version requirements, dependency versions, or the release/bundling process.
Full assessment

This PR extends the Codex CLI installer target to support --location=local (previously global-only), writing ./.codex/config.toml and a project-root AGENTS.md block, with an explicit note that Codex only applies project config in a trusted project. The AgentTarget interface and orchestrator comments are updated for consistency, and CHANGELOG.md gets a user-facing entry. Two new parameterized tests exercise local install and local uninstall end-to-end, matching the installer contract's reversible and sibling-preserving invariants.

Blast radius: 13 files affected beyond the diff · 157 symbols · 1 test file selected

Tests to run:

  • __tests__/installer-targets.test.ts

Full report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Codex CLI now supports project-local config (.codex/config.toml) — CodexTarget.supportsLocation('local') is stale

1 participant