claude-report-skill
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 7 GitHub stars
Code Warn
- network request — Outbound network request in bin/report_common.py
- network request — Outbound network request in viewer/serve.py
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
A Claude Code skill that generates beautiful HTML reports instead of long terminal output
/report — HTML reports for agent sessions
A Claude Code skill (usable by any agent that can run shell commands) that
produces self-contained, dark-themed HTML reports instead of dumping long
output into your terminal — plus a standalone viewer that makes those
reports interactive: reply boxes, live task lists, one-click next-step cards,
and archive/restore.

What you get
/reportskill — auto-triggers on long output; 5 report types
(task-completion, research, audit, error-blocker, comparison) with a strict,
accessible Aurora theme.bin/reportCLI — paths, indexing, archive/restore, the tiny task store,
and the marker-wrapped blocks the viewer splices live.viewer/serve.py— a stdlib-only viewer (python3, no deps): report
index, themed serving, per-task reply forms, a report-level reply box,
"Do it" prompt cards,.mdlinkification, archive with unbreakable links.- Reply relay — replies are composed into self-describing messages and
appended toreplies.jsonl(+ stdout), or piped to any command you choose.
Zero dependencies beyond Python 3.9+.
Quick start (demo in one command)
./examples/make-demo.sh
That seeds a throwaway REPORT_DIR with a fictional acme-widgets report,
three fake tasks, and starts the viewer on
http://127.0.0.1:8765. Open the report and try the
reply box, the per-task forms, and the Do it cards — everything lands in$REPORT_DIR/replies.jsonl.
Install
git clone <this repo> && cd claude-report-skill
# 1. the skill (Claude Code)
mkdir -p ~/.claude/skills/report
ln -s "$PWD/SKILL.md" ~/.claude/skills/report/SKILL.md
# 2. the CLI + its shared module (keep them together)
mkdir -p ~/.local/bin
ln -s "$PWD/bin/report" "$PWD/bin/report_common.py" ~/.local/bin/
# 3. the viewer — run it from the repo, or copy viewer/ + bin/ somewhere
python3 viewer/serve.py
Make sure ~/.local/bin is on your PATH. report --help should work.
How it works
Writing a report (what the skill does)
report --template # themed HTML template
report --path --project myproj --title "Audit" --type audit
# → ~/.local/share/claude-reports/myproj/2026-09-24/143022-audit.html
report related-tasks myproj --link "$(report --url <file>)" --title "Audit"
report next-prompts "Run the suite." "Tag the release."
report --open <file> # viewer URL if up, else file://
Reports live under $REPORT_DIR/<project>/<date>/<time>-<slug>.html with an
embedded report-meta JSON block that drives indexing, task splicing and
reply routing. Images sit next to the HTML as relative <img> links.
The viewer
python3 viewer/serve.py # 127.0.0.1:8765
python3 viewer/serve.py --port 9000 # or REPORT_VIEWER_PORT=9000
GET /— index of live + archived reports.GET /view/<rel>— serves the report and splices at view time: theme
rewrite,.mdpath linkification, a fresh related-tasks block (markers<!-- related-tasks:start/end -->baked by the CLI), an injected reply box,
rendered prompt cards, and Archive/Restore controls.- A live miss falls back to
_archive/<rel>— old links and reply targets
keep working after archiving (with an "archived" banner). GET /md/<path>— allow-listed markdown rendered inside a sandboxed iframe
(strict CSP).
Tasks
A minimal sqlite store at $REPORT_DIR/tasks.db (no daemon):
report task add myproj "Approve the deploy window" --link "$VIEW_URL" --blocking
report task list [myproj] [--all] [--json]
report task done 3
Linking a task to a report's view URL sorts it first in that report's
Related-tasks block.
How replies reach an agent session
Nothing executes in the viewer. Every reply is composed into a
self-describing message (task id / report title / view URL included) and
relayed:
appended as one JSON line to
$REPORT_DIR/replies.jsonl— the durable
record, always written first;printed to the viewer's stdout; or
piped on stdin to
$REPORT_RELAY_CMDwhen set — e.g. straight into a
tmux session:export REPORT_RELAY_CMD='tmux send-keys -t agent-session -l -- "$(cat)" \ && tmux send-keys -t agent-session Enter' python3 viewer/serve.py(or
xargs-style:REPORT_RELAY_CMD='xargs -0 -I{} tmux send-keys -t agent-session {} Enter')
Two origins exist and are recorded on every line:
human— the reply boxes: the user typed it; treat as their answer.card— the "Do it" prompt cards: model-written text relayed verbatim.
Security notes
- The viewer binds 127.0.0.1 by default and warns on any wider bind.
- All POSTs are origin-checked (
Origin/Sec-Fetch-Sitemust match the
viewer or be absent, i.e. same-machine tools). - Every served path is validated against traversal/symlink escape;
.md
serving is restricted to$REPORT_DIR,REPORT_MD_ROOTS, and registered
project roots; markdown renders in a sandboxed iframe with a strict CSP. - Reply POSTs use
redirect:'error'and a server-side byte-count echo, so a
redirected or truncated send fails visibly instead of delivering a
silent/empty message. - Trust model: a reply-box message relays as the user — but it is still
just text on a loopback page, so on a shared machine anything that can POST
to localhost could fake one. The mitigation is receiver-side: a card
(origin=card) is model-authored and must never trigger irreversible
actions on its own, and evenhumanreplies should name the concrete
action before a session treats them as authorization.replies.jsonl
records every relay either way.
Configuration
| Env var | Default | Purpose |
|---|---|---|
REPORT_DIR |
${XDG_DATA_HOME:-~/.local/share}/claude-reports |
report root (index, tasks.db, replies.jsonl, _archive/) |
REPORT_THEME_DIR |
<repo>/themes |
aurora css location |
REPORT_VIEWER_PORT |
8765 |
viewer port |
REPORT_VIEWER_URL |
http://127.0.0.1:$REPORT_VIEWER_PORT |
base for --open/--url/baked links |
REPORT_BIND |
127.0.0.1 |
viewer bind address |
REPORT_RELAY_CMD |
(unset) | shell cmd receiving each reply on stdin |
REPORT_REPLIES_FILE |
$REPORT_DIR/replies.jsonl |
reply sink |
REPORT_TASKS_DB |
$REPORT_DIR/tasks.db |
task store |
REPORT_PROJECT |
(unset) | default --project |
REPORT_PROJECTS_FILE |
(unset) | optional name→path JSON registry for project validation + bare .md resolution |
REPORT_MD_ROOTS |
(unset) | :-separated extra roots allowed for .md links |
CLI reference
report --path --project P --title T --type {task-completion|research|audit|error-blocker|comparison} [--force]
report --open FILE [--force-open] # viewer URL when the viewer is up, else file://
report --url FILE # print the viewer URL
report --template # print the themed template (theme path filled)
report --theme-dir # print resolved theme dir
report --list [--project P --type T --days N]
report --list --archived (or: report list --archived)
report --index # rebuild index.json from disk
report archive <rel> # -> _archive/<rel>
report restore <rel>
report related-tasks P [--link URL] [--title T]
report next-prompts [prompt ...] # argv or newline-separated stdin
report task add|list|done ...
Tests
python3 -m unittest discover tests
Covers path-traversal refusal, the relay file append, task add/reply,
archive/restore, prompt-card rendering, markdown serving, and POST origin
enforcement — all against a real server on an ephemeral port.
Author
Marcin Dudek — MIT (see LICENSE)
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found