One command

Two documentation tracks, forged from your code.

A manual your users can follow and a reference your engineers can trust are different documents. Docsmith writes both, reads the repository to do it, and tells you when they stop matching the code.

one command, read against your repository

docs/user/

For the person using the product. No module paths, no signatures, no jargon.

  • getting-started.md
  • features/<slug>.md
  • troubleshooting.md
  • faq.md

docs/developer/

For the engineer calling or extending it. Full signatures, every parameter, every version.

  • architecture.md
  • api/<version>/<resource>.md
  • reference/classes.md
  • extending.md

Install

Two ways in.

Use it inside Claude Code as a plugin, or install the CLI and drive it yourself. The CLI is what you want for CI, for scripting, and for running entirely on your own machine.

As a Claude Code plugin

No API key, no Python. Claude Code does the generation.

claude code
/plugin marketplace add nikolareljin/claude-plugins
/plugin install claude-docsmith@nikolareljin-plugins
# restart Claude Code, then
/plugin
# if needed: /reload-plugins
/claude-docsmith:nr-update-docs

As a standalone CLI

Python 3.10 or newer, tested through 3.12. Not on PyPI yet, so install from the repository.

shell
pipx install git+https://github.com/nikolareljin/claude-docsmith.git
claude-docsmith --version
claude-docsmith 1.4.0

In Claude Code

Say the word, get both tracks.

Run /claude-docsmith:nr-update-docs in the repository you want documented. Docsmith reads the code, the config, the commands and the tests, then writes each track separately so neither is squeezed to fit the other.

claude code
/claude-docsmith:nr-update-docs

Reading README.md, pyproject.toml, src/**, tests/**
Writing the user manual...
  docs/user/index.md            created
  docs/user/getting-started.md  created
Writing the developer reference...
  docs/developer/architecture.md  updated
  docs/developer/api/v1/orders.md created
Updated docs/.docsmith/manifest.json

Standalone

Run the whole thing locally, with Ollama.

No API key and nothing leaves your machine. Docsmith asks Ollama which models you actually have, so there is no model name to look up and nothing to keep in sync when you pull a new one.

  1. Point Ollama at a model you have

    Any instruct model works. Bigger models write noticeably better documentation.

    shell
    ollama list
    NAME              SIZE      MODIFIED
    qwen2.5:14b       9.0 GB    2 days ago
    llama3.3:70b      42 GB     3 weeks ago
  2. See what will be sent, before anything is

    A dry run prints one prompt per track and never contacts a model. Credentials are stripped and sensitive files are skipped outright.

    shell
    claude-docsmith . --dry-run
    Skipped 1 sensitive file(s): src/.env
    Redacted 1 credential-shaped value(s) before prompting (github-token).
    === AUDIENCE: user ===
    ...
    === AUDIENCE: developer ===
  3. Generate

    One call per track. Docsmith prints the model it picked, so a run is never quietly using something you did not expect.

    shell
    claude-docsmith . --provider ollama --output-json docsmith-output.json
    Using ollama model: qwen2.5:14b
    
    # pin one instead, if you prefer
    claude-docsmith . --provider ollama --model llama3.3:70b --timeout 1800
    # or set it once
    export DOCSMITH_OLLAMA_MODEL=qwen2.5:14b
  4. Review, then write it into the repo

    The JSON lists every file before anything is written. A page that falls outside its own track is refused, and nothing is written at all if any file is rejected.

    shell
    claude-docsmith . --input-json docsmith-output.json --apply
    - docs/user/index.md (user, create)
    - docs/developer/index.md (developer, create)
    Wrote 2 file(s) and updated docs/.docsmith/manifest.json.
  5. Keep them honest

    Later, ask whether the docs still match the code. Fully offline, no API key, safe on a fork pull request.

    shell
    claude-docsmith . --check
    Documentation is stale (generated 2026-07-30T14:29:42Z):
    - src/client.py (changed since docs were generated)
    echo $?
    1

What lands in your repo

Two trees, and a record of how they got there.

Each track owns its directory, so you can publish one without the other — a wiki for your users, a docs site for your engineers, or both.

docs/
├── user/                      the manual
│   ├── index.md
│   ├── getting-started.md
│   ├── features/<slug>.md      every option, default, and when to change it
│   ├── screenshots/
│   ├── troubleshooting.md
│   └── faq.md
├── developer/                 the reference
│   ├── index.md
│   ├── architecture.md
│   ├── api/<version>/<resource>.md   v1 and v2 never share a page
│   ├── reference/classes.md    signatures, bases, raised errors
│   └── extending.md
└── .docsmith/manifest.json    what was written, and from which sources

Guard rails

What it refuses to do.

never reads secrets

.env, private keys, keystores, .npmrc, credential stores and Terraform state are skipped before they are opened. Credential-shaped values in files it does read are replaced with a marker.

--fail-on-secret

Exits non-zero when the repository contains credential-shaped content. Prints the file and line, never the value.

--check

Compares the manifest against the current sources and exits non-zero on drift. No network, no API key.

stays in its lane

A generated page can only be written inside its own track. It never edits your source code.