Skip to content

fix(hooks): return Cursor context from session start and the nudge - #16

Merged
sebastian-iancu merged 2 commits into
mainfrom
fix/cursor-hooks
Oct 27, 2026
Merged

sebastian-iancu merged 2 commits into
mainfrom
fix/cursor-hooks

Conversation

@sebastian-iancu

@sebastian-iancu sebastian-iancu commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Cursor plugin hooks were not delivering the Go standards line or the skill nudge. This PR fixes the Cursor wiring, and makes the validator and the hook tests hold it in place.

Hooks

  • session-start.sh: under Cursor (CURSOR_PLUGIN_ROOT set) it changes into CURSOR_PROJECT_DIR and returns {"additional_context": …} naming /go-coding and the /go-* skills. Without CURSOR_PROJECT_DIR it stays silent instead of reading the plugin install. Claude Code still gets the plain load go-coding line.
  • skill-nudge.sh: moves from Cursor afterFileEdit, which documents no output, to postToolUse with matcher Write|StrReplace, and returns additional_context telling Cursor to read /<skill>.
    • Cursor is recognised by CURSOR_PLUGIN_ROOT or a cursor_version key in the payload, checked before the Claude Code branch, because Cursor payloads also carry hook_event_name.
    • conversation_id is Cursor's once-per-session key.
    • A Cursor Write is reported as "this file", since its content can be the whole file.
    • The marker write no longer prints an error when TMPDIR cannot be written.
  • format-on-save.sh stays on afterFileEdit. Both edit hooks read file_path or path, and use the same Cursor test to join a relative path to CURSOR_PROJECT_DIR.
  • hooks/cursor-hooks.json carries "version": 1 and per-hook timeouts.

Validator (scripts/validate.py)

  • hooks/cursor-hooks.json needs "version": 1 and may not use ${CLAUDE_PLUGIN_ROOT}.
  • A Claude PostToolUse script may sit on Cursor afterFileEdit or postToolUse, but a script that prints additional_context may not sit on afterFileEdit.
  • Every postToolUse entry needs a matcher.
  • --selftest passes a case only when that check's own error appears, and a can-fail control proves it. One check's error can no longer hide another check's regression. The fixture exercises both Cursor events and parity in both directions.

Docs

  • docs/install.md states three Cursor facts:
    • Cursor 3.9.8 is the first build that passes postToolUse context to the model.
    • The edit tools' names and tool_input keys are reported, not documented.
    • Cursor's third-party import can run the hooks twice when the plugin is installed in both editors.
  • AGENTS.md, README.md, docs/testing.md, docs/authoring.md and CHANGELOG.md describe the wiring.

What Cursor documents, and what it doesn't

  • Documented in Cursor hooks:

    • "version": 1, and additional_context on sessionStart and postToolUse;
    • matchers are regular expressions;
    • plugin hooks run from CURSOR_PLUGIN_ROOT, and CURSOR_PROJECT_DIR is always set;
    • afterFileEdit passes an absolute file_path and lists no output.
  • Delivery to the model: Cursor staff said postToolUse context reaches the model from 3.9.8 (forum).

  • Not documented: the edit tool's tool_name and tool_input. Two reports disagree:

    The matcher and both scripts cover both shapes, and hooks-test.sh tests both.

Test plan

  • python3 scripts/validate.py and python3 scripts/validate.py --selftest (15 cases plus the can-fail control)
  • ./scripts/hooks-test.sh (55 cases)
  • claude plugin validate . and python3 scripts/validate.py --check-links (76 of 76)
  • Disabling each new validator rule, or the self-test's own-error check, makes --selftest fail. The real tree rejects three bad wirings: the nudge on afterFileEdit, the nudge on both events, and postToolUse without a matcher.
  • Install this branch as a Cursor plugin (3.9.8 or newer) in a Go repo and confirm:
    • the session line arrives;
    • an edit of a _test.go file nudges /go-testing (note the tool_name the Hooks output shows);
    • a messy .go file is reformatted.

Made with Cursor

🤖 Generated with Claude Code

sebastian-iancu and others added 2 commits October 27, 2026 14:10
Cursor reads additional_context, and afterFileEdit has no output.
session-start now inspects CURSOR_PROJECT_DIR, and the skill nudge
moves to postToolUse for Write and StrReplace. The formatter stays
on afterFileEdit.

Co-authored-by: Cursor <cursoragent@cursor.com>
Review follow-up for the Cursor hooks.

- validate.py: the parity self-test fixture keeps "version": 1, so the
  parity check is tested again; each self-test case must raise its own
  error, with a can-fail control. New rules: a script that prints
  additional_context may not sit on Cursor afterFileEdit, and every
  postToolUse entry needs a matcher. The fixture now exercises both
  Cursor events and parity in both directions.
- session-start.sh stays silent under Cursor without CURSOR_PROJECT_DIR
  instead of inspecting the plugin install.
- format-on-save.sh uses the same Cursor test as skill-nudge.sh
  (CURSOR_PLUGIN_ROOT or cursor_version) before joining a relative path.
- skill-nudge.sh says "this file" for a Cursor Write, whose content can
  be the whole file, and no longer prints an error when its marker
  cannot be written.
- hooks-test.sh: both reported Cursor edit shapes (StrReplace with path,
  Write with file_path and content), the once-per-conversation key, the
  relative-path join in both edit hooks, a Cursor terminal running
  Claude Code, and isolation from ambient CURSOR_* variables.
- Docs: Cursor 3.9.8 is the first build that passes postToolUse context
  to the model; the edit tool names and keys are reported, not
  documented; Cursor's third-party import can run the hooks twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sebastian-iancu
sebastian-iancu merged commit 5e65be6 into main Oct 27, 2026
2 checks passed
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.

1 participant