2026-10-15 11:04:30 +02:00
2026-10-15 11:04:38 +02:00
2026-10-15 11:04:36 +02:00
2026-10-15 11:04:33 +02:00
2026-10-15 11:04:48 +02:00

prompt-lint

A static linter for LLM system prompts and prompt templates. Zero dependencies, stdlib only.

Prompts are code. They have bugs — contradictions, missing output contracts, vague quantifiers, injection-prone interpolation. prompt-lint catches the mechanical ones before you burn tokens finding them at runtime.

Rules

rule severity catches
injection-prone error {user} / {input} / {document} interpolated raw into the prompt
no-output-contract warning no statement of expected format (JSON, list, schema…)
contradiction warning always + never, concise + detailed, etc.
over-long warning prompt > 1500 words (later instructions get diluted)
missing-delimiters warning placeholders present but nothing wraps them
no-role info no "You are a…" / "Act as…" anchor
negation-heavy info ≥5 "do not / never / avoid" instructions
ambiguous-quantifier info "a few", "several", "roughly", "maybe"
trailing-whitespace info copy-paste damage

Rules are plain functions registered with @rule(...) — adding one is ~10 lines.

Usage

python prompt_lint.py system_prompt.txt
cat system_prompt.txt | python prompt_lint.py
python prompt_lint.py prompt.txt --json
python prompt_lint.py prompt.txt --only injection-prone,contradiction
python prompt_lint.py prompt.txt --strict   # exit 1 on any error/warning (for CI)

Example output:

prompt.txt:4: ERROR [injection-prone] Untrusted value interpolated directly into the prompt.
    hint: Wrap it in delimiters and instruct the model to treat it as data.
prompt.txt:1: WARNING [no-output-contract] No explicit output contract found.
    hint: Add e.g. 'Respond with a JSON object: {"key": ...}'.
prompt.txt:9: INFO [ambiguous-quantifier] Vague quantifier/hedge in an instruction.
    hint: Prefer a concrete number or an explicit condition.

Clean prompt → prints clean.

Library

import prompt_lint as pl

for f in pl.lint(open("system_prompt.txt").read()):
    print(f.severity, f.rule, f.line, f.message)

# only specific rules
pl.lint(text, only=["injection-prone"])

# structured
[f.as_dict() for f in pl.lint(text)]

Findings sort by severity (error → warning → info) then line number.

Writing a rule

@pl.rule("no-examples", "Prompt gives no few-shot examples")
def r_no_examples(lines):
    text = "\n".join(lines).lower()
    if "example" in text or "e.g." in text:
        return []
    return [pl.Finding("no-examples", "info", 1, "No examples given.",
                       "Add one input/output pair to pin the format.")]

CI

- run: python prompt_lint.py prompts/system.txt --strict

Tests

python -m unittest discover -s tests -v

Limitations

Heuristic, not semantic. It cannot tell whether your instructions are good — only whether they're self-consistent and well-formed. It will not catch a rule that is wrong but confidently stated.

License

MIT.

Description
Static linter for LLM system prompts and prompt templates. Flags contradictions, missing output contracts, ambiguous instructions, injection-prone phrasing, and unbounded length. Stdlib-only Python with a rule registry and JSON output.
Readme MIT 38 KiB
Languages
Python 100%