vigiles 2.5.0 → 3.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.
Files changed (209) hide show
  1. package/.claude-plugin/plugin.json +9 -0
  2. package/README.md +73 -434
  3. package/action.yml +143 -7
  4. package/dist/action-gate.d.ts +1 -1
  5. package/dist/action-gate.js +1 -1
  6. package/dist/adapter-conformance.d.ts +30 -0
  7. package/dist/adapter-conformance.js +153 -0
  8. package/dist/adapter-registry.d.ts +42 -0
  9. package/dist/adapter-registry.js +55 -0
  10. package/dist/adapter.d.ts +26 -0
  11. package/dist/adapter.js +16 -0
  12. package/dist/adapters/claude-code/adapter.d.ts +3 -0
  13. package/dist/adapters/claude-code/adapter.js +46 -0
  14. package/dist/{agent-result.d.ts → adapters/claude-code/agent-result.d.ts} +1 -1
  15. package/dist/adapters/claude-code/dialect.d.ts +13 -0
  16. package/dist/adapters/claude-code/dialect.js +51 -0
  17. package/dist/adapters/claude-code/egress-entry.d.ts +2 -0
  18. package/dist/adapters/claude-code/egress-entry.js +115 -0
  19. package/dist/adapters/claude-code/egress-proxy.d.ts +2 -0
  20. package/dist/adapters/claude-code/egress-proxy.js +60 -0
  21. package/dist/adapters/claude-code/egress.d.ts +114 -0
  22. package/dist/adapters/claude-code/egress.js +276 -0
  23. package/dist/adapters/claude-code/eval-baseline.d.ts +68 -0
  24. package/dist/adapters/claude-code/eval-baseline.js +173 -0
  25. package/dist/{eval-cache.d.ts → adapters/claude-code/eval-cache.d.ts} +1 -1
  26. package/dist/{eval-cache.js → adapters/claude-code/eval-cache.js} +1 -1
  27. package/dist/{eval.d.ts → adapters/claude-code/eval.d.ts} +28 -4
  28. package/dist/{eval.js → adapters/claude-code/eval.js} +61 -25
  29. package/dist/{harness-test.d.ts → adapters/claude-code/harness-test.d.ts} +33 -37
  30. package/dist/{harness-test.js → adapters/claude-code/harness-test.js} +124 -43
  31. package/dist/adapters/claude-code/hook-protocol.d.ts +10 -0
  32. package/dist/adapters/claude-code/hook-protocol.js +10 -0
  33. package/dist/adapters/claude-code/layout.d.ts +8 -0
  34. package/dist/adapters/claude-code/layout.js +18 -0
  35. package/dist/{mock-model.d.ts → adapters/claude-code/mock-model.d.ts} +2 -24
  36. package/dist/adapters/claude-code/model-mock.d.ts +11 -0
  37. package/dist/adapters/claude-code/model-mock.js +10 -0
  38. package/dist/adapters/claude-code/plugin-loader.d.ts +25 -0
  39. package/dist/adapters/claude-code/plugin-loader.js +19 -0
  40. package/dist/adapters/claude-code/run-hook.d.ts +204 -0
  41. package/dist/adapters/claude-code/run-hook.js +418 -0
  42. package/dist/adapters/claude-code/run-scripts.d.ts +52 -0
  43. package/dist/adapters/claude-code/run-scripts.js +150 -0
  44. package/dist/adapters/claude-code/runtime.d.ts +16 -0
  45. package/dist/adapters/claude-code/runtime.js +39 -0
  46. package/dist/{sandbox.d.ts → adapters/claude-code/sandbox.d.ts} +45 -4
  47. package/dist/{sandbox.js → adapters/claude-code/sandbox.js} +76 -8
  48. package/dist/{skill-driver.d.ts → adapters/claude-code/skill-driver.d.ts} +1 -1
  49. package/dist/adapters/codex/adapter.d.ts +3 -0
  50. package/dist/adapters/codex/adapter.js +49 -0
  51. package/dist/adapters/codex/dialect.d.ts +10 -0
  52. package/dist/adapters/codex/dialect.js +30 -0
  53. package/dist/adapters/codex/driver.d.ts +20 -0
  54. package/dist/adapters/codex/driver.js +89 -0
  55. package/dist/adapters/codex/hook-protocol.d.ts +10 -0
  56. package/dist/adapters/codex/hook-protocol.js +18 -0
  57. package/dist/adapters/codex/layout.d.ts +16 -0
  58. package/dist/adapters/codex/layout.js +18 -0
  59. package/dist/adapters/codex/mock-model.d.ts +52 -0
  60. package/dist/adapters/codex/mock-model.js +210 -0
  61. package/dist/adapters/codex/model-mock.d.ts +11 -0
  62. package/dist/adapters/codex/model-mock.js +10 -0
  63. package/dist/adapters/codex/runtime.d.ts +38 -0
  64. package/dist/adapters/codex/runtime.js +62 -0
  65. package/dist/adapters/opencode/adapter.d.ts +3 -0
  66. package/dist/adapters/opencode/adapter.js +48 -0
  67. package/dist/adapters/opencode/dialect.d.ts +10 -0
  68. package/dist/adapters/opencode/dialect.js +36 -0
  69. package/dist/adapters/opencode/layout.d.ts +9 -0
  70. package/dist/adapters/opencode/layout.js +25 -0
  71. package/dist/adapters/opencode/model-mock.d.ts +10 -0
  72. package/dist/adapters/opencode/model-mock.js +10 -0
  73. package/dist/adapters/opencode/runtime.d.ts +9 -0
  74. package/dist/adapters/opencode/runtime.js +21 -0
  75. package/dist/claude-code.d.ts +16 -0
  76. package/dist/claude-code.js +32 -0
  77. package/dist/cli-flags.d.ts +22 -0
  78. package/dist/cli-flags.js +38 -0
  79. package/dist/cli.js +406 -206
  80. package/dist/codex.d.ts +20 -0
  81. package/dist/codex.js +36 -0
  82. package/dist/community-skills.d.ts +1 -1
  83. package/dist/community-skills.js +2 -2
  84. package/dist/core/adapter.d.ts +88 -0
  85. package/dist/core/adapter.js +3 -0
  86. package/dist/{compile.d.ts → core/compile.d.ts} +11 -2
  87. package/dist/{compile.js → core/compile.js} +68 -53
  88. package/dist/core/compose.d.ts +79 -0
  89. package/dist/core/compose.js +145 -0
  90. package/dist/core/dialect.d.ts +51 -0
  91. package/dist/core/dialect.js +3 -0
  92. package/dist/core/harness-driver.d.ts +134 -0
  93. package/dist/core/harness-driver.js +3 -0
  94. package/dist/core/hook-protocol.d.ts +28 -0
  95. package/dist/core/hook-protocol.js +3 -0
  96. package/dist/core/layout.d.ts +43 -0
  97. package/dist/core/layout.js +3 -0
  98. package/dist/core/model-mock.d.ts +27 -0
  99. package/dist/core/model-mock.js +3 -0
  100. package/dist/core/refs.d.ts +62 -0
  101. package/dist/{refs.js → core/refs.js} +49 -23
  102. package/dist/core/runtime.d.ts +38 -0
  103. package/dist/core/runtime.js +3 -0
  104. package/dist/{types.d.ts → core/types.d.ts} +29 -0
  105. package/dist/{validate.d.ts → core/validate.d.ts} +1 -1
  106. package/dist/{validate.js → core/validate.js} +41 -4
  107. package/dist/e2e.d.ts +19 -0
  108. package/dist/e2e.js +39 -0
  109. package/dist/harness-assert.d.ts +60 -11
  110. package/dist/harness-assert.js +111 -9
  111. package/dist/integration.d.ts +16 -0
  112. package/dist/integration.js +32 -0
  113. package/dist/leaderboard.d.ts +33 -0
  114. package/dist/leaderboard.js +107 -0
  115. package/dist/linting.d.ts +9 -0
  116. package/dist/linting.js +25 -0
  117. package/dist/plugin-loader.d.ts +6 -3
  118. package/dist/plugin-loader.js +101 -49
  119. package/dist/scan.d.ts +49 -0
  120. package/dist/scan.js +177 -0
  121. package/dist/setup-plan.d.ts +51 -0
  122. package/dist/setup-plan.js +85 -0
  123. package/dist/skill-test.d.ts +1 -1
  124. package/dist/skill-test.js +1 -1
  125. package/dist/test-coverage.d.ts +71 -0
  126. package/dist/test-coverage.js +228 -0
  127. package/dist/testing.d.ts +12 -0
  128. package/dist/testing.js +28 -0
  129. package/dist/unit.d.ts +17 -0
  130. package/dist/unit.js +36 -0
  131. package/hooks/refs-nudge.sh +24 -0
  132. package/package.json +35 -16
  133. package/skills/audit-feedback-loop/SKILL.md +76 -0
  134. package/skills/edit-spec/SKILL.md +131 -0
  135. package/skills/enforce-rules-format/SKILL.md +71 -0
  136. package/skills/generate-logo/SKILL.md +103 -0
  137. package/skills/generate-rule/SKILL.md +64 -0
  138. package/skills/linter-docs/clippy.md +241 -0
  139. package/skills/linter-docs/eslint.md +384 -0
  140. package/skills/linter-docs/pylint.md +288 -0
  141. package/skills/linter-docs/rubocop.md +277 -0
  142. package/skills/linter-docs/ruff.md +187 -0
  143. package/skills/linter-docs/stylelint.md +247 -0
  144. package/skills/migrate-to-spec/SKILL.md +126 -0
  145. package/skills/pr-to-lint-rule/SKILL.md +97 -0
  146. package/skills/strengthen/SKILL.md +168 -0
  147. package/skills/test-harness/SKILL.md +157 -0
  148. package/dist/action.d.ts +0 -7
  149. package/dist/action.js +0 -180
  150. package/dist/refs.d.ts +0 -44
  151. package/dist/run-hook.d.ts +0 -77
  152. package/dist/run-hook.js +0 -80
  153. package/dist/run-scripts.d.ts +0 -20
  154. package/dist/run-scripts.js +0 -70
  155. /package/dist/{agent-result.js → adapters/claude-code/agent-result.js} +0 -0
  156. /package/dist/{agent-runtime.d.ts → adapters/claude-code/agent-runtime.d.ts} +0 -0
  157. /package/dist/{agent-runtime.js → adapters/claude-code/agent-runtime.js} +0 -0
  158. /package/dist/{judge.d.ts → adapters/claude-code/judge.d.ts} +0 -0
  159. /package/dist/{judge.js → adapters/claude-code/judge.js} +0 -0
  160. /package/dist/{mock-entry.d.ts → adapters/claude-code/mock-entry.d.ts} +0 -0
  161. /package/dist/{mock-entry.js → adapters/claude-code/mock-entry.js} +0 -0
  162. /package/dist/{mock-model.js → adapters/claude-code/mock-model.js} +0 -0
  163. /package/dist/{skill-driver.js → adapters/claude-code/skill-driver.js} +0 -0
  164. /package/dist/{skill-runtime.d.ts → adapters/claude-code/skill-runtime.d.ts} +0 -0
  165. /package/dist/{skill-runtime.js → adapters/claude-code/skill-runtime.js} +0 -0
  166. /package/dist/{stats.d.ts → adapters/claude-code/stats.d.ts} +0 -0
  167. /package/dist/{stats.js → adapters/claude-code/stats.js} +0 -0
  168. /package/dist/{compile-generator.d.ts → core/compile-generator.d.ts} +0 -0
  169. /package/dist/{compile-generator.js → core/compile-generator.js} +0 -0
  170. /package/dist/{coverage.d.ts → core/coverage.d.ts} +0 -0
  171. /package/dist/{coverage.js → core/coverage.js} +0 -0
  172. /package/dist/{doc-refs.d.ts → core/doc-refs.d.ts} +0 -0
  173. /package/dist/{doc-refs.js → core/doc-refs.js} +0 -0
  174. /package/dist/{evolve.d.ts → core/evolve.d.ts} +0 -0
  175. /package/dist/{evolve.js → core/evolve.js} +0 -0
  176. /package/dist/{frontmatter.d.ts → core/frontmatter.d.ts} +0 -0
  177. /package/dist/{frontmatter.js → core/frontmatter.js} +0 -0
  178. /package/dist/{generate-schema.d.ts → core/generate-schema.d.ts} +0 -0
  179. /package/dist/{generate-schema.js → core/generate-schema.js} +0 -0
  180. /package/dist/{generate-types.d.ts → core/generate-types.d.ts} +0 -0
  181. /package/dist/{generate-types.js → core/generate-types.js} +0 -0
  182. /package/dist/{hash.d.ts → core/hash.d.ts} +0 -0
  183. /package/dist/{hash.js → core/hash.js} +0 -0
  184. /package/dist/{inline.d.ts → core/inline.d.ts} +0 -0
  185. /package/dist/{inline.js → core/inline.js} +0 -0
  186. /package/dist/{integrity.d.ts → core/integrity.d.ts} +0 -0
  187. /package/dist/{integrity.js → core/integrity.js} +0 -0
  188. /package/dist/{linters.d.ts → core/linters.d.ts} +0 -0
  189. /package/dist/{linters.js → core/linters.js} +0 -0
  190. /package/dist/{mcp.d.ts → core/mcp.d.ts} +0 -0
  191. /package/dist/{mcp.js → core/mcp.js} +0 -0
  192. /package/dist/{orphans.d.ts → core/orphans.d.ts} +0 -0
  193. /package/dist/{orphans.js → core/orphans.js} +0 -0
  194. /package/dist/{proofs.d.ts → core/proofs.d.ts} +0 -0
  195. /package/dist/{proofs.js → core/proofs.js} +0 -0
  196. /package/dist/{session.d.ts → core/session.d.ts} +0 -0
  197. /package/dist/{session.js → core/session.js} +0 -0
  198. /package/dist/{sidecar.d.ts → core/sidecar.d.ts} +0 -0
  199. /package/dist/{sidecar.js → core/sidecar.js} +0 -0
  200. /package/dist/{spec.d.ts → core/spec.d.ts} +0 -0
  201. /package/dist/{spec.js → core/spec.js} +0 -0
  202. /package/dist/{symbols.d.ts → core/symbols.d.ts} +0 -0
  203. /package/dist/{symbols.js → core/symbols.js} +0 -0
  204. /package/dist/{test-utils.d.ts → core/test-utils.d.ts} +0 -0
  205. /package/dist/{test-utils.js → core/test-utils.js} +0 -0
  206. /package/dist/{types.js → core/types.js} +0 -0
  207. /package/{.claude-plugin/hooks → hooks}/post-edit.sh +0 -0
  208. /package/{.claude-plugin/hooks → hooks}/pre-edit.sh +0 -0
  209. /package/{.claude-plugin/hooks → hooks}/session-start.sh +0 -0
@@ -0,0 +1,187 @@
1
+ # Ruff — Reference
2
+
3
+ Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and `pr-to-lint-rule` (write custom rules).
4
+
5
+ ## Check Existing Rules First
6
+
7
+ Ruff reimplements 800+ rules from flake8, pylint, isort, pyupgrade, and others. Before writing a custom rule, check if Ruff already covers it:
8
+
9
+ | Prefix | Source | Key rules |
10
+ | --------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
11
+ | `E` / `W` | pycodestyle | `E501` (line-too-long), `W291` (trailing-whitespace), `E711` (none-comparison) |
12
+ | `F` | pyflakes | `F401` (unused-import), `F841` (unused-variable), `F811` (redefined-unused-name) |
13
+ | `I` | isort | `I001` (unsorted-imports), `I002` (missing-required-import) |
14
+ | `N` | pep8-naming | `N801` (invalid-class-name), `N802` (invalid-function-name), `N806` (non-lowercase-variable) |
15
+ | `UP` | pyupgrade | `UP006` (non-pep585-annotation), `UP007` (non-pep604-annotation), `UP035` (deprecated-import) |
16
+ | `S` | flake8-bandit | `S101` (assert), `S105` (hardcoded-password-string), `S301` (suspicious-pickle-usage) |
17
+ | `B` | flake8-bugbear | `B006` (mutable-argument-default), `B007` (unused-loop-control-variable), `B905` (zip-without-explicit-strict) |
18
+ | `A` | flake8-builtins | `A001` (builtin-variable-shadowing), `A002` (builtin-argument-shadowing) |
19
+ | `C4` | flake8-comprehensions | `C400` (unnecessary-generator-list), `C401` (unnecessary-generator-set), `C408` (unnecessary-collection-call) |
20
+ | `T20` | flake8-print | `T201` (print), `T203` (pprint) |
21
+ | `SIM` | flake8-simplify | `SIM102` (collapsible-if), `SIM108` (if-else-block-instead-of-if-exp), `SIM110` (reimplemented-builtin) |
22
+ | `PL` | pylint | `PLC0414` (useless-import-alias), `PLE1205` (logging-too-many-args), `PLR0913` (too-many-arguments), `PLW0602` (global-variable-not-assigned) |
23
+ | `PTH` | flake8-use-pathlib | `PTH100` (os-path-abspath), `PTH118` (os-path-join), `PTH123` (builtin-open) |
24
+ | `RUF` | ruff-specific | `RUF001` (ambiguous-unicode-character), `RUF005` (collection-literal-concatenation), `RUF013` (implicit-optional) |
25
+ | `D` | pydocstyle | `D100` (undocumented-public-module), `D103` (undocumented-public-function), `D400` (first-line-should-end-with-period) |
26
+ | `ANN` | flake8-annotations | `ANN001` (missing-type-function-argument), `ANN201` (missing-return-type-public-function) |
27
+ | `ARG` | flake8-unused-arguments | `ARG001` (unused-function-argument), `ARG002` (unused-method-argument) |
28
+ | `ERA` | eradicate | `ERA001` (commented-out-code) |
29
+ | `TCH` | flake8-type-checking | `TCH001` (typing-only-first-party-import), `TCH002` (typing-only-third-party-import) |
30
+ | `FBT` | flake8-boolean-trap | `FBT001` (boolean-typed-positional-argument), `FBT002` (boolean-default-value-positional-argument) |
31
+ | `ICN` | flake8-import-conventions | `ICN001` (unconventional-import-alias) — e.g., `import numpy as np` |
32
+ | `PIE` | flake8-pie | `PIE790` (unnecessary-placeholder), `PIE804` (no-unnecessary-dict-kwargs) |
33
+ | `RSE` | flake8-raise | `RSE102` (unnecessary-paren-on-raise-exception) |
34
+ | `RET` | flake8-return | `RET501` (unnecessary-return-none), `RET504` (unnecessary-assign) |
35
+ | `TID` | flake8-tidy-imports | `TID252` (relative-imports) — ban relative imports |
36
+ | `PERF` | perflint | `PERF101` (unnecessary-list-cast), `PERF401` (manual-list-comprehension) |
37
+ | `FURB` | refurb | `FURB105` (print-empty-string), `FURB118` (reimplemented-operator) |
38
+
39
+ **Tip:** Run `ruff rule <CODE>` to see the full description of any rule. Run `ruff linter` to see all available rule groups.
40
+
41
+ ## Rule Selection
42
+
43
+ Configure in `pyproject.toml`:
44
+
45
+ ```toml
46
+ [tool.ruff.lint]
47
+ select = [
48
+ "E", "W", # pycodestyle
49
+ "F", # pyflakes
50
+ "I", # isort
51
+ "B", # flake8-bugbear
52
+ "S", # flake8-bandit
53
+ "UP", # pyupgrade
54
+ "SIM", # flake8-simplify
55
+ "T20", # flake8-print
56
+ "RUF", # ruff-specific
57
+ ]
58
+ ignore = [
59
+ "E501", # line-too-long (handled by formatter)
60
+ ]
61
+
62
+ [tool.ruff.lint.per-file-ignores]
63
+ "tests/**" = ["S101"] # allow assert in tests
64
+ "__init__.py" = ["F401"] # allow unused imports in __init__
65
+ ```
66
+
67
+ Or in `ruff.toml` (same syntax without `[tool.ruff]` prefix):
68
+
69
+ ```toml
70
+ [lint]
71
+ select = ["E", "F", "I", "B"]
72
+ ```
73
+
74
+ ### Selecting individual rules vs groups
75
+
76
+ - `"F"` enables all pyflakes rules
77
+ - `"F401"` enables only `F401`
78
+ - `"ALL"` enables everything (noisy — use `ignore` to subtract)
79
+ - Use `extend-select` to add rules without overriding the default set
80
+
81
+ ## Custom Rules
82
+
83
+ **Ruff does not support user-defined rules.** If no existing rule covers your pattern:
84
+
85
+ 1. **Configure existing rules** — many rules accept options (e.g., `flake8-import-conventions` lets you set allowed aliases)
86
+ 2. **Use `ruff.lint.flake8-import-conventions.aliases`** — for import alias enforcement
87
+ 3. **Use `ruff.lint.flake8-tidy-imports.banned-api`** — for banning specific imports/modules
88
+ 4. **Use ast-grep** — for arbitrary AST pattern matching, reference via `enforce()` with an ast-grep rule
89
+ 5. **Write a Pylint checker** — if you need the full power of a custom rule with AST analysis, use Pylint (Ruff can coexist with Pylint in CI)
90
+
91
+ ```toml
92
+ # Ban specific APIs without a custom rule
93
+ [tool.ruff.lint.flake8-tidy-imports.banned-api]
94
+ "os.system".msg = "Use subprocess.run instead."
95
+ "typing.Dict".msg = "Use dict instead (PEP 585)."
96
+ ```
97
+
98
+ ## Auto-Fix
99
+
100
+ Ruff provides auto-fix for many rules:
101
+
102
+ ```bash
103
+ ruff check --fix # apply safe fixes only
104
+ ruff check --fix --unsafe-fixes # include unsafe fixes
105
+ ruff check --fix-only # only fix, don't report remaining violations
106
+ ```
107
+
108
+ **Safe vs unsafe fixes:**
109
+
110
+ - **Safe** — guaranteed to not change semantics (e.g., removing unused imports)
111
+ - **Unsafe** — may change semantics (e.g., `UP007` rewriting `Optional[X]` to `X | None` can break runtime type checking)
112
+
113
+ Check fixability per rule with `ruff rule <CODE>` — it shows whether the rule has a fix and if it's safe.
114
+
115
+ ### vigiles enforce() reference
116
+
117
+ ```typescript
118
+ enforce("ruff/F401", "Remove unused imports.");
119
+ enforce("ruff/T201", "Use logging module instead of print.");
120
+ enforce("ruff/S101", "Don't use assert in production code.");
121
+ ```
122
+
123
+ vigiles checks `ruff rule <CODE>` and verifies the rule exists, then parses `ruff check --show-settings` to confirm it's enabled.
124
+
125
+ ## Edge Cases and Gotchas
126
+
127
+ ### Ruff vs Pylint (PL prefix)
128
+
129
+ Ruff reimplements many Pylint rules with the `PL` prefix. The mapping is not 1:1 — some Pylint rules have no Ruff equivalent, and Ruff's implementations may differ in edge cases.
130
+
131
+ | Ruff code | Pylint equivalent | Notes |
132
+ | --------- | -------------------------------------- | ------------- |
133
+ | `PLR0913` | `R0913` (too-many-arguments) | Same behavior |
134
+ | `PLC0414` | `C0414` (useless-import-alias) | Same behavior |
135
+ | `PLW0602` | `W0602` (global-variable-not-assigned) | Same behavior |
136
+ | `PLE1205` | `E1205` (logging-too-many-args) | Same behavior |
137
+
138
+ If the project uses both Ruff and Pylint, disable Pylint rules that Ruff already covers to avoid duplicate reports.
139
+
140
+ ### Preview rules
141
+
142
+ Some rules are behind `--preview` flag. These are not stable and may change between versions. Don't use `enforce()` on preview rules unless you pin the Ruff version.
143
+
144
+ ```toml
145
+ [tool.ruff]
146
+ preview = true # enables preview rules
147
+ ```
148
+
149
+ ### Formatter vs linter conflicts
150
+
151
+ Ruff has both a linter (`ruff check`) and a formatter (`ruff format`). Some linter rules conflict with the formatter:
152
+
153
+ - `E501` (line-too-long) — the formatter handles line length; disable in the linter
154
+ - `W291`/`W292`/`W293` — whitespace rules handled by formatter
155
+ - `COM812` (missing-trailing-comma) — conflicts with formatter in some cases
156
+
157
+ The Ruff docs recommend: `ignore = ["E501", "W291", "W292", "W293"]` when using `ruff format`.
158
+
159
+ ### Monorepo config inheritance
160
+
161
+ Ruff resolves config by walking up from the file being linted. In a monorepo:
162
+
163
+ - Each package can have its own `pyproject.toml` with `[tool.ruff]`
164
+ - A root config applies to all packages unless overridden
165
+ - Use `extend` to inherit from a shared config: `extend = "../../pyproject.toml"`
166
+
167
+ vigiles discovers Ruff config at `basePath` only. In a monorepo, run vigiles from each package root.
168
+
169
+ ## Mapping PR Feedback to Rule Strategy
170
+
171
+ | PR comment pattern | Best approach |
172
+ | ----------------------------- | ------------------------------------------------------------------ |
173
+ | "Remove unused imports" | `ruff/F401` — already exists |
174
+ | "Don't use print()" | `ruff/T201` — already exists |
175
+ | "Sort your imports" | `ruff/I001` — already exists |
176
+ | "Use pathlib not os.path" | `ruff/PTH*` — enable the PTH group |
177
+ | "Don't use assert in prod" | `ruff/S101` — already exists, ignore in tests via per-file-ignores |
178
+ | "Remove commented-out code" | `ruff/ERA001` — already exists |
179
+ | "Add type annotations" | `ruff/ANN*` — enable the ANN group |
180
+ | "Use comprehensions" | `ruff/C4*` — enable the C4 group |
181
+ | "Don't shadow builtins" | `ruff/A001` / `A002` — already exists |
182
+ | "Simplify this if" | `ruff/SIM102` / `SIM108` — already exists |
183
+ | "Don't use mutable defaults" | `ruff/B006` — already exists |
184
+ | "Ban specific import" | `flake8-tidy-imports.banned-api` config — no custom rule needed |
185
+ | "Naming convention violated" | `ruff/N*` — enable the N group |
186
+ | "Don't use os.system" | `flake8-tidy-imports.banned-api` config |
187
+ | "Complex pattern not in Ruff" | Write a Pylint checker or use ast-grep |
@@ -0,0 +1,247 @@
1
+ # Stylelint — Reference
2
+
3
+ Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and `pr-to-lint-rule` (write custom rules).
4
+
5
+ ## Check Existing Plugins First
6
+
7
+ Before writing a custom rule, search these packages — the pattern may already be covered:
8
+
9
+ | Plugin | Scope | Key rules |
10
+ | ------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
11
+ | `stylelint-config-standard` | Standard CSS conventions | Extends `stylelint-config-recommended`, adds `declaration-block-no-redundant-longhand-properties`, `shorthand-property-no-redundant-values` |
12
+ | `stylelint-config-recommended` | Error prevention | `no-descending-specificity`, `no-duplicate-selectors`, `declaration-block-no-duplicate-properties` |
13
+ | `stylelint-order` | Property ordering | `order/properties-order`, `order/properties-alphabetical-order` — configurable sort orders |
14
+ | `stylelint-scss` | SCSS syntax | `scss/no-duplicate-mixins`, `scss/no-unused-private-members`, `scss/at-rule-no-unknown`, `scss/dollar-variable-pattern` |
15
+ | `stylelint-config-css-modules` | CSS Modules | Adjusts rules for `:global`, `:local`, `composes` syntax |
16
+ | `stylelint-config-tailwindcss` | Tailwind CSS | Allows Tailwind directives (`@tailwind`, `@apply`, `@screen`) |
17
+ | `stylelint-no-unsupported-browser-features` | Browser compat | Flags CSS features not supported by your browserslist targets |
18
+ | `stylelint-declaration-strict-value` | Value enforcement | Require variables/functions for specific properties (colors, fonts, z-index) |
19
+ | `stylelint-config-clean-order` | Property order | Opinionated property ordering (positioning → box model → typography → visual → misc) |
20
+ | `stylelint-config-prettier` | Prettier compat | Disables rules that conflict with Prettier (deprecated in Stylelint v15+) |
21
+ | `stylelint-a11y` | Accessibility | `a11y/no-outline-none`, `a11y/no-text-size-adjust`, `a11y/media-prefers-reduced-motion` |
22
+
23
+ **Tip:** Stylelint v15+ removed all stylistic rules (spacing, formatting). Use Prettier for formatting, Stylelint for correctness.
24
+
25
+ ## Rule Anatomy
26
+
27
+ Every Stylelint rule is a function that receives options and returns a checker:
28
+
29
+ ```js
30
+ const stylelint = require("stylelint");
31
+
32
+ const ruleName = "plugin/no-important";
33
+ const messages = stylelint.utils.ruleMessages(ruleName, {
34
+ rejected: "Unexpected !important — use utility classes instead.",
35
+ });
36
+ const meta = { url: "https://example.com/rules/no-important" };
37
+
38
+ /** @type {import('stylelint').Rule} */
39
+ const ruleFunction = (primary, secondary, context) => {
40
+ return (root, result) => {
41
+ const validOptions = stylelint.utils.validateOptions(result, ruleName, {
42
+ actual: primary,
43
+ possible: [true],
44
+ });
45
+ if (!validOptions) return;
46
+
47
+ root.walkDecls((decl) => {
48
+ if (decl.important) {
49
+ stylelint.utils.report({
50
+ message: messages.rejected,
51
+ node: decl,
52
+ result,
53
+ ruleName,
54
+ });
55
+ }
56
+ });
57
+ };
58
+ };
59
+
60
+ ruleFunction.ruleName = ruleName;
61
+ ruleFunction.messages = messages;
62
+ ruleFunction.meta = meta;
63
+
64
+ module.exports = stylelint.createPlugin(ruleName, ruleFunction);
65
+ ```
66
+
67
+ ### Key concepts
68
+
69
+ | Concept | Purpose | Notes |
70
+ | ----------------------------------- | ------------------------------------------ | --------------------------------------------------------- |
71
+ | `primary` | First option (usually `true` or a pattern) | What the user passes in config |
72
+ | `secondary` | Second option (usually an object) | Additional configuration |
73
+ | `context` | Execution context | Contains `fix` boolean for auto-fix mode |
74
+ | `root` | PostCSS AST root node | Entry point for walking the CSS tree |
75
+ | `result` | Results accumulator | Pass to `stylelint.utils.report()` |
76
+ | `stylelint.utils.report()` | Report a violation | Canonical way to report — handles severity, ignores, etc. |
77
+ | `stylelint.utils.validateOptions()` | Validate rule config | Returns false if config is invalid |
78
+
79
+ ### PostCSS AST node types
80
+
81
+ | Node type | What it represents | Walker method | Example |
82
+ | ------------- | ----------------------- | --------------------- | --------------------------- |
83
+ | `Root` | Entire stylesheet | — | Top-level container |
84
+ | `Rule` | Selector + declarations | `root.walkRules()` | `.button { color: red }` |
85
+ | `Declaration` | Property: value pair | `root.walkDecls()` | `color: red` |
86
+ | `AtRule` | `@` rule | `root.walkAtRules()` | `@media (min-width: 768px)` |
87
+ | `Comment` | CSS comment | `root.walkComments()` | `/* comment */` |
88
+
89
+ Properties on `Declaration`: `decl.prop` (property name), `decl.value` (value string), `decl.important` (boolean).
90
+
91
+ ## Configuration
92
+
93
+ ### `.stylelintrc.json`
94
+
95
+ ```json
96
+ {
97
+ "extends": ["stylelint-config-standard"],
98
+ "plugins": ["stylelint-order"],
99
+ "rules": {
100
+ "declaration-no-important": true,
101
+ "selector-max-specificity": "0,3,0",
102
+ "order/properties-alphabetical-order": true
103
+ }
104
+ }
105
+ ```
106
+
107
+ ### `stylelint.config.js` (flat config)
108
+
109
+ ```js
110
+ module.exports = {
111
+ extends: ["stylelint-config-standard"],
112
+ rules: {
113
+ "declaration-no-important": true,
114
+ },
115
+ overrides: [
116
+ {
117
+ files: ["**/*.scss"],
118
+ extends: ["stylelint-config-standard-scss"],
119
+ },
120
+ ],
121
+ };
122
+ ```
123
+
124
+ ### Key config concepts
125
+
126
+ - **`extends`** — inherit from shared configs (order matters — later overrides earlier)
127
+ - **`plugins`** — load additional rule packages
128
+ - **`rules`** — enable/disable/configure individual rules (`true`, `null`, or `[value, options]`)
129
+ - **`overrides`** — per-file rule configuration (like ESLint overrides)
130
+
131
+ ## Testing Rules
132
+
133
+ ```js
134
+ const { lint } = require("stylelint");
135
+
136
+ async function testRule() {
137
+ const result = await lint({
138
+ code: "a { color: pink !important; }",
139
+ config: {
140
+ plugins: ["./plugin-no-important.js"],
141
+ rules: { "plugin/no-important": true },
142
+ },
143
+ });
144
+
145
+ console.log(result.results[0].warnings);
146
+ // [{ rule: "plugin/no-important", text: "Unexpected !important...", line: 1, column: 19 }]
147
+ }
148
+ ```
149
+
150
+ For plugin development, use `jest-preset-stylelint` or test via `lint()` directly.
151
+
152
+ **Testing best practices:**
153
+
154
+ 1. **Test with `lint()` API** — canonical method, works with any test runner
155
+ 2. **Test valid and invalid cases** — ensure no false positives
156
+ 3. **Test with SCSS/Less** if the plugin should support preprocessor syntax
157
+ 4. **Test `overrides`** — if the rule has options, test each configuration
158
+
159
+ ### vigiles enforce() reference
160
+
161
+ ```typescript
162
+ enforce("stylelint/declaration-no-important", "Use utility classes instead.");
163
+ enforce(
164
+ "stylelint/selector-max-specificity",
165
+ "Keep specificity low for maintainability.",
166
+ );
167
+ ```
168
+
169
+ vigiles loads Stylelint config via `createLinter` + `getConfigForFile`, checks if the rule value is not `null`.
170
+
171
+ ## Edge Cases and Gotchas
172
+
173
+ ### CSS-in-JS
174
+
175
+ Stylelint supports CSS-in-JS via custom syntaxes:
176
+
177
+ ```json
178
+ {
179
+ "overrides": [
180
+ {
181
+ "files": ["**/*.{js,jsx,ts,tsx}"],
182
+ "customSyntax": "@stylelint/postcss-css-in-js"
183
+ }
184
+ ]
185
+ }
186
+ ```
187
+
188
+ Note: `@stylelint/postcss-css-in-js` is deprecated as of Stylelint v15. For styled-components / emotion, consider `postcss-styled-syntax` or lint extracted CSS instead.
189
+
190
+ ### SCSS / Less
191
+
192
+ Use dedicated configs:
193
+
194
+ - SCSS: `stylelint-config-standard-scss` (includes `stylelint-scss` plugin)
195
+ - Less: `postcss-less` as `customSyntax`
196
+
197
+ SCSS nesting (`&-modifier`) and mixins may trigger false positives in standard rules. The SCSS config handles this.
198
+
199
+ ### Prettier conflicts (v15+)
200
+
201
+ Stylelint v15 removed all stylistic rules. If upgrading from v14:
202
+
203
+ - Remove `stylelint-config-prettier` (no longer needed)
204
+ - Remove manual disables of formatting rules (`indentation`, `string-quotes`, etc.)
205
+ - Stylelint now handles correctness only; Prettier handles formatting
206
+
207
+ ### Property order plugins
208
+
209
+ `stylelint-order` and `stylelint-config-clean-order` can conflict. Use one ordering strategy:
210
+
211
+ - Alphabetical: `order/properties-alphabetical-order`
212
+ - Grouped: `order/properties-order` with a custom group list
213
+ - Clean order: extend `stylelint-config-clean-order` (opinionated groups)
214
+
215
+ ### `extends` order matters
216
+
217
+ ```json
218
+ {
219
+ "extends": ["stylelint-config-standard", "stylelint-config-prettier"]
220
+ }
221
+ ```
222
+
223
+ Later configs override earlier ones. Put base configs first, overrides last.
224
+
225
+ ### Monorepo considerations
226
+
227
+ - Stylelint resolves config from the file being linted, walking up directories
228
+ - Each package can have its own `.stylelintrc.json`
229
+ - vigiles checks Stylelint config via `createLinter({ cwd: basePath })` — in a monorepo, run from each package root
230
+
231
+ ## Mapping PR Feedback to Rule Strategy
232
+
233
+ | PR comment pattern | Best approach |
234
+ | ---------------------------- | --------------------------------------------------------------------------------- |
235
+ | "Don't use !important" | `stylelint/declaration-no-important` — already exists |
236
+ | "Keep specificity low" | `stylelint/selector-max-specificity` with threshold |
237
+ | "Sort properties" | `stylelint-order` plugin — `order/properties-alphabetical-order` or custom groups |
238
+ | "Don't use ID selectors" | `stylelint/selector-max-id` — already exists |
239
+ | "Use variables for colors" | `stylelint-declaration-strict-value` plugin |
240
+ | "No vendor prefixes" | `stylelint/property-no-vendor-prefix`, `value-no-vendor-prefix` |
241
+ | "Don't nest too deep" | `stylelint/selector-max-compound-selectors` with threshold |
242
+ | "Use modern CSS" | `stylelint/declaration-property-value-no-unknown` + browserslist |
243
+ | "Remove empty blocks" | `stylelint/block-no-empty` — already exists |
244
+ | "Don't duplicate properties" | `stylelint/declaration-block-no-duplicate-properties` — already exists |
245
+ | "Use shorthand" | `stylelint/declaration-block-no-redundant-longhand-properties` — already exists |
246
+ | "Font naming convention" | `stylelint/font-family-name-quotes` — already exists |
247
+ | "No unknown @rules" | `stylelint/at-rule-no-unknown` (or `scss/at-rule-no-unknown` for SCSS) |
@@ -0,0 +1,126 @@
1
+ ---
2
+ name: migrate-to-spec
3
+ description: Convert an existing hand-written CLAUDE.md into a typed .spec.ts file for incremental adoption
4
+ disable-model-invocation: true
5
+ argument-hint: <path to CLAUDE.md, defaults to CLAUDE.md>
6
+ ---
7
+
8
+ Convert an existing hand-written CLAUDE.md (or AGENTS.md) into a typed `CLAUDE.md.spec.ts` file. This is the incremental adoption path — you keep your existing instruction file as the starting point and get type safety going forward.
9
+
10
+ > **Don't need full TypeScript?** A typed spec is the deepest commitment level. If the user only wants verified rules without a build step, point them at markdown mode first: inline `<!-- vigiles:enforce ... -->` comments (Level 0) or a `vigiles:` YAML frontmatter block with `vigiles generate-schema` for editor autocomplete (Level 1). Both are verified by `vigiles audit` with the same engine as a spec. See `docs/markdown-mode.md`. Migrate to a spec only when they want compiler-grade guarantees.
11
+
12
+ ## Instructions
13
+
14
+ ### Step 1: Read the Existing File
15
+
16
+ Read the target instruction file (default: `CLAUDE.md` in the repo root). If the user specified a path, use that.
17
+
18
+ Also check if vigiles is installed: look for `vigiles` in `package.json` devDependencies. If not, suggest:
19
+
20
+ ```bash
21
+ npm install -D vigiles
22
+ ```
23
+
24
+ ### Step 2: Parse the Structure
25
+
26
+ Identify these sections in the markdown:
27
+
28
+ - **Commands** — lines like `` `npm run build` — description `` or ``- `command` — description``
29
+ - **Key files** — lines like `` `src/foo.ts` — description `` listing important files
30
+ - **Rules** — `###` headings with `**Enforced by:**` or `**Guidance only**` annotations
31
+ - **Prose sections** — everything else (positioning, architecture, principles, etc.)
32
+
33
+ For each rule, classify it:
34
+
35
+ - Has `**Enforced by:** \`linter/rule\``→`enforce("linter/rule", "why")`
36
+ - Has `**Enforced by:** \`code-review\``or similar non-linter →`guidance("...")`
37
+ - Has `**Guidance only**` → `guidance("...")`
38
+ - Has no annotation → mark as TODO for the user to classify
39
+
40
+ ### Step 3: Generate the Spec File
41
+
42
+ Create `CLAUDE.md.spec.ts` (or the appropriate name based on the source file) with this structure:
43
+
44
+ ```typescript
45
+ import {
46
+ claude,
47
+ enforce,
48
+ guidance,
49
+ check,
50
+ every,
51
+ file,
52
+ cmd,
53
+ ref,
54
+ instructions,
55
+ } from "vigiles/spec";
56
+
57
+ export default claude({
58
+ sections: {
59
+ // Prose sections here
60
+ },
61
+
62
+ keyFiles: {
63
+ // Key files here
64
+ },
65
+
66
+ commands: {
67
+ // Commands here
68
+ },
69
+
70
+ rules: {
71
+ // Rules here
72
+ },
73
+ });
74
+ ```
75
+
76
+ **Important guidelines:**
77
+
78
+ - Use `file()` refs in sections where file paths appear in backticks — this enables stale reference detection
79
+ - Use `cmd()` refs for any `npm run` commands mentioned in sections
80
+ - Convert `**Enforced by:** \`code-review\``rules to`guidance()` — code review is not a mechanical enforcement
81
+ - For rules with no annotation, add a `// TODO: classify as enforce() or guidance()` comment
82
+ - Keep rule IDs as kebab-case versions of the heading text
83
+ - Preserve the `**Why:**` text as the second argument to `enforce()` or `guidance()`
84
+ - If sections reference other files or skills, use `ref()` for cross-references
85
+
86
+ ### Step 4: Verify the Spec Compiles
87
+
88
+ Run:
89
+
90
+ ```bash
91
+ npm run build
92
+ npx vigiles compile CLAUDE.md.spec.ts
93
+ ```
94
+
95
+ Compare the compiled output against the original file. Key differences are expected (formatting, section ordering), but all rules, commands, key files, and prose content should be preserved.
96
+
97
+ ### Step 5: Present the Result
98
+
99
+ Show the user:
100
+
101
+ 1. The generated spec file
102
+ 2. How many rules were converted (enforce vs guidance vs TODO)
103
+ 3. How many file/cmd refs were added for stale reference detection
104
+ 4. The command to compile: `npx vigiles compile`
105
+ 5. The command to verify: `npx vigiles check`
106
+
107
+ Ask if they want you to write the file. If yes, also suggest adding to `.gitignore` or updating CI to run `vigiles compile` and `vigiles check`.
108
+
109
+ ### Step 6: Optional — Set Up CI
110
+
111
+ If the user wants CI integration, suggest adding to their GitHub Actions workflow:
112
+
113
+ ```yaml
114
+ - name: Compile specs
115
+ run: npx vigiles compile
116
+ - name: Verify integrity
117
+ run: npx vigiles check
118
+ ```
119
+
120
+ Or using the vigiles GitHub Action:
121
+
122
+ ```yaml
123
+ - uses: zernie/vigiles@main
124
+ with:
125
+ command: check
126
+ ```
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: pr-to-lint-rule
3
+ description: Convert a recurring PR review comment into an automated lint rule with tests and spec entry
4
+ disable-model-invocation: true
5
+ argument-hint: <description of recurring PR feedback>
6
+ ---
7
+
8
+ Convert a recurring PR review comment into an automated lint rule.
9
+
10
+ ## Arguments
11
+
12
+ $ARGUMENTS — A natural language description of the pattern to enforce. Examples:
13
+
14
+ - "we keep telling people not to import directly from antd, use our design system barrel file instead"
15
+ - "people forget to use our custom logger instead of console.log"
16
+ - "don't use unwrap() in production code, use expect() or proper error handling"
17
+ - "API route handlers must use the withAuth wrapper"
18
+
19
+ ## Instructions
20
+
21
+ You are generating an automated lint rule from a recurring code review pattern. Follow these steps:
22
+
23
+ ### Step 1: Detect the Project Language and Toolchain
24
+
25
+ Look at the repository to determine:
26
+
27
+ - **Primary language** (JS/TS, Python, Rust, Go, Ruby, etc.)
28
+ - **Linter in use** (ESLint, Ruff, Clippy, golangci-lint, RuboCop, etc.)
29
+ - **Testing framework** (Vitest, Jest, pytest, cargo test, etc.)
30
+ - **Existing custom rules** (to match conventions)
31
+
32
+ **If the language or linter cannot be confidently detected** (e.g. polyglot repo, no linter config, or multiple candidates), **ask the user** which language and linter to target before generating anything.
33
+
34
+ ### Step 2: Generate the Lint Rule
35
+
36
+ Based on the detected (or user-specified) language, generate the appropriate rule type:
37
+
38
+ **Read the linter-specific reference doc before generating.** Each doc covers existing plugins to check first, rule/lint anatomy, AST patterns, auto-fix safety, testing, and edge cases.
39
+
40
+ | Language | Linter | Reference doc |
41
+ | --------------------- | --------- | ----------------------------- |
42
+ | JavaScript/TypeScript | ESLint | `../linter-docs/eslint.md` |
43
+ | Python | Ruff | `../linter-docs/ruff.md` |
44
+ | Python | Pylint | `../linter-docs/pylint.md` |
45
+ | Ruby | RuboCop | `../linter-docs/rubocop.md` |
46
+ | Rust | Clippy | `../linter-docs/clippy.md` |
47
+ | CSS | Stylelint | `../linter-docs/stylelint.md` |
48
+
49
+ For all linters, follow this order:
50
+
51
+ 1. **Check existing plugins/rules first** — see the plugin table in the linter doc
52
+ 2. **Try built-in config options** — most linters have `no-restricted-*` or equivalent rules that handle one-off patterns without custom code
53
+ 3. **Only write a custom rule** when you need AST analysis, auto-fix, or configurable options beyond what exists
54
+
55
+ If a custom rule is needed, the reference doc provides: rule anatomy, AST node cheat sheet, auto-fix/suggest patterns, testing examples, and registration instructions.
56
+
57
+ #### For Go (go/analysis)
58
+
59
+ No linter doc yet. Generate an analyzer using `golang.org/x/tools/go/analysis` with `analysistest` tests.
60
+
61
+ #### For other languages
62
+
63
+ Generate the most idiomatic linting approach with test cases and integration instructions.
64
+
65
+ ### Step 3: Add to Instruction File
66
+
67
+ **If the project uses v2 specs** (has `CLAUDE.md.spec.ts`):
68
+
69
+ Add an `enforce()` rule to the spec file:
70
+
71
+ ```typescript
72
+ "<rule-id>": enforce("<linter>/<rule-name>", "<why>"),
73
+ ```
74
+
75
+ Then run `npx vigiles compile` to regenerate CLAUDE.md.
76
+
77
+ **If the project uses v1** (hand-written CLAUDE.md):
78
+
79
+ Append an annotation block:
80
+
81
+ ```markdown
82
+ ### <Rule title — imperative, concise>
83
+
84
+ **Enforced by:** `<linter>/<rule-name>`
85
+ **Why:** <One sentence explaining the architectural reason>
86
+ ```
87
+
88
+ ### Step 4: Present the Output
89
+
90
+ Show the user:
91
+
92
+ 1. All generated files with full contents
93
+ 2. Step-by-step integration instructions
94
+ 3. The spec rule or CLAUDE.md block to add
95
+ 4. How to verify it works (run the linter, expect it to catch a violation)
96
+
97
+ Ask the user if they want you to write the files and update the spec/CLAUDE.md.