feat(skills): add 11 new skill packs covering major frameworks and languages

New skill packs with auto-detection:
- Java & Spring Boot (github/awesome-copilot, 9.1K installs)
- .NET & C# (github/awesome-copilot, 8.1K installs) — scans for *.csproj/*.sln
- Flutter (flutter/skills, official, 54.6K total)
- Angular (analogjs/angular-skills, official, 33.5K total)
- Vue.js (vuejs-ai/skills, official, 30.7K total) — triggers on nuxt.config.*
- Svelte (sveltejs/ai-tools, official, 3.1K total)
- Next.js (vercel-labs/vercel-plugin, official) — triggers on next.config.*
- Docker (github/awesome-copilot, 8.3K installs)
- Terraform (hashicorp/agent-skills, official, 9.4K total)
- Django (vintasoftware/django-ai-plugins, 2.2K total)
- PHP & Laravel (jeffallan/claude-skills, 10.1K total)

Detection additions:
- PROJECT_FILES: angular.json, next.config.*, nuxt.config.*, svelte.config.*,
  Dockerfile, docker-compose.*, main.tf, manage.py, requirements.txt
- Extension scanning: *.csproj/*.sln/*.fsproj for .NET detection
- LANGUAGE_MAP: manage.py and requirements.txt → python
- GREENFIELD_STACKS: Angular, Vue, Svelte, Next.js, Flutter, Java, .NET,
  PHP, Django entries
- 8 new detection tests (36 total, up from 28)
This commit is contained in:
Derek Pearson 2026-03-22 06:52:55 -04:00
parent a30b125020
commit 3a35eae883
3 changed files with 286 additions and 3 deletions

View file

@ -109,6 +109,24 @@ export const PROJECT_FILES = [
"metro.config.js",
"metro.config.ts",
"react-native.config.js",
// Frontend framework config files
"angular.json",
"next.config.js",
"next.config.ts",
"next.config.mjs",
"nuxt.config.ts",
"nuxt.config.js",
"svelte.config.js",
"svelte.config.ts",
// Container / DevOps config files
"Dockerfile",
"docker-compose.yml",
"docker-compose.yaml",
// Infrastructure as Code
"main.tf",
// Python framework markers
"manage.py",
"requirements.txt",
] as const;
/** File extensions that indicate SQLite databases in the project. */
@ -117,6 +135,9 @@ const SQLITE_EXTENSIONS = [".sqlite", ".sqlite3", ".db"] as const;
/** File extensions that indicate SQL usage (migrations, schemas, seeds). */
const SQL_EXTENSIONS = [".sql"] as const;
/** File extensions that indicate .NET / C# projects. */
const DOTNET_EXTENSIONS = [".csproj", ".sln", ".fsproj"] as const;
const LANGUAGE_MAP: Record<string, string> = {
"package.json": "javascript/typescript",
"Cargo.toml": "rust",
@ -134,6 +155,8 @@ const LANGUAGE_MAP: Record<string, string> = {
"mix.exs": "elixir",
"deno.json": "typescript/deno",
"deno.jsonc": "typescript/deno",
"manage.py": "python",
"requirements.txt": "python",
};
const MONOREPO_MARKERS = [
@ -289,9 +312,9 @@ export function detectProjectSignals(basePath: string): ProjectSignals {
}
}
// SQLite / SQL file detection — scan root entries for database file extensions.
// Adds synthetic markers (e.g. "*.sqlite", "*.sql") to detectedFiles so
// skill catalog matchFiles can reference them.
// SQLite / SQL / .NET file detection — scan root entries for file extensions.
// Adds synthetic markers (e.g. "*.sqlite", "*.sql", "*.csproj") to detectedFiles
// so skill catalog matchFiles can reference them.
try {
const rootEntries = readdirSync(basePath);
if (rootEntries.some((e) => SQLITE_EXTENSIONS.some((ext) => e.endsWith(ext)))) {
@ -300,6 +323,10 @@ export function detectProjectSignals(basePath: string): ProjectSignals {
if (rootEntries.some((e) => SQL_EXTENSIONS.some((ext) => e.endsWith(ext)))) {
detectedFiles.push("*.sql");
}
if (rootEntries.some((e) => DOTNET_EXTENSIONS.some((ext) => e.endsWith(ext)))) {
detectedFiles.push("*.csproj");
if (!primaryLanguage) primaryLanguage = "csharp";
}
} catch {
// unreadable root — skip extension scan
}

View file

@ -202,6 +202,98 @@ export const SKILL_CATALOG: SkillPack[] = [
skills: ["frontend-design"],
matchLanguages: ["javascript/typescript"],
},
// ── Angular ───────────────────────────────────────────────────────────────
{
label: "Angular",
description: "Angular components, signals, forms, routing, and testing",
repo: "analogjs/angular-skills",
skills: [
"angular-component",
"angular-signals",
"angular-forms",
"angular-routing",
"angular-testing",
],
matchFiles: ["angular.json"],
},
// ── Vue.js / Nuxt ────────────────────────────────────────────────────────
{
label: "Vue.js",
description: "Vue best practices, Pinia state, Vue Router, and testing",
repo: "vuejs-ai/skills",
skills: [
"vue-best-practices",
"vue-pinia-best-practices",
"vue-router-best-practices",
"vue-testing-best-practices",
],
matchFiles: ["nuxt.config.ts", "nuxt.config.js"],
},
// ── Svelte / SvelteKit ────────────────────────────────────────────────────
{
label: "Svelte",
description: "Svelte code patterns and SvelteKit best practices",
repo: "sveltejs/ai-tools",
skills: ["svelte-code-writer", "svelte-core-bestpractices"],
matchFiles: ["svelte.config.js", "svelte.config.ts"],
},
// ── Next.js ───────────────────────────────────────────────────────────────
{
label: "Next.js",
description: "Next.js app router, server components, and deployment patterns",
repo: "vercel-labs/vercel-plugin",
skills: ["nextjs"],
matchFiles: ["next.config.js", "next.config.ts", "next.config.mjs"],
},
// ── Java / Spring Boot ────────────────────────────────────────────────────
{
label: "Java & Spring Boot",
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# ────────────────────────────────────────────────────────────
{
label: ".NET & C#",
description: ".NET best practices, design patterns, and upgrade guidance",
repo: "github/awesome-copilot",
skills: ["dotnet-best-practices", "dotnet-design-pattern-review"],
matchLanguages: ["csharp"],
matchFiles: ["*.csproj"],
},
// ── Flutter / Dart ────────────────────────────────────────────────────────
{
label: "Flutter",
description: "Flutter layouts, architecture, state management, and testing",
repo: "flutter/skills",
skills: [
"flutter-building-layouts",
"flutter-architecting-apps",
"flutter-managing-state",
"flutter-testing-apps",
],
matchLanguages: ["dart/flutter"],
matchFiles: ["pubspec.yaml"],
},
// ── PHP / Laravel ─────────────────────────────────────────────────────────
{
label: "PHP & Laravel",
description: "Laravel patterns, PHP best practices, and testing",
repo: "jeffallan/claude-skills",
skills: ["laravel-specialist", "php-pro"],
matchLanguages: ["php"],
matchFiles: ["composer.json"],
},
// ── Django ────────────────────────────────────────────────────────────────
{
label: "Django",
description: "Django expert patterns, models, views, and middleware",
repo: "vintasoftware/django-ai-plugins",
skills: ["django-expert"],
matchFiles: ["manage.py"],
},
// ── Rust ──────────────────────────────────────────────────────────────────
{
label: "Rust",
@ -303,6 +395,22 @@ export const SKILL_CATALOG: SkillPack[] = [
skills: ["deploy", "aws-lambda", "aws-serverless-deployment"],
matchFiles: ["cdk.json", "samconfig.toml", "serverless.yml", "serverless.yaml"],
},
// ── Container / DevOps ─────────────────────────────────────────────────────
{
label: "Docker",
description: "Multi-stage Dockerfiles, layer optimization, and security hardening",
repo: "github/awesome-copilot",
skills: ["multi-stage-dockerfile"],
matchFiles: ["Dockerfile", "docker-compose.yml", "docker-compose.yaml"],
},
// ── Infrastructure as Code ─────────────────────────────────────────────────
{
label: "Terraform",
description: "Terraform style guide, testing, and stack patterns",
repo: "hashicorp/agent-skills",
skills: ["terraform-style-guide", "terraform-test", "terraform-stacks"],
matchFiles: ["main.tf"],
},
// ── Essential (all projects) ────────────────────────────────────────────
{
label: "Skill Discovery",
@ -427,6 +535,60 @@ export const GREENFIELD_STACKS: Array<{
description: "Azure deployment, AI, storage, diagnostics",
packs: ["Azure"],
},
{
id: "angular",
label: "Angular",
description: "Angular components, signals, forms, routing",
packs: ["Angular", "Frontend Design & UX"],
},
{
id: "vue",
label: "Vue.js / Nuxt",
description: "Vue.js with Pinia, Vue Router, and testing",
packs: ["Vue.js", "Frontend Design & UX"],
},
{
id: "svelte",
label: "Svelte / SvelteKit",
description: "Svelte 5 and SvelteKit patterns",
packs: ["Svelte", "Frontend Design & UX"],
},
{
id: "nextjs",
label: "Next.js",
description: "Next.js app router, React, and Vercel deployment",
packs: ["Next.js", "React & Web Frontend", "shadcn/ui"],
},
{
id: "flutter",
label: "Flutter",
description: "Cross-platform Flutter/Dart development",
packs: ["Flutter"],
},
{
id: "java",
label: "Java / Spring Boot",
description: "Spring Boot APIs, JPA, and testing",
packs: ["Java & Spring Boot"],
},
{
id: "dotnet",
label: ".NET / C#",
description: "ASP.NET Core, Entity Framework, and design patterns",
packs: [".NET & C#"],
},
{
id: "php",
label: "PHP / Laravel",
description: "Laravel patterns and PHP best practices",
packs: ["PHP & Laravel"],
},
{
id: "django",
label: "Django",
description: "Django models, views, middleware, and Celery",
packs: ["Django", "Python"],
},
{
id: "other",
label: "Other / Skip",

View file

@ -441,3 +441,97 @@ test("detectProjectSignals: no SQLite markers without matching files", () => {
cleanup(dir);
}
});
test("detectProjectSignals: .NET project via .csproj extension", () => {
const dir = makeTempDir("signals-dotnet");
try {
writeFileSync(join(dir, "MyApp.csproj"), "<Project></Project>", "utf-8");
const signals = detectProjectSignals(dir);
assert.ok(signals.detectedFiles.includes("*.csproj"), "should add synthetic *.csproj marker");
assert.equal(signals.primaryLanguage, "csharp");
} finally {
cleanup(dir);
}
});
test("detectProjectSignals: .NET project via .sln extension", () => {
const dir = makeTempDir("signals-sln");
try {
writeFileSync(join(dir, "MyApp.sln"), "", "utf-8");
const signals = detectProjectSignals(dir);
assert.ok(signals.detectedFiles.includes("*.csproj"), "should add synthetic *.csproj marker for .sln files");
} finally {
cleanup(dir);
}
});
test("detectProjectSignals: Angular project via angular.json", () => {
const dir = makeTempDir("signals-angular");
try {
writeFileSync(join(dir, "angular.json"), "{}", "utf-8");
writeFileSync(join(dir, "package.json"), "{}", "utf-8");
const signals = detectProjectSignals(dir);
assert.ok(signals.detectedFiles.includes("angular.json"));
assert.equal(signals.primaryLanguage, "javascript/typescript");
} finally {
cleanup(dir);
}
});
test("detectProjectSignals: Next.js project via next.config.ts", () => {
const dir = makeTempDir("signals-nextjs");
try {
writeFileSync(join(dir, "next.config.ts"), "export default {}", "utf-8");
writeFileSync(join(dir, "package.json"), "{}", "utf-8");
const signals = detectProjectSignals(dir);
assert.ok(signals.detectedFiles.includes("next.config.ts"));
} finally {
cleanup(dir);
}
});
test("detectProjectSignals: Flutter project via pubspec.yaml", () => {
const dir = makeTempDir("signals-flutter");
try {
writeFileSync(join(dir, "pubspec.yaml"), "name: my_app", "utf-8");
const signals = detectProjectSignals(dir);
assert.ok(signals.detectedFiles.includes("pubspec.yaml"));
assert.equal(signals.primaryLanguage, "dart/flutter");
} finally {
cleanup(dir);
}
});
test("detectProjectSignals: Django project via manage.py", () => {
const dir = makeTempDir("signals-django");
try {
writeFileSync(join(dir, "manage.py"), "#!/usr/bin/env python", "utf-8");
const signals = detectProjectSignals(dir);
assert.ok(signals.detectedFiles.includes("manage.py"));
assert.equal(signals.primaryLanguage, "python");
} finally {
cleanup(dir);
}
});
test("detectProjectSignals: Docker project via Dockerfile", () => {
const dir = makeTempDir("signals-docker");
try {
writeFileSync(join(dir, "Dockerfile"), "FROM node:18", "utf-8");
const signals = detectProjectSignals(dir);
assert.ok(signals.detectedFiles.includes("Dockerfile"));
} finally {
cleanup(dir);
}
});
test("detectProjectSignals: Terraform project via main.tf", () => {
const dir = makeTempDir("signals-terraform");
try {
writeFileSync(join(dir, "main.tf"), 'provider "aws" {}', "utf-8");
const signals = detectProjectSignals(dir);
assert.ok(signals.detectedFiles.includes("main.tf"));
} finally {
cleanup(dir);
}
});