readme2demo 0.7.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. readme2demo-0.7.1/.claude/agents/grounding-auditor.md +43 -0
  2. readme2demo-0.7.1/.claude/agents/run-debugger.md +46 -0
  3. readme2demo-0.7.1/.claude/commands/ship-check.md +22 -0
  4. readme2demo-0.7.1/.claude/skills/add-engine/SKILL.md +57 -0
  5. readme2demo-0.7.1/.claude/skills/add-failure-class/SKILL.md +52 -0
  6. readme2demo-0.7.1/.claude/skills/debug-run/SKILL.md +74 -0
  7. readme2demo-0.7.1/.claude/skills/docker-sandbox/SKILL.md +55 -0
  8. readme2demo-0.7.1/.claude/skills/git-github-operator/SKILL.md +57 -0
  9. readme2demo-0.7.1/.claude/skills/python-standards/SKILL.md +61 -0
  10. readme2demo-0.7.1/.claude/skills/sandbox-toolchains/SKILL.md +50 -0
  11. readme2demo-0.7.1/.claude/skills/vhs-video/SKILL.md +50 -0
  12. readme2demo-0.7.1/.env.example +36 -0
  13. readme2demo-0.7.1/.github/CODEOWNERS +10 -0
  14. readme2demo-0.7.1/.github/FUNDING.yml +6 -0
  15. readme2demo-0.7.1/.github/ISSUE_TEMPLATE/bug_report.md +50 -0
  16. readme2demo-0.7.1/.github/ISSUE_TEMPLATE/config.yml +8 -0
  17. readme2demo-0.7.1/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
  18. readme2demo-0.7.1/.github/PULL_REQUEST_TEMPLATE.md +39 -0
  19. readme2demo-0.7.1/.github/dependabot.yml +29 -0
  20. readme2demo-0.7.1/.github/workflows/action-selftest.yml +35 -0
  21. readme2demo-0.7.1/.github/workflows/ci.yml +53 -0
  22. readme2demo-0.7.1/.github/workflows/docs.yml +50 -0
  23. readme2demo-0.7.1/.github/workflows/release.yml +77 -0
  24. readme2demo-0.7.1/.gitignore +37 -0
  25. readme2demo-0.7.1/CHANGELOG.md +303 -0
  26. readme2demo-0.7.1/CITATION.cff +28 -0
  27. readme2demo-0.7.1/CLAUDE.md +164 -0
  28. readme2demo-0.7.1/CODE_OF_CONDUCT.md +136 -0
  29. readme2demo-0.7.1/CONTRIBUTING.md +57 -0
  30. readme2demo-0.7.1/LICENSE +21 -0
  31. readme2demo-0.7.1/PKG-INFO +254 -0
  32. readme2demo-0.7.1/README.md +190 -0
  33. readme2demo-0.7.1/ROADMAP.md +57 -0
  34. readme2demo-0.7.1/SECURITY.md +92 -0
  35. readme2demo-0.7.1/action.yml +167 -0
  36. readme2demo-0.7.1/architecture/README.md +131 -0
  37. readme2demo-0.7.1/assets/social-preview.png +0 -0
  38. readme2demo-0.7.1/docs/configuration.md +55 -0
  39. readme2demo-0.7.1/docs/demo.gif +0 -0
  40. readme2demo-0.7.1/docs/faq.md +73 -0
  41. readme2demo-0.7.1/docs/google316985d142132f04.html +1 -0
  42. readme2demo-0.7.1/docs/how-it-works.md +57 -0
  43. readme2demo-0.7.1/docs/index.md +67 -0
  44. readme2demo-0.7.1/docs/llms.txt +25 -0
  45. readme2demo-0.7.1/docs/security.md +32 -0
  46. readme2demo-0.7.1/docs/step-by-step.md +57 -0
  47. readme2demo-0.7.1/docs/usage.md +68 -0
  48. readme2demo-0.7.1/docs/whats-new-0.7.0.md +100 -0
  49. readme2demo-0.7.1/examples/README.md +55 -0
  50. readme2demo-0.7.1/examples/readme2demo/commands.sh +24 -0
  51. readme2demo-0.7.1/examples/readme2demo/demo.gif +0 -0
  52. readme2demo-0.7.1/examples/readme2demo/demo.mp4 +0 -0
  53. readme2demo-0.7.1/examples/readme2demo/howto.jsonld +77 -0
  54. readme2demo-0.7.1/examples/readme2demo/manifest.json +91 -0
  55. readme2demo-0.7.1/examples/readme2demo/step_by_step.md +57 -0
  56. readme2demo-0.7.1/examples/readme2demo/troubleshooting.md +56 -0
  57. readme2demo-0.7.1/examples/readme2demo/tutorial.md +126 -0
  58. readme2demo-0.7.1/examples/toolhive/commands.sh +21 -0
  59. readme2demo-0.7.1/examples/toolhive/demo.gif +0 -0
  60. readme2demo-0.7.1/examples/toolhive/demo.mp4 +0 -0
  61. readme2demo-0.7.1/examples/toolhive/howto.jsonld +73 -0
  62. readme2demo-0.7.1/examples/toolhive/manifest.json +96 -0
  63. readme2demo-0.7.1/examples/toolhive/step_by_step.md +202 -0
  64. readme2demo-0.7.1/examples/toolhive/troubleshooting.md +5 -0
  65. readme2demo-0.7.1/examples/toolhive/tutorial.md +130 -0
  66. readme2demo-0.7.1/images/base/Dockerfile +73 -0
  67. readme2demo-0.7.1/images/openhands/Dockerfile +94 -0
  68. readme2demo-0.7.1/mkdocs.yml +69 -0
  69. readme2demo-0.7.1/pyproject.toml +96 -0
  70. readme2demo-0.7.1/readme2demo.toml.example +38 -0
  71. readme2demo-0.7.1/scripts/self-demo.sh +111 -0
  72. readme2demo-0.7.1/src/readme2demo/__init__.py +21 -0
  73. readme2demo-0.7.1/src/readme2demo/agent.py +176 -0
  74. readme2demo-0.7.1/src/readme2demo/cli.py +622 -0
  75. readme2demo-0.7.1/src/readme2demo/config.py +73 -0
  76. readme2demo-0.7.1/src/readme2demo/distill.py +798 -0
  77. readme2demo-0.7.1/src/readme2demo/engines/__init__.py +9 -0
  78. readme2demo-0.7.1/src/readme2demo/engines/base.py +116 -0
  79. readme2demo-0.7.1/src/readme2demo/engines/claude_code.py +363 -0
  80. readme2demo-0.7.1/src/readme2demo/engines/openhands.py +323 -0
  81. readme2demo-0.7.1/src/readme2demo/ingest.py +287 -0
  82. readme2demo-0.7.1/src/readme2demo/llm.py +511 -0
  83. readme2demo-0.7.1/src/readme2demo/manifest.py +161 -0
  84. readme2demo-0.7.1/src/readme2demo/normalize.py +230 -0
  85. readme2demo-0.7.1/src/readme2demo/orchestrator.py +424 -0
  86. readme2demo-0.7.1/src/readme2demo/prompts/agent.md +97 -0
  87. readme2demo-0.7.1/src/readme2demo/prompts/distill.md +77 -0
  88. readme2demo-0.7.1/src/readme2demo/prompts/planner.md +99 -0
  89. readme2demo-0.7.1/src/readme2demo/prompts/tutorial.md +31 -0
  90. readme2demo-0.7.1/src/readme2demo/render.py +266 -0
  91. readme2demo-0.7.1/src/readme2demo/sandbox.py +184 -0
  92. readme2demo-0.7.1/src/readme2demo/templates/demo.tape.j2 +69 -0
  93. readme2demo-0.7.1/src/readme2demo/templates/tutorial.md.j2 +59 -0
  94. readme2demo-0.7.1/src/readme2demo/tutorial.py +647 -0
  95. readme2demo-0.7.1/src/readme2demo/types.py +175 -0
  96. readme2demo-0.7.1/src/readme2demo/verify.py +199 -0
  97. readme2demo-0.7.1/tests/conftest.py +21 -0
  98. readme2demo-0.7.1/tests/fixtures/claude_transcript.ndjson +26 -0
  99. readme2demo-0.7.1/tests/test_cli.py +631 -0
  100. readme2demo-0.7.1/tests/test_config.py +154 -0
  101. readme2demo-0.7.1/tests/test_distill.py +892 -0
  102. readme2demo-0.7.1/tests/test_ingest.py +387 -0
  103. readme2demo-0.7.1/tests/test_llm_backend.py +956 -0
  104. readme2demo-0.7.1/tests/test_normalize.py +727 -0
  105. readme2demo-0.7.1/tests/test_orchestrator.py +326 -0
  106. readme2demo-0.7.1/tests/test_render_timeout_image.py +58 -0
  107. readme2demo-0.7.1/tests/test_sandbox.py +318 -0
  108. readme2demo-0.7.1/tests/test_tutorial.py +698 -0
  109. readme2demo-0.7.1/tests/test_verify.py +336 -0
  110. readme2demo-0.7.1/tests/test_version.py +42 -0
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: grounding-auditor
3
+ description: Audit code or prompt changes against readme2demo's grounding invariant before commit. Use PROACTIVELY after any change touching distill.py, tutorial.py, normalize.py, engines/, prompts/, or templates/ — anything on the path between the LLM and the published artifacts.
4
+ tools: Read, Grep, Glob, Bash
5
+ ---
6
+
7
+ You are the guardian of readme2demo's core invariant:
8
+
9
+ > The LLM never publishes anything a fresh container didn't independently
10
+ > execute. Grounding is enforced in code, not prompts.
11
+
12
+ Given a diff or a set of changed files, answer ONE question: can any command
13
+ or output now reach a published artifact (tutorial.md, step_by_step.md,
14
+ commands.sh, demo.tape, howto.jsonld) without being backed by the agent log
15
+ or the verify replay?
16
+
17
+ ## Checklist
18
+
19
+ 1. `distill.py`: every path into `commands.sh`/`demo.tape` still passes
20
+ through `is_grounded`/`validate_grounding` or an explicitly documented
21
+ exception (`cd`, comments, harness clone, pipe variants, heredoc prefixes,
22
+ findings_success entries). New equivalences must be SYMMETRIC in
23
+ `normalize_cmd`/`_grounded_candidates` and regression-tested both ways
24
+ (grounded variant accepted, unproven command still rejected).
25
+ 2. `tutorial.py`: `enforce_commands` still restores every command AND
26
+ expected_output from the grounded outline after the LLM polish pass;
27
+ expected outputs come from verify.log or the agent log, never the model.
28
+ 3. `normalize.py` + `engines/*`: parsing stays pure/deterministic (no LLM
29
+ calls); `mark_findings_success` only marks entries matching BOTH the
30
+ success command and the expected pattern.
31
+ 4. Prompt-only fixes: reject them. A prompt change without a corresponding
32
+ code-level check or test is not a defense (search the diff for prompt
33
+ edits lacking sibling code/test changes).
34
+ 5. `sandbox.py`: hardening flags intact (--cap-drop ALL, no-new-privileges,
35
+ limits, non-root); docker-socket mounting stays opt-in.
36
+ 6. Run `python -m pytest tests/ -q` and report the count.
37
+
38
+ ## Report format
39
+
40
+ - **Verdict**: PASS / FAIL (+ the specific leak path if FAIL).
41
+ - **Weakened checks**: any check made more permissive, with justification
42
+ status (documented + tested, or not).
43
+ - **Missing regression tests**: for each behavior change.
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: run-debugger
3
+ description: Diagnose a failed or unverified readme2demo run directory. Use PROACTIVELY whenever a pipeline run fails, verify doesn't pass, a video comes out short/incomplete, or generated docs are missing steps. Input should include the runs/<run-id> path.
4
+ tools: Read, Grep, Glob, Bash
5
+ ---
6
+
7
+ You diagnose failed readme2demo runs. You are read-mostly: produce a precise
8
+ diagnosis and a repair plan; only write files when explicitly asked to repair.
9
+
10
+ ## Procedure
11
+
12
+ 1. Read `manifest.json` in the run dir: which stage failed/skipped, stage
13
+ `meta` (outcome, adjusted_success, source_modified,
14
+ guide_steps_unattempted, tape_coverage), `verified`, total cost.
15
+ 2. Read the failing stage's evidence:
16
+ - verify failures → `verify.log` (find `===== ATTEMPT n =====` markers,
17
+ read the tail of the LAST attempt; the line after the final `+ cmd`
18
+ trace is where it died) and `commands.sh`.
19
+ - agent failures → `command_log.json`: filter entries by exit_code,
20
+ read outputs of the failures; check `fixes`, `adjusted_success_command`,
21
+ `file_edits`.
22
+ - render failures → the RenderError text, `demo.tape`, tape vs
23
+ `expected_min_duration_s`.
24
+ - doc-quality complaints → `step_by_step.md` / `tutorial.md` vs
25
+ `command_log.json`: which proven commands are missing from the docs?
26
+ 3. Match against the "Known failure classes" table in CLAUDE.md — most
27
+ failures are a recurrence or a sibling of a known class. Name the class.
28
+ 4. If it is genuinely new: state the mechanism (what state/assumption
29
+ diverged between the agent container and the verify/render container),
30
+ which module owns it, and what code-level defense would have caught it.
31
+
32
+ ## Report format
33
+
34
+ - **Verdict**: one sentence — what failed and why.
35
+ - **Class**: known-class number from CLAUDE.md, or "NEW".
36
+ - **Evidence**: 2-4 quoted lines from logs.
37
+ - **Systemic fix**: file + function to change (code first; prompt second).
38
+ - **Run repair**: the cheapest way to salvage THIS run (patch commands.sh /
39
+ plan.json / regenerate guide+tape via the pure functions in distill.py and
40
+ tutorial.py) and the exact `readme2demo resume <run> --from-stage <s>`
41
+ command. Never suggest re-running the agent when a downstream repair works.
42
+
43
+ Key fact for repairs: distill/tutorial artifact writers
44
+ (`write_step_by_step`, `tape_from_guide`, `write_tape`) are pure functions —
45
+ they can regenerate a run's guide and tape from `command_log.json` +
46
+ `plan.json` without any LLM cost.
@@ -0,0 +1,22 @@
1
+ ---
2
+ description: Pre-commit gauntlet for readme2demo — invariant audit, tests, hygiene
3
+ ---
4
+
5
+ Run the pre-commit gauntlet on the current working tree:
6
+
7
+ 1. Run `python -m pytest tests/ -q` — must be fully green.
8
+ 2. `python -m compileall -q src/` — must be clean.
9
+ 3. Use the grounding-auditor agent on the staged/changed files if anything
10
+ under src/readme2demo/{distill,tutorial,normalize,engines,prompts,templates}
11
+ changed.
12
+ 4. Hygiene greps: no `runs/` paths staged (`git status --short | grep runs/`
13
+ must be empty), no credentials
14
+ (`git diff --cached | grep -iE 'sk-ant-[a-z0-9]{8}'` empty).
15
+ 5. If prompts/*.md changed: confirm the change has a sibling code check or
16
+ test, and note which real run motivated it.
17
+ 6. If images/base/Dockerfile changed: remind that users must rebuild
18
+ (`docker build --no-cache -t readme2demo/base:latest images/base/`) and
19
+ check the Claude Code pin ↔ stream-json parser coupling.
20
+ 7. Report: test count, files changed by area, any CLAUDE.md failure-class
21
+ entries that should be added for this change, and a suggested
22
+ conventional commit message.
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: add-engine
3
+ description: Add a new AI agent backend (engine) to readme2demo, or fix parsing for an existing one (claude-code stream-json drift, openhands trajectory changes). Use when adding support for a new coding agent or when transcripts stop parsing after an agent CLI upgrade.
4
+ ---
5
+
6
+ # Adding or fixing an agent engine
7
+
8
+ Engines are the ONLY place that knows which AI agent ran. Everything
9
+ downstream consumes `command_log.json`.
10
+
11
+ ## The contract (`engines/base.py`)
12
+
13
+ Implement `AgentEngine` and decorate with `@register`:
14
+
15
+ - `name`: CLI value for `--engine`.
16
+ - `required_env()` / `resolve_env()`: host env vars forwarded into the
17
+ sandbox at exec time (never baked into images). Support alternative auth
18
+ by overriding `resolve_env` (see ClaudeCodeEngine's key-OR-oauth-token,
19
+ including the credential-format validation — copy it).
20
+ - `build_command(limits)`: one shell string run via `bash -lc` inside the
21
+ container; reads the prompt from `PROMPT_CONTAINER_PATH`, writes the raw
22
+ transcript to `TRANSCRIPT_CONTAINER_PATH`, stderr next to it.
23
+ - `parse_transcript(path) -> CommandLog`: PURE, deterministic, no LLM calls,
24
+ tolerant of malformed lines and schema drift. Reuse `truncate_output`,
25
+ `scan_markers`, `scan_adjusted` from `claude_code.py` so markers and
26
+ truncation behave identically across engines.
27
+
28
+ ## Parsing requirements
29
+
30
+ - Pair command events with their results; unpaired commands get
31
+ `exit_code=None` (never fabricate 0).
32
+ - Extract: Bash-equivalent commands + outputs (head 3KB + tail 1KB
33
+ truncation), file edits, FIX/BLOCKED/ADJUSTED_SUCCESS markers, outcome
34
+ (blocked > success-marker > failed), cost/turns/duration when available
35
+ (None when not — budget logic treats None as unknown, not zero).
36
+
37
+ ## Checklist
38
+
39
+ 1. `engines/<name>.py` with the above; module docstring stating the
40
+ transcript format assumptions and its drift-tolerance strategy.
41
+ 2. Fixture transcript in `tests/fixtures/` exercising: success + failure
42
+ pairs, a marker, a malformed line, string-vs-list content shapes.
43
+ 3. Tests: parse the fixture, verify counts/exit codes/markers/outcome;
44
+ missing-env EngineError; build_command shape (uses the canonical
45
+ container paths, not hardcoded strings).
46
+ 4. If the format VERSION is coupled to a pinned CLI in
47
+ `images/base/Dockerfile`, bump pin and parser together and say so in the
48
+ commit message.
49
+ 5. README: add the engine to the auth/requirements section.
50
+
51
+ ## Known drift landmines
52
+
53
+ - claude-code stream-json: `tool_result.content` is a string OR a list of
54
+ text blocks; the final `result` event can be absent when the agent is
55
+ killed (marker alone then decides success).
56
+ - openhands trajectories: JSON array, `{"events": [...]}` wrapper, or JSONL
57
+ — detect all three.
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: add-failure-class
3
+ description: The workflow for turning a newly diagnosed readme2demo run failure into a permanent, tested defense. Use after debug-run identifies a failure mechanism that is not yet in CLAUDE.md's known-failure-classes table.
4
+ ---
5
+
6
+ # Adding a failure class (the maintenance meta-workflow)
7
+
8
+ Every robustness feature in this repo came from a real failed run processed
9
+ through exactly these steps. Do them in order; skipping the test or the
10
+ CLAUDE.md entry is how knowledge evaporates.
11
+
12
+ ## Steps
13
+
14
+ 1. **Name the mechanism, not the symptom.** "verify exit 1" is a symptom;
15
+ "findings tools exit nonzero on success and set -e kills the assertion"
16
+ is a mechanism. You must be able to say which two components disagreed
17
+ about an assumption.
18
+
19
+ 2. **Pick the owning module** (one, occasionally two):
20
+ - agent behavior → `prompts/agent.md` rule AND a detector in
21
+ `normalize.py` (rule 6 + repo_files_edited is the template)
22
+ - command equivalence → `distill.normalize_cmd` / `_grounded_candidates`
23
+ (always symmetric, both directions tested)
24
+ - replay semantics → `distill._render_commands_sh` (assertion block) or
25
+ `verify.py`
26
+ - video → `templates/demo.tape.j2` / `render.py` gates
27
+ - parsing → the engine adapter, kept pure and tolerant
28
+
29
+ 3. **Code first, prompt second.** If the fix involves an LLM behaving
30
+ differently, the prompt edit gets a sibling code-level check or
31
+ validation — prompts alone are not defenses.
32
+
33
+ 4. **Regression test named for the run.** Docstring starts with
34
+ `"""Regression (<repo> run): ..."""` describing the real failure. Must
35
+ fail on the old code. Tests stay free of docker/network/API.
36
+
37
+ 5. **Repair the triggering run cheaply** (see debug-run skill) and give the
38
+ exact resume command.
39
+
40
+ 6. **Document**: add a numbered entry to CLAUDE.md "Known failure classes"
41
+ (symptom → mechanism → defense location). If the class is user-visible,
42
+ consider a line in README or the troubleshooting-relevant prompt.
43
+
44
+ 7. **Full suite green** (`python -m pytest tests/ -q`) before claiming done.
45
+
46
+ ## Quality bar for grounding changes
47
+
48
+ New equivalences (like the 2>&1 or pipe-variant rules) must:
49
+ - apply to BOTH the candidate set and the queried command (symmetry),
50
+ - never make an unproven command grounded (add a negative test: the evil
51
+ variant is still rejected),
52
+ - be documented in the `is_grounded` docstring's accepted-forms list.
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: debug-run
3
+ description: Debug a failed, unverified, or low-quality readme2demo pipeline run. Use when a run fails at any stage, verify won't pass, the demo video is short or missing steps, generated docs lack detail, or the user pastes readme2demo CLI output showing a failure.
4
+ ---
5
+
6
+ # Debugging a readme2demo run
7
+
8
+ Every run leaves complete evidence in `runs/<run-id>/`. Read evidence before
9
+ theorizing — the answer is almost always in three files.
10
+
11
+ ## Evidence map
12
+
13
+ | File | Tells you |
14
+ |---|---|
15
+ | `manifest.json` | which stage failed, stage meta (outcome, adjusted_success, source_modified, guide_steps_unattempted, tape_coverage), verified flag, cost |
16
+ | `command_log.json` | every command the agent ran, exit codes, outputs, FIX markers, findings_success flags |
17
+ | `verify.log` | the clean-container replay, bash -x traced; last `+ cmd` before silence = point of death; `===== ATTEMPT n =====` separates tries |
18
+ | `commands.sh` | what verify executed — preamble (harness clone), steps, assertion block |
19
+ | `step_by_step.md` + `tape_coverage.json` | what made the docs/video and what was dropped (with reasons) |
20
+ | `transcript.ndjson` | raw agent stream (only needed for parser bugs) |
21
+
22
+ ## Decision tree
23
+
24
+ 1. **ingest failed** → planner/LLM issue or infeasible repo (read plan.json
25
+ blockers). Infeasible is often CORRECT behavior.
26
+ 2. **agent failed/blocked** → read final assistant text in command_log
27
+ result + failed entries. Check for the cutoff-denial pattern ("X isn't
28
+ released yet") and infrastructure rabbit holes (fake sockets). Blocked
29
+ for credentials/GPU is correct behavior.
30
+ 3. **distill failed (ungrounded)** → compare the rejected command against
31
+ the log: usually a syntax-equivalence gap (whitespace, 2>&1, env prefix,
32
+ chain, pipe-cap, heredoc body). Fix in `normalize_cmd` /
33
+ `_grounded_candidates` symmetrically, never by weakening validation.
34
+ 4. **verify failed** → tail the last attempt in verify.log. Common causes in
35
+ order: missing setup step, cwd drift (run `verify.cwd_hints` on the
36
+ script), findings-tool nonzero exit reaching `set -e`, wrong
37
+ expected_pattern, agent had patched source (manifest source_modified).
38
+ 5. **render failed** → duration gate (steps didn't play → stale image or
39
+ tape bug), VHS parse error (quoting), disk exhaustion (docker system
40
+ prune), socket permissions if the tape manages containers.
41
+ 6. **docs/video missing steps** → `tape_coverage.json` dropped list +
42
+ normalize's guide_steps_unattempted meta. Unattempted = agent never ran
43
+ it (needs fresh agent run); dropped = grounding gap (fixable + tape
44
+ regenerable without LLM).
45
+
46
+ ## Cheap repairs (no agent re-run)
47
+
48
+ The artifact writers are pure functions. From the repo root:
49
+
50
+ ```python
51
+ # regenerate guide + tape for an existing run after a code fix
52
+ PYTHONPATH=src python3 - <<'EOF'
53
+ from pathlib import Path
54
+ from readme2demo.distill import tape_from_guide, write_tape
55
+ from readme2demo.tutorial import write_step_by_step
56
+ from readme2demo.types import CommandLog, Plan, TutorialOutline
57
+ rd = Path("runs/<run-id>")
58
+ plan = Plan.model_validate_json((rd/"plan.json").read_text())
59
+ log = CommandLog.model_validate_json((rd/"command_log.json").read_text())
60
+ outline = TutorialOutline.model_validate_json((rd/"tutorial_outline.json").read_text())
61
+ write_step_by_step(rd, plan, outline, log, verified=False, repo_url="<url>")
62
+ write_tape(tape_from_guide((rd/"step_by_step.md").read_text(), log, [], "<url>"), rd)
63
+ EOF
64
+ ```
65
+
66
+ `commands.sh` and `plan.json` can be patched by hand for one run; then
67
+ `readme2demo resume runs/<run-id> --from-stage <verify|render|tutorial>`.
68
+ Resume from the EARLIEST stage whose inputs changed, never earlier.
69
+
70
+ ## After the diagnosis
71
+
72
+ If the cause is new, follow the add-failure-class skill: systemic code fix +
73
+ regression test + CLAUDE.md failure-class entry. A run-specific patch without
74
+ a systemic fix just schedules the same debugging session for the next repo.
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: docker-sandbox
3
+ description: Docker usage in readme2demo — the base image, hardened sandbox lifecycle, the Docker-socket opt-in, and diagnosing container/daemon failures. Use when touching sandbox.py, images/base/Dockerfile, render.py, verify.py, or when a run fails with a docker/daemon/permission/disk error.
4
+ ---
5
+
6
+ # Docker & the sandbox
7
+
8
+ The container IS the security boundary. READMEs are untrusted code; the agent
9
+ runs INSIDE the container so `--dangerously-skip-permissions` is safe.
10
+
11
+ ## The base image (`images/base/Dockerfile`)
12
+
13
+ - Built FROM `ghcr.io/charmbracelet/vhs:latest` so ONE image serves the agent
14
+ run, the verify replay, AND the video render (vhs + ttyd + ffmpeg from the
15
+ base; git/Go/Python/Node + Claude Code added on top). This is why the video
16
+ can execute real toolchain steps on camera.
17
+ - `ENTRYPOINT []` + `CMD ["bash"]`: the VHS image ships `ENTRYPOINT ["vhs"]`;
18
+ reset it so `sandbox.py` can run arbitrary commands. The renderer re-invokes
19
+ vhs via `--entrypoint vhs`.
20
+ - Go comes from the official tarball at build time (distro packages lag and
21
+ strand agents on "go >= 1.2x required"). Rebuild periodically to stay current.
22
+ - The Claude Code npm pin is COUPLED to the stream-json parser
23
+ (`engines/claude_code.py`) — bump them together.
24
+ - Rebuild after ANY Dockerfile change:
25
+ `docker build --no-cache -t readme2demo/base:latest images/base/`. The render
26
+ stage preflights the image (`render.check_render_image`) and errors clearly
27
+ if it's stale (missing vhs/ffmpeg/git).
28
+
29
+ ## Hardening (`sandbox.py` — never weaken without discussion)
30
+
31
+ Every container: `--cap-drop ALL`, `--security-opt no-new-privileges`,
32
+ `--memory/--cpus/--pids-limit`, non-root user `demo` (uid 1000), wall-clock
33
+ timeout, destroyed after the stage. Run dirs are mounted with ABSOLUTE paths
34
+ (docker -v treats relative as a volume name).
35
+
36
+ ## Docker-socket opt-in (`--allow-docker-socket`)
37
+
38
+ For tools that manage containers themselves (toolhive `thv run`,
39
+ testcontainers, compose repos). Mounts `/var/run/docker.sock` into agent +
40
+ verify + render. The non-root user needs `--group-add $(socket gid)` —
41
+ `sandbox.docker_socket_gid` probes it (`stat -c %g`) or every socket call is
42
+ EACCES and the tool reports "no container runtime available" despite the mount.
43
+ Pierces isolation: trusted repos only; the CLI prints the warning in red.
44
+
45
+ ## Diagnosing docker failures
46
+
47
+ - `input/output error` / `containerd meta.db` → Docker VM disk full (VHS writes
48
+ a PNG per frame). `docker system prune -af --volumes`; restart Docker
49
+ Desktop; raise the VM disk. The render mitigations (24fps, mp4-only, short
50
+ ffmpeg gif preview) exist because full-length GIFs once filled the disk.
51
+ - exit 125 from `docker run` → bad flags / relative mount path — check the
52
+ Sandbox args.
53
+ - "no container runtime available" WITH `--allow-docker-socket` → group-add gid
54
+ problem (above).
55
+ - verify passes but render image errors → stale base image; rebuild.
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: git-github-operator
3
+ description: Git and GitHub operations for maintaining the readme2demo repo — branching, conventional commits, PRs, releases, and the never-commit rules. Use when committing, opening a PR, tagging a release, or doing any git/gh operation on this repo.
4
+ ---
5
+
6
+ # Git & GitHub operator for readme2demo
7
+
8
+ Remote: `https://github.com/alphacrack/readme2demo` (origin). Default branch:
9
+ `main`. In non-interactive sessions you can stage/commit locally but the human
10
+ runs `git push` (auth lives on their machine).
11
+
12
+ ## Never commit
13
+
14
+ - `runs/` — every pipeline run (transcripts, videos, cloned repos, and
15
+ potentially the target repo's content). Gitignored; keep it that way.
16
+ Publishable examples are HAND-COPIED into `examples/` after review.
17
+ - `.env`, `readme2demo.toml`, any credential. Before every commit:
18
+ `git diff --cached | grep -iE 'sk-ant-[a-z0-9]{8}'` must be empty, and
19
+ `git status --short | grep runs/` must be empty.
20
+
21
+ ## Commit style
22
+
23
+ Conventional-ish, imperative subject, then a body that lists WHAT changed and
24
+ WHY — this repo's history is a design log (read `git log` to see the bar).
25
+ When a commit fixes a failure class, name the run that found it and note the
26
+ regression test. One concern per commit where practical; batch tightly-coupled
27
+ changes (code + test + CLAUDE.md entry) together.
28
+
29
+ ## Pre-push gate
30
+
31
+ Run the `/ship-check` command (or the ship-check flow): tests green, compile
32
+ clean, grounding-auditor on any src change touching the LLM→artifact path,
33
+ hygiene greps. If `images/base/Dockerfile` changed, the commit body must tell
34
+ users to rebuild (`docker build --no-cache -t readme2demo/base:latest images/base/`).
35
+
36
+ ## Branches & PRs
37
+
38
+ - Feature work on a branch; PR into main. PR description states the failure
39
+ class or feature, the systemic fix (code first), and the regression test.
40
+ - CI (`.github/workflows/ci.yml`) runs the suite on 3.10 and 3.12 — a red CI
41
+ blocks merge. Keep the suite docker/network/API-free so CI stays fast.
42
+ - `good first issue` / `help wanted` labels are seeded for contributors (see
43
+ launch/LAUNCH_CHECKLIST.md).
44
+
45
+ ## Releases
46
+
47
+ - Tag `vX.Y.Z` with honest notes (state the MVP tradeoffs — Docker required,
48
+ README treated as untrusted code, API key enters sandbox until the egress
49
+ proxy lands). The pinned Claude Code version in the base image is coupled to
50
+ the stream-json parser — call out a bump in the release notes.
51
+
52
+ ## Repairing a user's run without git
53
+
54
+ Run dirs are disposable and gitignored. To salvage a failed run, patch its
55
+ `commands.sh`/`plan.json` or regenerate its guide+tape with the pure functions
56
+ (see the debug-run skill) and give the `resume --from-stage` command — no
57
+ commit involved.
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: python-standards
3
+ description: Python coding standards, testing patterns, and pydantic/typing conventions for the readme2demo codebase. Use when writing or reviewing any Python in src/readme2demo/ or tests/, adding a module, or before committing code changes.
4
+ ---
5
+
6
+ # Python standards for readme2demo
7
+
8
+ Python ≥3.10. Match the surrounding code; these are the invariants.
9
+
10
+ ## Types & models
11
+
12
+ - Type hints on every public function signature; return types included.
13
+ - Data contracts are pydantic v2 models in `types.py` — the single source of
14
+ truth every stage serializes through. Add a field THERE first, never invent
15
+ an ad-hoc dict that crosses a stage boundary. Use `model_dump_json(indent=2)`
16
+ / `model_validate_json` for run-dir I/O.
17
+ - `Optional[X]` for nullable; default containers via `Field(default_factory=...)`.
18
+ - New fields must be backward-compatible (defaulted) — old run dirs on disk
19
+ must still load.
20
+
21
+ ## Purity boundary (enforced, not stylistic)
22
+
23
+ - `normalize.py` and every engine `parse_transcript` are PURE and
24
+ deterministic: no LLM calls, no network, no docker, no clock/random that
25
+ changes output. They are tested against fixtures. If you need an LLM, you
26
+ are in the wrong module.
27
+ - LLM calls live only in ingest (planner), distill, tutorial — via `llm.py`.
28
+
29
+ ## Docstrings
30
+
31
+ - One-line summary; then WHY when non-obvious. Regression-critical functions
32
+ carry the failure they defend against (search for "Regression (" in tests).
33
+ - Public functions in the pipeline path get a docstring naming their invariant.
34
+
35
+ ## Testing (this is where the repo's value lives)
36
+
37
+ - `python -m pytest tests/ -q` runs in <1s with NO docker/network/API. Keep it
38
+ that way: mock `llm.complete_json`, feed fixture transcripts, build models
39
+ inline. Anything needing real infra is marked `@pytest.mark.integration`.
40
+ - EVERY bug fix carries a regression test whose docstring starts
41
+ `"""Regression (<repo> run): ..."""` and describes the real failure. It must
42
+ fail on the old code.
43
+ - For grounding/equivalence changes: test BOTH directions — the new-variant is
44
+ accepted AND an unproven/evil command is still rejected.
45
+ - Prefer table-style asserts on the actual artifact (the rendered commands.sh,
46
+ the tape text, the guide markdown), not on internal state.
47
+
48
+ ## Style
49
+
50
+ - stdlib > third-party; the runtime deps are deliberately few (typer, rich,
51
+ pydantic, jinja2, anthropic). Don't add a dependency for something stdlib
52
+ does.
53
+ - `subprocess.run(..., capture_output=True, text=True, timeout=..., errors="replace")`
54
+ is the house pattern for shelling out; always set a timeout.
55
+ - Fail with a specific, actionable message (name the env var, the file, the
56
+ fix command) — see the preflight and EngineError messages for the tone.
57
+
58
+ ## Before you commit
59
+
60
+ Run `/ship-check`. Minimum: full suite green, `python -m compileall -q src/`
61
+ clean, no `runs/` or secrets staged.
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: sandbox-toolchains
3
+ description: How readme2demo handles TARGET-repo languages and toolchains (Go, Node, Python, Rust, Terraform, etc.) inside the sandbox — version currency, install patterns, and the agent rules that keep runs honest. Use when a run fails on a toolchain install/version, or when extending language support in the base image or agent prompt.
4
+ ---
5
+
6
+ # Target-repo toolchains in the sandbox
7
+
8
+ readme2demo runs OTHER projects' quickstarts. The base image ships common
9
+ toolchains; the agent installs anything else. Two recurring hazards, both with
10
+ agent-prompt rules AND code defenses.
11
+
12
+ ## Version currency (failure class 2)
13
+
14
+ The agent's training data is STALE — languages/tools newer than it remembers
15
+ almost certainly exist. `prompts/agent.md` rule 4: never declare a version
16
+ "unreleased"; CHECK the official source
17
+ (`curl -sL 'https://go.dev/VERSION?m=text'`, `pyenv install --list`,
18
+ `npm view <pkg> version`) then download it. Keep the base image's Go current
19
+ (built from the official tarball) so the common case needs no download.
20
+
21
+ ## Install patterns the agent uses (and that must survive verify)
22
+
23
+ - Python externally-managed sandbox → `pip install --break-system-packages`
24
+ (and `--no-input`). Editable installs (`pip install -e .`) are cwd-sensitive
25
+ — see below.
26
+ - Go: `go build` with a current toolchain; GOTOOLCHAIN pitfalls handled by
27
+ shipping a new-enough Go.
28
+ - Binaries fetched by curl+unzip go to `~/.local/bin` or `~/bin` on PATH.
29
+
30
+ ## cwd drift (failure class 6)
31
+
32
+ `cd` persists across script lines. The classic bug: `cd /tmp` to unpack a
33
+ binary, then `pip install -e .` editable-installs /tmp instead of the repo.
34
+ `prompts/distill.md` rule 5b requires a `cd` on/before any relative-path line;
35
+ `verify.cwd_hints` runs a static cwd simulation of a failing script and injects
36
+ precise hints into the distiller retry. The repo is always at `/work`.
37
+
38
+ ## Findings tools (failure class 4)
39
+
40
+ Linters, drift detectors, scanners (tfdrift, etc.) exit NONZERO when they find
41
+ what the demo exists to show. The assertion block uses `set +e` +
42
+ pattern-as-criterion; `normalize.mark_findings_success` reclassifies such
43
+ entries as successful so they count for grounding, the tape, and the payoff
44
+ step. When adding language support, remember success ≠ exit 0 for this class.
45
+
46
+ ## Extending support
47
+
48
+ Add a toolchain to `images/base/Dockerfile` only if it's common across target
49
+ repos (keep the image lean; the agent can install niche tools). Per-ecosystem
50
+ slim images are a known future optimization, not the MVP.
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: vhs-video
3
+ description: VHS tape syntax and ffmpeg video handling in readme2demo — how the demo video is generated, the hard-won gotchas, and the duration gate. Use when touching templates/demo.tape.j2, render.py, distill.tape_from_guide, or when a video is short, blank, mis-paced, or fails to render.
4
+ ---
5
+
6
+ # VHS tapes & video
7
+
8
+ The demo video is built from the FINAL `step_by_step.md` (render stage, after
9
+ tutorial finalizes the guide) so the video follows the published guide
10
+ step-for-step. `distill.tape_from_guide` parses the guide's fenced bash blocks
11
+ into `TapeCommand`s; `templates/demo.tape.j2` renders the tape; `render.py`
12
+ runs VHS in the base image.
13
+
14
+ ## Non-negotiable tape rules (each is a scar)
15
+
16
+ - **Never use `Wait+Screen` on output text.** It matches only the VISIBLE
17
+ buffer; any pattern in longer-than-one-screen output scrolls off and times
18
+ the render out. Pace with `Wait` (blocks until the shell prompt returns, so
19
+ the command — build included — has finished) then a short `Sleep` to read.
20
+ - **VHS string literals have NO backslash escapes.** `vhs_quote` picks a
21
+ delimiter the text doesn't contain: `"` → `` ` `` → `'`. A string with all
22
+ three can't be typed (rare; that step is skipped).
23
+ - **`Type` is single-line.** Multi-line commands (heredocs) are typed
24
+ LINE-BY-LINE via `TapeCommand.lines` (Type/Enter per line) so file creation
25
+ plays on camera — a real shell reads a heredoc until its terminator.
26
+ - **Framerate 24, `Output demo.mp4` only.** A GIF per-frame render of a
27
+ multi-minute tape once filled the Docker VM disk. `demo.gif` is a short
28
+ downscaled ffmpeg preview cut from the mp4 afterwards (best-effort, never
29
+ gates the stage).
30
+ - Hidden preamble `cd /work && clear` so the guide's own `git clone ... .`
31
+ starts from the empty workspace.
32
+
33
+ ## Duration gate (`render.validate_outputs`)
34
+
35
+ `expected_min_duration_s` sums the tape's Sleeps + typing time; real command
36
+ execution (the Waits) only adds on top. A video shorter than ~0.8× that bound
37
+ means steps didn't play (stale image, aborted tape) → HARD error, never a
38
+ silently short clip. Bounds: 5s..1500s.
39
+
40
+ ## Coverage
41
+
42
+ `render` writes `tape_coverage.json` (guide_steps / tape_steps / dropped) and
43
+ prints any dropped step. With heredocs now on camera, a dropped step means a
44
+ genuinely ungrounded guide command — investigate, don't ignore.
45
+
46
+ ## ffmpeg
47
+
48
+ Used for the gif preview (`fps=10,scale=800`) and, on the host, `ffprobe` for
49
+ the duration check. Both run inside the base image for the preview; the host
50
+ ffprobe check is skipped when ffprobe isn't on PATH.
@@ -0,0 +1,36 @@
1
+ # Copy to `.env` (gitignored) and fill in the auth you use. You need ONE of the
2
+ # options below. Do NOT commit real secrets.
3
+
4
+ # Option A — run on your Claude subscription (no API key), self-hosted single
5
+ # operator. This is the default when no provider flag is given. Create the
6
+ # token with `claude setup-token` and paste it here.
7
+ # (Copy the printed sk-ant-oat01-... value; do not use $(claude setup-token).)
8
+ CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-...
9
+
10
+ # Option B — Anthropic metered API billing (scale, concurrency, or hosting for
11
+ # others). Also powers `--anthropic`, which runs the sandboxed agent on the
12
+ # OpenHands engine with a Claude model.
13
+ # ANTHROPIC_API_KEY=sk-ant-...
14
+ # Optional model override for `--anthropic` (default: the config model):
15
+ # ANTHROPIC_MODEL=claude-sonnet-5
16
+
17
+ # Option C — run the whole session on Google Gemini with `--gemini`. This one
18
+ # key powers both the planner/distiller/tutorial passes and the sandboxed agent
19
+ # (via the OpenHands engine). Install the extra: pip install 'readme2demo[gemini]'.
20
+ # GEMINI_API_KEY=...
21
+ # No Gemini model name is built into readme2demo (Google retires old ones with
22
+ # a hard 404). Name it per run (`--gemini <model>`) or set it once here:
23
+ # GEMINI_MODEL=gemini-3.5-flash
24
+
25
+ # Option D — run the whole session on OpenAI with `--openai`. Same shape as
26
+ # Gemini: one key for the passes and the OpenHands agent, no built-in model
27
+ # name. Install the extra: pip install 'readme2demo[openai]'.
28
+ # OPENAI_API_KEY=sk-...
29
+ # OPENAI_MODEL=gpt-5.1
30
+
31
+ # Optional — only for the experimental OpenHands engine (--engine openhands).
32
+ # The provider presets (--gemini / --openai / --anthropic) fill these in from
33
+ # the provider's key automatically; set them by hand only to point OpenHands
34
+ # at a different litellm model/provider.
35
+ # LLM_API_KEY=...
36
+ # LLM_MODEL=...
@@ -0,0 +1,10 @@
1
+ # Default review owner for everything in the repo.
2
+ # Replace @alphacrack with the GitHub handle(s) or team that should be
3
+ # auto-requested for review on every pull request.
4
+ * @alphacrack
5
+
6
+ # The grounding invariant lives in these files — guard them closely.
7
+ /src/readme2demo/distill.py @alphacrack
8
+ /src/readme2demo/verify.py @alphacrack
9
+ /src/readme2demo/normalize.py @alphacrack
10
+ /src/readme2demo/sandbox.py @alphacrack
@@ -0,0 +1,6 @@
1
+ # Adds a "Sponsor" button to the repo. Uncomment and fill in the ones you use.
2
+ # GitHub Sponsors must be enabled for your account first:
3
+ # https://github.com/sponsors
4
+ # github: [alphacrack]
5
+ # ko_fi: your_handle
6
+ # custom: ["https://readme2demo.dev/sponsor"]