Skip to content

feat(agent): add windsurf as an install target#29

Merged
zeshi-du merged 1 commit into
TestSprite:mainfrom
Davidson3556:feat/add-windsurf-target
Jul 5, 2026
Merged

feat(agent): add windsurf as an install target#29
zeshi-du merged 1 commit into
TestSprite:mainfrom
Davidson3556:feat/add-windsurf-target

Conversation

@Davidson3556

@Davidson3556 Davidson3556 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Fixes #138

Describe the changes you have made in this PR -

Adds Windsurf (Codeium's Cascade editor) as an agent install target, so testsprite agent install --target windsurf and testsprite setup --agent windsurf wire the verification-loop skill into a Windsurf project.

  • New own-file target windsurf.windsurf/rules/testsprite-verify.md.
  • Cascade frontmatter trigger: model_decision + description: — the equivalent of the Cursor .mdc alwaysApply: false mode (only the description is surfaced up front; Cascade pulls in the full rule body when the description shows it is relevant), which matches how this skill should activate.
  • Budget handling: a .windsurf/rules/*.md file is capped at ~12 K characters and Cascade silently truncates beyond it. The full skill body (~21 KB) would be cut in half, so the windsurf target renders the compact body — the same trimmed variant the Codex/AGENTS.md target already ships (~5 KB) — selected via a new compactBody flag on the target spec. The rendered file is ~5.5 KB: well within budget, and it still carries the H1, the when-to-run guidance, and the load-bearing testsprite test run … --wait / test artifact get commands. agent.ts and renderForTarget choose the same body so the installed bytes equal the asserted render.
  • Everything else derives from the TARGETS map automatically (the agent list table, the setup --agent choices, skill-nudge install detection). Updated the two hardcoded help strings in agent.ts, the --help snapshot, the unit tests (render test for the Cascade frontmatter + a budget test asserting the windsurf render stays under 12 K and uses the compact body), the e2e matrix guards / multi-target / content-integrity coverage, and the README / DOCUMENTATION target lists.

Demo/Screenshot for feature changes and bug fixes -

image

Tests:

npm test          
image
npm run test:e2e  # e2e  
image
npm run typecheck && npm run lint && npm run format:check
image

Code Understanding and AI Usage

Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?

  • No, I wrote all the code myself
  • Yes, I used AI assistance (continue below)

If you used AI assistance:

  • I have reviewed every single line of the AI-generated code
  • I can explain the purpose and logic of each function/component I added
  • I have tested edge cases and understand how the code handles them
  • I have modified the AI output to follow this project's coding standards and conventions

Explain your implementation approach:

Problem: TestSprite's value is wiring the verify skill into coding agents, but Windsurf — a top-tier AI editor — wasn't a supported target.

The target system is a clean table (TARGETS in agent-targets.ts) with a per-target wrap() for the file's frontmatter, so the core addition is one entry plus a wrap function. I chose trigger: model_decision after checking Windsurf's rules docs: its semantics match the existing Cursor target (alwaysApply: false) — surface the description, load the body on demand — rather than always_on (injects the whole skill into every prompt) or manual (needs an @-mention, defeating an auto-verify skill).

The non-obvious part is the size budget. Windsurf caps a .windsurf/rules/*.md file at ~12 K characters and truncates silently beyond it; the full skill body renders to ~22 KB, so half the skill would be lost. The repo already maintains a trimmed/compact body for the Codex/AGENTS.md target, which is agent-agnostic (no Codex- or AGENTS.md-specific wording), so I reused it for Windsurf via a compactBody flag rather than authoring a third asset. Both the renderForTarget default and the agent.ts install path branch on this flag (loading each source body at most once), so what gets written equals what the unit test renders.

Alternatives considered: (1) reuse wrapMdc (Cursor's wrapper) — rejected, Cursor's description/alwaysApply keys are not Windsurf's trigger/description; (2) ship the full body and just emit a budget warning like the Codex target — rejected, because unlike Codex (whose body fits its 32 K budget) the full body always exceeds Windsurf's cap, so it would warn on every install and still truncate; (3) the legacy single-file .windsurfrules — rejected in favour of the current .windsurf/rules/*.md directory format.

Edge cases handled/tested: a render test asserts the Windsurf output carries trigger: model_decision + description: and not the name:/alwaysApply: keys; a budget test asserts the real windsurf render is < 12 K characters, uses the compact body (a full-body-only sentence is absent) yet keeps the load-bearing commands, and is smaller than the full claude render; the "install all own-file targets" unit test derives its list from OWN_FILE_TARGETS; and the e2e content-integrity loop gates the full-body assertion on non-compact targets while adding a Windsurf frontmatter branch.


Checklist before requesting a review

  • I have added proper PR title and linked to the issue
  • I have performed a self-review of my code
  • I can explain the purpose of every function, class, and logic block I added
  • I understand why my changes work and have tested them thoroughly
  • I have considered potential edge cases and how my code handles them
  • If it is a core feature, I have added thorough tests
  • My code follows the project's style guidelines and conventions

Note: Please check Allow edits from maintainers if you would like us to assist in the PR.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added support for the additional agent target windsurf (marked experimental), including setup, install, and list command coverage.
  • Bug Fixes

    • Improved generated rule rendering for windsurf to match Windsurf-compatible frontmatter (and apply a compact/trimmed skill body where supported).
  • Documentation

    • Updated onboarding and README quickstart/commands references to include windsurf, including updated --force backup/restore behavior descriptions for own-file targets.
  • Tests

    • Expanded unit and end-to-end test coverage to include windsurf, including content-integrity and rendering assertions.

@Davidson3556 Davidson3556 force-pushed the feat/add-windsurf-target branch from 4e02451 to eea1d02 Compare June 25, 2026 02:34
@zeshi-du

Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR adds windsurf as a new agent install target. It updates the target contract and rendering path, wires the CLI and help text, revises documentation, and extends unit and e2e tests to cover the new target and expanded target set.

Changes

Windsurf target support

Layer / File(s) Summary
Windsurf target contract and rendering logic
src/lib/agent-targets.ts, src/lib/agent-targets.test.ts
Adds windsurf to the target types, defines its own-file path and frontmatter wrapper, registers compact-body rendering, updates body selection, and extends unit tests for path, frontmatter, size, and body selection.
CLI install wiring and help text
src/commands/agent.ts, src/commands/agent.test.ts
Updates own-file rendering to choose compact or full bodies, adds lazy caching for compact bodies, updates command help text, and adjusts tests for the expanded target set and install coverage.
Documentation updates
README.md, DOCUMENTATION.md
Updates setup guidance, command references, supported targets, and agent install/agent list examples to include windsurf.
E2E test coverage for windsurf
test/e2e/agent-install.e2e.test.ts, test/e2e/setup.e2e.test.ts
Updates e2e coverage for windsurf frontmatter checks, compact-body branding behavior, and matrix guards in the agent-install and setup suites.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

Suggested reviewers: zeshi-du, ruili-testsprite

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding Windsurf as an install target.
Linked Issues check ✅ Passed The changes add the Windsurf own-file target, Cascade frontmatter, compact-body handling, and target wiring required by the linked issue.
Out of Scope Changes check ✅ Passed The PR stays focused on Windsurf target support and related docs/tests, with no clear unrelated code changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
DOCUMENTATION.md (1)

122-125: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Own-file target list at Line 124 is missing windsurf.

This PR adds windsurf as an own-file target (TARGETS.windsurf.mode === 'own-file'), but the --force backup-behavior callout still only lists claude, cursor, cline, antigravity. Windsurf is subject to the same backup-on---force behavior and should be included.

📝 Proposed fix
-Re-running with `--force` on **own-file targets** (claude, cursor, cline, antigravity) backs up the existing file to `<path>.bak` first.
+Re-running with `--force` on **own-file targets** (claude, cursor, cline, windsurf, antigravity) backs up the existing file to `<path>.bak` first.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DOCUMENTATION.md` around lines 122 - 125, The own-file target backup note is
incomplete because `windsurf` is now an own-file target too. Update the
`DOCUMENTATION.md` guidance around the `--force` behavior so the own-file target
list explicitly includes `windsurf` alongside `claude`, `cursor`, `cline`, and
`antigravity`, keeping the backup-on-`--force` description consistent with
`TARGETS.windsurf` and the other own-file targets.
🧹 Nitpick comments (1)
test/e2e/agent-install.e2e.test.ts (1)

164-168: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Comment claims "description" is verified, but assertion only checks trigger.

The comment says // Windsurf Cascade frontmatter: trigger + description (no name/alwaysApply), but no assertion verifies a description: field is present, even though the PR requirement explicitly calls for description: <skill description> in the frontmatter.

✏️ Suggested addition
       } else if (target === 'windsurf') {
         // Windsurf Cascade frontmatter: trigger + description (no name/alwaysApply)
         expect(content.startsWith('---'), `windsurf: should start with ---`).toBe(true);
         expect(content).toContain('trigger: model_decision');
+        expect(content).toContain('description:');
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/agent-install.e2e.test.ts` around lines 164 - 168, The Windsurf
frontmatter check in the e2e test only verifies trigger and does not assert the
required description field, so update the `agent-install.e2e.test.ts`
expectations in the `target === 'windsurf'` branch to confirm the generated
content includes a `description:` entry with the skill description. Keep the
existing `trigger: model_decision` assertion and add a matching check for the
`description` field so the test aligns with the `Windsurf Cascade frontmatter`
requirement.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@DOCUMENTATION.md`:
- Around line 122-125: The own-file target backup note is incomplete because
`windsurf` is now an own-file target too. Update the `DOCUMENTATION.md` guidance
around the `--force` behavior so the own-file target list explicitly includes
`windsurf` alongside `claude`, `cursor`, `cline`, and `antigravity`, keeping the
backup-on-`--force` description consistent with `TARGETS.windsurf` and the other
own-file targets.

---

Nitpick comments:
In `@test/e2e/agent-install.e2e.test.ts`:
- Around line 164-168: The Windsurf frontmatter check in the e2e test only
verifies trigger and does not assert the required description field, so update
the `agent-install.e2e.test.ts` expectations in the `target === 'windsurf'`
branch to confirm the generated content includes a `description:` entry with the
skill description. Keep the existing `trigger: model_decision` assertion and add
a matching check for the `description` field so the test aligns with the
`Windsurf Cascade frontmatter` requirement.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a5aca309-64c3-4edb-b169-8b9c5234b940

📥 Commits

Reviewing files that changed from the base of the PR and between 18f6e6e and eea1d02.

⛔ Files ignored due to path filters (1)
  • test/__snapshots__/help.snapshot.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (8)
  • DOCUMENTATION.md
  • README.md
  • src/commands/agent.test.ts
  • src/commands/agent.ts
  • src/lib/agent-targets.test.ts
  • src/lib/agent-targets.ts
  • test/e2e/agent-install.e2e.test.ts
  • test/e2e/setup.e2e.test.ts

@Davidson3556

Copy link
Copy Markdown
Contributor Author

Reworked this onto the v0.2.0 multi-skill agent-targets API (pathFor/SKILLS/DEFAULT_SKILLS) to clear the merge conflict, and addressed the CodeRabbit feedback:

  • DOCUMENTATION.md --force note: added windsurf to the own-file target list.
  • e2e frontmatter check: now asserts the description: field (not just trigger:) for the Windsurf rule files, for both testsprite-verify and testsprite-onboard.

Note on the v0.2.0 budget handling: own-file targets now install multiple skills as separate files, and Windsurf rules files cap at ~12 K characters. So Windsurf renders the compact body per skill — the trimmed codex asset for testsprite-verify (~5 KB; the full body is ~22 KB and would be truncated) and the full body for testsprite-onboard (~6.5 KB, already fits). Verified both land under 12 K (5,915 and 7,122 chars). All gates green locally (typecheck, lint, format, unit 1576, e2e 49).

@zeshi-du

zeshi-du commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Target policy settled: windsurf is accepted (see #28). This will get review in the next wave.

@zeshi-du

zeshi-du commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Approved — no changes requested on substance. This went CONFLICTING purely because today's merge wave landed a large batch into the same files first. Rebase onto current main (and re-run npm test if the help snapshot moved), push, and this merges.

Windsurf (Cascade) reads workspace rules from `.windsurf/rules/*.md`. Add it
as an own-file agent target so `testsprite agent install --target windsurf`
(and `setup --agent windsurf`) installs the TestSprite skills into a Windsurf
project. Reworked onto the v0.2.0 multi-skill agent-targets API (pathFor /
SKILLS / DEFAULT_SKILLS).

Rule files use Cascade frontmatter with `trigger: model_decision` — the
equivalent of the Cursor `.mdc` `alwaysApply: false` mode (description shown
up front; full body pulled in on relevance).

Budget handling: a `.windsurf/rules/*.md` file caps at ~12 K characters and
Cascade silently truncates beyond it, which would cut the full ~22 KB verify
skill in half. The windsurf target therefore renders the COMPACT body per
skill (new `compactBody` flag + `compactBodyFor`): a skill that ships a
trimmed codex asset (`testsprite-verify` → ~5 KB) uses it, while a skill whose
codex contribution is only a one-liner (`testsprite-onboard`, ~6.5 KB full)
keeps its full body — both land well under the cap. `agent.ts` and
`renderForTarget` select the same body so installed bytes match the render.

Everything else derives from the TARGETS map automatically (agent list, the
setup --agent choices, skill-nudge install detection). Updated the hardcoded
help strings, the --help snapshot, the agent-targets/agent unit tests (incl.
Cascade-frontmatter and per-skill budget tests), the e2e matrix guards /
content-integrity (gated on compactBody), and the README/DOCUMENTATION target
lists (incl. the --force own-file list).
@Davidson3556 Davidson3556 force-pushed the feat/add-windsurf-target branch from 3f851ab to a56f783 Compare July 5, 2026 20:06
@Davidson3556

Copy link
Copy Markdown
Contributor Author

Rebased onto latest main and resolved the conflicts from the kiro target + the new content-integrity marker mechanism (renderOwnFileWithMarker/buildSkillMarker). Windsurf now sits alongside kiro in TARGETS, pathFor, the help/docs, and both e2e matrix guards, and its compact body still renders through the marker path (verified: .windsurf/rules/testsprite-verify.md = ~6 KB, under the ~12 KB Cascade budget, with trigger: model_decision frontmatter). All gates green locally: typecheck, lint, format:check, 1772 unit tests, 52 e2e.

@Davidson3556

Copy link
Copy Markdown
Contributor Author

@zeshi-du kindly review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/commands/agent.test.ts (1)

952-978: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Hardcoded target list can silently drift from OWN_FILE_TARGETS.

Line 934's sibling test already switched to [...OWN_FILE_TARGETS], but this dry-run test still hardcodes ['claude', 'cursor', 'cline', 'antigravity', 'kiro', 'windsurf'] and the "12 would-write lines" / "six own-file targets" comments. If a new own-file target is added later, this test won't pick it up automatically and will keep passing with a stale count instead of catching the regression.

♻️ Suggested fix for consistency with the runInstall test above
-        target: ['claude', 'cursor', 'cline', 'antigravity', 'kiro', 'windsurf'],
+        target: [...OWN_FILE_TARGETS],
-    // 6 targets × 2 default skills = 12 would-write lines
-    const wouldWriteLines = stderrOut.split('\n').filter(l => l.includes('would write'));
-    expect(wouldWriteLines.length).toBe(12);
+    // OWN_FILE_TARGETS.length targets × 2 default skills = would-write lines
+    const wouldWriteLines = stderrOut.split('\n').filter(l => l.includes('would write'));
+    expect(wouldWriteLines.length).toBe(OWN_FILE_TARGETS.length * DEFAULT_SKILLS.length);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/agent.test.ts` around lines 952 - 978, The dry-run test is
hardcoding the own-file target list and expected count, which can drift from
OWN_FILE_TARGETS over time. Update the runInstall dry-run test to derive the
target array from OWN_FILE_TARGETS (as the sibling test does) and compute the
expected “would write” line count from that source so it stays aligned when
targets change. Use the existing runInstall and OWN_FILE_TARGETS symbols to keep
the test consistent and future-proof.
src/lib/agent-targets.ts (1)

140-150: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Unescaped description in YAML frontmatter.

wrapWindsurf interpolates description directly into an unquoted YAML scalar (description: ${description}). If a skill description ever contains a colon-space, leading special char, or newline, this would produce invalid YAML that Cascade's parser may reject or mis-parse. This mirrors existing wrap helpers for other targets, so it's a pre-existing pattern rather than new, but worth confirming skill descriptions are guaranteed plain and colon-free (or add quoting) so .windsurf/rules/*.md frontmatter stays valid as skill content evolves.

Optional: quote the YAML value defensively
-  return `---\ntrigger: model_decision\ndescription: ${description}\n---\n\n${body}\n`;
+  return `---\ntrigger: model_decision\ndescription: ${JSON.stringify(description)}\n---\n\n${body}\n`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/agent-targets.ts` around lines 140 - 150, The Windsurf frontmatter
builder in wrapWindsurf interpolates description into an unquoted YAML scalar,
so make it safe for any description content. Update wrapWindsurf to defensively
quote or escape the description before emitting the .windsurf/rules/*.md
frontmatter, keeping the YAML valid even if the text contains colons, special
characters, or newlines.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/commands/agent.test.ts`:
- Around line 952-978: The dry-run test is hardcoding the own-file target list
and expected count, which can drift from OWN_FILE_TARGETS over time. Update the
runInstall dry-run test to derive the target array from OWN_FILE_TARGETS (as the
sibling test does) and compute the expected “would write” line count from that
source so it stays aligned when targets change. Use the existing runInstall and
OWN_FILE_TARGETS symbols to keep the test consistent and future-proof.

In `@src/lib/agent-targets.ts`:
- Around line 140-150: The Windsurf frontmatter builder in wrapWindsurf
interpolates description into an unquoted YAML scalar, so make it safe for any
description content. Update wrapWindsurf to defensively quote or escape the
description before emitting the .windsurf/rules/*.md frontmatter, keeping the
YAML valid even if the text contains colons, special characters, or newlines.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f6ebfce0-17f1-431d-8e60-11e363cdc4af

📥 Commits

Reviewing files that changed from the base of the PR and between 3f851ab and a56f783.

⛔ Files ignored due to path filters (1)
  • test/__snapshots__/help.snapshot.test.ts.snap is excluded by !**/*.snap, !**/*.snap
📒 Files selected for processing (8)
  • DOCUMENTATION.md
  • README.md
  • src/commands/agent.test.ts
  • src/commands/agent.ts
  • src/lib/agent-targets.test.ts
  • src/lib/agent-targets.ts
  • test/e2e/agent-install.e2e.test.ts
  • test/e2e/setup.e2e.test.ts
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/e2e/agent-install.e2e.test.ts
  • test/e2e/setup.e2e.test.ts
  • src/commands/agent.ts

@zeshi-du zeshi-du merged commit 8eb4acf into TestSprite:main Jul 5, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Hackathon] Add Windsurf (Cascade) as an \agent install target

2 participants