{"id":34819381,"url":"https://github.com/jlevy/markform","last_synced_at":"2026-02-16T02:59:22.824Z","repository":{"id":330783340,"uuid":"1121347944","full_name":"jlevy/markform","owner":"jlevy","description":"Structured Markdown documents for agents and humans","archived":false,"fork":false,"pushed_at":"2026-01-25T18:55:22.000Z","size":4605,"stargazers_count":30,"open_issues_count":4,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-25T21:57:27.430Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jlevy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-12-22T21:00:43.000Z","updated_at":"2026-01-25T18:55:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jlevy/markform","commit_stats":null,"previous_names":["jlevy/markform"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/jlevy/markform","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jlevy%2Fmarkform","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jlevy%2Fmarkform/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jlevy%2Fmarkform/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jlevy%2Fmarkform/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jlevy","download_url":"https://codeload.github.com/jlevy/markform/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jlevy%2Fmarkform/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28975278,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-01T09:57:52.632Z","status":"ssl_error","status_checked_at":"2026-02-01T09:57:49.143Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-25T14:07:22.204Z","updated_at":"2026-02-13T13:17:11.312Z","avatar_url":"https://github.com/jlevy.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# Markform\n\n[![CI](https://github.com/jlevy/markform/actions/workflows/ci.yml/badge.svg)](https://github.com/jlevy/markform/actions/runs/21981024810)\n[![Coverage](https://raw.githubusercontent.com/jlevy/markform/main/badges/packages/markform/coverage-total.svg)](https://github.com/jlevy/markform/actions/runs/21981024810)\n[![npm version](https://img.shields.io/npm/v/markform)](https://www.npmjs.com/package/markform)\n[![X Follow](https://img.shields.io/twitter/follow/ojoshe)](https://x.com/ojoshe)\n\n### What if your Markdown docs had an agent-friendly semantic API?\n\n**Markform** is a text format for defining structured forms that humans can read,\nmachines can parse, and agents can fill via tool calls.\n\nDefine instructions, fields, and validation rules in a single `.form.md` file.\nAgents fill forms incrementally via patches.\nFields are validated, so errors are caught early and can be corrected.\nHumans can review or intervene at any point.\n\n### Why forms?\n\nFor deep research or complex AI tasks, you need more than just prompts or\nflow: you need *structure*, which is precise control over agent output at every stage of\na workflow. A well-designed form combines instructions, structured data, and validations\nin one place.\n\n### How it Works\n\n- A Markform document exposes a programmatic interface: users fill fields via CLI or web\n  UI, agents fill via tool calls ([Vercel AI SDK](https://github.com/vercel/ai)\n  integration included).\n\n- Changes are explicit patch operations\n  (`{ \"op\": \"set_string\", \"fieldId\": \"name\", \"value\": \"Alice\" }`) validated against a\n  schema specified in the form.\n  The agent sees validation errors and can self-correct.\n\n- The format extends Markdown with a\n  [precise specification](https://github.com/jlevy/markform/blob/main/docs/markform-spec.md).\n  Export Markform syntax to JSON, YAML, JSON Schema, or plain Markdown reports.\n\n- Optionally, the whole thing is wrapped in a harness where large forms can be filled\n  concurrently by any LLM in a structured agentic loop.\n\n### Useful details\n\n- Markform syntax is a good source format: it is **token-efficient text** you can read, diff, and\n  version control and it is **ideal for context engineering** because it combines\n  document context, data schema, and memory (data filled so far).\n\n- Structure is defined with HTML comment tags (`\u003c!-- field --\u003e`) that\n  render invisibly on GitHub, so **forms look like regular Markdown**. (Jinja-style\n  tag syntax also works if you prefer.)\n\n- Optionally, **a fill record** of the form-filling process is kept, so you can see\n  and debug exactly how forms are filled by agents, tool usage, LLM call time, etc.\n\n- The CLI has a built-in web renderer, **`markform serve`**, for easy viewing and debugging\n  of forms (including a form web UI, the form schema, and a waterfall-style overview of the\n  fill record, including performance details, which is useful for large, concurrently filled forms).\n\n## Simple Example: Research a Movie\n\n### Form Definition\n\nA `.form.md` file combines YAML frontmatter with HTML comment tags that define\nstructure. The text can be any Markdown.\nHere is the\n[movie-research-demo.form.md](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-research-demo.form.md):\n\n```markdown\n---\nmarkform:\n  spec: MF/0.1\n  title: Movie Research Demo\n  description: Movie lookup with ratings from IMDB and Rotten Tomatoes.\n  run_mode: research\n  roles:\n    - user\n    - agent\n  role_instructions:\n    user: \"Enter the movie title.\"\n    agent: |\n      Identify the movie with web searches and use imdb.com and rottentomatoes.com to fill in the ratings.\n---\n\n\u003c!-- form id=\"movie_research_demo\" --\u003e\n\u003c!-- group id=\"movie_input\" --\u003e\n\n## What movie do you want to research?\n\n\u003c!-- field kind=\"string\" id=\"movie\" label=\"Movie\" role=\"user\" required=true minLength=1 maxLength=300 --\u003e\u003c!-- /field --\u003e\n\u003c!-- instructions ref=\"movie\" --\u003eEnter the movie title (add year or details for disambiguation).\u003c!-- /instructions --\u003e\n\n\u003c!-- /group --\u003e\n\n\u003c!-- group id=\"about_the_movie\" title=\"About the Movie\" --\u003e\n\n## Movie Ratings\n\nHere are the ratings for the movie:\n\n\u003c!-- field kind=\"single_select\" id=\"mpaa_rating\" role=\"agent\" label=\"MPAA Rating\" --\u003e\n\n- [ ] G \u003c!-- #g --\u003e\n- [ ] PG \u003c!-- #pg --\u003e\n- [ ] PG-13 \u003c!-- #pg_13 --\u003e\n- [ ] R \u003c!-- #r --\u003e\n- [ ] NC-17 \u003c!-- #nc_17 --\u003e\n- [ ] NR/Unrated \u003c!-- #nr --\u003e\n\n\u003c!-- /field --\u003e\n\n\u003c!-- field kind=\"table\" id=\"ratings_table\" role=\"agent\" label=\"Ratings\" required=true columnIds=[\"source\", \"score\", \"votes\"] columnTypes=[\"string\", \"number\", \"number\"] minRows=0 maxRows=3 --\u003e\n\n| Source | Score | Votes |\n|--------|-------|-------|\n\n\u003c!-- /field --\u003e\n\n\u003c!-- instructions ref=\"ratings_table\" --\u003e\nFill in scores and vote counts from each source:\n- IMDB: Rating (1.0-10.0 scale), vote count\n- RT Critics: Tomatometer (0-100%), review count\n- RT Audience: Audience Score (0-100%), rating count\n\n\u003c!-- /instructions --\u003e\n\n\u003c!-- /group --\u003e\n\u003c!-- /form --\u003e\n```\n\nFields have types defined by the attributes.\nA field can have `role=\"user\"` (filled interactively) or `role=\"agent\"` (filled by an\nagent). Values are filled in incrementally, just like any form.\n\n### Filled Form\n\nThe key point is that with this structure **a Markdown file automatically gets a schema\nand a tool API.**\n\nAgents or users can fill in values using a TypeScript API or via agent tool calls.\n\nAnd agents find this format **highly context efficient**. All information needed to fill\nin the form is right there, not in long conversation history.\nAnd it can be done incrementally, a few fields at a time.\n\nOnce filled in, values appear directly inside the tags, in Markdown format:\n\n````markdown\n\u003c!-- form id=\"movie_research_demo\" --\u003e\n\u003c!-- group id=\"movie_input\" --\u003e\n\n\u003c!-- field kind=\"string\" id=\"movie\" label=\"Movie\" role=\"user\" required=true minLength=1 maxLength=300 --\u003e\n```value\nThe Shawshank Redemption\n```\n\u003c!-- /field --\u003e\n\n\u003c!-- /group --\u003e\n\n\u003c!-- group id=\"about_the_movie\" title=\"About the Movie\" --\u003e\n\n\u003c!-- field kind=\"single_select\" id=\"mpaa_rating\" role=\"agent\" label=\"MPAA Rating\" --\u003e\n\n- [ ] G \u003c!-- #g --\u003e\n- [ ] PG \u003c!-- #pg --\u003e\n- [ ] PG-13 \u003c!-- #pg_13 --\u003e\n- [x] R \u003c!-- #r --\u003e\n- [ ] NC-17 \u003c!-- #nc_17 --\u003e\n- [ ] NR/Unrated \u003c!-- #nr --\u003e\n\n\u003c!-- /field --\u003e\n\n\u003c!-- field kind=\"table\" id=\"ratings_table\" role=\"agent\" label=\"Ratings\" required=true columnIds=[\"source\", \"score\", \"votes\"] columnTypes=[\"string\", \"number\", \"number\"] minRows=0 maxRows=3 --\u003e\n\n| Source | Score | Votes |\n| --- | --- | --- |\n| IMDB | 9.3 | 3100000 |\n| RT Critics | 89 | 146 |\n| RT Audience | 98 | 250000 |\n\n\u003c!-- /field --\u003e\n\n\u003c!-- /group --\u003e\n\u003c!-- /form --\u003e\n````\n\n### Report Output\n\nRun `npx markform examples` to copy examples, then `npx markform run` and select\n`Movie Research Demo` to fill it.\n\nA form can be exported as the filled form (Markform), as a report (plain Markdown), as\nvalues (YAML or JSON), or as a JSON Schema (just the structure).\n\nThe report output (using `gpt-5-mini` to fill it in) looks like:\n\n```markdown\nMovie:\nThe Shawshank Redemption\n\n## About the Movie\n\nMPAA Rating:\nR\n\nRatings:\n| Source | Score | Votes |\n| --- | --- | --- |\n| IMDB | 9.3 | 3100000 |\n| RT Critics | 89 | 146 |\n| RT Audience | 98 | 250000 |\n```\n\n\u003cdetails\u003e \u003csummary\u003eYAML Export (click to expand)\u003c/summary\u003e\n\n```yaml\nvalues:\n  movie:\n    state: answered\n    value: The Shawshank Redemption\n  mpaa_rating:\n    state: answered\n    value: r\n  ratings_table:\n    state: answered\n    value:\n      - source: IMDB\n        score: 9.3\n        votes: 3100000\n      - source: RT Critics\n        score: 89\n        votes: 146\n      - source: RT Audience\n        score: 98\n        votes: 250000\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e \u003csummary\u003eJSON Schema (click to expand)\u003c/summary\u003e\n\n```json\n{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"movie_research_demo\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"movie\": {\n      \"type\": \"string\",\n      \"title\": \"Movie\",\n      \"minLength\": 1,\n      \"maxLength\": 300\n    },\n    \"mpaa_rating\": {\n      \"type\": \"string\",\n      \"enum\": [\"g\", \"pg\", \"pg_13\", \"r\", \"nc_17\", \"nr\"],\n      \"title\": \"MPAA Rating\"\n    },\n    \"ratings_table\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"source\": { \"title\": \"Source\", \"type\": \"string\" },\n          \"score\": { \"title\": \"Score\", \"type\": \"number\" },\n          \"votes\": { \"title\": \"Votes\", \"type\": \"number\" }\n        }\n      },\n      \"title\": \"Ratings\",\n      \"minItems\": 0,\n      \"maxItems\": 3\n    }\n  },\n  \"required\": [\"movie\", \"ratings_table\"]\n}\n```\n\n\u003c/details\u003e\n\n## Why Do Agents Need Forms?\n\nFor centuries, humans have used paper forms and\n[checklists](https://en.wikipedia.org/wiki/The_Checklist_Manifesto) to systematize\ncomplex processes. A form with instructions, field definitions, and validations is a\nconcise way to share context: goals, background knowledge, process rules, and state\n(memory). I don’t think AI changes this essential aspect of knowledge work.\n\nMost agent frameworks focus on *prompts* and *flow* (the how) over the *structure* of\nresults (the what).\nBut for deep research or other multi-step workflows, you need precise\ncontrol over intermediate states and final output.\nYou don’t want that structure in a GUI (not token-friendly), in code (hard to update),\nor dependent on model whims (changes unpredictably with model updates).\n\nForms solve this. Forms codify operational excellence.\nThey’re easy to read, easy to edit, and enforce standards.\nBecause LLMs handle Markdown well, agents can also help create and improve the forms\nthemselves—closing the meta-loop.\n\nIt’s time to bring bureaucracy to the agents!\nSee [the FAQ](#faq) for more on the design.\n\n## Quick Start\n\n```bash\n# Copy example forms to ./forms/ and run one interactively.\n# Set OPENAI_API_KEY or ANTHROPIC_API_KEY (or put in .env) for research examples\nnpx markform@latest examples\n\n# Read the docs (tell your agents to run these; they are agent-friendly!)\nnpx markform  # CLI help\nnpx markform readme   # This file\nnpx markform docs  # Quick reference for writing Markforms\nnpx markform spec  # Read the full spec\n```\n\nThe `markform examples` command copies some sample forms to `./forms` and prompts you to\nfill in a form interactively and then optionally have an agent complete it.\nPick `movie-research-demo.form.md` for a quick example.\n\n## Installation\n\nRequires Node.js 20+.\n\n```bash\n# As a global CLI\nnpm install -g markform\n\n# Or as a project dependency\nnpm install markform\n```\n\n### More Example Forms\n\nThe package includes example forms.\nView them with `markform examples --list`, copy with `markform examples`, and run with\n`markform run`:\n\n- [`simple.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/simple/simple.form.md)\n  \\- Basic form demonstrating all field kinds.\n\n- [`movie-research-demo.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-research-demo.form.md)\n  \\- The quick example above.\n\n- [`movie-deep-research.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-deep-research.form.md)\n  \\- Comprehensive movie analysis with streaming, box office, technical specs.\n\n### Why a New Format?\n\nThe closest alternatives I’ve seen are:\n\n- Plain Markdown docs can be used as templates and filled in by agents.\n  These are more expressive, but it is hard to edit them programmatically or use LLMs to\n  update them reliably.\n\n- JSON + JSON Schema which are good for struture but terrible for additional\n  unstructured context like instructions Markdown.\n\n- Agent to-do lists are part of many chat or coding interfaces and are programmatically\n  edited by agents. But these are limited to simple checklists, not forms with other\n  fields.\n\n- Numerous tools like Typeform, Google Forms, PDF forms, and Docusign offer\n  human-friendly UI. But these do not have a human-friendly text format for use by\n  agents as well as humans.\n\n| Approach | Usable GUI editor | Human-readable source format | Agent-editable | APIs and validation rules |\n| --- | :---: | :---: | :---: | :---: |\n| Plain Markdown | ✅\u003cbr\u003eIDEs/editors | ✅ | ⚠️\u003cbr\u003efragile | ❌ |\n| JSON + JSON Schema | ✅\u003cbr\u003eIDEs/editors | ⚠️\u003cbr\u003eno free text | ✅ | ✅ |\n| SaaS tools (Typeform, Docusign, PDF forms) | ✅ | ⚠️\u003cbr\u003erarely | ⚠️\u003cbr\u003esometimes | ⚠️\u003cbr\u003esometimes |\n| HTML/web Forms | ✅\u003cbr\u003eIDEs/editors | ⚠️\u003cbr\u003eHTML+code | ⚠️\u003cbr\u003ecoding agent | ✅ |\n| Excel/Google Sheets | ✅\u003cbr\u003eapp | ❌\u003cbr\u003e.csv/.xlsx | ⚠️\u003cbr\u003ewith tools | ✅\u003cbr\u003ewith some coding |\n| **Markform** | ✅\u003cbr\u003eIDEs/editors | ✅ | ✅\u003cbr\u003ewith this package | ✅\u003cbr\u003ewith this package |\n\n## Architecture\n\nThis repo has a specification and an implementation.\nThe implementation is a TypeScript API with Vercel AI SDK integration, and a CLI\ninterface.\n\n```mermaid\nflowchart LR\n    subgraph SPEC[\"\u003cb\u003eMARKFORM SPEC\u003c/b\u003e\"]\n        direction TB\n\n        subgraph L1[\"\u003cb\u003eLAYER 1: SYNTAX\u003c/b\u003e\u003cbr/\u003eMarkdoc tag syntax\u003cbr/\u003eand frontmatter (form,\u003cbr/\u003egroup, string-field, \u003cbr/\u003echeckboxes, etc.)\"]\n        end\n\n        subgraph L2[\"\u003cb\u003eLAYER 2: FORM DATA MODEL\u003c/b\u003e\u003cbr/\u003eSchema definitions\u003cbr/\u003efor forms, fields, values\"]\n        end\n\n        subgraph L3[\"\u003cb\u003eLAYER 3: VALIDATION \u003cbr/\u003eAND PATCHES\u003c/b\u003e\u003cbr/\u003eRules for filling forms\u003cbr/\u003evia patches, required\u003cbr/\u003efield semantics, validation\"]\n        end\n\n        subgraph L4[\"\u003cb\u003eLAYER 4: TOOL API \u003cbr/\u003eAND INTERFACES\u003c/b\u003e\u003cbr/\u003eAbstract form-filling\u003cbr/\u003eloop, concurrency\u003cbr/\u003emodel, tool layer\"]\n        end\n\n        L4 --\u003e L3 --\u003e L2 --\u003e L1\n    end\n\n    subgraph IMPL[\"\u003cb\u003eTHIS IMPLEMENTATION\u003c/b\u003e\"]\n        direction TB\n\n        subgraph CLI[\"\u003cb\u003e`markform` CLI\u003c/b\u003e\u003cbr/\u003eCommand-line interface\u003cbr/\u003eto all features\"]\n        end\n\n        subgraph AGENT[\"\u003cb\u003eAGENT TOOL INTERFACE\u003c/b\u003e\u003cbr/\u003eTool API library\u003e\u003cbr/\u003e(AI SDK tools)\"]\n        end\n\n        subgraph HARNESS[\"\u003cb\u003eEXECUTION HARNESS\u003c/b\u003e\u003cbr/\u003eConcurrent form-filling\u003cbr/\u003eagentic loop\u003cbr/\u003e(AI SDK)\"]\n        end\n\n        subgraph ENGINE[\"\u003cb\u003eCORE TYPESCRIPT APIS\u003c/b\u003e\u003cbr/\u003eMarkdoc parser, serializer,\u003cbr/\u003epatch application,\u003cbr/\u003evalidation (jiti for rules)\"]\n        end\n\n        subgraph TEST[\"\u003cb\u003eTESTING FRAMEWORK\u003c/b\u003e\u003cbr/\u003eGolden session testing\u003cbr/\u003e(.session.yaml transcripts)\"]\n        end\n\n        CLI --\u003e ENGINE\n        CLI --\u003e HARNESS\n        AGENT --\u003e HARNESS\n        AGENT --\u003e ENGINE\n        HARNESS --\u003e ENGINE\n        ENGINE --\u003e TEST\n    end\n\n    SPEC ~~~ IMPL\n\n    style SPEC fill:#e8f4f8,stroke:#0077b6\n    style L1 fill:#caf0f8,stroke:#0077b6\n    style L2 fill:#caf0f8,stroke:#0077b6\n    style L3 fill:#caf0f8,stroke:#0077b6\n    style L4 fill:#caf0f8,stroke:#0077b6\n    style IMPL fill:#fff3e6,stroke:#fb8500\n    style ENGINE fill:#ffe8cc,stroke:#fb8500\n    style CLI fill:#ffe8cc,stroke:#fb8500\n    style AGENT fill:#ffe8cc,stroke:#fb8500\n    style HARNESS fill:#ffe8cc,stroke:#fb8500\n    style TEST fill:#ffe8cc,stroke:#fb8500\n```\n\n## CLI Commands\n\n### Copy and Run Examples\n\n```bash\n# Copy all bundled examples to ./forms/\nmarkform examples\n# List available examples\nmarkform examples --list\n# Copy a specific example\nmarkform examples --name movie-research-demo\n```\n\n### Browse and Run Forms\n\n```bash\n# Browse forms in ./forms/ and run one interactively\nmarkform run\n# Run a specific form directly\nmarkform run forms/movie-research-demo.form.md\n```\n\n### Check Form Status\n\n```bash\n# Show fill progress with per-role breakdown\nmarkform status my-form.form.md\n```\n\n### Inspect Forms\n\n```bash\n# View form structure, progress, and validation issues\nmarkform inspect my-form.form.md\n# Output as JSON\nmarkform inspect my-form.form.md --format=json\n```\n\n### Fill Forms\n\n```bash\n# Interactive mode: fill user-role fields via prompts\nmarkform fill my-form.form.md --interactive\n# Agent mode: use an LLM to fill agent-role fields\nmarkform fill my-form.form.md --model=anthropic/claude-sonnet-4-5\n# Mock agent for testing (uses pre-filled form as source)\nmarkform fill my-form.form.md --mock --mock-source filled.form.md\n# Record fill session to JSON sidecar (tokens, timing, tool calls)\nmarkform fill my-form.form.md --model=openai/gpt-5-mini --record-fill\n```\n\n### Export and Transform\n\n```bash\n# Export as readable markdown (strips Markdoc tags)\nmarkform export my-form.form.md --format=markdown\n# Export values as JSON\nmarkform export my-form.form.md --format=json\n# Export values as YAML\nmarkform export my-form.form.md --format=yaml\n# Dump just the current values\nmarkform dump my-form.form.md\n```\n\n### Export JSON Schema\n\n```bash\n# Export form structure as JSON Schema (for validation, code generation, etc.)\nmarkform schema my-form.form.md\n# Pure JSON Schema without Markform extensions\nmarkform schema my-form.form.md --pure\n# Specify JSON Schema draft version\nmarkform schema my-form.form.md --draft draft-07\n```\n\n### Apply Patches\n\n```bash\n# Apply a JSON patch to update field values\nmarkform apply my-form.form.md --patch '[{\"op\":\"set_string\",\"fieldId\":\"name\",\"value\":\"Alice\"}]'\n```\n\n### Web Interface\n\n```bash\n# Serve a form as a web page for browsing\nmarkform serve my-form.form.md\n```\n\n### Documentation Commands\n\n```bash\n# Quick reference for writing forms (agent-friendly)\nmarkform docs\n# Full specification\nmarkform spec\n# TypeScript and AI SDK API documentation\nmarkform apis\n# This README\nmarkform readme\n# See supported AI providers and example models\nmarkform models\n# See all commands\nmarkform --help\n```\n\n## API Key Setup\n\nSet the appropriate environment variable for your provider before running\n`markform fill`. The CLI automatically loads from `.env.local` and `.env` files in the\ncurrent directory.\n\nSupported providers:\n\n| Provider | Env Variable | Native Web Search |\n| --- | --- | :---: |\n| openai | `OPENAI_API_KEY` | ✓ |\n| anthropic | `ANTHROPIC_API_KEY` | ✓ |\n| google | `GOOGLE_API_KEY` | ✓ |\n| xai | `XAI_API_KEY` | ✓ |\n| deepseek | `DEEPSEEK_API_KEY` | ✗ |\n\nRun `markform models` to see available models.\nSee\n[`src/settings.ts`](https://github.com/jlevy/markform/blob/main/packages/markform/src/settings.ts)\nfor defaults.\n\nIf unsure, try `gpt-5-mini` first as it’s fast and supports web search.\n\n## Programmatic Usage\n\nMarkform exports a parsing engine, rendering functions, and AI SDK integration for use in\nyour own applications.\n\n### Basic Parsing\n\n```typescript\nimport { parseForm, serialize } from \"markform\";\n\n// Parse a .form.md file\nconst form = parseForm(markdownContent);\n\n// Access schema and values\nconsole.log(form.schema.title);\nconsole.log(form.responsesByFieldId);\n\n// Serialize back to markdown\nconst output = serialize(form);\n```\n\n### High-Level Fill API\n\nThe simplest way to fill a form programmatically:\n\n```typescript\nimport { fillForm } from \"markform\";\n\nconst result = await fillForm({\n  form: markdownContent,\n  model: \"anthropic/claude-sonnet-4-5\",\n  enableWebSearch: true,\n  recordFill: true, // Capture tokens, timing, tool calls\n});\n\nif (result.status.ok) {\n  console.log(\"Values:\", result.values);\n  console.log(\"Tokens used:\", result.record?.llm.inputTokens);\n}\n```\n\nSee the\n[API documentation](https://github.com/jlevy/markform/blob/main/docs/markform-apis.md)\nfor options like parallel execution, callbacks, and checkpointing.\n\n### Rendering API\n\nImport from the `markform/render` subpath to render forms and fill records as HTML\nfragments — the same output as `markform serve`, without pulling in CLI/server\ndependencies:\n\n```typescript\nimport {\n  renderViewContent,\n  renderFillRecordContent,\n  FILL_RECORD_STYLES,\n  FILL_RECORD_SCRIPTS,\n} from \"markform/render\";\n\n// Render a filled form as read-only HTML\nconst formHtml = renderViewContent(parsedForm);\n\n// Render a fill record dashboard\nconst dashboardHtml = renderFillRecordContent(fillRecord);\n```\n\nAlso exports `renderSourceContent`, `renderMarkdownContent`, `renderYamlContent`,\n`renderJsonContent`, `escapeHtml`, `formatDuration`, and `formatTokens`.\n\nSee the\n[API documentation](https://github.com/jlevy/markform/blob/main/docs/markform-apis.md#rendering-api)\nfor full details.\n\n### AI SDK Integration\n\nMarkform provides tools compatible with the [Vercel AI SDK](https://sdk.vercel.ai/):\n\n```typescript\nimport { parseForm } from \"markform\";\nimport { createMarkformTools, MarkformSessionStore } from \"markform/ai-sdk\";\nimport { generateText } from \"ai\";\nimport { anthropic } from \"@ai-sdk/anthropic\";\n\n// Parse form and create session store (tracks state across tool calls)\nconst form = parseForm(markdownContent);\nconst store = new MarkformSessionStore(form);\n\n// Create tools the agent can call: inspect, apply patches, export\nconst tools = createMarkformTools({ sessionStore: store });\n\n// Agent fills the form via tool calls until complete or maxSteps reached\nconst result = await generateText({\n  model: anthropic(\"claude-sonnet-4-5-20250929\"),\n  prompt: \"Fill out this form with appropriate values...\",\n  tools,\n  maxSteps: 10,\n});\n```\n\n**Available tools:**\n\n| Tool | Description |\n| --- | --- |\n| `markform_inspect` | Get current form state, issues, progress |\n| `markform_apply` | Apply patches to update field values |\n| `markform_export` | Export schema and values as JSON |\n| `markform_get_markdown` | Get canonical Markdown representation |\n\n## Other Documentation\n\n- **[Quick Reference](https://github.com/jlevy/markform/blob/main/docs/markform-reference.md)**\n  (or run `markform docs`) — Concise syntax reference (agent-friendly)\n\n- **[Markform Spec](https://github.com/jlevy/markform/blob/main/docs/markform-spec.md)**\n  (or run `markform spec`) — Complete syntax and semantics\n\n- **[API Documentation](https://github.com/jlevy/markform/blob/main/docs/markform-apis.md)**\n  (or run `markform apis`) — TypeScript and AI SDK APIs\n\n- **[Design Doc](https://github.com/jlevy/markform/blob/main/docs/project/architecture/current/arch-markform-design.md)**\n  — Technical design and roadmap\n\n- **[Development](https://github.com/jlevy/markform/blob/main/docs/development.md)** —\n  Build, test, and contribute\n\n## FAQ\n\n### Can you talk more about why forms are cool?\n\nAs a matter of fact, I can!\nI’ve come to believe forms are a missing piece of the workflow problem with agents.\nFor deep research or complex multi-step workflows, key pieces need to be *precisely\ncontrolled*, *domain-specific*, and *always improving*. You need precise documentation\non the key intermediate states and final output from an AI pipeline.\n\nBut you don’t want structure in a GUI (not token friendly) or code (hard to update) or\ndependent on the whims of a thinking model (changes all the time).\nForms define these pieces and are easy to edit.\nAll other choices can be left to the agents themselves, with the structure and\nvalidations enforced by the form-filling tools the agents use.\n\nAnother cool thing about forms: they get rid of the inefficiencies of conversation chat\nhistory.\nOften when an agentic loop is built, it just saves the chat history for context.\nBut a form is inherently more efficient: the harness itself can be stateless.\nIt just shares the partly-filled form with the agent, and it has full context in one\nmessage. That’s what the agentic loop in this implementation does.\n\nFinally, the meta-loop of *creating and improving* forms is easier to automate:\n\n- To get started, you can ask a good coding model to convert any unstructured doc\n  describing a process to a form.\n  The model can also use the CLI or tools to validate and test it.\n\n- Any time you have a workflow problem, you can ask an LLM to diagnose it and if\n  possible, go back and fix up the form with additional instructions or fields or checks\n  that would prevent the problem from happening again.\n\nI suspect dynamic form structures like this could make complex deep research more\npowerful. Just as you plan a spec before implementing with a coding agent, you could use\nMarkform to encode a research plan before dispatching agents to fill it.\n\n### Is this mature?\n\nNo! I just wrote it.\nThe spec is a draft.\nBut it’s been useful for me already.\n\n### Was it Vibe Coded?\n\nThis is 100% agent-written code and the planning specs are also 100% agent written,\nusing a mix of Opus 4.5, GPT 5.2, GPT-5 Pro, Gemini 3, and occasionally others.\n\nBut it’s not slop. It is written via a strongly spec-driven process, using rules from my\n[Speculate](https://github.com/jlevy/speculate) repo and Steve Yegge’s\n[beads](https://github.com/steveyegge/beads) for tracking work.\n\nSee\n[my post](https://github.com/jlevy/speculate/blob/main/about/lessons_in_spec_coding.md)\nfor more thoughts on how this works.\nAnd see\n[the complete history of specs](https://github.com/jlevy/markform/tree/main/docs/project/specs/done)\nfor examples of how everything is done with specs.\n\nAlthough I didn’t write much code, there was a *lot* of management, review, and\niteration on design decisions.\nAnd yes, this README is written by me.\n:)\n\n### What are the goals of Markform?\n\n- **Markform should express complex structure and validation rules for outputs:** Fields\n  can be arbitrary types like checkboxes, strings, dates, numbers, URLs, and lists.\n  Validation rules can be simple (min and max value, regexes), arbitrary code, or LLM\n  calls.\n\n- **Markform is programmatically editable:** Field state should be updated via APIs, by\n  apps, or by agent tools.\n\n- **Markform is readable by humans and agents:** Both templates and field values of a\n  form should have a clear text format (not a binary or obscure XML format only readable\n  by certain applications).\n\n### How do agents fill in forms?\n\nThe data model and editing API let agents fill in forms.\nThis enables powerful AI workflows that assemble information in a defined structure:\n\n- **Form content, structure, and field values are in a single text file** for better\n  context engineering.\n  This is a major advantage for LLM agents and for humans reviewing their work.\n\n- **Incremental filling** means an agent or a human can take many iterations, filling\n  and correcting a form until it is complete and satisfies the validation rules.\n\n- **Multiple interfaces for humans or agents** can work with the same forms.\n  You can interact with a form via a CLI, a programmatic API, from Vercel AI SDK or in\n  an MCP server used by an agent, or in web form UIs for humans.\n\n- **Flexible validation** at multiple scopes (field/group/form), including declarative\n  constraints and external hooks to arbitrary code (currently TypeScript) or LLM-based\n  validation instructions.\n\n- An **agent execution harness** for step-by-step form filling, enabling deep research\n  agents that assemble validated output in a structured format.\n\n### What are example use cases?\n\n- Deep research tools where agents need to follow codified processes to assemble\n  information\n\n- Practical task execution plans with checklists and assembled answers and notes\n\n- Analysis processes, like assembling insights from unstructured sources in structured\n  form\n\n- Multi-agent and agent-human workflows, where humans and/or agents fill in different\n  parts of a form, or where humans or agents review each other’s work in structured ways\n\n- A clean and readable text format for web UIs that involve filling in forms, supporting\n  strings, lists, numbers, checkboxes, URLs, and other fields\n\n### Why use Markdoc as a base format?\n\nMarkdoc extends Markdown with structured tags, allowing AST parsing and programmatic\nmanipulation while preserving human and LLM readability.\nSee Stripe’s [Markdoc overview][markdoc-overview] and [blog post][stripe-markdoc] for\nmore on the philosophy behind “docs-as-data” that Markform extends to “forms-as-data.”\nWe could use XML tags, but Markdoc has some niceties like tagging Markdown AST nodes\n(`{% #some-id %}`) so I decided to go with this.\n\n### What editor settings work best?\n\n**HTML comment syntax (recommended):** Regular Markdown mode works perfectly since\n`\u003c!-- tag --\u003e` comments are standard Markdown.\n\n**Markdoc syntax (`{% tag %}`):** Install\n[Better Jinja](https://marketplace.visualstudio.com/items?itemName=samuelcolvin.jinjahtml)\nand associate `.form.md` files with `jinja-md` mode:\n\n```json\n\"files.associations\": {\n  \"*.form.md\": \"jinja-md\"\n}\n```\n\n## License\n\nThis project uses a dual licensing approach:\n\n- **Markform Specification** ([`docs/markform-spec.md`](docs/markform-spec.md),\n  [`docs/markform-reference.md`](docs/markform-reference.md)):\n  [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/). You may freely implement\n  this specification in your own software under any license.\n\n- **Reference Implementation** (all code in this repository):\n  [AGPL-3.0-or-later](./LICENSE). [Contact me](https://github.com/jlevy) for commercial\n  licensing options.\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for contribution terms.\n\n[markdoc-overview]: https://markdoc.dev/docs/overview\n[stripe-markdoc]: https://stripe.com/blog/markdoc\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjlevy%2Fmarkform","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjlevy%2Fmarkform","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjlevy%2Fmarkform/lists"}