feat(adapters): add Cursor CLI (cursor-agent) profile - #3
Open
thecontstruct wants to merge 10 commits into
Open
thecontstruct wants to merge 10 commits into
thecontstruct wants to merge 10 commits into
Conversation
thecontstruct
marked this pull request as ready for review
July 9, 2026 04:27
thecontstruct
pushed a commit
that referenced
this pull request
Aug 8, 2026
…arial-review findings (bmad-code-org#276) A second-model adversarial review of PR bmad-code-org#277 surfaced 7 valid findings. This lands the fixes for six of them (the 7th, a repo-wide policy-coercion consistency issue, is deferred to a follow-up): - #1 (High) M2 transition now outranks the M1 hash gate. A clean review that round-trips done->in-review->done back to byte-identical launch bytes while omitting its marker no longer false-refuses; the hash gate refuses only when no transition was observed. Safe: the dead-window false positive never records a transition (status never leaves `done`). - #2 (High) The stories-mode folder+id read-back now applies the same launch-snapshot gate, closing the identical false completion it previously bypassed (it accepted on the mtime floor alone). Inert on a dev leg (no snapshot). - #3 (High) The three in-place spec rewriters (append/strip/reset) now write atomically (temp + atomic_replace), so an interrupted/disk-full repair leaves the original spec intact instead of truncated. - #4 (Med) Adds engine-level coverage of the synthesized-REVIEW repair path (including a follow-up review that rewrites the spec) plus regressions for #1/#2/#3/bmad-code-org#5/bmad-code-org#7 and a _snapshot_verdict truth-table. - bmad-code-org#5 (Low) Snapshot/candidate identity is now by resolved filesystem path (_same_spec), not raw string spelling, so a `..`/symlink/case alias can't disable M1/M2. - bmad-code-org#7 (Low) A spec ending in a bare `\r` no longer receives an invisible heading (completed to CRLF so the scan's `^` regex sees it). #1 and #2 share a single `_snapshot_verdict` helper so the "M2 outranks M1" rule can't drift between the two paths. All edits sit on `_DevSynthesisMixin`, covering both the tmux and OpenCode adapters.
thecontstruct
force-pushed
the
feat/cursor-cli-provider
branch
from
August 8, 2026 01:25
4be70d1 to
f01b7eb
Compare
thecontstruct
force-pushed
the
feat/cursor-cli-provider
branch
from
August 24, 2026 01:27
f01b7eb to
b0f345c
Compare
Co-authored-by: Cursor <cursoragent@cursor.com>
bmad-loop could not drive Cursor. This adds a packaged `cursor` profile and a
`cursor-hooks-json` hook dialect, so the generic adapter runs it with no Python.
Skills load from .cursor/skills/. The relay registers `sessionStart` and `stop`
in a project .cursor/hooks.json. That file is versioned and its entries are bare
{"command": ...} objects, so merge_hooks always writes a top-level `version`:
Cursor 3.x loads none of a project file's hooks without one, which reads as a
session timeout rather than as an error anyone can see.
Launch flags are `--force --trust`. Measured against cursor-agent 2026.08.04,
each in a fresh untrusted git dir launched interactively under tmux:
bare -> blocks on the workspace-trust dialog
--force -> still blocks
--force --trust -> runs
seeded trust marker -> still blocks
That last row is why this drops the earlier approach of writing Cursor's
~/.cursor/projects/<slug>/.workspace-trusted marker. The marker is real, but
replaying a byte-correct copy does not satisfy the gate, so that code did not do
what it claimed. Dropping it also removes the `seed_workspace_trust` profile
field, the install-time writer, and the only code that wrote outside the
project. Because `--trust` applies per launch, isolation = "worktree" works
here, unlike antigravity.
The prompt is handed over as an argv positional, so a leading "/" never reaches
Cursor's slash menu. The template names the SKILL.md outright, as codex and
copilot do.
usage_parser stays "none". The Stop payload names a transcript, but its token
schema is unread, so nothing is claimed about it.
Marked experimental: no full dev/review loop has been run end to end. Finalize
with `probe-adapter cursor`.
Co-authored-by: Cursor <cursoragent@cursor.com>
The profile told the next reader to finalize it with `probe-adapter cursor --probe` before trusting it unattended. That probe has now run — 0 warnings, both hook events captured live — and a one-story dev loop then completed end to end with a real commit, so the EXPERIMENTAL warning is stale and goes. The probe also settled the open token question, and the answer is that `usage_parser` can never fix it. Cursor puts the counts on the Stop hook payload (input_tokens, output_tokens, cache_read_tokens, cache_write_tokens) and puts no token fields in the transcript at all, so a parser handed a transcript path has nothing to read whatever it is set to. The counts do reach this machine and are then dropped: the shared relay narrows every payload to ts/event/task_id/session_id/transcript_path/cwd. Wiring them up therefore means widening a contract every provider shares, not editing this profile. Recording that here stops the next reader retrying it as a cursor-local fix. Also note what has NOT been measured. Completion here comes from hooks rather than parsed stdout, so this profile does not depend on the terminal result frame that a dropped agent stream takes away from the print/stream-json transports. But the dev loop above ran on a healthy network path, so how these hooks behave when the stream drops mid-turn is untested, and the note says so rather than implying immunity.
thecontstruct
force-pushed
the
feat/cursor-cli-provider
branch
from
September 4, 2026 17:21
2107b35 to
c900451
Compare
Co-authored-by: Cursor <cursoragent@cursor.com>
…mmand Upstream bmad-code-org#825 replaced the copied bmad_loop_hook.py with an absolute `bmad-loop relay <Event>` command. The cursor tests still built the old command, so they never checked that Cursor's bare entries carry the new form or that the stale-relay strip recognizes it. - Cursor merge/strip/idempotency tests now use the installed relay form. - Add test_install_into_cursor: real `init --cli cursor` writes a versioned .cursor/hooks.json whose entries hold only "command". - Add cursor to the per-dialect tests for fresh init, legacy flat-hook migration, and probe hook registration. Co-authored-by: Cursor <cursoragent@cursor.com>
Upstream now lists each CLI's hook config to edit on uninstall. Cursor was missing, so a cursor user had no pointer to the file holding the relay. Co-authored-by: Cursor <cursoragent@cursor.com>
The cursor rows widened three markdown tables without realigning them, and taplo collapses the short seed_files array. Whitespace only. Co-authored-by: Cursor <cursoragent@cursor.com>
The profile header already recorded the graduation: probe-adapter cursor --probe passed with 0 warnings and a one-story dev loop completed with a real commit on cursor-agent 2026.09.02. README, FEATURES, setup guide and CHANGELOG still called it experimental. They now say supported, E2E-verified, and note that no token usage is recorded yet. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Cursor users need a path that needs no Node runtime, alongside the SDK provider. The
cursorprofile drives interactivecursor-agentthrough the existing tmux and hook transport, so it needs no adapter class.What
cursor-hooks-jsonhook dialect (adapters/profile.py,install.py). Cursor reads a project.cursor/hooks.jsonwith a numeric top-levelversionand bare{"command": …}entries. Cursor 3.x loads no hooks from a project file that has noversion, somerge_hooksalways writes one.data/profiles/cursor.toml:stopmaps to Stop, andsessionStartmarks liveness. There is no SessionEnd..cursor/skills/.usage_parser = "none".--force --trust. An interactive launch in an untrusted directory blocks on a trust dialog.--forcealone does not clear it, and neither does seeding Cursor's.workspace-trustedmarker. Trust is granted per launch, soisolation = "worktree"works.[adapter] extra_argsreplaces the bypass flags, so it must keep--trust.bmad-loop relay <Event>command (Wave 7b: register relay through the installed console script bmad-code-org/bmad-loop#825), and the cursor dialect wraps it as-is.cursoris added to the name lists incli.pyhelp and thepolicy.pytemplate.Dropped since the first draft
seed_workspace_trust()andseed_workspace_trust = true. Writing the trust marker did not clear the trust dialog, so--trustreplaced it.Testing
mainat8237188bon 2026-09-22.os.setxattrandos.getxattrinplatform_util.py) also fail on unmodified upstreammainon macOS. CI runs on Linux.test_install_into_cursorruns a realinit --cli cursor. It fails when the cursor dialect branch is removed.tests/test_install.pyandtests/test_probe.py.initwrites a versioned.cursor/hooks.jsonwith baresessionStartandstopentries.session_idtaken fromconversation_id.probe-adapter cursorin scan mode, with a fake binary, reports that hooks are registered.review.enabled = trueandtrigger = always: a separate review session completed, then verify and commit (2026-07-09).probe-adapter cursor --probewith 0 warnings, followed by a one-story dev loop (1 done, 0 deferred, 0 escalated) on cursor-agent 2026.09.02.Known limits
usage_parser = "none"). The Stop payload names a transcript, but its token schema has not been read.cursor.tomlheader refers to thecursor-cli-headlessprofile, which lands in feat(adapters): add cursor-cli-headless provider (Path B) #4.Made with Cursor