spec-distiller
Skill 2 of the YouTube intelligence pipeline — a consumption-layer analysis.
It reads the lossless artifacts produced by youtube-artifact-collector (Skill 1)
and produces a Module → Feature → Requirement document. It never touches
YouTube; the only input is artifact JSON, and the only coupling to Skill 1 is the
artifact's schema_version (read defensively).
When to use this skill
Use it when the request is to analyze already-collected artifacts:
- "Extract the requirements from this artifact."
- "What features and business rules does this video/collection demonstrate?"
- "Build a requirements/spec document from this collection."
If the user instead wants to collect metadata/transcripts from URLs or a
playlist, that is Skill 1 (youtube-artifact-collector), not this skill.
Engines
This skill ships two interchangeable engines that emit the same output shape:
claude (default, offline) — no script runs. You (Claude) read the artifact
and the external prompt/template files in-chat and produce the document. No API
key required.
openai — runs scripts/extract_requirements.py, which calls the OpenAI
API. Selected with --engine openai; needs OPENAI_API_KEY (see
.env.example).
The prompts and template are external and swappable — editing them changes the
output for both engines without any code change.
Inputs
Either:
- a single artifact file — an artifact
.json produced by Skill 1 (named after the
video's title, e.g. what-is-claude-code.json), or
- a collection folder — a directory containing
_manifest.json; process each
member whose status is ok (in manifest order), using the manifest for
module/collection context.
Claude-native engine — how to run it (default)
When asked to extract requirements with the default engine, do this in-chat:
- Resolve the input. If given an artifact
.json, use it directly. If given
a collection folder, read its _manifest.json and process every member with
status: ok, in order, resolving each member's file through its files.json
entry rather than reconstructing the name; the manifest also gives the
collection/module title.
- Read the artifact JSON. Pull the
video{} block (id, title, url, channel,
description), the collection{} block (module/collection title), and the
transcript.segments[] (each with its stable index, start, and verbatim
text). Treat transcript text as read-only — never edit it.
- Read the prompt files at runtime (this is what makes them swappable):
prompts/system_prompt.md — the analyst role, the strict output JSON
contract, and the <MODULE>-<FEATURE>-<NNN> id rules.
prompts/extraction_prompt.md — the task instructions, the
{{placeholders}} for the artifact data, and the module/action code
lookup table.
- Fill the prompt by substituting the artifact values for the placeholders
(
{{video_id}}, {{video_title}}, {{video_url}}, {{channel}},
{{description}}, {{collection_title}}, {{transcript}}).
- Produce the structured document following the system-prompt contract:
summary, modules[] → features[] → requirements[] (each requirement with
id = <MODULE>-<FEATURE>-<NNN>, text, source_video_id, and a
trace{timestamp, segment_index}), plus assumptions and open_questions.
Number requirements video-locally from 001; never embed the video id in
an id.
- Render and save. Format the document using
templates/requirement_doc.md and write <basename>.requirements.md plus a
mirrored <basename>.requirements.json alongside the source artifact (same
collection folder or _singles/), unless the user asks to print instead.
<basename> is the source artifact's filename without its extension —
01-tek-tek-ogrenci-yukleme.json → 01-tek-tek-ogrenci-yukleme.requirements.md.
Never rebuild the name from the video id: Skill 1 owns the naming policy, and
mirroring the input keeps both engines emitting identical filenames.
OpenAI engine — how to run it (optional)
uv run skills/spec-distiller/scripts/extract_requirements.py \
<artifact.json | collection_dir> --engine openai [flags]
It loads the same prompts/ and templates/ files, fills the placeholders,
calls OpenAI requesting structured JSON output, and renders the same document
shape. Configuration precedence is CLI flag > env var > built-in default.
| Flag |
Env var |
Default |
Meaning |
--engine claude|openai |
— |
claude |
Engine selector. |
--model NAME |
OPENAI_MODEL |
gpt-4o-mini |
OpenAI model. |
--temperature T |
OPENAI_TEMPERATURE |
0.2 |
Sampling temperature. |
--max-tokens N |
OPENAI_MAX_TOKENS |
4096 |
Max completion tokens. |
--response-format json_schema|text |
OPENAI_RESPONSE_FORMAT |
json_schema |
Structured-output mode. |
--timeout S |
OPENAI_TIMEOUT |
60 |
Request timeout (seconds). |
--retries N |
OPENAI_RETRIES |
3 |
Retry attempts. |
--concurrency N |
OPENAI_CONCURRENCY |
4 |
Parallel requests across members. |
--out-dir NAME |
— |
alongside source |
Override the output directory. |
--no-save / --print |
— |
off |
Print instead of writing files. |
The key is read from a .env in the working directory (then process env); copy
.env.example to .env and set OPENAI_API_KEY. A missing key fails with a
clear, secret-safe error.
Output
<basename>.requirements.md + <basename>.requirements.json, where <basename>
mirrors the source artifact's filename (the JSON mirrors the document). The
document = source header (title / url / channel / collection)
- summary + Module→Feature→Requirements (with per-requirement traces) +
Assumptions + Open Questions.
Swapping the analysis
Because the prompt and template files are read at runtime, switching the analysis
task (e.g. business-rule extraction, product discovery, a different doc style, or
a different module/action code scheme) is done by editing
prompts/extraction_prompt.md / prompts/system_prompt.md /
templates/requirement_doc.md — no code change, and the change applies to both
engines.
1---2name: spec-distiller3description: Turn already-extracted YouTube artifacts (the JSON produced by the youtube-artifact-collector skill) into a structured Module→Feature→Requirement document, with each requirement traced back to a transcript segment. Use when the user wants requirement extraction, feature/product discovery, business-rule extraction, or a spec/requirements document FROM existing artifacts — e.g. "extract requirements from this artifact", "what features does this video demonstrate", "build a requirements doc from this collection". Runs offline by default (Claude-native, no API key); an optional OpenAI engine is available via `--engine openai`. This consumes Skill 1 JSON, not URLs — it does not download videos or transcripts.4---56# spec-distiller78Skill 2 of the YouTube intelligence pipeline — a **consumption-layer** analysis.9It reads the lossless artifacts produced by `youtube-artifact-collector` (Skill 1)10and produces a **Module → Feature → Requirement** document. It never touches11YouTube; the only input is artifact JSON, and the only coupling to Skill 1 is the12artifact's `schema_version` (read defensively).1314## When to use this skill1516Use it when the request is to **analyze already-collected artifacts**:1718- "Extract the requirements from this artifact."19- "What features and business rules does this video/collection demonstrate?"20- "Build a requirements/spec document from this collection."2122If the user instead wants to *collect* metadata/transcripts from URLs or a23playlist, that is Skill 1 (`youtube-artifact-collector`), not this skill.2425## Engines2627This skill ships two interchangeable engines that **emit the same output shape**:2829- **`claude` (default, offline)** — no script runs. You (Claude) read the artifact30 and the external prompt/template files in-chat and produce the document. No API31 key required.32- **`openai`** — runs `scripts/extract_requirements.py`, which calls the OpenAI33 API. Selected with `--engine openai`; needs `OPENAI_API_KEY` (see34 `.env.example`).3536The prompts and template are **external and swappable** — editing them changes the37output for *both* engines without any code change.3839## Inputs4041Either:4243- a single artifact file — an artifact `.json` produced by Skill 1 (named after the44 video's title, e.g. `what-is-claude-code.json`), or45- a **collection folder** — a directory containing `_manifest.json`; process each46 member whose `status` is `ok` (in manifest order), using the manifest for47 module/collection context.4849## Claude-native engine — how to run it (default)5051When asked to extract requirements with the default engine, do this in-chat:52531. **Resolve the input.** If given an artifact `.json`, use it directly. If given54 a collection folder, read its `_manifest.json` and process every member with55 `status: ok`, in order, resolving each member's file through its `files.json`56 entry rather than reconstructing the name; the manifest also gives the57 collection/module title.582. **Read the artifact JSON.** Pull the `video{}` block (id, title, url, channel,59 description), the `collection{}` block (module/collection title), and the60 `transcript.segments[]` (each with its stable `index`, `start`, and verbatim61 `text`). Treat transcript text as read-only — never edit it.623. **Read the prompt files** at runtime (this is what makes them swappable):63 - `prompts/system_prompt.md` — the analyst role, the strict output JSON64 contract, and the `<MODULE>-<FEATURE>-<NNN>` id rules.65 - `prompts/extraction_prompt.md` — the task instructions, the66 `{{placeholders}}` for the artifact data, and the **module/action code67 lookup table**.684. **Fill the prompt** by substituting the artifact values for the placeholders69 (`{{video_id}}`, `{{video_title}}`, `{{video_url}}`, `{{channel}}`,70 `{{description}}`, `{{collection_title}}`, `{{transcript}}`).715. **Produce the structured document** following the system-prompt contract:72 `summary`, `modules[] → features[] → requirements[]` (each requirement with73 `id` = `<MODULE>-<FEATURE>-<NNN>`, `text`, `source_video_id`, and a74 `trace{timestamp, segment_index}`), plus `assumptions` and `open_questions`.75 Number requirements **video-locally from `001`**; never embed the video id in76 an `id`.776. **Render and save.** Format the document using78 `templates/requirement_doc.md` and write `<basename>.requirements.md` plus a79 mirrored `<basename>.requirements.json` alongside the source artifact (same80 collection folder or `_singles/`), unless the user asks to print instead.81 `<basename>` is the **source artifact's filename without its extension** —82 `01-tek-tek-ogrenci-yukleme.json` → `01-tek-tek-ogrenci-yukleme.requirements.md`.83 Never rebuild the name from the video id: Skill 1 owns the naming policy, and84 mirroring the input keeps both engines emitting identical filenames.8586## OpenAI engine — how to run it (optional)8788```bash89uv run skills/spec-distiller/scripts/extract_requirements.py \90 <artifact.json | collection_dir> --engine openai [flags]91```9293It loads the **same** `prompts/` and `templates/` files, fills the placeholders,94calls OpenAI requesting structured JSON output, and renders the same document95shape. Configuration precedence is **CLI flag > env var > built-in default**.9697| Flag | Env var | Default | Meaning |98| --- | --- | --- | --- |99| `--engine claude\|openai` | — | `claude` | Engine selector. |100| `--model NAME` | `OPENAI_MODEL` | `gpt-4o-mini` | OpenAI model. |101| `--temperature T` | `OPENAI_TEMPERATURE` | `0.2` | Sampling temperature. |102| `--max-tokens N` | `OPENAI_MAX_TOKENS` | `4096` | Max completion tokens. |103| `--response-format json_schema\|text` | `OPENAI_RESPONSE_FORMAT` | `json_schema` | Structured-output mode. |104| `--timeout S` | `OPENAI_TIMEOUT` | `60` | Request timeout (seconds). |105| `--retries N` | `OPENAI_RETRIES` | `3` | Retry attempts. |106| `--concurrency N` | `OPENAI_CONCURRENCY` | `4` | Parallel requests across members. |107| `--out-dir NAME` | — | alongside source | Override the output directory. |108| `--no-save` / `--print` | — | off | Print instead of writing files. |109110The key is read from a `.env` in the working directory (then process env); copy111`.env.example` to `.env` and set `OPENAI_API_KEY`. A missing key fails with a112clear, secret-safe error.113114## Output115116`<basename>.requirements.md` + `<basename>.requirements.json`, where `<basename>`117mirrors the source artifact's filename (the JSON mirrors the document). The118document = source header (title / url / channel / collection)119+ summary + Module→Feature→Requirements (with per-requirement traces) +120Assumptions + Open Questions.121122## Swapping the analysis123124Because the prompt and template files are read at runtime, switching the analysis125task (e.g. business-rule extraction, product discovery, a different doc style, or126a different module/action code scheme) is done by **editing127`prompts/extraction_prompt.md` / `prompts/system_prompt.md` /128`templates/requirement_doc.md`** — no code change, and the change applies to both129engines.