Commit Graph

23 Commits

  • Make fuzzy file search case insensitive (#15772)
    Makes fuzzy file search use case-insensitive matching instead of
    smart-case in `codex-file-search`. I find smart-case to be a poor user
    experience -using the wrong case for a letter drops its match so
    significantly, it often drops off the results list, effectively making a
    search case-sensitive.
  • ci: verify codex-rs Cargo manifests inherit workspace settings (#16353)
    ## Why
    
    Bazel clippy now catches lints that `cargo clippy` can still miss when a
    crate under `codex-rs` forgets to opt into workspace lints. The concrete
    example here was `codex-rs/app-server/tests/common/Cargo.toml`: Bazel
    flagged a clippy violation in `models_cache.rs`, but Cargo did not
    because that crate inherited workspace package metadata without
    declaring `[lints] workspace = true`.
    
    We already mirror the workspace clippy deny list into Bazel after
    [#15955](https://github.com/openai/codex/pull/15955), so we also need a
    repo-side check that keeps every `codex-rs` manifest opted into the same
    workspace settings.
    
    ## What changed
    
    - add `.github/scripts/verify_cargo_workspace_manifests.py`, which
    parses every `codex-rs/**/Cargo.toml` with `tomllib` and verifies:
      - `version.workspace = true`
      - `edition.workspace = true`
      - `license.workspace = true`
      - `[lints] workspace = true`
    - top-level crate names follow the `codex-*` / `codex-utils-*`
    conventions, with explicit exceptions for `windows-sandbox-rs` and
    `utils/path-utils`
    - run that script in `.github/workflows/ci.yml`
    - update the current outlier manifests so the check is enforceable
    immediately
    - fix the newly exposed clippy violations in the affected crates
    (`app-server/tests/common`, `file-search`, `feedback`,
    `shell-escalation`, and `debug-client`)
    
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/16353).
    * #16351
    * __->__ #16353
  • chore: clean up argument-comment lint and roll out all-target CI on macOS (#16054)
    ## Why
    
    `argument-comment-lint` was green in CI even though the repo still had
    many uncommented literal arguments. The main gap was target coverage:
    the repo wrapper did not force Cargo to inspect test-only call sites, so
    examples like the `latest_session_lookup_params(true, ...)` tests in
    `codex-rs/tui_app_server/src/lib.rs` never entered the blocking CI path.
    
    This change cleans up the existing backlog, makes the default repo lint
    path cover all Cargo targets, and starts rolling that stricter CI
    enforcement out on the platform where it is currently validated.
    
    ## What changed
    
    - mechanically fixed existing `argument-comment-lint` violations across
    the `codex-rs` workspace, including tests, examples, and benches
    - updated `tools/argument-comment-lint/run-prebuilt-linter.sh` and
    `tools/argument-comment-lint/run.sh` so non-`--fix` runs default to
    `--all-targets` unless the caller explicitly narrows the target set
    - fixed both wrappers so forwarded cargo arguments after `--` are
    preserved with a single separator
    - documented the new default behavior in
    `tools/argument-comment-lint/README.md`
    - updated `rust-ci` so the macOS lint lane keeps the plain wrapper
    invocation and therefore enforces `--all-targets`, while Linux and
    Windows temporarily pass `-- --lib --bins`
    
    That temporary CI split keeps the stricter all-targets check where it is
    already cleaned up, while leaving room to finish the remaining Linux-
    and Windows-specific target-gated cleanup before enabling
    `--all-targets` on those runners. The Linux and Windows failures on the
    intermediate revision were caused by the wrapper forwarding bug, not by
    additional lint findings in those lanes.
    
    ## Validation
    
    - `bash -n tools/argument-comment-lint/run.sh`
    - `bash -n tools/argument-comment-lint/run-prebuilt-linter.sh`
    - shell-level wrapper forwarding check for `-- --lib --bins`
    - shell-level wrapper forwarding check for `-- --tests`
    - `just argument-comment-lint`
    - `cargo test` in `tools/argument-comment-lint`
    - `cargo test -p codex-terminal-detection`
    
    ## Follow-up
    
    - Clean up remaining Linux-only target-gated callsites, then switch the
    Linux lint lane back to the plain wrapper invocation.
    - Clean up remaining Windows-only target-gated callsites, then switch
    the Windows lint lane back to the plain wrapper invocation.
  • Simple directory mentions (#14970)
    - Adds simple support for directory mentions in the TUI.
    - Codex App/VS Code will require minor change to recognize a directory
    mention as such and change the link behavior.
    - Directory mentions have a trailing slash to differentiate from
    extensionless files
    
    
    <img width="972" height="382" alt="image"
    src="https://github.com/user-attachments/assets/8035b1eb-0978-465b-8d7a-4db2e5feca39"
    />
    <img width="978" height="228" alt="image"
    src="https://github.com/user-attachments/assets/af22cf0b-dd10-4440-9bee-a09915f6ba52"
    />
  • Apply argument comment lint across codex-rs (#14652)
    ## Why
    
    Once the repo-local lint exists, `codex-rs` needs to follow the
    checked-in convention and CI needs to keep it from drifting. This commit
    applies the fallback `/*param*/` style consistently across existing
    positional literal call sites without changing those APIs.
    
    The longer-term preference is still to avoid APIs that require comments
    by choosing clearer parameter types and call shapes. This PR is
    intentionally the mechanical follow-through for the places where the
    existing signatures stay in place.
    
    After rebasing onto newer `main`, the rollout also had to cover newly
    introduced `tui_app_server` call sites. That made it clear the first cut
    of the CI job was too expensive for the common path: it was spending
    almost as much time installing `cargo-dylint` and re-testing the lint
    crate as a representative test job spends running product tests. The CI
    update keeps the full workspace enforcement but trims that extra
    overhead from ordinary `codex-rs` PRs.
    
    ## What changed
    
    - keep a dedicated `argument_comment_lint` job in `rust-ci`
    - mechanically annotate remaining opaque positional literals across
    `codex-rs` with exact `/*param*/` comments, including the rebased
    `tui_app_server` call sites that now fall under the lint
    - keep the checked-in style aligned with the lint policy by using
    `/*param*/` and leaving string and char literals uncommented
    - cache `cargo-dylint`, `dylint-link`, and the relevant Cargo
    registry/git metadata in the lint job
    - split changed-path detection so the lint crate's own `cargo test` step
    runs only when `tools/argument-comment-lint/*` or `rust-ci.yml` changes
    - continue to run the repo wrapper over the `codex-rs` workspace, so
    product-code enforcement is unchanged
    
    Most of the code changes in this commit are intentionally mechanical
    comment rewrites or insertions driven by the lint itself.
    
    ## Verification
    
    - `./tools/argument-comment-lint/run.sh --workspace`
    - `cargo test -p codex-tui-app-server -p codex-tui`
    - parsed `.github/workflows/rust-ci.yml` locally with PyYAML
    
    ---
    
    * -> #14652
    * #14651
  • fix(core): scope file search gitignore to repository context (#13250)
    Closes #3493
    
    ## Problem
    
    When a user's home directory (or any ancestor) contains a broad
    `.gitignore` (e.g. `*` + `!.gitignore`), the `@` file mention picker in
    Codex silently hides valid repository files like `package.json`. The
    picker returns `no matches` for searches that should succeed. This is
    surprising because manually typed paths still work, making the failure
    hard to diagnose.
    
    ## Mental model
    
    Git itself never walks above the repository root to assemble its ignore
    list. Its `.gitignore` resolution is strictly scoped: it reads
    `.gitignore` files from the repo root downward, the per-repo
    `.git/info/exclude`, and the user's global excludes file (via
    `core.excludesFile`). A `.gitignore` sitting in a parent directory above
    the repo root has no effect on `git status`, `git ls-files`, or any
    other git operation. Our file search should replicate this contract
    exactly.
    
    The `ignore` crate's `WalkBuilder` has a `require_git` flag that
    controls whether it follows this contract:
    
    - `require_git(false)` (the previous setting): the walker reads
    `.gitignore` files from _all_ ancestor directories, even those above or
    outside the repository root. This is a deliberate divergence from git's
    behavior in the `ignore` crate, intended for non-git use cases. It means
    a `~/.gitignore` with `*` will suppress every file in the walk—something
    git itself would never do.
    
    - `require_git(true)` (this fix): the walker only applies `.gitignore`
    semantics when it detects a `.git` directory, scoping ignore resolution
    to the repository boundary. This matches git's own behavior: parent
    `.gitignore` files above the repo root have no effect.
    
    The fix is a one-line change: `require_git(false)` becomes
    `require_git(true)`.
    
    ## How `require_git(false)` got here
    
    The setting was introduced in af338cc (#2981, "Improve @ file search:
    include specific hidden dirs such as .github, .gitlab"). That PR's goal
    was to make hidden directories like `.github` and `.vscode` discoverable
    by setting `.hidden(false)` on the walker. The `require_git(false)` was
    added alongside it with the comment _"Don't require git to be present to
    apply git-related ignore rules"_—the author likely intended gitignore
    rules to still filter results even when no `.git` directory exists (e.g.
    searching an extracted tarball that has a `.gitignore` but no `.git`).
    
    The unintended consequence: with `require_git(false)`, the `ignore`
    crate walks _above_ the search root to find `.gitignore` files in
    ancestor directories. This is a side effect the original author almost
    certainly didn't anticipate. The PR message says "Preserve `.gitignore`
    semantics," but `require_git(false)` actually _breaks_ git's semantics
    by applying ancestor ignore files that git itself would never read.
    
    In short: the intent was "apply gitignore even without `.git`" but the
    effect was "apply gitignore from every ancestor directory." This fix
    restores git-correct scoping.
    
    ## Non-goals
    
    - This PR does not change behavior when `respect_gitignore` is `false`
    (that path already disables all git-related ignore rules).
    - The first test
    (`parent_gitignore_outside_repo_does_not_hide_repo_files`) intentionally
    omits `git init`. The `ignore` crate's `require_git(true)` causes it to
    skip gitignore processing entirely when no `.git` exists, which is the
    desired behavior for that scenario. A second test
    (`git_repo_still_respects_local_gitignore_when_enabled`) covers the
    complementary case with a real git repo.
    
    ## Tradeoffs
    
    **Behavioral shift**: With `require_git(true)`, directories that contain
    `.gitignore` files but are _not_ inside a git repository will no longer
    have those ignore rules applied during `@` search. This is a correctness
    improvement for the primary use case (searching inside repos), but
    changes behavior for the edge case of searching non-repo directories
    that happen to have `.gitignore` files. In practice, Codex is
    overwhelmingly used inside git repositories, so this tradeoff strongly
    favors the fix.
    
    **Two test strategies**: The first test omits `git init` to verify
    parent ignore leakage is blocked; the second runs `git init` to verify
    the repo's own `.gitignore` is still honored. Together they cover both
    sides of the `require_git(true)` contract.
    
    ## Architecture
    
    The change is in `walker_worker()` within
    `codex-rs/file-search/src/lib.rs`, which configures the
    `ignore::WalkBuilder` used by the file search walker thread. The walker
    feeds discovered file paths into `nucleo` for fuzzy matching. The
    `require_git` flag controls whether the walker consults `.gitignore`
    files at all—it sits upstream of all ignore processing.
    
    ```
    walker_worker
      └─ WalkBuilder::new(root)
           ├─ .hidden(false)         — include dotfiles
           ├─ .follow_links(true)    — follow symlinks
           ├─ .require_git(true)     — ← THE FIX: only apply gitignore in git repos
           └─ (conditional) git_ignore(false), git_global(false), etc.
                └─ applied when respect_gitignore == false
    ```
    
    ## Tests
    
    - `parent_gitignore_outside_repo_does_not_hide_repo_files`: creates a
    temp directory tree with a parent `.gitignore` containing `*`, a child
    "repo" directory with `package.json` and `.vscode/settings.json`, and
    asserts that both files are discoverable via `run()` with
    `respect_gitignore: true`.
    - `git_repo_still_respects_local_gitignore_when_enabled`: the
    complementary test—runs `git init` inside the child directory and
    verifies that the repo's own `.gitignore` exclusions still work (e.g.
    `.vscode/extensions.json` is excluded while `.vscode/settings.json` is
    whitelisted). Confirms that `require_git(true)` does not disable
    gitignore processing inside actual git repositories.
  • Fixed a flaky Windows test that is consistently causing a CI failure (#10987)
    Loop wait_for_complete/wait_for_updates_at_least until deadline to
    prevent Windows CI false timeouts in query-change session tests.
  • file-search: multi-root walk (#10240)
    Instead of a separate walker for each root in a multi-root walk, use a
    single walker.
  • file-search: improve file query perf (#9939)
    switch nucleo-matcher for nucleo and use a "file search session" w/ live
    updating query instead of a single hermetic run per query.
  • feat: move file name derivation into codex-file-search (#8334)
    ## Summary
    
      - centralize file name derivation in codex-file-search
      - reuse the helper in app-server fuzzy search to avoid duplicate logic
      - add unit tests for file_name_from_path
    
      ## Testing
    
      - cargo test -p codex-file-search
      - cargo test -p codex-app-server
  • Follow symlinks during file search (#4453)
    I have read the CLA Document and I hereby sign the CLA
    
    Closes #4452
    
    This fixes a usability issue where users with symlinked folders in their
    working directory couldn't search those files using the `@` file search
    feature.
    
    ## Rationale
    
    The "bug" was in the file search implementation in
    `codex-rs/file-search/src/lib.rs`. The `WalkBuilder` was using default
    settings which don't follow symlinks, causing two related issues:
    
    1. Partial search results: The `@` search would find symlinked
    directories but couldn't find files inside them
    2. Inconsistent behavior: Users expect symlinked folders to behave like
    regular folders in search results.
    
    ## Root cause
    
    The `ignore` crate's `WalkBuilder` defaults to `.follow_links(false)`
    [[source](https://github.com/BurntSushi/ripgrep/blob/9802945e6342ec284633924cb7d8d3ce67204995/crates/ignore/src/walk.rs#L532)],
    so when traversing the file system, it would:
    
    - Detect symlinked directories as directory entries
    - But not traverse into them to index their contents
    - The `get_file_path` function would then filter out actual directories,
    leaving only the symlinked folder itself as a result
    
    Fix: Added `.follow_links(true)` to the `WalkBuilder` configuration,
    making the file search follow symlinks and index their contents just
    like regular directories.
    
    This change maintains backward compatibility since symlink following is
    generally expected behavior for file search tools, and it aligns with
    how users expect the `@` search feature to work.
    
    Co-authored-by: Eric Traut <etraut@openai.com>
  • [App Server] Allow fetching or resuming a conversation summary from the conversation id (#5890)
    This PR adds an option to app server to allow conversation summaries to
    be fetched from just the conversation id rather than rollout path for
    convenience at the cost of some latency to discover the rollout path.
    
    This convenience is non-trivial as it allows app servers to simply
    maintain conversation ids rather than rollout paths and the associated
    platform (Windows) handling associated with storing and encoding them
    correctly.
  • fix: resume lookup for gitignored CODEX_HOME (#5311)
    Walk the sessions tree instead of using file_search so gitignored
    CODEX_HOME directories can resume sessions. Add a regression test that
    covers a .gitignore'd sessions directory.
    
    Fixes #5247
    Fixes #5412
    
    ---------
    
    Co-authored-by: Owen Lin <owen@openai.com>
  • [mcp-server] Expose fuzzy file search in MCP (#2677)
    ## Summary
    Expose a simple fuzzy file search implementation for mcp clients to work
    with
    
    ## Testing
    - [x] Tested locally
  • Improve @ file search: include specific hidden dirs such as .github, .gitlab (#2981)
    # Improve @ file search: include specific hidden dirs
    
    This should close #2980
    
    ## What
    - Extend `@` fuzzy file search to include select top-level hidden
    directories:
    `.github`, `.gitlab`, `.circleci`, `.devcontainer`, `.azuredevops`,
    `.vscode`, `.cursor`.
    - Keep all other hidden directories excluded to avoid noise and heavy
    traversals.
    
    ## Why
    - Common project config lives under these dot-dirs (CI, editor,
    devcontainer); users expect `@.github/...` and similar paths to resolve.
    - Prior behavior hid all dot-dirs, making these files undiscoverable.
    
    ## How
    - In `codex-file-search` walker:
      - Enable hidden entries via `WalkBuilder.hidden(false)`.
    - Add `filter_entry` to only allow those specific root dot-directories;
    other hidden paths remain filtered out.
      - Preserve `.gitignore` semantics and existing exclude handling.
    
    ## Local checks
    - Ran formatting: `just fmt`
    - Ran lint (scoped): `just fix -p codex-file-search`
    - Ran tests:
      - `cargo test -p codex-file-search`
      - `cargo test -p codex-tui`
    
    ## Readiness
    - Branch is up-to-date locally; tests pass; lint/format applied.
    - No merge conflicts expected.
    - Marking Ready for review.
    
    ---------
    
    Signed-off-by: lionelchg <lionel.cheng@hotmail.fr>
  • chore: upgrade to Rust 1.89 (#2465)
    Codex created this PR from the following prompt:
    
    > upgrade this entire repo to Rust 1.89. Note that this requires
    updating codex-rs/rust-toolchain.toml as well as the workflows in
    .github/. Make sure that things are "clippy clean" as this change will
    likely uncover new Clippy errors. `just fmt` and `cargo clippy --tests`
    are sufficient to check for correctness
    
    Note this modifies a lot of lines because it folds nested `if`
    statements using `&&`.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/2465).
    * #2467
    * __->__ #2465
  • chore(rs): update dependencies (#1494)
    ### Chores
    - Update cargo dependencies
    - Remove unused cargo dependencies
    - Fix clippy warnings
    - Update Dockerfile (package.json requires node 22)
    - Let Dependabot update bun, cargo, devcontainers, docker,
    github-actions, npm (nix still not supported)
    
    ### TODO
    - Upgrade dependencies with breaking changes
    
    ```shell
    $ cargo update --verbose
       Unchanged crossterm v0.28.1 (available: v0.29.0)
       Unchanged schemars v0.8.22 (available: v1.0.4)
    ```
  • feat: introduce --compute-indices flag to codex-file-search (#1419)
    This is a small quality-of-life feature, the addition of
    `--compute-indices` to the CLI, which, if enabled, will compute and set
    the `indices` field for each `FileMatch` returned by `run()`. Note we
    only bother to compute `indices` once we have the top N results because
    there could be a lot of intermediate "top N" results during the search
    that are ultimately discarded.
    
    When set, the indices are included in the JSON output when `--json` is
    specified and the matching indices are displayed in bold when `--json`
    is not specified.
  • feat: make file search cancellable (#1414)
    Update `run()` to take `cancel_flag: Arc<AtomicBool>` that the worker
    threads will periodically check to see if it is `true`, exiting early
    (and returning empty results) if so.
  • chore: change arg from PathBuf to &Path (#1409)
    Caller no longer needs to clone a `PathBuf`: can just pass `&Path`.
  • fix: add tiebreaker logic for paths when scores are equal (#1400)
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/1400).
    * #1401
    * __->__ #1400
  • feat: standalone file search CLI (#1386)
    Standalone fuzzy filename search library that should be helpful in
    addressing https://github.com/openai/codex/issues/1261.