Skip to content

fix(codex): --with-hooks workaround for openai/codex#16430 (closes #509)#564

Merged
rohitg00 merged 1 commit into
mainfrom
fix/codex-hooks-fallback
May 20, 2026
Merged

fix(codex): --with-hooks workaround for openai/codex#16430 (closes #509)#564
rohitg00 merged 1 commit into
mainfrom
fix/codex-hooks-fallback

Conversation

@rohitg00
Copy link
Copy Markdown
Owner

@rohitg00 rohitg00 commented May 20, 2026

Summary

Closes #509.

Codex Desktop currently does not dispatch plugin-local hooks.json even though both CodexHooks and PluginHooks feature flags are stable + default-enabled in codex-rs/features/src/lib.rs. Upstream tracking issue: openai/codex#16430. MCP tools still work; lifecycle observations are silently missing.

Adds an opt-in workaround:

agentmemory connect codex --with-hooks

This mirrors the bundled plugin/hooks/hooks.codex.json into the user-scope ~/.codex/hooks.json, which Codex loads reliably today.

How it works

  • Resolves ${CLAUDE_PLUGIN_ROOT} to the absolute bundled plugin/ path. Codex only injects that env var for plugin-scope hooks, so user-scope entries need real paths.
  • Idempotent merge: previous agentmemory entries are stripped on re-install via the resolved <pluginRoot>/scripts/ prefix; unrelated user hooks are preserved untouched.
  • Preserves matcher fields from the bundled manifest so PreToolUse event routing keeps working (Edit|Write|Read|Glob|Grep).
  • findPluginRoot walks up from import.meta.url to locate the plugin/ dir; works for both dist/cli.mjs (bundled) and src/ (dev) layouts.
  • Dry-run path (--dry-run --with-hooks) previews both TOML and hooks.json changes without mutating either file.
  • Backs up existing ~/.codex/hooks.json to ~/.agentmemory/backups/ before write (same pattern as the TOML wiring).

Validation

  • 9 new unit tests in test/codex-connect-hooks.test.ts cover path rewrite, matcher preservation, event coverage, user-hook preservation across (re)installs, JSON round-trip
  • npm test: 97/97 test files pass, 1081/1081 tests pass
  • npm run build: dist bundles clean
  • Smoke test: node dist/cli.mjs connect codex --dry-run --with-hooks reports both TOML and hooks.json previews

Manual flow on Codex Desktop after merge

agentmemory connect codex --with-hooks
# restart Codex Desktop session
# normal tool calls now create observations again

When upstream lands the plugin hook dispatch fix, users can either keep the user-scope mirror (still correct, just redundant) or remove the agentmemory entries from ~/.codex/hooks.json manually.

Scope

  • No changes to MCP wiring path (existing TOML logic untouched)
  • No new top-level surface area — single opt-in flag
  • Other adapters ignore withHooks (no-op)

Summary by CodeRabbit

  • New Features

    • Added --with-hooks flag to the agentmemory connect codex command. When enabled, it writes to ~/.codex/hooks.json with plugin hook handlers to address a known Codex Desktop plugin issue.
  • Documentation

    • Updated README with explanation of the Codex Desktop plugin hooks limitation and how to use the new --with-hooks workaround.

Review Change Stack

… dispatch bug

Codex Desktop currently does not dispatch plugin-local hooks.json even
though both CodexHooks and PluginHooks feature flags are stable +
default-enabled in codex-rs/features/src/lib.rs (openai/codex#16430).
MCP tools still work; lifecycle observations are silently missing.

Adds `agentmemory connect codex --with-hooks` which mirrors the bundled
hooks.codex.json into the user-scope ~/.codex/hooks.json:

- Resolves ${CLAUDE_PLUGIN_ROOT} to the absolute bundled plugin/ path
  (user-scope hooks don't get plugin-root injection)
- Idempotent merge: previous agentmemory entries are stripped on
  reinstall via the resolved scripts/ path prefix; unrelated user
  hooks are preserved untouched
- Preserves matcher fields from the bundled manifest so PreToolUse
  routing still works
- findPluginRoot walks up from import.meta.url to locate the plugin/
  dir; works for both dist/cli.mjs (bundled) and src/ (dev) layouts
- Dry-run path previews both TOML and hooks.json changes

Closes #509.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agentmemory Ready Ready Preview, Comment May 20, 2026 2:14pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

📝 Walkthrough

Walkthrough

This PR implements a workaround for Codex Desktop's inability to dispatch plugin-local hooks.json files. It adds a --with-hooks CLI flag that installs the bundled hook manifest into Codex's global ~/.codex/hooks.json, enabling automatic lifecycle observations on tool use and user prompts.

Changes

Codex hooks.json workaround installation

Layer / File(s) Summary
Hook manifest discovery and merging
src/cli/connect/codex-hooks.ts, test/codex-connect-hooks.test.ts
findPluginRoot walks up directories to locate plugin/scripts and plugin/hooks; buildMergedHooks reads the bundled hook manifest, removes prior agentmemory entries, rewrites ${CLAUDE_PLUGIN_ROOT} placeholders to absolute paths, and merges with any existing user hooks. Tests verify path rewriting, placeholder substitution, lifecycle event preservation, idempotency across reinstalls, and JSON round-trip correctness.
Type contract and CLI flag threading
src/cli/connect/types.ts, src/cli/connect/index.ts
ConnectOptions gains optional withHooks?: boolean; parseFlags parses --with-hooks argument; runConnect threads withHooks through to adapter install.
Codex adapter hook installation and documentation
src/cli/connect/codex.ts, README.md
Codex adapter calls installCodexHooks when opts.withHooks is true, which tolerantly reads ~/.codex/hooks.json, merges with bundled hooks, backs up prior state, writes merged manifest atomically, and logs installation. protocolNote documents the MCP flow and --with-hooks workaround; README adds sections explaining that Codex Desktop does not dispatch plugin-local hooks, clarifying that MCP tools still function, and providing the agentmemory connect codex --with-hooks workaround.

Sequence Diagram(s)

(Diagrams are included in the hidden review stack artifact above.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • rohitg00/agentmemory#402: Adds the Codex TOML wiring adapter that this PR extends with hook installation.
  • rohitg00/agentmemory#311: Adds plugin/hooks/hooks.codex.json hook manifest that this PR reads and installs into ~/.codex/hooks.json.
  • rohitg00/agentmemory#495: Adds scripts/session-end.mjs hook script that is incorporated into Codex Stop hooks via this PR's merging logic.

Poem

🐰 The hooks were silent, the observations gone,
but bundled configs held the path all along!
With merging, rewriting, and idempotent care,
global hooks now work where desktop hooks dare not share. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: introducing a --with-hooks workaround for the Codex hooks dispatch issue (openai/codex#16430) and closing issue #509.
Linked Issues check ✅ Passed The PR fully addresses issue #509's core objectives: providing a workaround for hooks not triggering in Codex Desktop, enabling observation creation via --with-hooks flag, and preserving user environment/hooks.
Out of Scope Changes check ✅ Passed All changes are tightly scoped to the --with-hooks workaround: CLI flag parsing, hook manifest merging, path resolution, and related tests; no unrelated refactoring or modifications present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/codex-hooks-fallback

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cli/connect/codex.ts (1)

79-82: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

--with-hooks is skipped for already-wired Codex configs.

On Line 79, the early return prevents fallback hook installation for existing users unless they also pass --force. That breaks the intended agentmemory connect codex --with-hooks remediation path.

Proposed fix
-    if (wired && !opts.force) {
+    if (wired && !opts.force && !opts.withHooks) {
       logAlreadyWired("Codex CLI", CODEX_TOML);
       return { kind: "already-wired", mutatedPath: CODEX_TOML };
     }
+
+    if (wired && !opts.force && opts.withHooks) {
+      logAlreadyWired("Codex CLI", CODEX_TOML);
+      const hookResult = installCodexHooks(opts);
+      if (hookResult.kind === "skipped") {
+        p.log.warn(
+          `Codex hooks fallback skipped: ${hookResult.reason}. MCP wiring still applied.`,
+        );
+      }
+      return { kind: "installed", mutatedPath: CODEX_HOOKS };
+    }

Also applies to: 118-125

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/connect/codex.ts` around lines 79 - 82, The early return for
already-wired Codex configs (the block using wired, logAlreadyWired, CODEX_TOML)
prevents the --with-hooks flow from running; change the guard to only
short-circuit when the user did not request hooks (e.g. if (wired && !opts.force
&& !opts.withHooks) { logAlreadyWired("Codex CLI", CODEX_TOML); return { kind:
"already-wired", mutatedPath: CODEX_TOML }; }), and apply the same change to the
similar block that spans lines 118-125 so that passing --with-hooks (or the
corresponding opts['with-hooks'] flag name used in the code) allows fallback
hook installation even when wired is true.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cli/connect/codex-hooks.ts`:
- Around line 98-99: isAgentmemoryEntry currently matches by raw substring which
can falsely match unrelated commands; update isAgentmemoryEntry to resolve and
normalize scriptsDir (use path.resolve) and append path.sep, then for each
handler in HookEntry inspect the command tokens (split by whitespace) and
normalize each token (path.normalize) and check tokens startWith the normalized
scriptsDir + separator (not just includes). Modify the function
isAgentmemoryEntry and its use of handler.command to perform this true
path-prefix check so only commands whose executable/path actually live under the
scriptsDir are considered agentmemory entries.

---

Outside diff comments:
In `@src/cli/connect/codex.ts`:
- Around line 79-82: The early return for already-wired Codex configs (the block
using wired, logAlreadyWired, CODEX_TOML) prevents the --with-hooks flow from
running; change the guard to only short-circuit when the user did not request
hooks (e.g. if (wired && !opts.force && !opts.withHooks) {
logAlreadyWired("Codex CLI", CODEX_TOML); return { kind: "already-wired",
mutatedPath: CODEX_TOML }; }), and apply the same change to the similar block
that spans lines 118-125 so that passing --with-hooks (or the corresponding
opts['with-hooks'] flag name used in the code) allows fallback hook installation
even when wired is true.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 40f91846-61cc-4d50-9b9e-fa3758bacf01

📥 Commits

Reviewing files that changed from the base of the PR and between 7fb72f4 and d5e879b.

📒 Files selected for processing (6)
  • README.md
  • src/cli/connect/codex-hooks.ts
  • src/cli/connect/codex.ts
  • src/cli/connect/index.ts
  • src/cli/connect/types.ts
  • test/codex-connect-hooks.test.ts

Comment on lines +98 to +99
function isAgentmemoryEntry(entry: HookEntry, scriptsDir: string): boolean {
return entry.hooks.some((handler) => handler.command.includes(scriptsDir));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Tighten agentmemory-entry detection to avoid accidental user-hook removal.

Line 99 matches by raw substring, so unrelated commands containing the same text can be stripped on reinstall. Match the normalized scripts path as a true path prefix (with separator) instead.

Proposed fix
 function isAgentmemoryEntry(entry: HookEntry, scriptsDir: string): boolean {
-  return entry.hooks.some((handler) => handler.command.includes(scriptsDir));
+  const marker = `${scriptsDir}/`;
+  return entry.hooks.some((handler) => {
+    const command = handler.command.replace(/\\/g, "/");
+    return command.includes(marker);
+  });
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/connect/codex-hooks.ts` around lines 98 - 99, isAgentmemoryEntry
currently matches by raw substring which can falsely match unrelated commands;
update isAgentmemoryEntry to resolve and normalize scriptsDir (use path.resolve)
and append path.sep, then for each handler in HookEntry inspect the command
tokens (split by whitespace) and normalize each token (path.normalize) and check
tokens startWith the normalized scriptsDir + separator (not just includes).
Modify the function isAgentmemoryEntry and its use of handler.command to perform
this true path-prefix check so only commands whose executable/path actually live
under the scriptsDir are considered agentmemory entries.

@rohitg00 rohitg00 merged commit e371be9 into main May 20, 2026
7 checks passed
@rohitg00 rohitg00 deleted the fix/codex-hooks-fallback branch May 20, 2026 14:26
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 Desktop does not appear to trigger hooks.codex.json observations

1 participant