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.
- package/README.md +470 -52
- package/dist/{browse-7V4CRGTH.js → browse-JZNKTNHN.js} +4 -4
- package/dist/{chunk-3ERFQLAD.js → chunk-NYAP7NPA.js} +2 -2
- package/dist/{chunk-3R6PUA3E.js → chunk-SU2BLIFX.js} +5 -1
- package/dist/chunk-SU2BLIFX.js.map +1 -0
- package/dist/cli.js +4 -4
- package/dist/{doctor-4B7J2EH3.js → doctor-BHKDRHD4.js} +2 -2
- package/dist/{init-GLWLFVHN.js → init-5PXXIWA7.js} +81 -5
- package/dist/init-5PXXIWA7.js.map +1 -0
- package/dist/{lint-W7ZIDPL7.js → lint-BMJBNSBA.js} +3 -3
- package/dist/lint-BMJBNSBA.js.map +1 -0
- package/package.json +1 -1
- package/templates/java/springboot.md +64 -0
- package/templates/javascript/astro.md +51 -0
- package/templates/javascript/sveltekit.md +53 -0
- package/templates/javascript/typescript-monorepo.md +50 -0
- package/templates/javascript/vue-nuxt.md +57 -0
- package/templates/php/laravel.md +55 -0
- package/templates/ruby/rails.md +55 -0
- package/dist/chunk-3R6PUA3E.js.map +0 -1
- package/dist/init-GLWLFVHN.js.map +0 -1
- package/dist/lint-W7ZIDPL7.js.map +0 -1
- /package/dist/{browse-7V4CRGTH.js.map → browse-JZNKTNHN.js.map} +0 -0
- /package/dist/{chunk-3ERFQLAD.js.map → chunk-NYAP7NPA.js.map} +0 -0
- /package/dist/{doctor-4B7J2EH3.js.map → doctor-BHKDRHD4.js.map} +0 -0
|
@@ -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":"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","names":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|