Comments for anything. In plain text. In git.
Markback is a tiny text format for leaving feedback on any file — text, code, images, PDFs — that lints, diffs, and lives next to the work. CLI, Python, Node, a browser editor, and a VS Code extension.
- Try it without installing — markback.org/try-it
- Install the CLI —
pip install markbackornpm install markbackjs - Read the format — Specification
- Comment from your editor — VS Code extension
- Point your coding agent at it —
skills/markback
pip install -e .from markback import parse_file, parse_string
# Parse a file
result = parse_file("labels.mb")
for record in result.records:
print(f"{record.id}: {record.feedback}")
# Parse a string
text = """
@id example
Some content here.
<<< positive; good quality
"""
result = parse_string(text)from markback import Record, FileRef, write, append
# Write records to a file
records = [
Record(feedback="good", id="item-1", content="First item"),
Record(feedback="bad", id="item-2", content="Second item"),
]
write("output.mb", records)
# Append a single record
append("output.mb", Record(feedback="great", id="item-3", content="Third"))from markback import lint_file
result = lint_file("myfile.mb")
if result.has_errors:
for d in result.diagnostics:
print(d)The CLI is available via markback or mb (shorthand).
# Single file — inline feedback, appends to myfile.txt.mb
mb myfile.txt "good; clear writing"
# URL target — derives sidecar from last path segment (or hostname)
mb https://example.com/blog/post.html "great explanation"
# → writes post.html.mb with @file https://example.com/blog/post.html
# Quote a passage by editing the .mb file directly: inline content
# under an @file header can be a full snapshot OR an excerpt.
# @file https://example.com/post.html
#
# the quick brown fox jumps over the lazy dog
# <<< awkward phrasing
# Multi-segment section: several comments on one source, no repeated headers.
# @file ./essay.txt
#
# the lazy fox
# <<< awkward
#
# weak ending
# <<< needs punch
# With input reference (what produced the file)
mb output.txt "accurate" --input prompt.txt
# With tags and attribution
mb file.txt "good" --tag "review p1" --by [email protected]
# Multiple files — same feedback for all
mb *.jpg -f "approved"
# Interactive mode — steps through each file
mb *.jpg --print
# Sweep pattern — track issues across batches
mb *.txt -f "issue-A" --scope "issue-A issue-B" --covers "./*.txt"# Lint
mb --lint myfile.mb
mb --lint --json ./data/
# List records
mb --list myfile.mb
# Statistics
mb --stats myfile.mb
# Normalize to canonical format
mb --normalize input.mb
mb --normalize --in-place input.mb
# Convert between formats
mb --convert --to multi -o output.mb input.mb
mb --convert --to compact -o output.mb input.mb
# Upgrade V1 files to V2
mb --upgrade *.mb # preview
mb --upgrade --apply --in-place *.mb # apply| Header | Purpose |
|---|---|
@id |
Record identifier (plain string) |
@reply-to |
The @id this record replies to |
@by |
Who provided feedback |
@action |
<verb> <timestamp> [actor] lifecycle event; repeatable |
@tag |
Space-separated tags |
@input |
What produced the content (e.g., a prompt) |
@file |
Path to the content being annotated |
Listed in canonical order. All are optional.
%markback 2
%scope issue-A issue-B
%covers ./gen/batch3/*.txt
@id review-001
@by [email protected]
@file ./src/auth.py:45-67
@tag security p0
<<< vulnerable; sql-injection in query builder
@file ./images/001.jpg <<< approved; scene=beach
@file ./images/002.jpg <<< rejected; too dark
Several comments on one source, without repeating the headers — write
successive content + <<< pairs with no --- between them:
@file ./essay.txt
the lazy fox
<<< awkward
weak ending
<<< needs punch
Two records, both on ./essay.txt. A --- ends the section.
@id c1
@action created 2026-06-17T10:00:00Z [email protected]
@file ./login.py:42 <<< this branch never fires
---
@id c2
@reply-to c1
@file ./login.py:42 <<< it does — covered by test_login_edge()
When the text right after <<< is exactly """, feedback runs until a line
whose only content is """:
@id c1
@file ./login.py:42
<<< """
This branch looks dead, but I want to double-check before
suggesting removal.
"""
Content in report.pdf, annotation in report.pdf.mb:
@id report-001
<<< good; grade=B+
Track issues across batches with meaningful absence:
%markback 2
%scope issue-A issue-B
%covers ./gen/batch3/*.txt
@file ./gen/batch3/file2.txt <<< issue-B; tone is off
@file ./gen/batch3/file5.txt <<< issue-A; issue-B; both problems
Files matching %covers without annotations are implicitly clean for all %scope items.
V1 headers (@uri, @source, @prior) are automatically mapped to V2 equivalents with a W010 warning. The V2 parser reads V1 files transparently.
pip install -e ".[dev]"
pytestMIT