How Workspace Root Resolution and Project Detection Work in Codex Plugin CC

TLDR: The Codex Plugin CC resolves the workspace root by first executing git rev-parse --show-toplevel via ensureGitRepository, falling back to the current working directory when no repository exists, then canonicalizing the path with fs.realpathSync.native to ensure stable project detection and state storage.

The openai/codex-plugin-cc repository implements a Git-centric strategy for identifying project boundaries on disk. This mechanism ensures that every script—from hook handlers to state managers—operates within a deterministic directory scope. The resolution logic lives primarily in plugins/codex/scripts/lib/workspace.mjs and gracefully degrades when version control is unavailable.

Git-Based Workspace Root Resolution

The primary detection mechanism relies on Git metadata to establish an authoritative project root. When the plugin initializes, it invokes the resolution pipeline to determine where the current project lives.

Locating the Repository Top-Level

The ensureGitRepository function defined in plugins/codex/scripts/lib/git.mjs executes the following shell command:

git rev-parse --show-toplevel

This returns the absolute path to the top-level directory of the current Git repository. When successful, this path becomes the canonical workspace root for all subsequent file operations and state management.

The resolveWorkspaceRoot Fallback

If the Git command fails—indicating execution outside a repository—the resolveWorkspaceRoot function catches the error and returns the original cwd parameter. This implementation in plugins/codex/scripts/lib/workspace.mjs ensures the plugin never fails due to missing Git configuration:

export function resolveWorkspaceRoot(cwd) {
  try {
    return ensureGitRepository(cwd);
  } catch {
    return cwd;
  }
}

This fallback provides a deterministic root even for non-Git projects, allowing the plugin to function in any directory context.

Canonicalization and Path Stability

Many callers immediately normalize the resolved root to resolve symlinks and obtain a stable identifier for the workspace. As implemented in plugins/codex/scripts/lib/state.mjs, the code uses fs.realpathSync.native for this purpose:

const workspaceRoot = resolveWorkspaceRoot(cwd);
let canonicalWorkspaceRoot = workspaceRoot;
try {
  canonicalWorkspaceRoot = fs.realpathSync.native(workspaceRoot);
} catch {
  // If realpath fails (e.g., permission issues) we keep the original path.
}

This canonicalization prevents duplicate state entries when the same directory is accessed through different symbolic link paths.

Project Detection and State Management

The presence of a Git repository serves as the primary signal for project detection, triggering additional metadata inference and isolated state storage.

Detecting Default Branch Metadata

When a repository is detected, the plugin infers repository metadata via detectDefaultBranch, also located in plugins/codex/scripts/lib/git.mjs. This function identifies the default branch (e.g., main or master) to contextualize operations within the correct branch context.

Per-Project State Storage

The resolved workspace root scopes all persistent data. State management modules—including state.mjs, tracked-jobs.mjs, and job-control.mjs—store per-project data in a hidden .codex directory located at the workspace root. This ensures that review states, job logs, and metadata remain isolated to their respective repositories.

Integration Across the Plugin

Multiple subsystems rely on resolveWorkspaceRoot to maintain path consistency:

  • Hook scripts such as stop-review-gate-hook.mjs and session-lifecycle-hook.mjs use the root to locate configuration files
  • Runtime helpers including collectReviewContext and resolveStateDir store review state in workspace-scoped locations
  • Job tracking modules store execution logs under the workspace root via tracked-jobs.mjs

This centralized resolution prevents path mismatches and ensures state files are correctly scoped per repository.

Practical Implementation Examples

To resolve the workspace root in your own scripts:

import { resolveWorkspaceRoot } from "./lib/workspace.mjs";

// Inside a Git repo
const cwd = process.cwd();               // e.g. /home/user/project/src
const root = resolveWorkspaceRoot(cwd);  // → /home/user/project
console.log(root);

For non-Git directories, the function returns the input unchanged:

// Outside a Git repo
process.chdir("/tmp");
const fallbackRoot = resolveWorkspaceRoot(process.cwd());
// fallbackRoot is "/tmp"

To store state files relative to the detected root:

import { resolveStateFile } from "./lib/state.mjs";

const workspaceRoot = resolveWorkspaceRoot(process.cwd());
const statePath = resolveStateFile(workspaceRoot);
// Points to <workspaceRoot>/.codex/state.json

To retrieve repository metadata:

import { detectDefaultBranch } from "./lib/git.mjs";

const cwd = resolveWorkspaceRoot(process.cwd());
const defaultBranch = detectDefaultBranch(cwd);
console.log(`Default branch is ${defaultBranch}`);

Summary

  • The plugin uses git rev-parse --show-toplevel via ensureGitRepository in plugins/codex/scripts/lib/git.mjs to detect Git repositories
  • resolveWorkspaceRoot in plugins/codex/scripts/lib/workspace.mjs provides fallback to the current working directory when Git detection fails
  • Paths are canonicalized using fs.realpathSync.native to resolve symlinks and ensure stable identifiers across the codebase
  • Project state is stored in a .codex directory at the workspace root, scoped by state.mjs, job-control.mjs, and tracked-jobs.mjs
  • Repository metadata such as the default branch is detected via detectDefaultBranch for additional project context

Frequently Asked Questions

What happens if I run Codex Plugin CC outside a Git repository?

The resolveWorkspaceRoot function catches the Git detection error and returns the current working directory as the workspace root. This allows the plugin to function in non-Git projects, though without repository-specific metadata like default branch detection.

How does the plugin handle symlinked directories?

After resolving the workspace root, many callers use fs.realpathSync.native to canonicalize the path. This resolves symlinks to their physical location, ensuring that state files are stored consistently regardless of which symlink path was used to access the directory.

Where does the plugin store project-specific state data?

State is stored in a hidden .codex directory located at the resolved workspace root. Modules like state.mjs, job-control.mjs, and tracked-jobs.mjs use this location to isolate project data, creating per-project storage that moves with the repository.

Which source files contain the core workspace resolution logic?

The main entry point is plugins/codex/scripts/lib/workspace.mjs, which exports resolveWorkspaceRoot. Git-specific detection lives in plugins/codex/scripts/lib/git.mjs with ensureGitRepository and detectDefaultBranch. State management implementation using these roots appears in plugins/codex/scripts/lib/state.mjs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →