markform 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +451 -240
  2. package/dist/ai-sdk.d.mts +1 -1
  3. package/dist/ai-sdk.mjs +2 -2
  4. package/dist/{apply-g23rRn7p.mjs → apply-B2kt6C2z.mjs} +136 -32
  5. package/dist/bin.mjs +1 -1
  6. package/dist/{cli-Bqlm-WWw.mjs → cli-Dt_PlYi_.mjs} +519 -60
  7. package/dist/cli.mjs +1 -1
  8. package/dist/{coreTypes-__Cwxz5q.mjs → coreTypes-B1oI7qvV.mjs} +52 -4
  9. package/dist/{coreTypes-DCvD7feM.d.mts → coreTypes-JCPm418M.d.mts} +265 -9
  10. package/dist/index.d.mts +22 -10
  11. package/dist/index.mjs +5 -5
  12. package/dist/{session-DruaYPZ1.mjs → session-CzCh6JeY.mjs} +1 -1
  13. package/dist/{session-CgCNni0e.mjs → session-Dxqwt0RC.mjs} +3 -3
  14. package/dist/{shared-C9yW5FLZ.mjs → shared-CNqwaxUt.mjs} +1 -1
  15. package/dist/{shared-DQ6y3Ggc.mjs → shared-D3dNi-Gn.mjs} +1 -1
  16. package/dist/{src-BiuxbzF3.mjs → src-DFsC5wwy.mjs} +308 -59
  17. package/docs/markform-apis.md +30 -1
  18. package/docs/markform-reference.md +65 -6
  19. package/docs/markform-spec.md +3 -3
  20. package/examples/movie-research/{movie-research-deep.form.md → movie-deep-research.form.md} +17 -58
  21. package/examples/movie-research/movie-research-demo.form.md +25 -34
  22. package/examples/rejection-test/rejection-test-mock-filled.form.md +41 -0
  23. package/examples/rejection-test/rejection-test-mock-filled.report.md +15 -0
  24. package/examples/rejection-test/rejection-test-mock-filled.schema.json +59 -0
  25. package/examples/rejection-test/rejection-test-mock-filled.yml +13 -0
  26. package/examples/rejection-test/rejection-test.form.md +35 -0
  27. package/examples/rejection-test/rejection-test.session.yaml +534 -0
  28. package/examples/simple/simple-mock-filled.report.md +96 -0
  29. package/examples/simple/simple-mock-filled.schema.json +374 -0
  30. package/examples/simple/simple-mock-filled.yml +87 -0
  31. package/examples/simple/simple-skipped-filled.report.md +90 -0
  32. package/examples/simple/simple-skipped-filled.schema.json +374 -0
  33. package/examples/simple/simple-skipped-filled.yml +77 -0
  34. package/examples/simple/simple-with-skips.session.yaml +1969 -21
  35. package/examples/simple/simple.session.yaml +1982 -21
  36. package/package.json +1 -1
  37. package/examples/earnings-analysis/earnings-analysis.form.md +0 -159
  38. package/examples/earnings-analysis/earnings-analysis.raw.md +0 -801
  39. package/examples/earnings-analysis/earnings-analysis.valid.ts +0 -198
  40. package/examples/movie-research/movie-research-basic.form.md +0 -169
package/README.md CHANGED
@@ -1,49 +1,69 @@
1
1
  # Markform
2
2
 
3
- **Markform** turns Markdown documents into executable specifications for structured data
4
- collection.
5
- Define fields, validation rules, and instructions in a single `.form.md` file
6
- that is readable by humans, parseable by machines, and fillable by LLM agents.
7
-
8
- The core idea: **a form combines structure, unstructured context, and memory in a simple
9
- text document**. Users can give inputs via UIs or CLI. Agents can fill fields
10
- incrementally via tools or APIs.
11
- Validation catches errors early and humans can review or intervene at any point.
12
- The entire workflow is visible in a token-friendly text file you can read, diff, and
13
- version control.
3
+ [![CI](https://github.com/jlevy/markform/actions/workflows/ci.yml/badge.svg)](https://github.com/jlevy/markform/actions/workflows/ci.yml)
4
+ ![Coverage](./badges/coverage-total.svg)
5
+
6
+ **Markform** is a text format for defining structured forms that humans can read,
7
+ machines can parse, and agents can fill via tool calls.
8
+
9
+ Define instructions, fields, and validation rules in a single `.form.md` file.
10
+ Agents fill forms incrementally via patches.
11
+ Fields are validated, so errors are caught early and can be corrected.
12
+ Humans can review or intervene at any point.
13
+
14
+ **Why forms?** For deep research or complex AI tasks, you need more than just prompts or
15
+ flow: you need *structure*, which is precise control over agent output at every stage of
16
+ a workflow. A well-designed form combines instructions, structured data, and validations
17
+ in one place.
18
+
19
+ **How it works:**
14
20
 
15
- Syntax is [Markdoc](https://github.com/markdoc/markdoc), which is Markdown extended with
16
- `{% tag %}` annotations, so LLMs are already quite good at writing Markform docs.
21
+ - A Markform document exposes a programmatic interface: users fill fields via CLI or web
22
+ UI, agents fill via tool calls ([Vercel AI SDK](https://github.com/vercel/ai)
23
+ integration included).
17
24
 
18
- ## Why?
25
+ - Changes are explicit patch operations (`{ "op": "set_string", "fieldId": "name",
26
+ "value": "Alice" }`) validated against a schema specified in the form.
27
+ The agent sees validation errors and can self-correct.
19
28
 
20
- Many agent workflow frameworks emphasize *prompts* and the *flow* of information (the
21
- *how*) over the desired *structure* of the results (the *what*). Markform lets you build
22
- agent workflows by structuring and validating *what* you want (the structure of
23
- information, validations, and reviews encoded in a form) instead of *how* to run a
24
- workflow as code (via explicit workflows or just an unstructured swarm of agents).
29
+ - The format extends Markdown with a
30
+ [precise specification](https://github.com/jlevy/markform/blob/main/docs/markform-spec.md).
31
+ Export Markform syntax to JSON, YAML, JSON Schema, or plain Markdown reports.
25
32
 
26
- For centuries, humans have used paper forms to systematize and manage processes.
27
- A well-designed form with instructions, field definitions, and validations is a concise
28
- way to share context: background knowledge, goals, process rules, and memories.
29
- I don’t think AI changes this essential aspect of knowledge work.
30
- It’s time to bring bureaucracy to the agents.
33
+ Markform syntax is a good source format: token-efficient text you can read, diff, and
34
+ version control.
35
+ It is built with [Markdoc](https://github.com/markdoc/markdoc), which is
36
+ a Markdown extension from Stripe with Jinja-style `{% tag %}` annotations.
37
+
38
+ ## Why Do Agents Need Forms?
39
+
40
+ For centuries, humans have used paper forms and
41
+ [checklists](https://en.wikipedia.org/wiki/The_Checklist_Manifesto) to systematize
42
+ complex processes. A form with instructions, field definitions, and validations is a
43
+ concise way to share context: goals, background knowledge, process rules, and state
44
+ (memory). I don’t think AI changes this essential aspect of knowledge work.
31
45
 
32
- There’s one more key benefit to this approach: LLMs are good at writing forms!
33
- Because the syntax is just Markdown with Jinja-style tags, agents can convert an
34
- informal Markdown doc describing a process to a precise Markform process easily.
46
+ Most agent frameworks focus on *prompts* and *flow* (the how) over the *structure* of
47
+ results (the what).
48
+ But for deep research or other multi-step workflows, you need precise
49
+ control over intermediate states and final output.
50
+ You don’t want that structure in a GUI (not token-friendly), in code (hard to update),
51
+ or dependent on model whims (changes unpredictably with model updates).
35
52
 
36
- (For more, see [the FAQ](#faq).)
53
+ Forms solve this. Forms codify operational excellence.
54
+ They’re easy to read, easy to edit, and enforce standards.
55
+ Because LLMs handle Markdown and Jinja-style tags well, agents can also help create and
56
+ improve the forms themselves—closing the meta-loop.
57
+
58
+ It’s time to bring bureaucracy to the agents!
59
+ See [the FAQ](#faq) for more on the design.
37
60
 
38
61
  ## Quick Start
39
62
 
40
63
  ```bash
41
- # Copy example forms to ./forms/ and run one interactively
64
+ # Copy example forms to ./forms/ and run one interactively.
42
65
  # Set OPENAI_API_KEY or ANTHROPIC_API_KEY (or put in .env) for research examples
43
- npx markform examples # Copy examples to ./forms/, then offers to run one
44
-
45
- # Or run forms directly
46
- npx markform run # Browse and run forms interactively
66
+ npx markform@latest examples
47
67
 
48
68
  # Read the docs (tell your agents to run these; they are agent-friendly!)
49
69
  npx markform # CLI help
@@ -52,7 +72,8 @@ npx markform docs # Quick reference for writing Markforms
52
72
  npx markform spec # Read the full spec
53
73
  ```
54
74
 
55
- The `markform examples` command copies sample forms locally and prompts you to run one.
75
+ The `markform examples` command copies some sample forms to `./forms` and prompts you to
76
+ fill in a form interactively and then optionally have an agent complete it.
56
77
  Pick `movie-research-demo.form.md` for a quick example.
57
78
 
58
79
  ## Installation
@@ -72,56 +93,94 @@ npm install markform
72
93
  ### Form Definition
73
94
 
74
95
  A `.form.md` file is simply a Markdoc file.
75
- It combines YAML frontmatter with Markdoc-tagged content:
96
+ It combines YAML frontmatter with Markdoc-tagged content.
76
97
 
77
- ```markdown
98
+ The text can be any Markdown.
99
+ The tags define things like fields:
100
+
101
+ ```jinja
102
+ {% field kind="string" id="movie" label="Movie" role="user"
103
+ required=true minLength=1 maxLength=300 %}{% /field %}
104
+
105
+ {% field kind="single_select" id="mpaa_rating" role="agent" label="MPAA Rating" %}
106
+ - [ ] G {% #g %}
107
+ - [ ] PG {% #pg %}
108
+ - [ ] PG-13 {% #pg_13 %}
109
+ - [ ] R {% #r %}
110
+ - [ ] NC-17 {% #nc_17 %}
111
+ - [ ] NR/Unrated {% #nr %}
112
+ {% /field %}
113
+ ```
114
+
115
+ Fields have types defined by the attributes.
116
+ Values are filled in incrementally, just like any form.
117
+ Once filled in, values appear directly inside the tags, in Markdown format:
118
+
119
+ ````jinja
120
+ {% field kind="string" id="movie" label="Movie" role="user"
121
+ required=true minLength=1 maxLength=300 %}
122
+ ```value
123
+ The Shawshank Redemption
124
+ ```
125
+ {% /field %}
126
+
127
+ {% field kind="single_select" id="mpaa_rating" role="agent" label="MPAA Rating" %}
128
+ - [ ] G {% #g %}
129
+ - [ ] PG {% #pg %}
130
+ - [ ] PG-13 {% #pg_13 %}
131
+ - [x] R {% #r %}
132
+ - [ ] NC-17 {% #nc_17 %}
133
+ - [ ] NR/Unrated {% #nr %}
134
+ {% /field %}
135
+ ````
136
+
137
+ Note fields can have a `role="user"` to indicate they are filled interactively by the
138
+ user, or a `role="agent"` to indicate an agent should fill them in.
139
+
140
+ There are also tags for user or agent instructions per field or at form level and
141
+ grouping of forms.
142
+
143
+ Checkboxes and tables as values are supported!
144
+ Checkboxes can be single-select or multi-select.
145
+
146
+ Here’s a full example:
147
+
148
+ <details>
149
+
150
+ <summary>Markform for Movie Research Demo (click to expand)</summary>
151
+
152
+ ```jinja
78
153
  ---
79
154
  markform:
80
155
  spec: MF/0.1
81
- title: Movie Research (Demo)
82
- description: Quick movie lookup with just the essentials (title, year, ratings, summary).
156
+ title: Movie Research Demo
157
+ description: Movie lookup with ratings from IMDB and Rotten Tomatoes.
158
+ run_mode: research
83
159
  roles:
84
160
  - user
85
161
  - agent
86
162
  role_instructions:
87
163
  user: "Enter the movie title."
88
164
  agent: |
89
- Quickly identify the movie and fill in basic info from IMDB.
90
- This is a demo lookup - just get the core facts.
165
+ Identify the movie with web searches and use imdb.com and rottentomatoes.com to fill in the ratings.
91
166
  ---
92
- {% form id="movie_research_demo" title="Movie Research (Demo)" %}
93
-
94
- ## Movie Research Example
167
+ {% form id="movie_research_demo" %}
168
+ {% group id="movie_input" %}
95
169
 
96
- {% group id="movie_input" title="Movie Identification" %}
97
-
98
- What movie do you want to research? \[*This field is filled in by the user (`role="user"`).*\]
170
+ ## What movie do you want to research?
99
171
 
100
172
  {% field kind="string" id="movie" label="Movie" role="user" required=true minLength=1 maxLength=300 %}{% /field %}
101
173
  {% instructions ref="movie" %}Enter the movie title (add year or details for disambiguation).{% /instructions %}
102
174
 
103
175
  {% /group %}
104
176
 
105
- ## About the Movie
106
-
107
177
  {% group id="about_the_movie" title="About the Movie" %}
108
178
 
109
- **Title:**
110
-
111
- {% field kind="string" id="full_title" label="Full Title" role="agent" required=true %}{% /field %}
112
- {% instructions ref="full_title" %}Official title, including subtitle if any.{% /instructions %}
113
-
114
- **Release year:**
115
-
116
- {% field kind="number" id="year" label="Release Year" role="agent" required=true min=1888 max=2030 %}{% /field %}
117
-
118
- **IMDB:**
179
+ ## Movie Ratings
119
180
 
120
- {% field kind="url" id="imdb_url" label="IMDB URL" role="agent" required=true %}{% /field %}
181
+ Here are the ratings for the movie:
121
182
 
122
- **MPAA rating:**
123
-
124
- {% field kind="single_select" id="mpaa_rating" label="MPAA Rating" role="agent" %}
183
+ {% field kind="single_select" id="mpaa_rating" role="agent" label="MPAA Rating" %}
125
184
  - [ ] G {% #g %}
126
185
  - [ ] PG {% #pg %}
127
186
  - [ ] PG-13 {% #pg_13 %}
@@ -130,56 +189,184 @@ What movie do you want to research? \[*This field is filled in by the user (`rol
130
189
  - [ ] NR/Unrated {% #nr %}
131
190
  {% /field %}
132
191
 
133
- **IMDB rating:**
134
-
135
- {% field kind="number" id="imdb_rating" label="IMDB Rating" role="agent" min=1.0 max=10.0 %}{% /field %}
136
- {% instructions ref="imdb_rating" %}IMDB user rating (1.0-10.0 scale).{% /instructions %}
137
-
138
- **Summary:**
192
+ {% field kind="table" id="ratings_table" role="agent"
193
+ label="Ratings" required=true
194
+ columnIds=["source", "score", "votes"] columnTypes=["string", "number", "number"]
195
+ minRows=0 maxRows=3 %}
196
+ | Source | Score | Votes |
197
+ |--------|-------|-------|
198
+ {% /field %}
139
199
 
140
- {% field kind="string" id="logline" label="One-Line Summary" role="agent" maxLength=300 %}{% /field %}
141
- {% instructions ref="logline" %}Brief plot summary in 1-2 sentences, no spoilers.{% /instructions %}
200
+ {% instructions ref="ratings_table" %}
201
+ Fill in scores and vote counts from each source:
202
+ - IMDB: Rating (1.0-10.0 scale), vote count
203
+ - RT Critics: Tomatometer (0-100%), review count
204
+ - RT Audience: Audience Score (0-100%), rating count
205
+ {% /instructions %}
142
206
 
143
207
  {% /group %}
144
-
145
208
  {% /form %}
146
209
  ```
147
210
 
211
+ </details>
212
+
148
213
  ### Form Report Output
149
214
 
150
215
  Run `npx markform examples` to copy examples, then `npx markform run` and select `Movie
151
- Research (Demo)` to fill it.
152
- The report output looks like:
216
+ Research Demo` to fill it.
153
217
 
154
- ```markdown
155
- # Movie Research (Demo)
218
+ A form can be exported
219
+
220
+ - as the filled form (Markform format, just like the input)
221
+
222
+ - as a report (plain Markdown)
156
223
 
157
- ## Movie Identification
224
+ - as values (YAML or JSON)
158
225
 
226
+ - as a JSON schema (just the structure)
227
+
228
+ The report output (using `gpt-5-mini` to fill it in) looks like:
229
+
230
+ ```markdown
159
231
  Movie:
160
- shawshank redemption
232
+ The Shawshank Redemption
161
233
 
162
234
  ## About the Movie
163
235
 
164
- Full Title:
236
+ MPAA Rating:
237
+ R
238
+
239
+ Ratings:
240
+ | Source | Score | Votes |
241
+ | --- | --- | --- |
242
+ | IMDB | 9.3 | 3100000 |
243
+ | RT Critics | 89 | 146 |
244
+ | RT Audience | 98 | 250000 |
245
+ ```
246
+
247
+ Here is the schema and YAML values for the form above.
248
+
249
+ <details> <summary>Filled Markform (click to expand)</summary>
250
+
251
+ ````jinja
252
+ {% form id="movie_research_demo" %}
253
+
254
+ {% group id="movie_input" %}
255
+
256
+ {% field kind="string" id="movie" role="user" label="Movie" maxLength=300 minLength=1 required=true %}
257
+ ```value
165
258
  The Shawshank Redemption
259
+ ```
260
+ {% /field %}
166
261
 
167
- Release Year:
168
- 1994
262
+ {% instructions ref="movie" %}
263
+ Enter the movie title (add year or details for disambiguation).
264
+ {% /instructions %}
169
265
 
170
- IMDB URL:
171
- https://www.imdb.com/title/tt0111161/
266
+ {% /group %}
172
267
 
173
- MPAA Rating:
174
- R
268
+ {% group id="about_the_movie" title="About the Movie" %}
175
269
 
176
- IMDB Rating:
177
- 9.3
270
+ {% field kind="single_select" id="mpaa_rating" label="MPAA Rating" %}
271
+ - [ ] G {% #g %}
272
+ - [ ] PG {% #pg %}
273
+ - [ ] PG-13 {% #pg_13 %}
274
+ - [x] R {% #r %}
275
+ - [ ] NC-17 {% #nc_17 %}
276
+ - [ ] NR/Unrated {% #nr %}
277
+ {% /field %}
178
278
 
179
- One-Line Summary:
180
- Convicted banker Andy Dufresne is sent to Shawshank State Penitentiary, where he forms an unexpected friendship with inmate Red while holding onto hope and striving to maintain his dignity in a corrupt prison system.
279
+ {% field kind="table" id="ratings_table"
280
+ columnIds=["source", "score", "votes"] columnLabels=["Source", "Score", "Votes"]
281
+ columnTypes=["string", "number", "number"]
282
+ label="Ratings" maxRows=3 minRows=0 required=true %}
283
+ | Source | Score | Votes |
284
+ | --- | --- | --- |
285
+ | IMDB | 9.3 | 3100000 |
286
+ | RT Critics | 89 | 146 |
287
+ | RT Audience | 98 | 250000 |
288
+ {% /field %}
289
+
290
+ {% instructions ref="ratings_table" %}
291
+ Fill in scores and vote counts from each source:
292
+ - IMDB: Rating (1.0-10.0 scale), vote count
293
+ - RT Critics: Tomatometer (0-100%), review count
294
+ - RT Audience: Audience Score (0-100%), rating count
295
+ {% /instructions %}
296
+
297
+ {% /group %}
298
+
299
+ {% /form %}
300
+ ````
301
+
302
+ </details>
303
+
304
+ <details> <summary>YAML Export (click to expand)</summary>
305
+
306
+ ```yaml
307
+ values:
308
+ movie:
309
+ state: answered
310
+ value: The Shawshank Redemption
311
+ mpaa_rating:
312
+ state: answered
313
+ value: r
314
+ ratings_table:
315
+ state: answered
316
+ value:
317
+ - source: IMDB
318
+ score: 9.3
319
+ votes: 3100000
320
+ - source: RT Critics
321
+ score: 89
322
+ votes: 146
323
+ - source: RT Audience
324
+ score: 98
325
+ votes: 250000
181
326
  ```
182
327
 
328
+ </details>
329
+
330
+ <details> <summary>JSON Schema (click to expand)</summary>
331
+
332
+ ```json
333
+ {
334
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
335
+ "$id": "movie_research_demo",
336
+ "type": "object",
337
+ "properties": {
338
+ "movie": {
339
+ "type": "string",
340
+ "title": "Movie",
341
+ "minLength": 1,
342
+ "maxLength": 300
343
+ },
344
+ "mpaa_rating": {
345
+ "type": "string",
346
+ "enum": ["g", "pg", "pg_13", "r", "nc_17", "nr"],
347
+ "title": "MPAA Rating"
348
+ },
349
+ "ratings_table": {
350
+ "type": "array",
351
+ "items": {
352
+ "type": "object",
353
+ "properties": {
354
+ "source": { "title": "Source", "type": "string" },
355
+ "score": { "title": "Score", "type": "number" },
356
+ "votes": { "title": "Votes", "type": "number" }
357
+ }
358
+ },
359
+ "title": "Ratings",
360
+ "minItems": 0,
361
+ "maxItems": 3
362
+ }
363
+ },
364
+ "required": ["movie", "ratings_table"]
365
+ }
366
+ ```
367
+
368
+ </details>
369
+
183
370
  ### More Example Forms
184
371
 
185
372
  The package includes example forms.
@@ -192,14 +379,103 @@ View them with `markform examples --list`, copy with `markform examples`, and ru
192
379
  - [`movie-research-demo.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-research-demo.form.md)
193
380
  \- The quick example above.
194
381
 
195
- - [`movie-research-basic.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-research-basic.form.md)
196
- \- Standard movie research with IMDB, Rotten Tomatoes, Metacritic.
382
+ - [`movie-deep-research.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-deep-research.form.md)
383
+ \- Comprehensive movie analysis with streaming, box office, technical specs.
384
+
385
+ ### Why a New Format?
386
+
387
+ The closest alternatives I’ve seen are:
388
+
389
+ - Plain Markdown docs can be used as templates and filled in by agents.
390
+ These are more expressive, but it is hard to edit them programmatically or use LLMs to
391
+ update them reliably.
392
+
393
+ - JSON + JSON Schema which are good for struture but terrible for additional
394
+ unstructured context like instructions Markdown.
395
+
396
+ - Agent to-do lists are part of many chat or coding interfaces and are programmatically
397
+ edited by agents. But these are limited to simple checklists, not forms with other
398
+ fields.
399
+
400
+ - Numerous tools like Typeform, Google Forms, PDF forms, and Docusign offer
401
+ human-friendly UI. But these do not have a human-friendly text format for use by
402
+ agents as well as humans.
403
+
404
+ | Approach | Usable GUI editor | Human-readable source format | Agent-editable | APIs and validation rules |
405
+ | --- | :---: | :---: | :---: | :---: |
406
+ | Plain Markdown | ✅<br>IDEs/editors | ✅ | ⚠️<br>fragile | ❌ |
407
+ | JSON + JSON Schema | ✅<br>IDEs/editors | ⚠️<br>no free text | ✅ | ✅ |
408
+ | SaaS tools (Typeform, Docusign, PDF forms) | ✅ | ⚠️<br>rarely | ⚠️<br>sometimes | ⚠️<br>sometimes |
409
+ | HTML/web Forms | ✅<br>IDEs/editors | ⚠️<br>HTML+code | ⚠️<br>coding agent | ✅ |
410
+ | Excel/Google Sheets | ✅<br>app | ❌<br>.csv/.xlsx | ⚠️<br>with tools | ✅<br>with some coding |
411
+ | **Markform** | ✅<br>IDEs/editors | ✅ | ✅<br>with this package | ✅<br>with this package |
412
+
413
+ ## Architecture
414
+
415
+ This repo has a specification and an implementation.
416
+ The implementation is a TypeScript API with Vercel AI SDK integration, and a CLI
417
+ interface.
197
418
 
198
- - [`movie-research-deep.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-research-deep.form.md)
199
- \- Comprehensive movie analysis with streaming, box office, analysis.
419
+ ```mermaid
420
+ flowchart LR
421
+ subgraph SPEC["<b>MARKFORM SPEC</b>"]
422
+ direction TB
200
423
 
201
- - [`earnings-analysis.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/earnings-analysis/earnings-analysis.form.md)
202
- \- Financial analysis form.
424
+ subgraph L1["<b>LAYER 1: SYNTAX</b><br/>Markdoc tag syntax and frontmatter (form, group, string-field, checkboxes, etc.)"]
425
+ end
426
+
427
+ subgraph L2["<b>LAYER 2: FORM DATA MODEL</b><br/>Schema definitions for forms, fields, values (in Zod but mappable to JSON Schema or Pydantic)"]
428
+ end
429
+
430
+ subgraph L3["<b>LAYER 3: VALIDATION & FORM FILLING</b><br/>Rules for filling forms via patches, field ids, required field semantics, validation hooks"]
431
+ end
432
+
433
+ subgraph L4["<b>LAYER 4: TOOL API & INTERFACES</b><br/>Abstract API for agents and humans (TypeScript and AI SDK integration)"]
434
+ end
435
+
436
+ L4 --> L3 --> L2 --> L1
437
+ end
438
+
439
+ subgraph IMPL["<b>THIS IMPLEMENTATION</b>"]
440
+ direction TB
441
+
442
+ subgraph CLI["<b>`markform` CLI</b><br/>Command-line interface to all features"]
443
+ end
444
+
445
+ subgraph AGENT["<b>AGENT TOOL INTERFACE</b><br/>Tool API library using AI SDK tools"]
446
+ end
447
+
448
+ subgraph HARNESS["<b>EXECUTION HARNESS</b><br/>Step-by-step form-filling agentic loop"]
449
+ end
450
+
451
+ subgraph ENGINE["<b>CORE TYPESCRIPT APIS</b><br/>Markdoc parser, serializer, patch application, validation (uses jiti for TypeScript rules)"]
452
+ end
453
+
454
+ subgraph TEST["<b>TESTING FRAMEWORK</b><br/>Golden session testing with .session.yaml transcripts"]
455
+ end
456
+
457
+ CLI --> ENGINE
458
+ CLI --> HARNESS
459
+ AGENT --> HARNESS
460
+ AGENT --> ENGINE
461
+ HARNESS --> ENGINE
462
+ ENGINE --> TEST
463
+ end
464
+
465
+ SPEC ~~~ IMPL
466
+
467
+ style SPEC fill:#e8f4f8,stroke:#0077b6
468
+ style L1 fill:#caf0f8,stroke:#0077b6
469
+ style L2 fill:#caf0f8,stroke:#0077b6
470
+ style L3 fill:#caf0f8,stroke:#0077b6
471
+ style L4 fill:#caf0f8,stroke:#0077b6
472
+ style IMPL fill:#fff3e6,stroke:#fb8500
473
+ style ENGINE fill:#ffe8cc,stroke:#fb8500
474
+ style CLI fill:#ffe8cc,stroke:#fb8500
475
+ style AGENT fill:#ffe8cc,stroke:#fb8500
476
+ style HARNESS fill:#ffe8cc,stroke:#fb8500
477
+ style TEST fill:#ffe8cc,stroke:#fb8500
478
+ ```
203
479
 
204
480
  ## CLI Commands
205
481
 
@@ -208,10 +484,8 @@ View them with `markform examples --list`, copy with `markform examples`, and ru
208
484
  ```bash
209
485
  # Copy all bundled examples to ./forms/
210
486
  markform examples
211
-
212
487
  # List available examples
213
488
  markform examples --list
214
-
215
489
  # Copy a specific example
216
490
  markform examples --name movie-research-demo
217
491
  ```
@@ -221,7 +495,6 @@ markform examples --name movie-research-demo
221
495
  ```bash
222
496
  # Browse forms in ./forms/ and run one interactively
223
497
  markform run
224
-
225
498
  # Run a specific form directly
226
499
  markform run forms/movie-research-demo.form.md
227
500
  ```
@@ -238,7 +511,6 @@ markform status my-form.form.md
238
511
  ```bash
239
512
  # View form structure, progress, and validation issues
240
513
  markform inspect my-form.form.md
241
-
242
514
  # Output as JSON
243
515
  markform inspect my-form.form.md --format=json
244
516
  ```
@@ -248,10 +520,8 @@ markform inspect my-form.form.md --format=json
248
520
  ```bash
249
521
  # Interactive mode: fill user-role fields via prompts
250
522
  markform fill my-form.form.md --interactive
251
-
252
523
  # Agent mode: use an LLM to fill agent-role fields
253
524
  markform fill my-form.form.md --model=anthropic/claude-sonnet-4-5
254
-
255
525
  # Mock agent for testing (uses pre-filled form as source)
256
526
  markform fill my-form.form.md --mock --mock-source filled.form.md
257
527
  ```
@@ -261,22 +531,30 @@ markform fill my-form.form.md --mock --mock-source filled.form.md
261
531
  ```bash
262
532
  # Export as readable markdown (strips Markdoc tags)
263
533
  markform export my-form.form.md --format=markdown
264
-
265
534
  # Export values as JSON
266
535
  markform export my-form.form.md --format=json
267
-
268
536
  # Export values as YAML
269
537
  markform export my-form.form.md --format=yaml
270
-
271
538
  # Dump just the current values
272
539
  markform dump my-form.form.md
273
540
  ```
274
541
 
542
+ ### Export JSON Schema
543
+
544
+ ```bash
545
+ # Export form structure as JSON Schema (for validation, code generation, etc.)
546
+ markform schema my-form.form.md
547
+ # Pure JSON Schema without Markform extensions
548
+ markform schema my-form.form.md --pure
549
+ # Specify JSON Schema draft version
550
+ markform schema my-form.form.md --draft draft-07
551
+ ```
552
+
275
553
  ### Apply Patches
276
554
 
277
555
  ```bash
278
556
  # Apply a JSON patch to update field values
279
- markform apply my-form.form.md --patch '[{"op":"set","fieldId":"name","value":"Alice"}]'
557
+ markform apply my-form.form.md --patch '[{"op":"set_string","fieldId":"name","value":"Alice"}]'
280
558
  ```
281
559
 
282
560
  ### Web Interface
@@ -291,105 +569,37 @@ markform serve my-form.form.md
291
569
  ```bash
292
570
  # Quick reference for writing forms (agent-friendly)
293
571
  markform docs
294
-
295
572
  # Full specification
296
573
  markform spec
297
-
298
574
  # TypeScript and AI SDK API documentation
299
575
  markform apis
300
-
301
576
  # This README
302
577
  markform readme
303
-
304
578
  # See supported AI providers and example models
305
579
  markform models
306
-
307
580
  # See all commands
308
581
  markform --help
309
582
  ```
310
583
 
311
- ## Supported Providers
312
-
313
- Standard LLMs can be used to fill in forms or create research reports from form
314
- templates. The package currently has support for these models built in, and enables web
315
- search tools for them if possible.
316
-
317
- | Provider | Env Variable | Example Models |
318
- | --- | --- | --- |
319
- | openai | `OPENAI_API_KEY` | gpt-5-mini, gpt-5.1, gpt-5.2 |
320
- | anthropic | `ANTHROPIC_API_KEY` | claude-sonnet-4-5, claude-opus-4-5 |
321
- | google | `GOOGLE_API_KEY` | gemini-2.5-pro, gemini-2.5-flash |
322
- | xai | `XAI_API_KEY` | grok-4, grok-4-fast |
323
- | deepseek | `DEEPSEEK_API_KEY` | deepseek-chat, deepseek-reasoner |
584
+ ## API Key Setup
324
585
 
325
586
  Set the appropriate environment variable for your provider before running `markform
326
- fill`. See
327
- [`src/settings.ts`](https://github.com/jlevy/markform/blob/main/packages/markform/src/settings.ts)
328
- for the full list of models.
329
-
330
- ## Architecture
331
-
332
- ```mermaid
333
- flowchart LR
334
- subgraph SPEC["<b>MARKFORM SPEC</b>"]
335
- direction TB
336
-
337
- subgraph L1["<b>LAYER 1: SYNTAX</b><br/>Markdoc tag syntax and frontmatter (form, group, string-field, checkboxes, etc.)"]
338
- end
339
-
340
- subgraph L2["<b>LAYER 2: FORM DATA MODEL</b><br/>Schema definitions for forms, fields, values (in Zod but mappable to JSON Schema or Pydantic)"]
341
- end
342
-
343
- subgraph L3["<b>LAYER 3: VALIDATION & FORM FILLING</b><br/>Rules for filling forms via patches, field ids, required field semantics, validation hooks"]
344
- end
345
-
346
- subgraph L4["<b>LAYER 4: TOOL API & INTERFACES</b><br/>Abstract API for agents and humans (TypeScript and AI SDK integration)"]
347
- end
348
-
349
- L4 -->
350
-
351
- L3 --> L2 --> L1
352
- end
353
-
354
- subgraph IMPL["<b>THIS IMPLEMENTATION</b>"]
355
- direction TB
356
-
357
- subgraph ENGINE["<b>ENGINE IMPLEMENTATION</b><br/>Markdoc parser, serializer, patch application, validation (uses jiti for TypeScript rules)"]
358
- end
359
-
360
- subgraph UI["<b>USER INTERFACES</b><br/>CLI commands, web UI (serve), render to HTML"]
361
- end
362
-
363
- subgraph AGENT["<b>AGENT INTERFACES</b><br/>Tool API library, MCP server, AI SDK tools"]
364
- end
365
-
366
- subgraph HARNESS["<b>EXECUTION HARNESS</b><br/>Step-by-step form-filling agentic loop"]
367
- end
587
+ fill`:
368
588
 
369
- subgraph TEST["<b>TESTING FRAMEWORK</b><br/>Golden session testing with .session.yaml transcripts"]
370
- end
589
+ | Provider | Env Variable | Native Web Search |
590
+ | --- | --- | :---: |
591
+ | openai | `OPENAI_API_KEY` | ✓ |
592
+ | anthropic | `ANTHROPIC_API_KEY` | ✓ |
593
+ | google | `GOOGLE_API_KEY` | ✓ |
594
+ | xai | `XAI_API_KEY` | ✓ |
595
+ | deepseek | `DEEPSEEK_API_KEY` | ✗ |
371
596
 
372
- UI --> ENGINE
373
- AGENT --> HARNESS
374
- AGENT --> ENGINE
375
- HARNESS --> ENGINE
376
- ENGINE --> TEST
377
- end
597
+ Run `markform models` to see available models.
598
+ See
599
+ [`src/settings.ts`](https://github.com/jlevy/markform/blob/main/packages/markform/src/settings.ts)
600
+ for defaults.
378
601
 
379
- SPEC ~~~ IMPL
380
-
381
- style SPEC fill:#e8f4f8,stroke:#0077b6
382
- style L1 fill:#caf0f8,stroke:#0077b6
383
- style L2 fill:#caf0f8,stroke:#0077b6
384
- style L3 fill:#caf0f8,stroke:#0077b6
385
- style L4 fill:#caf0f8,stroke:#0077b6
386
- style IMPL fill:#fff3e6,stroke:#fb8500
387
- style ENGINE fill:#ffe8cc,stroke:#fb8500
388
- style UI fill:#ffe8cc,stroke:#fb8500
389
- style AGENT fill:#ffe8cc,stroke:#fb8500
390
- style HARNESS fill:#ffe8cc,stroke:#fb8500
391
- style TEST fill:#ffe8cc,stroke:#fb8500
392
- ```
602
+ If unsure, try `gpt-5-mini` first as it’s fast and supports web search.
393
603
 
394
604
  ## Programmatic Usage
395
605
 
@@ -399,17 +609,17 @@ applications.
399
609
  ### Basic Parsing
400
610
 
401
611
  ```typescript
402
- import { parseForm, serializeForm } from "markform";
612
+ import { parseForm, serialize } from "markform";
403
613
 
404
614
  // Parse a .form.md file
405
615
  const form = parseForm(markdownContent);
406
616
 
407
617
  // Access schema and values
408
618
  console.log(form.schema.title);
409
- console.log(form.values);
619
+ console.log(form.responsesByFieldId);
410
620
 
411
621
  // Serialize back to markdown
412
- const output = serializeForm(form);
622
+ const output = serialize(form);
413
623
  ```
414
624
 
415
625
  ### AI SDK Integration
@@ -422,10 +632,14 @@ import { createMarkformTools, MarkformSessionStore } from "markform/ai-sdk";
422
632
  import { generateText } from "ai";
423
633
  import { anthropic } from "@ai-sdk/anthropic";
424
634
 
635
+ // Parse form and create session store (tracks state across tool calls)
425
636
  const form = parseForm(markdownContent);
426
637
  const store = new MarkformSessionStore(form);
638
+
639
+ // Create tools the agent can call: inspect, apply patches, export
427
640
  const tools = createMarkformTools({ sessionStore: store });
428
641
 
642
+ // Agent fills the form via tool calls until complete or maxSteps reached
429
643
  const result = await generateText({
430
644
  model: anthropic("claude-sonnet-4-5-20250929"),
431
645
  prompt: "Fill out this form with appropriate values...",
@@ -443,11 +657,32 @@ const result = await generateText({
443
657
  | `markform_export` | Export schema and values as JSON |
444
658
  | `markform_get_markdown` | Get canonical Markdown representation |
445
659
 
660
+ ## Other Documentation
661
+
662
+ - **[Quick
663
+ Reference](https://github.com/jlevy/markform/blob/main/docs/markform-reference.md)**
664
+ (or run `markform docs`) — Concise syntax reference (agent-friendly)
665
+
666
+ - **[Markform Spec](https://github.com/jlevy/markform/blob/main/docs/markform-spec.md)**
667
+ (or run `markform spec`) — Complete syntax and semantics
668
+
669
+ - **[API
670
+ Documentation](https://github.com/jlevy/markform/blob/main/docs/markform-apis.md)**
671
+ (or run `markform apis`) — TypeScript and AI SDK APIs
672
+
673
+ - **[Design
674
+ Doc](https://github.com/jlevy/markform/blob/main/docs/project/architecture/current/arch-markform-design.md)**
675
+ — Technical design and roadmap
676
+
677
+ - **[Development](https://github.com/jlevy/markform/blob/main/docs/development.md)** —
678
+ Build, test, and contribute
679
+
446
680
  ## FAQ
447
681
 
448
- ### Can you say more why this is a good idea?
682
+ ### Can you talk more about why forms are cool?
449
683
 
450
- Yes! I’ve come to believe forms are a missing piece of the workflow problem with agents.
684
+ As a matter of fact, I can!
685
+ I’ve come to believe forms are a missing piece of the workflow problem with agents.
451
686
  For deep research or complex multi-step workflows, key pieces need to be *precisely
452
687
  controlled*, *domain-specific*, and *always improving*. You need precise documentation
453
688
  on the key intermediate states and final output from an AI pipeline.
@@ -458,6 +693,27 @@ Forms define these pieces and are easy to edit.
458
693
  All other choices can be left to the agents themselves, with the structure and
459
694
  validations enforced by the form-filling tools the agents use.
460
695
 
696
+ Another cool thing about forms: they get rid of the inefficiencies of conversation chat
697
+ history.
698
+ Often when an agentic loop is built, it just saves the chat history for context.
699
+ But a form is inherently more efficient: the harness itself can be stateless.
700
+ It just shares the partly-filled form with the agent, and it has full context in one
701
+ message. That’s what the agentic loop in this implementation does.
702
+
703
+ Finally, the meta-loop of *creating and improving* forms is easier to automate:
704
+
705
+ - To get started, you can ask a good coding model to convert any unstructured doc
706
+ describing a process to a form.
707
+ The model can also use the CLI or tools to validate and test it.
708
+
709
+ - Any time you have a workflow problem, you can ask an LLM to diagnose it and if
710
+ possible, go back and fix up the form with additional instructions or fields or checks
711
+ that would prevent the problem from happening again.
712
+
713
+ I suspect dynamic form structures like this could make complex deep research more
714
+ powerful. Just as you plan a spec before implementing with a coding agent, you could use
715
+ Markform to encode a research plan before dispatching agents to fill it.
716
+
461
717
  ### Is this mature?
462
718
 
463
719
  No! I just wrote it.
@@ -478,11 +734,11 @@ See
478
734
  for more thoughts on how this works.
479
735
  And see [the complete history of
480
736
  specs](https://github.com/jlevy/markform/tree/main/docs/project/specs/done) for examples
481
- of how everything is done with specs and with
737
+ of how everything is done with specs.
482
738
 
483
- Although I didn’t have to write much there was a *lot* of management and review by me
484
- and a lot of thought and iteration for all design decisions.
485
- And this doc is written by me.
739
+ Although I didn’t write much code, there was a *lot* of management, review, and
740
+ iteration on design decisions.
741
+ And yes, this README is written by me.
486
742
  :)
487
743
 
488
744
  ### What are the goals of Markform?
@@ -522,31 +778,6 @@ This enables powerful AI workflows that assemble information in a defined struct
522
778
  - An **agent execution harness** for step-by-step form filling, enabling deep research
523
779
  agents that assemble validated output in a structured format.
524
780
 
525
- ### Does anything like this already exist?
526
-
527
- Not that I have seen.
528
- The closest alternatives are:
529
-
530
- - Plain Markdown docs can be used as templates and filled in by agents.
531
- These are more expressive, but it is hard to edit them programmatically or use LLMs to
532
- update them reliably.
533
-
534
- - Agent to-do lists are part of many chat or coding interfaces and are programmatically
535
- edited by agents. But these are limited to simple checklists, not forms with other
536
- fields.
537
-
538
- - Numerous tools like Typeform, Google Forms, PDF forms, and Docusign offer
539
- human-friendly UI. But these do not have a human-friendly text format for use by
540
- agents as well as humans.
541
-
542
- | Approach | Has GUI | Human-readable source format | Agent-editable | APIs and validation rules |
543
- | --- | :---: | :---: | :---: | :---: |
544
- | Plain Markdown | ☑️ existing tools | ✅ | ⚠️ fragile | ❌ |
545
- | JSON with schema | ⚠️ in some apps | ⚠️ um it’s JSON | ✅ | ✅ |
546
- | SaaS tools (Typeform, Docusign, PDF forms) | ✅ | ⚠️ rarely | ⚠️ if they add it | ⚠️ if they add it |
547
- | Excel/Google Sheets | ✅ | ❌ .csv (poor) or .xlsx (worse) | ⚠️ with tools | ✅ with some coding |
548
- | **Markform** | ☑️ existing tools | ✅ | ✅ with this package | ✅ with this package |
549
-
550
781
  ### What are example use cases?
551
782
 
552
783
  - Deep research tools where agents need to follow codified processes to assemble
@@ -587,26 +818,6 @@ settings:
587
818
 
588
819
  Or see [markdoc/language-server](https://github.com/markdoc/language-server).
589
820
 
590
- ## Documentation
591
-
592
- - **[Quick
593
- Reference](https://github.com/jlevy/markform/blob/main/docs/markform-reference.md)**
594
- (or run `markform docs`) - Concise syntax reference (agent-friendly)
595
-
596
- - **[Markform Spec](https://github.com/jlevy/markform/blob/main/docs/markform-spec.md)**
597
- (or run `markform spec`) - Complete syntax and semantics
598
-
599
- - **[API
600
- Documentation](https://github.com/jlevy/markform/blob/main/docs/markform-apis.md)**
601
- (or run `markform apis`) - TypeScript and AI SDK APIs
602
-
603
- - **[Design
604
- Doc](https://github.com/jlevy/markform/blob/main/docs/project/architecture/current/arch-markform-design.md)**
605
- \- Technical design and roadmap
606
-
607
- - **[Development](https://github.com/jlevy/markform/blob/main/docs/development.md)** -
608
- Build, test, and contribute
609
-
610
821
  ## License
611
822
 
612
823
  AGPL-3.0-or-later. [Contact me](https://github.com/jlevy) for additional licensing