lgtm-hq/ai-skills

lintro-add

Guide for adding new linting/formatting tools to lintro. Use when implementing shellcheck, shfmt, sqlfluff, taplo, semgrep, gitleaks, or any new tool plugin.

Installation

$ npx skills add lgtm-hq/ai-skills --skill lintro-add

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from lgtm-hq/ai-skills · top by installs.

npx skills add lgtm-hq/ai-skills

Browse all from lgtm-hq/ai-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

License LICENSE
Default branch main
Open issues 9
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 8,133 B
  • docs SUMMARY.md 175 B

History

  1. First recorded snapshot · 27 installs

SKILL.md

Adding a New Tool to Lintro

Lintro is a unified CLI for code linting/formatting with a plugin architecture: tools are defined in lintro/tools/definitions/<tool>.py (via @registertool), parsed by lintro/parsers/<tool>/, tested in tests/unit/, with sample violation files in testsamples/tools/.

Related Skills

  • stand-py: Python coding standards (type hints, docstrings, trailing commas)
  • test: pytest best practices (no classes, use fixtures, parametrize)
  • commit: semantic commit format when committing changes

How to Implement: Copy a Reference, Don't Write From Scratch

Do NOT write plugin/parser/test code from a template. Pick the closest existing implementation, read all of its files (definition, issue class, parser, parser init.py, sample violation file, parser tests, plugin tests), and mirror that structure exactly for the new tool:

  • Simple tool (no fix): lintro/tools/definitions/actionlint.py, hadolint.py
  • Tool with fix support: lintro/tools/definitions/ruff.py, black.py
  • Security scanner: lintro/tools/definitions/bandit.py, semgrep.py
  • Shell tools: lintro/tools/definitions/shellcheck.py, shfmt.py

The parser lives in lintro/parsers/<reference-tool>/ and its tests in tests/unit/parsers/ and tests/unit/tools/<reference-tool>/ — copy the pattern from the same reference tool so imports, mocking, and naming stay consistent.

Quick Reference

New files (paths)

lintro/parsers/<tool>/__init__.py
lintro/parsers/<tool>/<tool>_issue.py
lintro/parsers/<tool>/<tool>_parser.py
lintro/tools/definitions/<tool>.py
test_samples/tools/<category>/<tool>/<tool>_violations.<ext>
tests/unit/parsers/test_<tool>_parser.py
tests/unit/tools/<tool>/__init__.py
tests/unit/tools/<tool>/test_<tool>_plugin.py

Updated files (paths)

lintro/enums/tool_name.py              # Add to ToolName enum (alphabetical)
lintro/tools/core/version_parsing.py   # Add to TOOLS_WITH_SIMPLE_VERSION_PATTERN
lintro/tools/core/version_checking.py  # Add install hints in get_install_hints()
lintro/_tool_versions.py               # Add version (binary/cargo/rustup tools only)
lintro/tools/manifest.src.json         # Add tool entry (NO version key; generator renders manifest.json)
lintro/cli_utils/commands/doctor.py    # Add to TOOL_COMMANDS for health check
package.json                           # Pin the npm package (the version source for npm tools)
renovate.json                          # Add a custom manager for _tool_versions.py (binary tools only)
pyproject.toml                         # Add parser package + [tool.lintro.versions] entry
Dockerfile                             # Add to verification steps (root AND non-root blocks)
Dockerfile.tools                       # Add to verification step (tool --version)
scripts/utils/install-tools.sh         # Add installation command (external tools)
scripts/ci/homebrew/templates/lintro.rb.template  # Add depends_on + update caveats (if Homebrew-installable)

For every updated file, find an existing tool's entry in that file and add the new tool the same way (alphabetical order where the file is ordered).

Version Consistency (CRITICAL for external tools)

For external tools (not bundled Python packages), versions must be consistent across:

  1. lintro/toolversions.py — source of truth for binary/cargo/rustup

tools (npm tools read package.json, bundled Python tools read pyproject.toml)

  1. lintro/tools/manifest.src.json — the hand-authored entry carries

install metadata only, never a version key; the build-time generator renders lintro/tools/manifest.json (gitignored, not committed) with the version injected from the source above

  1. Plugin min_version — in the tool definition, should match or be <=

the version source

  1. renovate.json — a custom regex manager updating toolversions.py

(binary tools only; the rendered manifest follows via the generator)

There is no committed generated copy to drift (py-lintro #2176): the derived artifacts are generated at package build time. Run just generate in the py-lintro checkout after editing a version source to refresh the gitignored working-tree copies.

Homebrew Formula (for Homebrew-installable tools)

  • Add depends_on "<tool>" to scripts/ci/homebrew/templates/lintro.rb.template

and list the tool in the caveats under the appropriate category.

  • Bundled Python tools (ruff, black, mypy, bandit, yamllint) are excluded from the

Homebrew venv via generateresources.py --exclude; they install as separate Homebrew formulae and are discovered via PATH (shutil.which), NOT python -m. PythonBundledBuilder in commandbuilders.py handles this automatically.

ToolType Options

ToolType.LINTER          # Code quality checker
ToolType.FORMATTER       # Code formatter
ToolType.TYPE_CHECKER    # Type checking (mypy)
ToolType.DOCUMENTATION   # Doc checker (darglint)
ToolType.SECURITY        # Security scanner (bandit, semgrep, gitleaks)
ToolType.INFRASTRUCTURE  # IaC linter (hadolint, actionlint)
ToolType.TEST_RUNNER     # Test framework (pytest)

Can be combined: ToolType.LINTER | ToolType.FORMATTER

Common Gotchas

  1. Version command variations: some tools use version instead of --version

(e.g., gitleaks version). Check the tool's CLI.

  1. ToolResult invariant for fix operations:

initialissuescount = fixedissuescount + remainingissuescount.

  1. Test mocking: mock the version check with

patch("lintro.plugins.executionpreparation.verifytoolversion", returnvalue=None) and subprocess calls with patch.object(plugin, "runsubprocess", ...) — copy the patterns from a reference tool's plugin tests.

  1. File discovery: prepareexecution() handles filtering by file_patterns;

use ctx.rel_files for the filtered list.

  1. Subprocess safety: always use list args, never shell=True; add

# nosec B404 on the subprocess import.

  1. Return early: if ctx.shouldskip is True, return ctx.earlyresult.
  2. Parser function naming: must be

parse<tool>output(output: str | None) -> list[<Tool>Issue].

  1. Issue class: must inherit from BaseIssue; use DISPLAYFIELDMAP for

custom field name mappings.

Deprecated Patterns to Avoid

  • Do NOT create tool-specific formatters (use the unified formatter)
  • Do NOT modify lintro/tools/tool_enum.py (deleted — registry is automatic)
  • Do NOT modify lintro/tools/core/tool_base.py (deleted — use BaseToolPlugin)

Verification Checklist

  • uv run lintro tools shows the new tool
  • uv run lintro check --tools <tool> . runs without error — single-tool runs are

normally banned by the lint skill; this is the explicit sanctioned exception for verifying a tool under development. Still finish with a full uv run lintro chk.

  • uv run lintro doctor shows the tool with correct version (no "No cmd defined")
  • Tool detects violations in the sample file
  • Parser unit tests pass: pytest tests/unit/parsers/test<tool>parser.py -v
  • Plugin unit tests pass: pytest tests/unit/tools/<tool>/ -v
  • Coverage >80% on new code
  • No linting errors: uv run lintro fmt && uv run lintro chk
  • just generate runs cleanly (derived artifacts are gitignored, not committed)
  • Tool added to Dockerfile (root and non-root blocks) and Dockerfile.tools
  • Tool added to install-tools.sh (external tools only)
  • Tool added to lintro/tools/manifest.src.json (no version key)
  • Renovate manager added for toolversions.py (binary tools only)
  • Homebrew template updated with depends_on + caveats (if Homebrew-installable)
  • Docker image builds: docker build -t py-lintro:test .

Use the lintro-verify skill for the full post-implementation review.