| name | docs-to-c4 |
| description | Transform a folder of mixed-format source documents (Markdown, PDF, DOCX, XLSX, images, etc.) into a C4 architecture model. Runs bmad-distillator on the sources, synthesizes a Structurizr DSL workspace (at minimum a System Context view; adds Container and Component views when the documents support them), and generates a browsable static site with `structurizr-site-generatr`. Also exposes a serve command so the model can be explored in a browser. Use this skill whenever the user wants to derive architecture diagrams, a C4 model, or a Structurizr workspace from existing documentation — even when they don't use those exact terms. Common triggers include "create an architecture diagram from these docs", "I have a folder of design docs, visualise the system", "generate a C4 model for this project", "build a Structurizr workspace", "reverse-engineer an architecture from docs". |
| argument-hint | <input_folder> [system_name] |
docs-to-c4
Turns a heterogeneous pile of documents into a living, browsable C4 model.
When to use
The user hands you a folder of documents describing a system and asks for architecture diagrams, a C4 model, a Structurizr workspace, or a visualisable design. The skill runs end-to-end:
- Distill the folder losslessly with
bmad-distillator.
- Synthesise a Structurizr DSL workspace — always a System Context view, plus Containers and Components whenever the distillate supports them.
- Generate a static site with
structurizr-site-generatr generate-site.
- Do not start
structurizr-site-generatr serve automatically.
- Provide command to the user so that they can start the live preview server when they're ready.
Every artefact lands in an output/ subfolder inside the input folder itself, so the model and the sources live together and the output travels with them.
Inputs
| Parameter | Required | Notes |
|---|
input_folder | yes | Absolute or project-relative path to a folder containing the source documents. The folder is searched recursively. Mixed formats are fine — bmad-distillator handles the heavy lifting. |
system_name | optional | Human-readable name for the software system. If the user does not supply one, pick a sensible default from the documents and confirm it with them. |
open_browser | optional | If the user asks for "serve" / "view" / "open in browser", also run serve_site.sh after generation. |
If the user hasn't explicitly told you which folder to use, ask — never guess.
Output layout
All generated artefacts go under an output/ subfolder of the input folder:
<input_folder>/
├── ...source documents (untouched)...
└── output/
├── converted/ # Binary docs (.docx, .pdf, .xlsx) converted to .md
├── distilled/ # bmad-distillator output (single file or split folder)
├── workspace.dsl # Structurizr DSL — the model
├── site/ # Static site generated by structurizr-site-generatr
└── README.md # How to regenerate and serve the model
So if the input folder is /Users/me/docs/acme-system, the workspace DSL ends up at /Users/me/docs/acme-system/output/workspace.dsl and the generated site at /Users/me/docs/acme-system/output/site/.
If <input_folder>/output/ already exists, do not silently overwrite. Show a diff of what would change and confirm with the user before replacing files.
Workflow
Step 1 — Preflight checks (fail loud)
Run scripts/check_prereqs.sh before doing anything else. It verifies:
java is on PATH and is version 17 or higher (structurizr-site-generatr requires it).
structurizr-site-generatr is on PATH.
The script exits non-zero with a clear install hint if either is missing. Stop the skill and surface the hint to the user — do not attempt to auto-install.
You also need the bmad-distillator skill to be available. If you cannot invoke it (because it's not installed in this Claude Code session), stop and tell the user how to install it (typically: install the BMAD skill bundle). Do not try to hand-roll distillation.
Step 2 — Resolve and validate the input folder
- Expand
~ and relative paths against the current working directory.
- Confirm the folder exists, is a directory, and contains at least one file (recursive).
- List a short sample of the files you found so the user can spot obvious problems early (e.g., they pointed you at the wrong folder).
Step 3 — Create the output scaffold
Let INPUT be the absolute path of the input folder and OUT=$INPUT/output. Create:
$OUT/
$OUT/converted/
$OUT/distilled/
Never create anything outside $OUT. The skill does not touch files elsewhere in the project.
Step 4 — Convert binary documents to Markdown
Binary formats (.docx, .pdf, .xlsx) are large because they contain embedded images, XML structure, and formatting overhead. Converting them to Markdown first dramatically reduces size (typically 50-100x) and makes downstream distillation faster and more reliable.
Run scripts/convert_docs_to_md.sh "$INPUT" "$OUT/converted/". This shell wrapper creates a Python virtual environment (if needed), installs dependencies, and runs the conversion. It:
- Converts
.docx, .pdf, .xlsx files to .md (extracting text, tables, and headings)
- Copies text-based files (
.md, .txt, .csv, .json, .yaml) as-is
- Preserves the relative directory structure from the input folder
- Skips the
output/ subfolder to avoid processing its own previous outputs
- Outputs a JSON report with per-file conversion results and token estimates
If the conversion script fails or reports errors for specific files, surface the errors to the user and continue with the files that did convert successfully.
Step 5 — Distill the documents
Invoke the bmad-distillator skill with:
source_documents = $OUT/converted/ — the folder of converted Markdown files from Step 4, not the original binary documents.
downstream_consumer = "C4 architecture modelling (Structurizr DSL) — System Context + Container + Component views"
output_path = $OUT/distilled/ (absolute path)
Do not try to read the source documents yourself before distillation. bmad-distillator is designed to be the single point of contact with the raw sources; letting it do its job keeps the pipeline reproducible and avoids duplicating work.
When bmad-distillator returns, note the distillate path(s) and token counts from its JSON result. If it returned a split distillate (a folder with _index.md + section files), read _index.md first and then the section files. If it returned a single file, just read that.
Step 6 — Synthesise the C4 model
This is the judgement-heavy part of the skill. Work from the distillate only.
Read references/c4-model-guide.md for a refresher on the C4 levels and what belongs in each, and references/structurizr-dsl-guide.md for DSL syntax patterns you can copy.
Start from assets/workspace-template.dsl, then fill it in as follows.
Always produce:
- People / actors. Everyone who interacts with the system (end users, admins, external teams, upstream/downstream systems owned by others).
- The software system itself. Named per
system_name, with a one-line description.
- External software systems. Third-party services, upstream producers, downstream consumers — anything the system integrates with that it doesn't own. Also include systems the docs explicitly mention as having no integration (e.g., "no integration with CaseMan"). These are architecturally significant boundaries — model them as external systems and label the relationship
"No integration (explicit boundary)". Omitting them loses important context about what the system deliberately does not connect to.
- System Context view (
systemContext) showing all of the above, with labelled relationships.
Add when the distillate supports it (it almost always will for non-trivial systems):
- Containers. Deployable/runnable units inside the system: web apps, APIs, databases, queues, workers, mobile apps, serverless functions, etc. Include the technology (e.g.,
"Node.js / Express", "PostgreSQL 15"). Add a Container view (container).
- Components. For each container where the docs describe internal structure (controllers, services, repositories, domain modules, etc.), add components and a Component view (
component). Don't invent components the docs don't describe — it's fine to cover only one or two containers at the component level.
Nice to have when the evidence is clear:
- Dynamic view for a key user journey that the docs describe end-to-end.
- Deployment view if the docs describe environments, regions, or deployment topology.
Rules of engagement:
- Every element and relationship must be traceable to the distillate. If you can't point to a sentence that justifies it, don't put it in the model. When in doubt about whether something exists, omit. But when the distillate explicitly names a system — even to say "no integration" or "not connected" — include it. Explicit non-relationships define system boundaries just as much as active integrations do.
- Label every relationship with both a verb phrase ("Reads from", "Publishes events to") and, where documented, a technology/protocol ("HTTPS/JSON", "gRPC", "JDBC").
- Keep identifiers short and code-friendly (
webApp, ordersApi, paymentsDb). Keep names human-friendly ("Web App", "Orders API").
- Apply the styles from
assets/workspace-template.dsl so views render nicely out of the box — do not reinvent the style block.
- If the distillate has clear gaps (e.g., "no mention of databases anywhere"), add a
# GAPS comment block at the top of workspace.dsl listing them, so the user knows what to backfill.
Write the final DSL to $OUT/workspace.dsl.
Step 7 — Validate the DSL
Run scripts/validate_dsl.sh "$OUT/workspace.dsl". This does a dry-run site generation into a user-local scratch dir and fails loudly if the DSL has syntax errors. Fix any errors before moving on — structurizr-site-generatr surfaces useful line numbers.
Step 8 — Generate the static site
Run scripts/generate_site.sh "$OUT/workspace.dsl" "$OUT/site". The generated site is fully static — it can be opened directly or checked in alongside the source docs for teammates to browse.
Step 9 — Write the output README
Write $OUT/README.md with:
- A one-paragraph summary of the system, drawn from the distillate.
- Regenerate the model: the exact command using the absolute path to
scripts/generate_site.sh within the skill directory, and absolute paths to workspace.dsl and site/. Resolve the skill directory at runtime (e.g., via realpath on SKILL.md's location) rather than hard-coding $HOME guesses.
- View interactively: the exact command using the absolute path to
scripts/serve_site.sh and absolute path to workspace.dsl.
- A list of known gaps (mirroring the
# GAPS block in workspace.dsl), if any.
Why absolute paths, not ... placeholders or relative paths: the user typically runs these commands from their project root or anywhere else, not from inside $OUT/. A placeholder (.../scripts/serve_site.sh) is not a real path — zsh will reject it. Relative paths break the moment the user cds somewhere else. Absolute paths just work from any shell.
Show only the absolute-path invocations in the README. Do not add Makefiles, shell aliases, or alternative forms — one documented way to run each command, end of story.
Step 10 — Print the serve command and offer to run it
Always print the exact shell command the user can copy-paste to start the live preview server. Use the absolute path to scripts/serve_site.sh within the skill directory and the absolute path to $OUT/workspace.dsl. Format it as a fenced code block so the user can copy it easily. Example:
/absolute/path/to/scripts/serve_site.sh /absolute/path/to/output/workspace.dsl
Then tell the user: the server runs at http://localhost:8080 by default, and they can stop it with Ctrl+C.
After printing the command, ask whether the user wants you to start it now in the background. If they say yes (or if they originally asked to "view" / "serve" / "open in browser"), run the command in the background and confirm the URL.
If port 8080 is already in use, try the next free port in 8081–8090 and pass it with --port.
Regeneration
The skill is idempotent. Re-running it on the same input folder should produce an equivalent model (modulo small wording changes). If the user edits workspace.dsl by hand, never overwrite their edits blindly — always show a diff and confirm first.
Failure modes and how to report them
| Failure | What to do |
|---|
| Java missing / <25 | Stop. Tell user to brew install openjdk@25 (macOS) or install from adoptium.net. |
structurizr-site-generatr missing | Stop. Tell user to brew tap avisi-cloud/tools && brew install structurizr-site-generatr or download from https://github.com/avisi-cloud/structurizr-site-generatr/releases. |
bmad-distillator skill not available | Stop. Tell user this skill depends on bmad-distillator and must be installed alongside it. |
| Document conversion fails | Surface the error from convert_docs_to_md.sh. Common cause: missing Python 3. If only some files fail, continue with the ones that converted. |
| Input folder empty / not a folder | Stop. Ask the user to double-check the path. |
| Distillate is too thin to build a Container view | Build only the System Context view, record a gap in the # GAPS block, continue. |
| DSL validation fails | Fix the DSL (usually a typo or missing relationship target) and re-validate — do not ship a broken workspace. |
| Port 8080 busy | Try 8081–8090 with --port. If all busy, tell the user and let them pick. |
What this skill does NOT do
- It does not infer architecture that isn't in the documents. Hallucinating services, databases, or relationships is worse than omitting them — the user loses trust in the diagram.
- It does not call any LLM cloud service directly. The distillation and DSL synthesis happen inside this Claude session.
- It does not modify the user's source documents.
- It does not commit to git.
Reference files
references/c4-model-guide.md — quick primer on C4 levels and what belongs where.
references/structurizr-dsl-guide.md — Structurizr DSL syntax crib sheet with copy-pasteable blocks.
assets/workspace-template.dsl — starting template with styles and default views.
scripts/convert_docs_to_md.sh — converts binary docs (.docx, .pdf, .xlsx) to Markdown; manages its own venv.
scripts/python/convert_docs_to_md.py — Python implementation of the converter.
scripts/python/requirements.txt — Python dependencies for the converter (python-docx, pypdf, openpyxl).
scripts/check_prereqs.sh — preflight check, exits non-zero if tools are missing.
scripts/validate_dsl.sh — dry-run site generation to catch DSL errors early.
scripts/generate_site.sh — full site generation.
scripts/serve_site.sh — starts the live preview server.