* 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:
5
.changeset/669-gsd-review-cursor-agent.md
Normal file
5
.changeset/669-gsd-review-cursor-agent.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 686
|
||||
---
|
||||
`/gsd-review --cursor` now actually invokes the Cursor agent. Detection probes the `cursor-agent` headless binary instead of the `cursor` IDE launcher, the invocation calls the single `cursor-agent` binary in print mode (not the two-token `cursor agent`, which the IDE treats as a file path), and the review prompt is passed as a file-path argument rather than piped to stdin (which `cursor-agent -p` ignores). On failure the captured stderr is surfaced instead of a silent empty result.
|
||||
@@ -22,7 +22,7 @@ command -v codex >/dev/null 2>&1 && echo "codex:available" || echo "codex:missin
|
||||
command -v coderabbit >/dev/null 2>&1 && echo "coderabbit:available" || echo "coderabbit:missing"
|
||||
command -v opencode >/dev/null 2>&1 && echo "opencode:available" || echo "opencode:missing"
|
||||
command -v qwen >/dev/null 2>&1 && echo "qwen:available" || echo "qwen:missing"
|
||||
command -v cursor >/dev/null 2>&1 && echo "cursor:available" || echo "cursor:missing"
|
||||
command -v cursor-agent >/dev/null 2>&1 && echo "cursor:available" || echo "cursor:missing"
|
||||
command -v agy >/dev/null 2>&1 && echo "antigravity:available" || echo "antigravity:missing"
|
||||
|
||||
# Check local model servers (OpenAI-compatible HTTP API — no CLI binary required)
|
||||
@@ -284,9 +284,15 @@ fi
|
||||
|
||||
**Cursor:**
|
||||
```bash
|
||||
cat /tmp/gsd-review-prompt-{phase}.md | cursor agent -p --mode ask --trust 2>/dev/null > /tmp/gsd-review-cursor-{phase}.md
|
||||
# cursor-agent is a SEPARATE binary from the `cursor` IDE launcher; print mode (-p) takes the
|
||||
# prompt as an ARGUMENT, not stdin. A full review prompt can exceed the OS argument limit, so
|
||||
# reference the prompt file by path rather than inlining it. Capture stderr so a failure is
|
||||
# diagnosable instead of a silent empty result.
|
||||
CURSOR_PROMPT_ARG="Read the file at /tmp/gsd-review-prompt-{phase}.md in full and carry out the review request it contains. Output only the resulting markdown review. Do not edit any files."
|
||||
cursor-agent -p --mode ask --trust --output-format text "$CURSOR_PROMPT_ARG" 2>/tmp/gsd-review-cursor-{phase}.err > /tmp/gsd-review-cursor-{phase}.md
|
||||
if [ ! -s /tmp/gsd-review-cursor-{phase}.md ]; then
|
||||
echo "Cursor review failed or returned empty output." > /tmp/gsd-review-cursor-{phase}.md
|
||||
echo "Cursor review failed or returned empty output. stderr:" > /tmp/gsd-review-cursor-{phase}.md
|
||||
cat /tmp/gsd-review-cursor-{phase}.err >> /tmp/gsd-review-cursor-{phase}.md
|
||||
fi
|
||||
```
|
||||
|
||||
|
||||
@@ -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'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user