dotclaudemd 0.1.1 → 0.2.0

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,55 @@
1
+ ---
2
+ name: rails
3
+ displayName: Ruby on Rails
4
+ description: Ruby on Rails web application
5
+ category: ruby
6
+ tags: [ruby, rails, web, api, backend]
7
+ variables:
8
+ - name: api_only
9
+ prompt: "API-only mode?"
10
+ options: ["No", "Yes"]
11
+ default: "No"
12
+ - name: db
13
+ prompt: "Database?"
14
+ options: [PostgreSQL, MySQL, SQLite]
15
+ default: PostgreSQL
16
+ detects:
17
+ files: [Gemfile, Rakefile, config.ru]
18
+ priority: 10
19
+ ---
20
+
21
+ # Project
22
+
23
+ Ruby on Rails application with {{db}}.
24
+
25
+ ## Commands
26
+
27
+ - `bin/rails server` — Start dev server on localhost:3000
28
+ - `bin/rails console` — Open Rails console (IRB with app context)
29
+ - `bin/rails test` or `bundle exec rspec` — Run tests
30
+ - `bin/rails db:migrate` — Run pending migrations
31
+ - `bin/rails db:seed` — Seed the database
32
+ - `bin/rails routes` — List all routes
33
+ - `bundle exec rubocop` — Run linter
34
+
35
+ ## Architecture
36
+
37
+ - `app/models/` — ActiveRecord models and business logic
38
+ - `app/controllers/` — Request handling and response rendering
39
+ - `app/views/` — ERB/Haml templates (full-stack) or Jbuilder (API)
40
+ - `app/services/` — Service objects for complex business operations
41
+ - `app/jobs/` — Background jobs (ActiveJob / Sidekiq)
42
+ - `db/migrate/` — Database migrations (timestamped, sequential)
43
+ - `config/routes.rb` — Route definitions
44
+ - `spec/` or `test/` — Test files
45
+
46
+ ## Conventions
47
+
48
+ - Follow Rails conventions: pluralized table names, RESTful routes, convention over configuration
49
+ - Use ActiveRecord callbacks sparingly; prefer service objects for complex logic
50
+ - Keep controllers thin: one action = find resource, perform operation, render response
51
+ - Use strong parameters (`params.require(:model).permit(...)`) for mass assignment protection
52
+ - Write model validations for all user-facing input
53
+ - Use concerns (`app/models/concerns/`, `app/controllers/concerns/`) to share behavior
54
+ - Use `has_many`, `belongs_to`, `has_one` for associations; avoid N+1 queries with `includes`
55
+ - Prefer scopes on models over complex controller queries
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/fs.ts","../src/utils/paths.ts"],"sourcesContent":["import { readFile as nodeReadFile, writeFile as nodeWriteFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport type { FsDeps } from \"../types.js\";\n\nexport const defaultFsDeps: FsDeps = {\n async readFile(path: string): Promise<string> {\n return nodeReadFile(path, \"utf-8\");\n },\n\n async writeFile(path: string, content: string): Promise<void> {\n await nodeWriteFile(path, content, \"utf-8\");\n },\n\n async fileExists(path: string): Promise<boolean> {\n return existsSync(path);\n },\n\n async readDir(path: string): Promise<string[]> {\n return readdir(path);\n },\n};\n","import { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { existsSync } from \"node:fs\";\n\nexport function getTemplatesDir(): string {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n // From dist/cli.mjs or src/utils/paths.ts, go up to package root\n let dir = __dirname;\n for (let i = 0; i < 5; i++) {\n const candidate = join(dir, \"templates\");\n if (existsSync(candidate)) {\n return candidate;\n }\n dir = dirname(dir);\n }\n // Fallback: relative to package root\n return join(dirname(dirname(__dirname)), \"templates\");\n}\n\nexport function findProjectRoot(startDir?: string): string {\n let dir = startDir ? resolve(startDir) : process.cwd();\n while (true) {\n // Check for common project root indicators\n const indicators = [\n \"package.json\",\n \"pyproject.toml\",\n \"Cargo.toml\",\n \"go.mod\",\n \".git\",\n ];\n for (const indicator of indicators) {\n if (existsSync(join(dir, indicator))) {\n return dir;\n }\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return startDir ? resolve(startDir) : process.cwd();\n}\n\nexport function findClaudeMd(projectRoot: string): string | null {\n // Check common CLAUDE.md locations\n const candidates = [\n join(projectRoot, \"CLAUDE.md\"),\n join(projectRoot, \".claude\", \"CLAUDE.md\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n return null;\n}\n"],"mappings":";;;AAAA,SAAS,YAAY,cAAc,aAAa,qBAAqB;AACrE,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AAGjB,IAAM,gBAAwB;AAAA,EACnC,MAAM,SAAS,MAA+B;AAC5C,WAAO,aAAa,MAAM,OAAO;AAAA,EACnC;AAAA,EAEA,MAAM,UAAU,MAAc,SAAgC;AAC5D,UAAM,cAAc,MAAM,SAAS,OAAO;AAAA,EAC5C;AAAA,EAEA,MAAM,WAAW,MAAgC;AAC/C,WAAO,WAAW,IAAI;AAAA,EACxB;AAAA,EAEA,MAAM,QAAQ,MAAiC;AAC7C,WAAO,QAAQ,IAAI;AAAA,EACrB;AACF;;;ACrBA,SAAS,SAAS,MAAM,eAAe;AACvC,SAAS,qBAAqB;AAC9B,SAAS,cAAAA,mBAAkB;AAEpB,SAAS,kBAA0B;AACxC,QAAM,aAAa,cAAc,YAAY,GAAG;AAChD,QAAM,YAAY,QAAQ,UAAU;AAEpC,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,YAAY,KAAK,KAAK,WAAW;AACvC,QAAIA,YAAW,SAAS,GAAG;AACzB,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,GAAG;AAAA,EACnB;AAEA,SAAO,KAAK,QAAQ,QAAQ,SAAS,CAAC,GAAG,WAAW;AACtD;AAEO,SAAS,gBAAgB,UAA2B;AACzD,MAAI,MAAM,WAAW,QAAQ,QAAQ,IAAI,QAAQ,IAAI;AACrD,SAAO,MAAM;AAEX,UAAM,aAAa;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,eAAW,aAAa,YAAY;AAClC,UAAIA,YAAW,KAAK,KAAK,SAAS,CAAC,GAAG;AACpC,eAAO;AAAA,MACT;AAAA,IACF;AACA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO,WAAW,QAAQ,QAAQ,IAAI,QAAQ,IAAI;AACpD;AAEO,SAAS,aAAa,aAAoC;AAE/D,QAAM,aAAa;AAAA,IACjB,KAAK,aAAa,WAAW;AAAA,IAC7B,KAAK,aAAa,WAAW,WAAW;AAAA,EAC1C;AACA,aAAW,aAAa,YAAY;AAClC,QAAIA,YAAW,SAAS,GAAG;AACzB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;","names":["existsSync"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/init.ts","../src/core/project-detector.ts"],"sourcesContent":["import { join, dirname } from \"node:path\";\nimport { mkdirSync } from \"node:fs\";\nimport { select, input, confirm } from \"@inquirer/prompts\";\nimport { renderTemplate } from \"../core/template-engine.js\";\nimport {\n listTemplates,\n suggestTemplates,\n} from \"../core/template-registry.js\";\nimport { detectStack } from \"../core/project-detector.js\";\nimport type { Template, FsDeps } from \"../types.js\";\nimport { defaultFsDeps } from \"../utils/fs.js\";\nimport { findProjectRoot, findClaudeMd } from \"../utils/paths.js\";\nimport * as logger from \"../utils/logger.js\";\nimport { createSpinner } from \"../utils/ui.js\";\n\nexport interface InitOptions {\n stack?: string;\n global?: boolean;\n noInteractive?: boolean;\n force?: boolean;\n}\n\nexport async function initCommand(\n options: InitOptions = {},\n deps: FsDeps = defaultFsDeps,\n promptFn?: typeof promptForVariables,\n): Promise<void> {\n const projectRoot = options.global\n ? join(process.env.HOME ?? \"~\", \".claude\")\n : findProjectRoot();\n\n const outputPath = options.global\n ? join(process.env.HOME ?? \"~\", \".claude\", \"CLAUDE.md\")\n : join(projectRoot, \"CLAUDE.md\");\n\n // Check for existing file\n const existing = findClaudeMd(projectRoot);\n if (existing && !options.force) {\n if (options.noInteractive) {\n logger.error(\n `CLAUDE.md already exists at ${existing}. Use --force to overwrite.`,\n );\n return;\n }\n const overwrite = await confirm({\n message: `CLAUDE.md already exists at ${existing}. Overwrite?`,\n default: false,\n });\n if (!overwrite) {\n logger.info(\"Cancelled.\");\n return;\n }\n }\n\n // Detect stack\n const spinner = createSpinner(\"Detecting project stack...\");\n spinner.start();\n const detected = options.stack\n ? null\n : await detectStack(projectRoot, deps);\n spinner.stop();\n\n if (detected) {\n logger.info(\n `Detected: ${detected.language}${detected.framework ? ` / ${detected.framework}` : \"\"}${detected.packageManager ? ` (${detected.packageManager})` : \"\"}`,\n );\n }\n\n // Find matching templates\n let template: Template;\n const allTemplates = await listTemplates();\n\n if (options.stack) {\n // Use specified stack name directly\n const match = allTemplates.find(\n (t) => t.frontmatter.name === options.stack,\n );\n if (!match) {\n logger.error(`No template found for stack: ${options.stack}`);\n logger.info(\n `Available: ${allTemplates.map((t) => t.frontmatter.name).join(\", \")}`,\n );\n return;\n }\n template = match;\n } else if (detected) {\n const suggestions = await suggestTemplates(detected);\n if (suggestions.length === 1 || options.noInteractive) {\n template = suggestions[0] ?? allTemplates[0];\n } else if (suggestions.length > 1) {\n template = await selectTemplate(suggestions);\n } else {\n template = await selectTemplate(allTemplates);\n }\n } else {\n if (options.noInteractive) {\n template = allTemplates.find((t) => t.frontmatter.name === \"default\") ?? allTemplates[0];\n } else {\n template = await selectTemplate(allTemplates);\n }\n }\n\n logger.info(`Using template: ${template.frontmatter.displayName}`);\n\n // Prompt for variables\n const variables: Record<string, string> = {};\n const doPrompt = promptFn ?? promptForVariables;\n\n if (template.frontmatter.variables.length > 0 && !options.noInteractive) {\n await doPrompt(template, variables, detected);\n } else {\n // Use defaults\n for (const v of template.frontmatter.variables) {\n if (v.default) variables[v.name] = v.default;\n }\n }\n\n // Render and write\n const rendered = renderTemplate(template, variables);\n\n // Ensure directory exists\n mkdirSync(dirname(outputPath), { recursive: true });\n await deps.writeFile(outputPath, rendered);\n\n logger.success(`Created ${outputPath}`);\n logger.info(\"Next steps:\");\n logger.dim(\" 1. Review and customize your CLAUDE.md\");\n logger.dim(\" 2. Run `dotclaudemd lint` to check for issues\");\n logger.dim(\" 3. Run `dotclaudemd doctor` to verify accuracy\");\n}\n\nasync function selectTemplate(templates: Template[]): Promise<Template> {\n const answer = await select({\n message: \"Choose a template:\",\n choices: templates.map((t) => ({\n name: `${t.frontmatter.displayName} — ${t.frontmatter.description}`,\n value: t.frontmatter.name,\n })),\n });\n\n return templates.find((t) => t.frontmatter.name === answer)!;\n}\n\nasync function promptForVariables(\n template: Template,\n variables: Record<string, string>,\n detected?: { packageManager?: string; testFramework?: string } | null,\n): Promise<void> {\n for (const v of template.frontmatter.variables) {\n const defaultValue =\n v.default ??\n (v.name === \"package_manager\" && detected?.packageManager\n ? detected.packageManager\n : undefined) ??\n (v.name === \"test_framework\" && detected?.testFramework\n ? detected.testFramework\n : undefined);\n\n if (v.options && v.options.length > 0) {\n const answer = await select({\n message: v.prompt,\n choices: v.options.map((o) => ({ name: o, value: o })),\n default: defaultValue,\n });\n variables[v.name] = answer;\n } else {\n const answer = await input({\n message: v.prompt,\n default: defaultValue,\n });\n variables[v.name] = answer;\n }\n }\n}\n","import { join } from \"node:path\";\nimport type { DetectedStack, FsDeps } from \"../types.js\";\nimport { defaultFsDeps } from \"../utils/fs.js\";\n\nexport async function detectStack(\n projectRoot: string,\n deps: FsDeps = defaultFsDeps,\n): Promise<DetectedStack | null> {\n // Try each detector in order of specificity\n const detectors = [\n detectNode,\n detectPython,\n detectRust,\n detectGo,\n ];\n\n for (const detector of detectors) {\n const result = await detector(projectRoot, deps);\n if (result) return result;\n }\n\n return null;\n}\n\nasync function detectNode(\n projectRoot: string,\n deps: FsDeps,\n): Promise<DetectedStack | null> {\n const pkgPath = join(projectRoot, \"package.json\");\n if (!(await deps.fileExists(pkgPath))) return null;\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(await deps.readFile(pkgPath));\n } catch {\n return null;\n }\n\n const allDeps = (pkg.dependencies ?? {}) as Record<string, string>;\n const allDevDeps = (pkg.devDependencies ?? {}) as Record<string, string>;\n const depNames = Object.keys(allDeps);\n const devDepNames = Object.keys(allDevDeps);\n\n const stack: DetectedStack = {\n language: \"javascript\",\n dependencies: depNames,\n devDependencies: devDepNames,\n };\n\n // Detect TypeScript\n if (\n devDepNames.includes(\"typescript\") ||\n (await deps.fileExists(join(projectRoot, \"tsconfig.json\")))\n ) {\n stack.language = \"javascript\"; // category stays javascript\n }\n\n // Detect framework\n if (depNames.includes(\"next\")) {\n stack.framework = \"Next.js\";\n } else if (depNames.includes(\"express\") && depNames.includes(\"react\")) {\n stack.framework = \"MERN\";\n } else if (depNames.includes(\"express\")) {\n stack.framework = \"Express\";\n } else if (depNames.includes(\"react\")) {\n stack.framework = \"React\";\n }\n\n // Detect package manager from lockfiles\n if (await deps.fileExists(join(projectRoot, \"pnpm-lock.yaml\"))) {\n stack.packageManager = \"pnpm\";\n } else if (await deps.fileExists(join(projectRoot, \"yarn.lock\"))) {\n stack.packageManager = \"yarn\";\n } else if (await deps.fileExists(join(projectRoot, \"bun.lockb\"))) {\n stack.packageManager = \"bun\";\n } else if (await deps.fileExists(join(projectRoot, \"package-lock.json\"))) {\n stack.packageManager = \"npm\";\n }\n\n // Detect test framework\n if (devDepNames.includes(\"vitest\")) {\n stack.testFramework = \"vitest\";\n } else if (devDepNames.includes(\"jest\")) {\n stack.testFramework = \"jest\";\n } else if (devDepNames.includes(\"mocha\")) {\n stack.testFramework = \"mocha\";\n }\n\n return stack;\n}\n\nasync function detectPython(\n projectRoot: string,\n deps: FsDeps,\n): Promise<DetectedStack | null> {\n const hasPyproject = await deps.fileExists(\n join(projectRoot, \"pyproject.toml\"),\n );\n const hasRequirements = await deps.fileExists(\n join(projectRoot, \"requirements.txt\"),\n );\n\n if (!hasPyproject && !hasRequirements) return null;\n\n const stack: DetectedStack = {\n language: \"python\",\n dependencies: [],\n devDependencies: [],\n };\n\n // Try to detect framework from requirements.txt\n if (hasRequirements) {\n try {\n const content = await deps.readFile(\n join(projectRoot, \"requirements.txt\"),\n );\n const lines = content\n .split(\"\\n\")\n .map((l) => l.trim().toLowerCase().split(\"==\")[0].split(\">=\")[0]);\n stack.dependencies = lines.filter((l) => l && !l.startsWith(\"#\"));\n\n if (lines.includes(\"fastapi\")) stack.framework = \"FastAPI\";\n else if (lines.includes(\"django\")) stack.framework = \"Django\";\n else if (lines.includes(\"flask\")) stack.framework = \"Flask\";\n } catch {\n // ignore\n }\n }\n\n // Try to detect from pyproject.toml\n if (hasPyproject) {\n try {\n const content = await deps.readFile(\n join(projectRoot, \"pyproject.toml\"),\n );\n if (content.includes(\"fastapi\")) stack.framework = \"FastAPI\";\n else if (content.includes(\"django\")) stack.framework = \"Django\";\n else if (content.includes(\"flask\")) stack.framework = \"Flask\";\n\n if (content.includes(\"pytest\")) stack.testFramework = \"pytest\";\n } catch {\n // ignore\n }\n }\n\n // Detect package manager\n if (await deps.fileExists(join(projectRoot, \"poetry.lock\"))) {\n stack.packageManager = \"poetry\";\n } else if (await deps.fileExists(join(projectRoot, \"Pipfile.lock\"))) {\n stack.packageManager = \"pipenv\";\n } else if (await deps.fileExists(join(projectRoot, \"uv.lock\"))) {\n stack.packageManager = \"uv\";\n } else {\n stack.packageManager = \"pip\";\n }\n\n return stack;\n}\n\nasync function detectRust(\n projectRoot: string,\n deps: FsDeps,\n): Promise<DetectedStack | null> {\n if (!(await deps.fileExists(join(projectRoot, \"Cargo.toml\")))) return null;\n\n const stack: DetectedStack = {\n language: \"rust\",\n packageManager: \"cargo\",\n dependencies: [],\n devDependencies: [],\n };\n\n try {\n const content = await deps.readFile(join(projectRoot, \"Cargo.toml\"));\n if (content.includes(\"actix-web\")) stack.framework = \"Actix\";\n else if (content.includes(\"axum\")) stack.framework = \"Axum\";\n else if (content.includes(\"rocket\")) stack.framework = \"Rocket\";\n } catch {\n // ignore\n }\n\n return stack;\n}\n\nasync function detectGo(\n projectRoot: string,\n deps: FsDeps,\n): Promise<DetectedStack | null> {\n if (!(await deps.fileExists(join(projectRoot, \"go.mod\")))) return null;\n\n const stack: DetectedStack = {\n language: \"go\",\n packageManager: \"go\",\n dependencies: [],\n devDependencies: [],\n };\n\n try {\n const content = await deps.readFile(join(projectRoot, \"go.mod\"));\n if (content.includes(\"gin-gonic/gin\")) stack.framework = \"Gin\";\n else if (content.includes(\"gofiber/fiber\")) stack.framework = \"Fiber\";\n else if (content.includes(\"go-chi/chi\")) stack.framework = \"Chi\";\n else if (content.includes(\"labstack/echo\")) stack.framework = \"Echo\";\n } catch {\n // ignore\n }\n\n return stack;\n}\n"],"mappings":"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","names":["join","join"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/lint.ts","../src/core/linter.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { lint as runLint } from \"../core/linter.js\";\nimport { findClaudeMd, findProjectRoot } from \"../utils/paths.js\";\nimport { defaultFsDeps } from \"../utils/fs.js\";\nimport * as logger from \"../utils/logger.js\";\nimport type { LintReport, FsDeps } from \"../types.js\";\n\nexport interface LintOptions {\n json?: boolean;\n}\n\nexport async function lintCommand(\n filePath?: string,\n options: LintOptions = {},\n deps: FsDeps = defaultFsDeps,\n): Promise<LintReport> {\n const projectRoot = findProjectRoot();\n const target =\n filePath ?? findClaudeMd(projectRoot);\n\n if (!target) {\n logger.error(\n \"No CLAUDE.md found. Run `dotclaudemd init` to create one.\",\n );\n process.exitCode = 1;\n return {\n file: \"\",\n results: [],\n errorCount: 0,\n warnCount: 0,\n infoCount: 0,\n };\n }\n\n const content = await deps.readFile(target);\n const report = runLint(content, target, projectRoot);\n\n if (options.json) {\n console.log(JSON.stringify(report, null, 2));\n } else {\n printReport(report);\n }\n\n if (report.errorCount > 0) {\n process.exitCode = 1;\n }\n\n return report;\n}\n\nfunction printReport(report: LintReport): void {\n console.log();\n console.log(chalk.bold(`Linting ${report.file}`));\n console.log();\n\n if (report.results.length === 0) {\n logger.success(\"No issues found!\");\n return;\n }\n\n for (const result of report.results) {\n const icon =\n result.severity === \"error\"\n ? chalk.red(\"✗\")\n : result.severity === \"warn\"\n ? chalk.yellow(\"⚠\")\n : chalk.blue(\"ℹ\");\n const severity =\n result.severity === \"error\"\n ? chalk.red(result.severity)\n : result.severity === \"warn\"\n ? chalk.yellow(result.severity)\n : chalk.blue(result.severity);\n\n console.log(` ${icon} ${severity} ${chalk.dim(result.rule)} ${result.message}`);\n }\n\n console.log();\n const summary: string[] = [];\n if (report.errorCount > 0)\n summary.push(chalk.red(`${report.errorCount} error(s)`));\n if (report.warnCount > 0)\n summary.push(chalk.yellow(`${report.warnCount} warning(s)`));\n if (report.infoCount > 0)\n summary.push(chalk.blue(`${report.infoCount} info`));\n console.log(` ${summary.join(\", \")}`);\n console.log();\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { LintRule, LintResult, LintReport } from \"../types.js\";\n\nexport const builtinRules: LintRule[] = [\n {\n name: \"line-count\",\n description: \"CLAUDE.md should be concise\",\n severity: \"warn\",\n check(content: string): LintResult[] {\n const lines = content.split(\"\\n\").length;\n if (lines > 150) {\n return [\n {\n rule: \"line-count\",\n severity: \"error\",\n message: `CLAUDE.md is ${lines} lines (max recommended: 150). Very long files waste context window.`,\n },\n ];\n }\n if (lines > 80) {\n return [\n {\n rule: \"line-count\",\n severity: \"warn\",\n message: `CLAUDE.md is ${lines} lines (recommended: <80). Consider trimming to essential instructions.`,\n },\n ];\n }\n return [];\n },\n },\n {\n name: \"has-commands\",\n description: \"Should include build/test/dev commands\",\n severity: \"warn\",\n check(content: string): LintResult[] {\n const lower = content.toLowerCase();\n const hasCommands =\n lower.includes(\"npm run\") ||\n lower.includes(\"pnpm \") ||\n lower.includes(\"yarn \") ||\n lower.includes(\"make \") ||\n lower.includes(\"cargo \") ||\n lower.includes(\"go \") ||\n lower.includes(\"python \") ||\n lower.includes(\"pytest\") ||\n lower.includes(\"```sh\") ||\n lower.includes(\"```bash\") ||\n lower.includes(\"```shell\");\n if (!hasCommands) {\n return [\n {\n rule: \"has-commands\",\n severity: \"warn\",\n message:\n \"No build/test/dev commands found. Include commands so Claude can build and test.\",\n },\n ];\n }\n return [];\n },\n },\n {\n name: \"no-personality\",\n description: \"Avoid personality instructions\",\n severity: \"warn\",\n check(content: string): LintResult[] {\n const patterns = [\n /be a senior engineer/i,\n /think step by step/i,\n /you are an? (?:expert|senior|experienced)/i,\n /act as an? /i,\n /pretend you are/i,\n /take a deep breath/i,\n ];\n const results: LintResult[] = [];\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n for (const pattern of patterns) {\n if (pattern.test(lines[i])) {\n results.push({\n rule: \"no-personality\",\n severity: \"warn\",\n message: `Line ${i + 1}: Personality instruction detected (\"${lines[i].trim().slice(0, 60)}...\"). CLAUDE.md should focus on project facts, not persona.`,\n line: i + 1,\n });\n break; // One match per line\n }\n }\n }\n return results;\n },\n },\n {\n name: \"no-at-file-refs\",\n description: \"Avoid @file references that embed entire files\",\n severity: \"warn\",\n check(content: string): LintResult[] {\n const results: LintResult[] = [];\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/@\\w+\\/[^\\s]+/.test(lines[i]) && !lines[i].includes(\"@types\")) {\n results.push({\n rule: \"no-at-file-refs\",\n severity: \"warn\",\n message: `Line ${i + 1}: @file reference detected. These embed entire file contents into context, wasting tokens.`,\n line: i + 1,\n });\n }\n }\n return results;\n },\n },\n {\n name: \"no-negative-only\",\n description: 'Avoid \"never X\" without \"prefer Y\"',\n severity: \"warn\",\n check(content: string): LintResult[] {\n const results: LintResult[] = [];\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (/\\b(?:never|don't|do not|avoid)\\b/i.test(line)) {\n const hasAlternative =\n /\\b(?:instead|prefer|use|rather)\\b/i.test(line) ||\n (i + 1 < lines.length &&\n /\\b(?:instead|prefer|use|rather)\\b/i.test(lines[i + 1]));\n if (!hasAlternative) {\n results.push({\n rule: \"no-negative-only\",\n severity: \"warn\",\n message: `Line ${i + 1}: Negative-only instruction without alternative. Add \"instead, prefer X\" for better results.`,\n line: i + 1,\n });\n }\n }\n }\n return results;\n },\n },\n {\n name: \"stale-file-refs\",\n description: \"Referenced file paths should exist\",\n severity: \"warn\",\n check(content: string, projectRoot?: string): LintResult[] {\n if (!projectRoot) return [];\n const results: LintResult[] = [];\n const lines = content.split(\"\\n\");\n // Match paths like src/foo.ts, ./config/bar.json, etc.\n const pathRegex =\n /(?:^|\\s)(?:\\.\\/)?([a-zA-Z][\\w\\-./]*\\.\\w{1,10})(?:\\s|$|`|\"|\\))/;\n for (let i = 0; i < lines.length; i++) {\n const match = pathRegex.exec(lines[i]);\n if (match) {\n const filePath = match[1];\n // Skip common false positives\n if (\n filePath.includes(\"example\") ||\n filePath.startsWith(\"http\") ||\n filePath.includes(\"*\")\n )\n continue;\n const fullPath = join(projectRoot, filePath);\n if (!existsSync(fullPath)) {\n results.push({\n rule: \"stale-file-refs\",\n severity: \"warn\",\n message: `Line ${i + 1}: Referenced file \"${filePath}\" does not exist.`,\n line: i + 1,\n });\n }\n }\n }\n return results;\n },\n },\n {\n name: \"no-unicode-bullets\",\n description: \"Use markdown list syntax instead of unicode bullets\",\n severity: \"info\",\n check(content: string): LintResult[] {\n const results: LintResult[] = [];\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/^\\s*[•‣⁃◦▪▸►]/.test(lines[i])) {\n results.push({\n rule: \"no-unicode-bullets\",\n severity: \"info\",\n message: `Line ${i + 1}: Unicode bullet character detected. Use markdown \"- \" or \"* \" instead.`,\n line: i + 1,\n });\n }\n }\n return results;\n },\n },\n {\n name: \"no-placeholder-vars\",\n description: \"No unreplaced {{variable}} placeholders\",\n severity: \"error\",\n check(content: string): LintResult[] {\n const results: LintResult[] = [];\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const matches = lines[i].match(/\\{\\{\\w+\\}\\}/g);\n if (matches) {\n results.push({\n rule: \"no-placeholder-vars\",\n severity: \"error\",\n message: `Line ${i + 1}: Unreplaced placeholder(s): ${matches.join(\", \")}`,\n line: i + 1,\n });\n }\n }\n return results;\n },\n },\n];\n\nexport function lint(\n content: string,\n filePath: string,\n projectRoot?: string,\n rules: LintRule[] = builtinRules,\n): LintReport {\n const results: LintResult[] = [];\n\n for (const rule of rules) {\n results.push(...rule.check(content, projectRoot));\n }\n\n return {\n file: filePath,\n results,\n errorCount: results.filter((r) => r.severity === \"error\").length,\n warnCount: results.filter((r) => r.severity === \"warn\").length,\n infoCount: results.filter((r) => r.severity === \"info\").length,\n };\n}\n"],"mappings":";;;;;;;;;;;;AAAA,OAAO,WAAW;;;ACAlB,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AAGd,IAAM,eAA2B;AAAA,EACtC;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,SAA+B;AACnC,YAAM,QAAQ,QAAQ,MAAM,IAAI,EAAE;AAClC,UAAI,QAAQ,KAAK;AACf,eAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,UAAU;AAAA,YACV,SAAS,gBAAgB,KAAK;AAAA,UAChC;AAAA,QACF;AAAA,MACF;AACA,UAAI,QAAQ,IAAI;AACd,eAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,UAAU;AAAA,YACV,SAAS,gBAAgB,KAAK;AAAA,UAChC;AAAA,QACF;AAAA,MACF;AACA,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,SAA+B;AACnC,YAAM,QAAQ,QAAQ,YAAY;AAClC,YAAM,cACJ,MAAM,SAAS,SAAS,KACxB,MAAM,SAAS,OAAO,KACtB,MAAM,SAAS,OAAO,KACtB,MAAM,SAAS,OAAO,KACtB,MAAM,SAAS,QAAQ,KACvB,MAAM,SAAS,KAAK,KACpB,MAAM,SAAS,SAAS,KACxB,MAAM,SAAS,QAAQ,KACvB,MAAM,SAAS,OAAO,KACtB,MAAM,SAAS,SAAS,KACxB,MAAM,SAAS,UAAU;AAC3B,UAAI,CAAC,aAAa;AAChB,eAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,UAAU;AAAA,YACV,SACE;AAAA,UACJ;AAAA,QACF;AAAA,MACF;AACA,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,SAA+B;AACnC,YAAM,WAAW;AAAA,QACf;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,YAAM,UAAwB,CAAC;AAC/B,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,mBAAW,WAAW,UAAU;AAC9B,cAAI,QAAQ,KAAK,MAAM,CAAC,CAAC,GAAG;AAC1B,oBAAQ,KAAK;AAAA,cACX,MAAM;AAAA,cACN,UAAU;AAAA,cACV,SAAS,QAAQ,IAAI,CAAC,wCAAwC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,cAC1F,MAAM,IAAI;AAAA,YACZ,CAAC;AACD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,SAA+B;AACnC,YAAM,UAAwB,CAAC;AAC/B,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAI,eAAe,KAAK,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,SAAS,QAAQ,GAAG;AACjE,kBAAQ,KAAK;AAAA,YACX,MAAM;AAAA,YACN,UAAU;AAAA,YACV,SAAS,QAAQ,IAAI,CAAC;AAAA,YACtB,MAAM,IAAI;AAAA,UACZ,CAAC;AAAA,QACH;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,SAA+B;AACnC,YAAM,UAAwB,CAAC;AAC/B,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,OAAO,MAAM,CAAC;AACpB,YAAI,oCAAoC,KAAK,IAAI,GAAG;AAClD,gBAAM,iBACJ,qCAAqC,KAAK,IAAI,KAC7C,IAAI,IAAI,MAAM,UACb,qCAAqC,KAAK,MAAM,IAAI,CAAC,CAAC;AAC1D,cAAI,CAAC,gBAAgB;AACnB,oBAAQ,KAAK;AAAA,cACX,MAAM;AAAA,cACN,UAAU;AAAA,cACV,SAAS,QAAQ,IAAI,CAAC;AAAA,cACtB,MAAM,IAAI;AAAA,YACZ,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,SAAiB,aAAoC;AACzD,UAAI,CAAC,YAAa,QAAO,CAAC;AAC1B,YAAM,UAAwB,CAAC;AAC/B,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAEhC,YAAM,YACJ;AACF,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,QAAQ,UAAU,KAAK,MAAM,CAAC,CAAC;AACrC,YAAI,OAAO;AACT,gBAAM,WAAW,MAAM,CAAC;AAExB,cACE,SAAS,SAAS,SAAS,KAC3B,SAAS,WAAW,MAAM,KAC1B,SAAS,SAAS,GAAG;AAErB;AACF,gBAAM,WAAW,KAAK,aAAa,QAAQ;AAC3C,cAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,oBAAQ,KAAK;AAAA,cACX,MAAM;AAAA,cACN,UAAU;AAAA,cACV,SAAS,QAAQ,IAAI,CAAC,sBAAsB,QAAQ;AAAA,cACpD,MAAM,IAAI;AAAA,YACZ,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,SAA+B;AACnC,YAAM,UAAwB,CAAC;AAC/B,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAI,gBAAgB,KAAK,MAAM,CAAC,CAAC,GAAG;AAClC,kBAAQ,KAAK;AAAA,YACX,MAAM;AAAA,YACN,UAAU;AAAA,YACV,SAAS,QAAQ,IAAI,CAAC;AAAA,YACtB,MAAM,IAAI;AAAA,UACZ,CAAC;AAAA,QACH;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,SAA+B;AACnC,YAAM,UAAwB,CAAC;AAC/B,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,UAAU,MAAM,CAAC,EAAE,MAAM,cAAc;AAC7C,YAAI,SAAS;AACX,kBAAQ,KAAK;AAAA,YACX,MAAM;AAAA,YACN,UAAU;AAAA,YACV,SAAS,QAAQ,IAAI,CAAC,gCAAgC,QAAQ,KAAK,IAAI,CAAC;AAAA,YACxE,MAAM,IAAI;AAAA,UACZ,CAAC;AAAA,QACH;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,KACd,SACA,UACA,aACA,QAAoB,cACR;AACZ,QAAM,UAAwB,CAAC;AAE/B,aAAW,QAAQ,OAAO;AACxB,YAAQ,KAAK,GAAG,KAAK,MAAM,SAAS,WAAW,CAAC;AAAA,EAClD;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,YAAY,QAAQ,OAAO,CAAC,MAAM,EAAE,aAAa,OAAO,EAAE;AAAA,IAC1D,WAAW,QAAQ,OAAO,CAAC,MAAM,EAAE,aAAa,MAAM,EAAE;AAAA,IACxD,WAAW,QAAQ,OAAO,CAAC,MAAM,EAAE,aAAa,MAAM,EAAE;AAAA,EAC1D;AACF;;;ADpOA,eAAsB,YACpB,UACA,UAAuB,CAAC,GACxB,OAAe,eACM;AACrB,QAAM,cAAc,gBAAgB;AACpC,QAAM,SACJ,YAAY,aAAa,WAAW;AAEtC,MAAI,CAAC,QAAQ;AACX,IAAO;AAAA,MACL;AAAA,IACF;AACA,YAAQ,WAAW;AACnB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,CAAC;AAAA,MACV,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,KAAK,SAAS,MAAM;AAC1C,QAAM,SAAS,KAAQ,SAAS,QAAQ,WAAW;AAEnD,MAAI,QAAQ,MAAM;AAChB,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,OAAO;AACL,gBAAY,MAAM;AAAA,EACpB;AAEA,MAAI,OAAO,aAAa,GAAG;AACzB,YAAQ,WAAW;AAAA,EACrB;AAEA,SAAO;AACT;AAEA,SAAS,YAAY,QAA0B;AAC7C,UAAQ,IAAI;AACZ,UAAQ,IAAI,MAAM,KAAK,WAAW,OAAO,IAAI,EAAE,CAAC;AAChD,UAAQ,IAAI;AAEZ,MAAI,OAAO,QAAQ,WAAW,GAAG;AAC/B,IAAO,QAAQ,kBAAkB;AACjC;AAAA,EACF;AAEA,aAAW,UAAU,OAAO,SAAS;AACnC,UAAM,OACJ,OAAO,aAAa,UAChB,MAAM,IAAI,QAAG,IACb,OAAO,aAAa,SAClB,MAAM,OAAO,QAAG,IAChB,MAAM,KAAK,QAAG;AACtB,UAAM,WACJ,OAAO,aAAa,UAChB,MAAM,IAAI,OAAO,QAAQ,IACzB,OAAO,aAAa,SAClB,MAAM,OAAO,OAAO,QAAQ,IAC5B,MAAM,KAAK,OAAO,QAAQ;AAElC,YAAQ,IAAI,KAAK,IAAI,IAAI,QAAQ,KAAK,MAAM,IAAI,OAAO,IAAI,CAAC,KAAK,OAAO,OAAO,EAAE;AAAA,EACnF;AAEA,UAAQ,IAAI;AACZ,QAAM,UAAoB,CAAC;AAC3B,MAAI,OAAO,aAAa;AACtB,YAAQ,KAAK,MAAM,IAAI,GAAG,OAAO,UAAU,WAAW,CAAC;AACzD,MAAI,OAAO,YAAY;AACrB,YAAQ,KAAK,MAAM,OAAO,GAAG,OAAO,SAAS,aAAa,CAAC;AAC7D,MAAI,OAAO,YAAY;AACrB,YAAQ,KAAK,MAAM,KAAK,GAAG,OAAO,SAAS,OAAO,CAAC;AACrD,UAAQ,IAAI,KAAK,QAAQ,KAAK,IAAI,CAAC,EAAE;AACrC,UAAQ,IAAI;AACd;","names":[]}