documenting-code
Imported from alexei-led/cc-thingz/dist/pi/skills/documenting-code.
Install
npx skills add https://github.com/alexei-led/cc-thingz/tree/master/dist/pi/skills/documenting-code
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install alexei-led-cc-thingz@llmmart
git clone https://github.com/alexei-led/cc-thingz.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole alexei-led/cc-thingz collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Documenting Code
Turn implementation facts into docs that a named reader can use. Every claim in the result matches the code, and every visual renders cleanly.
Pick the mode
- Update: a code change made docs stale. Change the smallest set of docs. Done when each changed behavior is documented where its reader looks, and nothing unrelated changed.
- Overhaul: the user asks to rewrite, restructure, or improve a doc set, for
example a README front page, guides, or an architecture doc. Done when:
- each doc has one reader and one job, and each fact has one owner
- every claim matches the code, and every visual passes its render check
- the gate passes
- A named doc or a named change is enough scope; start work. Ask only when the request names neither, with one question and these options: auto-detect from recent changes, README, API docs, or the full doc set.
Readers
Decide the reader before writing.
- Human: short, scannable text. Use a diagram, table, or chart when it
answers a question faster than prose. Load
references/doc-set.mdfor doc roles and outlines,references/style.mdfor language, andreferences/visuals.mdfor diagrams and charts. - Agent (AGENTS.md, CLAUDE.md, skills, prompts): terse operational text with
headers, bullets, numbered steps, exact contracts, and a table where it is the
clearest form. No diagrams or rationale that a model already knows. To score
or lint instruction files, use
reviewing-instructions. - Code: comments and docstrings state contracts, invariants, side effects,
errors, and non-obvious decisions. Delete comments that restate the code.
Load
references/code.mdfor shared comment rules and per-language doc checks.
Workflow
- Scope the work from the request and the changed files (
git diff --name-only), and find the existing docs that cover them. - List the doc files, the reader and the job of each, and the facts that more
than one doc states. In Overhaul mode, write the ownership map from
references/doc-set.mdbefore editing. - Read the code, tests, and configuration that each doc describes. When docs and code conflict, report it and update the docs to the code, unless the user says that the doc is the intended contract.
- Write. Keep each fact in one place and link to it from the others. Replace adjectives with measured facts.
- Check every claim against its source with
references/claims.md. Generate sample output from the real code. Mark each claim that you cannot confirm, and say where you looked. - Render every new or changed diagram and chart, look at the images, and fix
the faults named in
references/visuals.md. - Run the gate once. Run it again only after further edits.
- Report with the output contract.
Gate
Run the bundled scripts on the changed docs from the project root.
<skill-dir> is the directory that contains this SKILL.md, as the host
reports it. Do not use a scripts/ directory of the project instead.
python3 <skill-dir>/scripts/check-links.py <files or dirs> # relative links and #anchors
bash <skill-dir>/scripts/render-mermaid.sh <files or dirs> # renders each Mermaid block to PNG
python3 <skill-dir>/scripts/prose-lint.py <files or dirs> # advisory plain-language lint
- Open the rendered images. A diagram that parses can still have a bad layout.
- Also run the repo's own docs checks, for example
markdownlint-cli2or amakedocs target, when they exist. - Run documented commands and examples when practical.
Done when the relevant build/test/lint checks pass on what you changed, or you name each check that did not run and why.
Rules
- No speculative, future, or dead behavior.
- History (decision dates, "agreed with", replaced designs) belongs in git or the changelog, not in design docs.
- No secrets, tokens, private paths, or internal hosts.
- Generated docs: edit the source and run the generator.
- No ADRs or
docs/adr/changes unless explicitly requested. - Do not commit, push, or publish unless the user asks.
Output
## Documentation Update
Mode: update | overhaul
Updated:
- `path` — <what changed> (reader: <human | agent | code>)
Moved (overhaul only):
- <fact> → owned by `path`; other docs now link to it
Checked:
- claims: <n> checked against source; unconfirmed: none | <claim — where looked>
- visuals: <n> rendered and inspected | none changed
- gate: links <passed | failed>, diagrams <passed | skipped (reason)>, prose <n findings>
Issues: none | <remaining issue>
Without write access, return proposed changes (file, change, reason) instead of applying them.
Failure handling
- No stale docs found: say so and list what you checked.
- Large audit: one bounded read-only helper can map docs against code. Do not
trust its report. Check its claims and the actual diff (
git diff --stat) before you report success. - A check fails: quote the failure in Issues.
Files (cc-thingz)
-
assets
-
examples
-
decision-outcomes.mmd 572 B · in bundle
-
lifecycle.mmd 271 B · in bundle
-
pipeline.mmd 329 B · in bundle
-
request-flow.mmd 376 B · in bundle
-
system-context.mmd 670 B · in bundle
-
-
-
references
-
claims.md 2.6 KB
# Claims Use this reference to check each statement in a doc against its source. Docs that look correct often contain small false claims. Each class below produced real errors in reviewed docs. ## Check against the source Open the source line for each of these claims: - Commands, flags, and their effect. - Default values, limits, and thresholds. Compare tables of defaults with the configuration source, line by line. - Names that users see: labels, status text, error text, file names, paths. - Conditions: "X happens when Y". Check the full condition. Example of a real error: a doc said "the log rotates at start". The code rotates it at start only above 20 MB. - Direction of a dependency or a data flow. Read the imports and the call site. - Privacy and safety claims, for example "does not send files". Check what is sent, including user text that can contain the same data. - Counts, versions, and percentages. Parts must add up to the whole after rounding. Use one decimal when whole numbers do not add up. ## Generate samples from the code - Produce sample output (status lines, reports, error messages) by running the real code path: a script, a CLI command, or a test helper. Do not type it. - Make the numbers in a sample consistent with the rules that the doc explains. A sample that shows a threshold must use the formula that computes the threshold. - Mark a shortened sample with an ellipsis line (`…`). ## Measured claims A number that sells the project (savings, speed, accuracy) needs a baseline that a skeptical reader accepts. - Name the baseline in words, for example "a user who keeps the strongest model for the whole session". - Give both sides the same inputs. Remove only the effect under test. - Reject a baseline that pays for the product's own overhead. That inflates the result. - Reject a baseline with perfect conditions that the real alternative never gets. That hides the result. - Fix a data window with a cutoff time, and give the sample size. - If an assumption leans one way, say "lower bound" or "upper bound". If one assumption dominates the result, give the result at two or three values of it. - Include the data that goes against the claim, for example a second setup where the effect is zero. - Keep the analysis reproducible: a script path or a query. - Put the method, the tables, and the limits in an evaluation doc. The front page gets one line, the chart, and a link. ## Unconfirmed claims - If a claim cannot be checked, do not state it as fact. - Report it as `unconfirmed: <claim> — looked in <places>`. - Remove the claim when it is not necessary for the reader. -
code.md 1.5 KB
# Code Comments and API Docs Read when the change touches code comments, docstrings, public API docs, or code examples. ## All languages - Public API docs state what the name and types do not: behavior, constraints, errors, side effects, concurrency, and compatibility. Follow the project's existing doc style. - Keep comments that explain why: invariants, business rules, external limits, ordering requirements, workarounds, and deliberate suppressions (`unsafe`, `!`, `#[allow]`, casts). Delete comments that restate the code. - Tests explain themselves through names, table cases, and assertions. Comment only non-obvious external behavior or why an edge case matters. - README commands and examples match the current package manager, toolchain, and API. Run or compile examples when practical. ## Per language Prefer the project's configured checks. Use these when the tools exist and the project has no docs target. | Language | Doc checks | | --- | --- | | Go | `go doc <pkg>`, `golangci-lint run --enable=godot,godoclint,revive ./...` | | Python | `python -m pydoc <module>`, `sphinx-build -b html docs/ docs/_build/` | | Rust | `cargo doc --no-deps`, `cargo test --doc` | | TypeScript / JavaScript | `tsc --noEmit`, `typedoc` when configured | | C# / .NET | `dotnet build` with `GenerateDocumentationFile` (missing docs warn as CS1591) | | Java / Kotlin | `./gradlew javadoc`, `./mvnw javadoc:javadoc`, or the project's Dokka task | | Web (HTML, CSS, HTMX) | `npx html-validate .` | -
doc-set.md 4.5 KB
# Doc Set Use this reference to decide which doc owns which fact, and what each doc contains. The result is a doc set where each file has one reader, one job, and no copy of another file's facts. ## Roles - **README (front page)**: for a person who decides whether to try the project. Answers, in this order: what it is, why use it, how it works, how to install it. Link to everything else. - **User guide**: for a user after the install. Tasks in the order of use: check the setup, daily use, read the outputs, override defaults, update, stop or uninstall, troubleshoot. - **Configuration or API reference**: for a user who changes behavior. Each key, parameter, or endpoint with its default and meaning. Values come from the source, not from memory. - **Architecture**: for a contributor. How the system works inside: context, flows, decision logic, state, modules, failure handling, security, known limits. No history. - **Evaluation or benchmarks**: for a skeptical reader. The method, the data window, the tables behind each chart, and the limits. - **Contributing or develop**: build, test, and release commands. If the repo has no CONTRIBUTING file, a short section at the end of the architecture doc is enough. - **Changelog**: versioned project history and user-visible changes. Use `releasing-code` for release-note authoring and publication. ## Ownership map Write this map before an overhaul. It prevents duplicate facts and broken links. 1. List the facts that the current docs state, for example install steps, default values, tier or mode lists, output formats, and update steps. 2. For each fact, pick the owner doc by its reader. 3. In every other doc, replace the copy with a link or a one-line pointer. 4. After the edits, run `<skill-dir>/scripts/check-links.py` on the full doc set. Example map: ```text fact owner others install steps README#install user guide links to it update steps user-guide#update README: none tier list with purpose README#tiers architecture links to it tier to model mapping configuration#routes architecture links to it defaults of each key configuration nowhere else status line format user-guide architecture: endpoint only log retention configuration#data architecture links to it ``` ## README front page Answer the reader's two questions first: what is it, and what is in it for me. ```markdown # <project> <badges> **<one sentence: what it does, for whom>** <two or three sentences: the problem, and how the project removes it> > **Status:** <experimental | beta | stable>. <one sentence on maturity> ## Why use it - **<measured benefit>.** <fact with a number and its baseline> - **<second benefit>.** <fact> - **<what the user no longer does>.** <fact> <one chart or one diagram that proves the main claim; link to the method> ## How it works <one diagram of the main flow; three to five sentences> ## Install <requirements in one line; numbered steps; how to confirm that it works> ## Documentation - [User guide](docs/user-guide.md): <tasks it covers> - [Configuration](docs/configuration.md): <what it references> - [Architecture](docs/architecture.md): <what it explains> ``` Leave these out of the front page: update steps, develop commands, full configuration, troubleshooting, and history. ## User guide outline - Check the setup: three steps that prove the install works. - A short example of normal use, with a diagram if the behavior changes over time. - How to read each output that the user sees, with an annotated sample. - How to override or pin a default. - Update, and stop or uninstall. - Troubleshooting as a table: symptom, cause, fix. ## Architecture outline - System context diagram and three to five bullets on what crosses each boundary. - Main flow as a sequence diagram. - Decision logic as a flowchart with one box for each outcome. - State and lifecycle as state diagrams. - Modules: a dependency diagram and one line for each module. - Failure handling as a table: failure, behavior. - Security and privacy, known limits. ## Faults to remove - Stories and decision dates in design docs. - The same table or list in more than one doc. - Update, develop, or full configuration content on the front page. - Troubleshooting entries for versions that nobody runs. - Promotional adjectives in place of measured facts. - Headings that do not match their content. -
style.md 2.2 KB
# Style Use this reference for the language of human docs. It is a practical subset of ASD-STE100 Simplified Technical English. It makes each sentence readable in one pass, also for readers who are not native English speakers. If a `simple-english` skill is available and the user asks for strict STE, use that skill. ## Before writing - Pick one verb for each recurring concept, for example "check", and one noun, for example "configuration". Do not rotate synonyms. - Decide for each passage: procedure (tells the reader what to do) or description (explains what a thing is or does). ## Rules - Procedures: imperative verbs, 20 words or fewer for each sentence, one action for each sentence. Put a condition first: "If the build fails, read the log." - Descriptions: 25 words or fewer for each sentence, one new fact for each sentence, six sentences or fewer for each paragraph. - Active voice. Simple present, past, or future. - Use the modals "must", "can", and "will". Replace "should" with "must" for a requirement, or state a recommendation as a fact. Replace "may", "might", and "could" with "can". - No contractions. Keep articles and the word "that". - No semicolons. Write two sentences. - No filler: leverage, utilize, seamlessly, robust, powerful, simply, just, easily, "in order to", "it is worth noting". - No Latin abbreviations. Write "for example" and "that is", and name the items of a list in full. - Persuade with facts: a number with its baseline, not an adjective. - Leave code, commands, identifiers, file paths, and quoted output unchanged. ## Example Before: ```text You'll want to grab the API key from the dashboard before configuring the client, which you can easily do under Settings, since otherwise requests may fail. ``` After: ```text Get the API key from the dashboard, under Settings. Then configure the client with this key. Without the key, requests fail. ``` ## Self-check - Run `python3 <skill-dir>/scripts/prose-lint.py <files>`. It flags banned modals, contractions, semicolons, filler words, and long sentences. - Read the three longest sentences again, and split them if possible. - Make sure that each "if" and "when" starts its sentence in procedures. -
visuals.md 4.8 KB
# Visuals Use this reference for Mermaid diagrams, charts, and small annotated visuals in human docs. A visual earns its place when it answers a question faster than prose. Every visual needs a render check, because a diagram that parses can still be hard to read. ## Pick the form by the question - What talks to what: `flowchart LR` with the main component in the middle. Mark remote or external nodes by class and label, for example "(remote)". Example: `assets/examples/system-context.mmd`. - What happens in order, across components: `sequenceDiagram`. Show internal steps as `Note over`, not as self-messages. Example: `assets/examples/request-flow.mmd`. - How a decision is made: `flowchart TD` with one box for each outcome, labeled with the name that users see in logs or status. Example: `assets/examples/decision-outcomes.mmd`. - How a thing changes state: `stateDiagram-v2`. Example: `assets/examples/lifecycle.mmd`. - A pipeline of steps: `flowchart LR` with one class. Example: `assets/examples/pipeline.mmd`. - A small UI element, such as a status line: an annotated `text` block. - Reference data: a table. Troubleshooting: a symptom, cause, fix table. - A measured comparison: a chart, as a static SVG. ## Mermaid faults Check the rendered image for each fault. After the first render, add any new fault that you see to this list for the rest of the task. - An edge that crosses a box, or labels that overlap. Fix: merge a request and its response into one two-way edge (`A <-->|label| B`), or change the direction of the whole diagram. - An arrow that seems to connect the wrong nodes, because it passes behind another node. Fix: remove the subgraph around the target nodes, so each edge gets its own row. - A layout that depends on a subgraph `direction`. Mermaid ignores that direction when a node in the subgraph links outside it. Group by class and label instead, or keep all links of the subgraph inside it. - A shared end node, such as one "Stay" box, that pulls long edges across the diagram. Fix: one small box for each outcome. - The default yellow subgraph fill. Fix: `style <id> fill:#f8fafc,stroke:#94a3b8,color:#0f172a`. - Color as the only signal. Fix: put the meaning in the node text, and add one legend sentence under the diagram. - A color that means different things in different diagrams of the same doc set. Fix: one class palette for the doc set. - A label longer than about five words on one line. Fix: `<br/>` breaks. - HTML entities such as `<` in labels. They can render as literal text. - More than about 15 nodes. Fix: split the diagram, or move detail to a table. ## Class palette One palette keeps meaning consistent across diagrams. The fills are light, and the text color is explicit, so the nodes read on light and dark pages. ```text classDef core fill:#ecfdf5,stroke:#059669,color:#064e3b main component classDef ext fill:#eef2ff,stroke:#6366f1,color:#1e1b4b remote service classDef store fill:#fff7ed,stroke:#ea580c,color:#431407 storage, I/O classDef stay fill:#f1f5f9,stroke:#64748b,color:#0f172a no change classDef up fill:#fef3c7,stroke:#d97706,color:#451a03 increase, escalation classDef down fill:#e0f2fe,stroke:#0284c7,color:#082f49 decrease ``` ## Render and look 1. Run `bash <skill-dir>/scripts/render-mermaid.sh <doc.md>`. It writes one PNG for each Mermaid block and reports each block that fails to parse. 2. Open each PNG and check the fault list. 3. Fix, render again, and look again. If `mmdc` is not installed, the script says so. Then report that the diagrams are not render-checked. Keep them small, with one direction and few crossings. ## Charts - One message for each chart. Use one axis for each panel. Never use a dual axis. - Put the claim in the title. Put the sample size and the data window in the subtitle. - Direct labels on the bars. Text uses ink colors, not series colors. - The product in one accent color, baselines in grey. - A footnote with the key assumption, for example "lower bound", and a link to the method. - Add `<title>` and `<desc>` for screen readers. - Support dark mode with `@media (prefers-color-scheme: dark)` inside the SVG `<style>`, with its own background and ink colors. - Check contrast: 3:1 for marks, 4.5:1 for text. - Render with `rsvg-convert` and look at both themes. To preview dark mode, copy the SVG with the dark rules moved out of the media query. - Put a table of the chart data in the evaluation doc. - If a data-visualization skill is available, use it for palettes and checks. ## Small visuals Annotate a one-line output with a `text` block: ```text app ▸ model-name · high · upgrade │ │ └─ reason for the decision │ └─ effort └─ model of the last turn ``` Group a long table of codes by outcome, for example "changed" and "stayed", with one table for each group.
-
-
scripts
-
check-links.py 4 KB
#!/usr/bin/env python3 """Check relative links and #anchors in Markdown files. Usage: check-links.py [FILE_OR_DIR ...] (default: current directory) Checks each relative link target exists and each #anchor matches a heading in the target file, with GitHub heading slugs. External links (http, https, mailto) are skipped. Exit status 1 when a link is broken. """ from __future__ import annotations import re import sys import unicodedata from pathlib import Path SKIP_DIRS = {".git", "node_modules", "dist", "build", "vendor", ".venv", "venv"} LINK_RE = re.compile( r"(?<!!)\[[^\]]*\]\(([^)\s]+)(?:\s+\"[^\"]*\")?\)|<a\s+[^>]*href=\"([^\"]+)\"" ) IMAGE_RE = re.compile(r"!\[[^\]]*\]\(([^)\s]+)\)") HEADING_RE = re.compile(r"^(#{1,6})\s+(.*?)\s*#*\s*$") FENCE_RE = re.compile(r"^\s*(```|~~~)") EXTERNAL = ("http://", "https://", "mailto:", "tel:", "ftp://") def slugify(text: str) -> str: """GitHub-style slug: lowercase, drop punctuation, spaces to hyphens.""" text = re.sub(r"<[^>]+>", "", text) text = re.sub(r"!?\[([^\]]*)\]\([^)]*\)", r"\1", text) text = text.strip().lower() kept = [] for ch in text: category = unicodedata.category(ch) if ch in " -_" or category[0] in "LN" or category == "Mn": kept.append(ch) return "".join(kept).replace(" ", "-") def unfenced_lines(text: str): in_fence = False for number, line in enumerate(text.splitlines(), start=1): if FENCE_RE.match(line): in_fence = not in_fence continue if not in_fence: yield number, line def anchors_of(path: Path, cache: dict[Path, set[str]]) -> set[str]: if path not in cache: seen: dict[str, int] = {} anchors: set[str] = set() text = path.read_text(encoding="utf-8", errors="replace") for _, line in unfenced_lines(text): match = HEADING_RE.match(line) if not match: continue base = slugify(match.group(2)) count = seen.get(base, 0) anchors.add(base if count == 0 else f"{base}-{count}") seen[base] = count + 1 anchors.update(re.findall(r"<a\s+(?:name|id)=\"([^\"]+)\"", text)) cache[path] = anchors return cache[path] def markdown_files(args: list[str]) -> list[Path]: roots = [Path(a) for a in args] or [Path(".")] files: list[Path] = [] for root in roots: if root.is_file(): files.append(root) elif root.is_dir(): for path in sorted(root.rglob("*.md")): if not SKIP_DIRS.intersection(path.parts): files.append(path) else: print(f"{root}: not found", file=sys.stderr) return files def check(files: list[Path]) -> int: cache: dict[Path, set[str]] = {} broken = 0 checked = 0 for path in files: text = path.read_text(encoding="utf-8", errors="replace") for number, line in unfenced_lines(text): line = re.sub(r"`[^`]*`", "", line) targets = [m.group(1) or m.group(2) for m in LINK_RE.finditer(line)] targets += IMAGE_RE.findall(line) for target in targets: if target.startswith(EXTERNAL): continue checked += 1 file_part, _, anchor = target.partition("#") dest = ( (path.parent / file_part).resolve() if file_part else path.resolve() ) if file_part and not dest.exists(): print(f"{path}:{number}: missing file: {target}") broken += 1 continue if anchor and dest.suffix.lower() == ".md": if anchor.lower() not in anchors_of(dest, cache): print(f"{path}:{number}: missing anchor: {target}") broken += 1 print(f"checked {checked} links in {len(files)} files, {broken} broken") return 1 if broken else 0 if __name__ == "__main__": sys.exit(check(markdown_files(sys.argv[1:]))) -
prose-lint.py 4.4 KB
#!/usr/bin/env python3 """Advisory plain-language lint for Markdown prose. Usage: prose-lint.py [--max-words N] [--strict] [FILE_OR_DIR ...] Flags weak modals, contractions, semicolons, filler words, Latin abbreviations, and sentences longer than --max-words (default 25). Skips front matter, fenced code, inline code, tables, headings, and HTML. Exit status is 0 unless --strict is given and there are findings. """ from __future__ import annotations import argparse import re import sys from pathlib import Path SKIP_DIRS = {".git", "node_modules", "dist", "build", "vendor", ".venv", "venv"} FENCE_RE = re.compile(r"^\s*(```|~~~)") LIST_RE = re.compile(r"^\s*(?:[-*+]|\d+[.)])\s+") RULES = [ ("modal", re.compile(r"\b(should|would|might|could|may)\b", re.I)), ( "contraction", re.compile( r"\b\w+(?:n't|'ll|'re|'ve|'d|'m)\b|\b(?:it|that|there|here|what|let)'s\b", re.I, ), ), ("semicolon", re.compile(r";")), ( "filler", re.compile( r"\b(leverage|utilize|seamless(?:ly)?|robust|powerful|simply|just|easily|" r"in order to|it is worth noting|out of the box|under the hood)\b", re.I, ), ), ("latin", re.compile(r"\b(?:e\.g\.|i\.e\.|etc\.)", re.I)), ] def markdown_files(args: list[str]) -> list[Path]: roots = [Path(a) for a in args] or [Path(".")] files: list[Path] = [] for root in roots: if root.is_file(): files.append(root) elif root.is_dir(): files += [ p for p in sorted(root.rglob("*.md")) if not SKIP_DIRS.intersection(p.parts) ] return files def prose_units(text: str): """Yield (line, text) for each paragraph or list item outside code and tables.""" lines = text.splitlines() start = 0 if lines and lines[0].strip() == "---": for i in range(1, len(lines)): if lines[i].strip() == "---": start = i + 1 break in_fence = False unit: list[str] = [] unit_line = 0 for number, line in enumerate(lines[start:], start=start + 1): if FENCE_RE.match(line): in_fence = not in_fence if unit: yield unit_line, " ".join(unit) unit = [] continue stripped = line.strip() boundary = ( in_fence or not stripped or stripped.startswith(("#", "|", "<", ">", "![")) or LIST_RE.match(line) ) if boundary and unit: yield unit_line, " ".join(unit) unit = [] if in_fence or not stripped or stripped.startswith(("#", "|", "<", "![")): continue if not unit: unit_line = number unit.append(LIST_RE.sub("", stripped).lstrip("> ")) if unit: yield unit_line, " ".join(unit) def clean(text: str) -> str: text = re.sub(r"`[^`]*`", "X", text) text = re.sub(r"\[([^\]]*)\]\([^)]*\)", r"\1", text) text = re.sub(r"\([^)]*\)", "X", text) return re.sub(r"\*\*|__", "", text) def lint(path: Path, max_words: int) -> list[str]: findings = [] for number, unit in prose_units(path.read_text(encoding="utf-8", errors="replace")): text = clean(unit) for name, pattern in RULES: match = pattern.search(text) if match: findings.append( f"{path}:{number}: {name}: {match.group(0)!r} in: {unit[:90]}" ) for sentence in re.split(r"(?<=[.!?])\s+", text): words = len(sentence.split()) if words > max_words: findings.append( f"{path}:{number}: long-sentence: {words} words: {sentence[:90]}" ) return findings def main() -> int: parser = argparse.ArgumentParser( description="Advisory plain-language lint for Markdown prose." ) parser.add_argument("paths", nargs="*") parser.add_argument("--max-words", type=int, default=25) parser.add_argument("--strict", action="store_true") args = parser.parse_args() findings = [f for p in markdown_files(args.paths) for f in lint(p, args.max_words)] for finding in findings: print(finding) print(f"{len(findings)} findings") return 1 if args.strict and findings else 0 if __name__ == "__main__": sys.exit(main()) -
render-mermaid.sh 2.6 KB
#!/usr/bin/env bash # Render each Mermaid diagram in Markdown or .mmd files to PNG for visual review. # # Usage: render-mermaid.sh [--out DIR] [--extract] FILE_OR_DIR... # --out DIR write .mmd and .png files to DIR (default: a new temp directory) # --extract only extract the diagrams to .mmd files; do not render # # Needs mmdc (@mermaid-js/mermaid-cli) to render. Exit status: 0 all rendered, # 1 a diagram failed to render, 2 usage error or mmdc missing. set -euo pipefail out="" extract_only=0 inputs=() while [ $# -gt 0 ]; do case "$1" in --out) out="${2:?--out needs a directory}" shift 2 ;; --extract) extract_only=1 shift ;; -h | --help) sed -n '2,9p' "$0" exit 0 ;; *) inputs+=("$1") shift ;; esac done if [ ${#inputs[@]} -eq 0 ]; then echo "usage: render-mermaid.sh [--out DIR] [--extract] FILE_OR_DIR..." >&2 exit 2 fi if [ "$extract_only" -eq 0 ] && ! command -v mmdc >/dev/null 2>&1; then echo "mmdc not found: install @mermaid-js/mermaid-cli to render diagrams" >&2 exit 2 fi out="${out:-$(mktemp -d "${TMPDIR:-/tmp}/mermaid-render.XXXXXX")}" mkdir -p "$out" files=() for input in "${inputs[@]}"; do if [ -d "$input" ]; then while IFS= read -r f; do files+=("$f"); done < <( find "$input" -type f \( -name '*.md' -o -name '*.mmd' \) \ -not -path '*/node_modules/*' -not -path '*/.git/*' | sort ) elif [ -f "$input" ]; then files+=("$input") else echo "$input: not found" >&2 exit 2 fi done diagrams=() for f in "${files[@]}"; do base="$(basename "$f" | sed 's/[^A-Za-z0-9._-]/_/g')" if [ "${f##*.}" = "mmd" ]; then cp "$f" "$out/$base" diagrams+=("$out/$base|$f") continue fi count=$(awk -v out="$out" -v base="${base%.md}" ' /^[[:space:]]*```mermaid[[:space:]]*$/ { n++; inblock = 1; file = sprintf("%s/%s-%d.mmd", out, base, n); next } inblock && /^[[:space:]]*```[[:space:]]*$/ { inblock = 0; close(file); next } inblock { print > file } END { print n + 0 } ' "$f") for i in $(seq 1 "$count"); do diagrams+=("$out/${base%.md}-$i.mmd|$f#$i") done done if [ ${#diagrams[@]} -eq 0 ]; then echo "no Mermaid diagrams found" exit 0 fi failed=0 for entry in "${diagrams[@]}"; do mmd="${entry%%|*}" src="${entry#*|}" if [ "$extract_only" -eq 1 ]; then echo "extracted $src -> $mmd" continue fi png="${mmd%.mmd}.png" if log=$(mmdc -q -i "$mmd" -o "$png" -b white -s 1.5 2>&1); then echo "ok $src -> $png" else echo "FAIL $src: $(printf '%s\n' "$log" | grep -m1 -iE 'error|expect' || printf '%s' "$log" | head -1)" failed=1 fi done [ "$extract_only" -eq 1 ] || echo "open each PNG and check the layout: rendering proves only that it parses" exit "$failed"
-
-
SKILL.md 5.8 KB
--- {"description":"Write, rewrite, or update project docs from implementation facts - README front pages, user guides, configuration references, architecture docs, evaluations, agent instructions, and code comments. Use when docs are stale after a change, or when a doc set must become clear, visual, and consistent with the code. NOT for release preparation or release notes (use releasing-code), external library docs (looking-up-docs), scoring instruction files (reviewing-instructions), or ADRs unless explicitly requested.","name":"documenting-code"} --- <!-- Pi platform guidance --> <!-- Use installed Pi tool names exactly, including extension toolsets such as Task*, Monitor*, and Loop*. --> <!-- When available, track work with Task* (`todo` is the fallback), run long or background commands with MonitorCreate, and schedule follow-up with LoopCreate instead of sleep/poll loops. --> # Documenting Code Turn implementation facts into docs that a named reader can use. Every claim in the result matches the code, and every visual renders cleanly. ## Pick the mode - **Update**: a code change made docs stale. Change the smallest set of docs. Done when each changed behavior is documented where its reader looks, and nothing unrelated changed. - **Overhaul**: the user asks to rewrite, restructure, or improve a doc set, for example a README front page, guides, or an architecture doc. Done when: - each doc has one reader and one job, and each fact has one owner - every claim matches the code, and every visual passes its render check - the gate passes - A named doc or a named change is enough scope; start work. Ask only when the request names neither, with one question and these options: auto-detect from recent changes, README, API docs, or the full doc set. ## Readers Decide the reader before writing. - **Human**: short, scannable text. Use a diagram, table, or chart when it answers a question faster than prose. Load `references/doc-set.md` for doc roles and outlines, `references/style.md` for language, and `references/visuals.md` for diagrams and charts. - **Agent** (AGENTS.md, CLAUDE.md, skills, prompts): terse operational text with headers, bullets, numbered steps, exact contracts, and a table where it is the clearest form. No diagrams or rationale that a model already knows. To score or lint instruction files, use `reviewing-instructions`. - **Code**: comments and docstrings state contracts, invariants, side effects, errors, and non-obvious decisions. Delete comments that restate the code. Load `references/code.md` for shared comment rules and per-language doc checks. ## Workflow 1. Scope the work from the request and the changed files (`git diff --name-only`), and find the existing docs that cover them. 2. List the doc files, the reader and the job of each, and the facts that more than one doc states. In Overhaul mode, write the ownership map from `references/doc-set.md` before editing. 3. Read the code, tests, and configuration that each doc describes. When docs and code conflict, report it and update the docs to the code, unless the user says that the doc is the intended contract. 4. Write. Keep each fact in one place and link to it from the others. Replace adjectives with measured facts. 5. Check every claim against its source with `references/claims.md`. Generate sample output from the real code. Mark each claim that you cannot confirm, and say where you looked. 6. Render every new or changed diagram and chart, look at the images, and fix the faults named in `references/visuals.md`. 7. Run the gate once. Run it again only after further edits. 8. Report with the output contract. ## Gate Run the bundled scripts on the changed docs from the project root. `<skill-dir>` is the directory that contains this SKILL.md, as the host reports it. Do not use a `scripts/` directory of the project instead. ```bash python3 <skill-dir>/scripts/check-links.py <files or dirs> # relative links and #anchors bash <skill-dir>/scripts/render-mermaid.sh <files or dirs> # renders each Mermaid block to PNG python3 <skill-dir>/scripts/prose-lint.py <files or dirs> # advisory plain-language lint ``` - Open the rendered images. A diagram that parses can still have a bad layout. - Also run the repo's own docs checks, for example `markdownlint-cli2` or a `make` docs target, when they exist. - Run documented commands and examples when practical. Done when the relevant build/test/lint checks pass on what you changed, or you name each check that did not run and why. ## Rules - No speculative, future, or dead behavior. - History (decision dates, "agreed with", replaced designs) belongs in git or the changelog, not in design docs. - No secrets, tokens, private paths, or internal hosts. - Generated docs: edit the source and run the generator. - No ADRs or `docs/adr/` changes unless explicitly requested. - Do not commit, push, or publish unless the user asks. ## Output ```markdown ## Documentation Update Mode: update | overhaul Updated: - `path` — <what changed> (reader: <human | agent | code>) Moved (overhaul only): - <fact> → owned by `path`; other docs now link to it Checked: - claims: <n> checked against source; unconfirmed: none | <claim — where looked> - visuals: <n> rendered and inspected | none changed - gate: links <passed | failed>, diagrams <passed | skipped (reason)>, prose <n findings> Issues: none | <remaining issue> ``` Without write access, return proposed changes (file, change, reason) instead of applying them. ## Failure handling - No stale docs found: say so and list what you checked. - Large audit: one bounded read-only helper can map docs against code. Do not trust its report. Check its claims and the actual diff (`git diff --stat`) before you report success. - A check fails: quote the failure in Issues.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.