Commit Graph

104 Commits

  • fix: insufficient quota message (#758)
    This pull request includes a change to improve the error message
    displayed when there is insufficient quota in the `AgentLoop` class. The
    updated message provides more detailed information and a link for
    managing or purchasing credits.
    
    Error message improvement:
    
    *
    [`codex-cli/src/utils/agent/agent-loop.ts`](diffhunk://#diff-b15957eac2720c3f1f55aa32f172cdd0ac6969caf4e7be87983df747a9f97083L1140-R1140):
    Updated the error message in the `AgentLoop` class to include the
    specific error message (if available) and a link to manage or purchase
    credits.
    
    
    Fixes #751
  • fix: remove unused _writableRoots arg to exec() function (#762)
    I suspect this was done originally so that `execForSandbox()` had a
    consistent signature for both the `SandboxType.NONE` and
    `SandboxType.MACOS_SEATBELT` cases, but that is not really necessary and
    turns out to make the upcoming Landlock support a bit more complicated
    to implement, so I had Codex remove it and clean up the call sites.
  • fix: read version from package.json instead of modifying session.ts (#753)
    I am working to simplify the build process. As a first step, update
    `session.ts` so it reads the `version` from `package.json` at runtime so
    we no longer have to modify it during the build process. I want to get
    to a place where the build looks like:
    
    ```
    cd codex-cli
    pnpm i
    pnpm build
    RELEASE_DIR=$(mktemp -d)
    cp -r bin "$RELEASE_DIR/bin"
    cp -r dist "$RELEASE_DIR/dist"
    cp -r src "$RELEASE_DIR/src" # important if we want sourcemaps to continue to work
    cp ../README.md "$RELEASE_DIR"
    VERSION=$(printf '0.1.%d' $(date +%y%m%d%H%M))
    jq --arg version "$VERSION" '.version = $version' package.json > "$RELEASE_DIR/package.json"
    ```
    
    Then the contents of `$RELEASE_DIR` should be good to `npm publish`, no?
  • Fixes issue #726 by adding config to configToSave object (#728)
    The saveConfig() function only includes a hardcoded subset of properties
    when writing the config file. Any property not explicitly listed (like
    disableResponseStorage) will be dropped.
    I have added `disableResponseStorage` to the `configToSave` object as
    the immediate fix.
    
    [Linking Issue this fixes.](https://github.com/openai/codex/issues/726)
  • feat: add --reasoning CLI flag (#314)
    This PR adds a new CLI flag: `--reasoning`, which allows users to
    customize the reasoning effort level (`low`, `medium`, or `high`) used
    by OpenAI's `o` models.
    By introducing the `--reasoning` flag, users gain more flexibility when
    working with the models. It enables optimization for either speed or
    depth of reasoning, depending on specific use cases.
    This PR resolves #107
    
    - **Flag**: `--reasoning`
    - **Accepted Values**: `low`, `medium`, `high`
    - **Default Behavior**: If not specified, the model uses the default
    reasoning level.
    
    ## Example Usage
    
    ```bash
    codex --reasoning=low "Write a simple function to calculate factorial"
    
    ---------
    
    Co-authored-by: Fouad Matin <169186268+fouad-openai@users.noreply.github.com>
    Co-authored-by: yashrwealthy <yash.rastogi@wealthy.in>
    Co-authored-by: Thibault Sottiaux <tibo@openai.com>
  • feat: lower default retry wait time and increase number of tries (#720)
    In total we now guarantee that we will wait for at least 60s before
    giving up.
    
    ---------
    
    Signed-off-by: Thibault Sottiaux <tibo@openai.com>
  • fix: tighten up check for /usr/bin/sandbox-exec (#710)
    * In both TypeScript and Rust, we now invoke `/usr/bin/sandbox-exec`
    explicitly rather than whatever `sandbox-exec` happens to be on the
    `PATH`.
    * Changed `isSandboxExecAvailable` to use `access()` rather than
    `command -v` so that:
      *  We only do the check once over the lifetime of the Codex process.
      * The check is specific to `/usr/bin/sandbox-exec`.
    * We now do a syscall rather than incur the overhead of spawning a
    process, dealing with timeouts, etc.
    
    I think there is still room for improvement here where we should move
    the `isSandboxExecAvailable` check earlier in the CLI, ideally right
    after we do arg parsing to verify that we can provide the Seatbelt
    sandbox if that is what the user has requested.
  • fix: check if sandbox-exec is available (#696)
    - Introduce `isSandboxExecAvailable()` helper and tidy import ordering
    in `handle-exec-command.ts`.
    - Add runtime check for the `sandbox-exec` binary on macOS; fall back to
    `SandboxType.NONE` with a warning if it’s missing, preventing crashes.
    
    ---------
    
    Signed-off-by: Thibault Sottiaux <tibo@openai.com>
    Co-authored-by: Fouad Matin <fouad@openai.com>
  • feat: user config api key (#569)
    Adds support for reading OPENAI_API_KEY (and other variables) from a
    user‑wide dotenv file (~/.codex.config). Precedence order is now:
      1. explicit environment variable
      2. project‑local .env (loaded earlier)
      3. ~/.codex.config
    
    Also adds a regression test that ensures the multiline editor correctly
    handles cases where printable text and the CSI‑u Shift+Enter sequence
    arrive in the same input chunk.
    
    House‑kept with Prettier; removed stray temp.json artifact.
  • fix: duplicate messages in quiet mode (#680)
    Addressing #600 and #664 (partially)
    
    ## Bug
    Codex was staging duplicate items in output running when the same
    response item appeared in both the streaming events. Specifically:
    
    1. Items would be staged once when received as a
    `response.output_item.done` event
    2. The same items would be staged again when included in the final
    `response.completed` payload
    
    This duplication would result in each message being sent several times
    in the quiet mode output.
    
    ## Changes
    - Added a Set (`alreadyStagedItemIds`) to track items that have already
    been staged
    - Modified the `stageItem` function to check if an item's ID is already
    in this set before staging it
    - Added a regression test (`agent-dedupe-items.test.ts`) that verifies
    items with the same ID are only staged once
    
    ## Testing
    Like other tests, the included test creates a mock OpenAI stream that
    emits the same message twice (once as an incremental event and once in
    the final response) and verifies the item is only passed to `onItem`
    once.
  • bump(version): 0.1.2504251709 (#660)
    ## `0.1.2504251709`
    
    ### 🚀 Features
    
    - Add openai model info configuration (#551)
    - Added provider to run quiet mode function (#571)
    - Create parent directories when creating new files (#552)
    - Print bug report URL in terminal instead of opening browser (#510)
    (#528)
    - Add support for custom provider configuration in the user config
    (#537)
    - Add support for OpenAI-Organization and OpenAI-Project headers (#626)
    - Add specific instructions for creating API keys in error msg (#581)
    - Enhance toCodePoints to prevent potential unicode 14 errors (#615)
    - More native keyboard navigation in multiline editor (#655)
    - Display error on selection of invalid model (#594)
    
    ### 🪲 Bug Fixes
    
    - Model selection (#643)
    - Nits in apply patch (#640)
    - Input keyboard shortcuts (#676)
    - `apply_patch` unicode characters (#625)
    - Don't clear turn input before retries (#611)
    - More loosely match context for apply_patch (#610)
    - Update bug report template - there is no --revision flag (#614)
    - Remove outdated copy of text input and external editor feature (#670)
    - Remove unreachable "disableResponseStorage" logic flow introduced in
    #543 (#573)
    - Non-openai mode - fix for gemini content: null, fix 429 to throw
    before stream (#563)
    - Only allow going up in history when not already in history if input is
    empty (#654)
    - Do not grant "node" user sudo access when using run_in_container.sh
    (#627)
    - Update scripts/build_container.sh to use pnpm instead of npm (#631)
    - Update lint-staged config to use pnpm --filter (#582)
    - Non-openai mode - don't default temp and top_p (#572)
    - Fix error catching when checking for updates (#597)
    - Close stdin when running an exec tool call (#636)
  • perf: optimize token streaming with balanced approach (#635)
    - Replace setTimeout(10ms) with queueMicrotask for immediate processing
    - Add minimal 3ms setTimeout for rendering to maintain readable UX
    - Reduces per-token delay while preserving streaming experience
    - Add performance test to verify optimization works correctly
    
    ---------
    
    Co-authored-by: Claude <noreply@anthropic.com>
    Co-authored-by: Thibault Sottiaux <tibo@openai.com>
  • feat: Add support for OpenAI-Organization and OpenAI-Project headers (#626)
    Added support for OpenAI-Organization and OpenAI-Project headers for
    OpenAI API calls.
    
    This is for #74
  • fix: nits in apply patch (#640)
    ## Description
    
    Fix a nit in `apply patch`, potentially improving performance slightly.
  • chore: upgrade prettier to v3 (#644)
    ## Description
    
    This PR addresses the following improvements:
    
    **Unify Prettier Version**: Currently, the Prettier version used in
    `/package.json` and `/codex-cli/package.json` are different. In this PR,
    we're updating both to use Prettier v3.
    
    - Prettier v3 introduces improved support for JavaScript and TypeScript.
    (e.g. the formatting scenario shown in the image below. This is more
    aligned with the TypeScript indentation standard).
    
    <img width="1126" alt="image"
    src="https://github.com/user-attachments/assets/6e237eb8-4553-4574-b336-ed9561c55370"
    />
    
    **Add Prettier Auto-Formatting in lint-staged**: We've added a step to
    automatically run prettier --write on JavaScript and TypeScript files as
    part of the lint-staged process, before the ESLint checks.
    
    - This will help ensure that all committed code is properly formatted
    according to the project's Prettier configuration.
  • fix(utils): save config (#578)
    ## Description
    
    When `saveConfig` is called, the project doc is incorrectly saved into
    user instructions. This change ensures that only user instructions are
    saved to `instructions.md` during saveConfig, preventing data
    corruption.
    
    close: #576
    
    ---------
    
    Co-authored-by: Thibault Sottiaux <tibo@openai.com>
  • feat(bug-report): print bug report URL in terminal instead of opening browser (#510) (#528)
    Solves #510 
    This PR changes the `/bug` command to print the URL into the terminal
    (so it works in headless sessions) instead of trying to open a browser.
    
    ---------
    
    Co-authored-by: Thibault Sottiaux <tibo@openai.com>
  • fix: apply_patch unicode characters (#625)
    fuzzy-er matching for apply_patch to handle u00A0 and u202F spaces.
  • fix(agent-loop): notify type (#608)
    ## Description
    
    The `as AppConfig` type assertion in the constructor may introduce
    potential type safety risks. Removing the assertion and making `notify`
    an optional parameter could enhance type robustness and prevent
    unexpected runtime errors.
    
    close: #605
  • feat: update README and config to support custom providers with API k… (#577)
    When using a non-built-in provider with the `--provider` option, users
    are prompted:
    
    ```
    Set the environment variable <provider>_API_KEY and re-run this command.
    You can create a <provider>_API_KEY in the <provider> dashboard.
    ```
    
    However, many users are confused because, even after correctly setting
    `<provider>_API_KEY`, authentication may still fail unless
    `OPENAI_API_KEY` is _also_ present in the environment. This is not
    intuitive and leads to ambiguity about which API key is actually
    required and used as a fallback, especially when using custom or
    third-party (non-listed) providers.
    
    Furthermore, the original README/documentation did not mention the
    requirement to set `<provider>_BASE_URL` for non-built-in providers,
    which is necessary for proper client behavior. This omission made the
    configuration process more difficult for users trying to integrate with
    custom endpoints.
  • feat: more loosely match context for apply_patch (#610)
    More of a proposal than anything but models seem to struggle with
    composing valid patches for `apply_patch` for context matching when
    there are unicode look-a-likes involved. This would normalize them.
    
    ```
    top-level          # ASCII
    top-level          # U+2011 NON-BREAKING HYPHEN
    top–level          # U+2013 EN DASH
    top—level          # U+2014 EM DASH
    top‒level          # U+2012 FIGURE DASH
    ```
    
    thanks unicode.
  • fix: don't clear turn input before retries (#611)
    The current turn input in the agent loop is being discarded before
    consuming the stream events which causes the stream reconnect (after
    rate limit failure) to not include the inputs. Since the new stream
    includes the previous response ID, it triggers a bad request exception
    considering the input doesn't match what OpenAI has stored on the server
    side and subsequently a very confusing error message of: `No tool output
    found for function call call_xyz`.
    
    This should fix https://github.com/openai/codex/issues/586.
    
    ## Testing
    
    I have a personal project that I'm working on that runs multiple Codex
    CLIs in parallel and often runs into rate limit errors (as seen in the
    OpenAI logs). After making this change, I am no longer experiencing
    Codex crashing and it was able to retry and handle everything gracefully
    until completion (even though I still see rate limiting in the OpenAI
    logs).
  • feat: add support for custom provider configuration in the user config (#537)
    ### What
    
    - Add support for loading and merging custom provider configurations
    from a local `providers.json` file.
    - Allow users to override or extend default providers with their own
    settings.
    
    ### Why
    
    This change enables users to flexibly customize and extend provider
    endpoints and API keys without modifying the codebase, making the CLI
    more adaptable for various LLM backends and enterprise use cases.
    
    ### How
    
    - Introduced `loadProvidersFromFile` and `getMergedProviders` in config
    logic.
    - Added/updated related tests in [tests/config.test.tsx]
    
    
    ### Checklist
    
    - [x] Lint passes for changed files
    - [x] Tests pass for all files
    - [x] Documentation/comments updated as needed
    
    ---------
    
    Co-authored-by: Thibault Sottiaux <tibo@openai.com>
  • bug: non-openai mode - don't default temp and top_p (#572)
    I haven't seen any actual errors due to this, but it's been bothering me
    that I had it defaulted to 1. I think best to leave it undefined and
    have each provider do their thing
  • bug: non-openai mode - fix for gemini content: null, fix 429 to throw before stream (#563)
    Gemini's API is finicky, it 400's without an error when you pass
    content: null
    Also fixed the rate limiting issues by throwing outside of the iterator.
    I think there's a separate issue with the second isRateLimit check in
    agent-loop - turnInput is cleared by that time, so it retries without
    the last message.
  • feat: create parent directories when creating new files. (#552)
    apply_patch doesn't create parent directories when creating a new file
    leading to confusion and flailing by the agent. This will create parent
    directories automatically when absent.
    
    ---------
    
    Co-authored-by: Thibault Sottiaux <tibo@openai.com>
  • bump(version): 0.1.2504221401 (#559)
    ## `0.1.2504221401`
    
    ### 🚀 Features
    
    - Show actionable errors when api keys are missing (#523)
    - Add CLI `--version` flag (#492)
    
    ### 🐛 Bug Fixes
    
    - Agent loop for ZDR (`disableResponseStorage`) (#543)
    - Fix relative `workdir` check for `apply_patch` (#556)
    - Minimal mid-stream #429 retry loop using existing back-off (#506)
    - Inconsistent usage of base URL and API key (#507)
    - Remove requirement for api key for ollama (#546)
    - Support `[provider]_BASE_URL` (#542)
  • when a shell tool call invokes apply_patch, resolve relative paths against workdir, if specified (#556)
    Previously, we were ignoring the `workdir` field in an `ExecInput` when
    running it through `canAutoApprove()`. For ordinary `exec()` calls, that
    was sufficient, but for `apply_patch`, we need the `workdir` to resolve
    relative paths in the `apply_patch` argument so that we can check them
    in `isPathConstrainedTowritablePaths()`.
    
    Likewise, we also need the workdir when running `execApplyPatch()`
    because the paths need to be resolved again.
    
    Ideally, the `ApplyPatchCommand` returned by `canAutoApprove()` would
    not be a simple `patch: string`, but the parsed patch with all of the
    paths resolved, in which case `execApplyPatch()` could expect absolute
    paths and would not need `workdir`.
  • fix: agent loop for disable response storage (#543)
    - Fixes post-merge of #506
    
    ---------
    
    Co-authored-by: Ilan Bigio <ilan@openai.com>
  • fix: support [provider]_BASE_URL (#542)
    Resolved issue where an OLLAMA_BASE_URL was not properly handled
    (openai/codex#516).
  • agent-loop: minimal mid-stream #429 retry loop using existing back-off (#506)
    As requested by @tibo-openai at
    https://github.com/openai/codex/pull/357#issuecomment-2816554203, this
    attempts a more minimal implementation of #357 that preserves as much as
    possible of the existing code's exponential backoff logic.
    
    Adds a small retry wrapper around the streaming for‑await loop so that
    HTTP 429s which occur *after* the stream has started no longer crash the
    CLI.
    
    Highlights
    • Re‑uses existing RATE_LIMIT_RETRY_WAIT_MS constant and 5‑attempt
    limit.
    • Exponential back‑off identical to initial request handling. 
    
    This comment is probably more useful here in the PR:
    // The OpenAI SDK may raise a 429 (rate‑limit) *after* the stream has
    // started. Prior logic already retries the initial `responses.create`
            // call, but we need to add equivalent resilience for mid‑stream
            // failures.  We keep the implementation minimal by wrapping the
    // existing `for‑await` loop in a small retry‑for‑loop that re‑creates
            // the stream with exponential back‑off.
  • fix: inconsistent usage of base URL and API key (#507)
    A recent commit introduced the ability to use third-party model
    providers. (Really appreciate it!)
    
    However, the usage is inconsistent: some pieces of code use the custom
    providers, whereas others still have the old behavior. Additionally,
    `OPENAI_BASE_URL` is now being disregarded when it shouldn't be.
    
    This PR normalizes the usage to `getApiKey` and `getBaseUrl`, and
    enables the use of `OPENAI_BASE_URL` if present.
    
    ---------
    
    Co-authored-by: Gabriel Bianconi <GabrielBianconi@users.noreply.github.com>
  • fix(agent-loop): update required properties to include workdir and ti… (#530)
    Without this I get an issue running codex it in a docker container. I
    receive:
    
    ```
    {
        "answer": "{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"\\\"Say hello world\\\"\"}],\"type\":\"message\"}\n{\"id\":\"error-1745325184914\",\"type\":\"message\",\"role\":\"system\",\"content\":[{\"type\":\"input_text\",\"text\":\"⚠️  OpenAI rejected the request (request ID: req_f9027b59ebbce00061e9cd2dbb2d529a). Error details: Status: 400, Code: invalid_function_parameters, Type: invalid_request_error, Message: 400 Invalid schema for function 'shell': In context=(), 'required' is required to be supplied and to be an array including every key in properties. Missing 'workdir'.. Please verify your settings and try again.\"}]}\n"
    }
    ```
    
    This fix makes it work.
  • bump(version): 0.1.2504220136 (#518)
    ## `0.1.2504220136`
    
    ### 🚀 Features
    
    - Add support for ZDR orgs (#481)
    - Include fractional portion of chunk that exceeds stdout/stderr limit
    (#497)
  • feat: add support for ZDR orgs (#481)
    - Add `store: boolean` to `AgentLoop` to enable client-side storage of
    response items
    - Add `--disable-response-storage` arg + `disableResponseStorage` config
  • include fractional portion of chunk that exceeds stdout/stderr limit (#497)
    I saw cases where the first chunk of output from `ls -R` could be large
    enough to exceed `MAX_OUTPUT_BYTES` or `MAX_OUTPUT_LINES`, in which case
    the loop would exit early in `createTruncatingCollector()` such that
    nothing was appended to the `chunks` array. As a result, the reported
    `stdout` of `ls -R` would be empty.
    
    I asked Codex to add logic to handle this edge case and write a unit
    test. I used this as my test:
    
    ```
    ./codex-cli/dist/cli.js -q 'what is the output of `ls -R`'
    ```
    
    now it appears to include a ton of stuff whereas before this change, I
    saw:
    
    ```
    {"type":"function_call_output","call_id":"call_a2QhVt7HRJYKjb3dIc8w1aBB","output":"{\"output\":\"\\n\\n[Output truncated: too many lines or bytes]\",\"metadata\":{\"exit_code\":0,\"duration_seconds\":0.5}}"}
    ```
  • bump(version): 0.1.2504211509 (#493)
    ## `0.1.2504211509`
    
    ### 🚀 Features
    
    - Support multiple providers via Responses-Completion transformation
    (#247)
    - Add user-defined safe commands configuration and approval logic #380
    (#386)
    - Allow switching approval modes when prompted to approve an
    edit/command (#400)
    - Add support for `/diff` command autocomplete in TerminalChatInput
    (#431)
    - Auto-open model selector if user selects deprecated model (#427)
    - Read approvalMode from config file (#298)
    - `/diff` command to view git diff (#426)
    - Tab completions for file paths (#279)
    - Add /command autocomplete (#317)
    - Allow multi-line input (#438)
    
    ### 🐛 Bug Fixes
    
    - `full-auto` support in quiet mode (#374)
    - Enable shell option for child process execution (#391)
    - Configure husky and lint-staged for pnpm monorepo (#384)
    - Command pipe execution by improving shell detection (#437)
    - Name of the file not matching the name of the component (#354)
    - Allow proper exit from new Switch approval mode dialog (#453)
    - Ensure /clear resets context and exclude system messages from
    approximateTokenUsed count (#443)
    - `/clear` now clears terminal screen and resets context left indicator
    (#425)
    - Correct fish completion function name in CLI script (#485)
    - Auto-open model-selector when model is not found (#448)
    - Remove unnecessary isLoggingEnabled() checks (#420)
    - Improve test reliability for `raw-exec` (#434)
    - Unintended tear down of agent loop (#483)
    - Remove extraneous type casts (#462)
  • revert #386 due to unsafe shell command parsing (#478)
    Reverts https://github.com/openai/codex/pull/386 because:
    
    * The parsing logic for shell commands was unsafe (`split(/\s+/)`
    instead of something like `shell-quote`)
    * We have a different plan for supporting auto-approved commands.
  • fix: /clear now clears terminal screen and resets context left indicator (#425)
    ## What does this PR do?
    * Implements the full `/clear` command in **codex‑cli**:
      * Resets chat history **and** wipes the terminal screen.
      * Shows a single system message: `Context cleared`.
    * Adds comprehensive unit tests for the new behaviour.
    
    ## Why is it needed?
    * Fixes user‑reported bugs:  
      * **#395**  
      * **#405**
    
    ## How is it implemented?
    * **Code** – Adds `process.stdout.write('\x1b[3J\x1b[H\x1b[2J')` in
    `terminal.tsx`. Removed reference to `prev` in `
            setItems((prev) => [
              ...prev,
    ` in `terminal-chat-new-input.tsx` & `terminal-chat-input.tsx`.
    
    ## CI / QA
    All commands pass locally:
    ```bash
    pnpm test      # green
    pnpm run lint  # green
    pnpm run typecheck  # zero TS errors
    ```
    
    ## Results
    
    
    
    https://github.com/user-attachments/assets/11dcf05c-e054-495a-8ecb-ac6ef21a9da4
    
    ---------
    
    Co-authored-by: Thibault Sottiaux <tibo@openai.com>
  • chore: improve storage/ implementation; use log(...) consistently (#473)
    This PR tidies up primitives under storage/.
    
    **Noop changes:**
    
    * Promote logger implementation to top-level utility outside of agent/
    * Use logger within storage primitives
    * Cleanup doc strings and comments
    
    **Functional changes:**
    
    * Increase command history size to 10_000
    * Remove unnecessary debounce implementation and ensure a session ID is
    created only once per agent loop
    
    ---------
    
    Signed-off-by: Thibault Sottiaux <tibo@openai.com>
  • refactor(updates): fetch version from registry instead of npm CLI to support multiple managers (#446)
    ## Background  
    Addressing feedback from
    https://github.com/openai/codex/pull/333#discussion_r2050893224, this PR
    adds support for Bun alongside npm, pnpm while keeping the code simple.
    
    ## Summary  
    The update‑check flow is refactored to use a direct registry lookup
    (`fast-npm-meta` + `semver`) instead of shelling out to `npm outdated`,
    and adds a lightweight installer‑detection mechanism that:
    
    1. Checks if the invoked script lives under a known global‑bin directory
    (npm, pnpm, or bun)
    2. If not, falls back to local detection via `getUserAgent()` (the
    `package‑manager‑detector` library)
    
    ## What’s Changed  
    - **Registry‑based version check**  
    - Replace `execFile("npm", ["outdated"])` with `getLatestVersion()` and
    `semver.gt()`
    - **Multi‑manager support**  
    - New `renderUpdateCommand` handles update commands for `npm`, `pnpm`,
    and `bun`.
      - Detect global installer first via `detectInstallerByPath()`  
      - Fallback to local detection via `getUserAgent()`  
    - **Module cleanup**  
    - Extract `detectInstallerByPath` into
    `utils/package-manager-detector.ts`
    - Remove legacy `checkOutdated`, `getNPMCommandPath`, and child‑process
    JSON parsing
    - **Flow improvements in `checkForUpdates`**  
      1. Short‑circuit by `UPDATE_CHECK_FREQUENCY`  
      3. Fetch & compare versions  
      4. Persist new timestamp immediately  
      5. Render & display styled box only when an update exists  
    - **Maintain simplicity**
    - All multi‑manager logic lives in one small helper and a concise lookup
    rather than a complex adapter hierarchy
    - Core `checkForUpdates` remains a single, easy‑to‑follow async function
    - **Dependencies added**  
    - `fast-npm-meta`, `semver`, `package-manager-detector`, `@types/semver`
    
    ## Considerations
    If we decide to drop the interactive update‑message (`npm install -g
    @openai/codex`) rendering altogether, we could remove most of the
    installer‑detection code and dependencies, which would simplify the
    codebase further but result in a less friendly UX.
    
    ## Preview
    
    * npm
    
    ![refactor-update-check-flow-npm](https://github.com/user-attachments/assets/57320114-3fb6-4985-8780-3388a1d1ec85)
    
    * bun
    
    ![refactor-update-check-flow-bun](https://github.com/user-attachments/assets/d93bf0ae-a687-412a-ab92-581b4f967307)
    
    ## Simple Flow Chart
    
    ```mermaid
    flowchart TD
      A(Start) --> B[Read state]
      B --> C{Recent check?}
      C -- Yes --> Z[End]
      C -- No --> D[Fetch latest version]
      D --> E[Save check time]
      E --> F{Version data OK?}
      F -- No --> Z
      F -- Yes --> G{Update available?}
      G -- No --> Z
      G -- Yes --> H{Global install?}
      H -- Yes --> I[Select global manager]
      H -- No --> K{Local install?}
      K -- No --> Z
      K -- Yes --> L[Select local manager]
      I & L --> M[Render update message]
      M --> N[Format with boxen]
      N --> O[Print update]
      O --> Z
    ```
  • feat: tab completions for file paths (#279)
    Made a PR as was requested in the #113
  • fix: command pipe execution by improving shell detection (#437)
    ## Description
    This PR fixes Issue #421 where commands with pipes (e.g., `grep -R ...
    -n | head -n 20`) were failing to execute properly after PR #391 was
    merged.
    
    ## Changes
    - Modified the `requiresShell` function to only enable shell mode when
    the command is a single string containing shell operators
    - Added logic to handle the case where shell operators are passed as
    separate arguments
    - Added comprehensive tests to verify the fix
    
    ## Root Cause
    The issue was that the `requiresShell` function was detecting shell
    operators like `|` even when they were passed as separate arguments,
    which caused the command to be executed with `shell: true`
    unnecessarily. This was causing syntax errors when running commands with
    pipes.
    
    ## Testing
    - Added unit tests to verify the fix
    - Manually tested with real commands using pipes
    - Ensured all existing tests pass
    
    Fixes #421