Codraft is a document assembly tool for people already working with Claude and AI. If you're comfortable chatting with Claude, there's nothing new to learn — just ask it to prepare a document. Claude interviews you conversationally, collects the answers, and renders completed documents from your templates. No server, no database, no scripting language to learn. The whole project is a folder you can share as a zip — or deploy as a Claude Code plugin across your organisation in minutes.
- Place your templates in
templates/, each in its own subdirectory - Add
{{ variable_name }}placeholders wherever you need dynamic content - Use
{% if %}/{% else %}blocks for sections that should only appear based on user answers - Use
{% for item in items %}blocks for repeating sections (e.g., line items, milestones) - Optionally add a
config.yamlto customize questions, grouping, and validation - Ask Claude to "prepare a [document type]"
- Claude walks you through a conversational interview -- skipping irrelevant sections and collecting lists naturally
- A completed document is rendered and saved to a job folder in
output/
Two template formats are supported:
.docx-- rendered viadocxtpl, produces a Word document.html-- rendered viajinja2, produces both an HTML file and a PDF (viaweasyprint)
You'll need a Claude Cowork account.
Go to the Releases page and download the latest codraft-v*.zip file. Extract it to a folder of your choice (e.g., your Documents folder).
- Open Claude Cowork and add the extracted folder as a project
- That's it — no terminal or coding knowledge required. Python dependencies are installed automatically the first time you use the skill.
From within Claude Code:
- Add the marketplace:
/plugin marketplace add houfu/codraft - Install the plugin:
/plugin install codraft@codraft - Say "prepare an NDA" to try it out with a built-in template
Create a subdirectory in templates/ and place your template file inside:
templates/
└── nda/
└── nda.docx <- contains {{ disclosing_party_name }}, {{ effective_date }}, etc.
Or for an HTML template:
templates/
└── invoice/
└── invoice.html <- same {{ variable }} syntax, styled with CSS
Tell Claude:
"I need to prepare an NDA"
Claude will find the template, extract its variables, and interview you for the values -- grouping related fields together for a natural flow.
After confirming all values, the rendered document is saved to a job folder:
output/
└── nda_acme_pte_ltd_2026-02-15/
└── nda_acme_pte_ltd_2026-02-15.docx
For HTML templates, both the HTML and PDF are saved:
output/
└── invoice_acme_2026-02-15/
├── invoice_acme_2026-02-15.html
└── invoice_acme_2026-02-15.pdf
Templates can include or exclude sections based on user answers. The interview automatically skips questions that aren't relevant.
{% if include_ip_assignment %}
The Consultant hereby assigns all intellectual property
created during the engagement to {{ ip_ownership_entity }},
effective {{ ip_assignment_date }}.
{% endif %}
Equality conditions are also supported:
{% if payment_method == 'bank_transfer' %}
Bank: {{ bank_name }}
Account: {{ account_number }}
{% else %}
Payment will be made via {{ payment_method }}.
{% endif %}
Templates can have repeating sections. The interview collects items one at a time with an "add another?" flow.
{% for milestone in milestones %}
Milestone: {{ milestone.description }}
Due Date: {{ milestone.date }}
Amount: {{ milestone.amount }}
{% endfor %}
Template developers can place an optional config.yaml alongside their template to customize the interview experience:
meta:
display_name: "Consulting Agreement"
description: "Standard consulting engagement agreement"
variables:
client_name:
label: "Client's Legal Name"
question: "What is the client's full legal name?"
required: true
payment_method:
type: choice
choices: [bank_transfer, cheque, crypto]
default: "bank_transfer"
include_ip_assignment:
type: boolean
default: false
groups:
- name: "Parties"
variables: [client_name, client_address]
- name: "IP Assignment"
condition: include_ip_assignment
variables: [ip_ownership_entity, ip_assignment_date]
- name: "Milestones"
loop: milestones
variables: [description, date, amount]
validation:
- rule: "end_date > effective_date"
message: "The end date must be after the effective date"Config features include:
- Custom questions and labels for each variable
- Variable types:
text,date,number,email,phone,boolean,choice - Interview groups to control question ordering and grouping
- Conditional groups that only appear based on gate variable answers
- Loop groups for collecting lists
- Default values (including
"today"for dates) - Validation rules evaluated during confirmation
v2 splits the monolithic skill into three focused skills:
| Skill | Purpose |
|---|---|
| Orchestrator | Entry point. Discovery, interview, confirmation, post-render. |
| Analyzer | Template parsing, variable extraction, manifest generation. |
| Renderer | Docx/HTML rendering, output validation. |
Use Jinja2-style double-brace syntax in both .docx and .html templates:
This Agreement is entered into by {{ disclosing_party_name }}
(the "Disclosing Party") with address at {{ disclosing_party_address }}.
Use {% if %} / {% else %} / {% endif %} to include or exclude sections:
{% if include_warranty %}
The Seller warrants that the goods are free from defects.
{% else %}
The goods are sold "as is" without warranty.
{% endif %}
Two condition forms are supported:
- Truthiness:
{% if variable_name %}-- variable is truthy (boolean true, non-empty) - Equality:
{% if variable_name == 'value' %}-- variable equals a specific string
Use {% for %} / {% endfor %} for repeating sections:
{% for service in services %}
Service: {{ service.description }}
Fee: {{ service.fee }}
{% endfor %}
Inside the loop body, reference sub-variables with dot notation: {{ loop_var.sub_variable }}.
Name your variables with descriptive suffixes for automatic type inference:
| Suffix | Inferred type | Example |
|---|---|---|
*_name |
text | landlord_name |
*_address |
text | property_address |
*_date |
date | commencement_date |
*_email |
tenant_email |
|
*_amount, *_price, *_fee |
number | rental_amount |
*_phone, *_tel, *_mobile |
phone | contact_phone |
| (other) | text | governing_law |
- One template file per directory (
.docxor.html, not both) - Use
{{ }}with spaces around the variable name:{{ name }}not{{name}} - Variable names must be valid Python identifiers: lowercase, underscores, no spaces
- The same variable can appear multiple times in the document -- it will be filled with one value
- Single-level nesting only: no
{% for %}inside{% if %}, or vice versa {% elif %}is not supported (use separate{% if %}blocks instead)
codraft/
├── CLAUDE.md # Project instructions for Claude
├── README.md # This file
├── LICENSE # MIT License
├── .gitignore
├── .claude/
│ └── skills/
│ ├── codraft/
│ │ └── SKILL.md # Orchestrator skill (entry point)
│ ├── codraft-analyzer/
│ │ └── SKILL.md # Analyzer skill (template parsing)
│ └── codraft-renderer/
│ └── SKILL.md # Renderer skill (document output)
├── docs/
│ ├── codraft_mvp_spec.md # Original MVP specification
│ └── codraft_v2_spec.md # Full v2 specification
├── templates/
│ ├── _examples/ # Bundled example templates (tracked in git)
│ │ ├── Bonterms_Mutual_NDA/
│ │ │ └── Bonterms-Mutual-NDA.docx
│ │ ├── invoice/
│ │ │ └── invoice.html
│ │ ├── consulting_agreement/ # v2 example (conditionals + loops)
│ │ │ ├── Consulting-Agreement.docx
│ │ │ └── config.yaml
│ │ └── event_invitation/ # v2 example (conditionals + loops)
│ │ ├── event-invitation.html
│ │ └── config.yaml
│ └── <your_template>/ # Your templates (gitignored)
│ ├── <name>.docx or .html
│ ├── manifest.yaml # Auto-generated (do not edit)
│ └── config.yaml # Optional developer config
└── output/ # Rendered documents (gitignored)
└── <job_name>/ # One folder per rendering job
- Template engines:
docxtplfor docx (preserves formatting),jinja2+weasyprintfor HTML to PDF - Python environment: managed with
uv - Manifest caching: variable analysis is cached in
manifest.yamlper template and only regenerated when the template file changes - Manifest v2 schema: includes
schema_version: 2, conditionals, loops, dependencies, and optional groups/validation
Codraft v2 supports variable substitution, conditional logic ({% if %} / {% else %}), loops ({% for %}), and developer configuration via config.yaml. See docs/codraft_v2_spec.md for the full specification and docs/codraft_mvp_spec.md for the original MVP specification.
v2 constraints: single-level nesting only, two condition forms (truthiness and equality), no {% elif %}, no computed fields or expressions.
Codraft trades determinism for conversational flexibility. A few things to keep in mind:
- No fixed interview order — Claude manages the conversation, not a scripted sequence
- Prompt injection — a user can influence the document by saying "also add a clause that…" during the interview
- AI suggestions need review — Claude may suggest plausible but wrong values (e.g., a made-up address)
- Not legal advice — documents still need review by a qualified professional
- Single-level template nesting only — no blocks inside other blocks (v2 constraint)
- Requires Claude Cowork or Claude Code — no web or mobile interface
See the Limitations page in the docs for the full picture.
MIT -- see LICENSE.
