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:

CapabilityClaude CodeCursorHere
Disciplines at session startSessionStart injectsessionStartadditional_context✅ injected, same as Claude/Codex
Write-time quality checksPostToolUse(Edit)postToolUse (no matcher) → additional_context✅ ported — feedback.py self-filters to edit tools by tool_name/tool_input
Stop-time safety netStop decision:blockstopfollowup_message✅ ported — stop.py returns a continuation message
drop A intercept + confirmUserPromptSubmit blockbeforeSubmitPromptcontinue: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's afterFileEdit has the cleanest payload (file_path + edits) but is informational-only — it can't return anything to the agent. postToolUse does inject additional_context, so that's what feedback.py uses, self-filtering to edit tools.

Install

Needs a git repo and Python 3.10+ (stdlib only).

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

  • sessionStartsession-start.py — injects the disciplines via additional_context, probes + caches the repo's ADR and registry dirs, detects quality commands (or reads them from the AGENTS.md charter), and logs one session metric.
  • postToolUsefeedback.py — fires after every tool; idles unless the tool was a file edit (matched via tool_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 as additional_context.
  • stopstop.py — fires when the agent loop ends. Counts new TODO/FIXME/HACK/XXX markers vs new registry entries; if markers outpace registrations it returns a followup_message nudge, capped once per conversation so it can't loop.
  • beforeSubmitPromptdrop.py — intercepts drop A / drop A,C / drop all typed 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.