# index.html.md # typy

Build polished PDFs from Python and Markdown

typy combines a typed Python API, reusable Typst templates, and a CLI designed for both developers and AI agents.

Get started Open cookbook
## Quick start ```bash typy scaffold report --output data.json typy render --template report --data data.json --output report.pdf ``` ## Explore

Getting started

Install typy and render your first document in minutes.

Read guide

Cookbook

11 runnable examples from invoices to presentations and batch generation.

View recipes

Template reference

Inspect built-in templates and field schemas for each document type.

Browse templates

CLI reference

Command-by-command guidance and options for automation-friendly usage.

Open CLI docs

API reference

Module-level API pages generated from source and type signatures.

Read API

Package format

RFC: .typy container structure, manifest v1 schema, versioning policy, and error model.

Read RFC

LLM resources

Machine-oriented entry points and generated llms.txt context files.

Open LLM page
# index.html.md # API reference | Module | Description | |-----------------------------------|--------------------------------------------------------------------| | [builder](builder.md) | `DocumentBuilder` — compose and render documents | | [templates](templates.md) | Built-in template classes (`ReportTemplate`, `InvoiceTemplate`, …) | | [content](content.md) | `Content` block and list helpers | | [markup](markup.md) | Inline markup helpers (`Text`, `Bold`, `Italic`, …) | | [functions](functions.md) | Helper functions for building document data | | [typst_encoder](typst_encoder.md) | Internal Typst serialisation layer | # builder.html.md # builder ### *class* typy.builder.DocumentBuilder(verbose=False) Bases: `object` * **Parameters:** **verbose** (*bool*) #### add_template(template) * **Parameters:** **template** ([*Template*](templates.md#typy.templates.Template)) #### get_template_data_model() * **Return type:** *Type*[[*Template*](templates.md#typy.templates.Template)] #### add_data(data) * **Parameters:** **data** (*dict* *|* [*Template*](templates.md#typy.templates.Template)) #### add_typ_template(typ_path, data=None) Add a raw Typst .typ template file with optional JSON-serialisable data dict. * **Parameters:** * **typ_path** (*Path*) – Path to the .typ Typst template file to render. * **data** (*dict* *|* *None*) – Optional dictionary of template variables. When provided, the data is encoded and written to `typy_data.typ` in the build directory so the template can import it. * **Returns:** self – enables method chaining. #### copy_assets_from(source_dir) Copy files from source_dir into the build temp dir. This makes relative paths (e.g. `assets/image.png`) in the document resolvable during Typst compilation, which otherwise runs in an isolated temporary directory. Files named `main.typ`, `typy_data.typ`, and `typy.typ` are intentionally excluded so the generated build files are not overwritten. * **Parameters:** **source_dir** (*Path*) – Directory whose contents are copied to the build dir. * **Returns:** self – enables method chaining. * **Return type:** [*DocumentBuilder*](#typy.builder.DocumentBuilder) #### add_file(filepath) * **Parameters:** **filepath** (*Path*) * **Return type:** *Path* #### compile(typ_file, output) * **Parameters:** * **typ_file** (*Path*) * **output** (*Path*) #### save_pdf(filepath) * **Parameters:** **filepath** (*Path*) #### to_buffer() * **Return type:** *BytesIO* #### save_source(dirpath) * **Parameters:** **dirpath** (*Path*) # cli.html.md # CLI reference The CLI is workflow-oriented and supports direct discovery through `--help`. ## Commands ### list List built-in templates. ```bash typy list ``` ### info Inspect template schema. ```bash typy info report typy info report --json ``` ### scaffold Generate sample JSON input for a template. ```bash typy scaffold report --output data.json ``` ### render Render PDF from template data, Markdown, or both. ```bash typy render --template report --data data.json --output report.pdf typy render --markdown notes.md --output notes.pdf typy render --template report --markdown body.md --output report.pdf ``` Constraints: - At least one of `--template` or `--markdown` is required. - If `--template` is provided without `--data` and without `--markdown`, typy auto-generates sample data for preview rendering. ### package Manage `.typy` template packages. Run `typy package --help` for details. #### package validate Validate the structure and manifest of a `.typy` package. ```bash typy package validate my-template.typy ``` Reports all errors in a single pass so they can be fixed together. #### package export Export a template as a self-contained `.typy` package archive. ```bash typy package export template.py \ --manifest manifest.json \ --output my-template.typy ``` Optional flags: | Flag | Description | |-------------------|-------------------------------------------------------------------------| | `--assets ` | Bundle a directory of static assets (images, fonts, …) under `assets/`. | | `--readme ` | Bundle a README file as `README.md`. | Minimal `manifest.json`: ```json { "manifest_version": 1, "name": "my-report", "version": "1.0.0", "description": "A general-purpose report template.", "author": "Jane Doe ", "typy_compatibility": ">=0.1.0" } ``` See [package-format.md](package-format.md) for the full manifest schema. #### package install Validate and install a `.typy` package into the local template store (`~/.typy/packages/` by default). ```bash typy package install my-template.typy typy package install my-template.typy --store /path/to/store typy package install my-template.typy --force # overwrite existing ``` After installation the package is unpacked to `///` and can be used by name in `typy render`: ```bash typy render --template my-report --data data.json --output report.pdf ``` ## Typical package workflow ```bash # 1. Author creates a template and writes a manifest typy scaffold report --output data.json # optional: inspect field schema cat manifest.json # verify manifest fields # 2. Export as a distributable package typy package export template.py \ --manifest manifest.json \ --output my-report.typy # 3. Validate before distribution typy package validate my-report.typy # --- Option A: render directly from the .typy file (no install needed) --- typy render --template my-report.typy --data data.json --output report.pdf # --- Option B: install once, then use by name like a built-in template --- typy package install my-report.typy typy render --template my-report --data data.json --output report.pdf ``` # content.html.md # content ### *class* typy.content.Content(content, \*\*kwargs) Bases: `Encodable` #### content_item_encode(item) #### encode() # cookbook.html.md # Cookbook Each entry is designed so an LLM can copy, adapt, and run it directly. Every recipe includes a clear goal and complete code or commands. ## 1. Generate a report from a pandas DataFrame Goal: render a report PDF containing a real table from analytics data. **Prerequisites:** `pandas` must be installed. Run `pip install "typy[pandas]"` or `pip install pandas`. ```python from datetime import date import pandas as pd from typy.builder import DocumentBuilder from typy.content import Content from typy.functions import Figure, Table from typy.markup import Heading, Text from typy.templates import ReportTemplate df = pd.DataFrame( { "Region": ["EMEA", "AMER", "APAC"], "Revenue": [1_200_000, 980_000, 1_450_000], "GrowthPct": [8.1, 5.4, 11.3], } ) body = Content( [ Heading(1, "Quarterly performance"), Text("This report summarizes regional revenue and growth."), Figure(Table(df.to_dict()), caption="Revenue by region"), ] ) template = ReportTemplate( title="Q1 revenue report", subtitle="Regional breakdown", author="Finance Analytics", date=date.today().isoformat(), body=body, toc=True, ) DocumentBuilder().add_template(template).save_pdf("q1-report.pdf") ``` ## 2. Create an invoice from JSON data Goal: render an invoice with line items loaded from a JSON payload. ```python import json from typy.builder import DocumentBuilder from typy.templates import InvoiceTemplate payload = { "company_name": "Northwind Design LLC", "company_address": "101 River St, Porto", "client_name": "Contoso Retail", "client_address": "25 Market Ave, Lisbon", "invoice_number": "INV-2026-0411", "date": "2026-04-11", "due_date": "2026-05-11", "tax_rate": 23.0, "notes": "Thank you for your business.", "items": [ {"description": "UI redesign", "quantity": 32, "unit_price": 75.0}, {"description": "Design system audit", "quantity": 12, "unit_price": 90.0}, ], } with open("invoice.json", "w", encoding="utf-8") as f: json.dump(payload, f, indent=2) data = json.loads(open("invoice.json", encoding="utf-8").read()) template = InvoiceTemplate(**data) DocumentBuilder().add_template(template).save_pdf("invoice.pdf") ``` ## 3. Convert a Markdown file to a styled PDF Goal: convert existing Markdown notes into a PDF without writing Python. ```bash typy render --markdown README.md --output readme.pdf ``` ## 4. Batch-generate certificates from a CSV Goal: produce one PDF per person in a CSV list. ```python import csv from pathlib import Path from typy.builder import DocumentBuilder from typy.templates import BasicTemplate Path("out").mkdir(exist_ok=True) rows = [ {"name": "Alice Martins", "course": "Data Visualization"}, {"name": "Bruno Costa", "course": "Prompt Engineering"}, ] with open("certificates.csv", "w", encoding="utf-8", newline="") as f: writer = csv.DictWriter(f, fieldnames=["name", "course"]) writer.writeheader() writer.writerows(rows) with open("certificates.csv", encoding="utf-8") as f: for row in csv.DictReader(f): body = ( "# Certificate of Completion\n\n" f"This certifies that **{row['name']}** completed " f"**{row['course']}**." ) template = BasicTemplate( title="Certificate", date="2026-04-11", author="Training Team", body=body, ) filename = f"out/{row['name'].lower().replace(' ', '-')}.pdf" DocumentBuilder().add_template(template).save_pdf(filename) ``` ## 5. Build and render a custom template from scratch Goal: use your own Typst template file with JSON data. ```bash cat > custom.typ <<'TYP' #import "typy.typ": init_typy #import "typy_data.typ": typy_data #let t = init_typy(typy_data) = #t("title", "str") #t("body", "content") TYP cat > custom-data.json <<'JSON' { "title": "Custom template demo", "body": "## Hello\n\nRendered with a raw Typst template." } JSON typy render --template custom.typ --data custom-data.json --output custom.pdf ``` ## 6. Use typy only from the CLI Goal: full CLI workflow from discovery to render. ```bash typy list typy info report --json typy scaffold report --output data.json typy render --template report --data data.json --output report.pdf ``` ## 7. Use typy with an AI agent code-execution pattern Goal: reliable command sequence for tools that can execute shell commands. ```bash # Step 1: discover and inspect schema typy list typy info letter --json > schema-letter.json # Step 2: produce data.json that matches required fields cat > data-letter.json <<'JSON' { "sender_name": "Northwind Legal", "sender_address": "101 River St, Porto", "recipient_name": "Maria Silva", "recipient_address": "12 Elm Road, Braga", "date": "2026-04-11", "subject": "Contract renewal", "body": "Dear Maria,\n\nPlease find the updated renewal terms attached.", "signature_name": "Pedro Azevedo" } JSON # Step 3: render typy render --template letter --data data-letter.json --output letter.pdf ``` ## 8. Combine markdown with programmatic figures and tables Goal: mix human-written markdown and generated visual content in one document. ```python from typy.builder import DocumentBuilder from typy.content import Content from typy.functions import Figure, Table from typy.markup import Markdown from typy.templates import ReportTemplate table_data = { "Metric": {0: "Latency", 1: "Throughput"}, "Before": {0: "120ms", 1: "1000 req/s"}, "After": {0: "45ms", 1: "3200 req/s"}, } body = Content( [ Markdown("## Benchmark summary\n\nPerformance improved after optimization."), Figure(Table(table_data), caption="Before vs after"), ] ) template = ReportTemplate( title="Performance benchmark", author="Platform Team", date="2026-04-11", body=body, ) DocumentBuilder().add_template(template).save_pdf("benchmark.pdf") ``` ## 9. Generate a business letter from Python Goal: create a formal letter programmatically. ```python from typy.builder import DocumentBuilder from typy.templates import LetterTemplate template = LetterTemplate( sender_name="Acme Procurement", sender_address="44 Harbor Road, Porto", recipient_name="Nova Supplies", recipient_address="9 Cedar St, Faro", date="2026-04-11", subject="Request for quotation", body="Dear team,\n\nPlease send a quote for 500 units of Model X by next Friday.", closing="Best regards", signature_name="Joana Pereira", ) DocumentBuilder().add_template(template).save_pdf("rfq-letter.pdf") ``` ## 10. Build a presentation with multiple slides Goal: generate a slide deck PDF from structured slide content. ```python from typy.builder import DocumentBuilder from typy.templates import PresentationTemplate, Slide slides = [ Slide(title="Context", body="## Current state\n\nUsage has grown 40% year over year."), Slide(title="Plan", body="## Next steps\n\n- Stabilize API\n- Improve docs\n- Expand templates"), ] template = PresentationTemplate( title="Product update", subtitle="Q2 planning", author="Product Team", date="2026-04-11", slides=slides, ) DocumentBuilder().add_template(template).save_pdf("product-update.pdf") ``` ## 11. Render markdown into a report template body Goal: keep metadata in JSON and body content in a separate Markdown file. ```bash cat > report-data.json <<'JSON' { "title": "Research summary", "author": "R&D Team", "date": "2026-04-11", "toc": true } JSON cat > body.md <<'MD' ## Findings The experiments show statistically significant improvements. ## Next actions - Expand sample size - Validate in production conditions MD typy render --template report --data report-data.json --markdown body.md --output research-summary.pdf ``` # functions.html.md # functions ### *class* typy.functions.Function(name, content=None, \*\*kwargs) Bases: `Encodable` * **Parameters:** **name** (*str*) #### encode() ### *class* typy.functions.Block(content, \*\*kwargs) Bases: [`Function`](#typy.functions.Function) * **Parameters:** **content** ([*Function*](#typy.functions.Function) *|* [*Markup*](markup.md#typy.markup.Markup) *|* [*Content*](content.md#typy.content.Content)) #### encode() ### *class* typy.functions.Figure(content, \*\*kwargs) Bases: [`Function`](#typy.functions.Function) ### *class* typy.functions.Image(src, \*\*kwargs) Bases: [`Function`](#typy.functions.Function) * **Parameters:** **src** (*Path*) ### *class* typy.functions.Grid(content, \*\*kwargs) Bases: [`Function`](#typy.functions.Function) * **Parameters:** **content** ([*Content*](content.md#typy.content.Content)) ### *class* typy.functions.Columns(content, \*\*kwargs) Bases: [`Function`](#typy.functions.Function) * **Parameters:** **content** ([*Content*](content.md#typy.content.Content)) ### *class* typy.functions.Badge(label, fill=, stroke=, radius=, inset=, \*\*kwargs) Bases: [`Function`](#typy.functions.Function) * **Parameters:** * **label** (*str*) * **fill** ([*Raw*](markup.md#typy.markup.Raw)) * **stroke** ([*Raw*](markup.md#typy.markup.Raw)) * **radius** ([*Raw*](markup.md#typy.markup.Raw)) * **inset** ([*Raw*](markup.md#typy.markup.Raw)) ### *class* typy.functions.Callout(content, title=None, fill=, stroke=, radius=, inset=, \*\*kwargs) Bases: [`Function`](#typy.functions.Function) * **Parameters:** * **title** (*str* *|* *None*) * **fill** ([*Raw*](markup.md#typy.markup.Raw)) * **stroke** ([*Raw*](markup.md#typy.markup.Raw)) * **radius** ([*Raw*](markup.md#typy.markup.Raw)) * **inset** ([*Raw*](markup.md#typy.markup.Raw)) ### *class* typy.functions.Table(data, \*\*kwargs) Bases: [`Function`](#typy.functions.Function) * **Parameters:** **data** (*dict*) ### *class* typy.functions.Datetime(date=None, \*\*kwargs) Bases: [`Function`](#typy.functions.Function) * **Parameters:** **date** (*datetime*) ### *class* typy.functions.Lorem(words=100) Bases: [`Function`](#typy.functions.Function) * **Parameters:** **words** (*int*) # getting-started.html.md # Getting started This page gets you from zero to your first PDF with both CLI and Python API. ## What typy produces typy generates polished PDFs powered by Typst. All built-in templates share a consistent visual identity: blue-600 (`#2563eb`) accent colour, clean typographic hierarchy, and A4 page layouts. The presentation template uses 16:9 slides. Run the example scripts under `examples/` to see each template in action, or generate preview images with `python scripts/generate_previews.py`. ## 1. Install typy ```bash pip install typy # or uv add typy ``` ## 2. Render your first PDF with the CLI ```bash typy scaffold report --output data.json typy render --template report --data data.json --output first-report.pdf ``` ## 3. Render Markdown directly ```bash typy render --markdown README.md --output readme.pdf ``` ## 4. Minimal Python example ```python from typy.builder import DocumentBuilder from typy.templates import BasicTemplate template = BasicTemplate( title="Hello typy", date="2026-04-11", author="Your Name", body="## Welcome\n\nThis PDF was generated from Python.", ) DocumentBuilder().add_template(template).save_pdf("hello-typy.pdf") ``` ## 5. Understand what fields are required ```bash typy info report --json ``` Use this JSON output as the source of truth before creating or validating `data.json`. ## Next - Continue with complete examples in [cookbook](cookbook.md) - Check built-in schemas in [template reference](templates.md) - Explore modules in [API reference](api/index.md) # llm.html.md # LLM-oriented usage Use these pages as machine-readable entry points. ## Core pages - [Getting started](getting-started.md) - [Cookbook](cookbook.md) - [Template reference](templates.md) - [CLI reference](cli.md) - [API reference](api/index.md) ## Generated LLM artifacts If these files are missing, rebuild docs with the docs dependency group enabled. # markup.html.md # markup ### *class* typy.markup.Markup(text) Bases: `Encodable`, `ABC` * **Parameters:** **text** (*str*) #### *abstract* encode() ### *class* typy.markup.Text(text) Bases: [`Markup`](#typy.markup.Markup) * **Parameters:** **text** (*str*) #### encode() ### *class* typy.markup.Raw(text) Bases: [`Markup`](#typy.markup.Markup) * **Parameters:** **text** (*str*) #### encode() ### *class* typy.markup.Heading(level, text) Bases: [`Markup`](#typy.markup.Markup) * **Parameters:** * **level** (*int*) * **text** (*str*) #### encode() ### *class* typy.markup.Markdown(text) Bases: [`Markup`](#typy.markup.Markup) * **Parameters:** **text** (*str*) #### encode() # package-format.html.md # RFC: .typy template package format and manifest v1 This document defines the portable template package format used by typy. A `.typy` file is a self-contained, distributable unit that bundles a Python `Template` class, the Typst files it references, and any associated assets. ## Overview A `.typy` package is a **ZIP archive** with the `.typy` file extension. The archive contains a mandatory `manifest.json` (the package descriptor) and a `template.py` file that defines the template data model as a subclass of `typy.templates.Template`. Because it is plain ZIP, consumers can inspect and unpack it with any standard ZIP tool. --- ## Container structure ```default my-template.typy ← ZIP archive with .typy extension ├── manifest.json ← REQUIRED: package descriptor (manifest v1) ├── template.py ← REQUIRED: Python file defining the Template subclass ├── templates/ ← REQUIRED: Typst files referenced by __template_path__ in template.py │ └── template.typ ├── assets/ ← optional: images, fonts, or other static resources │ ├── logo.png │ └── font.ttf └── README.md ← optional: human-readable notes for the package ``` ### Required files | File | Description | |-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `manifest.json` | Package descriptor in JSON format conforming to manifest schema v1 (see below). | | `template.py` | Python module containing exactly one `typy.templates.Template` subclass. The class defines the data model and sets `__template_name__` and `__template_path__`. `__template_path__` must be a path relative to the archive root pointing to the entry `.typ` file inside `templates/`. | | `templates/` | Directory containing the Typst file(s) referenced by `__template_path__` in `template.py`. At least one `.typ` file must be present. | ### Optional files | File/directory | Description | |------------------|----------------------------------------------------------------------------------------------------------------------------------| | `assets/` | Static resources (images, fonts, etc.) referenced by the template. Paths must be relative and must stay inside the archive root. | | `README.md` | Package documentation for human readers. Shown by tools that display package info. | ### Path rules - All paths inside the archive must be **relative** (no leading `/` or `..` traversal). - `__template_path__` in `template.py` and any files it references **must not** escape the archive root via path traversal (`../`). - File names are case-sensitive. --- ## Manifest schema v1 The `manifest.json` file is UTF-8-encoded JSON. The `"manifest_version"` field determines which schema applies. This document specifies manifest version **1**. ### Fields | Field | Type | Required | Description | |----------------------|------------------|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| | `manifest_version` | integer | **yes** | Schema version. Must be `1` for this spec. | | `name` | string | **yes** | Package identifier. Lowercase alphanumeric, hyphens allowed. Pattern: `^[a-z0-9][a-z0-9-]*[a-z0-9]$` (min length 2). | | `version` | string | **yes** | [Semantic version](https://semver.org/) of this package, e.g. `"1.0.0"`. | | `description` | string | **yes** | One-line human-readable summary of the template’s purpose. | | `author` | string | **yes** | Author name, optionally with email in angle brackets, e.g. `"Jane Doe "`. | | `typy_compatibility` | string | **yes** | PEP 440 version specifier for the typy version this package requires, e.g. `">=0.1.0"`. | | `dependencies` | array of strings | no | List of other `.typy` package identifiers that must be installed before this package. Format for each entry: `"@"`. Defaults to `[]`. | | `license` | string | no | SPDX license identifier, e.g. `"MIT"`. | | `homepage` | string | no | URL for the package’s home page or repository. | | `keywords` | array of strings | no | Tags for discovery. Each keyword: max 32 characters. | ### Example: minimal valid manifest ```json { "manifest_version": 1, "name": "my-report", "version": "1.0.0", "description": "A general-purpose report template with cover page and TOC.", "author": "Jane Doe ", "typy_compatibility": ">=0.1.0" } ``` ### Example: full valid manifest ```json { "manifest_version": 1, "name": "corporate-invoice", "version": "2.1.0", "description": "Professional invoice template with logo, line items, and tax calculation.", "author": "Acme Corp ", "typy_compatibility": ">=0.2.0,<1.0.0", "dependencies": [ "acme-base@>=1.0.0" ], "license": "MIT", "homepage": "https://github.com/acme/typy-templates", "keywords": ["invoice", "billing", "business"] } ``` --- ## Compatibility and versioning policy ### Manifest version The `"manifest_version"` integer is the **primary compatibility signal** for readers. | manifest_version | Status | Supported by | |--------------------|-------------|--------------------| | 1 | **Current** | This specification | Future schemas increment `manifest_version`. A reader that does not recognise a `manifest_version` value **must** reject the package with a clear diagnostic (see [error model]()) rather than silently ignoring unknown fields. ### Forward compatibility within v1 - **New optional fields** may be added to manifest v1 in minor typy releases. Readers encountering unknown fields **must** ignore them (be liberal in what they accept). - **Removing or renaming** required fields, or changing the type of any existing field, requires a new `manifest_version`. ### Package versioning Package authors use [Semantic Versioning](https://semver.org/) for `"version"`: - **MAJOR** – incompatible API or field changes in the template. - **MINOR** – new optional fields or backwards-compatible feature additions. - **PATCH** – bug fixes or asset updates that do not alter the template interface. ### typy_compatibility The `"typy_compatibility"` field uses [PEP 440](https://peps.python.org/pep-0440/#version-specifiers) version specifiers so that the installed typy version can be checked before importing a package. Using a compatible-release specifier (`~=`) or an upper bound (`<`) is recommended when a package relies on behaviour introduced in a specific release. --- ## Error model for invalid packages Errors are reported as structured diagnostics. Each diagnostic has a **code**, a human-readable **message**, and an optional **hint** with a corrective action. ### Diagnostic structure ```json { "code": "PKG_E003", "message": "manifest.json is missing required field: 'name'", "hint": "Add a 'name' field using only lowercase letters, digits, and hyphens (e.g. 'my-template')." } ``` ### Error codes | Code | Trigger condition | Example message | |------------|--------------------------------------------------------------|--------------------------------------------------------------------------------------------------| | `PKG_E001` | File is not a valid ZIP archive | `"Not a valid .typy package: file is not a ZIP archive."` | | `PKG_E002` | `manifest.json` is absent from the archive root | `"manifest.json not found in package root."` | | `PKG_E003` | `manifest.json` is not valid JSON | `"manifest.json contains invalid JSON: ."` | | `PKG_E004` | `manifest_version` is missing | `"manifest.json is missing required field: 'manifest_version'."` | | `PKG_E005` | `manifest_version` is not a recognised integer | `"Unsupported manifest_version: 99. This version of typy supports manifest_version 1."` | | `PKG_E006` | A required field is absent | `"manifest.json is missing required field: ''."` | | `PKG_E007` | A field has the wrong type | `"manifest.json field '' must be , got ."` | | `PKG_E008` | `name` does not match the naming pattern | `"'name' must match ^[a-z0-9][a-z0-9-]*[a-z0-9]$, got ''."` | | `PKG_E009` | `version` is not a valid semver string | `"'version' must be a valid semantic version (e.g. '1.0.0'), got ''."` | | `PKG_E010` | `template.py` is absent from the archive root | `"template.py not found in package root. The Template subclass must be defined in template.py."` | | `PKG_E011` | A path inside the archive escapes the root (`../`) | `"Unsafe path detected in archive: ''. Paths must not traverse outside the package root."` | | `PKG_E012` | `typy_compatibility` is not a valid PEP 440 specifier | `"'typy_compatibility' is not a valid version specifier: ''."` | | `PKG_E013` | Installed typy version does not satisfy `typy_compatibility` | `"Package requires typy but typy is installed."` | Validators **must** collect all applicable errors before reporting, so that a user can fix all problems in one pass. ### Example: invalid manifest — multiple errors ```json { "manifest_version": 1, "name": "My Template", "version": "not-a-version", "description": "A report template.", "author": "Jane Doe", "typy_compatibility": ">=0.1.0" } ``` Expected diagnostics: ```default PKG_E008 'name' must match ^[a-z0-9][a-z0-9-]*[a-z0-9]$, got 'My Template'. Hint: Use only lowercase letters, digits, and hyphens (e.g. 'my-template'). PKG_E009 'version' must be a valid semantic version (e.g. '1.0.0'), got 'not-a-version'. Hint: Use MAJOR.MINOR.PATCH format as defined at https://semver.org. PKG_E010 template.py not found in package root. The Template subclass must be defined in template.py. Hint: Add a template.py file to the archive root that defines a subclass of typy.templates.Template. ``` --- ## Non-goals (MVP) - Implementing `typy package export/import` commands — tracked in a separate issue. - A remote registry or marketplace for packages. - Package signing or tamper-detection. - Dependency resolution or a lock-file mechanism. --- ## See also - [Template reference](templates.md) — built-in templates and field schemas - [CLI reference](cli.md) — available commands - [Getting started](getting-started.md) — first-run walkthrough # templates.html.md # Template reference typy ships seven built-in templates, all sharing a consistent colour palette (blue-600 `#2563eb` accent, slate body text) and typographic family. ## Templates at a glance | report | invoice | letter | |----------------------------------------------|--------------------------------------------------|----------------------------------------------------------| | [![report](../assets/previews/report.png)]() | [![invoice](../assets/previews/invoice.png)]() | [![letter](../assets/previews/letter.png)]() | | **cv** | **academic** | **presentation** | | [![cv](../assets/previews/cv.png)]() | [![academic](../assets/previews/academic.png)]() | [![presentation](../assets/previews/presentation.png)]() | | Template | Best for | Key fields | |--------------------|-----------------------------------|----------------------------------------------------------| | [`report`]() | Multi-section reports with TOC | `title`, `author`, `body`, `abstract`, `toc` | | [`invoice`]() | Business invoices with line items | `company_name`, `client_name`, `items`, `tax_rate` | | [`letter`]() | Formal letters with letterhead | `sender_name`, `recipient_name`, `subject`, `body` | | [`cv`]() | CV / résumé | `name`, `contact`, `experience`, `education`, `skills` | | [`academic`]() | Academic papers with bibliography | `title`, `authors`, `abstract`, `body`, `two_column` | | [`presentation`]() | 16:9 slide decks | `title`, `author`, `slides` (each with `layout_variant`) | | [`basic`]() | Minimal single-section documents | `title`, `author`, `body` | Use `typy info