@turri-dev/cli 0.1.2 → 0.1.3-next.17f0f7f

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/docs/headless.md CHANGED
@@ -62,9 +62,14 @@ conformance-tests: true # write and run conformance tests where there
62
62
  ```
63
63
 
64
64
  The same file names the [testing scripts](regenerating.md#the-testing-scripts); every key is in
65
- [the project config](config.md). Options on the command
66
- line win over it; `turri regenerate`, `spec-diff` and `conform` take its
67
- `coding-model` too, and `--coding-model` means the same as `--model` on every one of them.
65
+ [the project config](config.md). `turri regenerate`, `spec-diff` and `conform` take five of its keys on
66
+ the command line too - `--harness-model`, `--coding-model` (`--model` is the same), `--judge-model`,
67
+ `--review` and, but for `conform`, `--conformance-tests`. Each is read as if the file had it, in its place:
68
+ it takes that key's value, and every key keeps its place in the order the config resolves them in - a
69
+ `models:` entry still wins over `harness-model`, `coding-model` or `judge-model`, the planner and the
70
+ conformance chooser run on the harness model, the judge falls back on the coding model. So a run with
71
+ the options is the same as one, or a chat, with those keys in `config.turri.yaml` - and a `config.yaml`
72
+ the project has is left as it is.
68
73
  A project whose parts are on different stacks keeps each stack's settings in
69
74
  `config.turri.<stack>.yaml` beside it ([stacks](config.md#stacks)). Choose the one to build with
70
75
  `--stack <name>` - on any command: `regenerate`, `conform`, `spec-diff`, `glimpse`, `release`, `cultivate` -
@@ -75,29 +80,39 @@ Useful options - `turri regenerate --help` lists all of them:
75
80
 
76
81
  | | |
77
82
  | --- | --- |
78
- | `--model <provider/id>` | the model every agent runs on (`--coding-model` too), with `:level` for its thinking (`provider/id:high`); default, `config.turri.yaml`'s `coding-model`, then its `harness-model`, then pi's configured model |
79
- | `--judge-model <provider/id>` | `conformance`: the model of the judge and the test review; default, `config.turri.yaml`'s `judge-model`, then the coding model |
83
+ | `--harness-model <provider/id>` | `harness-model`: the planner, the conformance chooser, the diagnosis of a failed pulse |
84
+ | `--coding-model <provider/id>` | `coding-model` (`--model` too): the agents that build - with `:level` for how hard it thinks (`provider/id:high`); not given, the harness model's |
85
+ | `--judge-model <provider/id>` | `judge-model`: the judge and the test review; not given, the coding model |
86
+ | `--conformance-tests <all\|chosen\|module\|false>` | `conformance-tests`, with its values (`true` is `all`) |
87
+ | `--review <true\|false>` | `review`: whether the code and its tests are reviewed once they pass |
80
88
  | `--stack <name>` | build with the stack's `config.turri.<name>.yaml` over `config.turri.yaml`; default, `TURRI_STACK`, else none |
81
- | `--to <frid>` | queue functionalities only up to this one |
82
- | `--only` | build just this module, not the ones it requires |
83
- | `--build-dir <dir>` | where the code goes; default `plain_modules/<module>/code` |
89
+ | `--start-over` | start over, unasked, a codebase built without a module it now requires - without a terminal, a run that needs it and lacks it stops |
90
+ | `--build-dir <dir>` | as the run ends, export the code there - see below |
91
+ | `--report <file.json>` | write [the run report](#the-run-report) there |
92
+
93
+ An option a command does not take is refused, not ignored.
84
94
 
85
95
  `turri regenerate` queues the jobs the chat's `regenerate` does - a spec diff that brings what
86
96
  is built in line, when it no longer matches the spec, then one render functionality job per
87
97
  functional spec not built, each with its conformance job - and runs them all to the end, the queue
88
98
  master reworking them as in the chat (see [a whole module](regenerating.md#a-whole-module)).
89
- Conformance tests follow `conformance-tests:` in `config.turri.yaml`.
99
+ Conformance tests follow `conformance-tests:` in `config.turri.yaml`, or `--conformance-tests`.
90
100
 
91
101
  ## In the morning
92
102
 
93
- The command exits `0` only when every module is complete. The log says, per functionality, what
94
- was built and whether its tests passed; the last lines summarise each module.
103
+ The command exits `0` only when every module is complete: its code regenerated from the spec as it is
104
+ now, and its conformance tests written for what is built when they are on - no pulse failed, nothing
105
+ halted, nothing waiting for a start over. Anything else exits `1`, and the last lines say which
106
+ module is not complete, and why. The log says, per functionality, what was built and whether its
107
+ tests passed.
95
108
 
96
109
  | | |
97
110
  | --- | --- |
98
111
  | `plain_modules/<module>/code/` | the code and unit tests |
99
112
  | `plain_modules/<module>/tests/` | the conformance tests |
100
113
  | `plain_modules/<module>/code/.turri.json` | the marshalled plain tree the code is built from |
114
+ | `.turri/jobs/` | each pulse's log: its agents, its test runs, how it ended |
115
+ | `.turri/debug/` | each agent's whole conversation |
101
116
 
102
117
  And what happened, step by step, in the module's history:
103
118
 
@@ -105,13 +120,112 @@ And what happened, step by step, in the module's history:
105
120
  git --git-dir=plain_modules/<module>/.plaingit --work-tree=plain_modules/<module> log --stat
106
121
  ```
107
122
 
123
+ ### Where the code is
124
+
125
+ A module that `requires` others is built in its own codebase on top of theirs: the code of
126
+ `plain_modules/<module>/code` holds every module below it. `--build-dir <dir>` exports it as the run
127
+ ends, however it ends - done, failed, stopped by a signal: the last commit of the furthest-built
128
+ codebase of the chain, with the harness's own files (`.turri.json`, the notes) left out, so what is in
129
+ it is only what landed, its unit tests passing. The folder's contents are replaced each time; a
130
+ folder that holds the project, or one inside `plain_modules`, is refused. Point an external test
131
+ suite at it.
132
+
133
+ ### The run report
134
+
135
+ `--report <file.json>` writes one JSON summary of the run, for a benchmark or CI to read rather than
136
+ the log. It is written as the run starts, again as each pulse ends, and last as the process exits -
137
+ whether the run is done, a pulse failed, the provider failed, it crashed, or `SIGTERM` / `SIGINT` /
138
+ `SIGHUP` stopped it. Only a process killed outright (`SIGKILL`) keeps the one written as its last pulse
139
+ ended. Each write replaces the file whole (written beside it, then renamed), so a reader never sees
140
+ half of one. Keys are snake_case, numbers are numbers, and `schema_version` changes only when a field
141
+ changes meaning or goes; new fields may appear in the same version. Paths in `pulses`,
142
+ `functionalities` and `logs` are relative to `project_dir`.
143
+
144
+ ```jsonc
145
+ {
146
+ "schema_version": 1,
147
+ "turri": { "version": "0.1.1", "git_commit": "48dacfc…", "git_dirty": false }, // commit: null outside a clone
148
+ "command": ["regenerate", "app.plain", "--model", "…", "--report", "report.json"],
149
+ "spec_file": "/work/app.plain", "project_dir": "/work", "build_dir": "/work/out", // build_dir: null without one
150
+ "conformance_tests": "all", // all | chosen | module | false
151
+ "review": false,
152
+ "models": {
153
+ "harness": { "model": "…", "thinking": "medium" }, // the planner, the chooser, the diagnosis
154
+ "coding": { "model": "…", "thinking": "low" },
155
+ "judge": { "model": "…", "thinking": "medium" },
156
+ "observed": { "coding": ["provider/id:low"], "planner": ["provider/id:medium"] } // what each role really ran on
157
+ },
158
+ "started_at": "…", "finished_at": "…", "duration_seconds": 4351.4,
159
+ "outcome": {
160
+ "status": "completed", // completed | incomplete | failed | interrupted | crashed | running (written mid-run)
161
+ "exit_code": 0, "signal": null, "error": null,
162
+ "modules": [{ "module": "app", "state": "complete" }] // complete | incomplete | halted | start_over_needed | running, with "why"
163
+ },
164
+ "usage": {
165
+ "total": {
166
+ "input_tokens": 0, "output_tokens": 0, "cache_read_tokens": 0, "cache_write_tokens": 0, "cost_usd": 0,
167
+ "llm_calls": 0, "agent_turns": 0, "compactions": 0, "model_seconds": 0
168
+ },
169
+ "in_pulses": { }, // the pulses' usage added up - the same fields
170
+ "outside_pulses": { }, // the planner, the conformance chooser, a failed pulse's diagnosis
171
+ "by_model": { "provider/id:low": { } },
172
+ "by_role": { "coding": { }, "reviewer": { }, "planner": { } },
173
+ "by_pulse_kind": { "code": { }, "conformance": { } }
174
+ },
175
+ "pulses": {
176
+ "counts": { "total": 7, "landed": 5, "failed": 1, "stopped": 0, "rolled_back": 2, "running": 0, "not_started": 0 },
177
+ "list": [{
178
+ "id": "2a058729", "module": "app", "number": 1, "kind": "code", "title": "…",
179
+ "status": "succeeded", "landed": true, "failed_for_good": false,
180
+ "started_at": "…", "duration_seconds": 512.3, "usage": { },
181
+ "agents": [{ "label": "app 1 coding agent", "model": "…", "turns": 14, "compactions": 0, "input_tokens": 0, "…": 0 }],
182
+ "log_file": ".turri/jobs/….log", "error": null
183
+ }]
184
+ },
185
+ "functionalities": {
186
+ "total": { "specified": 12, "completed": 10, "conformance_tested": 8 },
187
+ "modules": [
188
+ { "module": "base", "specified": 4, "completed": 4, "conformance_tested": 4, "incomplete_frids": [] },
189
+ { "module": "app", "specified": 8, "completed": 6, "conformance_tested": 4, "incomplete_frids": ["7", "8"] }
190
+ ],
191
+ "codebases": ["plain_modules/app/code"]
192
+ },
193
+ "build_dir_export": { "exported": true, "dir": "/work/out", "module": "app", "commit": "8036b3b", "files": 23 },
194
+ "logs": { "jobs_dir": ".turri/jobs", "debug_dir": ".turri/debug", "sessions_dir": ".turri/sessions" }
195
+ }
196
+ ```
197
+
198
+ **Usage.** Every model response an agent gets counts, in a pulse or not. `total` is `in_pulses` plus
199
+ `outside_pulses`, and `in_pulses` is the pulses' own usage added up - the figures their lines in the
200
+ notes and the pulse logs print - so the totals can be checked by hand against them. `llm_calls` counts
201
+ model responses, a compaction's included; `agent_turns` the harness's requests to an agent, each as
202
+ many responses as it takes; `model_seconds` the time from each request to its response's end, added
203
+ up - several agents working at once each count theirs, so it can exceed the run's duration. Tokens and
204
+ cost are as the provider reports them to pi. `by_model`, `by_role` and `by_pulse_kind` split the same
205
+ calls; a role or model turri could not tell is `unknown`.
206
+
207
+ **Pulses.** `landed`: its work is committed. `rolled_back`: it ended without landing - failed,
208
+ stopped, or did not pass - and everything it wrote was undone. A pulse that failed is tried once more
209
+ before it counts as failed; both tries are the one pulse.
210
+
211
+ **Completed functional specs** - the pass rate, the figure to compare runs by.
212
+ A functional spec is completed when the code record of the codebase the run delivers - the
213
+ furthest-built of the chain, the one `--build-dir` exports - as last committed, holds it as the spec has
214
+ it now: the same text, every file it links holding the same, no definition it uses changed, and not
215
+ marked to regenerate again. A record moves only when a pulse lands, and a pulse lands only once the
216
+ code's unit tests pass (its conformance tests too, when they are on), so completed means landed and
217
+ passing. Each module counts its own functional specs: a module below the tip counts from what the
218
+ tip's code was built on, and a module above the delivered codebase has none completed.
219
+ `conformance_tested` is those of the completed ones whose conformance tests are recorded as written
220
+ for them as they are now (`plain_modules/<module>/tests/.turri.json`).
221
+
108
222
  ## If it stopped
109
223
 
110
224
  Run the same command again. Functionalities already built from the current spec are left alone,
111
225
  and modules that are complete are skipped, so it picks up where it stopped - after a crash, a
112
226
  provider outage, or a machine that went to sleep anyway. Whatever the stopped run left
113
227
  half-written is undone first: each step starts from the module's last commit. A functionality whose spec has changed
114
- since counts as not built, so editing the spec and re-running rebuilds what changed. `--force`
228
+ since counts as not built, so editing the spec and re-running rebuilds what changed. `--from-scratch`
115
229
  rebuilds everything.
116
230
 
117
231
  ## Rebuilding one functionality
@@ -125,14 +239,46 @@ it is - without rebuilding the ones before it. To change what it does, change it
125
239
  spec is what the code is built from, and a change made there lasts. Then it updates the
126
240
  functionality's conformance tests and runs every conformance test in the codebase, since a change can break something built
127
241
  later as easily as something built earlier. It works in the codebase the functionality now lives
128
- in: when later modules are built on top of this one, the furthest-built of them (`--in <module>`
129
- picks one when there are several). It exits `0` when everything passes, and refuses when there is
130
- no code yet - build it with `module` first.
242
+ in: when later modules are built on top of this one, the furthest-built of them. It exits `0` when
243
+ everything passes, and refuses when there is no code yet - regenerate the module first.
131
244
 
132
245
  Several changes do not need one command each: after editing the spec, re-running `turri regenerate`
133
246
  brings the code up to it - one spec diff for what changed in what is built, then the functional
134
247
  specs not built yet.
135
248
 
249
+ ## In a container
250
+
251
+ A benchmark or CI task runs it with no terminal and nothing to ask, so give it everything up front:
252
+
253
+ ```bash
254
+ turri regenerate app.plain --harness-model amazon-bedrock/eu.anthropic.claude-opus-5-5 \
255
+ --coding-model amazon-bedrock/eu.anthropic.claude-sonnet-5-5 \
256
+ --conformance-tests all --review false --judge-model amazon-bedrock/eu.anthropic.claude-opus-5-5 \
257
+ --start-over --build-dir /out/build --report /out/report.json < /dev/null
258
+ ```
259
+
260
+ - **What it needs:** Node 22.19 or newer, `bash`, `git` (each module's history, and the spec diffs
261
+ the work is planned from), `/bin/sh` with `kill` and `sleep`, a writable `HOME` (pi keeps
262
+ `~/.pi/agent/auth.json` there, even with no login - or set `PI_CODING_AGENT_DIR`) and `TMPDIR`, and
263
+ the toolchains the project's testing scripts and implementation reqs name. `turri doctor --no-models`
264
+ in the project checks them; `--no-models` because a `config.yaml` written for another tool names no models, and
265
+ doctor would count that against it.
266
+ - **Without bubblewrap:** a container that cannot make user namespaces - no
267
+ `--security-opt seccomp=unconfined` - has no OS sandbox. turri runs anyway, the command check and the
268
+ audit holding alone under the agents' commands; `turri doctor` shows it as `!`, not `✗`. Installing
269
+ the `bubblewrap` package is worth it where it can start.
270
+ - **As another user:** run it as the user who will own `plain_modules/`. git refuses a repository
271
+ another user made (`dubious ownership`), so do not let root create it first.
272
+ - **Amazon Bedrock:** `AWS_BEARER_TOKEN_BEDROCK` (or the usual AWS credentials) and `AWS_REGION` are
273
+ enough - no pi login. An `eu.` inference profile needs an EU region. A model id pi does not know
274
+ stops the first pulse; `turri --list-models bedrock` lists the ones it does.
275
+ - **A project with only a `config.yaml`:** it is read while there is no `config.turri*.yaml` -
276
+ `unittests-script`, `conformance-tests-script` and `prepare-environment-script` included, relative to
277
+ the project; the keys turri does not know are ignored. Do not write a `config.turri.yaml` beside it to
278
+ turn on conformance tests or review: from then on `config.yaml` is not read. The options above do it instead.
279
+ - **Long test suites:** a testing script run is stopped after 120 seconds; set `TURRI_COMMAND_TIMEOUT`
280
+ (seconds) for more.
281
+
136
282
  ## Asking in words instead
137
283
 
138
284
  pi's print mode runs one request and exits, with the render tools waiting for their work rather
package/docs/parser.md CHANGED
@@ -9,7 +9,7 @@
9
9
  | `src/plain/spec.ts` | `plain_spec.py` - functionality ids, the per-functionality context, linked resources, ranges |
10
10
  | `src/plain/concepts.ts` | `concept_utils.py` - concept extraction, validation, topological ordering |
11
11
  | `src/plain/markdown.ts` | mistletoe's AST + `PlainRenderer` (mdast + a matching serializer) |
12
- | `src/plain/liquid.ts` | `plain2code_nodes.py` + the two filters in `plain_spec.py` (liquidjs) |
12
+ | `src/plain/liquid.ts` | the reference implementation's Liquid nodes + the two filters in `plain_spec.py` (liquidjs) |
13
13
  | `src/plain/files.ts` | `file_utils.open_from` / `get_loaded_templates`, base64 blob detection |
14
14
  | `src/plain/stdlib.ts` | nothing - turri's own: `stdlib/` is a reserved import prefix, read from turri's standard library only, and a project module at such a path is an error (see [the standard library](authoring-plain.md#the-standard-library)) |
15
15
 
@@ -495,7 +495,7 @@ scripts are not linked from the specs; a link to one is ignored.
495
495
  They are required: a build with no unit testing script, or with conformance on and no conformance
496
496
  testing script, stops before it starts and says which skill adds it.
497
497
 
498
- **The prepare script** is codeplain's, and optional: turri never writes one, and runs one the project has -
498
+ **The prepare script** is optional: turri never writes one, and runs one the project has -
499
499
  `prepare-environment-script:` in the config, else the one `prepare_environment*.sh` at the root, else in
500
500
  `test_scripts/` - on the build, before every run of the conformance testing script: the harness's own, and the agents'
501
501
  `run_conformance_tests`. Both get the same build folder - the code in place, or the same fresh copy - so what it sets up
@@ -685,7 +685,7 @@ change to the code brings that module's code in, so the codebase stops: it has t
685
685
  and conformance tests taken out, committed so its history keeps them, and the chain built again from the
686
686
  bottom. That is never done unasked. In the chat the model asks the person, saying why, and calls
687
687
  `regenerate` with `from_scratch` once they agree; `turri regenerate` and `spec-diff` ask at the terminal, and
688
- `--start-over` agrees in advance - with no terminal and no flag, nothing more is built there.
688
+ `--start-over` agrees in advance - with no terminal and no flag, the run stops there, exit 1, saying so.
689
689
 
690
690
  ## Conformance tests
691
691
 
@@ -36,7 +36,7 @@ bash run_conformance_tests_<lang>.sh <build_folder> <conformance_tests_folder>
36
36
 
37
37
  Both arguments are absolute paths: `<build_folder>` is the module's generated code (`plain_modules/<module>/code`), and `<conformance_tests_folder>` is the folder holding the whole suite. The harness runs the script on the person's machine - macOS, Linux or WSL, with its toolchains, in the person's own environment - and its exit code alone decides whether the conformance tests pass.
38
38
 
39
- Every conformance script is self-contained: toolchain check, argument validation, stage the build into `<temp>/<lang>_<basename of $1>`, install dependencies there in isolation, check the tests in `$2` statically, run the suite in `$2` against it, clean up on exit. Nothing is prepared ahead of time by another script - unless the project already has a `prepare_environment*.sh` (codeplain's prepare script, or one `prepare-environment-script:` names): turri runs it on the same build before every run of the conformance script, so keep it, and let the conformance script rely on what it sets up rather than doing it again. Never write a prepare script yourself.
39
+ Every conformance script is self-contained: toolchain check, argument validation, stage the build into `<temp>/<lang>_<basename of $1>`, install dependencies there in isolation, check the tests in `$2` statically, run the suite in `$2` against it, clean up on exit. Nothing is prepared ahead of time by another script - unless the project already has a `prepare_environment*.sh` (a prepare script the project brought, or one `prepare-environment-script:` names): turri runs it on the same build before every run of the conformance script, so keep it, and let the conformance script rely on what it sets up rather than doing it again. Never write a prepare script yourself.
40
40
 
41
41
  ## How conformance scripts differ from unit-test scripts
42
42
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turri-dev/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3-next.17f0f7f",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {