Claude Cursor Skill

documenting-code

Imported from alexei-led/cc-thingz/dist/pi/skills/documenting-code.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download alexei-led-cc-thingz-dist_pi_skills_documenting-code-ce56bb4.zip · 17 KB
Part of alexei-led/cc-thingz — 91 skills

Install

skills CLI npx skills add https://github.com/alexei-led/cc-thingz/tree/master/dist/pi/skills/documenting-code
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install alexei-led-cc-thingz@llmmart
Git 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.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.

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

## 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 `&lt;` 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.

No comments yet.

Reviews (0)

No reviews yet.

Related