Skip to content

fix(cli): a plugin installed for Codex loses the hooks a marketplace install keeps #698

Description

@blafourcade

Context

Installing aidd-telemetry for Codex through the CLI's own translation puts the skills and their scripts in place and silently drops hooks/. The run journal is a hook, so a Codex session records nothing — not because of a payload shape, but because the hook is never installed.

Measured by installing it the way the framework does:

aidd plugin install <path> --tool codex --scope project --yes
→ .codex/plugins/aidd-telemetry/skills/…       18 files
→ .codex/plugins/aidd-telemetry/hooks/…        absent

Why it happens

PluginsCapability.acceptsHooks defaults to false, and only Claude Code, Copilot and Cursor declare true. Codex does not, so translateFile returns null for everything under hooks/.

That default is wrong for Codex, and this machine proves it. Codex records hook state for plugin-provided hooks in its own config:

[hooks.state."aidd-context@aidd-framework:hooks/hooks.json:session_start:0:0"]
[hooks.state."aidd-refine@aidd-framework:hooks/hooks.json:user_prompt_submit:0:0"]

and a marketplace-installed plugin keeps its directory:

~/.codex/plugins/cache/aidd-framework/aidd-context/2.6.2/hooks/

So the two install routes disagree about what a plugin is. The marketplace path delivers hooks; the translation path drops them.

The second half, which flipping the flag does not fix

With acceptsHooks: true the hooks do land, byte-identical — the artefact rule holds through the real install — but the command inside them does not work:

"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/journal.js session-start"

Codex expands a different variable. A plugin that works there spells it:

"command": "node ${PLUGIN_ROOT}/hooks/update_memory.js"

Installed as-is, the hook would resolve to node /hooks/journal.js and fail silently on every event — worse than not installing it, since a broken hook looks like an installed one. The flag was flipped, measured, and reverted for exactly that reason.

The event names need no translation: Codex takes the same PascalCase keys.

Expected

A plugin installed for a tool that runs hooks arrives with hooks that run. The plugin-root variable is that tool's own, declared beside its other plugin facts, never one tool's spelling shipped to another.

Acceptance

  • Codex declares that it accepts hooks, and the declaration is checked against what the tool actually does rather than assumed.
  • The plugin-root variable is declared per tool and substituted on translation.
  • A hook installed for any tool resolves to a path that exists — asserted, since a hook that resolves to nothing produces no error and no line.
  • The two install routes agree: what a marketplace install delivers and what a local install delivers hold the same files.
  • OpenCode is looked at too. It also leaves acceptsHooks unset, and its skip is already recorded with a reason — this ticket should confirm that reason still holds rather than inherit it.

What it unblocks

The run journal on Codex, and with it every step attribution for that tool. Codex's records already carry a moment and join to a journal interval correctly; there has simply never been a journal for them to join to.

Relations

Field Value
parent #631
related #681, #676, #653

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    Status
    In review

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions