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.
- package/README.md +420 -198
- package/dist/ai-sdk.d.mts +1 -1
- package/dist/ai-sdk.mjs +2 -2
- package/dist/{apply-g23rRn7p.mjs → apply-BUU2QcJ2.mjs} +102 -20
- package/dist/bin.mjs +1 -1
- package/dist/{cli-Bqlm-WWw.mjs → cli-BZh25bvy.mjs} +26 -8
- package/dist/cli.mjs +1 -1
- package/dist/{coreTypes-DCvD7feM.d.mts → coreTypes-BSPJ9H27.d.mts} +51 -1
- package/dist/{coreTypes-__Cwxz5q.mjs → coreTypes-DJtu8OOp.mjs} +16 -1
- package/dist/index.d.mts +10 -5
- package/dist/index.mjs +4 -4
- package/dist/{session-CgCNni0e.mjs → session-CmHdAPyg.mjs} +1 -1
- package/dist/{session-DruaYPZ1.mjs → session-DSTNiHza.mjs} +1 -1
- package/dist/{src-BiuxbzF3.mjs → src-kUggXhN1.mjs} +115 -29
- package/docs/markform-apis.md +30 -1
- package/docs/markform-reference.md +65 -6
- package/examples/movie-research/movie-research-deep.form.md +15 -56
- package/examples/movie-research/movie-research-demo.form.md +25 -34
- package/examples/rejection-test/rejection-test-mock-filled.form.md +41 -0
- package/examples/rejection-test/rejection-test-mock-filled.report.md +15 -0
- package/examples/rejection-test/rejection-test-mock-filled.schema.json +59 -0
- package/examples/rejection-test/rejection-test-mock-filled.yml +13 -0
- package/examples/rejection-test/rejection-test.form.md +35 -0
- package/examples/rejection-test/rejection-test.session.yaml +88 -0
- package/examples/simple/simple-mock-filled.report.md +96 -0
- package/examples/simple/simple-mock-filled.schema.json +374 -0
- package/examples/simple/simple-mock-filled.yml +87 -0
- package/examples/simple/simple-skipped-filled.report.md +90 -0
- package/examples/simple/simple-skipped-filled.schema.json +374 -0
- package/examples/simple/simple-skipped-filled.yml +77 -0
- package/examples/simple/simple-with-skips.session.yaml +3 -3
- package/examples/simple/simple.session.yaml +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,49 +1,66 @@
|
|
|
1
1
|
# Markform
|
|
2
2
|
|
|
3
|
-
**Markform**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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 don’t 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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
82
|
-
description:
|
|
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
|
-
|
|
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"
|
|
93
|
-
|
|
94
|
-
## Movie Research Example
|
|
162
|
+
{% form id="movie_research_demo" %}
|
|
163
|
+
{% group id="movie_input" %}
|
|
95
164
|
|
|
96
|
-
|
|
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
|
-
|
|
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
|
-
|
|
176
|
+
Here are the ratings for the movie:
|
|
123
177
|
|
|
124
|
-
{% field kind="single_select" id="mpaa_rating" label="MPAA Rating"
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
{%
|
|
141
|
-
|
|
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
|
|
152
|
-
The report output looks like:
|
|
211
|
+
Research Demo` to fill it.
|
|
153
212
|
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
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
|
-
|
|
227
|
+
The Shawshank Redemption
|
|
161
228
|
|
|
162
229
|
## About the Movie
|
|
163
230
|
|
|
164
|
-
|
|
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
|
-
|
|
168
|
-
|
|
257
|
+
{% instructions ref="movie" %}
|
|
258
|
+
Enter the movie title (add year or details for disambiguation).
|
|
259
|
+
{% /instructions %}
|
|
169
260
|
|
|
170
|
-
|
|
171
|
-
https://www.imdb.com/title/tt0111161/
|
|
261
|
+
{% /group %}
|
|
172
262
|
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
177
|
-
|
|
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
|
-
|
|
180
|
-
|
|
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":"
|
|
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
|
-
##
|
|
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
|
|
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
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
667
|
+
### Can you talk more about why forms are cool?
|
|
449
668
|
|
|
450
|
-
|
|
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
|
|
722
|
+
of how everything is done with specs.
|
|
482
723
|
|
|
483
|
-
Although I didn’t
|
|
484
|
-
|
|
485
|
-
And this
|
|
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 |
|
|
783
|
+
| Approach | Usable GUI editor | Human-readable source format | Agent-editable | APIs and validation rules |
|
|
543
784
|
| --- | :---: | :---: | :---: | :---: |
|
|
544
|
-
| Plain Markdown |
|
|
545
|
-
| JSON
|
|
546
|
-
| SaaS tools (Typeform, Docusign, PDF forms) | ✅ | ⚠️ rarely | ⚠️
|
|
547
|
-
|
|
|
548
|
-
|
|
|
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
|