fix(#669): /gsd-review --cursor actually invokes cursor-agent (#686)

* fix(#669): /gsd-review --cursor actually invokes cursor-agent

The Cursor reviewer branch in review.md never ran the agent:
- detection probed `cursor` (the IDE launcher) instead of the headless
  `cursor-agent` binary
- the invocation used the two-token `cursor agent` (the IDE treats `agent`
  as a file-path argument, so the agent never starts)
- the prompt was piped via stdin, but `cursor-agent -p` reads the prompt
  from a command-line argument, and `2>/dev/null` hid the empty result

Probe `cursor-agent`; invoke `cursor-agent -p --mode ask --trust
--output-format text` with the prompt passed as a file-path-reference
argument (avoids the OS arg-length limit on large prompts); capture stderr
so failures are diagnosable. Invert tests/cursor-reviewer.test.cjs to assert
the corrected contract, with negative guards against the two-token form and
the stdin pipe. The sibling `agy` reviewer already used the argument form.

Closes #669

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#669): set changeset pr number to 686

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-05 10:36:19 -04:00
committed by GitHub
parent afe61f015c
commit ecc42cefc6
3 changed files with 56 additions and 9 deletions

View File

@@ -36,11 +36,11 @@ describe('Cursor CLI reviewer in /gsd-review (#1960)', () => {
content = fs.readFileSync(reviewPath, 'utf-8');
});
test('contains cursor CLI detection via command -v', () => {
test('contains cursor CLI detection via command -v cursor-agent', () => {
const c = fs.readFileSync(reviewPath, 'utf-8');
assert.ok(
c.includes('command -v cursor'),
'review.md should detect cursor CLI via "command -v cursor"'
c.includes('command -v cursor-agent'),
'review.md should detect cursor CLI via "command -v cursor-agent" (not the cursor IDE launcher)'
);
});
@@ -60,11 +60,47 @@ describe('Cursor CLI reviewer in /gsd-review (#1960)', () => {
);
});
test('contains cursor agent invocation command', () => {
test('invocation uses cursor-agent single binary with -p flag', () => {
const c = fs.readFileSync(reviewPath, 'utf-8');
assert.ok(
c.includes('cursor agent -p --mode ask --trust'),
'review.md should invoke cursor via "cursor agent -p --mode ask --trust"'
c.includes('cursor-agent -p'),
'review.md should invoke cursor via "cursor-agent -p" (single binary, not two-token "cursor agent")'
);
});
test('invocation includes --output-format text', () => {
const c = fs.readFileSync(reviewPath, 'utf-8');
assert.ok(
c.includes('--output-format text'),
'review.md cursor-agent invocation should include "--output-format text"'
);
});
test('invocation passes prompt as a file-path argument (not via stdin pipe)', () => {
const c = fs.readFileSync(reviewPath, 'utf-8');
assert.ok(
c.includes('Read the file at /tmp/gsd-review-prompt-'),
'review.md cursor-agent invocation should pass prompt by referencing the file path as an argument'
);
});
test('does NOT use broken two-token "cursor agent " form', () => {
const c = fs.readFileSync(reviewPath, 'utf-8');
// Must not match "cursor agent " (cursor + space + agent + space)
// The hyphenated "cursor-agent" must NOT trip this check — the regex uses a space, not a hyphen.
assert.ok(
!/cursor agent /.test(c),
'review.md must NOT contain the broken two-token form "cursor agent " (use "cursor-agent" instead)'
);
});
test('does NOT pipe the prompt into a cursor command via stdin', () => {
const c = fs.readFileSync(reviewPath, 'utf-8');
// Must not match a pipe feeding into a cursor command (e.g. "| cursor" or "|cursor")
// "cursor-agent" (hyphenated) must NOT trip this — the regex anchors on "cursor" not followed by "-agent"
assert.ok(
!/\| *cursor(?!-agent)/.test(c),
'review.md must NOT pipe the prompt to a cursor command via stdin'
);
});