markform 0.1.4 → 0.1.6
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 +110 -100
- package/dist/ai-sdk.d.mts +1 -1
- package/dist/ai-sdk.mjs +1 -1
- package/dist/{apply-C54EMAJ1.mjs → apply-DMQl-VVd.mjs} +6 -26
- package/dist/bin.mjs +4 -4
- package/dist/{cli-BhWhn6L9.mjs → cli-CXjkdym_.mjs} +83 -21
- package/dist/cli.mjs +4 -4
- package/dist/{coreTypes-cbNTYAcb.d.mts → coreTypes-9XZSNOv6.d.mts} +2 -2
- package/dist/index.d.mts +61 -7
- package/dist/index.mjs +2 -2
- package/dist/{shared-BqPnYXrn.mjs → shared-DRlgu2ZJ.mjs} +1 -1
- package/dist/{shared-CZsyShck.mjs → shared-u22MtBRo.mjs} +1 -1
- package/dist/{src-BNh7Cx9P.mjs → src-o_5TSoHQ.mjs} +105 -23
- package/docs/markform-apis.md +52 -1
- package/docs/markform-reference.md +19 -19
- package/docs/markform-spec.md +29 -20
- package/examples/earnings-analysis/earnings-analysis.form.md +86 -808
- package/examples/earnings-analysis/earnings-analysis.valid.ts +16 -148
- package/examples/movie-research/movie-research-basic.form.md +16 -16
- package/examples/movie-research/movie-research-deep.form.md +36 -36
- package/examples/movie-research/movie-research-minimal.form.md +4 -4
- package/examples/simple/simple-mock-filled.form.md +16 -16
- package/examples/simple/simple-skipped-filled.form.md +16 -16
- package/examples/simple/simple-with-skips.session.yaml +3 -3
- package/examples/simple/simple.form.md +16 -16
- package/examples/simple/simple.session.yaml +3 -3
- package/examples/startup-deep-research/startup-deep-research.form.md +22 -22
- package/examples/startup-research/startup-research-mock-filled.form.md +12 -12
- package/examples/startup-research/startup-research.form.md +12 -12
- package/package.json +1 -1
- package/examples/celebrity-deep-research/celebrity-deep-research.form.md +0 -967
package/README.md
CHANGED
|
@@ -1,47 +1,73 @@
|
|
|
1
1
|
# Markform
|
|
2
2
|
|
|
3
|
-
|
|
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.
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
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.
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
validation rules.
|
|
12
|
-
Fields, validation rules, and instructions are encoded as Markdoc tags.
|
|
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.
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
you want (the form structure and validations) instead of *how* to get it (coding up
|
|
16
|
-
agent workflows). For deep research and other tasks where you want a high level of
|
|
17
|
-
control on intermediate states and final output from an AI pipeline, this approach is a
|
|
18
|
-
compelling alternative to programmatic or UI-based agent workflows.
|
|
19
|
-
Because it’s just Markdown with tags, agents are also very good at writing Markform,
|
|
20
|
-
which makes creating new workflows much easier.
|
|
18
|
+
## Why?
|
|
21
19
|
|
|
22
|
-
|
|
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).
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
For centuries, humans have used paper forms to systematize and manage processes.
|
|
27
|
+
A well-designed form with instructions, field definitions, and validations is a concise
|
|
28
|
+
way to share context: background knowledge, goals, process rules, and memories.
|
|
29
|
+
I don’t think AI changes this essential aspect of knowledge work.
|
|
30
|
+
It’s time to bring bureaucracy to the agents.
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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.
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
(For more, see [the FAQ](#faq).)
|
|
33
37
|
|
|
34
38
|
## Quick Start
|
|
35
39
|
|
|
36
40
|
```bash
|
|
37
|
-
# Try
|
|
41
|
+
# Try filling in one of a few example forms without installing.
|
|
42
|
+
# First set OPENAI_API_KEY or ANTHROPIC_API_KEY in environment or .env file
|
|
43
|
+
# to try the research examples.
|
|
38
44
|
npx markform examples
|
|
45
|
+
|
|
46
|
+
# Read the docs
|
|
47
|
+
npx markform # CLI help
|
|
48
|
+
npx markform readme # This file
|
|
49
|
+
npx markform docs # Quick start for humans or agents to write Markforms
|
|
50
|
+
npx markform spec # Read the full spec
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This lets you walk through a form interactively from the CLI, with the option to have an
|
|
54
|
+
agent fill in parts of the form.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
Requires Node.js 20+.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# As a global CLI
|
|
62
|
+
npm install -g markform
|
|
63
|
+
|
|
64
|
+
# Or as a project dependency
|
|
65
|
+
npm install markform
|
|
39
66
|
```
|
|
40
67
|
|
|
41
|
-
|
|
42
|
-
You’ll need at least one [API key](#supported-providers) to have LLMs fill in forms.
|
|
68
|
+
## Example: Research a Movie
|
|
43
69
|
|
|
44
|
-
###
|
|
70
|
+
### Form Definition
|
|
45
71
|
|
|
46
72
|
A `.form.md` file is simply a Markdoc file.
|
|
47
73
|
It combines YAML frontmatter with Markdoc-tagged content:
|
|
@@ -65,18 +91,18 @@ markform:
|
|
|
65
91
|
|
|
66
92
|
## Movie Research Example
|
|
67
93
|
|
|
68
|
-
{%
|
|
94
|
+
{% group id="movie_input" title="Movie Identification" %}
|
|
69
95
|
|
|
70
96
|
What movie do you want to research? \[*This field is filled in by the user (`role="user"`).*\]
|
|
71
97
|
|
|
72
98
|
{% field kind="string" id="movie" label="Movie" role="user" required=true minLength=1 maxLength=300 %}{% /field %}
|
|
73
99
|
{% instructions ref="movie" %}Enter the movie title (add year or details for disambiguation).{% /instructions %}
|
|
74
100
|
|
|
75
|
-
{% /
|
|
101
|
+
{% /group %}
|
|
76
102
|
|
|
77
103
|
## About the Movie
|
|
78
104
|
|
|
79
|
-
{%
|
|
105
|
+
{% group id="about_the_movie" title="About the Movie" %}
|
|
80
106
|
|
|
81
107
|
**Title:**
|
|
82
108
|
|
|
@@ -112,11 +138,13 @@ What movie do you want to research? \[*This field is filled in by the user (`rol
|
|
|
112
138
|
{% field kind="string" id="logline" label="One-Line Summary" role="agent" maxLength=300 %}{% /field %}
|
|
113
139
|
{% instructions ref="logline" %}Brief plot summary in 1-2 sentences, no spoilers.{% /instructions %}
|
|
114
140
|
|
|
115
|
-
{% /
|
|
141
|
+
{% /group %}
|
|
116
142
|
|
|
117
143
|
{% /form %}
|
|
118
144
|
```
|
|
119
145
|
|
|
146
|
+
### Form Report Output
|
|
147
|
+
|
|
120
148
|
Run the `npx markform examples` and select the `Movie Research (Minimal)` example and
|
|
121
149
|
view the report:
|
|
122
150
|
|
|
@@ -149,76 +177,25 @@ One-Line Summary:
|
|
|
149
177
|
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.
|
|
150
178
|
```
|
|
151
179
|
|
|
152
|
-
See the
|
|
153
|
-
[examples/](https://github.com/jlevy/markform/tree/main/packages/markform/examples)
|
|
154
|
-
directory for a few more complex form examples.
|
|
155
|
-
|
|
156
|
-
**Key concepts:**
|
|
157
|
-
|
|
158
|
-
- **Roles**: Define who fills what (`user` for humans, `agent` for AI)
|
|
159
|
-
|
|
160
|
-
- **Field kinds**: `string-field`, `number-field`, `date-field`, `year-field`,
|
|
161
|
-
`url-field`, `url-list`, `string-list`, `single-select`, `multi-select`, `checkboxes`
|
|
162
|
-
|
|
163
|
-
- **Validation**: `required`, `min/max`, `minLength/maxLength`, `pattern`
|
|
164
|
-
|
|
165
|
-
- **Structure**: Fields organized in `field-group` containers
|
|
166
|
-
|
|
167
|
-
- **Instructions**: Per-field guidance for users or agents
|
|
168
|
-
|
|
169
180
|
### More Example Forms
|
|
170
181
|
|
|
171
|
-
The package includes example forms
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
- [`movie-research/movie-research-minimal.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-research-minimal.form.md)
|
|
175
|
-
\- Quick movie lookup (title, year, rating)
|
|
176
|
-
|
|
177
|
-
- [`movie-research/movie-research-basic.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-research-basic.form.md)
|
|
178
|
-
\- Standard research with IMDB, Rotten Tomatoes, Metacritic
|
|
179
|
-
|
|
180
|
-
- [`movie-research/movie-research-deep.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-research-deep.form.md)
|
|
181
|
-
\- Comprehensive analysis with streaming, box office
|
|
182
|
-
|
|
183
|
-
- [`simple/simple.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/simple/simple.form.md)
|
|
184
|
-
\- Basic form demonstrating all field kinds
|
|
185
|
-
|
|
186
|
-
- [`earnings-analysis/earnings-analysis.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/earnings-analysis/earnings-analysis.form.md)
|
|
187
|
-
\- Financial analysis form
|
|
188
|
-
|
|
189
|
-
View them with `markform examples --list` or try them interactively.
|
|
190
|
-
|
|
191
|
-
## Supported Providers
|
|
192
|
-
|
|
193
|
-
Standard LLMs can be used to fill in forms or create research reports from form
|
|
194
|
-
templates. The package currently has support for these models built in, and enables web
|
|
195
|
-
search tools for them if possible.
|
|
182
|
+
The package includes example forms.
|
|
183
|
+
View them with `markform examples --list` or try these interactively:
|
|
196
184
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
| openai | `OPENAI_API_KEY` | gpt-5-mini, gpt-5.1, gpt-5.2 |
|
|
200
|
-
| anthropic | `ANTHROPIC_API_KEY` | claude-sonnet-4-5, claude-opus-4-5 |
|
|
201
|
-
| google | `GOOGLE_API_KEY` | gemini-2.5-pro, gemini-2.5-flash |
|
|
202
|
-
| xai | `XAI_API_KEY` | grok-4, grok-4-fast |
|
|
203
|
-
| deepseek | `DEEPSEEK_API_KEY` | deepseek-chat, deepseek-reasoner |
|
|
185
|
+
- [`simple.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/simple/simple.form.md)
|
|
186
|
+
\- Basic form demonstrating all field kinds.
|
|
204
187
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
[`src/settings.ts`](https://github.com/jlevy/markform/blob/main/packages/markform/src/settings.ts)
|
|
208
|
-
for the full list of models.
|
|
188
|
+
- [`movie-research-minimal.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-research-minimal.form.md)
|
|
189
|
+
\- The quick example above.
|
|
209
190
|
|
|
210
|
-
|
|
191
|
+
- [`movie-research-basic.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-research-basic.form.md)
|
|
192
|
+
\- Standard movie research with IMDB, Rotten Tomatoes, Metacritic.
|
|
211
193
|
|
|
212
|
-
|
|
213
|
-
|
|
194
|
+
- [`movie-research-deep.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/movie-research/movie-research-deep.form.md)
|
|
195
|
+
\- Comprehensive movie analysis with streaming, box office, analysis.
|
|
214
196
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
Humans have for centuries used paper forms to systematize and manage processes.
|
|
219
|
-
The key insight of Markform is that the most natural way to express the state and
|
|
220
|
-
context for a workflow is often *forms*. Just as Markdown is a transparent format for
|
|
221
|
-
documents, Markform is a transparent text format for structured information.
|
|
197
|
+
- [`earnings-analysis.form.md`](https://github.com/jlevy/markform/blob/main/packages/markform/examples/earnings-analysis/earnings-analysis.form.md)
|
|
198
|
+
\- Financial analysis form.
|
|
222
199
|
|
|
223
200
|
## CLI Commands
|
|
224
201
|
|
|
@@ -310,6 +287,25 @@ markform models
|
|
|
310
287
|
markform --help
|
|
311
288
|
```
|
|
312
289
|
|
|
290
|
+
## Supported Providers
|
|
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 |
|
|
303
|
+
|
|
304
|
+
Set the appropriate environment variable for your provider before running `markform
|
|
305
|
+
fill`. See
|
|
306
|
+
[`src/settings.ts`](https://github.com/jlevy/markform/blob/main/packages/markform/src/settings.ts)
|
|
307
|
+
for the full list of models.
|
|
308
|
+
|
|
313
309
|
## Architecture
|
|
314
310
|
|
|
315
311
|
```mermaid
|
|
@@ -317,7 +313,7 @@ flowchart LR
|
|
|
317
313
|
subgraph SPEC["<b>MARKFORM SPEC</b>"]
|
|
318
314
|
direction TB
|
|
319
315
|
|
|
320
|
-
subgraph L1["<b>LAYER 1: SYNTAX</b><br/>Markdoc tag syntax and frontmatter (form,
|
|
316
|
+
subgraph L1["<b>LAYER 1: SYNTAX</b><br/>Markdoc tag syntax and frontmatter (form, group, string-field, checkboxes, etc.)"]
|
|
321
317
|
end
|
|
322
318
|
|
|
323
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)"]
|
|
@@ -428,6 +424,19 @@ const result = await generateText({
|
|
|
428
424
|
|
|
429
425
|
## FAQ
|
|
430
426
|
|
|
427
|
+
### Can you say more why this is a good idea?
|
|
428
|
+
|
|
429
|
+
Yes! I’ve come to believe forms are a missing piece of the workflow problem with agents.
|
|
430
|
+
For deep research or complex multi-step workflows, key pieces need to be *precisely
|
|
431
|
+
controlled*, *domain-specific*, and *always improving*. You need precise documentation
|
|
432
|
+
on the key intermediate states and final output from an AI pipeline.
|
|
433
|
+
|
|
434
|
+
But you don’t want structure in a GUI (not token friendly) or code (hard to update) or
|
|
435
|
+
dependent on the whims of a thinking model (changes all the time).
|
|
436
|
+
Forms define these pieces and are easy to edit.
|
|
437
|
+
All other choices can be left to the agents themselves, with the structure and
|
|
438
|
+
validations enforced by the form-filling tools the agents use.
|
|
439
|
+
|
|
431
440
|
### Is this mature?
|
|
432
441
|
|
|
433
442
|
No! I just wrote it.
|
|
@@ -480,7 +489,8 @@ This enables powerful AI workflows that assemble information in a defined struct
|
|
|
480
489
|
|
|
481
490
|
### Does anything like this already exist?
|
|
482
491
|
|
|
483
|
-
Not
|
|
492
|
+
Not that I have seen.
|
|
493
|
+
The closest alternatives are:
|
|
484
494
|
|
|
485
495
|
- Plain Markdown docs can be used as templates and filled in by agents.
|
|
486
496
|
These are more expressive, but it is hard to edit them programmatically or use LLMs to
|
|
@@ -494,13 +504,13 @@ Not really. The closest alternatives are:
|
|
|
494
504
|
human-friendly UI. But these do not have a human-friendly text format for use by
|
|
495
505
|
agents as well as humans.
|
|
496
506
|
|
|
497
|
-
| Approach | Has GUI | Human-readable
|
|
507
|
+
| Approach | Has GUI | Human-readable source format | Agent-editable | APIs and validation rules |
|
|
498
508
|
| --- | :---: | :---: | :---: | :---: |
|
|
499
509
|
| Plain Markdown | ☑️ existing tools | ✅ | ⚠️ fragile | ❌ |
|
|
500
|
-
| JSON with schema |
|
|
510
|
+
| JSON with schema | ⚠️ in some apps | ⚠️ um it’s JSON | ✅ | ✅ |
|
|
501
511
|
| SaaS tools (Typeform, Docusign, PDF forms) | ✅ | ⚠️ rarely | ⚠️ if they add it | ⚠️ if they add it |
|
|
502
|
-
| Excel/Google Sheets | ✅ | ❌ .xlsx | ⚠️ with tools | ✅ |
|
|
503
|
-
| **Markform** | ☑️ existing tools | ✅ | ✅ | ✅ |
|
|
512
|
+
| Excel/Google Sheets | ✅ | ❌ .csv (poor) or .xlsx (worse) | ⚠️ with tools | ✅ with some coding |
|
|
513
|
+
| **Markform** | ☑️ existing tools | ✅ | ✅ with this package | ✅ with this package |
|
|
504
514
|
|
|
505
515
|
### What are example use cases?
|
|
506
516
|
|
package/dist/ai-sdk.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { Dt as PatchSchema, Et as Patch, G as FormSchema, Tt as ParsedForm, X as Id, ar as ValidatorRegistry, r as ApplyResult, tt as InspectResult, z as FieldResponse } from "./coreTypes-
|
|
3
|
+
import { Dt as PatchSchema, Et as Patch, G as FormSchema, Tt as ParsedForm, X as Id, ar as ValidatorRegistry, r as ApplyResult, tt as InspectResult, z as FieldResponse } from "./coreTypes-9XZSNOv6.mjs";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
//#region src/integrations/toolTypes.d.ts
|
package/dist/ai-sdk.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { L as PatchSchema } from "./coreTypes-pyctKRgc.mjs";
|
|
2
|
-
import { r as inspect, t as applyPatches, u as serialize } from "./apply-
|
|
2
|
+
import { r as inspect, t as applyPatches, u as serialize } from "./apply-DMQl-VVd.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
5
|
//#region src/integrations/vercelAiSdkTools.ts
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
|
|
3
1
|
//#region src/llms.ts
|
|
4
2
|
/**
|
|
5
3
|
* Parse a model ID string into provider and model components for display.
|
|
@@ -113,13 +111,6 @@ function getWebSearchConfig(provider) {
|
|
|
113
111
|
//#endregion
|
|
114
112
|
//#region src/settings.ts
|
|
115
113
|
/**
|
|
116
|
-
* Global settings and constants for Markform.
|
|
117
|
-
*
|
|
118
|
-
* This file consolidates non-changing default values that were previously
|
|
119
|
-
* scattered across the codebase. These are NOT runtime configurable - they
|
|
120
|
-
* are compile-time constants.
|
|
121
|
-
*/
|
|
122
|
-
/**
|
|
123
114
|
* The current Markform spec version in full notation (e.g., "MF/0.1").
|
|
124
115
|
* This is distinct from npm package version and tracks the format that
|
|
125
116
|
* .form.md files conform to.
|
|
@@ -165,25 +156,14 @@ function parseRolesFlag(raw) {
|
|
|
165
156
|
*/
|
|
166
157
|
const DEFAULT_PRIORITY = "medium";
|
|
167
158
|
/**
|
|
168
|
-
* The default port for the serve command.
|
|
169
|
-
*/
|
|
170
|
-
const DEFAULT_PORT = 3344;
|
|
171
|
-
/**
|
|
172
159
|
* Default forms directory for CLI output (relative to cwd).
|
|
173
160
|
* Commands write form outputs here to avoid cluttering the workspace.
|
|
174
161
|
*/
|
|
175
162
|
const DEFAULT_FORMS_DIR = "./forms";
|
|
176
163
|
/**
|
|
177
|
-
*
|
|
178
|
-
* Uses the provided override or falls back to DEFAULT_FORMS_DIR.
|
|
179
|
-
*
|
|
180
|
-
* @param override Optional override path from CLI --forms-dir option
|
|
181
|
-
* @param cwd Base directory for resolving relative paths (defaults to process.cwd())
|
|
182
|
-
* @returns Absolute path to the forms directory
|
|
164
|
+
* The default port for the serve command.
|
|
183
165
|
*/
|
|
184
|
-
|
|
185
|
-
return resolve(cwd, override ?? DEFAULT_FORMS_DIR);
|
|
186
|
-
}
|
|
166
|
+
const DEFAULT_PORT = 3344;
|
|
187
167
|
/**
|
|
188
168
|
* Default maximum turns for the fill harness.
|
|
189
169
|
* Prevents runaway loops during agent execution.
|
|
@@ -811,7 +791,7 @@ function serializeNotes(notes) {
|
|
|
811
791
|
/**
|
|
812
792
|
* Serialize a field group.
|
|
813
793
|
* Implicit groups (fields placed directly under the form) are serialized
|
|
814
|
-
* without the
|
|
794
|
+
* without the group wrapper tags.
|
|
815
795
|
*/
|
|
816
796
|
function serializeFieldGroup(group, responses, docs) {
|
|
817
797
|
const lines = [];
|
|
@@ -821,7 +801,7 @@ function serializeFieldGroup(group, responses, docs) {
|
|
|
821
801
|
if (group.validate) attrs.validate = group.validate;
|
|
822
802
|
if (group.report !== void 0) attrs.report = group.report;
|
|
823
803
|
const attrStr = serializeAttrs(attrs);
|
|
824
|
-
lines.push(`{%
|
|
804
|
+
lines.push(`{% group ${attrStr} %}`);
|
|
825
805
|
}
|
|
826
806
|
const docsByRef = /* @__PURE__ */ new Map();
|
|
827
807
|
for (const doc of docs) {
|
|
@@ -840,7 +820,7 @@ function serializeFieldGroup(group, responses, docs) {
|
|
|
840
820
|
}
|
|
841
821
|
if (!group.implicit) {
|
|
842
822
|
lines.push("");
|
|
843
|
-
lines.push("{% /
|
|
823
|
+
lines.push("{% /group %}");
|
|
844
824
|
}
|
|
845
825
|
return lines.join("\n");
|
|
846
826
|
}
|
|
@@ -2824,4 +2804,4 @@ function applyPatches(form, patches) {
|
|
|
2824
2804
|
}
|
|
2825
2805
|
|
|
2826
2806
|
//#endregion
|
|
2827
|
-
export {
|
|
2807
|
+
export { SUGGESTED_LLMS as A, DEFAULT_ROLE_INSTRUCTIONS as C, deriveReportPath as D, deriveExportPath as E, parseModelIdForDisplay as F, formatSuggestedLlms as M, getWebSearchConfig as N, detectFileType as O, hasWebSearchSupport as P, DEFAULT_ROLES as S, USER_ROLE as T, DEFAULT_MAX_TURNS as _, computeAllSummaries as a, DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN as b, computeStructureSummary as c, serializeRawMarkdown as d, serializeReportMarkdown as f, DEFAULT_MAX_PATCHES_PER_TURN as g, DEFAULT_MAX_ISSUES_PER_TURN as h, validate as i, WEB_SEARCH_CONFIG as j, parseRolesFlag as k, isFormComplete as l, DEFAULT_FORMS_DIR as m, getFieldsForRoles as n, computeFormState as o, AGENT_ROLE as p, inspect as r, computeProgressSummary as s, applyPatches as t, serialize as u, DEFAULT_PORT as v, REPORT_EXTENSION as w, DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN as x, DEFAULT_PRIORITY as y };
|
package/dist/bin.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import "./coreTypes-pyctKRgc.mjs";
|
|
3
|
-
import "./apply-
|
|
4
|
-
import "./src-
|
|
3
|
+
import "./apply-DMQl-VVd.mjs";
|
|
4
|
+
import "./src-o_5TSoHQ.mjs";
|
|
5
5
|
import "./session-uF0e6m6k.mjs";
|
|
6
|
-
import "./
|
|
7
|
-
import
|
|
6
|
+
import { t as runCli } from "./cli-CXjkdym_.mjs";
|
|
7
|
+
import "./shared-DRlgu2ZJ.mjs";
|
|
8
8
|
import { resolve } from "node:path";
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
10
10
|
import { config } from "dotenv";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { L as PatchSchema } from "./coreTypes-pyctKRgc.mjs";
|
|
2
|
-
import { A as
|
|
3
|
-
import { a as resolveHarnessConfig, c as getProviderNames, f as createMockAgent, i as runResearch, l as resolveModel, m as createHarness, s as getProviderInfo, t as VERSION, u as createLiveAgent, w as parseForm } from "./src-
|
|
2
|
+
import { A as SUGGESTED_LLMS, D as deriveReportPath, E as deriveExportPath, F as parseModelIdForDisplay, M as formatSuggestedLlms, O as detectFileType, P as hasWebSearchSupport, T as USER_ROLE, _ as DEFAULT_MAX_TURNS, b as DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN, d as serializeRawMarkdown, f as serializeReportMarkdown, g as DEFAULT_MAX_PATCHES_PER_TURN, h as DEFAULT_MAX_ISSUES_PER_TURN, j as WEB_SEARCH_CONFIG, k as parseRolesFlag, m as DEFAULT_FORMS_DIR, p as AGENT_ROLE, r as inspect, t as applyPatches, u as serialize, v as DEFAULT_PORT, w as REPORT_EXTENSION, x as DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN } from "./apply-DMQl-VVd.mjs";
|
|
3
|
+
import { a as resolveHarnessConfig, c as getProviderNames, f as createMockAgent, i as runResearch, l as resolveModel, m as createHarness, s as getProviderInfo, t as VERSION, u as createLiveAgent, w as parseForm } from "./src-o_5TSoHQ.mjs";
|
|
4
4
|
import { n as serializeSession } from "./session-uF0e6m6k.mjs";
|
|
5
|
-
import { a as formatPath, c as logError, d as logTiming, f as logVerbose, g as writeFile, i as formatOutput, l as logInfo, m as readFile$1, n as createSpinner, o as getCommandContext, p as logWarn, r as ensureFormsDir, s as logDryRun, t as OUTPUT_FORMATS, u as logSuccess } from "./shared-
|
|
5
|
+
import { a as formatPath, c as logError, d as logTiming, f as logVerbose, g as writeFile, i as formatOutput, l as logInfo, m as readFile$1, n as createSpinner, o as getCommandContext, p as logWarn, r as ensureFormsDir, s as logDryRun, t as OUTPUT_FORMATS, u as logSuccess } from "./shared-DRlgu2ZJ.mjs";
|
|
6
6
|
import YAML from "yaml";
|
|
7
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
8
7
|
import { Command } from "commander";
|
|
9
8
|
import pc from "picocolors";
|
|
9
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
10
10
|
import { existsSync, readFileSync } from "node:fs";
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
12
12
|
import { readFile } from "node:fs/promises";
|
|
@@ -14,6 +14,27 @@ import * as p from "@clack/prompts";
|
|
|
14
14
|
import { exec, spawn } from "node:child_process";
|
|
15
15
|
import { createServer } from "node:http";
|
|
16
16
|
|
|
17
|
+
//#region src/cli/lib/paths.ts
|
|
18
|
+
/**
|
|
19
|
+
* Path utilities for CLI commands.
|
|
20
|
+
*
|
|
21
|
+
* This module contains Node.js-dependent path utilities that are only used
|
|
22
|
+
* by CLI code. Keeping these separate from settings.ts allows the core
|
|
23
|
+
* library to remain Node.js-free.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the forms directory path to an absolute path.
|
|
27
|
+
* Uses the provided override or falls back to DEFAULT_FORMS_DIR.
|
|
28
|
+
*
|
|
29
|
+
* @param override Optional override path from CLI --forms-dir option
|
|
30
|
+
* @param cwd Base directory for resolving relative paths (defaults to process.cwd())
|
|
31
|
+
* @returns Absolute path to the forms directory
|
|
32
|
+
*/
|
|
33
|
+
function getFormsDir(override, cwd = process.cwd()) {
|
|
34
|
+
return resolve(cwd, override ?? DEFAULT_FORMS_DIR);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
17
38
|
//#region src/cli/commands/apis.ts
|
|
18
39
|
/**
|
|
19
40
|
* Get the path to the markform-apis.md file.
|
|
@@ -684,12 +705,6 @@ const EXAMPLE_DEFINITIONS = [
|
|
|
684
705
|
filename: "startup-deep-research.form.md",
|
|
685
706
|
path: "startup-deep-research/startup-deep-research.form.md",
|
|
686
707
|
type: "research"
|
|
687
|
-
},
|
|
688
|
-
{
|
|
689
|
-
id: "celebrity-deep-research",
|
|
690
|
-
filename: "celebrity-deep-research.form.md",
|
|
691
|
-
path: "celebrity-deep-research/celebrity-deep-research.form.md",
|
|
692
|
-
type: "research"
|
|
693
708
|
}
|
|
694
709
|
];
|
|
695
710
|
/**
|
|
@@ -703,7 +718,7 @@ function getExamplesDir() {
|
|
|
703
718
|
}
|
|
704
719
|
/**
|
|
705
720
|
* Load the content of an example form.
|
|
706
|
-
* @param exampleId - The example ID (e.g., 'simple', '
|
|
721
|
+
* @param exampleId - The example ID (e.g., 'simple', 'movie-research-deep')
|
|
707
722
|
* @returns The form content as a string
|
|
708
723
|
* @throws Error if the example is not found
|
|
709
724
|
*/
|
|
@@ -725,7 +740,7 @@ function getExampleById(id) {
|
|
|
725
740
|
}
|
|
726
741
|
/**
|
|
727
742
|
* Get the absolute path to an example's source file.
|
|
728
|
-
* @param exampleId - The example ID (e.g., 'simple', '
|
|
743
|
+
* @param exampleId - The example ID (e.g., 'simple', 'movie-research-deep')
|
|
729
744
|
* @returns The absolute path to the example form file
|
|
730
745
|
* @throws Error if the example is not found
|
|
731
746
|
*/
|
|
@@ -750,7 +765,7 @@ function extractFrontmatter(content) {
|
|
|
750
765
|
}
|
|
751
766
|
/**
|
|
752
767
|
* Load metadata (title, description) from an example's YAML frontmatter.
|
|
753
|
-
* @param exampleId - The example ID (e.g., 'simple', '
|
|
768
|
+
* @param exampleId - The example ID (e.g., 'simple', 'movie-research-deep')
|
|
754
769
|
* @returns Object with title and description from frontmatter
|
|
755
770
|
*/
|
|
756
771
|
function loadExampleMetadata(exampleId) {
|
|
@@ -2107,6 +2122,39 @@ function registerExportCommand(program) {
|
|
|
2107
2122
|
});
|
|
2108
2123
|
}
|
|
2109
2124
|
|
|
2125
|
+
//#endregion
|
|
2126
|
+
//#region src/cli/lib/fillCallbacks.ts
|
|
2127
|
+
/**
|
|
2128
|
+
* Create FillCallbacks for CLI commands.
|
|
2129
|
+
*
|
|
2130
|
+
* Provides spinner feedback during tool execution (especially web search).
|
|
2131
|
+
* Only implements tool callbacks - turn/LLM callbacks are handled by CLI's
|
|
2132
|
+
* own logging which has richer context.
|
|
2133
|
+
*
|
|
2134
|
+
* @param spinner - Active spinner handle to update
|
|
2135
|
+
* @param ctx - Command context for verbose logging
|
|
2136
|
+
* @returns FillCallbacks with onToolStart and onToolEnd
|
|
2137
|
+
*
|
|
2138
|
+
* @example
|
|
2139
|
+
* ```typescript
|
|
2140
|
+
* const spinner = createSpinner({ type: 'api', provider, model });
|
|
2141
|
+
* const callbacks = createCliToolCallbacks(spinner, ctx);
|
|
2142
|
+
* const agent = createLiveAgent({ model, callbacks, ... });
|
|
2143
|
+
* ```
|
|
2144
|
+
*/
|
|
2145
|
+
function createCliToolCallbacks(spinner, ctx) {
|
|
2146
|
+
return {
|
|
2147
|
+
onToolStart: ({ name }) => {
|
|
2148
|
+
if (name.includes("search")) spinner.message(`🔍 Web search...`);
|
|
2149
|
+
logVerbose(ctx, ` Tool started: ${name}`);
|
|
2150
|
+
},
|
|
2151
|
+
onToolEnd: ({ name, durationMs, error }) => {
|
|
2152
|
+
if (error) logVerbose(ctx, ` Tool ${name} failed: ${error} (${durationMs}ms)`);
|
|
2153
|
+
else logVerbose(ctx, ` Tool ${name} completed (${durationMs}ms)`);
|
|
2154
|
+
}
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2110
2158
|
//#endregion
|
|
2111
2159
|
//#region src/cli/commands/fill.ts
|
|
2112
2160
|
/**
|
|
@@ -2249,6 +2297,7 @@ function registerFillCommand(program) {
|
|
|
2249
2297
|
let mockPath;
|
|
2250
2298
|
let agentProvider;
|
|
2251
2299
|
let agentModelName;
|
|
2300
|
+
let currentSpinner = null;
|
|
2252
2301
|
if (options.mock) {
|
|
2253
2302
|
mockPath = resolve(options.mockSource);
|
|
2254
2303
|
logVerbose(ctx, `Reading mock source: ${mockPath}`);
|
|
@@ -2268,13 +2317,21 @@ function registerFillCommand(program) {
|
|
|
2268
2317
|
logVerbose(ctx, `Reading system prompt from: ${promptPath}`);
|
|
2269
2318
|
systemPrompt = await readFile$1(promptPath);
|
|
2270
2319
|
}
|
|
2320
|
+
const callbacks = createCliToolCallbacks({
|
|
2321
|
+
message: (msg) => currentSpinner?.message(msg),
|
|
2322
|
+
update: (context) => currentSpinner?.update(context),
|
|
2323
|
+
stop: (msg) => currentSpinner?.stop(msg),
|
|
2324
|
+
error: (msg) => currentSpinner?.error(msg),
|
|
2325
|
+
getElapsedMs: () => currentSpinner?.getElapsedMs() ?? 0
|
|
2326
|
+
}, ctx);
|
|
2271
2327
|
const primaryRole = targetRoles[0] === "*" ? AGENT_ROLE : targetRoles[0];
|
|
2272
2328
|
const liveAgent = createLiveAgent({
|
|
2273
2329
|
model,
|
|
2274
2330
|
provider,
|
|
2275
2331
|
systemPromptAddition: systemPrompt,
|
|
2276
2332
|
targetRole: primaryRole,
|
|
2277
|
-
enableWebSearch: true
|
|
2333
|
+
enableWebSearch: true,
|
|
2334
|
+
callbacks
|
|
2278
2335
|
});
|
|
2279
2336
|
agent = liveAgent;
|
|
2280
2337
|
logInfo(ctx, `Available tools: ${liveAgent.getAvailableToolNames().join(", ")}`);
|
|
@@ -2291,18 +2348,23 @@ function registerFillCommand(program) {
|
|
|
2291
2348
|
logInfo(ctx, `${pc.bold(`Turn ${stepResult.turnNumber}:`)} ${formatTurnIssues(stepResult.issues)}`);
|
|
2292
2349
|
while (!stepResult.isComplete && !harness.hasReachedMaxTurns()) {
|
|
2293
2350
|
let spinner = null;
|
|
2294
|
-
if (!options.mock && agentProvider && agentModelName && process.stdout.isTTY && !ctx.quiet)
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2351
|
+
if (!options.mock && agentProvider && agentModelName && process.stdout.isTTY && !ctx.quiet) {
|
|
2352
|
+
spinner = createSpinner({
|
|
2353
|
+
type: "api",
|
|
2354
|
+
provider: agentProvider,
|
|
2355
|
+
model: agentModelName,
|
|
2356
|
+
turnNumber: stepResult.turnNumber
|
|
2357
|
+
});
|
|
2358
|
+
currentSpinner = spinner;
|
|
2359
|
+
}
|
|
2300
2360
|
let response;
|
|
2301
2361
|
try {
|
|
2302
2362
|
response = await agent.generatePatches(stepResult.issues, harness.getForm(), harnessConfig.maxPatchesPerTurn);
|
|
2303
2363
|
spinner?.stop();
|
|
2364
|
+
currentSpinner = null;
|
|
2304
2365
|
} catch (error) {
|
|
2305
2366
|
spinner?.error("LLM call failed");
|
|
2367
|
+
currentSpinner = null;
|
|
2306
2368
|
throw error;
|
|
2307
2369
|
}
|
|
2308
2370
|
const { patches, stats } = response;
|
|
@@ -4043,7 +4105,7 @@ function registerResearchCommand(program) {
|
|
|
4043
4105
|
if (options.transcript && result.transcript) {
|
|
4044
4106
|
const { serializeSession: serializeSession$1 } = await import("./session-B_stoXQn.mjs");
|
|
4045
4107
|
const transcriptPath = outputPath.replace(/\.form\.md$/, ".session.yaml");
|
|
4046
|
-
const { writeFile: writeFile$1 } = await import("./shared-
|
|
4108
|
+
const { writeFile: writeFile$1 } = await import("./shared-u22MtBRo.mjs");
|
|
4047
4109
|
await writeFile$1(transcriptPath, serializeSession$1(result.transcript));
|
|
4048
4110
|
logInfo(ctx, `Transcript: ${transcriptPath}`);
|
|
4049
4111
|
}
|
package/dist/cli.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "./coreTypes-pyctKRgc.mjs";
|
|
2
|
-
import "./apply-
|
|
3
|
-
import "./src-
|
|
2
|
+
import "./apply-DMQl-VVd.mjs";
|
|
3
|
+
import "./src-o_5TSoHQ.mjs";
|
|
4
4
|
import "./session-uF0e6m6k.mjs";
|
|
5
|
-
import "./
|
|
6
|
-
import
|
|
5
|
+
import { t as runCli } from "./cli-CXjkdym_.mjs";
|
|
6
|
+
import "./shared-DRlgu2ZJ.mjs";
|
|
7
7
|
|
|
8
8
|
export { runCli };
|
|
@@ -195,8 +195,8 @@ interface FieldGroup {
|
|
|
195
195
|
report?: boolean;
|
|
196
196
|
/**
|
|
197
197
|
* True if this group was implicitly created for fields placed directly
|
|
198
|
-
* under the form (not wrapped in an explicit
|
|
199
|
-
* Implicit groups are serialized without
|
|
198
|
+
* under the form (not wrapped in an explicit group).
|
|
199
|
+
* Implicit groups are serialized without group wrapper tags.
|
|
200
200
|
*/
|
|
201
201
|
implicit?: boolean;
|
|
202
202
|
}
|