rolobits/nestjs-doctor/nestjs-doctor-create-rule

Claude Code and agent skill by rolobits/nestjs-doctor

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Use when the user wants a convention of their own enforced, asks for a custom nestjs-doctor rule, or says the built-in rules do not cover something their team cares about. Walks through choosing the scope, matching the AST with ts-morph, registering the rule, and verifying it loads.

Install

npx vskill@latest install rolobits/nestjs-doctor/nestjs-doctor-create-rule

SKILL.md

Create a custom rule

v0.0.0

Create a custom rule for nestjs-doctor that detects a specific pattern or anti-pattern in the user's NestJS codebase.

Capabilities and Limitations

Before generating any rule, use this reference to determine what is possible.

What file rules can do

File rules receive a single ts-morph SourceFile at a time. Available APIs:

  • Classes: getClasses(), getName(), getDecorator("Name"), getDecorators(), getImplements(), getConstructors(), getProperties(), getMethods()
  • Constructor params: ctor.getParameters() -> param.getName(), param.getType().getText(), param.isReadonly(), param.getTypeNode()?.getText()
  • Methods: getMethods() -> method.getName(), method.getDecorators(), method.getReturnType(), method.getStartLineNumber()
  • Imports: getImportDeclarations() -> imp.getModuleSpecifierValue(), imp.getNamedImports()
  • AST traversal: getDescendantsOfKind(SyntaxKind.X) — find any AST node type
  • Position: node.getStartLineNumber(), node.getStart(), node.getStartLinePos(), getText()
  • External: Can require() npm packages and Node.js builtins (e.g., fs)
  • Source lines: The runner automatically attaches +-5 lines of context to each diagnostic

Type info caveat: param.getType().getText() often returns import("/path").ClassName because the project uses skipFileDependencyResolution. Use param.getTypeNode()?.getText() for the literal annotation text, or regex-extract the class name from the type string.

What project rules can do (everything above, plus)

Project rules receive the entire Project and cross-file analysis data:

  • context.project.getSourceFile(path) — access any file's AST
  • context.files — array of all file paths being analyzed
  • context.moduleGraph.modules — Map<string, ModuleNode> where each ModuleNode has: { name, filePath, classDeclaration, imports[], exports[], providers[], controllers[] }
  • context.moduleGraph.edges — Map<string, Set<string>> (module name -> set of imported module names)
  • context.moduleGraph.providerToModule — Map<string, ModuleNode> (provider class name -> owning module)
  • context.providers — Map<string, ProviderInfo> where each ProviderInfo has: { name, filePath, classDeclaration, dependencies: string[], publicMethodCount }
  • context.config — full NestjsDoctorConfig object

Caveats:

  • providers only includes @Injectable() classes — value/factory/alias providers are not indexed
  • Project rule diagnostics do NOT get automatic sourceLines — include them yourself if needed
Hard limits (CRITICAL)
  • `check()` MUST be synchronous — the runner calls rule.check(context) without await. An async check() silently returns a Promise that is discarded. Zero diagnostics, zero errors. This is the #1 silent failure mode.
  • Cannot modify source files — mutations corrupt the shared AST used by all rules
  • File rules cannot access other files, the module graph, or providers
  • Only `.ts` rule files are loaded — .js, .mjs, .cjs are ignored by the custom rule loader
  • Cross-file type resolution is often incomplete — prefer param.getTypeNode()?.getText() over param.getType().getText() for reliable results

Step 1: Assess Feasibility

Before asking for details, evaluate the user's request against the capabilities above.

  1. Determine detection scope: Single-file pattern -> file rule. Cross-file analysis -> project rule.
  2. Map the core operation to available APIs. Ask yourself: can ts-morph or the module graph answer this question?
RequestFeasible?Why
"Check that every @Controller has @ApiTags()"YEScls.getDecorator("Controller") + cls.getDecorator("ApiTags")
"Check service X is only used in module Y"YESmoduleGraph.providerToModule.get("X")
"Detect providers with too many dependencies"YESprovider.dependencies.length threshold check
"Ban a specific npm import"YESimp.getModuleSpecifierValue() === "banned-pkg"
"Check runtime types match"NOStatic analysis only — no runtime access
"Check database query results"NONo runtime or I/O during analysis
"Check code is formatted"NOUse a linter/formatter instead
"Check that an async handler awaits a call"PARTIALCan detect async keyword and check for AwaitExpression nodes, but cannot trace all control-flow paths
  1. Check for the async pitfall — if the detection logic needs to read files from disk asynchronously, make HTTP calls, or do any I/O that requires await, the rule CANNOT work because check() must be synchronous. Synchronous fs.readFileSync() or fs.existsSync() is fine.
  1. Report assessment to the user:
  • FEASIBLE — proceed to Step 2
  • PARTIALLY FEASIBLE — explain what can be detected and what gap remains, let user decide
  • NOT FEASIBLE — explain why, suggest an alternative tool or approach

Step 2: Understand the Request

Ask the user what they want to detect. Determine:

  • Pattern to detect: What code pattern is bad (or required)?
  • Scope: Does the rule check individual files (file) or need cross-file analysis (project)?
  • Category: security, correctness, architecture, or performance
  • Severity: error, warning, or info
  • Rule ID: A short kebab-case name (e.g., require-logger-in-services)

If the user is unsure, see the Suggestions section at the bottom for common rule ideas.

Step 3: Check Config

Find the existing nestjs-doctor configuration. Check these locations in order:

!
cat nestjs-doctor.config.json 2>/dev/null || cat .nestjs-doctor.json 2>/dev/null || node -e "const p=require('./package.json'); if(p['nestjs-doctor']) console.log(JSON.stringify(p['nestjs-doctor'],null,2)); else console.log('NO_CONFIG')"

Note whether customRulesDir is already set. If it is, use that directory. If not, default to ./nestjs-doctor-rules.

Step 4: Create Rules Directory

Create the custom rules directory if it doesn't exist:

!
mkdir -p <customRulesDir>

Step 5: Generate the Rule

Read the reference that matches what you are about to write:

ReferenceRead it for
references/rule-api.mdThe rule object's shape, every meta field, what validation rejects, and putting several rules in one file
references/examples.mdA complete file-scoped rule and a complete project-scoped rule
references/ts-morph.mdFinding decorators, classes, imports, and call expressions in the AST

Write a .ts file to the custom rules directory. The filename should match the rule ID (e.g., require-logger-in-services.ts).

Step 6: Update Config

If customRulesDir is not already set in the project config, add it.

If `nestjs-doctor.config.json` or `.nestjs-doctor.json` exists, read it and add the customRulesDir field:

!
# Read existing config, add customRulesDir, write back
node -e "
const fs = require('fs');
const f = fs.existsSync('nestjs-doctor.config.json') ? 'nestjs-doctor.config.json' : '.nestjs-doctor.json';
const cfg = JSON.parse(fs.readFileSync(f, 'utf-8'));
cfg.customRulesDir = '<customRulesDir>';
fs.writeFileSync(f, JSON.stringify(cfg, null, 2) + '\n');
console.log('Updated ' + f);
"

If config is in `package.json`, update the nestjs-doctor key.

If no config exists, create nestjs-doctor.config.json:

json
{
"customRulesDir": "<customRulesDir>"
}

Step 7: Verify

Run nestjs-doctor and check that the custom rule loads without warnings:

!
npx nestjs-doctor $ARGUMENTS --json 2>&1

Check the output for:

  • The custom rule appearing in diagnostics with custom/ prefix
  • No validation warnings about the rule file
  • The scan completing successfully

If there are warnings, fix the rule file and re-run.

Suggestions

If the user isn't sure what to check, suggest these common custom rules:

Rule ideaScopeCategoryDetection logic
Require a Logger in every servicefilecorrectnesscls.getDecorator("Injectable") && !ctor.getParameters().some(p => p.getTypeNode()?.getText()?.includes("Logger"))
Ban specific npm imports (e.g., moment)fileperformanceimp.getModuleSpecifierValue() === "moment"
Require @ApiTags() on all controllersfilearchitecturecls.getDecorator("Controller") && !cls.getDecorator("ApiTags")
Require @ApiOperation() on HTTP handlersfilearchitecturemethod.getDecorators().some(d => httpDecorators.has(d.getName())) && !method.getDecorator("ApiOperation")
Enforce max constructor dependenciesfilearchitecturector.getParameters().length > MAX on @Injectable() classes
Ban direct DB queries outside repositoriesfilearchitectureCheck getDescendantsOfKind(SyntaxKind.CallExpression) for ORM calls in non-*Repository classes
Enforce naming conventionsfilearchitecturecls.getDecorator("Injectable") && !name.endsWith("Service") && !name.endsWith("Repository")
Require services have test filesprojectcorrectnessrequire("fs").existsSync(provider.filePath.replace(".ts", ".spec.ts"))
Detect providers in multiple modulesprojectarchitectureCount each provider name across moduleGraph.modules.values() entries' providers[] — flag if > 1
Require DTO validation pipes on POST/PUTfilecorrectnessCheck @Post()/@Put() handler params for @Body() + @UsePipes(ValidationPipe) or global pipe

Security

  • Security scanPassed, 93 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/rolobits/nestjs-doctor/nestjs-doctor-create-rule/badge)](https://verified-skill.com/skills/rolobits/nestjs-doctor/nestjs-doctor-create-rule)

Something wrong with this skill or its page? Tell us.