markform 0.1.6 → 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 +464 -207
- package/dist/ai-sdk.d.mts +1 -2
- package/dist/ai-sdk.mjs +3 -2
- package/dist/{apply-DMQl-VVd.mjs → apply-BUU2QcJ2.mjs} +130 -23
- package/dist/bin.d.mts +0 -1
- package/dist/bin.mjs +3 -6
- package/dist/{cli-CXjkdym_.mjs → cli-BZh25bvy.mjs} +1380 -632
- package/dist/cli.d.mts +0 -1
- package/dist/cli.mjs +2 -6
- package/dist/coreTypes-BSPJ9H27.d.mts +3253 -0
- package/dist/{coreTypes-pyctKRgc.mjs → coreTypes-DJtu8OOp.mjs} +26 -4
- package/dist/index.d.mts +112 -5
- package/dist/index.mjs +6 -5
- package/dist/{session-uF0e6m6k.mjs → session-CmHdAPyg.mjs} +3 -2
- package/dist/session-DSTNiHza.mjs +4 -0
- package/dist/{shared-u22MtBRo.mjs → shared-C9yW5FLZ.mjs} +2 -1
- package/dist/{shared-DRlgu2ZJ.mjs → shared-DQ6y3Ggc.mjs} +3 -1
- package/dist/{src-o_5TSoHQ.mjs → src-kUggXhN1.mjs} +519 -98
- package/docs/markform-apis.md +30 -1
- package/docs/markform-reference.md +65 -6
- package/docs/markform-spec.md +2 -2
- package/examples/earnings-analysis/earnings-analysis.form.md +1 -0
- package/examples/movie-research/movie-research-basic.form.md +1 -0
- package/examples/movie-research/movie-research-deep.form.md +16 -56
- package/examples/movie-research/movie-research-demo.form.md +60 -0
- 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.form.md +1 -0
- package/examples/simple/simple.schema.json +374 -0
- package/examples/simple/simple.session.yaml +3 -3
- package/examples/startup-deep-research/startup-deep-research.form.md +1 -0
- package/examples/startup-research/startup-research.form.md +1 -0
- package/package.json +11 -9
- package/dist/coreTypes-9XZSNOv6.d.mts +0 -8951
- package/dist/session-B_stoXQn.mjs +0 -4
- package/examples/movie-research/movie-research-minimal.form.md +0 -68
package/README.md
CHANGED
|
@@ -1,57 +1,77 @@
|
|
|
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.
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
`{% tag %}` annotations, so LLMs are already quite good at writing Markform docs.
|
|
16
|
+
**How it works:**
|
|
17
17
|
|
|
18
|
-
|
|
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).
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
agent
|
|
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).
|
|
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
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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.
|
|
31
34
|
|
|
32
|
-
|
|
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.
|
|
35
|
+
## Why Do Agents Need Forms?
|
|
35
36
|
|
|
36
|
-
|
|
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.
|
|
42
|
+
|
|
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).
|
|
49
|
+
|
|
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.
|
|
54
|
+
|
|
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
|
-
#
|
|
42
|
-
#
|
|
43
|
-
|
|
44
|
-
npx markform examples
|
|
61
|
+
# Copy example forms to ./forms/ and run one interactively.
|
|
62
|
+
# Set OPENAI_API_KEY or ANTHROPIC_API_KEY (or put in .env) for research examples
|
|
63
|
+
npx markform@latest examples
|
|
45
64
|
|
|
46
|
-
# Read the docs
|
|
65
|
+
# Read the docs (tell your agents to run these; they are agent-friendly!)
|
|
47
66
|
npx markform # CLI help
|
|
48
67
|
npx markform readme # This file
|
|
49
|
-
npx markform docs # Quick
|
|
68
|
+
npx markform docs # Quick reference for writing Markforms
|
|
50
69
|
npx markform spec # Read the full spec
|
|
51
70
|
```
|
|
52
71
|
|
|
53
|
-
|
|
54
|
-
|
|
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.
|
|
74
|
+
Pick `movie-research-demo.form.md` for a quick example.
|
|
55
75
|
|
|
56
76
|
## Installation
|
|
57
77
|
|
|
@@ -70,56 +90,92 @@ npm install markform
|
|
|
70
90
|
### Form Definition
|
|
71
91
|
|
|
72
92
|
A `.form.md` file is simply a Markdoc file.
|
|
73
|
-
It combines YAML frontmatter with Markdoc-tagged content
|
|
93
|
+
It combines YAML frontmatter with Markdoc-tagged content.
|
|
74
94
|
|
|
75
|
-
|
|
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
|
|
76
148
|
---
|
|
77
149
|
markform:
|
|
78
150
|
spec: MF/0.1
|
|
79
|
-
title: Movie Research
|
|
80
|
-
description:
|
|
151
|
+
title: Movie Research Demo
|
|
152
|
+
description: Movie lookup with ratings from IMDB and Rotten Tomatoes.
|
|
153
|
+
run_mode: research
|
|
81
154
|
roles:
|
|
82
155
|
- user
|
|
83
156
|
- agent
|
|
84
157
|
role_instructions:
|
|
85
158
|
user: "Enter the movie title."
|
|
86
159
|
agent: |
|
|
87
|
-
|
|
88
|
-
This is a minimal 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.
|
|
89
161
|
---
|
|
90
|
-
{% form id="
|
|
162
|
+
{% form id="movie_research_demo" %}
|
|
163
|
+
{% group id="movie_input" %}
|
|
91
164
|
|
|
92
|
-
##
|
|
93
|
-
|
|
94
|
-
{% group id="movie_input" title="Movie Identification" %}
|
|
95
|
-
|
|
96
|
-
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?
|
|
97
166
|
|
|
98
167
|
{% field kind="string" id="movie" label="Movie" role="user" required=true minLength=1 maxLength=300 %}{% /field %}
|
|
99
168
|
{% instructions ref="movie" %}Enter the movie title (add year or details for disambiguation).{% /instructions %}
|
|
100
169
|
|
|
101
170
|
{% /group %}
|
|
102
171
|
|
|
103
|
-
## About the Movie
|
|
104
|
-
|
|
105
172
|
{% group id="about_the_movie" title="About the Movie" %}
|
|
106
173
|
|
|
107
|
-
|
|
174
|
+
## Movie Ratings
|
|
108
175
|
|
|
109
|
-
|
|
110
|
-
{% instructions ref="full_title" %}Official title, including subtitle if any.{% /instructions %}
|
|
176
|
+
Here are the ratings for the movie:
|
|
111
177
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
{% field kind="number" id="year" label="Release Year" role="agent" required=true min=1888 max=2030 %}{% /field %}
|
|
115
|
-
|
|
116
|
-
**IMDB:**
|
|
117
|
-
|
|
118
|
-
{% field kind="url" id="imdb_url" label="IMDB URL" role="agent" required=true %}{% /field %}
|
|
119
|
-
|
|
120
|
-
**MPAA rating:**
|
|
121
|
-
|
|
122
|
-
{% 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" %}
|
|
123
179
|
- [ ] G {% #g %}
|
|
124
180
|
- [ ] PG {% #pg %}
|
|
125
181
|
- [ ] PG-13 {% #pg_13 %}
|
|
@@ -128,64 +184,191 @@ What movie do you want to research? \[*This field is filled in by the user (`rol
|
|
|
128
184
|
- [ ] NR/Unrated {% #nr %}
|
|
129
185
|
{% /field %}
|
|
130
186
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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 %}
|
|
137
194
|
|
|
138
|
-
{%
|
|
139
|
-
|
|
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 %}
|
|
140
201
|
|
|
141
202
|
{% /group %}
|
|
142
|
-
|
|
143
203
|
{% /form %}
|
|
144
204
|
```
|
|
145
205
|
|
|
206
|
+
</details>
|
|
207
|
+
|
|
146
208
|
### Form Report Output
|
|
147
209
|
|
|
148
|
-
Run
|
|
149
|
-
|
|
210
|
+
Run `npx markform examples` to copy examples, then `npx markform run` and select `Movie
|
|
211
|
+
Research Demo` to fill it.
|
|
150
212
|
|
|
151
|
-
|
|
152
|
-
|
|
213
|
+
A form can be exported
|
|
214
|
+
|
|
215
|
+
- as the filled form (Markform format, just like the input)
|
|
153
216
|
|
|
154
|
-
|
|
217
|
+
- as a report (plain Markdown)
|
|
155
218
|
|
|
219
|
+
- as values (YAML or JSON)
|
|
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
|
|
156
226
|
Movie:
|
|
157
|
-
|
|
227
|
+
The Shawshank Redemption
|
|
158
228
|
|
|
159
229
|
## About the Movie
|
|
160
230
|
|
|
161
|
-
|
|
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
|
|
162
253
|
The Shawshank Redemption
|
|
254
|
+
```
|
|
255
|
+
{% /field %}
|
|
163
256
|
|
|
164
|
-
|
|
165
|
-
|
|
257
|
+
{% instructions ref="movie" %}
|
|
258
|
+
Enter the movie title (add year or details for disambiguation).
|
|
259
|
+
{% /instructions %}
|
|
166
260
|
|
|
167
|
-
|
|
168
|
-
https://www.imdb.com/title/tt0111161/
|
|
261
|
+
{% /group %}
|
|
169
262
|
|
|
170
|
-
|
|
171
|
-
R
|
|
263
|
+
{% group id="about_the_movie" title="About the Movie" %}
|
|
172
264
|
|
|
173
|
-
|
|
174
|
-
|
|
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 %}
|
|
175
273
|
|
|
176
|
-
|
|
177
|
-
|
|
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 %}
|
|
288
|
+
|
|
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
|
+
```
|
|
319
|
+
|
|
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
|
+
}
|
|
178
358
|
```
|
|
179
359
|
|
|
360
|
+
</details>
|
|
361
|
+
|
|
180
362
|
### More Example Forms
|
|
181
363
|
|
|
182
364
|
The package includes example forms.
|
|
183
|
-
View them with `markform examples --list`
|
|
365
|
+
View them with `markform examples --list`, copy with `markform examples`, and run with
|
|
366
|
+
`markform run`:
|
|
184
367
|
|
|
185
368
|
- [`simple.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/simple/simple.form.md)
|
|
186
369
|
\- Basic form demonstrating all field kinds.
|
|
187
370
|
|
|
188
|
-
- [`movie-research-
|
|
371
|
+
- [`movie-research-demo.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-research-demo.form.md)
|
|
189
372
|
\- The quick example above.
|
|
190
373
|
|
|
191
374
|
- [`movie-research-basic.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-research-basic.form.md)
|
|
@@ -196,20 +379,103 @@ View them with `markform examples --list` or try these interactively:
|
|
|
196
379
|
|
|
197
380
|
- [`earnings-analysis.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/earnings-analysis/earnings-analysis.form.md)
|
|
198
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
|
+
```
|
|
199
448
|
|
|
200
449
|
## CLI Commands
|
|
201
450
|
|
|
202
|
-
###
|
|
451
|
+
### Copy and Run Examples
|
|
203
452
|
|
|
204
453
|
```bash
|
|
205
|
-
#
|
|
454
|
+
# Copy all bundled examples to ./forms/
|
|
206
455
|
markform examples
|
|
207
456
|
|
|
208
457
|
# List available examples
|
|
209
458
|
markform examples --list
|
|
210
459
|
|
|
211
|
-
#
|
|
212
|
-
markform examples --name
|
|
460
|
+
# Copy a specific example
|
|
461
|
+
markform examples --name movie-research-demo
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Browse and Run Forms
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
# Browse forms in ./forms/ and run one interactively
|
|
468
|
+
markform run
|
|
469
|
+
|
|
470
|
+
# Run a specific form directly
|
|
471
|
+
markform run forms/movie-research-demo.form.md
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Check Form Status
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
# Show fill progress with per-role breakdown
|
|
478
|
+
markform status my-form.form.md
|
|
213
479
|
```
|
|
214
480
|
|
|
215
481
|
### Inspect Forms
|
|
@@ -251,11 +517,24 @@ markform export my-form.form.md --format=yaml
|
|
|
251
517
|
markform dump my-form.form.md
|
|
252
518
|
```
|
|
253
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
|
+
|
|
254
533
|
### Apply Patches
|
|
255
534
|
|
|
256
535
|
```bash
|
|
257
536
|
# Apply a JSON patch to update field values
|
|
258
|
-
markform apply my-form.form.md --patch '[{"op":"
|
|
537
|
+
markform apply my-form.form.md --patch '[{"op":"set_string","fieldId":"name","value":"Alice"}]'
|
|
259
538
|
```
|
|
260
539
|
|
|
261
540
|
### Web Interface
|
|
@@ -287,88 +566,25 @@ markform models
|
|
|
287
566
|
markform --help
|
|
288
567
|
```
|
|
289
568
|
|
|
290
|
-
##
|
|
291
|
-
|
|
292
|
-
Standard LLMs can be used to fill in forms or create research reports from form
|
|
293
|
-
templates. The package currently has support for these models built in, and enables web
|
|
294
|
-
search tools for them if possible.
|
|
295
|
-
|
|
296
|
-
| Provider | Env Variable | Example Models |
|
|
297
|
-
| --- | --- | --- |
|
|
298
|
-
| openai | `OPENAI_API_KEY` | gpt-5-mini, gpt-5.1, gpt-5.2 |
|
|
299
|
-
| anthropic | `ANTHROPIC_API_KEY` | claude-sonnet-4-5, claude-opus-4-5 |
|
|
300
|
-
| google | `GOOGLE_API_KEY` | gemini-2.5-pro, gemini-2.5-flash |
|
|
301
|
-
| xai | `XAI_API_KEY` | grok-4, grok-4-fast |
|
|
302
|
-
| deepseek | `DEEPSEEK_API_KEY` | deepseek-chat, deepseek-reasoner |
|
|
569
|
+
## API Key Setup
|
|
303
570
|
|
|
304
571
|
Set the appropriate environment variable for your provider before running `markform
|
|
305
|
-
fill
|
|
572
|
+
fill`:
|
|
573
|
+
|
|
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` | ✗ |
|
|
581
|
+
|
|
582
|
+
Run `markform models` to see available models.
|
|
583
|
+
See
|
|
306
584
|
[`src/settings.ts`](https://github.com/jlevy/markform/blob/main/packages/markform/src/settings.ts)
|
|
307
|
-
for
|
|
585
|
+
for defaults.
|
|
308
586
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
```mermaid
|
|
312
|
-
flowchart LR
|
|
313
|
-
subgraph SPEC["<b>MARKFORM SPEC</b>"]
|
|
314
|
-
direction TB
|
|
315
|
-
|
|
316
|
-
subgraph L1["<b>LAYER 1: SYNTAX</b><br/>Markdoc tag syntax and frontmatter (form, group, string-field, checkboxes, etc.)"]
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
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)"]
|
|
320
|
-
end
|
|
321
|
-
|
|
322
|
-
subgraph L3["<b>LAYER 3: VALIDATION & FORM FILLING</b><br/>Rules for filling forms via patches, field ids, required field semantics, validation hooks"]
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
subgraph L4["<b>LAYER 4: TOOL API & INTERFACES</b><br/>Abstract API for agents and humans (TypeScript and AI SDK integration)"]
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
L4 -->
|
|
329
|
-
|
|
330
|
-
L3 --> L2 --> L1
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
subgraph IMPL["<b>THIS IMPLEMENTATION</b>"]
|
|
334
|
-
direction TB
|
|
335
|
-
|
|
336
|
-
subgraph ENGINE["<b>ENGINE IMPLEMENTATION</b><br/>Markdoc parser, serializer, patch application, validation (uses jiti for TypeScript rules)"]
|
|
337
|
-
end
|
|
338
|
-
|
|
339
|
-
subgraph UI["<b>USER INTERFACES</b><br/>CLI commands, web UI (serve), render to HTML"]
|
|
340
|
-
end
|
|
341
|
-
|
|
342
|
-
subgraph AGENT["<b>AGENT INTERFACES</b><br/>Tool API library, MCP server, AI SDK tools"]
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
subgraph HARNESS["<b>EXECUTION HARNESS</b><br/>Step-by-step form-filling agentic loop"]
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
subgraph TEST["<b>TESTING FRAMEWORK</b><br/>Golden session testing with .session.yaml transcripts"]
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
UI --> ENGINE
|
|
352
|
-
AGENT --> HARNESS
|
|
353
|
-
AGENT --> ENGINE
|
|
354
|
-
HARNESS --> ENGINE
|
|
355
|
-
ENGINE --> TEST
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
SPEC ~~~ IMPL
|
|
359
|
-
|
|
360
|
-
style SPEC fill:#e8f4f8,stroke:#0077b6
|
|
361
|
-
style L1 fill:#caf0f8,stroke:#0077b6
|
|
362
|
-
style L2 fill:#caf0f8,stroke:#0077b6
|
|
363
|
-
style L3 fill:#caf0f8,stroke:#0077b6
|
|
364
|
-
style L4 fill:#caf0f8,stroke:#0077b6
|
|
365
|
-
style IMPL fill:#fff3e6,stroke:#fb8500
|
|
366
|
-
style ENGINE fill:#ffe8cc,stroke:#fb8500
|
|
367
|
-
style UI fill:#ffe8cc,stroke:#fb8500
|
|
368
|
-
style AGENT fill:#ffe8cc,stroke:#fb8500
|
|
369
|
-
style HARNESS fill:#ffe8cc,stroke:#fb8500
|
|
370
|
-
style TEST fill:#ffe8cc,stroke:#fb8500
|
|
371
|
-
```
|
|
587
|
+
If unsure, try `gpt-5-mini` first as it’s fast and supports web search.
|
|
372
588
|
|
|
373
589
|
## Programmatic Usage
|
|
374
590
|
|
|
@@ -401,10 +617,14 @@ import { createMarkformTools, MarkformSessionStore } from "markform/ai-sdk";
|
|
|
401
617
|
import { generateText } from "ai";
|
|
402
618
|
import { anthropic } from "@ai-sdk/anthropic";
|
|
403
619
|
|
|
620
|
+
// Parse form and create session store (tracks state across tool calls)
|
|
404
621
|
const form = parseForm(markdownContent);
|
|
405
622
|
const store = new MarkformSessionStore(form);
|
|
623
|
+
|
|
624
|
+
// Create tools the agent can call: inspect, apply patches, export
|
|
406
625
|
const tools = createMarkformTools({ sessionStore: store });
|
|
407
626
|
|
|
627
|
+
// Agent fills the form via tool calls until complete or maxSteps reached
|
|
408
628
|
const result = await generateText({
|
|
409
629
|
model: anthropic("claude-sonnet-4-5-20250929"),
|
|
410
630
|
prompt: "Fill out this form with appropriate values...",
|
|
@@ -422,11 +642,32 @@ const result = await generateText({
|
|
|
422
642
|
| `markform_export` | Export schema and values as JSON |
|
|
423
643
|
| `markform_get_markdown` | Get canonical Markdown representation |
|
|
424
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
|
+
|
|
425
665
|
## FAQ
|
|
426
666
|
|
|
427
|
-
### Can you
|
|
667
|
+
### Can you talk more about why forms are cool?
|
|
428
668
|
|
|
429
|
-
|
|
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.
|
|
430
671
|
For deep research or complex multi-step workflows, key pieces need to be *precisely
|
|
431
672
|
controlled*, *domain-specific*, and *always improving*. You need precise documentation
|
|
432
673
|
on the key intermediate states and final output from an AI pipeline.
|
|
@@ -437,6 +678,27 @@ Forms define these pieces and are easy to edit.
|
|
|
437
678
|
All other choices can be left to the agents themselves, with the structure and
|
|
438
679
|
validations enforced by the form-filling tools the agents use.
|
|
439
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
|
+
|
|
440
702
|
### Is this mature?
|
|
441
703
|
|
|
442
704
|
No! I just wrote it.
|
|
@@ -445,10 +707,24 @@ But it’s been useful for me already.
|
|
|
445
707
|
|
|
446
708
|
### Was it Vibe Coded?
|
|
447
709
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
710
|
+
This is 100% agent-written code and the planning specs are also 100% agent written,
|
|
711
|
+
using a mix of Opus 4.5, GPT 5.2, GPT-5 Pro, Gemini 3, and occasionally others.
|
|
712
|
+
|
|
713
|
+
But it’s not slop. It is written via a strongly spec-driven process, using rules from my
|
|
714
|
+
[Speculate](https://github.com/jlevy/speculate) repo and Steve Yegge’s
|
|
715
|
+
[beads](https://github.com/steveyegge/beads) for tracking work.
|
|
716
|
+
|
|
717
|
+
See
|
|
718
|
+
[my post](https://github.com/jlevy/speculate/blob/main/about/lessons_in_spec_coding.md)
|
|
719
|
+
for more thoughts on how this works.
|
|
720
|
+
And see [the complete history of
|
|
721
|
+
specs](https://github.com/jlevy/markform/tree/main/docs/project/specs/done) for examples
|
|
722
|
+
of how everything is done with specs.
|
|
723
|
+
|
|
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.
|
|
727
|
+
:)
|
|
452
728
|
|
|
453
729
|
### What are the goals of Markform?
|
|
454
730
|
|
|
@@ -504,13 +780,14 @@ The closest alternatives are:
|
|
|
504
780
|
human-friendly UI. But these do not have a human-friendly text format for use by
|
|
505
781
|
agents as well as humans.
|
|
506
782
|
|
|
507
|
-
| Approach |
|
|
783
|
+
| Approach | Usable GUI editor | Human-readable source format | Agent-editable | APIs and validation rules |
|
|
508
784
|
| --- | :---: | :---: | :---: | :---: |
|
|
509
|
-
| Plain Markdown |
|
|
510
|
-
| JSON
|
|
511
|
-
| SaaS tools (Typeform, Docusign, PDF forms) | ✅ | ⚠️ rarely | ⚠️
|
|
512
|
-
|
|
|
513
|
-
|
|
|
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 |
|
|
514
791
|
|
|
515
792
|
### What are example use cases?
|
|
516
793
|
|
|
@@ -552,26 +829,6 @@ settings:
|
|
|
552
829
|
|
|
553
830
|
Or see [markdoc/language-server](https://github.com/markdoc/language-server).
|
|
554
831
|
|
|
555
|
-
## Documentation
|
|
556
|
-
|
|
557
|
-
- **[Quick
|
|
558
|
-
Reference](https://github.com/jlevy/markform/blob/main/docs/markform-reference.md)**
|
|
559
|
-
(or run `markform docs`) - Concise syntax reference (agent-friendly)
|
|
560
|
-
|
|
561
|
-
- **[Markform Spec](https://github.com/jlevy/markform/blob/main/docs/markform-spec.md)**
|
|
562
|
-
(or run `markform spec`) - Complete syntax and semantics
|
|
563
|
-
|
|
564
|
-
- **[API
|
|
565
|
-
Documentation](https://github.com/jlevy/markform/blob/main/docs/markform-apis.md)**
|
|
566
|
-
(or run `markform apis`) - TypeScript and AI SDK APIs
|
|
567
|
-
|
|
568
|
-
- **[Design
|
|
569
|
-
Doc](https://github.com/jlevy/markform/blob/main/docs/project/architecture/current/arch-markform-design.md)**
|
|
570
|
-
\- Technical design and roadmap
|
|
571
|
-
|
|
572
|
-
- **[Development](https://github.com/jlevy/markform/blob/main/docs/development.md)** -
|
|
573
|
-
Build, test, and contribute
|
|
574
|
-
|
|
575
832
|
## License
|
|
576
833
|
|
|
577
834
|
AGPL-3.0-or-later. [Contact me](https://github.com/jlevy) for additional licensing
|