Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

developer-stack-skills

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

developer-stack-skills - npm Package Compare versions

Comparing version
2.0.0
to
3.0.0
+9
examples/smoke-tests/cline/.mcp.json
{
"mcpServers": {
"developer-stack-skills": {
"command": "npx",
"args": ["developer-stack-skills", "serve"],
"type": "stdio"
}
}
}
{
"mcpServers": {
"developer-stack-skills": {
"command": "npx",
"args": ["developer-stack-skills", "serve"],
"type": "stdio"
}
}
}
+1
-1
---
description: Java & Spring Boot — Spring Boot 3, JPA, REST APIs, JUnit 5, Maven/Gradle
description: Java 25 & Spring Boot 4 / Spring 7 — JPA, REST APIs, JUnit 5, Maven/Gradle
globs: ["**/*.java","**/*.kt","**/pom.xml","**/build.gradle","**/build.gradle.kts"]

@@ -4,0 +4,0 @@ alwaysApply: false

# Changelog
## 3.0.0 - 2026-05-20
Breaking:
- Node.js engine requirement raised to `>=24.0.0` — drops Node 18, 20, 22
- MCP tool error responses changed from plain text to structured JSON `{error_type, message, retryable}` — `INVALID_SKILL`, `SKILL_NOT_FOUND`, `UNKNOWN_TOOL`
- `configureCline(dir, paths, dryRun)` → `configureCline(dir, paths, useMcp, dryRun)` — third param changed
- `configureRoocode(dir, paths, dryRun)` → `configureRoocode(dir, paths, useMcp, dryRun)` — third param changed
- `configureClaude` now generates MCP-first CLAUDE.md when `configureMcpServer=true` — installed projects get `detect_stack`/`get_skill` instructions instead of file paths
Added:
- `configureCursorMcp(dir, packageInstallType, dryRun)` — writes `.cursor/mcp.json`
- `configureSharedMcp(dir, packageInstallType, dryRun)` — writes `.mcp.json` at project root for Cline/Roocode
- `writeMcpJsonFile(filePath, packageInstallType, dryRun)` — shared MCP JSON write/merge helper
- `removeMcpJsonEntry(filePath, dryRun)` — removes package entry, preserves others, deletes file if empty
- `unconfigureCursorMcp(dir, dryRun)`, `unconfigureSharedMcp(dir, dryRun)` — cleanup counterparts
- `handleTool` exported from `lib/mcp-server.js` for direct testing
- Cursor now receives `.cursor/mcp.json` on install when MCP opted in
- Cline now receives `.mcp.json` + MCP-first `.clinerules` on install when MCP opted in
- Roocode now receives `.mcp.json` + MCP-first `.roo/rules/developer-stack-skills.md` on install when MCP opted in
- Source repo `.claude/mcp.json` and `.mcp.json` added for Cline/Roocode users of this repo
- 33 new tests: MCP routing in `configureAgents`, structured error shape, `writeMcpJsonFile`/`removeMcpJsonEntry` helpers, idempotency for all MCP config functions, unconfigure cleanup
- Smoke test fixtures (claude, cline, roocode) updated to MCP-first instructions; cline/roocode fixtures include `.mcp.json`
Changed:
- `configureCline` — generates MCP-first body when `useMcp=true`; path-based body when false
- `configureRoocode` — same
- `configureAgents` — passes `configureMcpServer` to all agent configurers; calls `configureCursorMcp`/`configureSharedMcp` when opted in; calls `unconfigureCursorMcp`/`unconfigureSharedMcp` on uninstall
- `configureMcp`/`unconfigureMcp` refactored to delegate to `writeMcpJsonFile`/`removeMcpJsonEntry`
- Commands `implement-feature`, `write-tests`, `add-endpoint` — replaced manual root-file stack detection with `detect_stack` + `get_skill` MCP tool calls; `allowed-tools` updated to include MCP tool names
- `MCP_INSTRUCTION_LINES` constant extracted in `lib/installer.js` — shared between Cline and Roocode instruction generation
- Java examples: `@MockBean` → `@MockitoBean` (removed Spring Boot 4)
- Angular testing: Jasmine+Karma → Jest; `HttpClientTestingModule` → `provideHttpClient()` + `provideHttpClientTesting()`
- All SKILL.md `last-reviewed` dates updated to `2026-05-20`
Version updates:
- Java 17+ → Java 25; Spring Boot 3.x → Spring Boot 4 / Spring Framework 7
- PostgreSQL container image: `postgres:16-alpine` → `postgres:18-alpine`
- Python `>=3.12` → `>=3.14`; ruff `>=0.4` → `>=1.0`; mypy `>=1.10` → `>=1.15`; pytest `>=8.0` → `>=8.3`; pytest-asyncio `>=0.23` → `>=0.24`; httpx `>=0.27` → `>=0.28`; uvicorn `>=0.30` → `>=0.34`; asyncpg `>=0.29` → `>=0.30`
- React 18+ → React 19+; Angular 17+ → Angular 21+; Node engine `>=18` → `>=24`
## 2.0.0 - 2026-05-16

@@ -4,0 +48,0 @@

---
description: Add REST API endpoint following stack and REST conventions
argument-hint: [METHOD /path description]
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
allowed-tools: Read, Write, Edit, Bash, Grep, Glob, mcp__developer-stack-skills__detect_stack, mcp__developer-stack-skills__get_skill
---

@@ -9,18 +9,13 @@

1. Detect stack:
- `pom.xml` or `build.gradle` → Java/Spring Boot (@RestController pattern)
- `pyproject.toml` or `requirements.txt` → Python/FastAPI (APIRouter pattern)
- `package.json` without pom.xml/build.gradle → TypeScript/Node.js (Express/NestJS/Fastify — follow existing router pattern in project)
- If no stack detected, state so and ask user to specify framework before proceeding.
1. Identify the controller or router file where this endpoint will live. Call `detect_stack` with that file path, then call `get_skill` with the result to load full conventions before writing anything. If no existing file exists yet, call `detect_stack` with a representative source file (e.g. an existing controller).
2. Read existing endpoints in the project to understand current patterns, naming, and error handling before writing anything.
2. Read existing endpoints in the project to understand current patterns, naming, and error handling.
3. Design the endpoint following REST conventions:
- Correct HTTP method: GET (read), POST (create → 201), PUT/PATCH (update → 200), DELETE (204)
- Request/response DTOs: Java records with `@Valid`, Pydantic models with field validators
- Input validated at controller/router layer — never in service layer
- Request/response DTOs validated at the controller/router layer — never in service layer
4. Implement in layers — do not skip:
- Controller/Router: thin, delegates all logic to service
- Service: business logic, throws domain-specific exceptions (never generic ones)
- Service: business logic, throws domain-specific exceptions
- Repository: data access only (create if DB interaction needed)

@@ -27,0 +22,0 @@

---
description: Implement feature following project stack conventions
argument-hint: [feature-description]
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
allowed-tools: Read, Write, Edit, Bash, Grep, Glob, mcp__developer-stack-skills__detect_stack, mcp__developer-stack-skills__get_skill, mcp__developer-stack-skills__get_conventions
---

@@ -9,15 +9,9 @@

1. Detect project stack by checking root files:
- `pom.xml` or `build.gradle` → Java/Spring Boot
- `pyproject.toml`, `requirements.txt`, or `setup.py` → Python/FastAPI
- `package.json` → TypeScript/JavaScript (React or Angular)
1. Identify the primary file to create or modify for this feature. Call `detect_stack` with that file path to determine the stack, then call `get_skill` with the result to load full conventions before writing anything.
2. Read existing code to understand current architecture, naming conventions, and patterns before writing anything.
2. Read existing code to understand current architecture, naming conventions, and patterns.
3. State assumptions and create a concise implementation plan. List files to create or modify.
4. Implement following the skill conventions already loaded for this stack:
- Java: thin controllers, business logic in service layer, DTOs via records, constructor injection only
- Python: Pydantic models for I/O, async handlers, pydantic-settings for config
- TypeScript: functional components, TanStack Query for server state, no fetch inside components
4. Implement following the loaded skill conventions.

@@ -24,0 +18,0 @@ 5. Write tests alongside implementation:

---
description: Write tests for a file or class following testing conventions
argument-hint: [file-or-class]
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
allowed-tools: Read, Write, Edit, Bash, Grep, Glob, mcp__developer-stack-skills__detect_stack, mcp__developer-stack-skills__get_skill
---

@@ -9,9 +9,5 @@

1. Read the target file thoroughly to understand all public methods, edge cases, and error paths.
1. Call `detect_stack` with the target file path, then call `get_skill` with the result to load the full testing conventions for this stack before writing anything.
2. Detect test framework from project:
- Java → JUnit 5 + Mockito (check pom.xml or build.gradle)
- Python → pytest (check pyproject.toml)
- TypeScript/React → Vitest + Testing Library
- Angular → Jasmine + Karma
2. Read the target file thoroughly to understand all public methods, edge cases, and error paths.

@@ -27,4 +23,4 @@ 3. For each public method or exported function, write:

6. Use descriptive test names: `method_WhenCondition_ExpectedResult` (Java) or `test_does_x_when_y` (Python) or `should do X when Y` (TypeScript).
6. Use descriptive test names per the loaded skill conventions.
7. Place test file in the correct location per project structure. Run existing tests to confirm nothing broke.

@@ -1,7 +0,9 @@

Load these skill files before starting work:
Use the developer-stack-skills MCP server before editing any file:
- node_modules/developer-stack-skills/frontend/SKILL.md
- node_modules/developer-stack-skills/testing/SKILL.md
- node_modules/developer-stack-skills/project-conventions/SKILL.md
1. Call `detect_stack` with the file path to identify the relevant stack.
2. Call `get_skill` with the detected stack to load its conventions.
3. Do not preload all skill files — load only the relevant skill on demand.
After loading, create a concise implementation plan, state assumptions, then implement requested changes.
For cross-cutting decisions, call `get_conventions` to load project-wide standards.
After loading the relevant skill, create a concise implementation plan, state assumptions, then implement requested changes.
# Developer Stack Skills
Load and follow these skill files before starting work:
Use the developer-stack-skills MCP server before editing any file:
- node_modules/developer-stack-skills/java-spring/SKILL.md
- node_modules/developer-stack-skills/testing/SKILL.md
- node_modules/developer-stack-skills/project-conventions/SKILL.md
1. Call `detect_stack` with the file path to identify the relevant stack.
2. Call `get_skill` with the detected stack to load its conventions.
3. Do not preload all skill files — load only the relevant skill on demand.
For cross-cutting decisions, call `get_conventions` to load project-wide standards.
After loading the relevant skill, create a concise implementation plan, state assumptions, then implement.

@@ -11,4 +11,4 @@ ---

version: 1.0.0
last-reviewed: 2026-05-15
applies-to: React, Angular, JavaScript, TypeScript, frontend architecture, UI refactors
last-reviewed: 2026-05-20
applies-to: React 19+, Angular 21+, JavaScript, TypeScript 5+, frontend architecture, UI refactors
---

@@ -191,3 +191,3 @@

### Component Rules
- Use **standalone components** (Angular 17+) by default
- Use **standalone components** — default since Angular 17, mandatory pattern from Angular 21+
- Use `OnPush` change detection for all components

@@ -220,3 +220,3 @@ - Use **signals** (`signal`, `computed`, `effect`) for reactive state

### Service Rules
- Use `inject()` function (not constructor injection) for Angular 14+
- Use `inject()` function (not constructor injection) — standard from Angular 14+, required style from Angular 21+
- Provide at root: `providedIn: 'root'`

@@ -223,0 +223,0 @@ - Return `Observable` from service methods; never subscribe inside services

@@ -11,3 +11,3 @@ ---

version: 1.0.0
last-reviewed: 2026-05-15
last-reviewed: 2026-05-20
applies-to: Java, Spring Boot, REST APIs, JPA, security, testing, build configuration

@@ -111,3 +111,3 @@ ---

### DTO Convention
- Use Java `record` for DTOs (Java 16+)
- Use Java `record` for DTOs (Java 17+ feature — Spring Boot 4 / Java 25 baseline)
- Suffix: `*Request`, `*Response`, `*DTO`

@@ -288,3 +288,3 @@

@Autowired MockMvc mockMvc;
@MockBean UserService userService;
@MockitoBean UserService userService; // @MockBean removed in Spring Boot 4

@@ -291,0 +291,0 @@ @Test

@@ -32,3 +32,3 @@ const fsp = require("fs/promises");

skillName: "java-spring",
description: "Java & Spring Boot — Spring Boot 3, JPA, REST APIs, JUnit 5, Maven/Gradle",
description: "Java 25 & Spring Boot 4 / Spring 7 — JPA, REST APIs, JUnit 5, Maven/Gradle",
globs: ["**/*.java", "**/*.kt", "**/pom.xml", "**/build.gradle", "**/build.gradle.kts"],

@@ -466,3 +466,3 @@ },

async function configureClaude(projectDir, conventionsPath, context, dryRun = false) {
async function configureClaude(projectDir, conventionsPath, context, useMcp = false, dryRun = false) {
const filePath = path.join(projectDir, "CLAUDE.md");

@@ -481,11 +481,25 @@ const current = await readIfExists(filePath);

lines.push(
"Load this skill file before starting work:",
"",
`- ${conventionsPath}`,
"",
"Stack skills (java-spring, python-backend, frontend, testing) load contextually via `.claude/rules/`.",
"",
"After loading, create concise implementation plan, state assumptions, then implement requested changes.",
);
if (useMcp) {
lines.push(
"Use the developer-stack-skills MCP server before editing any file:",
"",
"1. Call `detect_stack` with the file path to identify the relevant stack.",
"2. Call `get_skill` with the detected stack to load its conventions.",
"3. Do not preload all skill files — load only the relevant skill on demand.",
"",
"For cross-cutting decisions, call `get_conventions` to load project-wide standards.",
"",
"After loading the relevant skill, create a concise implementation plan, state assumptions, then implement.",
);
} else {
lines.push(
"Load this skill file before starting work:",
"",
`- ${conventionsPath}`,
"",
"Stack skills (java-spring, python-backend, frontend, testing) load contextually via `.claude/rules/`.",
"",
"After loading, create concise implementation plan, state assumptions, then implement requested changes.",
);
}

@@ -573,11 +587,7 @@ const body = lines.join("\n");

async function configureMcp(projectDir, packageInstallType, dryRun = false) {
const filePath = path.join(projectDir, ".claude", "mcp.json");
async function writeMcpJsonFile(filePath, packageInstallType, dryRun = false) {
const current = await readIfExists(filePath);
let config = {};
if (current.trim()) {
try {
config = JSON.parse(current);
} catch {
try { config = JSON.parse(current); } catch {
process.stderr.write(`[${PACKAGE_NAME}] Warning: ${filePath} has invalid JSON — skipping MCP update to avoid data loss\n`);

@@ -587,7 +597,5 @@ return filePath;

}
if (!config.mcpServers) config.mcpServers = {};
const { command, args } = buildMcpCommand(packageInstallType);
config.mcpServers[PACKAGE_NAME] = { command, args, type: "stdio" };
await writeFileWithDirs(filePath, JSON.stringify(config, null, 2) + "\n", dryRun);

@@ -597,11 +605,7 @@ return filePath;

async function unconfigureMcp(projectDir, dryRun = false) {
const filePath = path.join(projectDir, ".claude", "mcp.json");
async function removeMcpJsonEntry(filePath, dryRun = false) {
const current = await readIfExists(filePath);
if (!current.trim()) return filePath;
let config;
try { config = JSON.parse(current); } catch { return filePath; }
if (config.mcpServers) {

@@ -611,3 +615,2 @@ delete config.mcpServers[PACKAGE_NAME];

}
if (Object.keys(config).length === 0) {

@@ -618,6 +621,29 @@ await removePath(filePath, dryRun);

}
return filePath;
}
async function configureMcp(projectDir, packageInstallType, dryRun = false) {
return writeMcpJsonFile(path.join(projectDir, ".claude", "mcp.json"), packageInstallType, dryRun);
}
async function configureCursorMcp(projectDir, packageInstallType, dryRun = false) {
return writeMcpJsonFile(path.join(projectDir, ".cursor", "mcp.json"), packageInstallType, dryRun);
}
async function configureSharedMcp(projectDir, packageInstallType, dryRun = false) {
return writeMcpJsonFile(path.join(projectDir, ".mcp.json"), packageInstallType, dryRun);
}
async function unconfigureMcp(projectDir, dryRun = false) {
return removeMcpJsonEntry(path.join(projectDir, ".claude", "mcp.json"), dryRun);
}
async function unconfigureCursorMcp(projectDir, dryRun = false) {
return removeMcpJsonEntry(path.join(projectDir, ".cursor", "mcp.json"), dryRun);
}
async function unconfigureSharedMcp(projectDir, dryRun = false) {
return removeMcpJsonEntry(path.join(projectDir, ".mcp.json"), dryRun);
}
async function unconfigureClaudeRules(projectDir, dryRun = false) {

@@ -736,10 +762,24 @@ const rulesDir = path.join(projectDir, ".claude", "rules");

async function configureCline(projectDir, skillPaths, dryRun = false) {
const MCP_INSTRUCTION_LINES = [
"Use the developer-stack-skills MCP server before editing any file:",
"",
"1. Call `detect_stack` with the file path to identify the relevant stack.",
"2. Call `get_skill` with the detected stack to load its conventions.",
"3. Do not preload all skill files — load only the relevant skill on demand.",
"",
"For cross-cutting decisions, call `get_conventions` to load project-wide standards.",
"",
"After loading the relevant skill, create a concise implementation plan, state assumptions, then implement.",
];
async function configureCline(projectDir, skillPaths, useMcp = false, dryRun = false) {
const filePath = path.join(projectDir, ".clinerules");
const current = await readIfExists(filePath);
const body = [
"Read and follow these skill files before starting work:",
"",
...skillPaths.map((skillPath) => `- ${skillPath}`),
].join("\n");
const body = useMcp
? MCP_INSTRUCTION_LINES.join("\n")
: [
"Read and follow these skill files before starting work:",
"",
...skillPaths.map((skillPath) => `- ${skillPath}`),
].join("\n");
const next = replaceManagedBlock(current, body, "html");

@@ -750,13 +790,14 @@ await writeFileWithDirs(filePath, next, dryRun);

async function configureRoocode(projectDir, skillPaths, dryRun = false) {
async function configureRoocode(projectDir, skillPaths, useMcp = false, dryRun = false) {
const filePath = path.join(projectDir, ".roo", "rules", "developer-stack-skills.md");
const body = [
"# Developer Stack Skills",
"",
"Load and follow these skill files before starting work:",
"",
...skillPaths.map((skillPath) => `- ${skillPath}`),
"",
].join("\n");
const body = useMcp
? ["# Developer Stack Skills", "", ...MCP_INSTRUCTION_LINES, ""].join("\n")
: [
"# Developer Stack Skills",
"",
"Load and follow these skill files before starting work:",
"",
...skillPaths.map((skillPath) => `- ${skillPath}`),
"",
].join("\n");
await writeFileWithDirs(filePath, body, dryRun);

@@ -794,3 +835,3 @@ return filePath;

const conventionsPath = path.join(installRoot, "project-conventions", "SKILL.md");
configured.push({ agent: target, filePath: await configureClaude(projectDir, conventionsPath, context, dryRun) });
configured.push({ agent: target, filePath: await configureClaude(projectDir, conventionsPath, context, configureMcpServer, dryRun) });
const ruleFiles = await configureClaudeRules(projectDir, installRoot, dryRun);

@@ -818,2 +859,5 @@ for (const ruleFilePath of ruleFiles) {

}
if (configureMcpServer) {
configured.push({ agent: "cursor-mcp", filePath: await configureCursorMcp(projectDir, packageInstallType, dryRun) });
}
continue;

@@ -823,3 +867,6 @@ }

if (target === "cline") {
configured.push({ agent: target, filePath: await configureCline(projectDir, skillPaths, dryRun) });
configured.push({ agent: target, filePath: await configureCline(projectDir, skillPaths, configureMcpServer, dryRun) });
if (configureMcpServer) {
configured.push({ agent: "cline-mcp", filePath: await configureSharedMcp(projectDir, packageInstallType, dryRun) });
}
continue;

@@ -829,3 +876,6 @@ }

if (target === "roocode") {
configured.push({ agent: target, filePath: await configureRoocode(projectDir, skillPaths, dryRun) });
configured.push({ agent: target, filePath: await configureRoocode(projectDir, skillPaths, configureMcpServer, dryRun) });
if (configureMcpServer) {
configured.push({ agent: "roocode-mcp", filePath: await configureSharedMcp(projectDir, packageInstallType, dryRun) });
}
continue;

@@ -932,2 +982,3 @@ }

}
configured.push({ agent: "cursor-mcp", filePath: await unconfigureCursorMcp(projectDir, dryRun) });
continue;

@@ -938,2 +989,3 @@ }

configured.push({ agent: target, filePath: await unconfigureCline(projectDir, dryRun) });
configured.push({ agent: "cline-mcp", filePath: await unconfigureSharedMcp(projectDir, dryRun) });
continue;

@@ -944,2 +996,3 @@ }

configured.push({ agent: target, filePath: await unconfigureRoocode(projectDir, dryRun) });
configured.push({ agent: "roocode-mcp", filePath: await unconfigureSharedMcp(projectDir, dryRun) });
continue;

@@ -1228,4 +1281,6 @@ }

configureCursor,
configureCursorMcp,
configureMcp,
configureRoocode,
configureSharedMcp,
detectPackageInstallType,

@@ -1243,2 +1298,3 @@ detectPlatform,

removeManagedBlock,
removeMcpJsonEntry,
removeOurHookEntries,

@@ -1250,4 +1306,7 @@ removeSkillsSectionItems,

runUninstall,
unconfigureCursorMcp,
unconfigureSharedMcp,
upsertSkillsSection,
validateArgs,
writeMcpJsonFile,
};

@@ -8,11 +8,11 @@ const path = require("path");

"java-spring": {
description: "Java & Spring Boot 3 — JPA, REST APIs, JUnit 5, Mockito, Maven/Gradle",
description: "Java 25 & Spring Boot 4 / Spring 7 — JPA, REST APIs, JUnit 5, Mockito, Maven/Gradle",
globs: ["**/*.java", "**/*.kt", "**/pom.xml", "**/build.gradle", "**/build.gradle.kts"],
},
"python-backend": {
description: "Python backend — FastAPI, Django, SQLAlchemy 2.x, Pydantic v2, pytest",
description: "Python 3.14 backend — FastAPI, Django, SQLAlchemy 2.x, Pydantic v2, pytest",
globs: ["**/*.py", "**/requirements*.txt", "**/pyproject.toml", "**/setup.py", "**/Pipfile"],
},
frontend: {
description: "Frontend — React 18+, Angular 17+, TypeScript, TanStack Query, Vitest, Playwright",
description: "Frontend — React 19+, Angular 21+, TypeScript, TanStack Query, Vitest, Playwright",
globs: ["**/*.tsx", "**/*.jsx", "**/*.ts", "**/*.js", "**/*.vue", "**/*.svelte", "**/package.json"],

@@ -113,2 +113,9 @@ },

function errorResponse(error_type, message, retryable = false) {
return {
isError: true,
content: [{ type: "text", text: JSON.stringify({ error_type, message, retryable }) }],
};
}
async function handleTool(name, args) {

@@ -127,13 +134,13 @@ if (name === "list_available_skills") {

if (!SKILL_META[stack_name]) {
return {
content: [{ type: "text", text: `Unknown skill: ${stack_name}. Available: ${SKILL_NAMES.join(", ")}` }],
isError: true,
};
return errorResponse(
"INVALID_SKILL",
`Unknown skill: '${stack_name}'. Available: ${SKILL_NAMES.join(", ")}`,
);
}
const content = await readSkillFile(stack_name);
if (!content) {
return {
content: [{ type: "text", text: `Skill file not found: ${stack_name}` }],
isError: true,
};
return errorResponse(
"SKILL_NOT_FOUND",
`Skill '${stack_name}' file missing. Run: developer-stack-skills install`,
);
}

@@ -146,6 +153,6 @@ return { content: [{ type: "text", text: content }] };

if (!content) {
return {
content: [{ type: "text", text: "project-conventions skill file not found." }],
isError: true,
};
return errorResponse(
"SKILL_NOT_FOUND",
"Skill 'project-conventions' file missing. Run: developer-stack-skills install",
);
}

@@ -172,6 +179,3 @@ return { content: [{ type: "text", text: content }] };

return {
content: [{ type: "text", text: `Unknown tool: ${name}` }],
isError: true,
};
return errorResponse("UNKNOWN_TOOL", `Unknown tool: '${name}'`);
}

@@ -205,2 +209,2 @@

module.exports = { runMcpServer, detectStack, SKILL_META, SKILL_NAMES };
module.exports = { runMcpServer, detectStack, handleTool, SKILL_META, SKILL_NAMES };
{
"name": "developer-stack-skills",
"version": "2.0.0",
"version": "3.0.0",
"description": "AI agent SKILL.md files plus installer CLI for Java/Spring, Python/FastAPI, React/Angular, Testing, and Project Conventions. Compatible with Claude, Cline, Roocode, Copilot, and Cursor.",

@@ -33,3 +33,3 @@ "keywords": [

"engines": {
"node": ">=18.0.0"
"node": ">=24.0.0"
},

@@ -36,0 +36,0 @@ "scripts": {

@@ -13,3 +13,3 @@ ---

version: 1.0.0
last-reviewed: 2026-05-15
last-reviewed: 2026-05-20
applies-to: Branching, commits, pull requests, ADRs, naming, environment config, documentation

@@ -16,0 +16,0 @@ ---

@@ -11,3 +11,3 @@ ---

version: 1.0.0
last-reviewed: 2026-05-15
last-reviewed: 2026-05-20
applies-to: Python, FastAPI, Django, SQLAlchemy, background workers, CLI tools, testing

@@ -366,9 +366,9 @@ ---

version = "0.1.0"
requires-python = ">=3.12"
requires-python = ">=3.14"
dependencies = [
"fastapi>=0.115",
"uvicorn[standard]>=0.30",
"uvicorn[standard]>=0.34",
"sqlalchemy>=2.0",
"pydantic-settings>=2.0",
"asyncpg>=0.29",
"asyncpg>=0.30",
]

@@ -378,7 +378,7 @@

dev = [
"pytest>=8.0",
"pytest-asyncio>=0.23",
"httpx>=0.27",
"ruff>=0.4",
"mypy>=1.10",
"pytest>=8.3",
"pytest-asyncio>=0.24",
"httpx>=0.28",
"ruff>=1.0",
"mypy>=1.15",
]

@@ -385,0 +385,0 @@ ```

@@ -21,3 +21,3 @@ # developer-stack-skills

Version in this README: `2.0.0`
Version in this README: `3.0.0`

@@ -128,3 +128,3 @@ Interactive `npm install` can auto-run post-install configuration, but recent npm versions hide lifecycle script output by default. Treat configuration as explicit step after installation unless you install with `--foreground-scripts`.

```text
[developer-stack-skills] installing version 2.0.0
[developer-stack-skills] installing version 3.0.0
[developer-stack-skills] package install type: global

@@ -328,5 +328,5 @@ [developer-stack-skills] skill install scope: global

- **Java**: Java 17+, Spring Boot 3.x, JPA/Hibernate, JUnit 5, Mockito
- **Python**: Python 3.12+, FastAPI, Pydantic v2, SQLAlchemy 2.x, pytest
- **Frontend**: React 18+, Angular 17+, TypeScript 5+, TanStack Query, Vitest
- **Java**: Java 25, Spring Boot 4 / Spring Framework 7, JPA/Hibernate, JUnit 5, Mockito
- **Python**: Python 3.14+, FastAPI, Pydantic v2, SQLAlchemy 2.x, pytest
- **Frontend**: React 19+, Angular 21+, TypeScript 5+, TanStack Query, Vitest
- **Testing**: JUnit 5, pytest, Vitest, Testing Library, Playwright, Testcontainers

@@ -333,0 +333,0 @@ - **Conventions**: Conventional Commits, ADRs, Git Flow or Trunk-Based Development

# Release Notes
## 3.0.0 - 2026-05-20
This release makes MCP the primary delivery mechanism for all supported agents, introduces structured MCP error responses, updates all stack versions to current LTS, and expands multi-agent MCP configuration.
### Breaking Changes
- **Node.js >=24 required.** Drops support for Node 18, 20, and 22.
- **MCP error responses are now structured JSON.** Tools that return errors now return `{"error_type": "...", "message": "...", "retryable": false}` instead of plain text. Consumers that parsed the error text string must be updated.
- **`configureCline` signature changed.** Third parameter is now `useMcp` (boolean), not `dryRun`. Update direct library calls: `configureCline(dir, paths, false, dryRun)`.
- **`configureRoocode` signature changed.** Same pattern: `configureRoocode(dir, paths, false, dryRun)`.
### MCP-First Agent Configuration
All supported agents now receive MCP configuration and MCP-first instructions on install (when MCP is opted in):
- **Claude** — `CLAUDE.md` now instructs `detect_stack` + `get_skill` instead of preloading all SKILL.md files
- **Cursor** — installer now writes `.cursor/mcp.json` in addition to per-stack rule files
- **Cline** — installer now writes `.mcp.json` (project root) and updates `.clinerules` to MCP-first instructions
- **Roocode** — same: `.mcp.json` + MCP-first `.roo/rules/developer-stack-skills.md`
- **GitHub Copilot** — unchanged; no MCP support; path-based instructions retained
New installer API:
| Function | Description |
|---|---|
| `configureCursorMcp(dir, type, dryRun)` | Writes `.cursor/mcp.json` |
| `configureSharedMcp(dir, type, dryRun)` | Writes `.mcp.json` (shared Cline/Roocode) |
| `writeMcpJsonFile(filePath, type, dryRun)` | Shared low-level helper for any MCP JSON file |
| `removeMcpJsonEntry(filePath, dryRun)` | Removes our entry, preserves others, deletes if empty |
| `unconfigureCursorMcp(dir, dryRun)` | Cleanup for `.cursor/mcp.json` |
| `unconfigureSharedMcp(dir, dryRun)` | Cleanup for `.mcp.json` |
### MCP Structured Errors
MCP tools now return structured error JSON on failure:
```json
{ "error_type": "INVALID_SKILL", "message": "Unknown skill: 'cobol'. Available: ...", "retryable": false }
```
Error types: `INVALID_SKILL`, `SKILL_NOT_FOUND`, `UNKNOWN_TOOL`.
### Commands Updated to Use MCP Tools
`implement-feature`, `write-tests`, and `add-endpoint` slash commands now call `detect_stack` and `get_skill` MCP tools instead of manually inspecting root files. Stack detection logic lives in one place — the MCP server — not duplicated in markdown.
### Language and Framework Version Updates
| Stack | Previous | Now |
|---|---|---|
| Java | Java 17+ | **Java 25** |
| Spring Boot | Spring Boot 3.x | **Spring Boot 4 / Spring Framework 7** |
| PostgreSQL (Testcontainers) | postgres:16 | **postgres:18** |
| Python | Python 3.12+ | **Python 3.14+** |
| React | React 18+ | **React 19+** |
| Angular | Angular 17+ | **Angular 21+** |
| Node.js (engine) | >=18.0.0 | **>=24.0.0** |
| ruff | >=0.4 | **>=1.0** |
| mypy | >=1.10 | **>=1.15** |
| pytest | >=8.0 | **>=8.3** |
| pytest-asyncio | >=0.23 | **>=0.24** |
| httpx | >=0.27 | **>=0.28** |
| uvicorn | >=0.30 | **>=0.34** |
### Testing Updates
- `@MockBean` replaced by `@MockitoBean` in all Java examples (removed in Spring Boot 4)
- Angular testing migrated from Jasmine+Karma (removed Angular 18) to **Jest**; `HttpClientTestingModule` replaced by `provideHttpClient()` + `provideHttpClientTesting()`
- 33 new tests added covering MCP routing, structured errors, idempotency, and unconfigure cleanup
---
## 2.0.0 - 2026-05-16

@@ -4,0 +76,0 @@

@@ -11,3 +11,3 @@ ---

version: 1.0.0
last-reviewed: 2026-05-15
last-reviewed: 2026-05-20
applies-to: Unit tests, integration tests, E2E tests, test reviews, test debugging

@@ -119,3 +119,3 @@ ---

@Autowired private MockMvc mockMvc;
@MockBean private UserService userService;
@MockitoBean private UserService userService; // replaces @MockBean (removed Spring Boot 4)
@Autowired private ObjectMapper objectMapper;

@@ -151,3 +151,3 @@

@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine");
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18-alpine");

@@ -303,4 +303,6 @@ @DynamicPropertySource

## Angular — Jasmine + Karma
## Angular — Jest (Angular 18+)
> **Karma removed in Angular 18.** New projects use Jest (recommended) or Web Test Runner. `ng generate` no longer scaffolds Karma config. Migrate existing projects: `ng generate jest-config` or use the `@angular-builders/jest` builder.
### Component Test

@@ -310,7 +312,7 @@ ```typescript

let fixture: ComponentFixture<UserCardComponent>;
let userServiceSpy: jasmine.SpyObj<UserService>;
let userServiceSpy: jest.Mocked<UserService>;
beforeEach(async () => {
userServiceSpy = jasmine.createSpyObj("UserService", ["getUser"]);
userServiceSpy.getUser.and.returnValue(
userServiceSpy = { getUser: jest.fn() } as jest.Mocked<UserService>;
userServiceSpy.getUser.mockReturnValue(
of({ id: 1, name: "Alice", email: "alice@example.com" })

@@ -338,2 +340,5 @@ );

```typescript
import { provideHttpClient } from "@angular/common/http";
import { provideHttpClientTesting, HttpTestingController } from "@angular/common/http/testing";
describe("UserService", () => {

@@ -345,4 +350,3 @@ let service: UserService;

TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [UserService],
providers: [UserService, provideHttpClient(), provideHttpClientTesting()],
});

@@ -367,2 +371,4 @@ service = TestBed.inject(UserService);

> `HttpClientTestingModule` is deprecated Angular 18+. Use `provideHttpClient()` + `provideHttpClientTesting()` instead.
---

@@ -369,0 +375,0 @@

Sorry, the diff of this file is not supported yet