markform 0.1.7 → 0.1.8

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 (33) hide show
  1. package/README.md +420 -198
  2. package/dist/ai-sdk.d.mts +1 -1
  3. package/dist/ai-sdk.mjs +2 -2
  4. package/dist/{apply-g23rRn7p.mjs → apply-BUU2QcJ2.mjs} +102 -20
  5. package/dist/bin.mjs +1 -1
  6. package/dist/{cli-Bqlm-WWw.mjs → cli-BZh25bvy.mjs} +26 -8
  7. package/dist/cli.mjs +1 -1
  8. package/dist/{coreTypes-DCvD7feM.d.mts → coreTypes-BSPJ9H27.d.mts} +51 -1
  9. package/dist/{coreTypes-__Cwxz5q.mjs → coreTypes-DJtu8OOp.mjs} +16 -1
  10. package/dist/index.d.mts +10 -5
  11. package/dist/index.mjs +4 -4
  12. package/dist/{session-CgCNni0e.mjs → session-CmHdAPyg.mjs} +1 -1
  13. package/dist/{session-DruaYPZ1.mjs → session-DSTNiHza.mjs} +1 -1
  14. package/dist/{src-BiuxbzF3.mjs → src-kUggXhN1.mjs} +115 -29
  15. package/docs/markform-apis.md +30 -1
  16. package/docs/markform-reference.md +65 -6
  17. package/examples/movie-research/movie-research-deep.form.md +15 -56
  18. package/examples/movie-research/movie-research-demo.form.md +25 -34
  19. package/examples/rejection-test/rejection-test-mock-filled.form.md +41 -0
  20. package/examples/rejection-test/rejection-test-mock-filled.report.md +15 -0
  21. package/examples/rejection-test/rejection-test-mock-filled.schema.json +59 -0
  22. package/examples/rejection-test/rejection-test-mock-filled.yml +13 -0
  23. package/examples/rejection-test/rejection-test.form.md +35 -0
  24. package/examples/rejection-test/rejection-test.session.yaml +88 -0
  25. package/examples/simple/simple-mock-filled.report.md +96 -0
  26. package/examples/simple/simple-mock-filled.schema.json +374 -0
  27. package/examples/simple/simple-mock-filled.yml +87 -0
  28. package/examples/simple/simple-skipped-filled.report.md +90 -0
  29. package/examples/simple/simple-skipped-filled.schema.json +374 -0
  30. package/examples/simple/simple-skipped-filled.yml +77 -0
  31. package/examples/simple/simple-with-skips.session.yaml +3 -3
  32. package/examples/simple/simple.session.yaml +3 -3
  33. package/package.json +1 -1
package/README.md CHANGED
@@ -1,49 +1,66 @@
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
+ **Markform** is a text format for defining structured forms that humans can read,
4
+ machines can parse, and agents can fill via tool calls.
5
+
6
+ Define instructions, fields, and validation rules in a single `.form.md` file.
7
+ Agents fill forms incrementally via patches.
8
+ Fields are validated, so errors are caught early and can be corrected.
9
+ Humans can review or intervene at any point.
10
+
11
+ **Why forms?** For deep research or complex AI tasks, you need more than just prompts or
12
+ flow: you need *structure*, which is precise control over agent output at every stage of
13
+ a workflow. A well-designed form combines instructions, structured data, and validations
14
+ in one place.
15
+
16
+ **How it works:**
14
17
 
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.
18
+ - A Markform document exposes a programmatic interface: users fill fields via CLI or web
19
+ UI, agents fill via tool calls ([Vercel AI SDK](https://github.com/vercel/ai)
20
+ integration included).
21
+
22
+ - Changes are explicit patch operations (`{ "op": "set_string", "fieldId": "name",
23
+ "value": "Alice" }`) validated against a schema specified in the form.
24
+ The agent sees validation errors and can self-correct.
25
+
26
+ - The format extends Markdown with a
27
+ [precise specification](https://github.com/jlevy/markform/blob/main/docs/markform-spec.md).
28
+ Export Markform syntax to JSON, YAML, JSON Schema, or plain Markdown reports.
29
+
30
+ Markform syntax is a good source format: token-efficient text you can read, diff, and
31
+ version control.
32
+ It is built with [Markdoc](https://github.com/markdoc/markdoc), which is
33
+ a Markdown extension from Stripe with Jinja-style `{% tag %}` annotations.
17
34
 
18
- ## Why?
35
+ ## Why Do Agents Need Forms?
19
36
 
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).
37
+ For centuries, humans have used paper forms and
38
+ [checklists](https://en.wikipedia.org/wiki/The_Checklist_Manifesto) to systematize
39
+ complex processes. A form with instructions, field definitions, and validations is a
40
+ concise way to share context: goals, background knowledge, process rules, and state
41
+ (memory). I don’t think AI changes this essential aspect of knowledge work.
25
42
 
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
- Its time to bring bureaucracy to the agents.
43
+ Most agent frameworks focus on *prompts* and *flow* (the how) over the *structure* of
44
+ results (the what).
45
+ But for deep research or other multi-step workflows, you need precise
46
+ control over intermediate states and final output.
47
+ You dont want that structure in a GUI (not token-friendly), in code (hard to update),
48
+ or dependent on model whims (changes unpredictably with model updates).
31
49
 
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.
50
+ Forms solve this. Forms codify operational excellence.
51
+ They’re easy to read, easy to edit, and enforce standards.
52
+ Because LLMs handle Markdown and Jinja-style tags well, agents can also help create and
53
+ improve the forms themselves—closing the meta-loop.
35
54
 
36
- (For more, see [the FAQ](#faq).)
55
+ It’s time to bring bureaucracy to the agents!
56
+ See [the FAQ](#faq) for more on the design.
37
57
 
38
58
  ## Quick Start
39
59
 
40
60
  ```bash
41
- # Copy example forms to ./forms/ and run one interactively
61
+ # Copy example forms to ./forms/ and run one interactively.
42
62
  # 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
63
+ npx markform@latest examples
47
64
 
48
65
  # Read the docs (tell your agents to run these; they are agent-friendly!)
49
66
  npx markform # CLI help
@@ -52,7 +69,8 @@ npx markform docs # Quick reference for writing Markforms
52
69
  npx markform spec # Read the full spec
53
70
  ```
54
71
 
55
- The `markform examples` command copies sample forms locally and prompts you to run one.
72
+ The `markform examples` command copies some sample forms to `./forms` and prompts you to
73
+ fill in a form interactively and then optionally have an agent complete it.
56
74
  Pick `movie-research-demo.form.md` for a quick example.
57
75
 
58
76
  ## Installation
@@ -72,56 +90,92 @@ npm install markform
72
90
  ### Form Definition
73
91
 
74
92
  A `.form.md` file is simply a Markdoc file.
75
- It combines YAML frontmatter with Markdoc-tagged content:
93
+ It combines YAML frontmatter with Markdoc-tagged content.
76
94
 
77
- ```markdown
95
+ The text can be any Markdown.
96
+ The tags define things like fields:
97
+
98
+ ```jinja
99
+ {% field kind="string" id="movie" label="Movie" role="user"
100
+ required=true minLength=1 maxLength=300 %}{% /field %}
101
+
102
+ {% field kind="single_select" id="mpaa_rating" role="agent" label="MPAA Rating" %}
103
+ - [ ] G {% #g %}
104
+ - [ ] PG {% #pg %}
105
+ - [ ] PG-13 {% #pg_13 %}
106
+ - [ ] R {% #r %}
107
+ - [ ] NC-17 {% #nc_17 %}
108
+ - [ ] NR/Unrated {% #nr %}
109
+ {% /field %}
110
+ ```
111
+
112
+ Fields have types defined by the attributes.
113
+ Values are filled in incrementally, just like any form.
114
+ Once filled in, values appear directly inside the tags, in Markdown format:
115
+
116
+ ```jinja
117
+ {% field kind="string" id="movie" label="Movie" role="user"
118
+ required=true minLength=1 maxLength=300 %}
119
+ The Shawshank Redemption
120
+ {% /field %}
121
+
122
+ {% field kind="single_select" id="mpaa_rating" role="agent" label="MPAA Rating" %}
123
+ - [ ] G {% #g %}
124
+ - [ ] PG {% #pg %}
125
+ - [ ] PG-13 {% #pg_13 %}
126
+ - [X] R {% #r %}
127
+ - [ ] NC-17 {% #nc_17 %}
128
+ - [ ] NR/Unrated {% #nr %}
129
+ {% /field %}
130
+ ```
131
+
132
+ Note fields can have a `role="user"` to indicate they are filled interactively by the
133
+ user, or a `role="agent"` to indicate an agent should fill them in.
134
+
135
+ There are also tags for user or agent instructions per field or at form level and
136
+ grouping of forms.
137
+
138
+ Checkboxes and tables as values are supported!
139
+ Checkboxes can be single-select or multi-select.
140
+
141
+ Here’s a full example:
142
+
143
+ <details>
144
+
145
+ <summary>Markform for Movie Research Demo (click to expand)</summary>
146
+
147
+ ```jinja
78
148
  ---
79
149
  markform:
80
150
  spec: MF/0.1
81
- title: Movie Research (Demo)
82
- description: Quick movie lookup with just the essentials (title, year, ratings, summary).
151
+ title: Movie Research Demo
152
+ description: Movie lookup with ratings from IMDB and Rotten Tomatoes.
153
+ run_mode: research
83
154
  roles:
84
155
  - user
85
156
  - agent
86
157
  role_instructions:
87
158
  user: "Enter the movie title."
88
159
  agent: |
89
- Quickly identify the movie and fill in basic info from IMDB.
90
- This is a demo lookup - just get the core facts.
160
+ Identify the movie with web searches and use imdb.com and rottentomatoes.com to fill in the ratings.
91
161
  ---
92
- {% form id="movie_research_demo" title="Movie Research (Demo)" %}
93
-
94
- ## Movie Research Example
162
+ {% form id="movie_research_demo" %}
163
+ {% group id="movie_input" %}
95
164
 
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"`).*\]
165
+ ## What movie do you want to research?
99
166
 
100
167
  {% field kind="string" id="movie" label="Movie" role="user" required=true minLength=1 maxLength=300 %}{% /field %}
101
168
  {% instructions ref="movie" %}Enter the movie title (add year or details for disambiguation).{% /instructions %}
102
169
 
103
170
  {% /group %}
104
171
 
105
- ## About the Movie
106
-
107
172
  {% group id="about_the_movie" title="About the Movie" %}
108
173
 
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:**
119
-
120
- {% field kind="url" id="imdb_url" label="IMDB URL" role="agent" required=true %}{% /field %}
174
+ ## Movie Ratings
121
175
 
122
- **MPAA rating:**
176
+ Here are the ratings for the movie:
123
177
 
124
- {% field kind="single_select" id="mpaa_rating" label="MPAA Rating" role="agent" %}
178
+ {% field kind="single_select" id="mpaa_rating" role="agent" label="MPAA Rating" %}
125
179
  - [ ] G {% #g %}
126
180
  - [ ] PG {% #pg %}
127
181
  - [ ] PG-13 {% #pg_13 %}
@@ -130,56 +184,181 @@ What movie do you want to research? \[*This field is filled in by the user (`rol
130
184
  - [ ] NR/Unrated {% #nr %}
131
185
  {% /field %}
132
186
 
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:**
187
+ {% field kind="table" id="ratings_table" role="agent"
188
+ label="Ratings" required=true
189
+ columnIds=["source", "score", "votes"] columnTypes=["string", "number", "number"]
190
+ minRows=0 maxRows=3 %}
191
+ | Source | Score | Votes |
192
+ |--------|-------|-------|
193
+ {% /field %}
139
194
 
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 %}
195
+ {% instructions ref="ratings_table" %}
196
+ Fill in scores and vote counts from each source:
197
+ - IMDB: Rating (1.0-10.0 scale), vote count
198
+ - RT Critics: Tomatometer (0-100%), review count
199
+ - RT Audience: Audience Score (0-100%), rating count
200
+ {% /instructions %}
142
201
 
143
202
  {% /group %}
144
-
145
203
  {% /form %}
146
204
  ```
147
205
 
206
+ </details>
207
+
148
208
  ### Form Report Output
149
209
 
150
210
  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:
211
+ Research Demo` to fill it.
153
212
 
154
- ```markdown
155
- # Movie Research (Demo)
213
+ A form can be exported
214
+
215
+ - as the filled form (Markform format, just like the input)
216
+
217
+ - as a report (plain Markdown)
156
218
 
157
- ## Movie Identification
219
+ - as values (YAML or JSON)
158
220
 
221
+ - as a JSON schema (just the structure)
222
+
223
+ The report output (using `gpt-5-mini` to fill it in) looks like:
224
+
225
+ ```markdown
159
226
  Movie:
160
- shawshank redemption
227
+ The Shawshank Redemption
161
228
 
162
229
  ## About the Movie
163
230
 
164
- Full Title:
231
+ MPAA Rating:
232
+ R
233
+
234
+ Ratings:
235
+ | Source | Score | Votes |
236
+ | --- | --- | --- |
237
+ | IMDB | 9.3 | 3100000 |
238
+ | RT Critics | 89 | 146 |
239
+ | RT Audience | 98 | 250000 |
240
+ ```
241
+
242
+ Here is the schema and YAML values for the form above.
243
+
244
+ <details> <summary>Filled Markform (click to expand)</summary>
245
+
246
+ ````jinja
247
+ {% form id="movie_research_demo" %}
248
+
249
+ {% group id="movie_input" %}
250
+
251
+ {% field kind="string" id="movie" role="user" label="Movie" maxLength=300 minLength=1 required=true %}
252
+ ```value
165
253
  The Shawshank Redemption
254
+ ```
255
+ {% /field %}
166
256
 
167
- Release Year:
168
- 1994
257
+ {% instructions ref="movie" %}
258
+ Enter the movie title (add year or details for disambiguation).
259
+ {% /instructions %}
169
260
 
170
- IMDB URL:
171
- https://www.imdb.com/title/tt0111161/
261
+ {% /group %}
172
262
 
173
- MPAA Rating:
174
- R
263
+ {% group id="about_the_movie" title="About the Movie" %}
264
+
265
+ {% field kind="single_select" id="mpaa_rating" label="MPAA Rating" %}
266
+ - [ ] G {% #g %}
267
+ - [ ] PG {% #pg %}
268
+ - [ ] PG-13 {% #pg_13 %}
269
+ - [x] R {% #r %}
270
+ - [ ] NC-17 {% #nc_17 %}
271
+ - [ ] NR/Unrated {% #nr %}
272
+ {% /field %}
273
+
274
+ {% field kind="table" id="ratings_table"
275
+ columnIds=["source", "score", "votes"] columnLabels=["Source", "Score", "Votes"]
276
+ columnTypes=["string", "number", "number"]
277
+ label="Ratings" maxRows=3 minRows=0 required=true %}
278
+ | Source | Score | Votes |
279
+ | --- | --- | --- |
280
+ | IMDB | 9.3 | 3100000 |
281
+ | RT Critics | 89 | 146 |
282
+ | RT Audience | 98 | 250000 |
283
+ {% /field %}
284
+
285
+ {% instructions ref="ratings_table" %}
286
+ Fill in scores and vote counts from each source:IMDB: Rating (1.0-10.0 scale), vote countRT Critics: Tomatometer (0-100%), review countRT Audience: Audience Score (0-100%), rating count
287
+ {% /instructions %}
175
288
 
176
- IMDB Rating:
177
- 9.3
289
+ {% /group %}
290
+
291
+ {% /form %}
292
+ ````
293
+
294
+ </details>
295
+
296
+ <details> <summary>YAML Export (click to expand)</summary>
297
+
298
+ ```yaml
299
+ values:
300
+ movie:
301
+ state: answered
302
+ value: The Shawshank Redemption
303
+ mpaa_rating:
304
+ state: answered
305
+ value: r
306
+ ratings_table:
307
+ state: answered
308
+ value:
309
+ - source: IMDB
310
+ score: 9.3
311
+ votes: 3100000
312
+ - source: RT Critics
313
+ score: 89
314
+ votes: 146
315
+ - source: RT Audience
316
+ score: 98
317
+ votes: 250000
318
+ ```
178
319
 
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.
320
+ </details>
321
+
322
+ <details> <summary>JSON Schema (click to expand)</summary>
323
+
324
+ ```json
325
+ {
326
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
327
+ "$id": "movie_research_demo",
328
+ "type": "object",
329
+ "properties": {
330
+ "movie": {
331
+ "type": "string",
332
+ "title": "Movie",
333
+ "minLength": 1,
334
+ "maxLength": 300
335
+ },
336
+ "mpaa_rating": {
337
+ "type": "string",
338
+ "enum": ["g", "pg", "pg_13", "r", "nc_17", "nr"],
339
+ "title": "MPAA Rating"
340
+ },
341
+ "ratings_table": {
342
+ "type": "array",
343
+ "items": {
344
+ "type": "object",
345
+ "properties": {
346
+ "source": { "title": "Source", "type": "string" },
347
+ "score": { "title": "Score", "type": "number" },
348
+ "votes": { "title": "Votes", "type": "number" }
349
+ }
350
+ },
351
+ "title": "Ratings",
352
+ "minItems": 0,
353
+ "maxItems": 3
354
+ }
355
+ },
356
+ "required": ["movie", "ratings_table"]
357
+ }
181
358
  ```
182
359
 
360
+ </details>
361
+
183
362
  ### More Example Forms
184
363
 
185
364
  The package includes example forms.
@@ -200,6 +379,72 @@ View them with `markform examples --list`, copy with `markform examples`, and ru
200
379
 
201
380
  - [`earnings-analysis.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/earnings-analysis/earnings-analysis.form.md)
202
381
  \- Financial analysis form.
382
+ ## Architecture
383
+
384
+ This repo has a specification and an implementation.
385
+ The implementation is a TypeScript API with Vercel AI SDK integration, and a CLI
386
+ interface.
387
+
388
+ ```mermaid
389
+ flowchart LR
390
+ subgraph SPEC["<b>MARKFORM SPEC</b>"]
391
+ direction TB
392
+
393
+ subgraph L1["<b>LAYER 1: SYNTAX</b><br/>Markdoc tag syntax and frontmatter (form, group, string-field, checkboxes, etc.)"]
394
+ end
395
+
396
+ 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)"]
397
+ end
398
+
399
+ subgraph L3["<b>LAYER 3: VALIDATION & FORM FILLING</b><br/>Rules for filling forms via patches, field ids, required field semantics, validation hooks"]
400
+ end
401
+
402
+ subgraph L4["<b>LAYER 4: TOOL API & INTERFACES</b><br/>Abstract API for agents and humans (TypeScript and AI SDK integration)"]
403
+ end
404
+
405
+ L4 --> L3 --> L2 --> L1
406
+ end
407
+
408
+ subgraph IMPL["<b>THIS IMPLEMENTATION</b>"]
409
+ direction TB
410
+
411
+ subgraph CLI["<b>`markform` CLI</b><br/>Command-line interface to all features"]
412
+ end
413
+
414
+ subgraph AGENT["<b>AGENT TOOL INTERFACE</b><br/>Tool API library using AI SDK tools"]
415
+ end
416
+
417
+ subgraph HARNESS["<b>EXECUTION HARNESS</b><br/>Step-by-step form-filling agentic loop"]
418
+ end
419
+
420
+ subgraph ENGINE["<b>CORE TYPESCRIPT APIS</b><br/>Markdoc parser, serializer, patch application, validation (uses jiti for TypeScript rules)"]
421
+ end
422
+
423
+ subgraph TEST["<b>TESTING FRAMEWORK</b><br/>Golden session testing with .session.yaml transcripts"]
424
+ end
425
+
426
+ CLI --> ENGINE
427
+ CLI --> HARNESS
428
+ AGENT --> HARNESS
429
+ AGENT --> ENGINE
430
+ HARNESS --> ENGINE
431
+ ENGINE --> TEST
432
+ end
433
+
434
+ SPEC ~~~ IMPL
435
+
436
+ style SPEC fill:#e8f4f8,stroke:#0077b6
437
+ style L1 fill:#caf0f8,stroke:#0077b6
438
+ style L2 fill:#caf0f8,stroke:#0077b6
439
+ style L3 fill:#caf0f8,stroke:#0077b6
440
+ style L4 fill:#caf0f8,stroke:#0077b6
441
+ style IMPL fill:#fff3e6,stroke:#fb8500
442
+ style ENGINE fill:#ffe8cc,stroke:#fb8500
443
+ style CLI fill:#ffe8cc,stroke:#fb8500
444
+ style AGENT fill:#ffe8cc,stroke:#fb8500
445
+ style HARNESS fill:#ffe8cc,stroke:#fb8500
446
+ style TEST fill:#ffe8cc,stroke:#fb8500
447
+ ```
203
448
 
204
449
  ## CLI Commands
205
450
 
@@ -272,11 +517,24 @@ markform export my-form.form.md --format=yaml
272
517
  markform dump my-form.form.md
273
518
  ```
274
519
 
520
+ ### Export JSON Schema
521
+
522
+ ```bash
523
+ # Export form structure as JSON Schema (for validation, code generation, etc.)
524
+ markform schema my-form.form.md
525
+
526
+ # Pure JSON Schema without Markform extensions
527
+ markform schema my-form.form.md --pure
528
+
529
+ # Specify JSON Schema draft version
530
+ markform schema my-form.form.md --draft draft-07
531
+ ```
532
+
275
533
  ### Apply Patches
276
534
 
277
535
  ```bash
278
536
  # Apply a JSON patch to update field values
279
- markform apply my-form.form.md --patch '[{"op":"set","fieldId":"name","value":"Alice"}]'
537
+ markform apply my-form.form.md --patch '[{"op":"set_string","fieldId":"name","value":"Alice"}]'
280
538
  ```
281
539
 
282
540
  ### Web Interface
@@ -308,88 +566,25 @@ markform models
308
566
  markform --help
309
567
  ```
310
568
 
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 |
569
+ ## API Key Setup
324
570
 
325
571
  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
572
+ fill`:
359
573
 
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
368
-
369
- subgraph TEST["<b>TESTING FRAMEWORK</b><br/>Golden session testing with .session.yaml transcripts"]
370
- end
371
-
372
- UI --> ENGINE
373
- AGENT --> HARNESS
374
- AGENT --> ENGINE
375
- HARNESS --> ENGINE
376
- ENGINE --> TEST
377
- end
574
+ | Provider | Env Variable | Native Web Search |
575
+ | --- | --- | :---: |
576
+ | openai | `OPENAI_API_KEY` | ✓ |
577
+ | anthropic | `ANTHROPIC_API_KEY` | |
578
+ | google | `GOOGLE_API_KEY` | ✓ |
579
+ | xai | `XAI_API_KEY` | ✓ |
580
+ | deepseek | `DEEPSEEK_API_KEY` | ✗ |
378
581
 
379
- SPEC ~~~ IMPL
582
+ Run `markform models` to see available models.
583
+ See
584
+ [`src/settings.ts`](https://github.com/jlevy/markform/blob/main/packages/markform/src/settings.ts)
585
+ for defaults.
380
586
 
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
- ```
587
+ If unsure, try `gpt-5-mini` first as it’s fast and supports web search.
393
588
 
394
589
  ## Programmatic Usage
395
590
 
@@ -422,10 +617,14 @@ import { createMarkformTools, MarkformSessionStore } from "markform/ai-sdk";
422
617
  import { generateText } from "ai";
423
618
  import { anthropic } from "@ai-sdk/anthropic";
424
619
 
620
+ // Parse form and create session store (tracks state across tool calls)
425
621
  const form = parseForm(markdownContent);
426
622
  const store = new MarkformSessionStore(form);
623
+
624
+ // Create tools the agent can call: inspect, apply patches, export
427
625
  const tools = createMarkformTools({ sessionStore: store });
428
626
 
627
+ // Agent fills the form via tool calls until complete or maxSteps reached
429
628
  const result = await generateText({
430
629
  model: anthropic("claude-sonnet-4-5-20250929"),
431
630
  prompt: "Fill out this form with appropriate values...",
@@ -443,11 +642,32 @@ const result = await generateText({
443
642
  | `markform_export` | Export schema and values as JSON |
444
643
  | `markform_get_markdown` | Get canonical Markdown representation |
445
644
 
645
+ ## Other Documentation
646
+
647
+ - **[Quick
648
+ Reference](https://github.com/jlevy/markform/blob/main/docs/markform-reference.md)**
649
+ (or run `markform docs`) — Concise syntax reference (agent-friendly)
650
+
651
+ - **[Markform Spec](https://github.com/jlevy/markform/blob/main/docs/markform-spec.md)**
652
+ (or run `markform spec`) — Complete syntax and semantics
653
+
654
+ - **[API
655
+ Documentation](https://github.com/jlevy/markform/blob/main/docs/markform-apis.md)**
656
+ (or run `markform apis`) — TypeScript and AI SDK APIs
657
+
658
+ - **[Design
659
+ Doc](https://github.com/jlevy/markform/blob/main/docs/project/architecture/current/arch-markform-design.md)**
660
+ — Technical design and roadmap
661
+
662
+ - **[Development](https://github.com/jlevy/markform/blob/main/docs/development.md)** —
663
+ Build, test, and contribute
664
+
446
665
  ## FAQ
447
666
 
448
- ### Can you say more why this is a good idea?
667
+ ### Can you talk more about why forms are cool?
449
668
 
450
- Yes! I’ve come to believe forms are a missing piece of the workflow problem with agents.
669
+ As a matter of fact, I can!
670
+ I’ve come to believe forms are a missing piece of the workflow problem with agents.
451
671
  For deep research or complex multi-step workflows, key pieces need to be *precisely
452
672
  controlled*, *domain-specific*, and *always improving*. You need precise documentation
453
673
  on the key intermediate states and final output from an AI pipeline.
@@ -458,6 +678,27 @@ Forms define these pieces and are easy to edit.
458
678
  All other choices can be left to the agents themselves, with the structure and
459
679
  validations enforced by the form-filling tools the agents use.
460
680
 
681
+ Another cool thing about forms: they get rid of the inefficiencies of conversation chat
682
+ history.
683
+ Often when an agentic loop is built, it just saves the chat history for context.
684
+ But a form is inherently more efficient: the harness itself can be stateless.
685
+ It just shares the partly-filled form with the agent, and it has full context in one
686
+ message. That’s what the agentic loop in this implementation does.
687
+
688
+ Finally, the meta-loop of *creating and improving* forms is easier to automate:
689
+
690
+ - To get started, you can ask a good coding model to convert any unstructured doc
691
+ describing a process to a form.
692
+ The model can also use the CLI or tools to validate and test it.
693
+
694
+ - Any time you have a workflow problem, you can ask an LLM to diagnose it and if
695
+ possible, go back and fix up the form with additional instructions or fields or checks
696
+ that would prevent the problem from happening again.
697
+
698
+ I suspect dynamic form structures like this could make complex deep research more
699
+ powerful. Just as you plan a spec before implementing with a coding agent, you could use
700
+ Markform to encode a research plan before dispatching agents to fill it.
701
+
461
702
  ### Is this mature?
462
703
 
463
704
  No! I just wrote it.
@@ -478,11 +719,11 @@ See
478
719
  for more thoughts on how this works.
479
720
  And see [the complete history of
480
721
  specs](https://github.com/jlevy/markform/tree/main/docs/project/specs/done) for examples
481
- of how everything is done with specs and with
722
+ of how everything is done with specs.
482
723
 
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.
724
+ Although I didn’t write much code, there was a *lot* of management, review, and
725
+ iteration on design decisions.
726
+ And yes, this README is written by me.
486
727
  :)
487
728
 
488
729
  ### What are the goals of Markform?
@@ -539,13 +780,14 @@ The closest alternatives are:
539
780
  human-friendly UI. But these do not have a human-friendly text format for use by
540
781
  agents as well as humans.
541
782
 
542
- | Approach | Has GUI | Human-readable source format | Agent-editable | APIs and validation rules |
783
+ | Approach | Usable GUI editor | Human-readable source format | Agent-editable | APIs and validation rules |
543
784
  | --- | :---: | :---: | :---: | :---: |
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 |
785
+ | Plain Markdown | IDEs/editors | ✅ | ⚠️ fragile | ❌ |
786
+ | JSON + JSON Schema | IDEs/editors | ⚠️ no free text | ✅ | ✅ |
787
+ | SaaS tools (Typeform, Docusign, PDF forms) | ✅ | ⚠️ rarely | ⚠️ sometimes | ⚠️ sometimes |
788
+ | HTML/web Forms | ✅ IDEs/editors | ⚠️ HTML+code | ⚠️ coding agent | ✅ |
789
+ | Excel/Google Sheets | app | .csv/.xlsx | ⚠️ with tools | ✅ with some coding |
790
+ | **Markform** | ✅ IDEs/editors | ✅ | ✅ with this package | ✅ with this package |
549
791
 
550
792
  ### What are example use cases?
551
793
 
@@ -587,26 +829,6 @@ settings:
587
829
 
588
830
  Or see [markdoc/language-server](https://github.com/markdoc/language-server).
589
831
 
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
832
  ## License
611
833
 
612
834
  AGPL-3.0-or-later. [Contact me](https://github.com/jlevy) for additional licensing