main
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.
Languages
Python
100%