developer-stack-skills
Advanced tools
| #!/usr/bin/env node | ||
| const { parseArgs, printHelp, runInstall } = require("../lib/installer"); | ||
| async function main() { | ||
| const args = parseArgs(process.argv.slice(2)); | ||
| if (args.command === "install") { | ||
| await runInstall(args); | ||
| return; | ||
| } | ||
| printHelp(); | ||
| } | ||
| main().catch((error) => { | ||
| console.error(`[developer-stack-skills] install failed: ${error.message}`); | ||
| process.exitCode = 1; | ||
| }); |
+23
| # Changelog | ||
| ## 1.1.0 - 2026-05-15 | ||
| Added: | ||
| - `developer-stack-skills install` CLI | ||
| - interactive install flow for agent selection, install mode, and target directory | ||
| - automatic OS detection | ||
| - `copy` and `symlink` install modes | ||
| - install logs for package version, OS, source directory, install directory, installed skills, and updated config files | ||
| - project-level agent config generation for `Claude`, `Cursor`, `Cline`, `Roocode`, and `GitHub Copilot` | ||
| - basic Node test coverage for installer argument parsing and config helpers | ||
| Changed: | ||
| - README now documents interactive and non-interactive installer usage | ||
| - published package files now include `bin/`, `lib/`, and `CHANGELOG.md` | ||
| - package description updated to mention installer CLI | ||
| Notes: | ||
| - installer is manual by design; it does not auto-run during `npm install` |
+507
| const fsp = require("fs/promises"); | ||
| const path = require("path"); | ||
| const readline = require("readline"); | ||
| const PACKAGE_NAME = "developer-stack-skills"; | ||
| const MANAGED_START = "developer-stack-skills:start"; | ||
| const MANAGED_END = "developer-stack-skills:end"; | ||
| const AGENTS = ["all", "claude", "cursor", "cline", "roocode", "copilot"]; | ||
| const MODES = ["copy", "symlink"]; | ||
| const SKILLS = [ | ||
| "java-spring", | ||
| "python-backend", | ||
| "frontend", | ||
| "testing", | ||
| "project-conventions", | ||
| ]; | ||
| function detectPlatform() { | ||
| switch (process.platform) { | ||
| case "win32": | ||
| return "windows"; | ||
| case "darwin": | ||
| return "macos"; | ||
| default: | ||
| return "linux"; | ||
| } | ||
| } | ||
| function parseArgs(argv) { | ||
| const args = { | ||
| command: argv[0] || "help", | ||
| agent: null, | ||
| mode: null, | ||
| projectDir: null, | ||
| yes: false, | ||
| }; | ||
| for (let index = 1; index < argv.length; index += 1) { | ||
| const token = argv[index]; | ||
| if (token === "--yes" || token === "-y") { | ||
| args.yes = true; | ||
| continue; | ||
| } | ||
| if (token.startsWith("--agent=")) { | ||
| args.agent = token.slice("--agent=".length); | ||
| continue; | ||
| } | ||
| if (token === "--agent") { | ||
| args.agent = argv[index + 1] || null; | ||
| index += 1; | ||
| continue; | ||
| } | ||
| if (token.startsWith("--mode=")) { | ||
| args.mode = token.slice("--mode=".length); | ||
| continue; | ||
| } | ||
| if (token === "--mode") { | ||
| args.mode = argv[index + 1] || null; | ||
| index += 1; | ||
| continue; | ||
| } | ||
| if (token.startsWith("--dir=")) { | ||
| args.projectDir = token.slice("--dir=".length); | ||
| continue; | ||
| } | ||
| if (token === "--dir") { | ||
| args.projectDir = argv[index + 1] || null; | ||
| index += 1; | ||
| } | ||
| } | ||
| return args; | ||
| } | ||
| function normalizeAgent(agent) { | ||
| if (!agent) { | ||
| return null; | ||
| } | ||
| const normalized = agent.trim().toLowerCase(); | ||
| if (normalized === "roo") { | ||
| return "roocode"; | ||
| } | ||
| if (normalized === "github-copilot") { | ||
| return "copilot"; | ||
| } | ||
| return normalized; | ||
| } | ||
| function normalizeMode(mode) { | ||
| return mode ? mode.trim().toLowerCase() : null; | ||
| } | ||
| function printHelp() { | ||
| console.log(`${PACKAGE_NAME} installer`); | ||
| console.log(""); | ||
| console.log("Usage:"); | ||
| console.log(" developer-stack-skills install"); | ||
| console.log(" developer-stack-skills install --agent all --mode symlink --dir ."); | ||
| console.log(" npx developer-stack-skills install --agent cline --mode copy"); | ||
| console.log(""); | ||
| console.log("Options:"); | ||
| console.log(" --agent <all|claude|cursor|cline|roocode|copilot>"); | ||
| console.log(" --mode <copy|symlink>"); | ||
| console.log(" --dir <project-directory>"); | ||
| console.log(" --yes"); | ||
| } | ||
| function createPrompt() { | ||
| const rl = readline.createInterface({ | ||
| input: process.stdin, | ||
| output: process.stdout, | ||
| }); | ||
| return { | ||
| ask(question) { | ||
| return new Promise((resolve) => { | ||
| rl.question(question, (answer) => resolve(answer.trim())); | ||
| }); | ||
| }, | ||
| close() { | ||
| rl.close(); | ||
| }, | ||
| }; | ||
| } | ||
| async function chooseValue(prompt, question, options, fallback) { | ||
| const answer = await prompt.ask(question); | ||
| const normalized = answer ? answer.trim().toLowerCase() : fallback; | ||
| if (!normalized) { | ||
| throw new Error(`Missing required value. Allowed: ${options.join(", ")}`); | ||
| } | ||
| if (!options.includes(normalized)) { | ||
| throw new Error(`Invalid value "${answer}". Allowed: ${options.join(", ")}`); | ||
| } | ||
| return normalized; | ||
| } | ||
| function getPackageRoot() { | ||
| return path.resolve(__dirname, ".."); | ||
| } | ||
| function getVersion() { | ||
| const packageJson = require(path.join(getPackageRoot(), "package.json")); | ||
| return packageJson.version; | ||
| } | ||
| function detectPackageInstallType(packageRoot, projectDir) { | ||
| const normalizedPackageRoot = path.resolve(packageRoot); | ||
| const localNodeModulesRoot = path.resolve(projectDir, "node_modules", PACKAGE_NAME); | ||
| return normalizedPackageRoot === localNodeModulesRoot ? "local" : "global"; | ||
| } | ||
| function getInstallRoot(projectDir) { | ||
| return path.join(projectDir, ".ai-skills", PACKAGE_NAME); | ||
| } | ||
| function getSkillSourcePath(packageRoot, skillName) { | ||
| return path.join(packageRoot, skillName); | ||
| } | ||
| function getSkillDestPath(installRoot, skillName) { | ||
| return path.join(installRoot, skillName); | ||
| } | ||
| async function ensureDir(dirPath) { | ||
| await fsp.mkdir(dirPath, { recursive: true }); | ||
| } | ||
| async function removePath(targetPath) { | ||
| await fsp.rm(targetPath, { recursive: true, force: true }); | ||
| } | ||
| async function installSkill({ packageRoot, installRoot, skillName, mode, platform }) { | ||
| const sourcePath = getSkillSourcePath(packageRoot, skillName); | ||
| const destPath = getSkillDestPath(installRoot, skillName); | ||
| await removePath(destPath); | ||
| if (mode === "copy") { | ||
| await fsp.cp(sourcePath, destPath, { recursive: true }); | ||
| } else { | ||
| const symlinkType = platform === "windows" ? "junction" : "dir"; | ||
| await fsp.symlink(sourcePath, destPath, symlinkType); | ||
| } | ||
| return { | ||
| skillName, | ||
| sourcePath, | ||
| destPath, | ||
| }; | ||
| } | ||
| function buildSkillPaths(installRoot) { | ||
| return SKILLS.map((skill) => path.join(installRoot, skill, "SKILL.md")); | ||
| } | ||
| function quoteYamlString(value) { | ||
| return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`; | ||
| } | ||
| function replaceManagedBlock(content, block, commentStyle) { | ||
| const startMarker = commentStyle === "html" | ||
| ? `<!-- ${MANAGED_START} -->` | ||
| : `# ${MANAGED_START}`; | ||
| const endMarker = commentStyle === "html" | ||
| ? `<!-- ${MANAGED_END} -->` | ||
| : `# ${MANAGED_END}`; | ||
| const managedBlock = `${startMarker}\n${block}\n${endMarker}`; | ||
| const escapedStart = escapeRegExp(startMarker); | ||
| const escapedEnd = escapeRegExp(endMarker); | ||
| const pattern = new RegExp(`${escapedStart}[\\s\\S]*?${escapedEnd}`, "m"); | ||
| if (pattern.test(content)) { | ||
| return content.replace(pattern, managedBlock); | ||
| } | ||
| if (!content.trim()) { | ||
| return `${managedBlock}\n`; | ||
| } | ||
| return `${content.replace(/\s*$/, "")}\n\n${managedBlock}\n`; | ||
| } | ||
| function escapeRegExp(value) { | ||
| return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | ||
| } | ||
| function upsertSkillsSection(content, items, itemRenderer) { | ||
| const lines = content ? content.split(/\r?\n/) : []; | ||
| const sectionLines = ["skills:", ...items.map(itemRenderer)]; | ||
| const sectionStart = lines.findIndex((line) => /^skills:\s*$/.test(line.trim())); | ||
| if (sectionStart === -1) { | ||
| return `${lines.filter(Boolean).join("\n")}${lines.filter(Boolean).length ? "\n\n" : ""}${sectionLines.join("\n")}\n`; | ||
| } | ||
| let sectionEnd = sectionStart + 1; | ||
| while (sectionEnd < lines.length) { | ||
| const line = lines[sectionEnd]; | ||
| if (!line.trim()) { | ||
| sectionEnd += 1; | ||
| continue; | ||
| } | ||
| if (/^\s*-/.test(line) || /^\s*#/.test(line)) { | ||
| sectionEnd += 1; | ||
| continue; | ||
| } | ||
| break; | ||
| } | ||
| const merged = [ | ||
| ...lines.slice(0, sectionStart), | ||
| ...sectionLines, | ||
| ...lines.slice(sectionEnd), | ||
| ]; | ||
| return `${merged.join("\n").replace(/\s*$/, "")}\n`; | ||
| } | ||
| async function writeFileWithDirs(filePath, content) { | ||
| await ensureDir(path.dirname(filePath)); | ||
| await fsp.writeFile(filePath, content, "utf8"); | ||
| } | ||
| async function readIfExists(filePath) { | ||
| try { | ||
| return await fsp.readFile(filePath, "utf8"); | ||
| } catch (error) { | ||
| if (error.code === "ENOENT") { | ||
| return ""; | ||
| } | ||
| throw error; | ||
| } | ||
| } | ||
| async function configureClaude(projectDir, skillPaths) { | ||
| const filePath = path.join(projectDir, "CLAUDE.md"); | ||
| const current = await readIfExists(filePath); | ||
| const body = [ | ||
| "Load these skill files before starting work:", | ||
| "", | ||
| ...skillPaths.map((skillPath) => `- ${skillPath}`), | ||
| "", | ||
| "After loading, create concise implementation plan, state assumptions, then implement requested changes.", | ||
| ].join("\n"); | ||
| const next = replaceManagedBlock(current, body, "html"); | ||
| await writeFileWithDirs(filePath, next); | ||
| return filePath; | ||
| } | ||
| async function configureCursor(projectDir, skillPaths) { | ||
| const filePath = path.join(projectDir, ".cursor", "rules", "developer-stack-skills.mdc"); | ||
| const body = [ | ||
| "---", | ||
| "description: Load installed developer-stack-skills files before coding", | ||
| "globs: []", | ||
| "alwaysApply: false", | ||
| "---", | ||
| "", | ||
| "Read and follow these skill files before starting work:", | ||
| "", | ||
| ...skillPaths.map((skillPath) => `- ${skillPath}`), | ||
| ].join("\n"); | ||
| await writeFileWithDirs(filePath, body); | ||
| return filePath; | ||
| } | ||
| async function configureCline(projectDir, skillPaths) { | ||
| const filePath = path.join(projectDir, ".clinerules"); | ||
| const current = await readIfExists(filePath); | ||
| const next = upsertSkillsSection(current, skillPaths, (skillPath) => ` - ${quoteYamlString(skillPath)}`); | ||
| await writeFileWithDirs(filePath, next); | ||
| return filePath; | ||
| } | ||
| async function configureRoocode(projectDir, skillPaths) { | ||
| const filePath = path.join(projectDir, ".roo", "config.yml"); | ||
| const current = await readIfExists(filePath); | ||
| const next = upsertSkillsSection(current, skillPaths, (skillPath) => ` - path: ${quoteYamlString(skillPath)}`); | ||
| await writeFileWithDirs(filePath, next); | ||
| return filePath; | ||
| } | ||
| async function configureCopilot(projectDir, skillPaths) { | ||
| const filePath = path.join(projectDir, ".github", "copilot-instructions.md"); | ||
| const current = await readIfExists(filePath); | ||
| const body = [ | ||
| "Follow these skill files before producing code or process guidance:", | ||
| "", | ||
| ...skillPaths.map((skillPath) => `- ${skillPath}`), | ||
| ].join("\n"); | ||
| const next = replaceManagedBlock(current, body, "html"); | ||
| await writeFileWithDirs(filePath, next); | ||
| return filePath; | ||
| } | ||
| function getAgentTargets(agent) { | ||
| return agent === "all" ? AGENTS.filter((item) => item !== "all") : [agent]; | ||
| } | ||
| async function configureAgents(agent, projectDir, installRoot) { | ||
| const skillPaths = buildSkillPaths(installRoot); | ||
| const targets = getAgentTargets(agent); | ||
| const configured = []; | ||
| for (const target of targets) { | ||
| if (target === "claude") { | ||
| configured.push({ agent: target, filePath: await configureClaude(projectDir, skillPaths) }); | ||
| continue; | ||
| } | ||
| if (target === "cursor") { | ||
| configured.push({ agent: target, filePath: await configureCursor(projectDir, skillPaths) }); | ||
| continue; | ||
| } | ||
| if (target === "cline") { | ||
| configured.push({ agent: target, filePath: await configureCline(projectDir, skillPaths) }); | ||
| continue; | ||
| } | ||
| if (target === "roocode") { | ||
| configured.push({ agent: target, filePath: await configureRoocode(projectDir, skillPaths) }); | ||
| continue; | ||
| } | ||
| if (target === "copilot") { | ||
| configured.push({ agent: target, filePath: await configureCopilot(projectDir, skillPaths) }); | ||
| } | ||
| } | ||
| return configured; | ||
| } | ||
| async function collectAnswers(args) { | ||
| const prompt = createPrompt(); | ||
| try { | ||
| const agent = normalizeAgent(args.agent) || await chooseValue( | ||
| prompt, | ||
| "Agent to configure [all/claude/cursor/cline/roocode/copilot] (default: all): ", | ||
| AGENTS, | ||
| "all", | ||
| ); | ||
| const mode = normalizeMode(args.mode) || await chooseValue( | ||
| prompt, | ||
| "Install mode [copy/symlink] (default: symlink): ", | ||
| MODES, | ||
| "symlink", | ||
| ); | ||
| const projectDirInput = args.projectDir || await prompt.ask(`Project directory (default: ${process.cwd()}): `); | ||
| const projectDir = path.resolve(projectDirInput || process.cwd()); | ||
| return { | ||
| agent, | ||
| mode, | ||
| projectDir, | ||
| }; | ||
| } finally { | ||
| prompt.close(); | ||
| } | ||
| } | ||
| function validateArgs(args) { | ||
| const agent = normalizeAgent(args.agent || "all"); | ||
| const mode = normalizeMode(args.mode || "symlink"); | ||
| if (!AGENTS.includes(agent)) { | ||
| throw new Error(`Invalid agent "${args.agent}". Allowed: ${AGENTS.join(", ")}`); | ||
| } | ||
| if (!MODES.includes(mode)) { | ||
| throw new Error(`Invalid mode "${args.mode}". Allowed: ${MODES.join(", ")}`); | ||
| } | ||
| return { | ||
| agent, | ||
| mode, | ||
| projectDir: path.resolve(args.projectDir || process.cwd()), | ||
| }; | ||
| } | ||
| async function runInstall(rawArgs) { | ||
| const platform = detectPlatform(); | ||
| const packageRoot = getPackageRoot(); | ||
| const version = getVersion(); | ||
| const selected = rawArgs.yes ? validateArgs(rawArgs) : await collectAnswers(rawArgs); | ||
| const installRoot = getInstallRoot(selected.projectDir); | ||
| const packageInstallType = detectPackageInstallType(packageRoot, selected.projectDir); | ||
| console.log(`[${PACKAGE_NAME}] installing version ${version}`); | ||
| console.log(`[${PACKAGE_NAME}] package install type: ${packageInstallType}`); | ||
| console.log(`[${PACKAGE_NAME}] os: ${platform}`); | ||
| console.log(`[${PACKAGE_NAME}] package dir: ${packageRoot}`); | ||
| console.log(`[${PACKAGE_NAME}] project dir: ${selected.projectDir}`); | ||
| console.log(`[${PACKAGE_NAME}] install dir: ${installRoot}`); | ||
| console.log(`[${PACKAGE_NAME}] agent: ${selected.agent}`); | ||
| console.log(`[${PACKAGE_NAME}] mode: ${selected.mode}`); | ||
| await ensureDir(installRoot); | ||
| const installedSkills = []; | ||
| for (const skillName of SKILLS) { | ||
| const result = await installSkill({ | ||
| packageRoot, | ||
| installRoot, | ||
| skillName, | ||
| mode: selected.mode, | ||
| platform, | ||
| }); | ||
| installedSkills.push(result); | ||
| console.log(`[${PACKAGE_NAME}] skill installed: ${result.skillName} -> ${result.destPath}`); | ||
| } | ||
| const configured = await configureAgents(selected.agent, selected.projectDir, installRoot); | ||
| for (const item of configured) { | ||
| console.log(`[${PACKAGE_NAME}] ${item.agent} config updated: ${item.filePath}`); | ||
| } | ||
| console.log(`[${PACKAGE_NAME}] install complete`); | ||
| return { | ||
| version, | ||
| platform, | ||
| packageRoot, | ||
| projectDir: selected.projectDir, | ||
| installRoot, | ||
| installedSkills, | ||
| configured, | ||
| }; | ||
| } | ||
| module.exports = { | ||
| AGENTS, | ||
| MODES, | ||
| SKILLS, | ||
| buildSkillPaths, | ||
| configureAgents, | ||
| detectPlatform, | ||
| parseArgs, | ||
| printHelp, | ||
| replaceManagedBlock, | ||
| runInstall, | ||
| upsertSkillsSection, | ||
| validateArgs, | ||
| detectPackageInstallType, | ||
| }; |
+12
-3
| { | ||
| "name": "developer-stack-skills", | ||
| "version": "1.0.1", | ||
| "description": "AI agent SKILL.md files for Java/Spring, Python/FastAPI, React/Angular, Testing, and Project Conventions. Compatible with Claude, Cline, Roocode, Copilot, and Cursor.", | ||
| "version": "1.1.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.", | ||
| "keywords": [ | ||
@@ -29,3 +29,11 @@ "ai-agents", | ||
| "license": "MIT", | ||
| "bin": { | ||
| "developer-stack-skills": "bin/developer-stack-skills.js" | ||
| }, | ||
| "scripts": { | ||
| "test": "node --test" | ||
| }, | ||
| "files": [ | ||
| "bin/**/*", | ||
| "lib/**/*", | ||
| "java-spring/SKILL.md", | ||
@@ -37,3 +45,4 @@ "python-backend/SKILL.md", | ||
| "examples/**/*", | ||
| "README.md" | ||
| "README.md", | ||
| "CHANGELOG.md" | ||
| ], | ||
@@ -40,0 +49,0 @@ "skills": { |
+86
-33
@@ -15,51 +15,104 @@ # developer-stack-skills | ||
| --- | ||
| Global install: | ||
| ## Skills Included | ||
| ```bash | ||
| npm install -g developer-stack-skills | ||
| ``` | ||
| | Skill | File | Use When | | ||
| |---|---|---| | ||
| | `java-spring` | `java-spring/SKILL.md` | Spring Boot, JPA, REST APIs, JUnit | | ||
| | `python-backend` | `python-backend/SKILL.md` | FastAPI, SQLAlchemy, Pydantic, pytest | | ||
| | `frontend` | `frontend/SKILL.md` | React, Angular, TypeScript, TanStack Query | | ||
| | `testing` | `testing/SKILL.md` | Unit, integration, E2E across all stacks | | ||
| | `project-conventions` | `project-conventions/SKILL.md` | Git, ADRs, naming, PR standards, README | | ||
| Version in this README: `1.1.0` | ||
| --- | ||
| Run installer: | ||
| ## Usage by Agent | ||
| ```bash | ||
| developer-stack-skills install | ||
| ``` | ||
| ### Claude / Cursor | ||
| Point to the skill file in your system prompt or agent config: | ||
| Or run from local package without global install: | ||
| ```bash | ||
| npx developer-stack-skills install | ||
| ``` | ||
| Read node_modules/developer-stack-skills/java-spring/SKILL.md before writing any Java code. | ||
| Installer will: | ||
| 1. Detect OS automatically | ||
| 2. Ask which agent to configure: `all`, `claude`, `cursor`, `cline`, `roocode`, or `copilot` | ||
| 3. Ask whether to `copy` files or create `symlink` | ||
| 4. Ask which project directory to install into | ||
| 5. Install all skill folders into: | ||
| ```text | ||
| <project>/.ai-skills/developer-stack-skills/ | ||
| ``` | ||
| ### Cline (VS Code) | ||
| Add to `.clinerules` in your project root: | ||
| 6. Update agent-specific config files in that project | ||
| 7. Log package version, package install type (`local` or `global`), OS, source directory, install directory, and each generated config path | ||
| Installer does not run automatically on `npm install`. Run `developer-stack-skills install` or `npx developer-stack-skills install` when you want to configure a project. | ||
| Non-interactive install: | ||
| ```bash | ||
| developer-stack-skills install --agent all --mode symlink --dir . --yes | ||
| ``` | ||
| skills: | ||
| - 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 | ||
| ``` | ||
| ### Roocode | ||
| Add to `.roo/config.yml`: | ||
| ```yaml | ||
| skills: | ||
| - path: node_modules/developer-stack-skills/python-backend/SKILL.md | ||
| - path: node_modules/developer-stack-skills/frontend/SKILL.md | ||
| Example log output: | ||
| ```text | ||
| [developer-stack-skills] installing version 1.1.0 | ||
| [developer-stack-skills] package install type: global | ||
| [developer-stack-skills] os: windows | ||
| [developer-stack-skills] package dir: C:\Users\<you>\AppData\Roaming\npm\node_modules\developer-stack-skills | ||
| [developer-stack-skills] project dir: D:\Projects\my-app | ||
| [developer-stack-skills] install dir: D:\Projects\my-app\.ai-skills\developer-stack-skills | ||
| [developer-stack-skills] skill installed: java-spring -> D:\Projects\my-app\.ai-skills\developer-stack-skills\java-spring | ||
| [developer-stack-skills] cline config updated: D:\Projects\my-app\.clinerules | ||
| [developer-stack-skills] install complete | ||
| ``` | ||
| ### GitHub Copilot | ||
| Reference in `.github/copilot-instructions.md`: | ||
| ```markdown | ||
| Follow the conventions in: | ||
| - node_modules/developer-stack-skills/frontend/SKILL.md | ||
| - node_modules/developer-stack-skills/project-conventions/SKILL.md | ||
| Flags: | ||
| - `--agent <all|claude|cursor|cline|roocode|copilot>` | ||
| - `--mode <copy|symlink>` | ||
| - `--dir <project-directory>` | ||
| - `--yes` | ||
| --- | ||
| ## Installed Files | ||
| Skill files get copied or linked here: | ||
| ```text | ||
| <project>/.ai-skills/developer-stack-skills/ | ||
| ``` | ||
| Agent configs get created or updated here: | ||
| - `Claude`: `CLAUDE.md` | ||
| - `Cursor`: `.cursor/rules/developer-stack-skills.mdc` | ||
| - `Cline`: `.clinerules` | ||
| - `Roocode`: `.roo/config.yml` | ||
| - `GitHub Copilot`: `.github/copilot-instructions.md` | ||
| Notes: | ||
| - `copy` makes project-local copies of skill folders | ||
| - `symlink` keeps installed skills linked to package source | ||
| - Running installer again refreshes installed skill folders and rewrites managed config sections | ||
| --- | ||
| ## Skills Included | ||
| | Skill | File | Use When | | ||
| |---|---|---| | ||
| | `java-spring` | `java-spring/SKILL.md` | Spring Boot, JPA, REST APIs, JUnit | | ||
| | `python-backend` | `python-backend/SKILL.md` | FastAPI, SQLAlchemy, Pydantic, pytest | | ||
| | `frontend` | `frontend/SKILL.md` | React, Angular, TypeScript, TanStack Query | | ||
| | `testing` | `testing/SKILL.md` | Unit, integration, E2E across all stacks | | ||
| | `project-conventions` | `project-conventions/SKILL.md` | Git, ADRs, naming, PR standards, README | | ||
| --- | ||
| ## Stack | ||
@@ -66,0 +119,0 @@ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Empty package
Supply chain riskPackage does not contain any code. It may be removed, is name squatting, or the result of a faulty package publish.
Found 1 instance in 1 package
83877
24.81%19
18.75%436
Infinity%130
68.83%3
Infinity%