Metadata-Version: 2.4
Name: instruction-lint
Version: 0.5.0
Summary: The ESLint of AI coding assistant instructions — audit, validate, and fix your Copilot skills, Cursor rules, and agent instruction files.
Project-URL: Homepage, https://github.com/Mr-afroverse/agentlint
Project-URL: Repository, https://github.com/Mr-afroverse/agentlint
Project-URL: Issues, https://github.com/Mr-afroverse/agentlint/issues
Project-URL: Changelog, https://github.com/Mr-afroverse/agentlint/blob/main/CHANGELOG.md
Author-email: agentlint <hello@agentlint.dev>
License: MIT
License-File: LICENSE
Keywords: agent,ai,cursor,github-copilot,instructions,lint,skills,windsurf
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Requires-Dist: click>=8.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: dev
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.4; extra == 'dev'
Requires-Dist: watchdog>=3.0; extra == 'dev'
Provides-Extra: watch
Requires-Dist: watchdog>=3.0; extra == 'watch'
Description-Content-Type: text/markdown

# agentlint

[![CI](https://github.com/Mr-afroverse/agentlint/actions/workflows/ci.yml/badge.svg)](https://github.com/Mr-afroverse/agentlint/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/instruction-lint)](https://pypi.org/project/instruction-lint/)
[![Python](https://img.shields.io/pypi/pyversions/instruction-lint)](https://pypi.org/project/instruction-lint/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Discord](https://img.shields.io/badge/Discord-Join%20Server-5865F2?logo=discord&logoColor=white)](https://discord.gg/f5jQD5mtYj)

**The ESLint of AI coding assistant instructions.**

Audit, validate, and keep your GitHub Copilot skills, Cursor rules, and agent instruction files consistent with your actual codebase.

---

## Why

AI coding assistants are only as good as their instructions. Instruction files drift silently:

- A skill references a file that was renamed six months ago.
- A threshold is hardcoded in a SKILL.md but the constant changed in source code.
- Two skills have overlapping triggers — the agent picks randomly.
- A skill was added to disk but never wired into the dispatch table.

None of this gets caught by `markdownlint` or `yamllint`. These are **codebase-aware problems** that require **codebase-aware checks**.

---

## Quick start

```bash
pip install instruction-lint
cd your-project
agentlint
```

Or as a **pre-commit hook** (recommended — runs on every commit, zero maintenance):

```yaml
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/Mr-afroverse/agentlint
    rev: v0.5.0
    hooks:
      - id: agentlint
```

---

## What it checks

| ID | Check | Severity | Zero-config |
|----|-------|----------|-----------|
| AL-D01 | Skill path in dispatch table → exists on disk | Error | ✓ |
| AL-D02 | Skill file on disk → referenced in dispatch table | Error | ✓ |
| AL-D03 | No circular references between instruction files | Error | ✓ |
| AL-D04 | Every required role has at least one SKILL file | Error | — |
| AL-F01 | Source-file paths in skill files → exist on disk | Warning | ✓ |
| AL-F02 | Internal anchor links (`#section`) → heading exists in same file | Warning | ✓ |
| AL-N01 | Threshold numbers → have a source pointer | Warning | ✓ |
| AL-N02 | Written percentage claims (`N percent`) → have a source pointer | Warning | ✓ |
| AL-T01 | Trigger descriptions → no significant overlap | Warning | ✓ |
| AL-P* | Forbidden patterns (built-in defaults + configurable) | Error | ✓ |
| AL-S01 | Credentials / secrets leaked in instruction files | Warning | ✓ |
| AL-INV01 | Negative existence claims about paths that actually exist on disk | Warning | ✓ |
| AL-Q01 | Vague instructions without actionable criteria | Warning | ✓ |
| AL-TOK01 | Instruction file exceeds configured token budget | Warning | — |
| AL-E01 | `.env` vs `.env.example` key parity | Error | — |
| AL-C01 | Cross-file value consistency groups | Error | — |
| AL-V01 | Documented numeric values → match source constants | Error | — |
| AL-G01 | Documentation values → match ground-truth JSON/YAML | Error | — |

AL-D01, AL-D02, AL-D03, AL-F01, AL-F02, AL-N01, AL-N02, AL-T01, AL-P*, AL-S01, AL-INV01, and AL-Q01 work out of the box. AL-D04, AL-TOK01, AL-E01, AL-C01, AL-V01, and AL-G01 are config-driven — add rules in `.agentlint.yml` to activate them.

---

## Supported assistants

| Assistant | Monolithic File | Modular Rules Directory | Format Detected |
|-----------|:---:|:---:|----------------|
| **GitHub Copilot** | ✓ | ✓ | `.github/copilot-instructions.md` + `.github/skills/**/SKILL.md` |
| **Cursor** | ✓ | ✓ | `.cursorrules` + `.cursor/rules/*.mdc` |
| **Windsurf** | ✓ | ✓ | `.windsurfrules` + `.windsurf/rules/*.md` |
| **Aider** | ✓ | ✓ | `.aider.conf.yml` + `.aider/rules/*.md` |
| **Continue.dev** | ✓ | ✓ | `.continuerules` + `.continue/rules/*.md` |
| **Claude Code** | ✓ | ✓ | `CLAUDE.md` + `.claude/agents/*.md` + `.claude/commands/*.md` |
| **Gemini CLI** | ✓ | ✓ | `GEMINI.md` + `.gemini/rules/*.md` |

Multiple formats can be active at once. `agentlint` auto-detects which are present.

---

## Example output

```
[agentlint] 2 error(s), 1 warning(s) across 8 file(s).  Grade: C

  .github/skills/gps-scorer/SKILL.md
    ✖ [AL-D02]:1  Skill `gps-scorer` is not referenced in the dispatch file.
      Fix → Add an entry for `.github/skills/gps-scorer/SKILL.md` in the dispatch table.

  .github/copilot-instructions.md
    ✖ [AL-D01]:14  Skill path not found on disk: `.github/skills/old-scorer/SKILL.md`
      Fix → Create the file or correct the path in the dispatch table.

  .github/skills/eudr-standards/SKILL.md
    ⚠ [AL-N01]:48  Threshold number without source pointer: `| GREEN ≥ 90% |`
      Fix → Add '(Source: constants.py)' or a regulatory article reference.

  docs/ARCHITECTURE.md
    ✖ [AL-V01]:31  Documented value `25` ≠ source `NotificationConfig.minimum_risk_score` = `30` in `your-project/agents/config.py`
      Fix → Update the value to `30` or correct the source.
```

> **AL-V01 source annotation format:** Add a constant path to your `(Source:)` annotation to enable value validation:
> ```
> Notification threshold: 30  (Source: <your_module>/agents/config.py:NotificationConfig.minimum_risk_score)
> ```
> agentlint resolves the file, extracts the constant's current value, and errors if they disagree. Plain `(Source: file.py)` annotations (without `:constant`) are handled by AL-N01 as before.

> **AL-V01 limitation:** extraction uses regex, not AST. Computed expressions (`X = BASE * 0.9`), class properties, and runtime-only values will not be extracted. Annotate simple scalar assignments (`THRESHOLD = 30`) for reliable results.

---

## Health scoring

Every run produces a **grade** (A–F) based on error and warning density across scanned files. Use it to track skill health over time in your CI dashboard.

```
Grade: A  → no violations, or up to 2 warnings per file
Grade: B  → up to 3–4 warnings per file, or 1 error in a large repo
Grade: C  → 6 warnings per file, or 1 error per small file
Grade: D  → 2 errors per file, or many warnings
Grade: F  → 3+ errors per file, or high combined density
```

The score formula is `100 − (errors_per_file × 20) − (warnings_per_file × 5)`. Grades A–F map to score bands 90 / 80 / 70 / 60 / below 60.

Pipe `--format json` into your CI annotation step:

```bash
agentlint --format json | tee agentlint-report.json
```

Or emit **SARIF** for GitHub Code Scanning:

```bash
agentlint --format sarif | tee agentlint.sarif
```

---

## Configuration

`agentlint` works with zero config. Add `.agentlint.yml` to extend or override defaults:

```yaml
# .agentlint.yml

# Add project-specific forbidden patterns (extends built-in defaults)
forbidden_patterns:
  - id: MY001
    pattern: '\b8-stage pipeline\b'
    reason: "Pipeline has 7 stages — this count will drift."
    fix: "Use '7-stage' and add a pointer to orchestrator.py."
    severity: error

# How many lines above a number agentlint looks for a source pointer (default 15)
number_source_lookback: 10

# Adjust trigger-overlap sensitivity (0.0–1.0, default 0.5)
trigger_overlap_threshold: 0.6

# Add extra source-root directories for file-reference resolution
source_roots: [".", "src", "backend"]

# Add project-specific source markers (extends built-in list)
source_markers:
  - "my_constants\\.py"
  - "REGULATORY_GATES"

# Disable specific checks
checks:
  trigger-overlap: false

# Re-classify individual check severity ("error" | "warning")
severity_overrides:
  AL-N01: error    # promote number-sourcing from warning to error
  AL-T01: warning  # demote trigger-overlap from default to warning

# Fail the run when warnings are present (default: only errors fail)
fail_on_warnings: true

# Paths to skip (substring match on the file path).
# Each entry is a plain string. An optional `reason` field can be added
# for self-documentation — it is recorded but not used in output.
ignore_paths:
  - "archive/"
  - path: "docs/health/"
    reason: "health-check docs are generated and always stale"

# ── v0.2 features ────────────────────────────────────────────

# Glob patterns for extra documentation files to scan with AL-P* and AL-F01
extra_paths:
  - "docs/**/*.md"
  - "*.md"

# .env vs .env.example key parity (AL-E01)
config_parity:
  - source: ".env"
    template: ".env.example"
    severity: error

# AL-E01 parses both `KEY=value` and `export KEY=value` (bash-style) formats.
# Lines starting with `#` are treated as comments and ignored in both files.

# Cross-file value consistency groups (AL-C01)
consistency_groups:
  - id: test-count
    pattern: '\b(\d+)\s+passed'
    files: ["README.md", "CONTRIBUTING.md", "docs/RELEASE.md"]
    severity: error

# ── v0.3 features ────────────────────────────────────────────

# Validate documented numbers against their source constants (AL-V01)
# Works automatically on any file with (Source: file.py:CONSTANT) annotations.
# No config required — the annotation format is the trigger.

# Ground-truth file checks (AL-G01)
# value_match mode: scalar from JSON/YAML must match doc pattern
ground_truth_files:
  - id: TEST-COUNT
    json_file: "logs/test_results.json"  # written by CI
    json_path: "passed"
    doc_pattern: '\b(\d+) passed'
    files: ["README.md", "DEPLOYMENT_GUIDE.md"]
    reason: "Test count in docs must match pytest output"
    severity: error

  # no_stale_refs mode: list from JSON must include every referenced ID
  - id: ACTIVE-SOURCES
    json_file: "config/sources_production.json"
    json_path: "sources[*].id"
    mode: "no_stale_refs"
    ref_pattern: '\b([\w-]+-(?:news|feed|monitor))\b'
    files: ["**/*.md"]
    reason: "Source IDs in docs must exist in sources_production.json"
    severity: warning

# Opt-in: check filenames inside ASCII tree diagrams for AL-F01
# Only scans bare prose trees (├──/└── lines) — code fences are skipped by default.
tree_diagram_paths: true

# Opt-in: also scan tree diagrams inside ``` code fences (requires tree_diagram_paths: true)
tree_diagram_fenced: true

# AL-TOK01: warn when a SKILL or DISPATCH file exceeds this estimated token count.
# Token count is approximated as len(content) / 4 (OpenAI heuristic). 0 = disabled.
token_budget: 2000
```

### Replace built-in forbidden patterns entirely

```yaml
forbidden_patterns_mode: replace
forbidden_patterns:
  - id: PROJ001
    pattern: '\blegacy_mode\b'
    reason: "legacy_mode was removed in v2."
    fix: "Delete the flag entirely."
    severity: error
```

---

## Layer 2: Behavioral testing

The checks above are **static** — they analyse files without running the agent. To test whether your skills actually fire correctly and produce sound guidance, use the included behavioral test sheet:

```bash
agentlint --init   # copies SKILL_HEALTH_CHECK.md into .github/skills/
```

The template contains 10 ready-to-run prompts with explicit PASS/FAIL criteria covering trigger accuracy, over-firing prevention, source-pointer discipline, multi-skill invocation, and more.

---

## GitHub Action

```yaml
# .github/workflows/agentlint.yml
name: agentlint
on: [push, pull_request]

jobs:
  agentlint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: Mr-afroverse/agentlint@v0.5.0
```

### Action inputs

| Input | Default | Description |
|-------|---------|-------------|
| `path` | `.` | Directory to scan |
| `format` | `text` | Output format — `text`, `json`, `sarif`, `badge`, or `html` |
| `adapter` | `auto` | Force adapter — `copilot`, `cursor`, `windsurf`, `aider`, `continue`, `claudecode`, `gemini`, or `auto` |
| `fail-on-warnings` | `false` | Exit 1 when warnings are present |

Example — fail the build on warnings:

```yaml
- uses: Mr-afroverse/agentlint@v0.5.0
  with:
    fail-on-warnings: true
```

Example — emit SARIF for [GitHub Code Scanning](https://docs.github.com/en/code-security/code-scanning):

```yaml
- name: Install agentlint
  run: pip install instruction-lint
- name: Run agentlint (SARIF)
  run: agentlint --format sarif > agentlint.sarif
- name: Upload SARIF
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: agentlint.sarif
```

Example — write an HTML report as a build artifact:

```yaml
- name: Install agentlint
  run: pip install instruction-lint
- name: Run agentlint (HTML)
  run: agentlint --format html || true   # don't fail; upload the report regardless
- name: Upload HTML report
  uses: actions/upload-artifact@v4
  with:
    name: agentlint-report
    path: agentlint-report.html
```

---

## SARIF → GitHub inline PR annotations

agentlint's `--format sarif` output is SARIF 2.1.0. GitHub Code Scanning converts it to inline annotations on pull requests automatically once the SARIF file is uploaded.

### Option A — GitHub Code Scanning (recommended)

Requires GitHub Advanced Security (free for public repos, licensed for private).

```yaml
# .github/workflows/agentlint.yml
name: agentlint
on: [push, pull_request]

jobs:
  agentlint:
    runs-on: ubuntu-latest
    permissions:
      security-events: write   # required for upload-sarif
      contents: read
    steps:
      - uses: actions/checkout@v4
      - name: Install agentlint
        run: pip install instruction-lint
      - name: Run agentlint
        run: agentlint --format sarif > agentlint.sarif || true
      - name: Upload SARIF to GitHub Code Scanning
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: agentlint.sarif
```

Violations appear as inline annotations on the **Files changed** tab of every PR, with the check ID, message, and fix hint visible without leaving the review UI.

### Option B — Lightweight GitHub Actions annotations (no GHAS required)

For private repos without GitHub Advanced Security, pipe the JSON output through a small script to emit [workflow commands](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions).

```yaml
- name: Install agentlint
  run: pip install instruction-lint
- name: Run agentlint and emit annotations
  run: |
    agentlint --format json > agentlint-result.json || true
    python - <<'EOF'
    import json, sys
    data = json.load(open("agentlint-result.json"))
    level_map = {"error": "error", "warning": "warning", "info": "notice"}
    for v in data["violations"]:
        level = level_map.get(v["severity"], "warning")
        file  = v["file"]
        line  = v.get("line") or 1
        msg   = v["message"].replace("%", "%25").replace("\n", "%0A")
        print(f"::{level} file={file},line={line}::{v['check_id']} — {msg}")
    if data["errors"]:
        sys.exit(1)
    EOF
```

### Option C — reviewdog

If you already use [reviewdog](https://github.com/reviewdog/reviewdog) in your pipeline:

```yaml
- name: Install tools
  run: |
    pip install instruction-lint
    curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b /usr/local/bin
- name: Run agentlint via reviewdog
  env:
    REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  run: |
    agentlint --format sarif > agentlint.sarif || true
    reviewdog -f=sarif -name=agentlint -reporter=github-pr-review < agentlint.sarif
```

reviewdog posts each violation as a PR review comment on the exact changed line.

### Baseline suppression in CI

For repos with pre-existing violations, capture a baseline once and only fail on new regressions:

```yaml
# Step 1 (run once, commit .agentlint-baseline.json to the repo)
# agentlint --update-baseline .agentlint-baseline.json

# Step 2 (in CI — only new violations fail the build)
- name: Run agentlint with baseline
  run: agentlint --baseline .agentlint-baseline.json
```

---

## CLI reference

```
Usage: agentlint [OPTIONS] [PATH]

  agentlint — audit AI coding assistant instruction files.

Options:
  -V, --version                   Show the version and exit.
  --format [text|json|sarif|badge|html]
                                  Output format (default: text).
                                  'badge' writes agentlint-badge.svg to disk.
                                  'html' writes agentlint-report.html to disk.
  --config PATH                   Path to .agentlint.yml config file.
  --adapter [copilot|cursor|windsurf|aider|continue|claudecode|gemini|auto]
                                  Force a specific adapter (default: auto-
                                  detect).
  --fail-on-warnings              Exit 1 when warnings are present (overrides
                                  config).
  --init                          Generate .agentlint.yml and copy
                                  SKILL_HEALTH_CHECK.md into .github/skills/.
  --baseline PATH                 Suppress violations already recorded in
                                  PATH. Reports only new regressions.
  --update-baseline PATH          Snapshot current violations to PATH and
                                  exit 0. Commit the file to silence known
                                  issues in CI.
  --watch                         Re-run on file changes. Requires: pip
                                  install 'instruction-lint[watch]'.
  -h, --help                      Show this message and exit.
```

---

## Development

```bash
git clone https://github.com/Mr-afroverse/agentlint
cd agentlint
pip install -e ".[dev]"  # or: pip install instruction-lint
pytest
```

---

## License

MIT — see [LICENSE](LICENSE).
