Skip to content

feat(cli): merge plugin hooks into antigravity's .agents/hooks.json - #958

Open
tkman59 wants to merge 1 commit into
feat/511-antigravity-hostfrom
feat/511-antigravity-memory-hook
Open

tkman59 wants to merge 1 commit into
feat/511-antigravity-hostfrom
feat/511-antigravity-memory-hook

Conversation

@tkman59

@tkman59 tkman59 commented Oct 5, 2026 •

Copy link
Copy Markdown

🎯 What & why

aidd setup --ai antigravity now installs the AIDD memory hook: agy refreshes the project memory at session start. Phase 3 of #511, stacked on #957.

🛠️ How it works

  • Route. The memory hook reaches a project through the aidd-context plugin, not through HooksCapability. Codex's HooksCapability is never fed: no codex-hooks config ref exists, so copying it would have written nothing. Antigravity uses the project-hooks route Cursor already had, with idempotent merge, per-entry provenance and safe clean.
  • Generalisation, Cursor unchanged. A tool now declares a ProjectHooksFormat beside hooksDestination: "project", which flat mode may now declare too. project-hooks-materializer.ts and remove-project-hooks.ts read only that format, with no Cursor import left. Cursor's suites pass with no assertion changed.
  • Antigravity format (antigravity-hooks.ts):
    • one key per plugin, handlers directly under SessionStart;
    • scripts copied to .agents/hooks/<plugin>/;
    • every command prefixed with cd .. && ;
    • other events skipped with a warning;
    • translate --to antigravity --as flat uses the same merge function; hooksMerge now receives the plugin name.
  • Measured on agy 1.2.17, with an interactive session under a pty (script -q /dev/null agy -i … --add-dir <ws>). In print mode the model is never called, so no hook fires.
    • Claude's nested [{ "hooks": [...] }] is rejected: invalid hook "aidd-probe": command hook must specify 'command'.
    • A workspace hook runs from <repo>/.agents, through a shell. A probe hook logged "cwd":".../agy-cwd-probe/.agents" with "workspacePaths":[".../agy-cwd-probe"]. With cd .. && , the marker read AIDD-MEMORY-MARKER cwd=<repo> docs=true.
  • --force reaches the edit guard. sync --force and clean --force pass force down to the project-hooks guard (RestoreUseCase → RestoreAllPluginsUseCase → ApplyPluginFilesUseCase → translator → ProjectHooksMaterializer; CleanUseCase → preflight and removal). Under --force only the "edited after install" refusals are lifted: an unproven script path or install digest still refuses. Without --force the refusal says how to proceed. plugin add, update and remove never pass it, so they still refuse. Cursor gets the same behaviour, since it shares the guard.
  • One owner per hook script. A project-delivered hook script is recorded in projectHooks.scripts only, no longer also in plugins[].files.
  • Safety. An unparsable .agents/hooks.json now stops the install with the file named (.agents/hooks.json is not valid JSON (…); left untouched.), for Cursor too. translate --to antigravity --as flat reports the same message.

🧪 How to verify

  • cd cli && pnpm test: 7082 passed, 1 skipped. CI cli / Smoke green.
  • tests/e2e/antigravity-edited-hooks.e2e.test.ts: an edited hook script, an edited AIDD entry in .agents/hooks.json, then sync --force or clean --force. Red before the fix (... was edited after install; detach refused), green after, with the user lint key kept.
  • Real run in a temp git repo (with aidd_docs/memory/ and AGENTS.md carrying the memory markers):
    1. aidd setup --source local --path <framework> --ai antigravity --plugins recommended --yes writes .agents/hooks.json with the aidd-context key and .agents/hooks/aidd-context/update_memory.js.
    2. Add a user key lint, then run setup again: .agents/hooks.json is byte-identical.
    3. script -q /dev/null agy -i "reply OK" --add-dir <repo> logs loaded 2 named hooks from 1 hooks.json file(s), and AGENTS.md at the repo root gains the memory list.
    4. aidd clean --force leaves .agents/hooks.json holding only lint, and removes .agents/hooks/.
  • Golden: only the antigravity:flat cell moved (.agents/hooks.json and the copied script).

⚠️ Heads-up

  • No CI runtime proof: a turn needs a Google login, and print mode fires no hook on 1.2.17.
  • The second setup in the real run was a no-op ("already installed"). Re-merge idempotence is proven by the unit test.
  • Diff is about 540 lines of code and tests, plus about 65 lines of plan. The generalisation was not split out, because alone it has a single user.
  • Out of scope, worth an issue: Codex's HooksCapability and mergeCodexHooksJson are dead code.

🔗 Linked issue

Refs #511

✅ I certify

  • I DO CERTIFY I READ EACH LINE OF THE PULL REQUEST BECAUSE I AM A SOFTWARE ENGINEER, NOT A AI PUPPY.

🤖 Generated with Claude Code

@tkman59
tkman59 marked this pull request as ready for review October 7, 2026 20:38
@tkman59
tkman59 requested a review from a team as a code owner October 7, 2026 20:38
@tkman59
tkman59 force-pushed the feat/511-antigravity-memory-hook branch from 683c750 to 710ca6b Compare October 13, 2026 20:17
@tkman59
tkman59 force-pushed the feat/511-antigravity-host branch from 6b213e8 to 5c423e9 Compare October 13, 2026 20:23
@tkman59
tkman59 force-pushed the feat/511-antigravity-memory-hook branch from 710ca6b to f7fe515 Compare October 13, 2026 20:48
@tkman59
tkman59 force-pushed the feat/511-antigravity-host branch from 5c423e9 to 058b4ef Compare October 13, 2026 21:22
@tkman59
tkman59 force-pushed the feat/511-antigravity-memory-hook branch from f7fe515 to 90beffc Compare October 13, 2026 21:22

@blafourcade blafourcade left a comment

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.

Reviewed line by line and exercised on the stack tip (agy 1.3.5).

Major: sync --force and clean --force abort on any edit of an AIDD hook
Reproduced on a fresh aidd setup --ai antigravity --plugins recommended:

  • edit .agents/hooks/aidd-context/update_memory.js, then sync --force: hook script ... was edited after install; detach refused, Sync failed for: config-restore, nothing restored (a drifted skill stays drifted);
  • edit the AIDD entry in .agents/hooks.json, then sync --force: same refusal;
  • same edits, then clean --force: refusal, all 435 files remain.

Cursor refuses clean the same way, but its sync is not affected: its install tracks only 4 files, so restore never re-applies it. Here sync re-applies the plugin on any drift (ApplyPluginFilesUseCase -> assertProjectHooksUnchanged). #962 documents this without fixing it. Please fix it here or link an issue before merge.

Question

  • .agents/hooks/aidd-context/update_memory.js is recorded twice in .aidd/manifest.json (plugin files and projectHooks.scripts). Intended?

Minor

  • translate --as flat with an unparsable .agents/hooks.json fails with a raw Expected property name or '}' in JSON..., without the file name. The "left untouched" message only exists on the setup path.
  • aidd-telemetry installs only session-start; Stop and PostToolUse are skipped with a warning. Is a partial journal acceptable?
  • antigravity-hooks.ts: the comment pins agy 1.2.17; it will go stale.

Checked and fine

  • Cursor's behavior is unchanged (same functions, same assertions). A user key such as lint survives merge, sync and clean. A second setup is a no-op. cd .. && makes the hook work from .agents (smoke on 1.3.5 refreshes AGENTS.md).

Antigravity now receives the aidd-context memory hook. Setup and plugin
install merge each plugin's hooks into .agents/hooks.json under one key
named after the plugin, copy its scripts under .agents/hooks/<plugin>/,
and clean removes that key alone, deleting the file only when empty.

The project-hooks route Cursor used is generalised: a tool declares a
ProjectHooksFormat beside hooksDestination "project", now allowed in flat
mode too, and the installer and uninstaller read only that. Cursor's
behaviour is unchanged. An unparsable project hooks file now stops the
install with the file named.

agy 1.2.17 rejects Claude's nested { hooks: [...] } groups and runs a
workspace hook from <repo>/.agents through a shell, so each command is
prefixed with "cd .. && "; events other than SessionStart are skipped
with a warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tkman59
tkman59 force-pushed the feat/511-antigravity-host branch from 058b4ef to 7a4f6bc Compare October 14, 2026 20:52
@tkman59
tkman59 force-pushed the feat/511-antigravity-memory-hook branch from 90beffc to b864c7f Compare October 14, 2026 20:52
@tkman59

tkman59 commented Oct 15, 2026

Copy link
Copy Markdown
Author

Thanks, all addressed in the hooks commit (stack replayed).

  • Major, sync --force / clean --force abort on an edited AIDD hook: fixed. force was never forwarded past RestoreUseCase to the plugin restore, nor from clean to the hooks preflight. It now reaches the guard, which under --force lifts only the "edited after install" refusals; ownership proofs (script path inside the plugin's hooks dir, a well-formed install digest) still refuse. Your three reproductions are in tests/e2e/antigravity-edited-hooks.e2e.test.ts: all red before (... was edited after install; detach refused), green after, with the user lint key kept. Without --force the refusal ends with Rerun aidd sync --force to restore it, or aidd clean --force to remove it. Cursor's clean --force is fixed by the same change.
  • Question, script recorded twice: not intended. The flat built-tree route added the hook output paths to the plugin files even when the tool delivers hooks to the project, so clean could delete the script through the unchecked plugin-files path. It is now recorded in projectHooks.scripts only (antigravity is the only tool with both a flat hooks dir and project delivery).
  • Minor, raw JSON error on translate --as flat: fixed, same message as setup, file named and left untouched.
  • Minor, partial telemetry journal: accepted for now, aidd-telemetry is opt-in and beta. Only the tool-free SessionStart is carried over: PostToolUse matchers name Claude tools that agy names differently, and agy documents a Stop event that is not measured yet. Both are skipped with a warning, so an Antigravity journal holds session starts only. Worth a follow-up issue if you agree.
  • Minor, pinned agy version in the comment: removed.
  • Known edge left as is: if a user empties the aidd-context entries to [], clean --force leaves the empty key behind.

@tkman59
tkman59 requested a review from blafourcade October 15, 2026 05:33
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.

2 participants