feat(targets): converge windsurf skills onto .agents/skills/#1802
feat(targets): converge windsurf skills onto .agents/skills/#1802danielmeppiel wants to merge 5 commits into
Conversation
Add deploy_root=".agents" to the windsurf skills PrimitiveMapping so skills deploy to the cross-tool .agents/skills/<name>/SKILL.md path, mirroring copilot/cursor/codex/gemini/opencode. Windsurf (now Devin Desktop) was a laggard still on harness-native .windsurf/skills/. Back-compat is preserved by the existing apply_legacy_skill_paths() escape hatch (--legacy-skill-paths flag / APM_LEGACY_SKILL_PATHS=1), which is data-driven and auto-covers windsurf: legacy mode restores .windsurf/skills/. Rules, workflows, and hooks stay under .windsurf/. Scope is bounded to the skills primitive only; no target rename or devin alias (separate deferred issue). Tests: data-driven default-routing assertion extended to windsurf, plus a legacy-restore regression test (both compare Path.parts, not substrings). Windsurf uninstall/partition tests migrated to the new path. Docs (skills.md, targets-matrix.md, manifest-schema.md), target_detection compile-output description, and CHANGELOG updated. Refs #1520 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the windsurf target so the skills primitive deploys to the cross-tool .agents/skills/<name>/SKILL.md path (via deploy_root=".agents"), aligning windsurf/Devin Desktop with the existing multi-target skills convergence and keeping the existing legacy escape hatch.
Changes:
- Set windsurf
skillsmapping to deploy under.agents/(leaving other windsurf primitives under.windsurf/). - Update target description output and adjust/unblock uninstall+partition tests to use the new
.agents/skills/location. - Refresh docs and add a CHANGELOG entry documenting the breaking path change and opt-out.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/integration/test_windsurf_uninstall_skills.py | Migrates windsurf uninstall regression tests to validate cleanup under .agents/skills/ and asserts deploy_root=".agents". |
| tests/unit/integration/test_targets.py | Extends convergence/legacy-routing assertions to include windsurf; adds windsurf-specific routing tests. |
| tests/unit/integration/test_scope_integration.py | Updates a windsurf comment to reflect the converged .agents/skills/ location. |
| tests/unit/integration/test_data_driven_dispatch.py | Updates a comment describing windsurf’s skills deploy path. |
| src/apm_cli/integration/targets.py | Adds deploy_root=".agents" to windsurf’s skills mapping and updates the surrounding target comment block. |
| src/apm_cli/core/target_detection.py | Updates the windsurf target description string to mention .agents/skills/. |
| docs/src/content/docs/reference/targets-matrix.md | Documents windsurf as using both .windsurf/ and .agents/ and updates the skills path. |
| docs/src/content/docs/reference/manifest-schema.md | Updates the windsurf row to list .agents/skills/ instead of .windsurf/skills/. |
| docs/src/content/docs/producer/author-primitives/skills.md | Updates the skills routing table and narrative to include windsurf in .agents/skills/ convergence. |
| CHANGELOG.md | Adds an Unreleased entry documenting the windsurf skills deploy path change and legacy opt-out. |
Copilot's findings
- Files reviewed: 10/10 changed files
- Comments generated: 1
|
|
||
| - **Detection.** `.windsurf/` directory. | ||
| - **Deploy directory.** `.windsurf/` at project scope; `~/.codeium/windsurf/` at user scope. | ||
| - **Deploy directory.** `.windsurf/` plus `.agents/` for skills at project scope; `~/.codeium/windsurf/` at user scope. |
…both scopes Fold of copilot-pull-request-reviewer[bot] finding on PR #1802: the windsurf Deploy-directory bullet attached ".agents/ for skills" to "at project scope", wrongly implying the skills convergence was project-scope-only. At user scope deploy_root=".agents" resolves to ~/.agents/skills/ (get_deploy_root(USER) -> Path.home(); skill_integrator effective_root = deploy_root or root_dir). Restate so skills converge on .agents/skills/ at both scopes while native primitives stay under .windsurf/ (project) / ~/.codeium/windsurf/ (user). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
shepherd-driver advisory (PR #1802)Advisory only -- no verdict label applied, no merge gating. Surfaced for the maintainer to weigh. Folded in this run
Copilot signals reviewed
Copilot round 2 (post-fold head Deferred (out-of-scope follow-ups)
Test coverageProportionate review for a one-line Lint contract
CIAll required checks green (Lint, Build & Test Shard 1 + 2, Coverage Combine, APM Self-Check, Spec conformance gate, CodeQL, PR Binary Smoke, NOTICE Drift, build, gate, license/cla); Mergeability status
Convergence1 outer iteration; 2 Copilot rounds (round 2 drained). Ready for maintainer review. |
… entry The windsurf skills convergence intentionally leaves _CROSS_TARGET_MAPS["windsurf"] pointing at .windsurf/skills/ (matching the cursor/opencode precedent). Note the tracked follow-up #1805 so the pack-vs-deploy drift reads as a sequenced, deliberate defer rather than an oversight. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…f-skills-converge-agents-path # Conflicts: # CHANGELOG.md # docs/src/content/docs/reference/manifest-schema.md # docs/src/content/docs/reference/targets-matrix.md # src/apm_cli/core/target_detection.py
Conflict resolution: merged current mainMerged current
Lint contract silent post-merge (full CI-mirror chain: Post-push mergeability: Ready for maintainer review. |
TL;DR
Windsurf (now Devin Desktop) was the last active harness still deploying skills to its harness-native
.windsurf/skills/path while five peers — Copilot, Cursor, Codex, Gemini, OpenCode — already converged onto the cross-tool.agents/skills/<name>/SKILL.mdpath. This PR addsdeploy_root=".agents"to windsurf'sskillsmapping so it joins the converged set. Back-compat is preserved by the existing--legacy-skill-pathsescape hatch, which is data-driven and already covers windsurf. Refs #1520.Warning
This is a breaking path change for windsurf skills. Existing
.windsurf/skills/deployments are orphaned (not auto-migrated) on nextapm install. Opt out with--legacy-skill-pathsorAPM_LEGACY_SKILL_PATHS=1to keep the old layout — identical to the back-compat contract the five earlier harnesses already shipped.Problem (WHY)
APM deploys each primitive to per-harness on-disk locations defined by
TargetProfiles. The.agents/directory is the emerging cross-tool convention for agent skills, and APM has been moving harnesses onto it one at a time.skillsPrimitiveMappinghad nodeploy_root, so it fell back toroot_dir=".windsurf"and deployed to.windsurf/skills/— out of step with the converged majority.deploy_root=".agents". Windsurf and Kiro were the laggards. This issue moves windsurf only; Kiro stays the holdout, out of scope..agents/skills/, so convergence also realigns APM with the harness vendor's own docs.Approach (WHAT)
deploy_root=".agents"to windsurf'sskillsmapping (mirrors copilot reference).codeium/windsurf/user dir untouchedpack_prefixesdeploy_root; only codex addspack_prefixes. Mirror the 4-peer majorityapply_legacy_skill_paths(); no extension needed (it iterates all profiles)devinaliasImplementation (HOW)
src/apm_cli/integration/targets.py— windsurfskillsmapping gainsdeploy_root=".agents"(multi-line form matching the copilot reference). Comment block updated to state skills now land under.agents/skills/and that windsurf is now Devin Desktop; rules/workflows/hooks stay under.windsurf/.src/apm_cli/core/target_detection.py— theapm compile -t windsurfoutput-description string updated from.windsurf/skills/to.agents/skills/so the printed path matches reality (no test guarded this string; matches the codex line).tests/unit/integration/test_targets.py— data-driven default-routing assertion extended with"windsurf": ".agents"; legacy-restore list extended to 7 profiles; two new windsurf tests (default.agents/skills/, legacy.windsurf/skills/) usingPath.partscomparison.tests/unit/integration/test_windsurf_uninstall_skills.py— partition/cleanup paths migrated to.agents/skills/; added adeploy_root == ".agents"shape assertion. (See trade-off below — this file was an unanticipated but correct consequence of the path move.)tests/unit/integration/{test_data_driven_dispatch,test_scope_integration}.py— stale path comments refreshed.skills.md,targets-matrix.md,manifest-schema.md, and a[Unreleased] ### Changedentry.Diagram
Legend: how a windsurf skill resolves its deploy directory by default (converged) vs under the legacy escape hatch.
flowchart LR S["windsurf skills PrimitiveMapping"] --> Q{"deploy_root set?"} Q -->|"default: deploy_root='.agents'"| A[".agents/skills/name/SKILL.md"] Q -->|"--legacy-skill-paths resets to None"| W[".windsurf/skills/name/SKILL.md"] A --> C["converged with copilot, cursor, codex, gemini, opencode"] W --> L["per-client back-compat layout"]Trade-offs
.windsurf/skills/lockfile entries no longer route after the change (uninstall partitioning keys off the livedeploy_root). This is the same accepted tradeoff the five earlier harnesses shipped;--legacy-skill-pathsis the sanctioned remedy.test_windsurf_uninstall_skills.pywas migrated, not preserved at the old path. The pre-implementation plan assumed it was unaffected; in factpartition_managed_filesbuilds skill prefixes from the livedeploy_root or root_dir, so the test had to move to.agents/skills/. The [BUG] uninstall: skill directories survive cleanup on windsurf target #1481 regression intent (windsurf skill cleanup) is preserved at the new path.lockfile_enrichment.py_CROSS_TARGET_MAPS["windsurf"]left as-is (deferred). This pack-time cross-target map still points at.windsurf/skills/. It was likewise not updated when cursor/opencode converged (they still map to.cursor/skills//.opencode/skills/; only codex maps to.agents/skills/). Following that precedent keeps this change bounded; the pack-vs-deploy drift already exists identically for cursor/opencode and belongs in a separate sweep, tracked in Sweep lockfile cross-target skill maps onto.agents/skills/for converged targets (windsurf/cursor/opencode) #1805.Benefits
.agents/skills/path as five other harnesses — one fewer special case in the routing matrix.deploy_rootplus a regression test pinning the legacy restore.Validation
Lint chain (CI mirror) and the full unit suite are green on the branch tip.
Lint chain — all silent / exit 0
Full unit suite
Scenario evidence
.agents/skills/path by defaulttest_targets.py::TestDefaultSkillRouting::test_windsurf_skill_routing_uses_agents_dir_by_default--legacy-skill-pathsrestores windsurf to.windsurf/skills/test_targets.py::TestDefaultSkillRouting::test_windsurf_legacy_skill_paths_restores_per_client_routingtest_windsurf_uninstall_skills.py(migrated; #1481 regression intent)How to test
uv run --extra dev python -m pytest tests/unit/integration/test_targets.py -q— routing + legacy-restore tests pass.uv run --extra dev python -m pytest tests/unit/integration/test_windsurf_uninstall_skills.py -q— migrated uninstall tests pass.apm install --target windsurfand confirm the skill lands at.agents/skills/<name>/SKILL.md.APM_LEGACY_SKILL_PATHS=1 apm install --target windsurfand confirm it lands at.windsurf/skills/<name>/SKILL.md.Refs #1520
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com