Skip to content

feat: implement bundled rule packs (presets)#32

Merged
oshorefueled merged 10 commits intomainfrom
ft/presets
Jan 2, 2026
Merged

feat: implement bundled rule packs (presets)#32
oshorefueled merged 10 commits intomainfrom
ft/presets

Conversation

@oshorefueled
Copy link
Contributor

@oshorefueled oshorefueled commented Dec 7, 2025

Summary

Implements bundled rule packs (presets) that ship with VectorLint, allowing users to easily enable default rules while maintaining the ability to override them with custom rules. User-defined rules automatically shadow presets with the same name.

Changes

  • Added presets system - Created presets directory with meta.json registry for bundled rule packs
  • Implemented PresetLoader - Loads and validates preset registry with Zod schemas
  • Refactored to RulePackLoader - Renamed EvalPackLoader with shadowing logic (user rules > presets)
  • Migrated default rules - Moved rules from .github/rules/TinyRocket/ to presets/VectorLint/
  • Renamed terminology - Changed findEvalFiles → findRuleFiles, loadPromptFile → loadRuleFile, loadPrompts → loadRules for consistency
  • Updated packaging - Added presets directory to package.json files array for distribution

Breaking Changes

None - This is purely additive. Existing user rules continue to work unchanged.

Testing

  • All preset-related tests passing (preset-loader.test.ts, rule-pack-loader.test.ts, rule-pack-e2e.test.ts)
  • Verified shadowing logic: user rules override presets with the same name
  • Confirmed presets load correctly from bundled directory
  • Full test suite passing (229/229 tests)

Notes for Reviewers

The shadowing mechanism uses a Map where presets are loaded first, then user rules overwrite any matching keys. This ensures user control while providing sensible defaults.

Summary by CodeRabbit

  • New Features

    • Preset discovery and per-pack rule handling; hierarchical rule naming for per-criterion reporting.
  • New Rules

    • Added AI-pattern, Directness, Pseudo-Advice, and Repetition evaluators.
  • Documentation

    • New preset metadata and example config illustrating preset usage and rule specs.
  • Breaking / Notice

    • Public API: loadRules replaces loadPrompts; pack objects are richer and rulesPath is now optional—update callers.
  • Tests

    • New and updated tests and fixtures covering presets, loaders, and rules.

✏️ Tip: You can customize this high-level summary in your review settings.

- Add presets/ directory with meta.json registry
- Implement PresetLoader and refactor to RulePackLoader
- User rules shadow presets with same name
- Rename Eval/Prompt terminology to Rule for consistency
- Make pack field required in PROMPT_FILE_SCHEMA
- Add buildRuleName() helper for consistent rule name construction
- Update rule names to follow PackName.RuleId.CriterionId pattern
- Apply naming to both subjective and semi-objective evaluation paths
- Update loadRuleFile() to require packName parameter
- Use 'Default' pack for legacy loadRules() function in tests
- Add fileURLToPath and dirname imports for ESM compatibility
- Define __filename and __dirname using import.meta.url
- Fixes 'ReferenceError: __dirname is not defined' in built output
- Affects commands.ts and validate-command.ts preset loading
…t quality issues

- add pseudo-advice rule that catches vague guidance telling WHAT without explaining HOW
- add repetition rule that identifies redundant sections wasting reader time
- add directness rule that ensures sections immediately answer their header questions
- group violations by criterion in orchestrator for better multi-criterion rule reporting
- add ai-pattern test fixtures for buzzword and negation pattern detection
@coderabbitai
Copy link

coderabbitai bot commented Dec 30, 2025

📝 Walkthrough

Walkthrough

Adds a preset-driven rule-pack system: new VectorLint presets and meta, PresetLoader and RulePackLoader that merge presets with user packs, renames prompt APIs to rule APIs (loadRules/loadRuleFile), threads packName through CLI/orchestrator for hierarchical rule naming, and updates schemas, file resolution, and tests.

Changes

Cohort / File(s) Summary
Presets
presets/meta.json, presets/VectorLint/ai-pattern.md, presets/VectorLint/directness.md, presets/VectorLint/pseudo-advice.md, presets/VectorLint/repetition.md
Add a preset registry entry and four VectorLint rule/spec docs describing evaluators and detection criteria.
Preset loader & pack discovery
src/config/preset-loader.ts, src/boundaries/rule-pack-loader.ts
New PresetLoader reads/validates meta.json; new RulePackLoader replaces EvalPackLoader, exposes listAllPacks and findRuleFiles, merges presets with user packs and marks isPreset.
Removed loader
src/boundaries/eval-pack-loader.ts
Remove old EvalPackLoader.
Prompt → Rule loader API
src/prompts/prompt-loader.ts, tests/*
Rename loadPromptsloadRules, loadPromptFile(fullPath, pack?)loadRuleFile(fullPath, packName) (pack required); update call sites and tests.
CLI orchestration & types
src/cli/orchestrator.ts, src/cli/types.ts, src/cli/commands.ts, src/cli/validate-command.ts
Thread packName through processing, add buildRuleName(packName,ruleId,criterionId), adopt PresetLoader/RulePackLoader, iterate/report on pack objects (name,path,isPreset), update scoring/reporting to hierarchical rule names.
Schemas & validation
src/schemas/preset-schemas.ts, src/schemas/prompt-schemas.ts, src/schemas/config-schemas.ts
Add Zod schemas (PRESET_ENTRY_SCHEMA, PRESET_REGISTRY_SCHEMA, PresetRegistry type); make pack required in prompt schema; make rulesPath optional in config schema.
Scan & config changes
src/boundaries/config-loader.ts, src/scan/file-resolver.ts
Make rulesPath optional in config loader and file resolver; guard logic when absent; change resolveTargets signature to accept optional rulesPath and FilePatternConfig[] scanPaths.
Orchestrator reporting
src/cli/orchestrator.ts
Group semi-objective violations by criterion, propagate packName into criterion processing, and produce hierarchical rule IDs.
Tests & fixtures
tests/fixtures/ai-pattern/*, tests/preset-loader.test.ts, tests/rule-pack-loader.test.ts, tests/rule-pack-e2e.test.ts, tests/prompt-loader-validation.test.ts, tests/target.test.ts, tests/validator.test.ts, tests/evaluator.test.ts
Add fixtures and tests for presets/preset loader; update tests to use RulePackLoader, listAllPacks, findRuleFiles, loadRules, and adjusted PromptFile shapes.
Package manifest & examples
package.json, .vectorlint.ini.example
Add presets to files, add presets and vectorlint.ini.example to keywords; update example config to document optional RulesPath.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI (validate/commands)
    participant PresetLdr as PresetLoader
    participant RulePkgLdr as RulePackLoader
    participant FS as File System
    participant Loader as RuleLoader (loadRuleFile / loadRules)
    participant Orch as Orchestrator

    CLI->>PresetLdr: new PresetLoader(presetsDir)
    CLI->>RulePkgLdr: new RulePackLoader(PresetLoader)
    CLI->>RulePkgLdr: listAllPacks(userRulesPath)
    RulePkgLdr->>PresetLdr: getAvailablePresets()
    PresetLdr->>FS: read presets/meta.json
    FS-->>PresetLdr: meta.json
    PresetLdr-->>RulePkgLdr: preset entries
    RulePkgLdr->>FS: scan userRulesPath for directories
    FS-->>RulePkgLdr: user pack dirs
    Note over RulePkgLdr: Merge presets (low priority) with user packs (high priority)
    RulePkgLdr-->>CLI: ResolvedPack[] (name,path,isPreset)

    loop for each pack in ResolvedPack[]
        CLI->>RulePkgLdr: findRuleFiles(pack.path)
        RulePkgLdr->>FS: recursive *.md scan
        FS-->>RulePkgLdr: rule file paths
        RulePkgLdr-->>CLI: file paths
        CLI->>Loader: loadRuleFile(filePath, pack.name)
        Loader-->>CLI: parsed Rule object
        CLI->>Orch: process result (rule, packName)
        Orch->>Orch: buildRuleName(packName, ruleId, criterionId)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • hurshore
  • ayo6706

Poem

🐰 I hopped through presets, tidy and spry,
Pack paths and rules beneath a bright sky,
I stitched user packs with vendor delight,
Named rules in order, neat and polite,
VectorLint now hums — reviewers take flight.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: implement bundled rule packs (presets)' clearly summarizes the main change - adding bundled rule packs as a core feature. It is concise, specific, and directly relates to the changeset's primary objective.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
package.json (1)

21-25: Verify presets are included in the package distribution.

The PR summary states "Updates packaging to include presets in package.json files array," but the files array doesn't include the presets/ directory. This means presets won't be bundled with the published package, breaking the preset functionality for users.

🔎 Proposed fix
 "files": [
   "dist",
+  "presets",
   "README.md",
   "LICENSE.md"
 ],
tests/validator.test.ts (1)

60-73: Missing required pack field in manual PromptFile construction.

The PromptFile object constructed for testing is missing the pack field, which is now required according to the updated PROMPT_FILE_SCHEMA in src/schemas/prompt-schemas.ts. This test may fail validation or produce unintended behavior.

🔎 Proposed fix
 const p: PromptFile = {
   id: 'x',
   filename: 'x.md',
   fullPath: '/path/to/x.md',
   body: '',
+  pack: 'TestPack',
   meta: {
     id: 'x',
     name: 'X',
src/cli/validate-command.ts (1)

85-88: Duplicate "no prompts found" checks.

There are two checks for prompts.length === 0: one inside the try block (lines 85-88) and another after warnings display (lines 103-106). The second check at lines 103-106 is unreachable because the first check already exits with process.exit(1).

🔎 Proposed fix - remove the unreachable check
       // Display loader warnings
       if (warnings.length) {
         printFileHeader('Loader');
         for (const w of warnings) printValidationRow('warning', w);
         console.log('');
       }
 
-      // Ensure at least one prompt was found
-      if (prompts.length === 0) {
-        console.error(`Error: no .md prompts found in ${rulesPath}`);
-        process.exit(1);
-      }
-
       // Validate all prompts
       const result = validateAll(prompts);

Also applies to: 103-106

🧹 Nitpick comments (11)
package.json (1)

38-39: Reconsider "vectorlint.ini.example" as a keyword.

Keywords should represent searchable concepts or categories (e.g., "configuration", "ini"), not specific filenames. "vectorlint.ini.example" appears to be a filename and may not provide meaningful discoverability.

🔎 Suggested alternative
   "presets",
-  "vectorlint.ini.example",
+  "configuration",
   "vectorlint"
presets/VectorLint/repetition.md (3)

26-26: Consider LanguageTool suggestion (optional).

Static analysis flagged the spelling of "CRITERIA," though this appears to be intentional formatting for the section header.


37-37: Minor style: word repetition (optional).

LanguageTool noted repetition between "Explanations" and "explained" in close proximity. Consider rewording for variety, though the meaning is clear as written.

🔎 Possible alternative
-**Redundant Explanations**: A concept fully explained in one section is explained again later as if it's new information
+**Redundant Explanations**: A concept fully described in one section is explained again later as if it's new information

39-39: Minor style: consider "same" instead of "exact same" (optional).

LanguageTool suggests "exact same" may be wordy. "Same" alone would be sufficient, though this is a minor stylistic choice.

tests/fixtures/ai-pattern/negation-pattern.md (1)

145-151: Consider replacing hard tabs with spaces in the comparison table.

The comparison table uses hard tabs which triggers markdownlint MD010 warnings. While this is a test fixture, replacing tabs with spaces or using proper Markdown table syntax would maintain consistency.

🔎 Example of Markdown table format
| Tool | Primary Focus | Context Awareness | Pricing Model | Best Use Case |
|------|---------------|-------------------|---------------|---------------|
| Graphite Agent | Complete platform | PR diff + context | $40/user/month | Teams wanting velocity |
...
src/schemas/preset-schemas.ts (1)

8-12: Consider adding .min(1) constraint for the path field.

The current schema allows empty strings for path, which could cause issues during path resolution. A minimal constraint would prevent invalid entries.

🔎 Optional refinement
 export const PRESET_ENTRY_SCHEMA = z.object({
-    path: z.string(),
+    path: z.string().min(1),
     description: z.string().optional(),
 });
src/config/preset-loader.ts (1)

3-3: Missing .js extension in import for ESM compatibility.

Per project guidelines, TypeScript ESM requires explicit file extensions for imports. The import from ../schemas/preset-schemas should include the .js extension.

🔎 Proposed fix
-import { PRESET_REGISTRY_SCHEMA, type PresetRegistry } from '../schemas/preset-schemas';
+import { PRESET_REGISTRY_SCHEMA, type PresetRegistry } from '../schemas/preset-schemas.js';
src/cli/commands.ts (1)

110-113: Consider logging when presets provide rules.

Currently, the warning only fires when packs.length === 0. For debugging purposes, it might be helpful in verbose mode to also log which packs are from presets vs. user rules.

src/boundaries/rule-pack-loader.ts (2)

4-4: Missing .js extension in import for ESM compatibility.

Per project guidelines for TypeScript ESM with explicit imports.

🔎 Proposed fix
-import { PresetLoader } from '../config/preset-loader';
+import { PresetLoader } from '../config/preset-loader.js';

71-73: Mixed sync/async filesystem access.

The method uses synchronous existsSync for the existence check but async fs.readdir for directory traversal. Consider using the async fs.access for consistency, or keep existsSync since it's a simple guard before the async work begins (acceptable pattern).

src/cli/validate-command.ts (1)

63-83: Consider extracting shared pack-loading logic.

The pack enumeration and rule loading logic (lines 63-83) is nearly identical to commands.ts (lines 104-128). Consider extracting this into a shared utility function to reduce duplication.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 659ea2e and 71c5a28.

📒 Files selected for processing (26)
  • package.json
  • presets/VectorLint/ai-pattern.md
  • presets/VectorLint/directness.md
  • presets/VectorLint/pseudo-advice.md
  • presets/VectorLint/repetition.md
  • presets/meta.json
  • src/boundaries/eval-pack-loader.ts
  • src/boundaries/rule-pack-loader.ts
  • src/cli/commands.ts
  • src/cli/orchestrator.ts
  • src/cli/types.ts
  • src/cli/validate-command.ts
  • src/config/preset-loader.ts
  • src/prompts/prompt-loader.ts
  • src/schemas/preset-schemas.ts
  • src/schemas/prompt-schemas.ts
  • tests/evaluator.test.ts
  • tests/fixtures/ai-pattern/buzzword.md
  • tests/fixtures/ai-pattern/negation-pattern.md
  • tests/fixtures/ai-pattern/test.md
  • tests/preset-loader.test.ts
  • tests/prompt-loader-validation.test.ts
  • tests/rule-pack-e2e.test.ts
  • tests/rule-pack-loader.test.ts
  • tests/target.test.ts
  • tests/validator.test.ts
💤 Files with no reviewable changes (1)
  • src/boundaries/eval-pack-loader.ts
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.ts: Use TypeScript ESM with explicit imports and narrow types
Use 2-space indentation; avoid trailing whitespace
Maintain strict TypeScript with no any; use unknown + schema validation for external data
Use custom error types with proper inheritance; catch blocks use unknown type

Files:

  • src/schemas/preset-schemas.ts
  • src/schemas/prompt-schemas.ts
  • src/boundaries/rule-pack-loader.ts
  • src/cli/types.ts
  • src/cli/commands.ts
  • src/config/preset-loader.ts
  • src/prompts/prompt-loader.ts
  • src/cli/validate-command.ts
  • src/cli/orchestrator.ts
src/boundaries/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use Zod schemas for boundary validation of all external data (files, CLI, env, APIs) at system boundaries

Files:

  • src/boundaries/rule-pack-loader.ts
tests/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.test.ts: Write tests using Vitest framework with focus on config parsing, file discovery, schema/structured output, and locator
Use dependency injection in tests: mock providers; do not hit network in unit tests

Files:

  • tests/prompt-loader-validation.test.ts
  • tests/target.test.ts
  • tests/evaluator.test.ts
  • tests/rule-pack-e2e.test.ts
  • tests/preset-loader.test.ts
  • tests/rule-pack-loader.test.ts
  • tests/validator.test.ts
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Recursively load all `.md` files from within each rule pack; multiple packs can be used simultaneously
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Organize rules into subdirectories (packs) within RulesPath; use company names (e.g., Acme, TechCorp, Stripe) to indicate which style guide the rules implement
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Applies to .vectorlint.ini : Use `[glob/pattern]` sections in `.vectorlint.ini` to specify which packs run on which files

Applied to files:

  • package.json
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Applies to src/boundaries/**/*.ts : Use Zod schemas for boundary validation of all external data (files, CLI, env, APIs) at system boundaries

Applied to files:

  • src/schemas/preset-schemas.ts
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Applies to src/boundaries/env-parser.ts : All environment variables must be validated via Zod schemas in `src/boundaries/env-parser.ts`

Applied to files:

  • src/schemas/preset-schemas.ts
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Applies to src/**/*.ts : Maintain strict TypeScript with no `any`; use `unknown` + schema validation for external data

Applied to files:

  • src/schemas/preset-schemas.ts
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Recursively load all `.md` files from within each rule pack; multiple packs can be used simultaneously

Applied to files:

  • src/boundaries/rule-pack-loader.ts
  • src/cli/commands.ts
  • tests/rule-pack-e2e.test.ts
  • src/prompts/prompt-loader.ts
  • src/cli/validate-command.ts
  • tests/rule-pack-loader.test.ts
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Organize rules into subdirectories (packs) within RulesPath; use company names (e.g., Acme, TechCorp, Stripe) to indicate which style guide the rules implement

Applied to files:

  • src/boundaries/rule-pack-loader.ts
  • src/cli/commands.ts
  • tests/rule-pack-e2e.test.ts
  • src/cli/validate-command.ts
  • src/cli/orchestrator.ts
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Applies to src/output/**/*.ts : IDs should be shown as `PromptId.CriterionId` in output

Applied to files:

  • tests/prompt-loader-validation.test.ts
  • tests/target.test.ts
  • tests/evaluator.test.ts
  • src/cli/types.ts
  • src/cli/orchestrator.ts
  • tests/validator.test.ts
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Applies to tests/**/*.test.ts : Write tests using Vitest framework with focus on config parsing, file discovery, schema/structured output, and locator

Applied to files:

  • tests/prompt-loader-validation.test.ts
  • tests/target.test.ts
  • tests/rule-pack-e2e.test.ts
  • tests/preset-loader.test.ts
  • tests/rule-pack-loader.test.ts
  • tests/validator.test.ts
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Keep directories cohesive; maintain acyclic dependencies (CLI → prompts/providers/output); optimize for discoverability

Applied to files:

  • tests/target.test.ts
  • tests/evaluator.test.ts
  • src/cli/commands.ts
  • tests/validator.test.ts
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Applies to evals/**/*.md : Evals must include YAML frontmatter; the tool appends evidence instructions automatically

Applied to files:

  • tests/rule-pack-loader.test.ts
🧬 Code graph analysis (12)
src/boundaries/rule-pack-loader.ts (1)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
tests/prompt-loader-validation.test.ts (1)
src/prompts/prompt-loader.ts (1)
  • loadRules (88-129)
tests/target.test.ts (1)
src/prompts/prompt-loader.ts (1)
  • loadRules (88-129)
tests/evaluator.test.ts (1)
src/prompts/prompt-loader.ts (1)
  • loadRules (88-129)
src/cli/commands.ts (3)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
src/boundaries/rule-pack-loader.ts (1)
  • RulePackLoader (12-92)
src/prompts/prompt-loader.ts (1)
  • loadRuleFile (10-86)
tests/rule-pack-e2e.test.ts (2)
src/boundaries/rule-pack-loader.ts (1)
  • RulePackLoader (12-92)
src/boundaries/scan-path-resolver.ts (1)
  • ScanPathResolver (18-72)
src/config/preset-loader.ts (1)
src/schemas/preset-schemas.ts (2)
  • PresetRegistry (12-12)
  • PRESET_REGISTRY_SCHEMA (8-10)
tests/preset-loader.test.ts (1)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
src/prompts/prompt-loader.ts (1)
src/schemas/prompt-schemas.ts (1)
  • PromptFile (64-64)
src/cli/validate-command.ts (3)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
src/boundaries/rule-pack-loader.ts (1)
  • RulePackLoader (12-92)
src/prompts/prompt-loader.ts (1)
  • loadRuleFile (10-86)
tests/rule-pack-loader.test.ts (2)
src/boundaries/rule-pack-loader.ts (1)
  • RulePackLoader (12-92)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
tests/validator.test.ts (1)
src/prompts/prompt-loader.ts (1)
  • loadRules (88-129)
🪛 LanguageTool
presets/VectorLint/ai-pattern.md

[style] ~32-~32: This phrase is redundant. Consider using “outside”.
Context: ...ze. #### Important - Do not flag words outside of this list. ### 2. Negation Constrast F...

(OUTSIDE_OF)


[grammar] ~34-~34: Ensure spelling is correct
Context: ... outside of this list. ### 2. Negation Constrast Flag sentence that use artificial negati...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

presets/VectorLint/repetition.md

[grammar] ~26-~26: Ensure spelling is correct
Context: ...B'" which buries the section names. ## CRITERIA ### UNNECESSARY SECTION REPETITION Flag eve...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[style] ~37-~37: The words ‘Explanations’ and ‘explained’ are quite similar. Consider replacing ‘explained’ with a different word.
Context: ...dundant Explanations**: A concept fully explained in one section is explained again later...

(VERB_NOUN_SENT_LEVEL_REP)


[style] ~39-~39: ‘exact same’ might be wordy. Consider a shorter alternative.
Context: ...ctions use examples that illustrate the exact same point without revealing new dimensions ...

(EN_WORDINESS_PREMIUM_EXACT_SAME)

tests/fixtures/ai-pattern/negation-pattern.md

[grammar] ~82-~82: Ensure spelling is correct
Context: ... mind investing time in configuration. Greptile 4. Greptile (Best for Deep Context and RAG)...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[style] ~85-~85: Consider a different adjective to strengthen your wording.
Context: ...ation. Greptile 4. Greptile (Best for Deep Context and RAG) Category: Specialized ...

(DEEP_PROFOUND)


[style] ~90-~90: Consider a different adjective to strengthen your wording.
Context: ... uses this context to provide unusually deep analysis. When reviewing a PR, Greptile...

(DEEP_PROFOUND)


[style] ~155-~155: To elevate your writing, try using more descriptive phrasing here.
Context: ...repo querying: Choose Greptile. If your biggest challenge is understanding how changes ...

(BIGGEST_CHALLENGE)

🪛 markdownlint-cli2 (0.18.1)
presets/VectorLint/ai-pattern.md

81-81: Heading style
Expected: atx; Actual: setext

(MD003, heading-style)

tests/fixtures/ai-pattern/negation-pattern.md

145-145: Hard tabs
Column: 5

(MD010, no-hard-tabs)


145-145: Hard tabs
Column: 19

(MD010, no-hard-tabs)


145-145: Hard tabs
Column: 37

(MD010, no-hard-tabs)


145-145: Hard tabs
Column: 51

(MD010, no-hard-tabs)


146-146: Hard tabs
Column: 15

(MD010, no-hard-tabs)


146-146: Hard tabs
Column: 49

(MD010, no-hard-tabs)


146-146: Hard tabs
Column: 76

(MD010, no-hard-tabs)


146-146: Hard tabs
Column: 91

(MD010, no-hard-tabs)


147-147: Hard tabs
Column: 15

(MD010, no-hard-tabs)


147-147: Hard tabs
Column: 49

(MD010, no-hard-tabs)


147-147: Hard tabs
Column: 65

(MD010, no-hard-tabs)


147-147: Hard tabs
Column: 91

(MD010, no-hard-tabs)


148-148: Hard tabs
Column: 11

(MD010, no-hard-tabs)


148-148: Hard tabs
Column: 32

(MD010, no-hard-tabs)


148-148: Hard tabs
Column: 50

(MD010, no-hard-tabs)


148-148: Hard tabs
Column: 68

(MD010, no-hard-tabs)


149-149: Hard tabs
Column: 9

(MD010, no-hard-tabs)


149-149: Hard tabs
Column: 38

(MD010, no-hard-tabs)


149-149: Hard tabs
Column: 60

(MD010, no-hard-tabs)


149-149: Hard tabs
Column: 75

(MD010, no-hard-tabs)


150-150: Hard tabs
Column: 9

(MD010, no-hard-tabs)


150-150: Hard tabs
Column: 38

(MD010, no-hard-tabs)


150-150: Hard tabs
Column: 65

(MD010, no-hard-tabs)


150-150: Hard tabs
Column: 80

(MD010, no-hard-tabs)


151-151: Hard tabs
Column: 7

(MD010, no-hard-tabs)


151-151: Hard tabs
Column: 30

(MD010, no-hard-tabs)


151-151: Hard tabs
Column: 64

(MD010, no-hard-tabs)


151-151: Hard tabs
Column: 94

(MD010, no-hard-tabs)

🔇 Additional comments (27)
presets/VectorLint/pseudo-advice.md (1)

1-158: Well-structured preset with comprehensive guidance.

The Pseudo-Advice Detector preset is thorough and well-organized:

  • Clear metadata with appropriate semi-objective type and strictness
  • Systematic instruction workflow for evaluators
  • Concrete examples with AVOID/BETTER patterns
  • Explicit "When Acceptable" guidance reduces false positives
tests/fixtures/ai-pattern/buzzword.md (1)

1-7: Appropriate test fixture for AI-pattern detection.

This fixture effectively contains marketing buzzwords and AI-pattern language ("leverages", "revolutionize", "harness", "game-changers", "delve into deeper areas") that should trigger the VectorLint AI-pattern preset for testing purposes.

tests/validator.test.ts (1)

5-5: API migration to loadRules looks correct.

The update from loadPrompts to loadRules is consistently applied across all test cases in this file, aligning with the refactored API surface.

Also applies to: 31-31, 53-53

tests/fixtures/ai-pattern/test.md (1)

1-1: LGTM: Compact test fixture for AI-pattern detection.

This fixture provides a condensed example of marketing language with buzzwords for testing the AI-pattern preset.

src/cli/types.ts (1)

94-101: All call sites correctly provide the packName field.

Verification confirms that ProcessCriterionParams has been properly updated with packName as a required field. The single call site at src/cli/orchestrator.ts:577 correctly provides all required fields including packName: promptFile.pack, which is then used in buildRuleName() at line 236. No additional changes needed.

src/schemas/prompt-schemas.ts (1)

57-57: No action needed. The pack field is correctly required because it is always assigned by the loadRuleFile() function based on the packName parameter, regardless of rule file content. The field is never sourced from external data or constructed conditionally—every code path that creates a PromptFile object explicitly provides a pack value. Making the field required in the schema accurately reflects the actual runtime guarantee.

presets/meta.json (1)

1-8: LGTM!

The preset registry structure is valid and aligns with the PRESET_REGISTRY_SCHEMA. The relative path will be correctly resolved by PresetLoader.getPresetPath().

tests/target.test.ts (1)

5-5: LGTM!

The API rename from loadPrompts to loadRules is correctly applied. The test logic remains unchanged and continues to validate target gating behavior appropriately.

Also applies to: 13-13

tests/prompt-loader-validation.test.ts (1)

2-2: LGTM!

All loadPromptsloadRules renames are consistently applied. The test coverage for prompt validation scenarios remains intact.

Also applies to: 37-37, 52-52, 69-69, 82-82, 95-95, 111-111, 127-127

tests/evaluator.test.ts (1)

5-5: LGTM!

The API rename is correctly applied, and the evaluation aggregation test continues to verify that all prompts are processed for all files.

Also applies to: 43-43

tests/preset-loader.test.ts (1)

1-95: LGTM!

Comprehensive test coverage for PresetLoader including:

  • Empty/missing registry handling
  • Parsing and listing presets from meta.json
  • Relative and absolute path resolution
  • Error handling for invalid JSON and schema violations

The tests follow Vitest conventions and cover the key behaviors of the preset loading system.

src/schemas/preset-schemas.ts (1)

3-6: LGTM!

The schema correctly defines the preset entry structure with Zod validation, aligning with boundary validation guidelines. The optional description field provides flexibility while keeping path required.

src/prompts/prompt-loader.ts (2)

10-10: LGTM!

The signature change from optional to required packName parameter enforces explicit pack identification. The returned PromptFile now includes the pack field, enabling proper rule attribution in the preset/user rule shadowing system.

Also applies to: 72-81


88-129: LGTM!

The renamed loadRules function maintains backward compatibility by using 'Default' as the pack name for simple directory-based loading. Error handling follows the strict TypeScript pattern with unknown type and proper instanceof checks.

presets/VectorLint/directness.md (1)

1-11: Well-structured frontmatter and criteria definition.

The YAML frontmatter is properly formatted with all required fields (specVersion, type, severity, strictness, name, id, criteria). The criterion OpeningDirectness is well-defined with both name and id fields.

tests/rule-pack-loader.test.ts (2)

17-21: Mock PresetLoader implementation looks good.

The mock correctly implements all three methods of the PresetLoader interface (getAvailablePresets, getPresetPath, loadRegistry). The type assertion to PresetLoader is appropriate for testing purposes.


76-92: Excellent test for shadowing behavior.

This test correctly verifies the critical shadowing logic where user packs override presets with the same name. The assertions properly check that isPreset is false and the path points to the user directory.

src/config/preset-loader.ts (1)

16-32: Lazy-loading registry with proper validation.

The implementation correctly:

  • Uses early return for memoization
  • Handles missing meta.json gracefully with an empty registry
  • Uses unknown type for parsed JSON and validates with Zod schema
  • Wraps errors with context message

This aligns well with the coding guidelines for boundary validation.

src/cli/commands.ts (1)

21-22: Correct ESM __dirname derivation.

The pattern using fileURLToPath(import.meta.url) and dirname() is the standard approach for obtaining __dirname in ESM modules. This is necessary for the presets path resolution.

src/boundaries/rule-pack-loader.ts (1)

25-61: Clean implementation of pack shadowing with Map.

The shadowing logic is well-implemented:

  1. Presets loaded first (lowest priority)
  2. User packs overwrite matching keys (highest priority)
  3. Graceful error handling when user path doesn't exist

This matches the PR objective of allowing user rules to override presets.

src/cli/validate-command.ts (1)

15-16: Consistent ESM __dirname pattern.

Same correct pattern as in commands.ts for deriving __dirname in ESM modules.

tests/rule-pack-e2e.test.ts (2)

74-76: Updated test correctly uses new RulePackLoader API.

The test properly instantiates RulePackLoader without a PresetLoader (testing user-only packs scenario) and extracts pack names from the returned objects for compatibility with ScanPathResolver.


83-88: Good verification of pack object structure.

The test correctly verifies both the pack object retrieval (packs.find(p => p.name === 'VectorLint')) and the isPreset flag. Using pack.path for findRuleFiles aligns with the new API contract.

src/cli/orchestrator.ts (4)

30-45: LGTM!

Clean helper function that constructs hierarchical rule names following the documented pattern. The conditional inclusion of criterionId is handled correctly, and the implementation aligns well with the new pack-scoped naming scheme that extends the existing PromptId.CriterionId pattern with pack context. Based on learnings, the original pattern was PromptId.CriterionId; this now correctly prefixes with PackName.


576-588: LGTM!

The subjective path correctly propagates packName from promptFile.pack to extractAndReportCriterion, maintaining consistency with the semi-objective path and enabling hierarchical rule naming for all criteria.


229-236: LGTM!

The packName is correctly destructured and used to construct the hierarchical rule name via buildRuleName. This properly integrates the pack context into criterion-level reporting.


487-561: LGTM!

The refactored semi-objective path correctly:

  1. Groups violations by criterionName for per-criterion reporting
  2. Looks up criterionId from meta.criteria with proper optional chaining
  3. Constructs hierarchical rule names via buildRuleName
  4. Accumulates errors/warnings per group and returns the totals

The non-null assertion on line 494 is safe since the entry is created immediately before access. The PromptFile interface includes the pack property as a string, making its use in buildRuleName valid.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
package.json (1)

21-25: Critical: Add "presets" directory to the files array.

The PR introduces bundled rule packs in the presets/ directory, but this directory is not included in the files array. This means the presets will not be published with the npm package, breaking the core feature of this PR.

🔎 Proposed fix
 "files": [
   "dist",
+  "presets",
   "README.md",
   "LICENSE.md"
 ],
🧹 Nitpick comments (4)
src/cli/commands.ts (1)

23-24: ESLint naming convention violation for ESM compatibility variables.

The __filename and __dirname variables violate the project's UPPER_CASE naming rule for module-level constants. This is a common ESM compatibility pattern, but the linter flags it. Consider either:

  1. Suppressing the lint rule for these specific lines, or
  2. Renaming to follow the convention (e.g., CURRENT_FILE, CURRENT_DIR)
Option 1: Suppress lint rule (recommended for clarity)
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = dirname(__filename);
+// eslint-disable-next-line @typescript-eslint/naming-convention
+const __filename = fileURLToPath(import.meta.url);
+// eslint-disable-next-line @typescript-eslint/naming-convention
+const __dirname = dirname(__filename);
package.json (1)

39-39: Reconsider the "vectorlint.ini.example" keyword.

This appears to be a filename rather than a searchable keyword. Keywords should be generic terms that help users discover the package (e.g., "configuration", "ini-file"). Consider whether this specific filename adds value for package discovery.

tests/rule-pack-e2e.test.ts (2)

10-10: Update the test suite name to reflect new terminology.

The suite name still references "Eval Pack System" but the codebase has migrated to "Rule Pack" terminology for consistency.

🔎 Proposed fix
-describe('Eval Pack System End-to-End', () => {
+describe('Rule Pack System End-to-End', () => {

75-89: Consider testing preset integration in the end-to-end workflow.

The RulePackLoader is instantiated without a PresetLoader, so these tests only exercise user pack loading. Since the PR's core feature is preset support with shadowing behavior, consider adding a test case that:

  • Instantiates RulePackLoader with a PresetLoader
  • Verifies preset packs are discovered alongside user packs
  • Validates that user packs shadow presets with the same name

This would provide end-to-end coverage of the preset workflow described in the PR objectives.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 71c5a28 and b54e657.

📒 Files selected for processing (5)
  • package.json
  • src/cli/commands.ts
  • src/cli/orchestrator.ts
  • src/cli/types.ts
  • tests/rule-pack-e2e.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/cli/types.ts
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.ts: Use TypeScript ESM with explicit imports and narrow types
Use 2-space indentation; avoid trailing whitespace
Maintain strict TypeScript with no any; use unknown + schema validation for external data
Use custom error types with proper inheritance; catch blocks use unknown type

Files:

  • src/cli/commands.ts
  • src/cli/orchestrator.ts
tests/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.test.ts: Write tests using Vitest framework with focus on config parsing, file discovery, schema/structured output, and locator
Use dependency injection in tests: mock providers; do not hit network in unit tests

Files:

  • tests/rule-pack-e2e.test.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Organize rules into subdirectories (packs) within RulesPath; use company names (e.g., Acme, TechCorp, Stripe) to indicate which style guide the rules implement
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Recursively load all `.md` files from within each rule pack; multiple packs can be used simultaneously
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Applies to .vectorlint.ini : Use `[glob/pattern]` sections in `.vectorlint.ini` to specify which packs run on which files
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Recursively load all `.md` files from within each rule pack; multiple packs can be used simultaneously

Applied to files:

  • src/cli/commands.ts
  • tests/rule-pack-e2e.test.ts
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Keep directories cohesive; maintain acyclic dependencies (CLI → prompts/providers/output); optimize for discoverability

Applied to files:

  • src/cli/commands.ts
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Organize rules into subdirectories (packs) within RulesPath; use company names (e.g., Acme, TechCorp, Stripe) to indicate which style guide the rules implement

Applied to files:

  • src/cli/commands.ts
  • tests/rule-pack-e2e.test.ts
  • src/cli/orchestrator.ts
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Applies to .vectorlint.ini : Use `[glob/pattern]` sections in `.vectorlint.ini` to specify which packs run on which files

Applied to files:

  • package.json
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Applies to tests/**/*.test.ts : Write tests using Vitest framework with focus on config parsing, file discovery, schema/structured output, and locator

Applied to files:

  • tests/rule-pack-e2e.test.ts
📚 Learning: 2025-12-28T19:43:51.176Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.176Z
Learning: Applies to src/output/**/*.ts : IDs should be shown as `PromptId.CriterionId` in output

Applied to files:

  • src/cli/orchestrator.ts
🧬 Code graph analysis (2)
src/cli/commands.ts (3)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
src/boundaries/rule-pack-loader.ts (1)
  • RulePackLoader (12-92)
src/prompts/prompt-loader.ts (1)
  • loadRuleFile (10-86)
tests/rule-pack-e2e.test.ts (2)
src/boundaries/rule-pack-loader.ts (1)
  • RulePackLoader (12-92)
src/boundaries/scan-path-resolver.ts (1)
  • ScanPathResolver (18-72)
🪛 GitHub Check: ESLint
src/cli/commands.ts

[failure] 24-24:
Variable name __dirname must match one of the following formats: UPPER_CASE


[failure] 23-23:
Variable name __filename must match one of the following formats: UPPER_CASE

🔇 Additional comments (6)
src/cli/commands.ts (1)

116-136: LGTM! Clean integration of preset-aware rule loading.

The iteration logic correctly:

  • Uses listAllPacks to get merged preset + user packs
  • Iterates using pack.path and pack.name from the resolved pack objects
  • Passes pack.name to loadRuleFile for hierarchical naming

This aligns with the learnings about organizing rules into subdirectories (packs) and recursively loading .md files from each pack.

src/cli/orchestrator.ts (5)

36-51: LGTM! Clean hierarchical rule name construction.

The buildRuleName helper correctly implements the PackName.RuleId.CriterionId pattern documented in the learnings. The conditional inclusion of criterionId handles both criterion-specific and rule-level naming appropriately.


570-614: Well-structured grouped violation reporting.

The grouping logic correctly:

  • Groups violations by criterionName (including undefined for ungrouped)
  • Looks up criterionId from meta criteria for proper naming
  • Uses buildRuleName to construct hierarchical rule names per group
  • Accumulates error/warning counts per group

One minor observation: the non-null assertion on line 577 (violationsByCriterion.get(criterionName)!) is safe given the preceding has() check, but a single Map.get with fallback could be marginally cleaner.

Optional: Slightly cleaner pattern without non-null assertion
     for (const v of result.violations) {
       const criterionName = v.criterionName;
-      if (!violationsByCriterion.has(criterionName)) {
-        violationsByCriterion.set(criterionName, []);
-      }
-      violationsByCriterion.get(criterionName)!.push(v);
+      const group = violationsByCriterion.get(criterionName) ?? [];
+      group.push(v);
+      violationsByCriterion.set(criterionName, group);
     }

265-287: Correct integration of packName for criterion-level rule naming.

The packName is properly destructured from params and used in buildRuleName to produce the hierarchical PackName.RuleId.CriterionId format. This aligns with the output format requirements from the learnings (PromptId.CriterionId → now extended to include pack scope).


668-675: Correctly threads packName through subjective evaluation path.

The promptFile.pack is passed as packName to extractAndReportCriterion, ensuring consistent hierarchical naming across both semi-objective and subjective result processing paths.


633-645: Unable to verify the review comment due to repository access failure. Please provide the contents of src/cli/orchestrator.ts or grant access to the repository to complete the verification.

- Add presets/ to package.json files array for npm publishing
- Fix presetsDir path in commands.ts (../../presets → ../presets)
- Fix presetsDir path in validate-command.ts (../../presets → ../presets)
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/cli/validate-command.ts (1)

63-65: Incorrect presets directory path resolution.

Same issue as in src/cli/commands.ts line 112: the path ../presets resolves incorrectly for npm-installed packages. When compiled to dist/cli/validate-command.js, this will resolve to <packageRoot>/dist/presets instead of <packageRoot>/presets.

🔎 Fix: Use correct relative path
-        const presetsDir = path.resolve(__dirname, '../presets');
+        const presetsDir = path.resolve(__dirname, '../../presets');
🧹 Nitpick comments (2)
src/cli/commands.ts (2)

23-24: ESLint naming warnings are false positives.

The ESLint warnings about __filename and __dirname not matching UPPER_CASE format can be safely ignored. These are conventional Node.js identifiers, and using different names would harm readability. Consider adding an ESLint suppression comment if the warnings are disruptive.

💡 Optional: suppress ESLint warnings
+// eslint-disable-next-line @typescript-eslint/naming-convention
 const __filename = fileURLToPath(import.meta.url);
+// eslint-disable-next-line @typescript-eslint/naming-convention
 const __dirname = dirname(__filename);

112-136: Consider extracting preset loader initialization.

The preset loader initialization pattern (lines 112-114) is duplicated in src/cli/validate-command.ts (lines 63-65). Consider extracting this into a shared helper function to improve maintainability.

💡 Example helper function

Create a new file src/cli/preset-helpers.ts:

import * as path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import { PresetLoader } from '../config/preset-loader';
import { RulePackLoader } from '../boundaries/rule-pack-loader';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

export function createRulePackLoader(): RulePackLoader {
  const presetsDir = path.resolve(__dirname, '../../presets');
  const presetLoader = new PresetLoader(presetsDir);
  return new RulePackLoader(presetLoader);
}

Then use it in both command files:

-        const presetsDir = path.resolve(__dirname, '../presets');
-        const presetLoader = new PresetLoader(presetsDir);
-        const loader = new RulePackLoader(presetLoader);
+        const loader = createRulePackLoader();
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b54e657 and c5a2a91.

📒 Files selected for processing (3)
  • package.json
  • src/cli/commands.ts
  • src/cli/validate-command.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.ts: Use TypeScript ESM with explicit imports and narrow types
Use 2-space indentation; avoid trailing whitespace
Maintain strict TypeScript with no any; use unknown + schema validation for external data
Use custom error types with proper inheritance; catch blocks use unknown type

Files:

  • src/cli/commands.ts
  • src/cli/validate-command.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Organize rules into subdirectories (packs) within RulesPath; use company names (e.g., Acme, TechCorp, Stripe) to indicate which style guide the rules implement
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Recursively load all `.md` files from within each rule pack; multiple packs can be used simultaneously
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Applies to .vectorlint.ini : Use `[glob/pattern]` sections in `.vectorlint.ini` to specify which packs run on which files
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Recursively load all `.md` files from within each rule pack; multiple packs can be used simultaneously

Applied to files:

  • src/cli/commands.ts
  • src/cli/validate-command.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Keep directories cohesive; maintain acyclic dependencies (CLI → prompts/providers/output); optimize for discoverability

Applied to files:

  • src/cli/commands.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Organize rules into subdirectories (packs) within RulesPath; use company names (e.g., Acme, TechCorp, Stripe) to indicate which style guide the rules implement

Applied to files:

  • src/cli/commands.ts
  • src/cli/validate-command.ts
🧬 Code graph analysis (2)
src/cli/commands.ts (3)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
src/boundaries/rule-pack-loader.ts (1)
  • RulePackLoader (12-92)
src/prompts/prompt-loader.ts (1)
  • loadRuleFile (10-86)
src/cli/validate-command.ts (3)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
src/boundaries/rule-pack-loader.ts (1)
  • RulePackLoader (12-92)
src/prompts/prompt-loader.ts (1)
  • loadRuleFile (10-86)
🪛 GitHub Check: ESLint
src/cli/commands.ts

[failure] 24-24:
Variable name __dirname must match one of the following formats: UPPER_CASE


[failure] 23-23:
Variable name __filename must match one of the following formats: UPPER_CASE

src/cli/validate-command.ts

[failure] 16-16:
Variable name __dirname must match one of the following formats: UPPER_CASE


[failure] 15-15:
Variable name __filename must match one of the following formats: UPPER_CASE

🔇 Additional comments (4)
src/cli/commands.ts (2)

4-5: LGTM! Proper ESM compatibility imports.

These imports enable __dirname resolution in ESM modules, which is necessary for locating the presets directory relative to the compiled module location.


116-136: LGTM! Proper pack loading with preset shadowing support.

The refactored pack loading correctly uses listAllPacks() and threads pack.name through to loadRuleFile(), enabling the hierarchical rule naming scheme (PackName.RuleId.CriterionId) mentioned in the PR objectives.

src/cli/validate-command.ts (2)

10-11: ESM compatibility correctly implemented; ESLint warnings are false positives.

The ESM __dirname resolution is correctly implemented. The ESLint naming convention warnings can be safely ignored or suppressed as these are conventional Node.js identifiers.

Also applies to: 15-16


67-83: LGTM! Consistent pack loading implementation.

The validate command now correctly uses the refactored RulePackLoader with preset support, maintaining consistency with the main command implementation.

- Make rulesPath optional in config schema (presets can provide rules)
- Remove duplicate validation from config-loader.ts (schema is now single source of truth)
- Update commands.ts and validate-command.ts to handle optional rulesPath
- Update file-resolver.ts to skip rules exclusion when rulesPath is undefined
- Update rule-pack-loader.ts to accept undefined userRulesPath
- Rename vectorlint.ini.example to .vectorlint.ini.example and update with optional RulesPath instructions
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
src/cli/commands.ts (1)

113-113: Fix incorrect presets directory path resolution.

The path resolution '../presets' is incorrect for compiled code. When this file compiles to dist/cli/commands.js, __dirname becomes <packageRoot>/dist/cli, so path.resolve(__dirname, '../presets') resolves to <packageRoot>/dist/presets, which doesn't exist. The presets directory is at <packageRoot>/presets.

This issue was flagged in a previous review but appears unresolved despite being marked as addressed in commit c5a2a91.

🔎 Fix: Use correct relative path to package root
-        const presetsDir = path.resolve(__dirname, '../presets');
+        const presetsDir = path.resolve(__dirname, '../../presets');

This ensures that from dist/cli/, going up two levels (../../) reaches the package root where presets/ is located.

Run the following script to verify the presets directory location and package.json configuration:

#!/bin/bash
# Verify presets directory structure and packaging configuration

echo "=== Checking presets directory exists at package root ==="
ls -la presets/ 2>/dev/null || echo "ERROR: presets/ directory not found at package root"

echo ""
echo "=== Checking package.json files array includes presets ==="
cat package.json | jq '.files' | grep -q '"presets"' && echo "✓ presets included in package.json" || echo "ERROR: presets not in package.json files array"

echo ""
echo "=== Checking compiled output structure ==="
ls -la dist/cli/commands.js 2>/dev/null && echo "✓ Compiled file exists at dist/cli/commands.js" || echo "Note: Run build first to verify compiled structure"
🧹 Nitpick comments (2)
src/cli/commands.ts (1)

23-24: Consider suppressing ESLint rule for conventional Node.js variables.

The variables __filename and __dirname are conventional names in Node.js/ESM for module metadata. While ESLint expects UPPER_CASE for module-level constants, these specific names are idiomatic and widely recognized.

🔎 Option 1: Suppress the rule inline (recommended)
+// eslint-disable-next-line @typescript-eslint/naming-convention
 const __filename = fileURLToPath(import.meta.url);
+// eslint-disable-next-line @typescript-eslint/naming-convention
 const __dirname = dirname(__filename);
🔎 Option 2: Rename to follow convention (less idiomatic)
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = dirname(__filename);
+const CURRENT_FILENAME = fileURLToPath(import.meta.url);
+const CURRENT_DIRNAME = dirname(CURRENT_FILENAME);

Then update usage at Line 113.

src/cli/validate-command.ts (1)

15-16: Configure ESLint to allow Node.js ESM convention variables.

__filename and __dirname are the standard names used when replicating CommonJS globals in Node.js ESM modules (per Node.js documentation). The @typescript-eslint/naming-convention rule currently flags these as violations.

Add a filter exception to eslint.config.mjs in the @typescript-eslint/naming-convention rule:

Configuration fix
{
  selector: "variable",
  format: ["camelCase", "UPPER_CASE"],
  filter: {
    regex: "^__(filename|dirname)$",
    match: false
  }
},

Insert this as the first selector in the naming-convention array (before the existing variable rules).

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c5a2a91 and 605bd96.

📒 Files selected for processing (8)
  • .vectorlint.ini.example
  • src/boundaries/config-loader.ts
  • src/boundaries/rule-pack-loader.ts
  • src/cli/commands.ts
  • src/cli/types.ts
  • src/cli/validate-command.ts
  • src/scan/file-resolver.ts
  • src/schemas/config-schemas.ts
✅ Files skipped from review due to trivial changes (1)
  • .vectorlint.ini.example
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.ts: Use TypeScript ESM with explicit imports and narrow types
Use 2-space indentation; avoid trailing whitespace
Maintain strict TypeScript with no any; use unknown + schema validation for external data
Use custom error types with proper inheritance; catch blocks use unknown type

Files:

  • src/cli/types.ts
  • src/boundaries/rule-pack-loader.ts
  • src/cli/commands.ts
  • src/boundaries/config-loader.ts
  • src/scan/file-resolver.ts
  • src/schemas/config-schemas.ts
  • src/cli/validate-command.ts
src/boundaries/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use Zod schemas for boundary validation of all external data (files, CLI, env, APIs) at system boundaries

Files:

  • src/boundaries/rule-pack-loader.ts
  • src/boundaries/config-loader.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Organize rules into subdirectories (packs) within RulesPath; use company names (e.g., Acme, TechCorp, Stripe) to indicate which style guide the rules implement
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Recursively load all `.md` files from within each rule pack; multiple packs can be used simultaneously
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Applies to src/output/**/*.ts : IDs should be shown as `PromptId.CriterionId` in output

Applied to files:

  • src/cli/types.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Recursively load all `.md` files from within each rule pack; multiple packs can be used simultaneously

Applied to files:

  • src/boundaries/rule-pack-loader.ts
  • src/cli/commands.ts
  • src/cli/validate-command.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Organize rules into subdirectories (packs) within RulesPath; use company names (e.g., Acme, TechCorp, Stripe) to indicate which style guide the rules implement

Applied to files:

  • src/boundaries/rule-pack-loader.ts
  • src/cli/commands.ts
  • src/cli/validate-command.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Keep directories cohesive; maintain acyclic dependencies (CLI → prompts/providers/output); optimize for discoverability

Applied to files:

  • src/cli/commands.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Applies to src/boundaries/**/*.ts : Use Zod schemas for boundary validation of all external data (files, CLI, env, APIs) at system boundaries

Applied to files:

  • src/boundaries/config-loader.ts
  • src/schemas/config-schemas.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Applies to src/boundaries/env-parser.ts : All environment variables must be validated via Zod schemas in `src/boundaries/env-parser.ts`

Applied to files:

  • src/schemas/config-schemas.ts
🧬 Code graph analysis (4)
src/boundaries/rule-pack-loader.ts (1)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
src/cli/commands.ts (4)
src/prompts/prompt-loader.ts (2)
  • PromptFile (8-8)
  • loadRuleFile (10-86)
src/schemas/prompt-schemas.ts (1)
  • PromptFile (64-64)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
src/boundaries/rule-pack-loader.ts (1)
  • RulePackLoader (12-95)
src/scan/file-resolver.ts (1)
src/boundaries/file-section-parser.ts (1)
  • FilePatternConfig (2-6)
src/cli/validate-command.ts (3)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
src/boundaries/rule-pack-loader.ts (1)
  • RulePackLoader (12-95)
src/prompts/prompt-loader.ts (1)
  • loadRuleFile (10-86)
🪛 GitHub Actions: Lint
src/boundaries/rule-pack-loader.ts

[error] 49-49: Command 'npm run lint' failed. ESLint: 'e' is defined but never used. (@typescript-eslint/no-unused-vars)

🪛 GitHub Check: ESLint
src/boundaries/rule-pack-loader.ts

[failure] 49-49:
'e' is defined but never used

src/cli/commands.ts

[failure] 24-24:
Variable name __dirname must match one of the following formats: UPPER_CASE


[failure] 23-23:
Variable name __filename must match one of the following formats: UPPER_CASE

src/cli/validate-command.ts

[failure] 16-16:
Variable name __dirname must match one of the following formats: UPPER_CASE


[failure] 15-15:
Variable name __filename must match one of the following formats: UPPER_CASE

🔇 Additional comments (9)
src/boundaries/config-loader.ts (1)

136-158: LGTM! Optional rulesPath implemented correctly.

The implementation correctly handles optional rulesPath by conditionally resolving the path only when provided, otherwise setting it to undefined. The logic properly handles both absolute and relative paths, and the comment clearly documents the optionality.

src/cli/types.ts (2)

23-23: LGTM! Type correctly updated for optional rulesPath.

The type change from string to string | undefined correctly reflects that rulesPath is now optional, aligning with the preset-driven rule loading approach.


90-90: LGTM! packName field supports hierarchical rule naming.

The addition of packName: string enables the orchestrator to construct hierarchical rule names (e.g., packName.ruleId.criterionId) for proper rule identification and reporting across preset and user-defined packs.

src/schemas/config-schemas.ts (1)

5-5: LGTM! Schema correctly updated for optional rulesPath.

The Zod schema properly makes rulesPath optional while maintaining validation requirements (min length 1 when present). This aligns with the boundary validation requirements and supports the preset-driven configuration approach.

src/boundaries/rule-pack-loader.ts (2)

6-10: LGTM! ResolvedPack interface is well-defined.

The interface clearly captures pack metadata with name, path, and isPreset flag, supporting the preset shadowing logic and enabling callers to distinguish between preset and user-defined packs.


71-94: LGTM! File discovery logic is robust.

The recursive directory scanning correctly identifies all .md rule files and provides clear error messages when the pack directory is inaccessible.

src/cli/commands.ts (2)

105-109: LGTM! Correctly guards rulesPath existence check.

The conditional check ensures the existence validation only runs when rulesPath is provided, properly supporting the preset-only configuration scenario.


117-145: LGTM! Preset and user pack loading logic is well-structured.

The implementation correctly:

  • Loads both presets and user packs via RulePackLoader
  • Provides helpful warnings when no packs are found
  • Iterates through resolved packs using the pack object structure
  • Loads rules with proper pack name context
  • Handles the case where no rules are found with clear error messages
src/scan/file-resolver.ts (1)

12-64: LGTM! Optional rulesPath handling is well-implemented.

The changes correctly handle the optional rulesPath parameter throughout the function:

  • Conditional exclusion of the rules subtree (lines 21-25)
  • Proper guarding in the filtering logic (lines 60-61)
  • Updated CLI argument handling with cleaner existsSync check instead of try/catch
  • Correct extraction of .pattern from FilePatternConfig objects (line 49)

The removal of the extension check at line 38 is safe because the deduplication step (lines 55-63) filters out disallowed extensions.

- Resolve --evals CLI path to absolute before use (config paths already absolute)
- Remove unreachable duplicate prompts.length === 0 check after try block
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/cli/validate-command.ts (1)

15-16: Inline the directory resolution to fix ESLint violations.

ESLint flags __filename and __dirname for not matching UPPER_CASE convention. Since these are used only once (line 66), inline the computation directly to eliminate the violation and reduce unnecessary variables.

🔎 Proposed refactor to inline directory resolution
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = dirname(__filename);
-
 /*
  * Registers the 'validate' command with Commander.

Then on line 66:

-        const presetsDir = path.resolve(__dirname, '../presets');
+        const presetsDir = path.resolve(dirname(fileURLToPath(import.meta.url)), '../presets');
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 605bd96 and ba67e77.

📒 Files selected for processing (1)
  • src/cli/validate-command.ts
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.ts: Use TypeScript ESM with explicit imports and narrow types
Use 2-space indentation; avoid trailing whitespace
Maintain strict TypeScript with no any; use unknown + schema validation for external data
Use custom error types with proper inheritance; catch blocks use unknown type

Files:

  • src/cli/validate-command.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Recursively load all `.md` files from within each rule pack; multiple packs can be used simultaneously
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Organize rules into subdirectories (packs) within RulesPath; use company names (e.g., Acme, TechCorp, Stripe) to indicate which style guide the rules implement
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Applies to .vectorlint.ini : Use `[glob/pattern]` sections in `.vectorlint.ini` to specify which packs run on which files
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Recursively load all `.md` files from within each rule pack; multiple packs can be used simultaneously

Applied to files:

  • src/cli/validate-command.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Organize rules into subdirectories (packs) within RulesPath; use company names (e.g., Acme, TechCorp, Stripe) to indicate which style guide the rules implement

Applied to files:

  • src/cli/validate-command.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Applies to src/boundaries/env-parser.ts : All environment variables must be validated via Zod schemas in `src/boundaries/env-parser.ts`

Applied to files:

  • src/cli/validate-command.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Keep directories cohesive; maintain acyclic dependencies (CLI → prompts/providers/output); optimize for discoverability

Applied to files:

  • src/cli/validate-command.ts
🧬 Code graph analysis (1)
src/cli/validate-command.ts (3)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
src/boundaries/rule-pack-loader.ts (1)
  • RulePackLoader (12-95)
src/prompts/prompt-loader.ts (1)
  • loadRuleFile (10-86)
🪛 GitHub Check: ESLint
src/cli/validate-command.ts

[failure] 16-16:
Variable name __dirname must match one of the following formats: UPPER_CASE


[failure] 15-15:
Variable name __filename must match one of the following formats: UPPER_CASE

🔇 Additional comments (5)
src/cli/validate-command.ts (5)

4-6: LGTM! Import changes align with the refactoring.

The imports correctly reflect the renamed APIs (RulePackLoader, loadRuleFile) and the new PresetLoader. The ESM utilities (fileURLToPath, dirname) are necessary for computing directory paths in ESM modules.

Also applies to: 10-11


42-45: LGTM! CLI path resolution is now correct.

The code properly resolves CLI-provided paths to absolute paths using path.resolve(process.cwd(), ...), ensuring that pack paths will be absolute when passed to the loader. The comment accurately explains that CLI paths need resolution while config paths are already absolute.


56-60: LGTM! Conditional existence check supports preset-only usage.

The existence check correctly guards with rulesPath && to allow preset-only usage without requiring a local rules directory.


66-86: LGTM! Preset loading and pack iteration correctly implemented.

The preset/rule pack loading follows the correct flow:

  1. Computes presetsDir relative to bundled output (correctly resolves to package root's presets/)
  2. Instantiates PresetLoader and RulePackLoader with proper dependencies
  3. Calls listAllPacks which merges presets and user packs with shadowing semantics
  4. Iterates over resolved packs with absolute paths (guaranteed by prior path.resolve on line 43-44)
  5. Loads rule files using the new loadRuleFile API with pack names

The comment on line 74 is accurate given that rulesPath is resolved to absolute before being passed to listAllPacks.


88-94: No issues found. The error message correctly references "RulesPath" and "RunRules," which are actual configuration field names used in config files (verified in src/boundaries/config-loader.ts, src/cli/init-command.ts, and src/boundaries/file-section-parser.ts).

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba67e77 and 86ed602.

📒 Files selected for processing (7)
  • src/boundaries/rule-pack-loader.ts
  • src/cli/commands.ts
  • src/cli/orchestrator.ts
  • src/cli/validate-command.ts
  • tests/preset-loader.test.ts
  • tests/rule-pack-loader.test.ts
  • tests/target.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/cli/commands.ts
  • src/cli/validate-command.ts
  • tests/preset-loader.test.ts
🧰 Additional context used
📓 Path-based instructions (3)
tests/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.test.ts: Write tests using Vitest framework with focus on config parsing, file discovery, schema/structured output, and locator
Use dependency injection in tests: mock providers; do not hit network in unit tests

Files:

  • tests/target.test.ts
  • tests/rule-pack-loader.test.ts
src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.ts: Use TypeScript ESM with explicit imports and narrow types
Use 2-space indentation; avoid trailing whitespace
Maintain strict TypeScript with no any; use unknown + schema validation for external data
Use custom error types with proper inheritance; catch blocks use unknown type

Files:

  • src/boundaries/rule-pack-loader.ts
  • src/cli/orchestrator.ts
src/boundaries/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use Zod schemas for boundary validation of all external data (files, CLI, env, APIs) at system boundaries

Files:

  • src/boundaries/rule-pack-loader.ts
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Recursively load all `.md` files from within each rule pack; multiple packs can be used simultaneously
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Organize rules into subdirectories (packs) within RulesPath; use company names (e.g., Acme, TechCorp, Stripe) to indicate which style guide the rules implement
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Applies to .vectorlint.ini : Use `[glob/pattern]` sections in `.vectorlint.ini` to specify which packs run on which files
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Applies to src/output/**/*.ts : IDs should be shown as `PromptId.CriterionId` in output

Applied to files:

  • tests/target.test.ts
  • src/cli/orchestrator.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Applies to tests/**/*.test.ts : Write tests using Vitest framework with focus on config parsing, file discovery, schema/structured output, and locator

Applied to files:

  • tests/target.test.ts
  • tests/rule-pack-loader.test.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Recursively load all `.md` files from within each rule pack; multiple packs can be used simultaneously

Applied to files:

  • tests/rule-pack-loader.test.ts
  • src/boundaries/rule-pack-loader.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Applies to evals/**/*.md : Evals must include YAML frontmatter; the tool appends evidence instructions automatically

Applied to files:

  • tests/rule-pack-loader.test.ts
📚 Learning: 2025-12-28T19:43:51.189Z
Learnt from: CR
Repo: TRocket-Labs/vectorlint PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T19:43:51.189Z
Learning: Organize rules into subdirectories (packs) within RulesPath; use company names (e.g., Acme, TechCorp, Stripe) to indicate which style guide the rules implement

Applied to files:

  • src/boundaries/rule-pack-loader.ts
  • src/cli/orchestrator.ts
🧬 Code graph analysis (3)
tests/target.test.ts (1)
src/prompts/prompt-loader.ts (1)
  • loadRules (88-129)
tests/rule-pack-loader.test.ts (2)
src/boundaries/rule-pack-loader.ts (1)
  • RulePackLoader (12-95)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
src/boundaries/rule-pack-loader.ts (1)
src/config/preset-loader.ts (1)
  • PresetLoader (5-52)
🔇 Additional comments (13)
tests/target.test.ts (3)

5-5: LGTM! Function rename aligns with PR refactoring.

The import correctly reflects the renamed function from loadPrompts to loadRules.


13-16: LGTM! Defensive guards improve test robustness.

The added runtime guards ensure tests fail fast with clear error messages if the setup doesn't produce expected results. This pattern makes debugging easier when tests fail.


37-38: LGTM! Consistent application of defensive guards.

The optional chaining and runtime guards for criteria access are consistently applied across all test cases, ensuring uniform error handling throughout the test suite.

Also applies to: 59-60, 85-86, 107-108

src/boundaries/rule-pack-loader.ts (3)

6-10: LGTM! Well-defined interface.

The ResolvedPack interface clearly captures the structure of a resolved rule pack with appropriate fields for name, path, and preset status.


25-64: LGTM! Shadowing logic correctly implements preset override behavior.

The Map-based approach correctly implements the shadowing behavior where user rules override presets with matching names. Presets are loaded first (lines 28-37), then user packs overwrite them (lines 54-60), ensuring user definitions take precedence. The graceful handling of missing user paths (line 49) is appropriate.


71-94: LGTM! Recursive file discovery is correctly implemented.

The recursive scanning logic correctly discovers all .md files within a pack directory, appropriately throws for inaccessible paths, and returns absolute file paths as expected.

tests/rule-pack-loader.test.ts (3)

13-24: LGTM! Proper dependency injection in tests.

The mock PresetLoader setup follows best practices for unit testing with dependency injection, avoiding network calls and external dependencies.


32-103: LGTM! Comprehensive test coverage for listAllPacks.

The test suite thoroughly covers all key scenarios:

  • Basic pack discovery
  • Empty directory handling
  • Preset-only scenarios
  • Shadowing behavior (user packs override presets)
  • Graceful handling of non-existent paths

The shadowing test (lines 78-96) correctly verifies that user packs with matching names override presets, which is the core feature of this PR.


105-184: LGTM! Thorough test coverage for findRuleFiles.

The test cases comprehensively validate:

  • Recursive .md file discovery
  • Non-.md file exclusion
  • Empty directory handling
  • Error handling for non-existent paths
  • Absolute path verification

The defensive guard at lines 179-182 ensures test clarity, consistent with the pattern in other test files.

src/cli/orchestrator.ts (4)

36-51: LGTM!

The buildRuleName function correctly constructs hierarchical rule names in the format PackName.RuleId.CriterionId (or PackName.RuleId when no criterion). The implementation is clear and handles the optional criterion parameter appropriately.


667-667: Consistent threading of packName for hierarchical naming.

The subjective processing path correctly passes promptFile.pack as packName to extractAndReportCriterion, enabling consistent hierarchical rule naming across both subjective and semi-objective evaluation paths.


265-265: No action needed — packName is guaranteed to be defined.

The PromptFile type requires a pack field via Zod schema (pack: z.string()), which is validated when the prompt file is loaded. This guarantees that packName is always a non-empty string when passed to extractAndReportCriterion and buildRuleName. The code is correct as written.

Likely an incorrect or invalid review comment.


569-577: Semi-objective evaluators properly populate criterionName on violations.

Verification confirms that all semi-objective evaluators inherit from BaseEvaluator, which explicitly sets criterionName: item.description on every violation object (src/evaluators/base-evaluator.ts:166). The type definition in SemiObjectiveResult includes this property, and the orchestrator's usage at line 572 is safe due to the !isSubjectiveResult(result) type guard that narrows the result to SemiObjectiveResult.

@oshorefueled oshorefueled merged commit f8dded8 into main Jan 2, 2026
3 checks passed
oshorefueled added a commit that referenced this pull request Mar 2, 2026
* feat: implement bundled rule packs (presets)

- Add presets/ directory with meta.json registry
- Implement PresetLoader and refactor to RulePackLoader
- User rules shadow presets with same name
- Rename Eval/Prompt terminology to Rule for consistency

* Add pack name to rule naming hierarchy

- Make pack field required in PROMPT_FILE_SCHEMA
- Add buildRuleName() helper for consistent rule name construction
- Update rule names to follow PackName.RuleId.CriterionId pattern
- Apply naming to both subjective and semi-objective evaluation paths
- Update loadRuleFile() to require packName parameter
- Use 'Default' pack for legacy loadRules() function in tests

* bug: fix ESM __dirname compatibility in preset loading

- Add fileURLToPath and dirname imports for ESM compatibility
- Define __filename and __dirname using import.meta.url
- Fixes 'ReferenceError: __dirname is not defined' in built output
- Affects commands.ts and validate-command.ts preset loading

* expand VectorLint preset with three new rules targeting common content quality issues

- add pseudo-advice rule that catches vague guidance telling WHAT without explaining HOW
- add repetition rule that identifies redundant sections wasting reader time
- add directness rule that ensures sections immediately answer their header questions
- group violations by criterion in orchestrator for better multi-criterion rule reporting
- add ai-pattern test fixtures for buzzword and negation pattern detection

* fix: correct presets directory path resolution for dist builds

- Add presets/ to package.json files array for npm publishing
- Fix presetsDir path in commands.ts (../../presets → ../presets)
- Fix presetsDir path in validate-command.ts (../../presets → ../presets)

* feat: make RulesPath optional in config

- Make rulesPath optional in config schema (presets can provide rules)
- Remove duplicate validation from config-loader.ts (schema is now single source of truth)
- Update commands.ts and validate-command.ts to handle optional rulesPath
- Update file-resolver.ts to skip rules exclusion when rulesPath is undefined
- Update rule-pack-loader.ts to accept undefined userRulesPath
- Rename vectorlint.ini.example to .vectorlint.ini.example and update with optional RulesPath instructions

* fix: resolve CLI path and remove dead code in validate-command

- Resolve --evals CLI path to absolute before use (config paths already absolute)
- Remove unreachable duplicate prompts.length === 0 check after try block

* chore: fix lint errors
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant