strengthen — Claude Code skill: turn guidance() into enforce()
Scans vigiles specs and swaps guidance() rules for enforce(), matching each one to a linter rule that already exists. Useful when written conventions should fail CI on their own.
npx skills add zernie/vigiles --skill strengthen -a claude-codeWhat strengthen does
The skill walks every .spec.ts file in the project and looks for guidance() rules. It runs npx vigiles generate types, reads .vigiles/generated.d.ts, and checks the rule text against the rules already enabled for ESLint, Ruff, Clippy, Pylint, RuboCop or Stylelint. A hit on the rule name, the meaning or the description is a direct swap — no config edit involved.
When nothing matches on the fast path, the skill reads the linter reference docs and looks for workarounds: a rule from an installed but disabled plugin, a plugin that is not installed yet, or a no-restricted-* pattern. Blocking imports from internal modules maps to no-restricted-imports; banning console.log maps to no-restricted-syntax. Those edits touch linter config, so the skill shows the exact file and section and waits for your call.
Before it starts, the skill asks which mode you want. Auto applies the safe swaps and commits them, leaving anything risky in a summary; interactive presents each suggestion one at a time. Interactive is the default. The split is about cost, not strictness: a free and reversible swap happens on its own, while a plugin install or a config change is yours to approve. Rules with no linter equivalent stay as guidance.
What it helps with
- match linter rules to prose conventions in a spec
- swap guidance() for enforce() without touching linter config
- express a ban on an import or a call through no-restricted-*
- confirm the spec still compiles after each replacement
- separate safe swaps from ones that need a plugin install
Example prompts for the agent
- scan the specs and tell me which guidance() rules I can turn into enforce()
- harden the rules in my CLAUDE.md spec where the linter rule is already on
- show me what I'd need to install to cover the remaining guidance() rules
How to install strengthen
1Skills CLI (Vercel)
npx skills add zernie/vigiles --skill strengthen -a claude-code2Manually: put the files in place
~/.claude/skills/strengthen/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/strengthen/
1Skills CLI (Vercel)
npx skills add zernie/vigiles --skill strengthen -a codex2Manually: put the files in place
.agents/skills/strengthen/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add zernie/vigiles --skill strengthen -a cursor2Manually: put the files in place
.cursor/skills/strengthen/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install zernie/vigiles strengthen2Manually: put the files in place
.github/skills/strengthen/SKILL.md1Skills CLI (Vercel)
npx skills add zernie/vigiles --skill strengthen -a gemini-cli2Platform CLI
gemini skills install https://github.com/zernie/vigiles --path skills/strengthenManually: put the files in place
.devin/skills/strengthen/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add zernie/vigiles --skill strengthen -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about strengthen
How do I install strengthen in Claude?
Run npx skills add zernie/vigiles --skill strengthen -a claude-code. The skill file lands at ~/.claude/skills/strengthen/SKILL.md.
What does strengthen do?
It scans guidance() rules in vigiles specs and finds linter rules that back them, so they can become enforce(). It covers ESLint, Ruff, Clippy, Pylint, RuboCop and Stylelint.
How is this different from regular linting?
It does not hunt for or fix lint errors. It only converts written spec rules into enforceable ones, and leaves rules with no linter equivalent as guidance.
Source text
SKILL.md · MIT
---
name: strengthen
allowed-tools: Read, Edit, Write, Glob, Grep, Bash
description: Upgrade a vigiles spec's guidance() rules to enforce() — scan the guidance rules in a CLAUDE.md/AGENTS.md spec and find existing linter rules (ESLint, Ruff, Clippy, Pylint, RuboCop, Stylelint) that back them. Use when asked to strengthen, harden, or make vigiles rules enforceable; NOT for general linting or fixing lint errors.
---
Scan spec files for `guidance()` rules and suggest `enforce()` replacements backed by real linter rules.
## Principle: auto the free wins, nudge for the costs
The dividing line is **cost, not strictness**. A `guidance()` → `enforce()` swap where the linter rule **already exists and is enabled** is a pure win — free, reversible, no false-positive risk — so apply it (Tier 1 below). Anything that **costs** something — editing linter config, installing a plugin, or a change that could fail a clean CI — is the user's call: **present it with the tradeoff spelled out and let them choose** (Tiers 2–4). Never silently edit config, install a dependency, or escalate the repo into strict gating. This is the `init` enforcement model (structural = on by default, workflow/strict = opt-in) applied at edit time.
## Instructions
### Step 0: Choose Mode
Ask the user:
> **Auto or interactive?**
>
> - **Auto** — I'll apply all safe changes (direct replacements where the rule is already enabled), commit, and show you the diff. Risky changes (require config edits or plugin installs) go in a summary for you to review.
> - **Interactive** — I'll present each suggestion and you pick which ones to apply.
Default to interactive if the user doesn't specify.
### Step 1: Discover What's Installed
Run `npx vigiles generate types` to get the full list of enabled linter rules in the project. Read `.vigiles/generated.d.ts` to see every rule available across all detected linters.
Note which linter prefixes appear in the generated types (e.g., `EslintRule`, `RuffRule`). You'll only need reference docs for detected linters.
### Step 2: Find All Guidance Rules
Find all `.spec.ts` files in the project (`**/*.md.spec.ts`). For each file, identify every `guidance()` rule.
### Step 3: Match Against Generated Types (Fast Path)
For each guidance rule, check if an enabled rule in `.vigiles/generated.d.ts` directly matches. This is the fast, deterministic path — no doc reading needed.
Look for:
- **Exact rule name in text** — guidance says "no-console" and `no-console` is in EslintRule
- **Semantic match** — guidance says "don't use console.log" and `no-console` is available
- **Rule description match** — guidance says "unused variables" and `no-unused-vars` or `@typescript-eslint/no-unused-vars` is available
If a match is found and the rule is in the generated types (meaning it's already enabled), this is a **direct replacement** — no config changes needed.
### Step 4: Read Linter Docs (Slow Path)
For guidance rules that didn't match in Step 3, read the linter reference docs for the project's detected linters:
- ESLint → `../linter-docs/eslint.md`
- Stylelint → `../linter-docs/stylelint.md`
- Ruff → `../linter-docs/ruff.md`
- Pylint → `../linter-docs/pylint.md`
- RuboCop → `../linter-docs/rubocop.md`
- Clippy → `../linter-docs/clippy.md`
**Only read docs for linters the project actually uses.** Skip docs for linters with no rules in generated types.
Check the plugin tables and decision matrices. The guidance text may describe a pattern covered by:
- A plugin rule that's installed but not enabled
- A plugin that's not installed yet
- A `no-restricted-*` config pattern (see Step 4b)
### Step 4b: `no-restricted-*` Patterns
Many guidance rules can be enforced via built-in linter config without a custom rule. This is the most common strengthen pattern — "don't do X" maps to a restriction config.
**ESLint:**
```js
// "Don't import from internal modules"
"no-restricted-imports": ["error", {
patterns: [{ group: ["src/internal/*"], message: "Use the public API." }]
}],
// "Don't call console.log"
"no-restricted-syntax": ["error", {
selector: 'CallExpression[callee.object.name="console"]',
message: "Use the project logger."
}],
// "Don't use moment.js"
"no-restricted-imports": ["error", {
paths: [{ name: "moment", message: "Use dayjs instead." }]
}],
```
**Ruff:**
```toml
# "Don't use os.system"
[tool.ruff.lint.flake8-tidy-imports.banned-api]
"os.system".msg = "Use subprocess.run instead."
```
**RuboCop:**
```yaml
# "Don't use puts in production" — if Rails/Output doesn't fit
Custom/NoPuts:
Enabled: true
```
When suggesting a `no-restricted-*` change:
1. Show the exact config edit needed (which file, which section)
2. Show the `enforce()` rule that references it
3. Note that this changes linter config, not just the spec
### Step 5: Present Suggestions
Group the output into tiers:
**Tier 1: Direct replacements** (rule already enabled — zero risk)
```typescript
// Before
"no-console": guidance("Use structured logger instead of console.log"),
// After
"no-console": enforce("eslint/no-console", "Use structured logger instead of console.log"),
```
**Tier 2: Config-backed** (rule exists but needs config options)
```typescript
// Spec change:
"no-moment": enforce("eslint/no-restricted-imports", "Use dayjs instead of moment."),
// Config change needed (eslint.config.mjs):
"no-restricted-imports": ["error", {
paths: [{ name: "moment", message: "Use dayjs instead." }]
}],
```
**Tier 3: Plugin install needed**
```
"cognitive-complexity": guidance("Keep functions simple")
→ Install eslint-plugin-sonarjs, enable sonarjs/cognitive-complexity
→ enforce("eslint/sonarjs/cognitive-complexity", "Keep functions simple")
```
**Tier 4: No match** (stays as guidance — candidate for a future rule-synthesis skill)
```
"research-first": guidance("Google unfamiliar APIs first.")
→ No linter rule can enforce this. Stays as guidance for now.
→ (Custom-rule synthesis is planned but not yet shipped.)
```
### Step 6: Apply Changes
**In auto mode:**
1. Apply all Tier 1 changes (edit spec files, replace `guidance()` with `enforce()`)
2. Run `npm run build && npx vigiles compile` to verify each change compiles
3. If any compilation fails, revert that specific change and report the error
4. Commit all successful changes
5. Present Tier 2-4 as a summary for the user to review
**In interactive mode:**
1. Present all tiers
2. Ask the user which suggestions to apply
3. For approved Tier 2 changes: edit the linter config, then edit the spec
4. Run `npm run build && npx vigiles compile` to verify
5. If compilation fails, report the error and revert
**For Tier 4 (no match):** Tell the user these rules stay as guidance — custom-rule synthesis is a planned skill, not yet available.
© zernie · MIT · zernie/vigiles