debt-ops
June 23, 2026 · View on GitHub
Brings debt-ops' write-time feedback loop to Cursor via Cursor's agent hooks
(.cursor/hooks.json, Cursor 1.7+). Unlike a skills-only install, Cursor's hooks
fire deterministically on the agent loop, so quality checks, the stop-time safety
net, and drop shorthand all run on their own — the same posture as the Claude
Code and Codex adapters (ADR 0020).
Why Cursor is full-experience, not degraded
The portable skills already run on Cursor (it reads the open
SKILL.md standard), but skills are model-invoked — they can't fire on every
edit. Cursor's hook contract closes that gap, and it exposes every channel the
loop needs:
| Capability | Claude Code | Cursor | Here |
|---|---|---|---|
| Disciplines at session start | SessionStart inject | sessionStart → additional_context | ✅ injected, same as Claude/Codex |
| Write-time quality checks | PostToolUse(Edit) | postToolUse (no matcher) → additional_context | ✅ ported — feedback.py self-filters to edit tools by tool_name/tool_input |
| Stop-time safety net | Stop decision:block | stop → followup_message | ✅ ported — stop.py returns a continuation message |
drop A intercept + confirm | UserPromptSubmit block | beforeSubmitPrompt → continue:false + user_message | ✅ ported — the channel Copilot lacks |
So on Cursor you get the full write-time loop, the stop safety net, the drop intercept, and the session-start inject — capture/review/metrics via the bundled skills, disciplines injected per session and persistable to the charter.
Why not
afterFileEdit? Cursor'safterFileEdithas the cleanest payload (file_path+edits) but is informational-only — it can't return anything to the agent.postToolUsedoes injectadditional_context, so that's whatfeedback.pyuses, self-filtering to edit tools.
Install
Needs a git repo and Python 3.10+ (stdlib only).
Recommended: the Cursor marketplace plugin
This adapter is a self-contained Cursor plugin (cursor/.cursor-plugin/plugin.json,
bundling the hooks and skills), so the whole loop installs in one step once it's on
a marketplace:
/add-plugin # then pick debt-ops, or browse cursor.com/marketplace
Plugin-mode hooks (cursor/hooks/hooks.json) reference their scripts via the
${CURSOR_PLUGIN_ROOT} token, and each hook re-anchors to the workspace root
(workspace_roots[0]) before any git call — so the write-time loop fires whether
Cursor runs plugin hooks from the plugin dir or the project. To test locally
before publishing, symlink this dir into Cursor's local-plugin path:
ln -s "$PWD/cursor" ~/.cursor/plugins/local/debt-ops
Manual install (no marketplace)
Copy the hooks and skills into your repo by hand.
1. Hooks — note this uses hooks.local.json (project-relative paths), not the
plugin-mode hooks.json:
mkdir -p .cursor/hooks
cp cursor/hooks/hooks.local.json .cursor/hooks.json
cp cursor/hooks/session-start.py cursor/hooks/feedback.py cursor/hooks/stop.py cursor/hooks/drop.py .cursor/hooks/
The config must live at .cursor/hooks.json (Cursor's config location); the
scripts sit under .cursor/hooks/ and are referenced by relative path from the
project root. Cursor watches the config and reloads it automatically. A
user-level install works too — drop the same files under ~/.cursor/ and adjust
the paths.
2. Skills — drop the four debt-ops-* skills into a Cursor skills directory
(.agents/skills/ or .cursor/skills/, project-local; or ~/.agents/skills/
personal):
mkdir -p .agents/skills
cp -r cursor/skills/debt-ops-* .agents/skills/
debt-ops-init ships with disable-model-invocation: true, so it's
explicit-only — run it as /debt-ops-init when you want to write the disciplines
into AGENTS.md for the team. The other three are model-invoked.
(Optional) charter
The sessionStart hook injects the disciplines and detects quality commands
every session, so you don't need the charter. Run debt-ops-init if you want
the disciplines and the <!-- debt-ops:feedback v1 --> quality-commands block
persisted in AGENTS.md so the whole team shares them; feedback.py reads that
block when present (else the session-detected feedback.list cache).
How it works
sessionStart→session-start.py— injects the disciplines viaadditional_context, probes + caches the repo's ADR and registry dirs, detects quality commands (or reads them from theAGENTS.mdcharter), and logs onesessionmetric.postToolUse→feedback.py— fires after every tool; idles unless the tool was a file edit (matched viatool_name/tool_input). On an edit: runs each quality command in parallel under a 3 s budget, warns if the edit dropped the repo's test-file count, and returns the pass/fail summary asadditional_context.stop→stop.py— fires when the agent loop ends. Counts new TODO/FIXME/HACK/XXX markers vs new registry entries; if markers outpace registrations it returns afollowup_messagenudge, capped once per conversation so it can't loop.beforeSubmitPrompt→drop.py— interceptsdrop A/drop A,C/drop alltyped as the whole prompt: deletes the matching entries and blocks the submission (continue:false) with a one-line confirmation — no agent turn consumed.
Cache and metrics live under ~/.cache/debt-ops/ (override with
DEBT_OPS_CACHE) — the same base the skills and other adapters use, so
debt-ops-review and debt-ops-metrics see this adapter's data.
Cloud agents
Cursor's cloud agents run postToolUse but not sessionStart, stop, or
beforeSubmitPrompt (those aren't wired in cloud yet). So in a cloud agent you
get the write-time feedback loop; the session inject, stop net, and drop
shorthand are local/IDE only. Persist the disciplines with debt-ops-init so
cloud runs still see them via the charter.
Note on script duplication
These hooks and skills are near-verbatim copies of the Codex adapter's, on purpose — the copies are kept in sync by hand/AI per change rather than extracted into a shared module (ADR 0014). Don't "clean up" the duplication here.