How Workspace Root Resolution Works in Codex-Companion: Git-Aware Detection and Fallback Logic

Workspace root resolution in Codex-Companion uses Git-aware detection to walk up the directory tree until it finds a .git folder, falling back to the current working directory if no repository is found.

The openai/codex-plugin-cc repository implements deterministic workspace root resolution to ensure consistent state management across subdirectories. This mechanism anchors all plugin operations to a stable base directory, whether inside a Git repository or a standalone folder. The resolution logic lives in plugins/codex/scripts/lib/workspace.mjs and depends on Git repository detection to establish canonical project boundaries.

The Entry Point: resolveWorkspaceRoot in workspace.mjs

The resolveWorkspaceRoot function serves as the primary entry point for determining the workspace base directory. Located in plugins/codex/scripts/lib/workspace.mjs, this function implements a try-catch wrapper around Git detection logic that balances reliability with graceful degradation.

export function resolveWorkspaceRoot(cwd) {
  try {
    return ensureGitRepository(cwd);
  } catch {
    // Not a Git repo – fall back to the directory that was passed in
    return cwd;
  }
}

Git-Aware Resolution via ensureGitRepository

The actual directory traversal happens in ensureGitRepository, implemented in plugins/codex/scripts/lib/git.mjs. This function walks up the file tree from the provided cwd parameter until it locates a .git directory. When found, the containing directory is returned as the canonical workspace root. If the walk reaches the filesystem root without finding .git, it throws an error that triggers the fallback mechanism.

Fallback Behavior When Outside Git Repositories

When ensureGitRepository throws—indicating the current path is not inside a Git repository—resolveWorkspaceRoot catches the exception and returns the original cwd argument. This ensures the plugin degrades gracefully in standalone directories while maintaining predictable behavior.

// Assume the current directory is not inside a Git repository
const cwd = "/tmp/some-standalone-dir";
const root = resolveWorkspaceRoot(cwd);
console.log(root);   // → "/tmp/some-standalone-dir"

Canonicalization and State Management

After determining the workspace root, other modules like plugins/codex/scripts/lib/state.mjs often call fs.realpathSync.native to resolve symlinks. However, the decision about which directory constitutes the workspace is made solely by resolveWorkspaceRoot. Most Codex-Companion subsystems store data under paths derived from this root, ensuring project state remains consistent even when commands are invoked from subdirectories.

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

const cwd = process.cwd();               // e.g. invoked from a sub-folder
const workspaceRoot = resolveWorkspaceRoot(cwd);
const state = loadState(workspaceRoot);

Implementation Examples in Hook Scripts

Real-world usage appears in hook scripts like stop-review-gate-hook.mjs and session-lifecycle-hook.mjs. These demonstrate how to leverage the workspace root for file operations.

Getting the workspace root in a hook:

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

export async function runHook({ cwd }) {
  const workspaceRoot = resolveWorkspaceRoot(cwd);
  // Now use `workspaceRoot` for state files, job directories, etc.
}

Summary

  • Workspace root resolution is handled by resolveWorkspaceRoot in plugins/codex/scripts/lib/workspace.mjs
  • Git detection via ensureGitRepository in plugins/codex/scripts/lib/git.mjs walks up the directory tree looking for .git folders
  • Graceful fallback to cwd occurs when no Git repository is found
  • Downstream modules use the resolved path for state management and job tracking
  • Consistent behavior is ensured regardless of which subdirectory invokes the command

Frequently Asked Questions

What happens if I run Codex-Companion outside a Git repository?

When no .git directory is found, ensureGitRepository throws an error that resolveWorkspaceRoot catches, returning the current working directory as the workspace root. This allows the plugin to function in standalone directories without requiring Git initialization.

Where is the workspace root resolution logic implemented?

The main logic resides in plugins/codex/scripts/lib/workspace.mjs for the entry point and plugins/codex/scripts/lib/git.mjs for the Git-aware directory traversal. These files handle the detection and fallback mechanisms used throughout the codebase.

The resolution itself does not follow symlinks during the search phase, but downstream modules like state.mjs typically call fs.realpathSync.native on the resolved path to canonicalize it after the root is determined.

How does this ensure consistent state across subdirectories?

By anchoring to the Git repository root (or the invocation directory as fallback), all state files and job directories are stored relative to a single stable base path. This prevents fragmentation when commands are run from different subdirectories within the same project.

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 →