fix(skills): address QA round 6
QA6-1: Add 16 matchPacksForProject unit tests covering language match, file match, Xcode platform match, matchAlways, and isolation checks (FastAPI not in generic Python, Spring Boot not via language alone, Unity/Godot don't cross-contaminate). QA6-2: Remove FastAPI from brownfield auto-detection entirely — no matchFiles or matchLanguages. Available via greenfield stack selection or manual install only, since no FastAPI-specific file marker exists on disk. QA6-3: Remove matchLanguages from Java & Spring Boot pack so it only triggers via matchFiles (pom.xml, build.gradle*). Prevents Android projects from getting Spring Boot recommended via language match. QA6-4: False positive — F# gets 'csharp' label cosmetically but the .NET packs are correct for F# developers.
This commit is contained in:
parent
84657d0398
commit
064c9ce421
2 changed files with 143 additions and 2 deletions
|
|
@ -306,7 +306,6 @@ export const SKILL_CATALOG: SkillPack[] = [
|
|||
description: "Spring Boot best practices, DI, RESTful APIs, JPA, testing, and security",
|
||||
repo: "github/awesome-copilot",
|
||||
skills: ["java-springboot"],
|
||||
matchLanguages: ["java", "java/kotlin"],
|
||||
matchFiles: ["pom.xml", "build.gradle", "build.gradle.kts"],
|
||||
},
|
||||
// ── .NET / C# ────────────────────────────────────────────────────────────
|
||||
|
|
@ -395,12 +394,14 @@ export const SKILL_CATALOG: SkillPack[] = [
|
|||
matchLanguages: ["python"],
|
||||
matchFiles: ["pyproject.toml", "setup.py", "requirements.txt"],
|
||||
},
|
||||
// FastAPI — no brownfield auto-detection (generic Python markers can't
|
||||
// distinguish FastAPI from other frameworks). Available via greenfield
|
||||
// stack selection or manual install: npx skills add wshobson/agents --skill fastapi-templates
|
||||
{
|
||||
label: "FastAPI",
|
||||
description: "Production-ready FastAPI projects with async patterns and error handling",
|
||||
repo: "wshobson/agents",
|
||||
skills: ["fastapi-templates"],
|
||||
matchFiles: ["pyproject.toml", "setup.py", "requirements.txt"],
|
||||
},
|
||||
// ── Go ────────────────────────────────────────────────────────────────────
|
||||
{
|
||||
|
|
|
|||
140
src/resources/extensions/gsd/tests/skill-catalog.test.ts
Normal file
140
src/resources/extensions/gsd/tests/skill-catalog.test.ts
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
/**
|
||||
* Unit tests for GSD Skill Catalog — pack matching logic.
|
||||
*
|
||||
* Exercises matchPacksForProject() to verify that project signals
|
||||
* correctly map to skill packs.
|
||||
*/
|
||||
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { matchPacksForProject } from "../skill-catalog.ts";
|
||||
import type { ProjectSignals } from "../detection.ts";
|
||||
|
||||
function makeSignals(overrides: Partial<ProjectSignals> = {}): ProjectSignals {
|
||||
return {
|
||||
detectedFiles: [],
|
||||
isGitRepo: false,
|
||||
isMonorepo: false,
|
||||
xcodePlatforms: [],
|
||||
hasCI: false,
|
||||
hasTests: false,
|
||||
verificationCommands: [],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function packLabels(signals: ProjectSignals): string[] {
|
||||
return matchPacksForProject(signals).map((p) => p.label);
|
||||
}
|
||||
|
||||
// ── matchAlways packs are always included ────────────────────────────────────
|
||||
|
||||
test("matchPacksForProject: always includes matchAlways packs", () => {
|
||||
const labels = packLabels(makeSignals());
|
||||
assert.ok(labels.includes("Skill Discovery"), "should include Skill Discovery");
|
||||
assert.ok(labels.includes("Skill Authoring"), "should include Skill Authoring");
|
||||
assert.ok(labels.includes("Browser Automation"), "should include Browser Automation");
|
||||
assert.ok(labels.includes("Document Handling"), "should include Document Handling");
|
||||
assert.ok(labels.includes("Code Review & Quality"), "should include Code Review & Quality");
|
||||
assert.ok(labels.includes("Git Advanced Workflows"), "should include Git Advanced Workflows");
|
||||
});
|
||||
|
||||
// ── Language matching ────────────────────────────────────────────────────────
|
||||
|
||||
test("matchPacksForProject: Python language matches Python packs", () => {
|
||||
const labels = packLabels(makeSignals({ primaryLanguage: "python", detectedFiles: ["pyproject.toml"] }));
|
||||
assert.ok(labels.includes("Python"), "should include Python");
|
||||
assert.ok(labels.includes("Python Advanced"), "should include Python Advanced");
|
||||
});
|
||||
|
||||
test("matchPacksForProject: Rust language matches Rust packs", () => {
|
||||
const labels = packLabels(makeSignals({ primaryLanguage: "rust", detectedFiles: ["Cargo.toml"] }));
|
||||
assert.ok(labels.includes("Rust"), "should include Rust");
|
||||
assert.ok(labels.includes("Rust Async Patterns"), "should include Rust Async Patterns");
|
||||
});
|
||||
|
||||
test("matchPacksForProject: Go language matches Go packs", () => {
|
||||
const labels = packLabels(makeSignals({ primaryLanguage: "go", detectedFiles: ["go.mod"] }));
|
||||
assert.ok(labels.includes("Go"), "should include Go");
|
||||
assert.ok(labels.includes("Go Concurrency Patterns"), "should include Go Concurrency Patterns");
|
||||
});
|
||||
|
||||
test("matchPacksForProject: JS/TS matches web frontend packs", () => {
|
||||
const labels = packLabels(makeSignals({ primaryLanguage: "javascript/typescript", detectedFiles: ["package.json"] }));
|
||||
assert.ok(labels.includes("React & Web Frontend"), "should include React");
|
||||
assert.ok(labels.includes("TypeScript & JS Development"), "should include TS/JS Dev");
|
||||
assert.ok(labels.includes("React State & Patterns"), "should include React State");
|
||||
assert.ok(labels.includes("shadcn/ui"), "should include shadcn");
|
||||
assert.ok(labels.includes("Frontend Design & UX"), "should include Frontend Design");
|
||||
});
|
||||
|
||||
// ── File matching ────────────────────────────────────────────────────────────
|
||||
|
||||
test("matchPacksForProject: angular.json triggers Angular packs", () => {
|
||||
const labels = packLabels(makeSignals({ detectedFiles: ["angular.json"] }));
|
||||
assert.ok(labels.includes("Angular"), "should include Angular");
|
||||
assert.ok(labels.includes("Angular Migration"), "should include Angular Migration");
|
||||
});
|
||||
|
||||
test("matchPacksForProject: next.config.ts triggers Next.js packs", () => {
|
||||
const labels = packLabels(makeSignals({ detectedFiles: ["next.config.ts"] }));
|
||||
assert.ok(labels.includes("Next.js"), "should include Next.js");
|
||||
assert.ok(labels.includes("Next.js App Router Patterns"), "should include Next.js App Router");
|
||||
});
|
||||
|
||||
test("matchPacksForProject: *.vue triggers Vue.js", () => {
|
||||
const labels = packLabels(makeSignals({ detectedFiles: ["*.vue"] }));
|
||||
assert.ok(labels.includes("Vue.js"), "should include Vue.js");
|
||||
});
|
||||
|
||||
test("matchPacksForProject: Chart.yaml triggers Kubernetes", () => {
|
||||
const labels = packLabels(makeSignals({ detectedFiles: ["Chart.yaml"] }));
|
||||
assert.ok(labels.includes("Kubernetes"), "should include Kubernetes");
|
||||
});
|
||||
|
||||
test("matchPacksForProject: hardhat.config.ts triggers Blockchain", () => {
|
||||
const labels = packLabels(makeSignals({ detectedFiles: ["hardhat.config.ts"] }));
|
||||
assert.ok(labels.includes("Blockchain & Web3"), "should include Blockchain & Web3");
|
||||
});
|
||||
|
||||
test("matchPacksForProject: tailwind.config.ts triggers Tailwind CSS", () => {
|
||||
const labels = packLabels(makeSignals({ detectedFiles: ["tailwind.config.ts"] }));
|
||||
assert.ok(labels.includes("Tailwind CSS"), "should include Tailwind CSS");
|
||||
});
|
||||
|
||||
// ── Xcode platform matching ─────────────────────────────────────────────────
|
||||
|
||||
test("matchPacksForProject: iphoneos triggers iOS packs", () => {
|
||||
const labels = packLabels(makeSignals({ xcodePlatforms: ["iphoneos"] }));
|
||||
assert.ok(labels.includes("iOS App Frameworks"), "should include iOS App Frameworks");
|
||||
assert.ok(labels.includes("iOS Data Frameworks"), "should include iOS Data Frameworks");
|
||||
assert.ok(labels.includes("iOS AI & ML"), "should include iOS AI & ML");
|
||||
assert.ok(labels.includes("iOS Engineering"), "should include iOS Engineering");
|
||||
assert.ok(labels.includes("iOS Hardware"), "should include iOS Hardware");
|
||||
assert.ok(labels.includes("iOS Platform"), "should include iOS Platform");
|
||||
});
|
||||
|
||||
// ── Isolation checks — packs that should NOT match ──────────────────────────
|
||||
|
||||
test("matchPacksForProject: FastAPI does not match generic Python", () => {
|
||||
const labels = packLabels(makeSignals({ primaryLanguage: "python", detectedFiles: ["pyproject.toml"] }));
|
||||
assert.ok(!labels.includes("FastAPI"), "FastAPI should NOT match generic Python projects");
|
||||
});
|
||||
|
||||
test("matchPacksForProject: Spring Boot does not match via language alone", () => {
|
||||
// Simulate Android project: has java/kotlin language but no root pom.xml/build.gradle
|
||||
const labels = packLabels(makeSignals({ primaryLanguage: "java/kotlin", detectedFiles: ["app/build.gradle"] }));
|
||||
assert.ok(!labels.includes("Java & Spring Boot"), "Spring Boot should NOT match via language alone");
|
||||
});
|
||||
|
||||
test("matchPacksForProject: Unity does not include Godot", () => {
|
||||
const labels = packLabels(makeSignals({ detectedFiles: ["ProjectSettings/ProjectVersion.txt"] }));
|
||||
assert.ok(labels.includes("Unity"), "should include Unity");
|
||||
assert.ok(!labels.includes("Godot"), "should NOT include Godot");
|
||||
});
|
||||
|
||||
test("matchPacksForProject: Godot does not include Unity", () => {
|
||||
const labels = packLabels(makeSignals({ detectedFiles: ["project.godot"] }));
|
||||
assert.ok(labels.includes("Godot"), "should include Godot");
|
||||
assert.ok(!labels.includes("Unity"), "should NOT include Unity");
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue