llm-mddocgen - Large Language Model Markdown Document Generator
Project description
Large Language Model Markdown Document Generator (llm-mddocgen)
llm-mddocgen is a markdown based prompt builder for generating documents with large language models.
llm-mddocgen connects to any OpenAI-compatible LLM backend and produces reports, summaries, research documents, code, and more — driven by a configured set of tasks. It is not a chatbot or an agentic system. Markdown, text, and pdf data goes in, an LLM generated document comes out.
Philosophy
task + persona + template + sources → LLM synthesis → output document
llm-mddocgen is a one-directional pipeline. There is no interactive loop, it treats your model like a compiler. If the output needs adjustment, edit your task, persona, or template and run again. The goal is to produce reliable definitions of what you want to generate and get a progressivly better collection of documents or queue up various projects and questions and generate weekly digests to review on demand.
Getting Started
The fastest way to understand llm-mddocgen is to look at the examples/ directory. It contains example tasks and projects which you can copy to another location to get started. Copy the llm-mddocgen.toml.example to the location where you execute llm-mddocgen and configure your backend against it, then provide a task and project or if you have multiple you can provide a task-dir and/or project-dir, projects are optional if your task does not define one.
llm-mkdocgen --help
llm-mddocgen --task examples/tasks/roman-statesmen.md
llm-mddocgen --tasks-dir examples/tasks/
llm-mddocgen --task examples/tasks/marcus-agrippa-life-summary.md --project examples/projects/marcus-agrippa-research.md
Use --dry-run to preview what would run without calling the model:
llm-mddocgen --tasks-dir examples/tasks/ --dry-run
Core Concepts
Tasks
A task is a unit of work — a document you want generated. Each task is a markdown file with a YAML frontmatter block that declares which persona should perform the work and which template defines the output format.
---
name: roman-statesmen
persona: historian
template: report
tags: [history, rome]
---
Produce a structured list of famous Roman statesmen and their accomplishments,
spanning the Republic and Imperial periods.
Required frontmatter fields: name, persona, template. Optional fields:
| Field | Description |
|---|---|
project |
Links the task to a shared project context |
tags |
One or more tags merged into the output document's frontmatter |
enabled |
Set to false to skip this task without deleting it (default: true) |
The body of the task file is passed directly to the model as the user prompt. Any other .md or .txt files in the same task directory are automatically collected and injected as source material.
Personas
A persona is a role definition — it becomes the system prompt for the model. Each persona is a markdown file with a YAML frontmatter block and a body that describes the expert's behavior, style, and priorities.
---
name: my-analyst
description: Evaluates source material and surfaces key findings.
personality: direct, skeptical, evidence-oriented
behavior:
- lead with the strongest supported findings
- distinguish confident conclusions from inferences
- flag gaps and unresolved questions explicitly
---
You are a research analyst. Your job is to evaluate sources, identify the
strongest claims, and surface gaps in the available evidence.
Distinguish primary from secondary sources. Flag uncertainty explicitly
rather than smoothing it over.
Personas live in personas/<name>/persona.md (directory layout) or as a flat personas/<name>.md file. Files placed in personas/<name>/data/ are injected as source material on every run of that persona. A tags field in persona frontmatter is merged into the output's tag list.
A full set of built-in personas is included — see the table below. Use them as-is or copy one as a starting point for a custom persona.
Templates
A template is an output format contract. It defines the sections, structure, and style a generated document must follow. Templates are reusable across tasks.
---
name: brief
default_persona: analyst
tags: [brief]
---
Produce a short, focused brief. Essential information only.
Required sections:
- **Brief** — two to four sentence summary of the situation
- **Key Facts** — three to five bullets, each a single concrete claim
- **Recommendation** — one clear action or conclusion
When a task sets template: brief, the template body is appended to the user prompt. If default_persona is set, it is used when the task has no persona field.
A full set of built-in templates is included — see the table below.
Projects
A project is a shared context for a group of related tasks — an optional markdown file with a YAML frontmatter block and a body that provides standing background relevant to multiple tasks.
---
name: marcus-agrippa-research
description: Research on the life, career, and legacy of Marcus Vipsanius Agrippa
---
Produce a comprehensive research paper on Marcus Vipsanius Agrippa —
the Roman general, statesman, and close ally of Augustus Caesar.
Tasks reference a project via the project: frontmatter field. Project content is injected as source material alongside task-specific sources. Projects live in a projects/ directory at the project root. A tags field in project frontmatter is merged into the output's tag list.
Tags and Output Frontmatter
Any tags field defined in a task, persona, template, or project is collected and merged into the generated document as YAML frontmatter. Duplicate tags are removed. If no tags are defined anywhere, no frontmatter is added.
---
tags:
- history
- rome
- report
---
# Roman Statesmen
...
This makes llm-mddocgen output compatible with tools like Obsidian that read tags from document frontmatter.
Built-in Personas
| Name | Description |
|---|---|
advisor |
Practical, patient life and decision advisor focused on frugality, quality, and long-term wellbeing |
analyst |
Objective and data-driven with a focus on measurable results |
coder |
Explains software systems, produces code examples, and evaluates design tradeoffs |
copywriter |
Engaging, narrative-focused writer with a focus on improving writing and prose quality |
critic |
Rigorous, logic-testing skeptic designed to find flaws in arguments or ideas |
diplomat |
Empathetic, fair-minded mediator for navigating difficult human interactions |
educator |
Patient and focused on building understanding through clear, simple explanations |
engineer |
Technical, systems-focused, and concerned with real-world implementation constraints |
historian |
Produces factual, sourced accounts of historical events and individuals |
manager |
Execution-oriented organizer focused on unblocking dependencies and making decisions |
researcher |
Synthesizes source material into structured research documents on any topic |
strategist |
High-level, risk-aware planner focused on trade-offs and long-term impacts |
writer |
Produces clear, well-structured prose for documents, articles, and reports |
Built-in Templates
| Name | Description |
|---|---|
analysis |
Analytical breakdown that interprets evidence and produces a defensible judgment |
assessment |
Balanced evaluation of opposing sides of a core dilemma with pros and cons |
brief |
Short decision-support document with essential facts and a single clear recommendation |
code |
Complete, runnable source code implementation with explanation and known limitations |
critique |
Critical evaluation with specific strengths, weaknesses, and a clear overall verdict |
decision |
Factor-by-factor comparison of multiple choices to force a logical verdict |
design |
Structured design document defining components, interfaces, constraints, and open questions |
detailed |
Comprehensive, exhaustive breakdown with deep and granular detail |
draft |
Structured interpersonal correspondence with tone-setting and a clear purpose |
instruct |
Sequential, actionable instructions with prerequisites and success criteria |
plan |
Time-based organization into daily priorities and milestones toward a goal |
proofread |
Line-level proofreading with structural feedback and a clear revision verdict |
question |
Anticipated question-and-answer pairs extracted from raw source material |
recommend |
Decision-focused recommendation for an approach or outcome |
report |
Formal structured report with background, findings, analysis, and conclusions |
research |
Research document surfacing what is known, uncertain, and still missing on a topic |
review |
Opinionated breakdown of media and products with general consensus and a final score |
summary |
Bottom-line up front, key takeaways, and a final recommendation |
Custom personas and templates can be added to ~/.llm-mddocgen/custom/personas/ and ~/.llm-mddocgen/custom/templates/. User-defined files override built-ins with the same name.
Workspace Layout
llm-mddocgen.toml # Per-project config (optional; overrides ~/.llm-mddocgen/config.toml)
personas/ # Custom persona definitions (optional)
<name>/
persona.md # Required: system prompt + frontmatter
data/ # Optional: source files injected on every run
templates/ # Custom template definitions (optional)
<name>.md
tasks/ # Task definitions
<name>/
task.md # Required: task body + frontmatter
*.md / *.txt # Optional: source files injected into this task
projects/ # Shared project context files (optional)
<name>.md
profile/ # Standing user context injected into every run
default/
profile.md # Max 20 lines; use --profile <name> for alternates
output/ # Output reports (written by llm-mddocgen; resolved from CWD)
tasks/
<task-name>-<timestamp>.md
projects/
<project-name>/
<task-name>-<timestamp>.md
logs/
<timestamp>.md # Per-task generation logs and run summaries
Global config and built-in assets live at ~/.llm-mddocgen/.
Install
Recommended: uv
uv manages the virtualenv automatically — no activation step needed.
# Install uv (once)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install dependencies and run
uv sync
uv run python -m llm_mddocgen --help
# Install as a system command
uv tool install .
llm-mddocgen --version
PyPI
pip install llm-mddocgen
llm-mddocgen --version
pip (from source)
python -m venv .venv
source .venv/bin/activate
pip install -e .
llm-mddocgen --version
Docker
# Runs against the examples/ directory by default
docker compose up
# Run against a custom workspace
WORKSPACE_PATH=/path/to/your/tasks docker compose up
Configuration
llm-mddocgen loads config from one of two places:
./llm-mddocgen.toml— used if present in the current directory; global config is ignored~/.llm-mddocgen/config.toml— global fallback
Copy llm-mddocgen.toml.sample to get started:
# Per-project config. Values here override the global ~/.llm-mddocgen/config.toml.
# Uncomment to use project-local persona/template directories.
# personas_root = "personas"
# templates_root = "templates"
# Profile root for standing user context injected into every prompt.
# profile_root = "profile"
# Projects root for task-scoped shared context.
# projects_root = "projects"
# Output directory. Resolved relative to the current working directory.
generated_root = "output"
[llm]
# Set to true to enable LLM synthesis. False uses the deterministic fallback.
enabled = false
# Any OpenAI-compatible endpoint: Ollama, vLLM, OpenAI, etc.
base_url = "http://localhost:11434/v1"
model = "llama3.2"
# Auth key (or use LLM_MDDOCGEN_API_KEY env var).
api_key = ""
# Request tuning. Suggested starting points by provider:
# Ollama (llama3, mistral) — temperature = 0.2, max_tokens = 1200
# Qwen3 standard — temperature = 0.2, max_tokens = 4096
# Qwen3 thinking mode — temperature = 0.7, max_tokens = 10000
# OpenAI gpt-4o — temperature = 0.3, max_tokens = 2048
timeout_seconds = 60
temperature = 0.2
max_tokens = 1200
# Web search via tool use (requires a model that supports function calling, e.g. llama3.1+).
# llm-mddocgen asks the model to generate search queries, fetches results from DuckDuckGo,
# and injects them as source documents before synthesis.
# web_search = false
# web_search_max_queries = 3
# web_search_max_results = 5
# Save model reasoning output (<think>...</think> blocks) to a separate file.
# thinking_output = false
Quick Start
1. Point llm-mddocgen at your LLM.
Edit llm-mddocgen.toml with your endpoint and model, or set enabled = false to use the deterministic fallback (good for testing workspace structure without a running model).
2. Try the examples.
llm-mddocgen --tasks-dir examples/tasks/
Or scaffold a new task from scratch:
llm-mddocgen --init-task tasks/my-first-task/task.md
Edit the scaffolded file to set your persona, template, and task body.
3. Run it.
llm-mddocgen --task tasks/my-first-task/task.md
4. Read the output.
Reports land in output/tasks/ for standalone tasks, or output/projects/<name>/ for project-linked tasks.
CLI Reference
Task execution
| Flag | Description |
|---|---|
--task <path> |
Task file to run (repeatable) |
--tasks-dir <path> |
Run all tasks found in a directory |
--project <path> |
Project file to associate with --task |
--project-dir <path> |
Directory of project files for auto-association |
--fail-fast |
Stop on the first task failure |
Output and profile
| Flag | Description |
|---|---|
--output-path <path> |
Override the output directory |
--profile <name> |
Load a named profile from the profile root |
--profile-path <path> |
Override the profile root directory |
Configuration
| Flag | Description |
|---|---|
--config <path> |
Use a specific TOML config file |
Utility actions
| Flag | Description |
|---|---|
--list personas|tasks|templates |
List available personas, tasks, or templates |
--init-task <path> |
Write a starter task.md and exit |
--dry-run |
Show what would run without executing |
--version |
Print version and exit |
Source Injection
On each run, llm-mddocgen collects source documents from several locations and injects them into the prompt:
| Source | Content |
|---|---|
| Task directory | All .md and .txt files alongside task.md |
Task data/ |
Subdirectory of supporting files |
Persona data/ |
Reference files for the active persona |
| Project file | Shared context declared in project: frontmatter |
| Profile | Standing user context from profile/default/profile.md |
| Previous output | The most recent generated report for this task (for iterative refinement) |
| Web search | DuckDuckGo results fetched via tool use (if enabled) |
PDF files are supported when allow_pdf_materials = true in config.
Profile
The profile is a standing context document injected into every task run. Use it for user identity, preferences, or constraints that apply across all your tasks.
I am a software engineer at a mid-stage startup. I prefer direct, no-hedging analysis.
Assume I have strong technical context and skip basic explanations.
Prioritize actionable output over comprehensive coverage.
- Location:
profile/default/profile.md - Maximum 20 lines (enforced)
- Use
--profile <name>to load an alternate profile fromprofile/<name>/profile.md
Fallback Mode
When llm.enabled = false (the default), llm-mddocgen uses a deterministic fallback synthesizer. It extracts bullet points from source files and structures them into a fixed-section report. No model is called. This is useful for testing your workspace configuration and verifying that sources are collected correctly before connecting an LLM.
Development
# Install dev dependencies
uv sync --dev
# Run tests
uv run pytest -q
# Install as a system command
uv tool install .
Dev dependencies are declared using PEP 735 dependency groups in pyproject.toml. Use uv sync --dev — not pip install -e ".[dev]" — to install them.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file llm_mddocgen-0.1.0.tar.gz.
File metadata
- Download URL: llm_mddocgen-0.1.0.tar.gz
- Upload date:
- Size: 85.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a6bb173054193f8d330257e27eab8cbe300844f8c2963e77df872815c002a6c
|
|
| MD5 |
4febd2b5b23ed5c6430b90698b682fd6
|
|
| BLAKE2b-256 |
5d24e24b8f35c0600d788b6df1131c482b208b75df4aab9596759549b37c8fd6
|
Provenance
The following attestation bundles were made for llm_mddocgen-0.1.0.tar.gz:
Publisher:
publish.yml on rpenley/llm-mddocgen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
llm_mddocgen-0.1.0.tar.gz -
Subject digest:
2a6bb173054193f8d330257e27eab8cbe300844f8c2963e77df872815c002a6c - Sigstore transparency entry: 1098323350
- Sigstore integration time:
-
Permalink:
rpenley/llm-mddocgen@36f5e7e15fd89c1056d044813ee614435794f753 -
Branch / Tag:
refs/tags/latest - Owner: https://github.com/rpenley
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@36f5e7e15fd89c1056d044813ee614435794f753 -
Trigger Event:
release
-
Statement type:
File details
Details for the file llm_mddocgen-0.1.0-py3-none-any.whl.
File metadata
- Download URL: llm_mddocgen-0.1.0-py3-none-any.whl
- Upload date:
- Size: 65.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a52c2bdeac2b015dc011944ca6ecb968957b58e29206d62b65800ba2d09be82e
|
|
| MD5 |
cbcd7634811849bf1aa89b1ae0099d23
|
|
| BLAKE2b-256 |
4b93995e074d3453a785385f893669ac3d02a1becdc9c1b8a45dbb83d5dca50e
|
Provenance
The following attestation bundles were made for llm_mddocgen-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on rpenley/llm-mddocgen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
llm_mddocgen-0.1.0-py3-none-any.whl -
Subject digest:
a52c2bdeac2b015dc011944ca6ecb968957b58e29206d62b65800ba2d09be82e - Sigstore transparency entry: 1098323822
- Sigstore integration time:
-
Permalink:
rpenley/llm-mddocgen@36f5e7e15fd89c1056d044813ee614435794f753 -
Branch / Tag:
refs/tags/latest - Owner: https://github.com/rpenley
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@36f5e7e15fd89c1056d044813ee614435794f753 -
Trigger Event:
release
-
Statement type: