@abranjith/spec-lite 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/providers/copilot.ts","../src/providers/claude-code.ts","../src/providers/generic.ts","../src/providers/index.ts","../src/utils/prompts.ts","../src/utils/stacks.ts","../src/commands/update.ts","../src/commands/list.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { initCommand } from \"./commands/init.js\";\r\nimport { updateCommand } from \"./commands/update.js\";\r\nimport { listCommand } from \"./commands/list.js\";\r\nimport { createRequire } from \"module\";\r\n\r\nconst require = createRequire(import.meta.url);\r\nconst pkg = require(\"../package.json\");\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"spec-lite\")\r\n .description(\r\n \"Install structured AI sub-agent prompts into your workspace for any AI coding assistant\"\r\n )\r\n .version(pkg.version);\r\n\r\nprogram\r\n .command(\"init\")\r\n .description(\"Initialize spec-lite sub-agent prompts in your workspace\")\r\n .option(\r\n \"--ai <provider>\",\r\n \"AI provider to configure for (copilot, claude-code, generic)\"\r\n )\r\n .option(\r\n \"--exclude <prompts>\",\r\n \"Comma-separated list of prompts to exclude (e.g., brainstorm,readme)\"\r\n )\r\n .option(\"--force\", \"Overwrite existing files without prompting\", false)\r\n .option(\r\n \"--skip-profile\",\r\n \"Skip the project profile questionnaire (for CI/scripting)\"\r\n )\r\n .action(initCommand);\r\n\r\nprogram\r\n .command(\"update\")\r\n .description(\r\n \"Update spec-lite prompts to the latest version, preserving your Project Context edits\"\r\n )\r\n .option(\"--force\", \"Overwrite all files including user-modified ones\", false)\r\n .action(updateCommand);\r\n\r\nprogram\r\n .command(\"list\")\r\n .description(\"List all available spec-lite sub-agents and their purpose\")\r\n .action(listCommand);\r\n\r\nprogram.parse();\r\n","import path from \"path\";\r\nimport fs from \"fs-extra\";\r\nimport chalk from \"chalk\";\r\nimport inquirer from \"inquirer\";\r\nimport { getProvider, getAllProviders } from \"../providers/index.js\";\r\nimport type { SpecLiteConfig, ProjectProfile } from \"../providers/base.js\";\r\nimport { loadPrompts, replaceProjectContext } from \"../utils/prompts.js\";\r\nimport { generateClaudeRootMd } from \"../providers/claude-code.js\";\r\nimport { getStackSnippet } from \"../utils/stacks.js\";\r\n\r\ninterface InitOptions {\r\n ai?: string;\r\n exclude?: string;\r\n force?: boolean;\r\n skipProfile?: boolean;\r\n}\r\n\r\nconst LANGUAGE_CHOICES = [\r\n { name: \"TypeScript\", value: \"TypeScript\" },\r\n { name: \"Python\", value: \"Python\" },\r\n { name: \"Java\", value: \"Java\" },\r\n { name: \"C# / .NET\", value: \"C#\" },\r\n { name: \"Go\", value: \"Go\" },\r\n { name: \"Rust\", value: \"Rust\" },\r\n { name: \"Other (specify below)\", value: \"__other__\" },\r\n];\r\n\r\nconst ARCHITECTURE_CHOICES = [\r\n { name: \"Monolith\", value: \"Monolith\" },\r\n { name: \"Microservices\", value: \"Microservices\" },\r\n { name: \"Serverless\", value: \"Serverless\" },\r\n { name: \"Monorepo\", value: \"Monorepo\" },\r\n { name: \"Other (specify below)\", value: \"__other__\" },\r\n];\r\n\r\n/**\r\n * Collect project profile via interactive questionnaire.\r\n * Returns a ProjectProfile with the user's answers.\r\n */\r\nasync function collectProjectProfile(): Promise<ProjectProfile> {\r\n console.log(\r\n chalk.cyan(\r\n \"\\n šŸ“‹ Project Profile — a few questions to personalize your setup:\\n\"\r\n )\r\n );\r\n\r\n const answers = await inquirer.prompt([\r\n {\r\n type: \"list\",\r\n name: \"language\",\r\n message: \"Primary programming language?\",\r\n choices: LANGUAGE_CHOICES,\r\n },\r\n {\r\n type: \"input\",\r\n name: \"languageOther\",\r\n message: \"Specify your primary language:\",\r\n when: (prev: Record<string, string>) => prev.language === \"__other__\",\r\n validate: (input: string) =>\r\n input.trim() ? true : \"Please enter a language.\",\r\n },\r\n {\r\n type: \"input\",\r\n name: \"frameworks\",\r\n message:\r\n 'Framework(s) in use? (e.g., \"Express + React\", \"FastAPI\", \"ASP.NET Core\")',\r\n default: \"None / not sure yet\",\r\n },\r\n {\r\n type: \"input\",\r\n name: \"testFramework\",\r\n message:\r\n 'Testing framework? (e.g., \"Jest\", \"Vitest\", \"pytest\", \"xUnit\")',\r\n default: \"Not decided yet\",\r\n },\r\n {\r\n type: \"list\",\r\n name: \"architecture\",\r\n message: \"Architectural pattern?\",\r\n choices: ARCHITECTURE_CHOICES,\r\n },\r\n {\r\n type: \"input\",\r\n name: \"architectureOther\",\r\n message: \"Specify your architectural pattern:\",\r\n when: (prev: Record<string, string>) => prev.architecture === \"__other__\",\r\n validate: (input: string) =>\r\n input.trim() ? true : \"Please enter a pattern.\",\r\n },\r\n {\r\n type: \"input\",\r\n name: \"conventions\",\r\n message:\r\n 'Any specific coding conventions? (e.g., \"Airbnb style guide\", \"PEP 8\") — leave blank if none',\r\n default: \"\",\r\n },\r\n ]);\r\n\r\n return {\r\n language:\r\n answers.language === \"__other__\"\r\n ? answers.languageOther.trim()\r\n : answers.language,\r\n frameworks: answers.frameworks.trim(),\r\n testFramework: answers.testFramework.trim(),\r\n architecture:\r\n answers.architecture === \"__other__\"\r\n ? answers.architectureOther.trim()\r\n : answers.architecture,\r\n conventions: answers.conventions.trim(),\r\n };\r\n}\r\n\r\n/**\r\n * Build a Project Context block string from a ProjectProfile.\r\n * This replaces the placeholder content inside <!-- project-context-start/end --> markers.\r\n */\r\nfunction buildProjectContextBlock(profile: ProjectProfile): string {\r\n const lines = [\r\n \"\",\r\n \"## Project Context (Customize per project)\",\r\n \"\",\r\n \"> Auto-populated by spec-lite init. Edit these values as your project evolves.\",\r\n \"\",\r\n `- **Language(s)**: ${profile.language}`,\r\n `- **Framework(s)**: ${profile.frameworks}`,\r\n `- **Test Framework**: ${profile.testFramework}`,\r\n `- **Architecture**: ${profile.architecture}`,\r\n ];\r\n if (profile.conventions) {\r\n lines.push(`- **Conventions**: ${profile.conventions}`);\r\n }\r\n lines.push(\"\");\r\n return lines.join(\"\\n\");\r\n}\r\n\r\nexport async function initCommand(options: InitOptions): Promise<void> {\r\n const cwd = process.cwd();\r\n\r\n console.log(chalk.bold(\"\\n⚔ spec-lite init\\n\"));\r\n\r\n // 1. Resolve provider\r\n let providerAlias = options.ai;\r\n\r\n if (!providerAlias) {\r\n const providers = getAllProviders();\r\n const answer = await inquirer.prompt([\r\n {\r\n type: \"list\",\r\n name: \"provider\",\r\n message: \"Which AI coding assistant are you using?\",\r\n choices: providers.map((p) => ({\r\n name: `${p.name} — ${p.description}`,\r\n value: p.alias,\r\n })),\r\n },\r\n ]);\r\n providerAlias = answer.provider as string;\r\n }\r\n\r\n const provider = getProvider(providerAlias!);\r\n if (!provider) {\r\n console.error(\r\n chalk.red(\r\n `Unknown provider: \"${providerAlias}\". Available: ${getAllProviders()\r\n .map((p) => p.alias)\r\n .join(\", \")}`\r\n )\r\n );\r\n process.exit(1);\r\n }\r\n\r\n console.log(chalk.cyan(` Provider: ${provider.name}`));\r\n\r\n // 2. Collect project profile (unless --skip-profile)\r\n let projectProfile: ProjectProfile | undefined;\r\n if (!options.skipProfile) {\r\n projectProfile = await collectProjectProfile();\r\n console.log(chalk.green(\" āœ“ Project profile collected\"));\r\n } else {\r\n console.log(chalk.dim(\" Skipping project profile questionnaire.\"));\r\n }\r\n\r\n // 3. Parse exclusions (split on commas or spaces to handle both bash and PowerShell)\r\n const exclude = options.exclude\r\n ? options.exclude.split(/[,\\s]+/).map((s) => s.trim()).filter(Boolean)\r\n : [];\r\n\r\n if (exclude.length > 0) {\r\n console.log(chalk.dim(` Excluding: ${exclude.join(\", \")}`));\r\n }\r\n\r\n // 4. Check for existing files\r\n const existingFiles = await provider.detectExisting(cwd);\r\n if (existingFiles.length > 0 && !options.force) {\r\n console.log(\r\n chalk.yellow(\r\n `\\n Found existing instruction files:\\n${existingFiles\r\n .map((f) => ` - ${f}`)\r\n .join(\"\\n\")}`\r\n )\r\n );\r\n\r\n const answer = await inquirer.prompt([\r\n {\r\n type: \"list\",\r\n name: \"action\",\r\n message: \"How should we handle existing files?\",\r\n choices: [\r\n {\r\n name: \"Overwrite — replace all existing files\",\r\n value: \"overwrite\",\r\n },\r\n {\r\n name: \"Skip — only write files that don't exist yet\",\r\n value: \"skip\",\r\n },\r\n { name: \"Abort — cancel initialization\", value: \"abort\" },\r\n ],\r\n },\r\n ]);\r\n\r\n if (answer.action === \"abort\") {\r\n console.log(chalk.dim(\" Aborted.\"));\r\n return;\r\n }\r\n\r\n if (answer.action === \"skip\") {\r\n // We'll skip existing files during write\r\n console.log(chalk.dim(\" Skipping existing files.\"));\r\n }\r\n\r\n // For \"overwrite\", we just proceed normally\r\n }\r\n\r\n // 5. Build project context block (if profile was collected)\r\n const contextBlock = projectProfile\r\n ? buildProjectContextBlock(projectProfile)\r\n : null;\r\n\r\n // 6. Load and write prompts\r\n const prompts = await loadPrompts(exclude);\r\n let written = 0;\r\n let skipped = 0;\r\n const installedPrompts: string[] = [];\r\n\r\n for (const prompt of prompts) {\r\n const targetRelPath = provider.getTargetPath(prompt.name);\r\n const targetAbsPath = path.join(cwd, targetRelPath);\r\n\r\n // Skip if file exists and user chose \"skip\"\r\n if (\r\n !options.force &&\r\n existingFiles.includes(targetRelPath) &&\r\n existingFiles.length > 0\r\n ) {\r\n const answer = await inquirer.prompt([\r\n {\r\n type: \"list\",\r\n name: \"action\",\r\n message: `How should we handle existing files?`,\r\n choices: [\r\n { name: \"Overwrite\", value: \"overwrite\" },\r\n { name: \"Skip\", value: \"skip\" },\r\n ],\r\n },\r\n ]);\r\n if (answer.action === \"skip\") {\r\n skipped++;\r\n installedPrompts.push(prompt.name);\r\n continue;\r\n }\r\n }\r\n\r\n // Inject project context into prompt if profile was collected\r\n let content = prompt.content;\r\n if (contextBlock) {\r\n content = replaceProjectContext(content, contextBlock);\r\n }\r\n\r\n const transformed = provider.transformPrompt(content, {\r\n name: prompt.name,\r\n title: prompt.title,\r\n description: prompt.description,\r\n });\r\n\r\n await fs.ensureDir(path.dirname(targetAbsPath));\r\n await fs.writeFile(targetAbsPath, transformed, \"utf-8\");\r\n written++;\r\n installedPrompts.push(prompt.name);\r\n\r\n console.log(chalk.green(` āœ“ ${targetRelPath}`));\r\n }\r\n\r\n // 7. Provider-specific extras\r\n if (provider.alias === \"claude-code\") {\r\n const claudeMdPath = path.join(cwd, \"CLAUDE.md\");\r\n const claudeMdContent = generateClaudeRootMd(installedPrompts);\r\n await fs.writeFile(claudeMdPath, claudeMdContent, \"utf-8\");\r\n console.log(chalk.green(` āœ“ CLAUDE.md`));\r\n written++;\r\n }\r\n\r\n // 8. Create .spec/ directory structure\r\n const specDirs = [\r\n \".spec\",\r\n path.join(\".spec\", \"features\"),\r\n path.join(\".spec\", \"reviews\"),\r\n ];\r\n for (const dir of specDirs) {\r\n const absDir = path.join(cwd, dir);\r\n if (!(await fs.pathExists(absDir))) {\r\n await fs.ensureDir(absDir);\r\n console.log(chalk.green(` āœ“ ${dir}/`));\r\n }\r\n }\r\n\r\n // 8b. Create .spec/TODO.md skeleton\r\n const todoPath = path.join(cwd, \".spec\", \"TODO.md\");\r\n if (!(await fs.pathExists(todoPath))) {\r\n const todoContent = [\r\n \"# TODO — Enhancements & Ideas\",\r\n \"\",\r\n \"> Discovered by sub-agents during planning and development.\",\r\n \"> Items here are out-of-scope for their current task but worth tracking.\",\r\n \"\",\r\n \"## General\",\r\n \"\",\r\n \"## General / Caching\",\r\n \"\",\r\n \"## UI\",\r\n \"\",\r\n \"## Performance\",\r\n \"\",\r\n \"## Security\",\r\n \"\",\r\n \"## DX (Developer Experience)\",\r\n \"\",\r\n ].join(\"\\n\");\r\n await fs.writeFile(todoPath, todoContent, \"utf-8\");\r\n console.log(chalk.green(` āœ“ .spec/TODO.md`));\r\n }\r\n\r\n // 9. Copy bundled stack snippet to .spec-lite/stacks/ (if profile was collected)\r\n if (projectProfile) {\r\n const snippet = getStackSnippet(projectProfile.language);\r\n if (snippet) {\r\n const stacksTargetDir = path.join(cwd, \".spec-lite\", \"stacks\");\r\n await fs.ensureDir(stacksTargetDir);\r\n const snippetFileName = `${projectProfile.language.toLowerCase().replace(/[^a-z0-9]/g, \"-\")}.md`;\r\n const snippetPath = path.join(stacksTargetDir, snippetFileName);\r\n\r\n if (await fs.pathExists(snippetPath) && !options.force) {\r\n console.log(\r\n chalk.dim(` – .spec-lite/stacks/${snippetFileName} already exists (kept your edits)`)\r\n );\r\n } else {\r\n await fs.writeFile(snippetPath, snippet, \"utf-8\");\r\n console.log(\r\n chalk.green(` āœ“ .spec-lite/stacks/${snippetFileName}`)\r\n );\r\n console.log(\r\n chalk.dim(\" ↳ Edit this file to customize defaults before running /memorize bootstrap\")\r\n );\r\n written++;\r\n }\r\n }\r\n }\r\n\r\n // 10. Write .spec-lite.json config\r\n const pkg = await loadPackageVersion();\r\n const config: SpecLiteConfig = {\r\n version: pkg,\r\n provider: provider.alias,\r\n installedPrompts,\r\n installedAt: new Date().toISOString(),\r\n updatedAt: new Date().toISOString(),\r\n ...(projectProfile ? { projectProfile } : {}),\r\n };\r\n const configPath = path.join(cwd, \".spec-lite.json\");\r\n await fs.writeJson(configPath, config, { spaces: 2 });\r\n console.log(chalk.green(` āœ“ .spec-lite.json`));\r\n\r\n // 11. Summary\r\n console.log(\r\n chalk.bold(\r\n `\\n Done! ${written} files written, ${skipped} skipped.`\r\n )\r\n );\r\n console.log(provider.getPostInitMessage());\r\n\r\n // 12. Bootstrap next-step guidance\r\n if (projectProfile) {\r\n console.log(\r\n chalk.cyan(\r\n \"\\n šŸ“Œ Next step: Run \") +\r\n chalk.bold(\"/memorize bootstrap\") +\r\n chalk.cyan(\r\n \" in your AI assistant to auto-generate\\n coding standards, architecture guidelines, and best practices\\n for your project based on the profile you just provided.\"\r\n )\r\n );\r\n }\r\n}\r\n\r\nasync function loadPackageVersion(): Promise<string> {\r\n try {\r\n const { createRequire } = await import(\"module\");\r\n const require = createRequire(import.meta.url);\r\n const pkg = require(\"../../package.json\");\r\n return pkg.version;\r\n } catch {\r\n return \"1.0.0\";\r\n }\r\n}\r\n","import path from \"path\";\r\nimport fs from \"fs-extra\";\r\nimport type { Provider, PromptMeta } from \"./base.js\";\r\n\r\n/**\r\n * GitHub Copilot provider.\r\n *\r\n * Writes individual agent files to `.github/copilot-instructions/` as markdown,\r\n * plus a main `.github/copilot-instructions.md` that references the spec-lite agents.\r\n *\r\n * GitHub Copilot supports:\r\n * - `.github/copilot-instructions.md` — global instructions loaded into every Copilot Chat session\r\n * - Individual prompt files that can be referenced via @workspace or loaded as custom instructions\r\n */\r\nexport class CopilotProvider implements Provider {\r\n name = \"GitHub Copilot\";\r\n alias = \"copilot\";\r\n description = \"GitHub Copilot (VS Code, JetBrains, Neovim)\";\r\n\r\n getTargetPath(promptName: string): string {\r\n return path.join(\".github\", \"copilot\", `${promptName}.prompt.md`);\r\n }\r\n\r\n transformPrompt(content: string, meta: PromptMeta): string {\r\n // Copilot expects standard markdown. We add a brief header comment\r\n // identifying this as a spec-lite managed file.\r\n const header = [\r\n `<!-- spec-lite | ${meta.name} | DO NOT EDIT below the project-context block — managed by spec-lite -->`,\r\n `<!-- To update: run \"spec-lite update\" — your Project Context edits will be preserved -->`,\r\n \"\",\r\n ].join(\"\\n\");\r\n\r\n return header + content;\r\n }\r\n\r\n async detectExisting(workspaceRoot: string): Promise<string[]> {\r\n const existing: string[] = [];\r\n\r\n // Check for copilot instructions directory\r\n const copilotDir = path.join(workspaceRoot, \".github\", \"copilot\");\r\n if (await fs.pathExists(copilotDir)) {\r\n const files = await fs.readdir(copilotDir);\r\n for (const f of files) {\r\n if (f.endsWith(\".prompt.md\") || f.endsWith(\".md\")) {\r\n existing.push(path.join(\".github\", \"copilot\", f));\r\n }\r\n }\r\n }\r\n\r\n // Check for main instructions file\r\n const mainFile = path.join(\r\n workspaceRoot,\r\n \".github\",\r\n \"copilot-instructions.md\"\r\n );\r\n if (await fs.pathExists(mainFile)) {\r\n existing.push(\".github/copilot-instructions.md\");\r\n }\r\n\r\n return existing;\r\n }\r\n\r\n getPostInitMessage(): string {\r\n return [\r\n \"\",\r\n \"šŸ“‹ GitHub Copilot setup complete!\",\r\n \"\",\r\n \" Your sub-agent prompts are in .github/copilot/\",\r\n \"\",\r\n \" How to use:\",\r\n \" 1. Open GitHub Copilot Chat in VS Code\",\r\n \" 2. Sub-agents are available as slash commands — type /prompt_name\",\r\n \" (e.g., /brainstorm, /planner, /feature)\",\r\n \" 3. Files ending in .prompt.md are natively recognized by Copilot\",\r\n \" 4. Customize the Project Context block in each file for your project\",\r\n \"\",\r\n ].join(\"\\n\");\r\n }\r\n}\r\n","import path from \"path\";\r\nimport fs from \"fs-extra\";\r\nimport type { Provider, PromptMeta } from \"./base.js\";\r\n\r\n/**\r\n * Claude Code (Anthropic) provider.\r\n *\r\n * Claude Code supports:\r\n * - `CLAUDE.md` at the project root — loaded automatically as project instructions\r\n * - `.claude/` directory for additional configuration\r\n * - Nested `CLAUDE.md` files in subdirectories for scoped instructions\r\n *\r\n * Strategy:\r\n * - Write individual agent files to `.claude/prompts/<name>.md`\r\n * - Create/update a root `CLAUDE.md` that references the spec-lite agent collection\r\n * and provides a high-level overview\r\n */\r\nexport class ClaudeCodeProvider implements Provider {\r\n name = \"Claude Code\";\r\n alias = \"claude-code\";\r\n description = \"Claude Code (Anthropic's coding agent)\";\r\n\r\n getTargetPath(promptName: string): string {\r\n return path.join(\".claude\", \"prompts\", `${promptName}.md`);\r\n }\r\n\r\n transformPrompt(content: string, meta: PromptMeta): string {\r\n // Claude Code reads markdown natively. We add a header comment.\r\n const header = [\r\n `<!-- spec-lite | ${meta.name} | DO NOT EDIT below the project-context block — managed by spec-lite -->`,\r\n `<!-- To update: run \"spec-lite update\" — your Project Context edits will be preserved -->`,\r\n \"\",\r\n ].join(\"\\n\");\r\n\r\n return header + content;\r\n }\r\n\r\n async detectExisting(workspaceRoot: string): Promise<string[]> {\r\n const existing: string[] = [];\r\n\r\n // Check for claude prompts directory\r\n const claudePromptsDir = path.join(\r\n workspaceRoot,\r\n \".claude\",\r\n \"prompts\"\r\n );\r\n if (await fs.pathExists(claudePromptsDir)) {\r\n const files = await fs.readdir(claudePromptsDir);\r\n for (const f of files) {\r\n if (f.endsWith(\".md\")) {\r\n existing.push(path.join(\".claude\", \"prompts\", f));\r\n }\r\n }\r\n }\r\n\r\n // Check for root CLAUDE.md\r\n const rootFile = path.join(workspaceRoot, \"CLAUDE.md\");\r\n if (await fs.pathExists(rootFile)) {\r\n existing.push(\"CLAUDE.md\");\r\n }\r\n\r\n return existing;\r\n }\r\n\r\n getPostInitMessage(): string {\r\n return [\r\n \"\",\r\n \"šŸ“‹ Claude Code setup complete!\",\r\n \"\",\r\n \" Your sub-agent prompts are in .claude/prompts/\",\r\n \" A root CLAUDE.md has been created with references to the sub-agents.\",\r\n \"\",\r\n \" How to use:\",\r\n \" 1. Claude Code automatically reads CLAUDE.md for project context\",\r\n ' 2. Reference specific sub-agents: \"Use the planner from .claude/prompts/planner.md\"',\r\n \" 3. Customize the Project Context block in each file for your project\",\r\n \"\",\r\n ].join(\"\\n\");\r\n }\r\n}\r\n\r\n/**\r\n * Generate the root CLAUDE.md content that references spec-lite sub-agents.\r\n */\r\nexport function generateClaudeRootMd(\r\n installedPrompts: string[]\r\n): string {\r\n const lines = [\r\n \"<!-- spec-lite managed — regenerated on spec-lite init/update -->\",\r\n \"\",\r\n \"# Project Instructions\",\r\n \"\",\r\n \"This project uses [spec-lite](https://github.com/ranjithab/spec-lite) sub-agent prompts\",\r\n \"for structured software engineering workflows.\",\r\n \"\",\r\n \"## Available Sub-Agents\",\r\n \"\",\r\n \"The following specialist sub-agents are available in `.claude/prompts/`:\",\r\n \"\",\r\n ];\r\n\r\n for (const name of installedPrompts) {\r\n lines.push(`- [${name}](.claude/prompts/${name}.md)`);\r\n }\r\n\r\n lines.push(\r\n \"\",\r\n \"## Usage\",\r\n \"\",\r\n \"To use a sub-agent, reference its prompt file in your conversation:\",\r\n \"\",\r\n '```text',\r\n \"Use the planner from .claude/prompts/planner.md to create a technical plan for this project.\",\r\n '```',\r\n \"\",\r\n \"## Output Directory\",\r\n \"\",\r\n \"Sub-agent outputs are written to the `.spec/` directory:\",\r\n \"\",\r\n \"```text\",\r\n \".spec/\",\r\n \"ā”œā”€ā”€ brainstorm.md\",\r\n \"ā”œā”€ā”€ plan.md # Default plan (simple projects)\",\r\n \"ā”œā”€ā”€ plan_<name>.md # Named plans (complex projects)\",\r\n \"ā”œā”€ā”€ TODO.md\",\r\n \"ā”œā”€ā”€ features/\",\r\n \"└── reviews/\",\r\n \"```\",\r\n \"\"\r\n );\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n","import path from \"path\";\r\nimport fs from \"fs-extra\";\r\nimport type { Provider, PromptMeta } from \"./base.js\";\r\n\r\n/**\r\n * Generic provider — for manual usage or unsupported AI tools.\r\n *\r\n * Writes prompts to `.spec-lite/prompts/` as raw markdown.\r\n * Users can copy-paste these into any LLM chat.\r\n */\r\nexport class GenericProvider implements Provider {\r\n name = \"Generic\";\r\n alias = \"generic\";\r\n description = \"Raw prompts in .spec-lite/prompts/ (copy-paste into any LLM)\";\r\n\r\n getTargetPath(promptName: string): string {\r\n return path.join(\".spec-lite\", \"prompts\", `${promptName}.md`);\r\n }\r\n\r\n transformPrompt(content: string, meta: PromptMeta): string {\r\n // No transformation — copy as-is with a light header\r\n const header = [\r\n `<!-- spec-lite | ${meta.name} | managed by spec-lite -->`,\r\n `<!-- To update: run \"spec-lite update\" -->`,\r\n \"\",\r\n ].join(\"\\n\");\r\n\r\n return header + content;\r\n }\r\n\r\n async detectExisting(workspaceRoot: string): Promise<string[]> {\r\n const existing: string[] = [];\r\n const dir = path.join(workspaceRoot, \".spec-lite\", \"prompts\");\r\n\r\n if (await fs.pathExists(dir)) {\r\n const files = await fs.readdir(dir);\r\n for (const f of files) {\r\n if (f.endsWith(\".md\")) {\r\n existing.push(path.join(\".spec-lite\", \"prompts\", f));\r\n }\r\n }\r\n }\r\n\r\n return existing;\r\n }\r\n\r\n getPostInitMessage(): string {\r\n return [\r\n \"\",\r\n \"šŸ“‹ Generic setup complete!\",\r\n \"\",\r\n \" Your sub-agent prompts are in .spec-lite/prompts/\",\r\n \"\",\r\n \" How to use:\",\r\n \" 1. Open any prompt file and copy its content\",\r\n \" 2. Paste into your LLM of choice (ChatGPT, Claude, Gemini, etc.)\",\r\n \" 3. Fill in the Project Context section for your project\",\r\n \" 4. Start the conversation with your requirements\",\r\n \"\",\r\n ].join(\"\\n\");\r\n }\r\n}\r\n","import type { Provider } from \"./base.js\";\r\nimport { CopilotProvider } from \"./copilot.js\";\r\nimport { ClaudeCodeProvider } from \"./claude-code.js\";\r\nimport { GenericProvider } from \"./generic.js\";\r\n\r\n/**\r\n * Registry of all supported AI provider adapters.\r\n */\r\nconst providers: Provider[] = [\r\n new CopilotProvider(),\r\n new ClaudeCodeProvider(),\r\n new GenericProvider(),\r\n];\r\n\r\n/**\r\n * Get a provider by its CLI alias (e.g., \"copilot\", \"claude-code\", \"generic\").\r\n */\r\nexport function getProvider(alias: string): Provider | undefined {\r\n return providers.find((p) => p.alias === alias);\r\n}\r\n\r\n/**\r\n * Get all registered providers.\r\n */\r\nexport function getAllProviders(): Provider[] {\r\n return [...providers];\r\n}\r\n\r\n/**\r\n * Get a formatted list of provider aliases for display.\r\n */\r\nexport function getProviderAliases(): string[] {\r\n return providers.map((p) => p.alias);\r\n}\r\n\r\n// Re-export types\r\nexport type { Provider, PromptMeta, SpecLiteConfig } from \"./base.js\";\r\n","import path from \"path\";\r\nimport fs from \"fs-extra\";\r\nimport { fileURLToPath } from \"url\";\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = path.dirname(__filename);\r\n\r\n/** Path to the bundled prompts directory (shipped with the npm package) */\r\nexport function getPromptsDir(): string {\r\n // In the built output (tsup bundles to dist/index.js),\r\n // __dirname resolves to dist/ — prompts/ is one level up at the package root\r\n return path.resolve(__dirname, \"..\", \"prompts\");\r\n}\r\n\r\n/** Metadata extracted from prompt files */\r\nexport interface PromptFile {\r\n /** File name without extension */\r\n name: string;\r\n /** Full file path */\r\n filePath: string;\r\n /** Raw markdown content */\r\n content: string;\r\n /** Extracted title */\r\n title: string;\r\n /** Extracted description */\r\n description: string;\r\n}\r\n\r\n/** Map of prompt names to their human titles and descriptions */\r\nexport const PROMPT_CATALOG: Record<string, { title: string; description: string; output?: string }> = {\r\n spec_help: {\r\n title: \"Spec Help\",\r\n description: \"Lists available sub-agents, their purpose, inputs, and outputs\",\r\n output: \"(interactive guide)\",\r\n },\r\n brainstorm: {\r\n title: \"Brainstorm\",\r\n description: \"Refines a vague idea into a clear, actionable vision\",\r\n output: \".spec/brainstorm.md\",\r\n },\r\n planner: {\r\n title: \"Planner\",\r\n description: \"Creates a detailed technical blueprint from requirements\",\r\n output: \".spec/plan.md or .spec/plan_<name>.md\",\r\n },\r\n feature: {\r\n title: \"Feature\",\r\n description: \"Breaks one feature into granular, verifiable vertical slices\",\r\n output: \".spec/features/feature_<name>.md\",\r\n },\r\n implement: {\r\n title: \"Implement\",\r\n description: \"Picks up a feature spec and executes its tasks with code\",\r\n output: \"Working code + updated feature spec\",\r\n },\r\n code_review: {\r\n title: \"Code Review\",\r\n description: \"Reviews code for correctness, architecture, and readability\",\r\n output: \".spec/reviews/code_review_<name>.md\",\r\n },\r\n security_audit: {\r\n title: \"Security Audit\",\r\n description: \"Scans for vulnerabilities, misconfigurations, and security risks\",\r\n output: \".spec/reviews/security_audit_<scope>.md\",\r\n },\r\n performance_review: {\r\n title: \"Performance Review\",\r\n description: \"Identifies bottlenecks and optimization opportunities\",\r\n output: \".spec/reviews/performance_review_<scope>.md\",\r\n },\r\n integration_tests: {\r\n title: \"Integration Tests\",\r\n description: \"Writes traceable integration test scenarios from feature specs\",\r\n output: \"tests/\",\r\n },\r\n unit_tests: {\r\n title: \"Unit Tests\",\r\n description: \"Generates comprehensive unit tests with edge-case coverage and smart coverage exclusions\",\r\n output: \".spec/features/unit_tests_<name>.md\",\r\n },\r\n devops: {\r\n title: \"DevOps\",\r\n description: \"Sets up Docker, CI/CD, environments, and deployment\",\r\n output: \"Project infrastructure files\",\r\n },\r\n fix: {\r\n title: \"Fix & Refactor\",\r\n description: \"Debugs issues or restructures code safely\",\r\n output: \"Targeted fixes with verification\",\r\n },\r\n memorize: {\r\n title: \"Memorize\",\r\n description:\r\n \"Stores standing instructions that all sub-agents enforce. Use `/memorize bootstrap` to auto-generate from project analysis.\",\r\n output: \".spec/memory.md\",\r\n },\r\n technical_docs: {\r\n title: \"Technical Docs\",\r\n description: \"Creates deep architecture documentation for developers\",\r\n output: \"docs/technical_architecture.md\",\r\n },\r\n readme: {\r\n title: \"README\",\r\n description: \"Writes the project README and optional user guide\",\r\n output: \"README.md + docs/user_guide.md\",\r\n },\r\n};\r\n\r\n/** Non-agent files to skip */\r\nconst SKIP_FILES = new Set([\"orchestrator\"]);\r\n\r\n/**\r\n * Load all available prompt files from the bundled prompts directory.\r\n * Excludes non-agent files (orchestrator).\r\n */\r\nexport async function loadPrompts(\r\n exclude: string[] = []\r\n): Promise<PromptFile[]> {\r\n const promptsDir = getPromptsDir();\r\n const excludeSet = new Set([...exclude, ...SKIP_FILES]);\r\n\r\n const files = await fs.readdir(promptsDir);\r\n const prompts: PromptFile[] = [];\r\n\r\n for (const file of files) {\r\n if (!file.endsWith(\".md\")) continue;\r\n\r\n const name = file.replace(\".md\", \"\");\r\n if (excludeSet.has(name)) continue;\r\n\r\n const filePath = path.join(promptsDir, file);\r\n const content = await fs.readFile(filePath, \"utf-8\");\r\n const catalog = PROMPT_CATALOG[name];\r\n\r\n prompts.push({\r\n name,\r\n filePath,\r\n content,\r\n title: catalog?.title ?? name,\r\n description: catalog?.description ?? \"\",\r\n });\r\n }\r\n\r\n return prompts;\r\n}\r\n\r\n/**\r\n * Get the list of all available prompt names.\r\n */\r\nexport function getAvailablePromptNames(): string[] {\r\n return Object.keys(PROMPT_CATALOG);\r\n}\r\n\r\n/**\r\n * Get the full prompt catalog (for display in CLI list command).\r\n */\r\nexport function getPromptCatalog(): Record<string, { title: string; description: string; output?: string }> {\r\n return PROMPT_CATALOG;\r\n}\r\n\r\n/**\r\n * Project Context markers used to preserve user edits during updates.\r\n */\r\nexport const CONTEXT_START_MARKER = \"<!-- project-context-start -->\";\r\nexport const CONTEXT_END_MARKER = \"<!-- project-context-end -->\";\r\n\r\n/**\r\n * Extract the Project Context block from a prompt.\r\n * Returns the content between markers, or null if not found.\r\n */\r\nexport function extractProjectContext(content: string): string | null {\r\n const startIdx = content.indexOf(CONTEXT_START_MARKER);\r\n const endIdx = content.indexOf(CONTEXT_END_MARKER);\r\n\r\n if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) return null;\r\n\r\n return content.substring(\r\n startIdx + CONTEXT_START_MARKER.length,\r\n endIdx\r\n );\r\n}\r\n\r\n/**\r\n * Replace the Project Context block in a prompt with new content.\r\n */\r\nexport function replaceProjectContext(\r\n content: string,\r\n newContext: string\r\n): string {\r\n const startIdx = content.indexOf(CONTEXT_START_MARKER);\r\n const endIdx = content.indexOf(CONTEXT_END_MARKER);\r\n\r\n if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) return content;\r\n\r\n return (\r\n content.substring(0, startIdx + CONTEXT_START_MARKER.length) +\r\n newContext +\r\n content.substring(endIdx)\r\n );\r\n}\r\n","import path from \"path\";\r\nimport fs from \"fs-extra\";\r\nimport { fileURLToPath } from \"url\";\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = path.dirname(__filename);\r\n\r\n/**\r\n * Path to the bundled stacks directory (shipped with the npm package).\r\n * In the built output (tsup bundles to dist/index.js),\r\n * __dirname resolves to dist/ — stacks/ is copied to dist/stacks/ during build.\r\n */\r\nexport function getStacksDir(): string {\r\n return path.resolve(__dirname, \"stacks\");\r\n}\r\n\r\n/**\r\n * Map of language names (lowercased) to their stack snippet filenames.\r\n * Supports common aliases so the questionnaire answer maps correctly.\r\n */\r\nconst LANGUAGE_MAP: Record<string, string> = {\r\n typescript: \"typescript.md\",\r\n ts: \"typescript.md\",\r\n javascript: \"typescript.md\",\r\n js: \"typescript.md\",\r\n \"node.js\": \"typescript.md\",\r\n node: \"typescript.md\",\r\n react: \"react.md\",\r\n \"react.js\": \"react.md\",\r\n \"next.js\": \"react.md\",\r\n nextjs: \"react.md\",\r\n python: \"python.md\",\r\n py: \"python.md\",\r\n \"c#\": \"dotnet.md\",\r\n csharp: \"dotnet.md\",\r\n \".net\": \"dotnet.md\",\r\n dotnet: \"dotnet.md\",\r\n java: \"java.md\",\r\n \"spring\": \"java.md\",\r\n \"spring boot\": \"java.md\",\r\n \"spring-boot\": \"java.md\",\r\n};\r\n\r\n/**\r\n * Get the bundled best-practice snippet for a given language.\r\n * Returns the markdown content if a matching snippet exists, or null if not.\r\n */\r\nexport function getStackSnippet(language: string): string | null {\r\n const key = language.toLowerCase().trim();\r\n const filename = LANGUAGE_MAP[key];\r\n\r\n if (!filename) return null;\r\n\r\n const filePath = path.join(getStacksDir(), filename);\r\n if (!fs.pathExistsSync(filePath)) return null;\r\n\r\n return fs.readFileSync(filePath, \"utf-8\");\r\n}\r\n\r\n/**\r\n * List all available stack snippet names (for display purposes).\r\n */\r\nexport function listAvailableStacks(): string[] {\r\n const stacksDir = getStacksDir();\r\n if (!fs.pathExistsSync(stacksDir)) return [];\r\n\r\n return fs\r\n .readdirSync(stacksDir)\r\n .filter((f) => f.endsWith(\".md\"))\r\n .map((f) => f.replace(\".md\", \"\"));\r\n}\r\n","import path from \"path\";\r\nimport fs from \"fs-extra\";\r\nimport chalk from \"chalk\";\r\nimport { getProvider } from \"../providers/index.js\";\r\nimport type { SpecLiteConfig } from \"../providers/base.js\";\r\nimport {\r\n loadPrompts,\r\n extractProjectContext,\r\n replaceProjectContext,\r\n} from \"../utils/prompts.js\";\r\nimport { generateClaudeRootMd } from \"../providers/claude-code.js\";\r\n\r\ninterface UpdateOptions {\r\n force?: boolean;\r\n}\r\n\r\nexport async function updateCommand(options: UpdateOptions): Promise<void> {\r\n const cwd = process.cwd();\r\n\r\n console.log(chalk.bold(\"\\n⚔ spec-lite update\\n\"));\r\n\r\n // 1. Read existing config\r\n const configPath = path.join(cwd, \".spec-lite.json\");\r\n if (!(await fs.pathExists(configPath))) {\r\n console.error(\r\n chalk.red(\r\n ' No .spec-lite.json found. Run \"spec-lite init\" first.'\r\n )\r\n );\r\n process.exit(1);\r\n }\r\n\r\n const config: SpecLiteConfig = await fs.readJson(configPath);\r\n const provider = getProvider(config.provider);\r\n\r\n if (!provider) {\r\n console.error(\r\n chalk.red(\r\n ` Unknown provider \"${config.provider}\" in .spec-lite.json. Re-run \"spec-lite init\".`\r\n )\r\n );\r\n process.exit(1);\r\n }\r\n\r\n console.log(chalk.cyan(` Provider: ${provider.name}`));\r\n console.log(\r\n chalk.dim(` Installed: ${config.installedPrompts.length} prompts`)\r\n );\r\n\r\n // 2. Load latest prompts (only the ones that were previously installed)\r\n const allPrompts = await loadPrompts();\r\n const installedSet = new Set(config.installedPrompts);\r\n const prompts = allPrompts.filter((p) => installedSet.has(p.name));\r\n\r\n let updated = 0;\r\n let preserved = 0;\r\n let unchanged = 0;\r\n let migrated = 0;\r\n\r\n for (const prompt of prompts) {\r\n const targetRelPath = provider.getTargetPath(prompt.name);\r\n const targetAbsPath = path.join(cwd, targetRelPath);\r\n\r\n // Migration: if copilot provider, check for old-format .md files and rename to .prompt.md\r\n if (provider.alias === \"copilot\") {\r\n const oldRelPath = path.join(\".github\", \"copilot\", `${prompt.name}.md`);\r\n const oldAbsPath = path.join(cwd, oldRelPath);\r\n if (\r\n targetRelPath !== oldRelPath &&\r\n (await fs.pathExists(oldAbsPath)) &&\r\n !(await fs.pathExists(targetAbsPath))\r\n ) {\r\n await fs.ensureDir(path.dirname(targetAbsPath));\r\n await fs.rename(oldAbsPath, targetAbsPath);\r\n console.log(\r\n chalk.cyan(\r\n ` ↗ ${oldRelPath} → ${targetRelPath} (migrated to .prompt.md)`\r\n )\r\n );\r\n migrated++;\r\n }\r\n }\r\n\r\n // Transform the new prompt content\r\n const newContent = provider.transformPrompt(prompt.content, {\r\n name: prompt.name,\r\n title: prompt.title,\r\n description: prompt.description,\r\n });\r\n\r\n if (!(await fs.pathExists(targetAbsPath))) {\r\n // File was deleted by user — re-create it\r\n await fs.ensureDir(path.dirname(targetAbsPath));\r\n await fs.writeFile(targetAbsPath, newContent, \"utf-8\");\r\n console.log(chalk.green(` āœ“ ${targetRelPath} (restored)`));\r\n updated++;\r\n continue;\r\n }\r\n\r\n // Read current installed version\r\n const currentContent = await fs.readFile(targetAbsPath, \"utf-8\");\r\n\r\n // Check if content is identical (no update needed)\r\n if (currentContent === newContent) {\r\n unchanged++;\r\n continue;\r\n }\r\n\r\n // Try to preserve user's Project Context edits\r\n if (!options.force) {\r\n const userContext = extractProjectContext(currentContent);\r\n if (userContext) {\r\n const mergedContent = replaceProjectContext(newContent, userContext);\r\n await fs.writeFile(targetAbsPath, mergedContent, \"utf-8\");\r\n console.log(\r\n chalk.green(\r\n ` āœ“ ${targetRelPath} (updated, Project Context preserved)`\r\n )\r\n );\r\n preserved++;\r\n updated++;\r\n continue;\r\n }\r\n }\r\n\r\n // No context block found or --force — full overwrite\r\n await fs.writeFile(targetAbsPath, newContent, \"utf-8\");\r\n console.log(chalk.green(` āœ“ ${targetRelPath} (updated)`));\r\n updated++;\r\n }\r\n\r\n // 3. Update provider-specific extras\r\n if (provider.alias === \"claude-code\") {\r\n const claudeMdPath = path.join(cwd, \"CLAUDE.md\");\r\n const claudeMdContent = generateClaudeRootMd(config.installedPrompts);\r\n await fs.writeFile(claudeMdPath, claudeMdContent, \"utf-8\");\r\n console.log(chalk.green(` āœ“ CLAUDE.md (regenerated)`));\r\n }\r\n\r\n // 4. Update config timestamp\r\n config.updatedAt = new Date().toISOString();\r\n try {\r\n const { createRequire } = await import(\"module\");\r\n const require = createRequire(import.meta.url);\r\n const pkg = require(\"../../package.json\");\r\n config.version = pkg.version;\r\n } catch {\r\n // Keep existing version\r\n }\r\n await fs.writeJson(configPath, config, { spaces: 2 });\r\n\r\n // 5. Summary\r\n console.log(\r\n chalk.bold(\r\n `\\n Done! ${updated} updated, ${unchanged} unchanged, ${preserved} with preserved edits${migrated > 0 ? `, ${migrated} migrated` : \"\"}.`\r\n )\r\n );\r\n}\r\n","import chalk from \"chalk\";\r\nimport { getPromptCatalog } from \"../utils/prompts.js\";\r\n\r\nexport async function listCommand(): Promise<void> {\r\n const catalog = getPromptCatalog();\r\n\r\n console.log(chalk.bold(\"\\n⚔ spec-lite — Available Sub-Agents\\n\"));\r\n\r\n console.log(\r\n chalk.dim(\r\n \" Each sub-agent is a specialist prompt for one phase of the development lifecycle.\\n\"\r\n )\r\n );\r\n\r\n // Calculate column widths\r\n const entries = Object.entries(catalog);\r\n const maxName = Math.max(...entries.map(([name]) => name.length), 4);\r\n const maxTitle = Math.max(\r\n ...entries.map(([, v]) => v.title.length),\r\n 5\r\n );\r\n\r\n // Header\r\n const header = ` ${\"Name\".padEnd(maxName + 2)}${\"Title\".padEnd(maxTitle + 2)}${\"Description\"}`;\r\n console.log(chalk.cyan(header));\r\n console.log(chalk.dim(` ${\"─\".repeat(header.trim().length + 10)}`));\r\n\r\n // Rows\r\n for (const [name, meta] of entries) {\r\n const nameCol = chalk.green(name.padEnd(maxName + 2));\r\n const titleCol = chalk.white(meta.title.padEnd(maxTitle + 2));\r\n const descCol = chalk.dim(meta.description);\r\n console.log(` ${nameCol}${titleCol}${descCol}`);\r\n if (meta.output) {\r\n console.log(\r\n ` ${\"\".padEnd(maxName + 2)}${\"\".padEnd(maxTitle + 2)}${chalk.dim(`→ ${meta.output}`)}`\r\n );\r\n }\r\n }\r\n\r\n console.log(\r\n chalk.dim(\r\n `\\n ${entries.length} sub-agents available. Run \"spec-lite init\" to install them.\\n`\r\n )\r\n );\r\n\r\n // Pipeline\r\n console.log(chalk.bold(\" Recommended Pipeline:\\n\"));\r\n console.log(\r\n chalk.dim(\r\n \" Brainstorm → Planner → Feature (ƗN) → Reviews → Tests → DevOps → Docs\"\r\n )\r\n );\r\n console.log(\r\n chalk.dim(\r\n \" (Not every project needs every sub-agent. Start with Planner if you have clear requirements.)\\n\"\r\n )\r\n );\r\n}\r\n"],"mappings":"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","names":["path","fs","path","fs","path","fs","path","fs","path","fs","fileURLToPath","__filename","__dirname","providers","path","fs","pkg","createRequire","require","path","fs","chalk","chalk","path","fs","createRequire","require","pkg","chalk","chalk","require"]}
@@ -0,0 +1,84 @@
1
+ # .NET / C# — Best Practices & Conventions
2
+
3
+ > Curated by spec-lite. **Edit this file freely** to match your project — your changes are preserved across `spec-lite update`. The `/memorize bootstrap` agent reads this file as its starting baseline.
4
+
5
+ ## Coding Standards
6
+
7
+ - Follow the **Microsoft C# Coding Conventions** and **.NET naming guidelines**.
8
+ - **Naming**: `PascalCase` for types, methods, properties, events, and namespaces. `camelCase` for local variables and parameters. `_camelCase` for private fields. `I` prefix for interfaces (`IUserRepository`).
9
+ - Use **nullable reference types** (`<Nullable>enable</Nullable>`) and treat all warnings as errors.
10
+ - Prefer `var` for local variables when the type is obvious from the right-hand side. Use explicit types when clarity is needed.
11
+ - Use `record` types for immutable data (DTOs, value objects). Use `class` for entities with behavior.
12
+ - Use `readonly` and `const` by default. Avoid mutable state unless necessary.
13
+ - XML documentation comments (`///`) on all public APIs. Include `<summary>`, `<param>`, `<returns>`, and `<exception>` tags.
14
+ - Keep methods short and focused — one responsibility per method. If a method exceeds ~30 lines, refactor.
15
+ - Use `file-scoped namespaces` (C# 10+) and `global using` directives for common imports.
16
+
17
+ ## Async & Error Handling
18
+
19
+ - Use `async/await` for all I/O-bound operations. Never block on async code (`Task.Result`, `.Wait()`).
20
+ - Use `CancellationToken` on all async methods and pass it through the call chain.
21
+ - Define custom exception classes for domain-specific failures. Include context (what failed, why, correlation ID).
22
+ - Never catch `Exception` broadly — catch specific exceptions. Re-throw with `throw;` (not `throw ex;`) to preserve stack traces.
23
+ - Use **Result pattern** (or libraries like `FluentResults`, `OneOf`) for expected failures that aren't exceptional.
24
+ - For ASP.NET Core: use global exception handling middleware or `IExceptionHandler` (NET 8+). Don't scatter `try/catch` in controllers.
25
+
26
+ ## Architecture Patterns
27
+
28
+ - **Clean Architecture**: Domain → Application → Infrastructure → Presentation. Dependencies point inward.
29
+ - **SOLID principles**: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion.
30
+ - **Dependency Injection**: Use the built-in `IServiceCollection` / `IServiceProvider`. Register services with appropriate lifetimes (`Singleton`, `Scoped`, `Transient`).
31
+ - **Repository + Unit of Work**: Abstract data access behind interfaces. Use EF Core's `DbContext` as the Unit of Work.
32
+ - **MediatR or Wolverine** for CQRS / mediator pattern — decouple controllers from business logic.
33
+ - **Options pattern** (`IOptions<T>`, `IOptionsSnapshot<T>`) for strongly-typed configuration.
34
+ - For **Minimal APIs** (NET 7+): use endpoint groups and filters for clean organization.
35
+ - For **Controllers**: keep them thin — delegate to services/handlers. Controllers should only handle HTTP concerns.
36
+
37
+ ## Testing Conventions
38
+
39
+ - **Framework**: xUnit (preferred) or NUnit + FluentAssertions for readable assertions.
40
+ - **File organization**: Separate test project per source project — `MyApp.Domain` → `MyApp.Domain.Tests`.
41
+ - **Naming**: `MethodName_Scenario_ExpectedResult` — e.g., `GetUser_WhenUserNotFound_Returns404`.
42
+ - **Arrange-Act-Assert (AAA)** pattern in every test.
43
+ - Use `Moq`, `NSubstitute`, or `FakeItEasy` for mocking interfaces. Mock at the boundary — never mock internal classes.
44
+ - Use `WebApplicationFactory<T>` for ASP.NET Core integration tests.
45
+ - Use **test fixtures** (`IClassFixture<T>`) for expensive setup (DB, HTTP clients). Avoid per-test setup overhead.
46
+ - Use `Bogus` or `AutoFixture` for test data generation.
47
+
48
+ ## Logging
49
+
50
+ - Use the built-in `ILogger<T>` interface (Microsoft.Extensions.Logging). Never use `Console.WriteLine` in production.
51
+ - Use **structured logging** with Serilog (preferred) or NLog as the provider.
52
+ - Log levels: `Critical`, `Error`, `Warning`, `Information`, `Debug`, `Trace` — follow Microsoft severity definitions.
53
+ - Include correlation IDs via `Activity` or custom middleware.
54
+ - Use **log message templates** with named placeholders: `logger.LogInformation("User {UserId} created order {OrderId}", userId, orderId)`.
55
+ - Never log secrets, connection strings, tokens, or PII.
56
+ - Configure log sinks (console, file, Seq, Application Insights) via `appsettings.json`.
57
+
58
+ ## Security
59
+
60
+ - Validate all input via **Data Annotations**, **FluentValidation**, or model binding validation. Never trust raw input.
61
+ - Use **ASP.NET Core Identity** or a proven auth library for authentication. Don't roll your own.
62
+ - Enforce authorization with `[Authorize]` attributes and policy-based authorization.
63
+ - Store secrets in **Azure Key Vault**, **User Secrets** (dev), or environment variables. Never in `appsettings.json` committed to source control.
64
+ - Use **HTTPS** everywhere. Enforce with `UseHttpsRedirection()` and HSTS middleware.
65
+ - For passwords: use `PasswordHasher<T>` (ASP.NET Identity) which uses PBKDF2. For custom hashing, use `Argon2`.
66
+ - Keep NuGet packages updated — use `dotnet list package --vulnerable` to check.
67
+
68
+ ## Entity Framework Core Conventions
69
+
70
+ - Keep `DbContext` configurations in separate `IEntityTypeConfiguration<T>` classes.
71
+ - Use **migrations** for schema changes. Never modify the database manually.
72
+ - Prefer **AsNoTracking** for read-only queries.
73
+ - Use **projection** (`Select`) for queries that don't need full entities — avoid loading unnecessary data.
74
+ - Configure **global query filters** for soft deletes and multi-tenancy.
75
+ - Avoid lazy loading in web applications — use eager loading (`Include`) or explicit loading.
76
+
77
+ ## Common Pitfalls
78
+
79
+ - **Blocking on async**: `Task.Result` or `.Wait()` causes deadlocks in ASP.NET. Always `await`.
80
+ - **Service lifetime mismatches**: Injecting a `Scoped` service into a `Singleton` captures a stale instance. Use `IServiceScopeFactory` instead.
81
+ - **Missing `CancellationToken`**: Not passing tokens means requests can't be cancelled, wasting resources.
82
+ - **N+1 queries**: Loading related entities in a loop. Use `Include()` or projection to batch.
83
+ - **Throwing exceptions for control flow**: Exceptions are expensive. Use Result pattern for expected failures.
84
+ - **Over-abstraction**: Don't create interfaces for every class. Only abstract at boundaries (repositories, external services, infrastructure).
@@ -0,0 +1,76 @@
1
+ # Java / Spring — Best Practices & Conventions
2
+
3
+ > Curated by spec-lite. **Edit this file freely** to match your project — your changes are preserved across `spec-lite update`. The `/memorize bootstrap` agent reads this file as its starting baseline.
4
+
5
+ ## Coding Standards
6
+
7
+ - Follow the **Google Java Style Guide** (or your team's agreed-upon variant). Enforce with Checkstyle or Spotless in CI.
8
+ - **Naming**: `camelCase` for methods/variables, `PascalCase` for classes/interfaces/enums, `UPPER_SNAKE_CASE` for constants. Packages are all lowercase (`com.example.myapp`).
9
+ - **File naming**: One top-level public class per file. File name must match the class name (`UserService.java`).
10
+ - Use **Java 17+** features where possible: records, sealed classes, pattern matching, text blocks.
11
+ - Prefer `final` for variables, parameters, and fields by default. Mutate only when there's a clear reason.
12
+ - Prefer `Optional<T>` over returning `null`. Never pass `Optional` as a method parameter.
13
+ - Use **Lombok** judiciously — `@Slf4j`, `@Builder`, `@Value` are helpful; avoid `@Data` on JPA entities (breaks equals/hashCode).
14
+ - Javadoc on all public classes and methods. Include `@param`, `@return`, and `@throws` tags.
15
+ - Keep methods short and focused — one responsibility per method. If a method exceeds ~30 lines, refactor.
16
+
17
+ ## Spring Boot Patterns
18
+
19
+ - Use **constructor injection** exclusively — no field injection (`@Autowired` on fields). Let Spring auto-detect single-constructor beans.
20
+ - Organize by **feature/domain**, not by layer. Prefer `user/UserController.java`, `user/UserService.java` over `controllers/UserController.java`, `services/UserService.java`.
21
+ - Use `@RestController` + `@RequestMapping` for REST APIs. Return `ResponseEntity<T>` for explicit status codes.
22
+ - Use **Spring profiles** (`application-{profile}.yml`) for environment-specific configuration. Never hard-code secrets or URLs.
23
+ - Externalize configuration with `@ConfigurationProperties` over scattered `@Value` annotations.
24
+ - Use `@Transactional` at the service layer, not on controllers or repositories. Keep transactions as short as possible.
25
+ - Prefer **Spring Data JPA** repositories with derived query methods. Fall back to `@Query` for complex queries, and native queries only as a last resort.
26
+ - Use **DTOs** to decouple API contracts from domain entities. Map with MapStruct or manual mapping — avoid exposing JPA entities directly in APIs.
27
+
28
+ ## Error Handling
29
+
30
+ - Define a `@RestControllerAdvice` global exception handler. Map domain exceptions to appropriate HTTP status codes.
31
+ - Create custom exception classes that extend `RuntimeException` (e.g., `ResourceNotFoundException`, `BusinessRuleException`).
32
+ - Never catch `Exception` or `Throwable` broadly. Catch specific exceptions and handle them meaningfully.
33
+ - Use **Problem Details** (RFC 9457 / `application/problem+json`) for structured error responses.
34
+ - Validate inputs with **Bean Validation** (`@Valid`, `@NotBlank`, `@Size`, etc.) at the controller layer.
35
+
36
+ ## Architecture Patterns
37
+
38
+ - **Layered architecture**: Controller → Service → Repository. Controllers handle HTTP concerns, services contain business logic, repositories handle persistence.
39
+ - For complex domains, consider **hexagonal architecture**: domain core has no Spring/JPA dependencies, adapters connect to infra.
40
+ - Use **interfaces** for services when there are multiple implementations or when you need to decouple for testing (otherwise, concrete classes are fine).
41
+ - Avoid the **anemic domain model** — put behavior on domain entities where it belongs, not only in services.
42
+ - Use **events** (`ApplicationEventPublisher`) to decouple cross-cutting concerns (audit, notifications) from core business logic.
43
+
44
+ ## Testing
45
+
46
+ - Use **JUnit 5** with **AssertJ** for fluent assertions and **Mockito** for mocking.
47
+ - Follow the pattern: `@ExtendWith(MockitoExtension.class)` for unit tests, `@SpringBootTest` for integration tests.
48
+ - Use **`@WebMvcTest`** for controller-layer tests (fast, no full context). Use **`@DataJpaTest`** for repository-layer tests with an embedded database (H2 or Testcontainers).
49
+ - Use **Testcontainers** for integration tests against real databases, message brokers, or external services.
50
+ - **Naming**: `should_doSomething_when_condition()` or `givenX_whenY_thenZ()`. Be descriptive.
51
+ - Aim for high coverage on service/domain logic. Don't unit-test trivial getters or Spring-generated code.
52
+ - Use `@DirtiesContext` sparingly — prefer test isolation through transactions or test data builders.
53
+
54
+ ## Logging & Observability
55
+
56
+ - Use **SLF4J** (`@Slf4j` with Lombok, or `LoggerFactory.getLogger()`). Never use `System.out.println()`.
57
+ - Log levels: `ERROR` for failures needing attention, `WARN` for recoverable issues, `INFO` for business events, `DEBUG` for diagnostic detail.
58
+ - Use **structured logging** with Logback + JSON encoder (or Log4j2) for production. Include correlation IDs.
59
+ - Use **Spring Boot Actuator** for health checks, metrics, and info endpoints. Expose `/actuator/health` and `/actuator/prometheus` for monitoring.
60
+ - Add **Micrometer** metrics for custom business KPIs (e.g., orders processed, cache hit ratio).
61
+
62
+ ## Security
63
+
64
+ - Use **Spring Security** with sensible defaults. Configure via `SecurityFilterChain` bean (not by extending `WebSecurityConfigurerAdapter` — deprecated).
65
+ - Use **BCrypt** (`BCryptPasswordEncoder`) for password hashing. Never store passwords in plain text.
66
+ - Enable **CSRF protection** for browser-based apps. Disable only for stateless APIs with token-based auth.
67
+ - Validate and sanitize all inputs. Use parameterized queries (JPA/Hibernate handles this) — never concatenate user input into SQL.
68
+ - Keep dependencies up to date. Use `mvn versions:display-dependency-updates` or Dependabot/Renovate.
69
+ - Store secrets in environment variables or a vault (Spring Cloud Vault, AWS Secrets Manager). Never commit secrets to source control.
70
+
71
+ ## Dependency Management
72
+
73
+ - Use **Maven** (with `pom.xml`) or **Gradle** (with `build.gradle.kts` — prefer Kotlin DSL). Pick one and be consistent.
74
+ - Use the **Spring Boot BOM** (`spring-boot-starter-parent` or `spring-boot-dependencies`) to manage dependency versions. Don't override Spring-managed versions unless absolutely necessary.
75
+ - Keep the dependency tree lean. Audit with `mvn dependency:tree` or `gradle dependencies`.
76
+ - Pin plugin and dependency versions explicitly. Avoid `LATEST` or `RELEASE` version ranges.
@@ -0,0 +1,76 @@
1
+ # Python — Best Practices & Conventions
2
+
3
+ > Curated by spec-lite. **Edit this file freely** to match your project — your changes are preserved across `spec-lite update`. The `/memorize bootstrap` agent reads this file as its starting baseline.
4
+
5
+ ## Coding Standards
6
+
7
+ - Follow **PEP 8** for style. Use a formatter (Black or Ruff) and linter (Ruff, flake8, or pylint) enforced in CI.
8
+ - **Type hints** (PEP 484) on all function signatures and return types. Use `mypy` or `pyright` in strict mode.
9
+ - **Naming**: `snake_case` for functions/variables/modules, `PascalCase` for classes, `UPPER_SNAKE_CASE` for constants.
10
+ - **File naming**: `snake_case.py` for all modules.
11
+ - Keep functions short and focused — one purpose per function.
12
+ - Prefer **dataclasses** or **Pydantic models** over plain dicts for structured data.
13
+ - Use `from __future__ import annotations` for forward references (Python 3.7–3.9) or target Python 3.10+.
14
+ - Docstrings: Use Google or NumPy style consistently. All public functions, classes, and modules must have docstrings.
15
+ - Avoid mutable default arguments (`def f(items=[])` → `def f(items=None)`).
16
+
17
+ ## Async & Error Handling
18
+
19
+ - Use `async/await` with `asyncio` for I/O-bound operations (FastAPI, aiohttp, etc.).
20
+ - Define custom exception classes inheriting from `Exception` for domain-specific errors. Include context.
21
+ - Never use bare `except:` — always catch specific exceptions (`except ValueError`, `except HTTPError`).
22
+ - Use context managers (`with` statement) for resource management (files, DB connections, locks).
23
+ - For FastAPI: use exception handlers and `HTTPException` with appropriate status codes. Don't let raw Python exceptions leak to API responses.
24
+
25
+ ## Architecture Patterns
26
+
27
+ - **Layered architecture**: Separate routers/views → services/use-cases → repositories/data-access.
28
+ - **Dependency Injection**: FastAPI has built-in DI via `Depends()`. Django uses middleware and signals. Flask uses extensions and blueprints.
29
+ - **Repository Pattern**: Abstract data access behind a class/interface. Makes swapping databases or mocking trivial.
30
+ - **Pydantic for validation**: All external input must be validated via Pydantic models at the API boundary.
31
+ - For **Django**: follow the "fat models, thin views" pattern. Use Django's ORM idiomatically — don't fight it.
32
+ - For **FastAPI**: use routers for route grouping, dependency injection for shared logic, and background tasks for async work.
33
+ - For **Flask**: use Blueprints for modularization, Flask-SQLAlchemy for ORM, and Flask-Marshmallow for serialization.
34
+
35
+ ## Testing Conventions
36
+
37
+ - **Framework**: pytest (preferred) with `pytest-asyncio` for async tests, `pytest-cov` for coverage.
38
+ - **File organization**: `tests/` directory mirroring `src/` structure — `src/services/user.py` → `tests/services/test_user.py`.
39
+ - **Naming**: `test_<behavior_description>` — e.g., `test_returns_404_when_user_not_found`.
40
+ - Use **fixtures** (`@pytest.fixture`) for test setup and teardown. Prefer factory fixtures over static data.
41
+ - Use `unittest.mock.patch` or `pytest-mock` to mock external dependencies. Never mock internal business logic.
42
+ - Use `httpx.AsyncClient` (FastAPI) or Django's `TestClient` for API integration tests.
43
+ - **Arrange-Act-Assert** pattern in every test.
44
+
45
+ ## Logging
46
+
47
+ - Use Python's built-in `logging` module or `structlog` for structured logging.
48
+ - Never use `print()` for logging in production code.
49
+ - Configure logging in a central `logging_config.py` or via `dictConfig`.
50
+ - Log levels: `ERROR`, `WARNING`, `INFO`, `DEBUG` — follow Python's standard definitions.
51
+ - Include request/correlation IDs via contextvars or middleware.
52
+ - Never log secrets, tokens, passwords, or PII.
53
+
54
+ ## Security
55
+
56
+ - Validate all external input via Pydantic models or Django forms. Never trust raw `request.data`.
57
+ - Use `python-dotenv` or environment variables for secrets. Never hardcode secrets.
58
+ - For passwords: use `passlib` with `bcrypt` or `argon2`. Never store plaintext.
59
+ - Keep dependencies updated — use `pip-audit` or `safety` to scan for known vulnerabilities.
60
+ - Use `CORS` middleware with explicit allowed origins (never `*` in production).
61
+ - For Django: enable CSRF protection, use `django.contrib.auth` for authentication, and follow Django's security checklist.
62
+
63
+ ## Dependency Management
64
+
65
+ - Use `pyproject.toml` (PEP 621) as the single source of truth for project metadata and dependencies.
66
+ - Pin dependencies with a lockfile: `poetry.lock`, `pdm.lock`, or `pip-compile` output (`requirements.txt`).
67
+ - Separate dev dependencies from production dependencies.
68
+ - Use virtual environments (`venv`, `poetry`, `pdm`, or `conda`) — never install into the system Python.
69
+
70
+ ## Common Pitfalls
71
+
72
+ - **Mutable default arguments**: `def f(items=[])` shares the same list across calls. Use `None` and create inside.
73
+ - **Circular imports**: Restructure modules or use lazy imports (`TYPE_CHECKING` for type hints only).
74
+ - **Missing `await`**: Forgetting `await` on coroutines silently returns a coroutine object instead of the result.
75
+ - **Overusing `*args, **kwargs`**: Hurts readability and type safety. Be explicit about parameters.
76
+ - **Not closing resources**: Always use `with` or `async with` for files, connections, and sessions.
@@ -0,0 +1,79 @@
1
+ # React / Next.js — Best Practices & Conventions
2
+
3
+ > Curated by spec-lite. **Edit this file freely** to match your project — your changes are preserved across `spec-lite update`. The `/memorize bootstrap` agent reads this file as its starting baseline.
4
+
5
+ ## Coding Standards
6
+
7
+ - Use **TypeScript** with strict mode for all React projects.
8
+ - **Naming**: `PascalCase` for components and component files, `camelCase` for hooks (`useAuth`, `useFetch`), `UPPER_SNAKE_CASE` for constants.
9
+ - One component per file. File name must match the exported component name.
10
+ - Prefer **function components** with hooks over class components.
11
+ - Prefer **named exports** for components — default exports only for page-level components in Next.js (required by the framework).
12
+ - Use absolute imports with path aliases (e.g., `@/components/Button`) over deep relative paths.
13
+ - Co-locate related files: `Button/Button.tsx`, `Button/Button.test.tsx`, `Button/Button.module.css`.
14
+
15
+ ## Component Patterns
16
+
17
+ - Keep components **small and focused** — if a component exceeds ~150 lines, extract sub-components.
18
+ - Separate **presentational** (UI) from **container** (data-fetching/logic) concerns. Use custom hooks to extract logic.
19
+ - Use **composition** over prop-drilling. Prefer `children` and render props for flexible layouts.
20
+ - Avoid prop drilling deeper than 2 levels — use Context or state management for shared state.
21
+ - For forms: use a form library (React Hook Form, Formik) for anything beyond trivial forms. Validate with Zod or Yup.
22
+ - Prefer **controlled components** over uncontrolled unless performance requires otherwise.
23
+
24
+ ## Hooks Rules
25
+
26
+ - Only call hooks at the top level — never inside conditions, loops, or nested functions.
27
+ - Custom hooks must start with `use` prefix.
28
+ - Use `useMemo` and `useCallback` only when there's a measurable performance benefit — don't prematurely optimize.
29
+ - Use `useRef` for values that don't trigger re-renders (timers, DOM refs, previous values).
30
+ - Avoid `useEffect` for derived state — compute it during render instead.
31
+ - Cleanup side effects in `useEffect` — return a cleanup function for subscriptions, timers, and event listeners.
32
+
33
+ ## State Management
34
+
35
+ - Start with **local state** (`useState`). Lift state only when siblings need it.
36
+ - Use **React Context** for low-frequency global state (theme, auth, locale).
37
+ - For complex client-side state, use Zustand (lightweight) or Redux Toolkit (large apps).
38
+ - For server state, use **TanStack Query** (React Query) or SWR — never manage server cache manually with `useState` + `useEffect`.
39
+ - In Next.js App Router: prefer **Server Components** for data fetching. Use `"use client"` only when client interactivity is needed.
40
+
41
+ ## Testing Conventions
42
+
43
+ - **Framework**: Vitest or Jest + React Testing Library.
44
+ - Test **behavior**, not implementation — query by role, label, and text, not by test IDs or CSS selectors.
45
+ - Never test internal component state directly. Test what the user sees and interacts with.
46
+ - Use `userEvent` over `fireEvent` for realistic user interaction simulation.
47
+ - Mock API calls with MSW (Mock Service Worker) for integration tests.
48
+ - Snapshot tests: use sparingly and only for stable UI — they become maintenance burdens quickly.
49
+
50
+ ## Next.js Specific
51
+
52
+ - Use the **App Router** (default since Next.js 13+) unless maintaining a legacy Pages Router project.
53
+ - Prefer **Server Components** by default. Add `"use client"` only for interactive components.
54
+ - Use `loading.tsx`, `error.tsx`, and `not-found.tsx` for built-in loading/error states.
55
+ - Data fetching: use `fetch` in Server Components with built-in caching, or Server Actions for mutations.
56
+ - Image optimization: always use `next/image` over raw `<img>` tags.
57
+ - Use route groups `(group)` to organize routes without affecting the URL structure.
58
+
59
+ ## Performance
60
+
61
+ - Use `React.lazy()` and `Suspense` for code-splitting large components/routes.
62
+ - Virtualize long lists with `react-window` or `@tanstack/react-virtual`.
63
+ - Avoid unnecessary re-renders: use `React.memo` for expensive pure components, but measure first.
64
+ - Keep bundle size in check — analyze with `@next/bundle-analyzer` or `source-map-explorer`.
65
+
66
+ ## Security
67
+
68
+ - Sanitize any user-generated HTML before rendering — never use `dangerouslySetInnerHTML` with unsanitized content.
69
+ - Use CSP (Content Security Policy) headers — Next.js supports them via `next.config.js`.
70
+ - Store tokens in httpOnly cookies, not localStorage.
71
+ - Validate all form input client-side (UX) AND server-side (security).
72
+
73
+ ## Common Pitfalls
74
+
75
+ - **Infinite re-render loops**: Caused by `useEffect` with missing or incorrect dependencies.
76
+ - **Stale closures**: Hooks capture values at render time — use `useRef` for mutable values that need to be current.
77
+ - **Over-fetching in `useEffect`**: Use TanStack Query or SWR instead of manual `useEffect` + `fetch` patterns.
78
+ - **Hydration mismatches** (Next.js): Ensure server-rendered HTML matches client-rendered output. Avoid `typeof window` checks in render.
79
+ - **Prop type drift**: Always type component props with TypeScript interfaces. Never use `any` for props.
@@ -0,0 +1,67 @@
1
+ # TypeScript / Node.js — Best Practices & Conventions
2
+
3
+ > Curated by spec-lite. **Edit this file freely** to match your project — your changes are preserved across `spec-lite update`. The `/memorize bootstrap` agent reads this file as its starting baseline.
4
+
5
+ ## Coding Standards
6
+
7
+ - Use **strict TypeScript** (`"strict": true` in `tsconfig.json`). Never use `any` unless explicitly justified and documented.
8
+ - **Naming**: `camelCase` for variables/functions, `PascalCase` for classes/interfaces/types/enums, `UPPER_SNAKE_CASE` for constants.
9
+ - **File naming**: `kebab-case.ts` for files, `PascalCase.ts` for files that export a single class.
10
+ - Prefer `interface` over `type` for object shapes that may be extended. Use `type` for unions, intersections, and mapped types.
11
+ - Use `readonly` and `const` by default. Mutate only when there's a clear reason.
12
+ - Prefer named exports over default exports for better refactoring support and tree-shaking.
13
+ - Use `enum` sparingly — prefer `const` objects with `as const` or union types for simple value sets.
14
+ - Avoid `null` where possible — prefer `undefined` for absence. Be consistent within the project.
15
+ - All public APIs must have JSDoc with `@param`, `@returns`, and `@example` where appropriate.
16
+
17
+ ## Async & Error Handling
18
+
19
+ - Use `async/await` over raw Promises. Never mix callbacks with Promises.
20
+ - Always handle errors explicitly — no unhandled promise rejections. Use `try/catch` around `await` calls.
21
+ - Define custom error classes that extend `Error` for domain-specific failures. Include context (what failed, why, with what input).
22
+ - Never catch generic `Error` and silently swallow it. At minimum, log and re-throw.
23
+ - For Express/Fastify: use centralized error-handling middleware. Don't scatter `try/catch` in every route handler.
24
+
25
+ ## Architecture Patterns
26
+
27
+ - **Layered architecture**: Separate routes/controllers → services/use-cases → repositories/data-access. Business logic must not depend on HTTP or database directly.
28
+ - **Dependency Injection**: Pass dependencies via constructor or function parameters. Avoid module-level singletons that are hard to test.
29
+ - **Repository Pattern** for data access — abstracts storage behind an interface, making it easy to swap implementations or mock in tests.
30
+ - **DTOs (Data Transfer Objects)**: Use Zod, class-validator, or io-ts to validate external input at the boundary. Never trust raw `req.body`.
31
+ - For NestJS: leverage modules, providers, and guards idiomatically. Keep controllers thin.
32
+ - For Express: use router-level middleware for cross-cutting concerns (auth, validation, logging).
33
+
34
+ ## Testing Conventions
35
+
36
+ - **Framework**: Jest or Vitest (prefer Vitest for ESM-first projects).
37
+ - **File organization**: Mirror source structure — `src/services/user.ts` → `tests/services/user.test.ts` (or co-locate as `user.spec.ts`).
38
+ - **Naming**: Describe behavior, not methods — `it('returns 404 when user does not exist')` over `it('test getUser')`.
39
+ - **Arrange-Act-Assert (AAA)** pattern in every test.
40
+ - Mock external dependencies (DB, HTTP, file system) — never mock internal business logic.
41
+ - Use factories or builders for test data — avoid hardcoded fixtures that become brittle.
42
+ - Aim for high coverage on business logic and critical paths. Don't chase 100% on glue code.
43
+
44
+ ## Logging
45
+
46
+ - Use **structured logging** (JSON format) with `pino` (preferred for performance) or `winston`.
47
+ - Never use `console.log` in production code. Use the configured logger.
48
+ - Log levels: `error` (failures), `warn` (recoverable issues), `info` (key business events), `debug` (diagnostics).
49
+ - Include correlation/request IDs in all log entries for traceability.
50
+ - Never log secrets, tokens, passwords, or PII.
51
+
52
+ ## Security
53
+
54
+ - Validate and sanitize all external input at the boundary (Zod schemas, express-validator, etc.).
55
+ - Use `helmet` middleware for HTTP security headers.
56
+ - Store secrets in environment variables — never commit to source control.
57
+ - Use `bcrypt` or `argon2` for password hashing. Never store plaintext passwords.
58
+ - Keep dependencies updated — run `npm audit` regularly. Pin major versions.
59
+ - For JWTs: use short-lived access tokens + refresh tokens. Store refresh tokens securely (httpOnly cookies).
60
+
61
+ ## Common Pitfalls
62
+
63
+ - **Circular dependencies**: Refactor shared types into a separate module. Use barrel files carefully.
64
+ - **Overusing `any`**: Defeats the purpose of TypeScript. Use `unknown` + type narrowing instead.
65
+ - **Missing `await`**: Always `await` async calls — floating promises are a common source of bugs.
66
+ - **Barrel file bloat**: Large `index.ts` re-exports can slow down build tools and create circular deps.
67
+ - **Overly complex generics**: If a generic type is hard to read, simplify or add a type alias with a clear name.
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@abranjith/spec-lite",
3
+ "version": "0.0.1",
4
+ "description": "Lightweight, modular, LLM-agnostic prompt collection for structured software engineering — installable CLI",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "spec-lite": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "prompts",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsup",
18
+ "dev": "tsup --watch",
19
+ "prepublishOnly": "npm run build",
20
+ "typecheck": "tsc --noEmit"
21
+ },
22
+ "keywords": [
23
+ "ai",
24
+ "llm",
25
+ "prompts",
26
+ "copilot",
27
+ "claude",
28
+ "cursor",
29
+ "agents",
30
+ "sdlc",
31
+ "cli",
32
+ "spec"
33
+ ],
34
+ "author": "Ranjith A B",
35
+ "license": "MIT",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/abranjith/spec-lite"
39
+ },
40
+ "engines": {
41
+ "node": ">=20"
42
+ },
43
+ "dependencies": {
44
+ "chalk": "^5.3.0",
45
+ "commander": "^12.1.0",
46
+ "fs-extra": "^11.2.0",
47
+ "inquirer": "^9.2.0"
48
+ },
49
+ "devDependencies": {
50
+ "@types/fs-extra": "^11.0.4",
51
+ "@types/inquirer": "^9.0.7",
52
+ "@types/node": "^20.11.0",
53
+ "tsup": "^8.0.0",
54
+ "typescript": "^5.3.0"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public",
58
+ "provenance": true
59
+ }
60
+ }