vigiles 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +46 -0
- package/.claude/settings.local.json +8 -0
- package/.claude-plugin/hooks/post-edit.sh +34 -0
- package/.claude-plugin/hooks/pre-edit.sh +40 -0
- package/.claude-plugin/hooks/session-start.sh +38 -0
- package/.claude-plugin/marketplace.json +14 -0
- package/.claude-plugin/plugin.json +47 -0
- package/.github/workflows/ci.yml +81 -0
- package/.prettierignore +1 -0
- package/.vigiles/generated.d.ts +205 -0
- package/CLAUDE.md +95 -0
- package/CLAUDE.md.spec.ts +142 -0
- package/CONTRIBUTING.md +121 -0
- package/LICENSE +21 -0
- package/README.md +377 -0
- package/action.yml +25 -0
- package/dist/action.d.ts +7 -0
- package/dist/action.d.ts.map +1 -0
- package/dist/action.js +180 -0
- package/dist/action.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1267 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.test.d.ts +2 -0
- package/dist/cli.test.d.ts.map +1 -0
- package/dist/cli.test.js +650 -0
- package/dist/cli.test.js.map +1 -0
- package/dist/compile.d.ts +101 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +503 -0
- package/dist/compile.js.map +1 -0
- package/dist/evolve.d.ts +132 -0
- package/dist/evolve.d.ts.map +1 -0
- package/dist/evolve.js +599 -0
- package/dist/evolve.js.map +1 -0
- package/dist/freshness.d.ts +67 -0
- package/dist/freshness.d.ts.map +1 -0
- package/dist/freshness.js +244 -0
- package/dist/freshness.js.map +1 -0
- package/dist/freshness.test.d.ts +2 -0
- package/dist/freshness.test.d.ts.map +1 -0
- package/dist/freshness.test.js +356 -0
- package/dist/freshness.test.js.map +1 -0
- package/dist/generate-types.d.ts +34 -0
- package/dist/generate-types.d.ts.map +1 -0
- package/dist/generate-types.js +381 -0
- package/dist/generate-types.js.map +1 -0
- package/dist/inline.d.ts +58 -0
- package/dist/inline.d.ts.map +1 -0
- package/dist/inline.js +142 -0
- package/dist/inline.js.map +1 -0
- package/dist/inline.test.d.ts +5 -0
- package/dist/inline.test.d.ts.map +1 -0
- package/dist/inline.test.js +152 -0
- package/dist/inline.test.js.map +1 -0
- package/dist/linters.d.ts +38 -0
- package/dist/linters.d.ts.map +1 -0
- package/dist/linters.js +588 -0
- package/dist/linters.js.map +1 -0
- package/dist/proofs.d.ts +272 -0
- package/dist/proofs.d.ts.map +1 -0
- package/dist/proofs.js +622 -0
- package/dist/proofs.js.map +1 -0
- package/dist/proofs.test.d.ts +9 -0
- package/dist/proofs.test.d.ts.map +1 -0
- package/dist/proofs.test.js +952 -0
- package/dist/proofs.test.js.map +1 -0
- package/dist/spec.d.ts +258 -0
- package/dist/spec.d.ts.map +1 -0
- package/dist/spec.js +113 -0
- package/dist/spec.js.map +1 -0
- package/dist/spec.test.d.ts +2 -0
- package/dist/spec.test.d.ts.map +1 -0
- package/dist/spec.test.js +1222 -0
- package/dist/spec.test.js.map +1 -0
- package/dist/types.d.ts +101 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +10 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +286 -0
- package/dist/validate.js.map +1 -0
- package/dist/validate.test.d.ts +2 -0
- package/dist/validate.test.d.ts.map +1 -0
- package/dist/validate.test.js +531 -0
- package/dist/validate.test.js.map +1 -0
- package/docs/agent-setup.md +85 -0
- package/docs/agent-workflows.md +103 -0
- package/docs/comparison.md +71 -0
- package/docs/freshness.md +124 -0
- package/docs/inline-mode.md +119 -0
- package/docs/linter-support.md +166 -0
- package/docs/spec-format.md +194 -0
- package/eslint.config.mjs +79 -0
- package/examples/CLAUDE.md +54 -0
- package/examples/CLAUDE.md.spec.ts +65 -0
- package/examples/SKILL.md +50 -0
- package/examples/SKILL.md.spec.ts +57 -0
- package/fixtures/example-project/CLAUDE.md +11 -0
- package/fixtures/example-project/package.json +9 -0
- package/fixtures/example-project/src/index.ts +3 -0
- package/fixtures/example-project/src/utils.test.ts +2 -0
- package/fixtures/example-project/src/utils.ts +3 -0
- package/logo.png +0 -0
- package/package.json +42 -0
- package/research/adoption-strategy.md +111 -0
- package/research/agent-integration.md +145 -0
- package/research/ai-code-quality.md +197 -0
- package/research/code-search-for-agents.md +313 -0
- package/research/competitive-landscape.md +163 -0
- package/research/doc-freshness.md +516 -0
- package/research/executable-specs.md +368 -0
- package/research/feature-ideas.md +464 -0
- package/research/formal-proofs-for-agents.md +338 -0
- package/research/fp-for-agent-harness.md +150 -0
- package/research/fp-for-deterministic-ai.md +131 -0
- package/research/self-evolving-specs.md +298 -0
- package/schemas/claude-md-strict.yml +18 -0
- package/schemas/claude-md.yml +6 -0
- package/schemas/skill-strict.yml +12 -0
- package/schemas/skill.yml +5 -0
- package/skills/audit-feedback-loop/SKILL.md +76 -0
- package/skills/edit-spec/SKILL.md +131 -0
- package/skills/enforce-rules-format/SKILL.md +71 -0
- package/skills/generate-logo/SKILL.md +102 -0
- package/skills/generate-rule/SKILL.md +90 -0
- package/skills/linter-docs/clippy.md +241 -0
- package/skills/linter-docs/eslint.md +384 -0
- package/skills/linter-docs/pylint.md +288 -0
- package/skills/linter-docs/rubocop.md +277 -0
- package/skills/linter-docs/ruff.md +187 -0
- package/skills/linter-docs/stylelint.md +247 -0
- package/skills/migrate-to-spec/SKILL.md +124 -0
- package/skills/pr-to-lint-rule/SKILL.md +97 -0
- package/skills/strengthen/SKILL.md +168 -0
- package/src/action.ts +214 -0
- package/src/cli.test.ts +914 -0
- package/src/cli.ts +1631 -0
- package/src/compile.ts +691 -0
- package/src/evolve.ts +781 -0
- package/src/freshness.test.ts +449 -0
- package/src/freshness.ts +299 -0
- package/src/generate-types.ts +448 -0
- package/src/inline.test.ts +206 -0
- package/src/inline.ts +164 -0
- package/src/linters.ts +739 -0
- package/src/proofs.test.ts +1314 -0
- package/src/proofs.ts +849 -0
- package/src/spec.test.ts +1471 -0
- package/src/spec.ts +427 -0
- package/src/types.ts +117 -0
- package/src/validate.test.ts +701 -0
- package/src/validate.ts +381 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Agent Setup Guide
|
|
2
|
+
|
|
3
|
+
How to set up vigiles when an AI agent is doing the installation (non-interactive).
|
|
4
|
+
|
|
5
|
+
## What an Agent Can Do
|
|
6
|
+
|
|
7
|
+
| Action | Agent can do it? | How |
|
|
8
|
+
| ------------------------- | ---------------- | ------------------------------------------- |
|
|
9
|
+
| Create spec file | Yes | `npx vigiles init` (non-interactive wizard) |
|
|
10
|
+
| Generate types | Yes | `npx vigiles generate-types` |
|
|
11
|
+
| Compile specs | Yes | `npx vigiles compile` |
|
|
12
|
+
| Add CI step | Yes | Edit `.github/workflows/*.yml` directly |
|
|
13
|
+
| Install hooks | Yes | Write to `.claude/settings.json` directly |
|
|
14
|
+
| Install plugin via skills | **No** | Requires user to run `npx skills add` |
|
|
15
|
+
|
|
16
|
+
The `skills add` command requires user action — an agent can't install plugins for itself. But it CAN write the hook configuration directly to `.claude/settings.json`, which achieves the same result.
|
|
17
|
+
|
|
18
|
+
## Non-Interactive Setup
|
|
19
|
+
|
|
20
|
+
### Step 1: Run the wizard
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx vigiles init
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This works non-interactively. It auto-detects the project, creates a spec, generates types, compiles, and adds a CI step. No prompts.
|
|
27
|
+
|
|
28
|
+
### Step 2: Install hooks directly
|
|
29
|
+
|
|
30
|
+
Instead of `npx skills add zernie/vigiles`, the agent can write the hooks to `.claude/settings.json`:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"hooks": {
|
|
35
|
+
"PreToolUse": [
|
|
36
|
+
{
|
|
37
|
+
"matcher": "Edit|Write",
|
|
38
|
+
"command": "FILE=$(cat | jq -r '.tool_input.file_path // empty') && case \"$FILE\" in *.md) [ -f \"$FILE\" ] && head -1 \"$FILE\" | grep -q 'vigiles:sha256:' && { echo \"BLOCKED: Edit the .spec.ts source instead.\" >&2; exit 2; } ;; esac; exit 0"
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"PostToolUse": [
|
|
42
|
+
{
|
|
43
|
+
"matcher": "Edit|Write",
|
|
44
|
+
"command": "FILE=$(cat | jq -r '.tool_input.file_path // empty') && case \"$(basename \"$FILE\")\" in eslint.config.*|.eslintrc*|package.json|pyproject.toml|Cargo.toml) npx vigiles generate-types 2>&1 || true ;; esac && case \"$FILE\" in *.spec.ts) npx vigiles compile 2>&1 || true ;; esac"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This is equivalent to what the plugin installs, but written directly without the skills system.
|
|
52
|
+
|
|
53
|
+
### Step 3: Edit the spec
|
|
54
|
+
|
|
55
|
+
The agent should read the generated `.spec.ts` file and fill in the project's actual conventions — sections, key files, commands, and rules. Use the `edit-spec` skill instructions as a guide for the spec format.
|
|
56
|
+
|
|
57
|
+
### Step 4: Compile and verify
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npx vigiles compile
|
|
61
|
+
npx vigiles audit
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Recommended Agent Prompt
|
|
65
|
+
|
|
66
|
+
If you want an agent to set up vigiles in a project, use this prompt:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Set up vigiles for this project:
|
|
70
|
+
1. Run `npx vigiles init`
|
|
71
|
+
2. Read the generated .spec.ts file
|
|
72
|
+
3. Fill in the project's actual conventions based on the codebase
|
|
73
|
+
4. Add hooks to .claude/settings.json for auto-compilation
|
|
74
|
+
5. Run `npx vigiles compile` to verify everything works
|
|
75
|
+
6. Commit the .spec.ts, compiled .md, .vigiles/generated.d.ts, and settings changes
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## What the Agent Gets Wrong
|
|
79
|
+
|
|
80
|
+
Common issues when agents set up vigiles:
|
|
81
|
+
|
|
82
|
+
- **Editing CLAUDE.md directly** — the PreToolUse hook prevents this if installed
|
|
83
|
+
- **Using wrong rule names** — `enforce("no-console")` instead of `enforce("eslint/no-console")`. The compiler catches this.
|
|
84
|
+
- **Forgetting to compile** — the PostToolUse hook handles this automatically
|
|
85
|
+
- **Adding headers inside sections** — the compiler catches `#`/`##` headers in section content
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Agent Workflows
|
|
2
|
+
|
|
3
|
+
vigiles compiles typed specs to markdown instruction files. Different AI agents read different files, but the compilation and validation pipeline is the same.
|
|
4
|
+
|
|
5
|
+
## Auto-Detection
|
|
6
|
+
|
|
7
|
+
`vigiles init` scans your project and auto-detects:
|
|
8
|
+
|
|
9
|
+
| Signal | What it means |
|
|
10
|
+
| ------------------------------------------ | ------------------------------------------------- |
|
|
11
|
+
| `CLAUDE.md` exists | Claude Code in use — suggest migration if no spec |
|
|
12
|
+
| `AGENTS.md` exists | Codex / GitHub Copilot in use |
|
|
13
|
+
| `.claude/` directory | Claude Code project config |
|
|
14
|
+
| `.cursorrules` | Cursor in use — suggest rule-porter |
|
|
15
|
+
| `.github/copilot-instructions.md` | GitHub Copilot custom instructions |
|
|
16
|
+
| `.windsurfrules` | Windsurf in use |
|
|
17
|
+
| `rule-porter` / `rulesync` in package.json | Sync tool already installed |
|
|
18
|
+
| Symlinked instruction files | Notes them in output |
|
|
19
|
+
|
|
20
|
+
The wizard creates specs for detected targets, generates types, compiles, and adds a CI step. No `--target` flag needed unless you want to override the auto-detection.
|
|
21
|
+
|
|
22
|
+
## Claude Code
|
|
23
|
+
|
|
24
|
+
**Instruction file:** `CLAUDE.md`
|
|
25
|
+
|
|
26
|
+
**Setup:**
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx vigiles init
|
|
30
|
+
npx skills add zernie/vigiles
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**What the plugin does:**
|
|
34
|
+
|
|
35
|
+
| Hook | Trigger | Action |
|
|
36
|
+
| ----------- | ----------------------------------------------- | ---------------------------------------- |
|
|
37
|
+
| PreToolUse | Agent tries to Edit/Write a compiled `.md` file | Blocks the edit, redirects to `.spec.ts` |
|
|
38
|
+
| PostToolUse | Agent edits a `.spec.ts` file | Auto-runs `vigiles compile` |
|
|
39
|
+
| PostToolUse | Agent edits linter config or `package.json` | Auto-runs `vigiles generate-types` |
|
|
40
|
+
|
|
41
|
+
**Without the plugin**, you must run `vigiles compile` manually after editing specs. CI still catches stale files.
|
|
42
|
+
|
|
43
|
+
## Codex / GitHub Copilot
|
|
44
|
+
|
|
45
|
+
**Instruction file:** `AGENTS.md`
|
|
46
|
+
|
|
47
|
+
**Setup:**
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx vigiles init --target=AGENTS.md
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Codex and GitHub Copilot read `AGENTS.md` directly. There is no plugin or hook system — these agents don't support it. The enforcement path is:
|
|
54
|
+
|
|
55
|
+
1. Edit `AGENTS.md.spec.ts` (the source of truth)
|
|
56
|
+
2. Run `npx vigiles compile` to regenerate `AGENTS.md`
|
|
57
|
+
3. CI verifies freshness: `npx vigiles audit && npx vigiles generate-types --check`
|
|
58
|
+
|
|
59
|
+
If you also use Claude Code, install the plugin (`npx skills add zernie/vigiles`) to get auto-recompilation.
|
|
60
|
+
|
|
61
|
+
## Multi-Agent (Claude + Codex)
|
|
62
|
+
|
|
63
|
+
Use a single spec with multiple targets:
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
export default claude({
|
|
67
|
+
target: ["CLAUDE.md", "AGENTS.md"],
|
|
68
|
+
rules: { ... },
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
One spec, two outputs. Both files are compiled from the same source of truth with the same linter verification.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx vigiles init # for CLAUDE.md (primary)
|
|
76
|
+
npx vigiles init --target=AGENTS.md # adds AGENTS.md target
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Or just set `target: ["CLAUDE.md", "AGENTS.md"]` in your spec directly.
|
|
80
|
+
|
|
81
|
+
## Cursor / Windsurf / Other Formats
|
|
82
|
+
|
|
83
|
+
vigiles compiles to **markdown only** (CLAUDE.md, AGENTS.md). For non-markdown formats (`.cursorrules`, `.github/copilot-instructions.md`, Windsurf), use a sync tool to convert from the compiled markdown:
|
|
84
|
+
|
|
85
|
+
- [rule-porter](https://github.com/nichochar/rule-porter) — bidirectional conversion between agent formats
|
|
86
|
+
- [rulesync](https://github.com/dyoshikawa/rulesync) — unified rule management across 10+ tools
|
|
87
|
+
|
|
88
|
+
vigiles is the source of truth compiler. Sync tools handle the last mile.
|
|
89
|
+
|
|
90
|
+
## CI Pipeline
|
|
91
|
+
|
|
92
|
+
All agents share the same CI step:
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
- name: Verify specs
|
|
96
|
+
run: npx vigiles audit && npx vigiles generate-types --check
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This catches:
|
|
100
|
+
|
|
101
|
+
- Hash mismatches (someone edited the compiled `.md` directly)
|
|
102
|
+
- Missing specs (`require-spec` rule — every `.md` should have a `.spec.ts`)
|
|
103
|
+
- Stale generated types (linter config changed but types weren't regenerated)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# What Changes With vigiles
|
|
2
|
+
|
|
3
|
+
## Claude Code
|
|
4
|
+
|
|
5
|
+
| | Without vigiles | With vigiles |
|
|
6
|
+
| ----------------------------------- | ---------------------------- | -------------------------------------------------------------- |
|
|
7
|
+
| **Instructions** | Hand-written CLAUDE.md | Compiled from `.spec.ts` (build artifact) |
|
|
8
|
+
| **Linter rule references** | Trust-based (nobody checks) | Verified at compile time against real config |
|
|
9
|
+
| **File paths** | Rot silently when renamed | `file()` references checked against filesystem |
|
|
10
|
+
| **Commands** | Stale scripts go unnoticed | `cmd()` references checked against package.json |
|
|
11
|
+
| **Direct edits to CLAUDE.md** | Anyone can, nobody knows | PreToolUse hook blocks edits, redirects to spec |
|
|
12
|
+
| **Linter config changes** | CLAUDE.md drifts out of sync | PostToolUse hook auto-regenerates types |
|
|
13
|
+
| **Spec edits** | N/A | PostToolUse hook auto-compiles to markdown |
|
|
14
|
+
| **guidance → enforce upgrades** | Manual guesswork | `/strengthen` reads per-linter docs, suggests upgrades |
|
|
15
|
+
| **New lint rules from PR feedback** | Copy-paste from review | `/pr-to-lint-rule` generates rule + tests + spec entry |
|
|
16
|
+
| **CI** | Nothing to verify | `vigiles audit` catches hash drift, disabled rules, stale refs |
|
|
17
|
+
|
|
18
|
+
## Codex
|
|
19
|
+
|
|
20
|
+
| | Without vigiles | With vigiles |
|
|
21
|
+
| ----------------------------- | -------------------------------- | ------------------------------------------------------- |
|
|
22
|
+
| **Instructions** | Hand-written AGENTS.md | Compiled from `.spec.ts` |
|
|
23
|
+
| **Linter rule references** | Trust-based | Verified at compile time |
|
|
24
|
+
| **File paths / commands** | Rot silently | Checked at compile time |
|
|
25
|
+
| **Direct edits to AGENTS.md** | Undetected | CI catches hash mismatch |
|
|
26
|
+
| **Hooks / auto-compile** | Not available (no plugin system) | Not available — run `vigiles compile` manually or in CI |
|
|
27
|
+
| **CI** | Nothing to verify | Same `vigiles audit` pipeline as Claude |
|
|
28
|
+
|
|
29
|
+
Codex has no hook or plugin system. The compile-time verification and CI enforcement still work — the difference is there's no auto-recompilation on edit. You run `vigiles compile` before committing, and CI catches drift.
|
|
30
|
+
|
|
31
|
+
## What's Deterministic vs What's Not
|
|
32
|
+
|
|
33
|
+
| Check | Deterministic? | How |
|
|
34
|
+
| -------------------------------- | -------------- | ------------------------------------------------------------------------------------ |
|
|
35
|
+
| Linter rule exists in catalog | Yes | Node API (`builtinRules`) or CLI (`ruff rule`, `rubocop --show-cops`) |
|
|
36
|
+
| Linter rule is enabled in config | Yes | `calculateConfigForFile` (ESLint), `--show-settings` (Ruff), `--show-cops` (RuboCop) |
|
|
37
|
+
| File path exists | Yes | `fs.existsSync` |
|
|
38
|
+
| npm script exists | Yes | Parsed from `package.json` |
|
|
39
|
+
| SHA-256 hash matches | Yes | Recompute and compare |
|
|
40
|
+
| Duplicate rule detection | Yes | Normalized Compression Distance (NCD) with fixed threshold |
|
|
41
|
+
| guidance → enforce suggestion | **No** | Agent reads linter docs, reasons about intent — `/strengthen` skill |
|
|
42
|
+
| PR comment → lint rule | **No** | Agent generates custom rule code — `/pr-to-lint-rule` skill |
|
|
43
|
+
| Spec content authoring | **No** | Agent or human writes the spec — vigiles verifies it |
|
|
44
|
+
|
|
45
|
+
Everything vigiles compiles and audits is deterministic — same input, same output, no LLM in the loop. The non-deterministic parts (authoring specs, suggesting upgrades, writing custom rules) are agent skills that run outside the compilation pipeline.
|
|
46
|
+
|
|
47
|
+
## Flow
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
DETERMINISTIC AGENT-ASSISTED
|
|
51
|
+
┌─────────────────────────┐ ┌──────────────────────────┐
|
|
52
|
+
│ │ │ │
|
|
53
|
+
.spec.ts ──────┤ vigiles compile │ │ /strengthen │
|
|
54
|
+
│ │ ✓ linter rules exist │ │ guidance → enforce │
|
|
55
|
+
│ │ ✓ rules enabled │ │ │
|
|
56
|
+
│ │ ✓ file paths valid │ │ /pr-to-lint-rule │
|
|
57
|
+
│ │ ✓ commands valid │ │ PR comment → rule │
|
|
58
|
+
│ │ → CLAUDE.md + hash │ │ │
|
|
59
|
+
│ └─────────────────────────┘ │ /edit-spec │
|
|
60
|
+
│ │ agent edits .spec.ts │
|
|
61
|
+
│ ┌─────────────────────────┐ └──────────────────────────┘
|
|
62
|
+
└────────▶│ vigiles audit │ │
|
|
63
|
+
│ ✓ hash integrity │ │
|
|
64
|
+
│ ✓ inline rule checks │ ▼
|
|
65
|
+
│ ✓ duplicate detection │ ┌──────────────────────────┐
|
|
66
|
+
│ ✓ coverage gaps │ │ hooks (Claude Code) │
|
|
67
|
+
└─────────────────────────┘ │ auto-compile on edit │
|
|
68
|
+
│ auto-regen types │
|
|
69
|
+
│ block direct md edits │
|
|
70
|
+
└──────────────────────────┘
|
|
71
|
+
```
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Freshness Detection
|
|
2
|
+
|
|
3
|
+
vigiles detects when compiled instruction files are out of date. The `freshness` validation rule catches drift between your specs and the compiled markdown.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
In `.vigilesrc.json`:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"rules": {
|
|
12
|
+
"freshness": "error"
|
|
13
|
+
},
|
|
14
|
+
"freshnessMode": "strict"
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Severity
|
|
19
|
+
|
|
20
|
+
| Value | Behavior |
|
|
21
|
+
| ------------------ | ----------------------------------------- |
|
|
22
|
+
| `"error"` | `vigiles audit` exits non-zero (CI fails) |
|
|
23
|
+
| `"warn"` (default) | Prints warning, exits 0 |
|
|
24
|
+
| `false` | Skip freshness checks entirely |
|
|
25
|
+
|
|
26
|
+
### Mode
|
|
27
|
+
|
|
28
|
+
| Mode | What it checks | Cost | False positives | False negatives |
|
|
29
|
+
| -------------------- | -------------------------------------------------------------- | ------------- | --------------------------------------- | ----------------------------- |
|
|
30
|
+
| `"strict"` (default) | Recompiles in memory, diffs against existing output | 2-5s per spec | Zero | Zero |
|
|
31
|
+
| `"input-hash"` | Compares stored input fingerprint against current file state | <100ms | Possible (whitespace changes in config) | Possible (transitive deps) |
|
|
32
|
+
| `"output-hash"` | Only checks if the `.md` was hand-edited (existing hash check) | <1ms | Zero | Many (misses all input drift) |
|
|
33
|
+
|
|
34
|
+
**Strict mode** is the correct default. It catches every kind of staleness with zero false positives. The cost is running a full recompile in memory on every `vigiles audit`.
|
|
35
|
+
|
|
36
|
+
**Input-hash mode** is faster. Use it when compilation is slow (many specs, heavy linter config loading). It tracks a fingerprint of all input files (spec source, linter configs, lock files, keyFiles, generated types). When any input changes, audit flags the output as stale. To use it, set `freshnessMode: "input-hash"` — `vigiles compile` will embed the input fingerprint in the compiled markdown.
|
|
37
|
+
|
|
38
|
+
**Output-hash mode** is the minimal option. It only detects hand-edits to the compiled markdown (the pre-existing hash check). It won't catch disabled linter rules, deleted files, or spec changes.
|
|
39
|
+
|
|
40
|
+
## What Counts as an Input
|
|
41
|
+
|
|
42
|
+
In input-hash mode, vigiles tracks these files:
|
|
43
|
+
|
|
44
|
+
| Category | Files | Why |
|
|
45
|
+
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
|
|
46
|
+
| Spec source | `CLAUDE.md.spec.ts` | Any spec change should force recompile |
|
|
47
|
+
| Linter configs | `eslint.config.*`, `.eslintrc.*`, `pyproject.toml`, `ruff.toml`, `Cargo.toml`, `clippy.toml`, `.pylintrc`, `.rubocop.yml`, `.stylelintrc.*`, `setup.cfg` | Disabling a rule makes `enforce()` claims stale |
|
|
48
|
+
| Package manifest | `package.json` | Scripts (`cmd()` refs) and deps (linter plugins) |
|
|
49
|
+
| Lock files | `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `bun.lockb`, `Gemfile.lock`, `poetry.lock`, `uv.lock`, `pdm.lock`, `Cargo.lock`, `go.sum`, `composer.lock`, `packages.lock.json`, `Package.resolved`, `mix.lock`, `requirements.txt` | Dependency version changes can add/remove linter rules |
|
|
50
|
+
| Referenced files | Every `file()` path in `keyFiles` | Deletion or rename makes the reference stale |
|
|
51
|
+
| Generated types | `.vigiles/generated.d.ts` | If types are stale, rule references may be invalid |
|
|
52
|
+
| Extra inputs | Configured via `freshnessInputs` | For non-standard files (e.g., monorepo root lock file) |
|
|
53
|
+
|
|
54
|
+
All files are auto-detected by checking existence at `basePath`. Lock files and linter configs cover 6+ ecosystems (Node.js, Ruby, Python, Rust, Go, PHP, .NET, Swift, Elixir).
|
|
55
|
+
|
|
56
|
+
## Extra Inputs
|
|
57
|
+
|
|
58
|
+
For monorepos or non-standard layouts, add extra files to track:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"freshnessMode": "input-hash",
|
|
63
|
+
"freshnessInputs": ["../../yarn.lock", "shared/eslint-config/index.js"]
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## How the Input Hash Works
|
|
68
|
+
|
|
69
|
+
At compile time (`vigiles compile`), when `freshnessMode` is `"input-hash"`:
|
|
70
|
+
|
|
71
|
+
1. Discover all input files (spec, configs, lock files, keyFiles, etc.)
|
|
72
|
+
2. Compute SHA-256 of each file's contents (missing files hash to `MISSING:<path>`)
|
|
73
|
+
3. Combine all file hashes into a single fingerprint (SHA-256 of sorted hashes)
|
|
74
|
+
4. Embed the fingerprint in the compiled markdown:
|
|
75
|
+
|
|
76
|
+
```html
|
|
77
|
+
<!-- vigiles:sha256:a1b2c3d4e5f6g7h8 compiled from CLAUDE.md.spec.ts -->
|
|
78
|
+
<!-- vigiles:inputs:f9e8d7c6b5a49382 -->
|
|
79
|
+
|
|
80
|
+
# CLAUDE.md ...
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
At audit time (`vigiles audit`):
|
|
84
|
+
|
|
85
|
+
1. Extract the stored input fingerprint
|
|
86
|
+
2. Recompute the fingerprint from current file state
|
|
87
|
+
3. If they differ → stale
|
|
88
|
+
|
|
89
|
+
## Audit Output
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
Freshness check:
|
|
93
|
+
|
|
94
|
+
✓ CLAUDE.md — fresh (strict)
|
|
95
|
+
✗ AGENTS.md — Output would differ if recompiled — run `vigiles compile`
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
With `--summary`:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
vigiles: 1 stale (run vigiles compile)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Lock File Detection
|
|
105
|
+
|
|
106
|
+
vigiles auto-detects lock files for every major ecosystem. No configuration needed — if the file exists, it's tracked.
|
|
107
|
+
|
|
108
|
+
| Lock file | Ecosystem |
|
|
109
|
+
| -------------------- | --------------- |
|
|
110
|
+
| `package-lock.json` | Node.js (npm) |
|
|
111
|
+
| `yarn.lock` | Node.js (Yarn) |
|
|
112
|
+
| `pnpm-lock.yaml` | Node.js (pnpm) |
|
|
113
|
+
| `bun.lockb` | Node.js (Bun) |
|
|
114
|
+
| `Gemfile.lock` | Ruby (Bundler) |
|
|
115
|
+
| `poetry.lock` | Python (Poetry) |
|
|
116
|
+
| `uv.lock` | Python (uv) |
|
|
117
|
+
| `pdm.lock` | Python (PDM) |
|
|
118
|
+
| `requirements.txt` | Python (pip) |
|
|
119
|
+
| `Cargo.lock` | Rust (Cargo) |
|
|
120
|
+
| `go.sum` | Go |
|
|
121
|
+
| `composer.lock` | PHP (Composer) |
|
|
122
|
+
| `packages.lock.json` | .NET (NuGet) |
|
|
123
|
+
| `Package.resolved` | Swift (SPM) |
|
|
124
|
+
| `mix.lock` | Elixir (Mix) |
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Inline mode
|
|
2
|
+
|
|
3
|
+
Inline mode lets you adopt vigiles **one rule at a time**, without committing
|
|
4
|
+
to a `.spec.ts` compile step. You add `<!-- vigiles:enforce ... -->` HTML
|
|
5
|
+
comments directly to your existing `CLAUDE.md` / `AGENTS.md`, and
|
|
6
|
+
`vigiles audit` verifies them the same way it verifies spec-declared rules:
|
|
7
|
+
linter-backed existence check, closest-match typo suggestions, disabled-rule
|
|
8
|
+
detection, and GitHub Actions annotations.
|
|
9
|
+
|
|
10
|
+
It's the vigiles equivalent of `// eslint-disable-next-line` — minimum
|
|
11
|
+
commitment, maximum incrementalism.
|
|
12
|
+
|
|
13
|
+
## When to use it
|
|
14
|
+
|
|
15
|
+
- You already have a `CLAUDE.md` and don't want to port it to `.spec.ts`
|
|
16
|
+
- You want to experiment with a single rule before committing to the full
|
|
17
|
+
vigiles workflow
|
|
18
|
+
- Your project isn't a TypeScript project at all and the build step feels
|
|
19
|
+
like dead weight
|
|
20
|
+
- Hesitant teammates want to see the verification work before accepting a
|
|
21
|
+
new file type in the repo
|
|
22
|
+
|
|
23
|
+
If you already have a real TypeScript project and you want the strongest
|
|
24
|
+
guarantees (editor-time type safety, programmatic rule composition, the
|
|
25
|
+
`generate-types` moat), use spec mode instead — see the main README.
|
|
26
|
+
|
|
27
|
+
## Format
|
|
28
|
+
|
|
29
|
+
A single HTML comment per rule:
|
|
30
|
+
|
|
31
|
+
```md
|
|
32
|
+
<!-- vigiles:enforce eslint/no-console "Use structured logger for app output" -->
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Three required pieces:
|
|
36
|
+
|
|
37
|
+
1. `vigiles:enforce` — only `enforce` is supported inline. Guidance rules
|
|
38
|
+
are just paragraphs in the surrounding prose, so a `guidance` comment
|
|
39
|
+
would be a tautology.
|
|
40
|
+
2. `<linter>/<rule>` — the same reference format as `enforce()` in spec
|
|
41
|
+
mode. Supports all six linters (ESLint, Stylelint, Ruff, Clippy, Pylint,
|
|
42
|
+
RuboCop) and scoped plugin names (`eslint/@typescript-eslint/...`).
|
|
43
|
+
3. `"<why>"` — a simple double-quoted string. No newlines, no embedded
|
|
44
|
+
quotes. If you need either, move to spec mode.
|
|
45
|
+
|
|
46
|
+
## Example
|
|
47
|
+
|
|
48
|
+
```md
|
|
49
|
+
# My Project
|
|
50
|
+
|
|
51
|
+
<!-- vigiles:enforce eslint/no-console "Route output through logger.ts" -->
|
|
52
|
+
<!-- vigiles:enforce eslint/@typescript-eslint/no-floating-promises "Await or explicitly void" -->
|
|
53
|
+
<!-- vigiles:enforce ruff/F401 "No unused imports" -->
|
|
54
|
+
|
|
55
|
+
## Logging
|
|
56
|
+
|
|
57
|
+
All application output must go through the shared logger module.
|
|
58
|
+
Do not use `console.log` directly in src/.
|
|
59
|
+
|
|
60
|
+
## Async
|
|
61
|
+
|
|
62
|
+
Every promise must be awaited or explicitly voided. The ESLint rule
|
|
63
|
+
enforces this automatically.
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## What audit catches
|
|
67
|
+
|
|
68
|
+
Running `vigiles audit CLAUDE.md` on the above file will:
|
|
69
|
+
|
|
70
|
+
- Verify each `eslint/…`, `ruff/…` reference against your actual linter
|
|
71
|
+
config
|
|
72
|
+
- Emit closest-match suggestions on typos: `"no-consol"` →
|
|
73
|
+
`did you mean "eslint/no-console"?`
|
|
74
|
+
- Emit `::error` annotations when running inside GitHub Actions
|
|
75
|
+
- Exit with code 2 (hard error) on any failed rule, so CI fails fast
|
|
76
|
+
|
|
77
|
+
## What audit does NOT do in inline mode
|
|
78
|
+
|
|
79
|
+
- **No type safety at edit time.** The `.spec.ts` path gets TypeScript
|
|
80
|
+
squiggles in the editor because `StrictLinterRule` is a type union of
|
|
81
|
+
every rule in your linters. Inline mode is strings-in-markdown, so
|
|
82
|
+
typos only surface at `vigiles audit` time. Still catches them before
|
|
83
|
+
CI, just not in the editor.
|
|
84
|
+
- **No programmatic composition.** You can't reuse a batch of rules from
|
|
85
|
+
a helper. Each comment is its own line.
|
|
86
|
+
- **No rule deduplication via NCD.** Duplicate-rule detection runs on
|
|
87
|
+
spec-mode files; inline rules are ungrouped.
|
|
88
|
+
|
|
89
|
+
All of this is fine for the adoption-onramp use case. When you outgrow it,
|
|
90
|
+
port to spec mode.
|
|
91
|
+
|
|
92
|
+
## Mixing inline and spec mode
|
|
93
|
+
|
|
94
|
+
Spec mode wins. If a file has both `CLAUDE.md.spec.ts` and inline
|
|
95
|
+
comments inside `CLAUDE.md`, the spec compiler will overwrite the markdown
|
|
96
|
+
on the next compile, and your inline comments will be gone. Pick one per
|
|
97
|
+
file.
|
|
98
|
+
|
|
99
|
+
## Ignoring `require-spec`
|
|
100
|
+
|
|
101
|
+
The built-in `require-spec` validation rule demands a `.spec.ts` sibling
|
|
102
|
+
for every `CLAUDE.md` / `AGENTS.md`. Inline mode satisfies it — any file
|
|
103
|
+
with at least one `<!-- vigiles:enforce ... -->` comment is treated as
|
|
104
|
+
spec-equivalent, so you do not need to add a `vigiles-disable require-spec`
|
|
105
|
+
comment.
|
|
106
|
+
|
|
107
|
+
## Graduating to spec mode
|
|
108
|
+
|
|
109
|
+
When you've accumulated a dozen or so inline rules and the prose is
|
|
110
|
+
starting to feel crowded, run:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npx vigiles init --target=CLAUDE.md
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
That scaffolds a `CLAUDE.md.spec.ts` next to your existing `CLAUDE.md`.
|
|
117
|
+
Copy the inline enforce rules into the `rules:` block, delete the inline
|
|
118
|
+
comments, and run `vigiles compile`. The markdown output will be rebuilt
|
|
119
|
+
with a `sha256` hash header, and future edits flow through the spec.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Linter Cross-Referencing
|
|
2
|
+
|
|
3
|
+
vigiles verifies that every `enforce()` rule in your spec actually exists and is enabled in your project. This is the core differentiator -- no other tool resolves rules against 6 linter APIs and checks config-enabled status.
|
|
4
|
+
|
|
5
|
+
## How It Works
|
|
6
|
+
|
|
7
|
+
When you write:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
enforce("eslint/no-console", "Use the project logger instead.");
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
vigiles does two things at compile time:
|
|
14
|
+
|
|
15
|
+
1. **Existence check** -- verifies the rule `no-console` exists in ESLint (via `builtinRules` or plugin resolution).
|
|
16
|
+
2. **Config check** -- loads your ESLint flat config, calls `calculateConfigForFile`, and confirms the rule's severity is not `0` / `"off"`.
|
|
17
|
+
|
|
18
|
+
If either check fails, compilation fails with a clear error:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Error: Rule "no-consloe" not found in eslint
|
|
22
|
+
Error: Rule "no-console" is disabled in eslint config
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The reference format is `<linter>/<rule>` -- e.g., `eslint/no-console`, `ruff/F401`, `clippy/needless_return`.
|
|
26
|
+
|
|
27
|
+
## Supported Linters
|
|
28
|
+
|
|
29
|
+
| Linter | Detection | Existence Check | Config Check |
|
|
30
|
+
| --------- | -------------- | --------------------------------------------- | ----------------------------------------------------- |
|
|
31
|
+
| ESLint | `node_modules` | Node API (`builtinRules` + plugin resolution) | Loads flat config, checks severity > 0 |
|
|
32
|
+
| Stylelint | `node_modules` | Node API (`rules` export) | Loads config, checks rule value is not `null` |
|
|
33
|
+
| Ruff | `PATH` | CLI (`ruff rule <name>`) | Parses `ruff check --show-settings` for enabled codes |
|
|
34
|
+
| Clippy | `PATH` | CLI (`cargo clippy --explain <name>`) | Parses `Cargo.toml` `[lints.clippy]` section |
|
|
35
|
+
| Pylint | `PATH` | CLI (`pylint --help-msg=<name>`) | Runs `pylint --list-msgs-enabled` |
|
|
36
|
+
| RuboCop | `PATH` | CLI (`rubocop --show-cops <name>`) | Parses `Enabled: true/false` from output |
|
|
37
|
+
|
|
38
|
+
Node-based linters (ESLint, Stylelint) are resolved via `createRequire` from the project's `node_modules`. CLI-based linters (Ruff, Clippy, Pylint, RuboCop) must be available on `PATH`.
|
|
39
|
+
|
|
40
|
+
## ESLint Plugin Support
|
|
41
|
+
|
|
42
|
+
Plugin rules use the format `eslint/<plugin>/<rule>`. vigiles resolves the plugin package and checks its exported `rules` object.
|
|
43
|
+
|
|
44
|
+
**Scoped plugins** (`@scope/plugin`):
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
// Resolves @typescript-eslint/eslint-plugin, checks its "no-explicit-any" rule
|
|
48
|
+
enforce("eslint/@typescript-eslint/no-explicit-any", "Use `unknown` instead.");
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Resolution order for `@scope/name`:
|
|
52
|
+
|
|
53
|
+
1. `@scope/eslint-plugin-name`
|
|
54
|
+
2. `@scope/eslint-plugin`
|
|
55
|
+
|
|
56
|
+
For bare scopes like `@typescript-eslint`, it resolves `@typescript-eslint/eslint-plugin`.
|
|
57
|
+
|
|
58
|
+
**Unscoped plugins**:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// Resolves eslint-plugin-import, checks its "no-unresolved" rule
|
|
62
|
+
enforce("eslint/import/no-unresolved", "Fix the import path.");
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Resolution: `import` becomes `eslint-plugin-import`.
|
|
66
|
+
|
|
67
|
+
Config-enabled checks for plugin rules go through ESLint's `calculateConfigForFile` with the full qualified name (e.g., `@typescript-eslint/no-explicit-any`), so they respect your flat config.
|
|
68
|
+
|
|
69
|
+
## Custom Linters
|
|
70
|
+
|
|
71
|
+
For tools not built in, use the `linters` config option in `vigiles.json` (or `package.json` under `"vigiles"`):
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"linters": {
|
|
76
|
+
"my-tool": {
|
|
77
|
+
"rulesDir": "tools/my-tool/rules/"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
vigiles checks if a file matching the rule name exists in the specified directory. For example, `enforce("my-tool/no-foo")` passes if `tools/my-tool/rules/no-foo.*` exists (any extension).
|
|
84
|
+
|
|
85
|
+
Multiple directories are supported via an array:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"linters": {
|
|
90
|
+
"my-tool": {
|
|
91
|
+
"rulesDir": ["tools/my-tool/rules/", "tools/my-tool/extra-rules/"]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Custom linters only support existence checks. Config-enabled status is always `"unknown"`.
|
|
98
|
+
|
|
99
|
+
## generate-types
|
|
100
|
+
|
|
101
|
+
`vigiles generate-types` scans all 6 linter APIs, `package.json`, and project files, then emits `.vigiles/generated.d.ts` with type unions derived from your actual project state.
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
npx vigiles generate-types
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
This produces a `.d.ts` file like:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
/**
|
|
111
|
+
* Auto-generated by `vigiles generate-types`.
|
|
112
|
+
* DO NOT EDIT -- re-run `vigiles generate-types` to update.
|
|
113
|
+
*/
|
|
114
|
+
|
|
115
|
+
declare module "vigiles/generated" {
|
|
116
|
+
/** 42 enabled eslint rules (via flat config). */
|
|
117
|
+
export type EslintRule =
|
|
118
|
+
| "no-console"
|
|
119
|
+
| "no-debugger"
|
|
120
|
+
| "@typescript-eslint/no-explicit-any"
|
|
121
|
+
// ...
|
|
122
|
+
|
|
123
|
+
/** 5 enabled ruff rules (via CLI). */
|
|
124
|
+
export type RuffRule = "E501" | "F401" | "F841" | "I001" | "UP006";
|
|
125
|
+
|
|
126
|
+
/** All enabled linter rules across all detected linters. */
|
|
127
|
+
export type LinterRule = EslintRule | RuffRule;
|
|
128
|
+
|
|
129
|
+
/** 8 npm scripts from package.json. */
|
|
130
|
+
export type NpmScript = "build" | "test" | "lint" | "fmt" | "fmt:check" | ...;
|
|
131
|
+
|
|
132
|
+
/** 23 project files. */
|
|
133
|
+
export type ProjectFile = "src/cli.ts" | "src/compile.ts" | "src/linters.ts" | ...;
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The TypeScript compiler then proves references are valid at authoring time. A typo like `enforce("eslint/no-consloe")` becomes a type error in your editor before you ever run `vigiles compile`. This shifts rule-reference validation from runtime to authoring time.
|
|
138
|
+
|
|
139
|
+
Discovery methods per linter:
|
|
140
|
+
|
|
141
|
+
- **ESLint** -- loads flat config via `calculateConfigForFile`, collects rules with severity > 0
|
|
142
|
+
- **Stylelint** -- loads config via `createLinter` + `getConfigForFile`, collects non-null rules
|
|
143
|
+
- **Ruff** -- parses `ruff check --show-settings` for `linter.rules.enabled`
|
|
144
|
+
- **Pylint** -- parses `pylint --list-msgs-enabled`, extracts both IDs (`C0114`) and symbolic names
|
|
145
|
+
- **RuboCop** -- parses `rubocop --show-cops` output for cop names
|
|
146
|
+
- **Clippy** -- reads `Cargo.toml` `[lints.clippy]` section, excludes rules set to `"allow"`
|
|
147
|
+
|
|
148
|
+
Project files default to `src/**/*` but can be configured via `fileGlobs`.
|
|
149
|
+
|
|
150
|
+
## Catalog-Only Mode
|
|
151
|
+
|
|
152
|
+
Set `catalogOnly: true` to skip config-enabled checks and only verify that rules exist in the linter's catalog:
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"catalogOnly": true
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
In this mode, `checkLinterRule` returns `"unknown"` for the `enabled` field instead of loading linter configs. This is useful when:
|
|
161
|
+
|
|
162
|
+
- Full config loading is slow in CI (ESLint/Stylelint config resolution can take seconds)
|
|
163
|
+
- You only care that referenced rules are real, not that they are currently turned on
|
|
164
|
+
- The linter config is not available in the build environment
|
|
165
|
+
|
|
166
|
+
The option can be set in `vigiles.json`, `package.json` under `"vigiles"`, or via the `INPUT_CATALOG_ONLY` environment variable in the GitHub Action.
|