| name | setup-lint |
| description | Configure per-project linting for the code-lint plugin. Detects languages and linters, interviews the user, writes config. Use when user says "/setup-lint". |
| tools | Bash, Read, Write, Edit, AskUserQuestion |
Setup Lint
Interactive setup wizard for the code-lint plugin. Detects project languages and linters, asks what to enable, writes per-project config.
Step 1: Detect Project
Run the detection script to discover languages, linter configs, and available binaries:
bash "${CLAUDE_PLUGIN_ROOT}/skills/setup-lint/scripts/detect-project.sh" "${CLAUDE_PROJECT_DIR:-$(pwd)}"
If CLAUDE_PLUGIN_ROOT is not set, find the script relative to this SKILL.md file (./scripts/detect-project.sh).
Parse the JSON output — it describes which languages are present and which linters are available/configured.
Step 2: Interview User
For each detected language, use AskUserQuestion to confirm:
- Language enablement: "I found Ruby files (Gemfile, .rubocop.yml). Enable Ruby linting?"
- Linter selection: "rubocop and reek are available. Which linters do you want to enable?"
- Autofix: "Auto-fix style issues before checking? (rubocop -A will run before rubocop lint)"
- Exclusions: "Any paths to exclude beyond defaults? (Current defaults: test/, spec/, db/schema.rb)"
Defaults to suggest
Ruby:
- Enable: rubocop, reek
- Exclude:
test/, spec/, db/schema.rb
- Autofix: rubocop only
JavaScript/TypeScript:
- Enable: eslint (or biome if configured)
- Exclude:
node_modules/, dist/, build/
- Autofix: eslint --fix (or biome --write)
Python:
- Enable: ruff (preferred), flake8 as fallback
- Exclude:
.venv/, __pycache__/, migrations/
- Autofix: ruff --fix
Go:
- Enable: golangci-lint
- Exclude:
vendor/
Rust:
- Enable: clippy (whole-project only — won't run in hooks, only in /lint)
Markdown:
- Enable: markdownlint
- Exclude:
node_modules/, vendor/
- Autofix: markdownlint --fix
HTML:
- Enable: prettier (for check + format)
- Exclude:
node_modules/, dist/, build/
- Autofix: prettier --write
Shell:
- Enable: shellcheck
- Exclude:
node_modules/, vendor/
Tool linters (language-agnostic)
After the per-language interview, check for tool linters detected in the JSON output (tool_linters key).
Semgrep (if detected as available):
- Enable Semgrep? "Semgrep (language-agnostic SAST scanner) is available. Enable it?"
- Rulesets: "Which rulesets? (default:
p/default). Other options: p/security-audit, p/owasp-top-ten, or a local .semgrep.yml if present."
- Hook mode: "Run Semgrep in the PostToolUse hook (per-file) or only via
/lint? (default: /lint only — recommended, as per-file scanning is slow)"
If Semgrep is not installed, note it and suggest: pip install semgrep or brew install semgrep.
Hook settings (ask once)
- Stop on first failure: false (default) — run all linters and aggregate
- Autofix before lint: true (default) — run autofix commands before checking
- Skip generated files: true (default) — skip files with "auto-generated" headers
Step 3: Write Config
Compute the config path:
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
PROJECT_HASH=$(echo "$PROJECT_DIR" | tr '/' '-' | sed 's/^-//')
CONFIG_DIR="$HOME/.claude/code-lint/$PROJECT_HASH"
mkdir -p "$CONFIG_DIR"
Write config to $CONFIG_DIR/config.json using the schema below. Only include languages/linters that the user enabled.
Config Schema
{
"version": 1,
"project_dir": "/absolute/path/to/project",
"languages": {
"<language>": {
"enabled": true,
"linters": {
"<linter>": {
"enabled": true,
"command": "the lint command",
"autofix_command": "the autofix command (optional)",
"file_patterns": ["regex patterns for file extensions"],
"exclude_patterns": ["regex patterns for paths to skip"],
"timeout": 30,
"whole_project_only": false
}
}
}
},
"tool_linters": {
"semgrep": {
"enabled": true,
"command": "semgrep scan --quiet",
"autofix_command": "semgrep scan --autofix --quiet",
"rulesets": ["p/default"],
"exclude_patterns": ["node_modules/", "vendor/", ".venv/", "dist/", "build/"],
"timeout": 120,
"hook_mode": "off"
}
},
"hook_settings": {
"stop_on_first_failure": false,
"autofix_before_lint": true,
"skip_generated_files": true
}
}
tool_linters fields
- enabled: Whether the tool linter is active
- command: The lint command (file path appended in hook mode, runs on
. in /lint mode)
- autofix_command: Optional autofix variant
- rulesets: Semgrep-specific — list of ruleset IDs (e.g.
p/default, p/security-audit)
- exclude_patterns: Path prefixes to skip (matched as substrings in hook mode; passed as
--exclude glob flags in /lint mode)
- timeout: Seconds before the command is killed (default: 120)
- hook_mode:
"off" (only runs via /lint) or "per-file" (runs in PostToolUse hook on each edited file)
File patterns by language
- Ruby:
["\\.rb$", "\\.rake$", "\\.gemspec$"]
- JavaScript/TypeScript:
["\\.js$", "\\.jsx$", "\\.ts$", "\\.tsx$", "\\.mjs$", "\\.cjs$"]
- Python:
["\\.py$"]
- Go:
["\\.go$"]
- Rust:
["\\.rs$"]
- Markdown:
["\\.md$", "\\.mdx$"]
- HTML:
["\\.html$", "\\.htm$"] (prettier also: ["\\.html$", "\\.htm$", "\\.css$"])
- Shell:
["\\.sh$", "\\.bash$", "\\.zsh$"]
Step 4: Test Linters
For each enabled linter, run a quick test on an existing file to verify it works:
SAMPLE=$(find "$PROJECT_DIR" -name "*.rb" -not -path "*/vendor/*" -not -path "*/node_modules/*" | head -1)
cd "$PROJECT_DIR" && bundle exec rubocop "$SAMPLE"
Report results: which linters passed, which failed (with error output). If a linter fails, offer to disable it or help troubleshoot.
Step 5: Summary
Print a summary of what was configured:
## Lint Setup Complete
**Project:** /path/to/project
**Config:** ~/.claude/code-lint/<hash>/config.json
**Enabled linters:**
- Ruby: rubocop (autofix), reek
- Shell: shellcheck
**Hook behavior:**
- Runs on Edit/Write of matching files
- Auto-fixes style issues before checking
- Skips generated files
**Usage:**
- Edit any .rb file — linters run automatically via hook
- `/lint` — Run all linters across the full project
- `/lint ruby` — Run only Ruby linters
- `/lint --fix` — Run autofix on all files
Behavior
- Run through steps sequentially
- If config already exists, show current config and ask if user wants to reconfigure
- Be concise in the interview — group related questions when possible
- If only one language is detected, skip the "enable language?" question (assume yes)
- If a linter binary is not found, note it and suggest how to install
- After setup, suggest editing a file to test the hook