@turri-dev/cli 0.1.4-next.5a94e68 → 0.1.4-next.67734c0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +39 -127
  2. package/dist/extension.js +11 -17
  3. package/dist/regenerate/side-panel.js +1 -1
  4. package/docs/README.md +5 -12
  5. package/docs/authoring-plain.md +37 -119
  6. package/docs/config.md +50 -176
  7. package/docs/deploy.md +21 -80
  8. package/docs/development.md +13 -41
  9. package/docs/headless.md +30 -136
  10. package/docs/installation.md +13 -40
  11. package/docs/parser.md +15 -43
  12. package/docs/regenerating.md +231 -964
  13. package/docs/sandbox.md +38 -152
  14. package/docs/skills-and-gates.md +50 -173
  15. package/docs/theming.md +45 -46
  16. package/docs/using.md +66 -356
  17. package/genome/rules/bullet-continuation.md +5 -13
  18. package/genome/rules/concise-specs-examples.md +2 -4
  19. package/genome/rules/concise-specs-integration-examples.md +2 -6
  20. package/genome/rules/concise-specs.md +1 -2
  21. package/genome/rules/func-specs.md +1 -0
  22. package/genome/rules/integration-embedded-testing.md +1 -4
  23. package/genome/rules/integrations.md +1 -2
  24. package/genome/rules/project-model.md +14 -35
  25. package/genome/rules/stdlib.md +14 -49
  26. package/genome/rules/turri-regeneration.md +48 -0
  27. package/genome/skills/add-implementation-requirement/SKILL.md +1 -2
  28. package/genome/skills/analyze-if-func-spec-too-complex/SKILL.md +3 -8
  29. package/genome/skills/brood/SKILL.md +37 -127
  30. package/genome/skills/brood/references/intent.md +4 -12
  31. package/genome/skills/brood/references/product.md +24 -76
  32. package/genome/skills/brood/references/stack.md +7 -29
  33. package/genome/skills/brood/references/testing.md +11 -42
  34. package/genome/skills/bud/SKILL.md +32 -109
  35. package/genome/skills/bud/references/intent.md +4 -11
  36. package/genome/skills/implement-conformance-testing-script/SKILL.md +3 -6
  37. package/genome/skills/implement-unit-testing-script/SKILL.md +1 -2
  38. package/genome/skills/mature/SKILL.md +21 -69
  39. package/genome/skills/spawn/SKILL.md +28 -113
  40. package/package.json +1 -1
package/docs/headless.md CHANGED
@@ -10,15 +10,9 @@ turri doctor
10
10
 
11
11
  Everything marked `✗` stops the run, so fix those first. Two more things matter for a long run:
12
12
 
13
- - **The toolchains.** The agents use this machine's: run `turri doctor` in the project, and it says which toolchain
14
- a module's implementation reqs name that this machine lacks.
15
- - **The models.** In a project, `turri doctor` sends each model `config.turri.yaml` names one short request, as turri's
16
- agents will - so a key the provider refuses, a model pi does not know or an account without access to it shows now,
17
- not an hour into the run. Models the run is given on the command line are given to doctor the same way -
18
- `--harness-model`, `--coding-model`, `--judge-model`, read as the config's keys - and are checked with the config's.
19
- - **Credentials that do not expire mid-run.** An API key in the environment
20
- (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, ...) or pi's saved login - whatever
21
- `doctor` shows as the model provider.
13
+ - **The toolchains.** The agents use this machine's: run `turri doctor` in the project, and it says which toolchain a module's implementation reqs name that this machine lacks.
14
+ - **The models.** In a project, `turri doctor` sends each model `config.turri.yaml` names one short request, as turri's agents will - so a key the provider refuses, a model pi does not know or an account without access to it shows now, not an hour into the run. Models the run is given on the command line are given to doctor the same way - `--harness-model`, `--coding-model`, `--judge-model`, read as the config's keys - and are checked with the config's.
15
+ - **Credentials that do not expire mid-run.** An API key in the environment (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, ...) or pi's saved login - whatever `doctor` shows as the model provider.
22
16
 
23
17
  Check the spec parses, without spending anything:
24
18
 
@@ -33,10 +27,7 @@ turri validate app.plain
33
27
  turri regenerate app.plain --model anthropic/claude-sonnet-5
34
28
  ```
35
29
 
36
- That builds every functionality in order - code and unit tests, then conformance tests when the
37
- module has `***test reqs***`, then every conformance test so far - and advances only when they
38
- pass. A module that `requires` others builds them first, bottom up. When it is done it prints
39
- `To try it: turri release <spec>` - see [deploy](deploy.md).
30
+ That builds every functionality in order - code and unit tests, then conformance tests when the module has `***test reqs***`, then every conformance test so far - and advances only when they pass. A module that `requires` others builds them first, bottom up. When it is done it prints `To try it: turri release <spec>` - see [deploy](deploy.md).
40
31
 
41
32
  Keep it running when the terminal closes and the laptop would sleep, and keep the log:
42
33
 
@@ -50,8 +41,7 @@ nohup turri regenerate app.plain --model anthropic/claude-sonnet-5 > render.log
50
41
 
51
42
  Or run it in `tmux` / `screen` to look in on it. `tail -f render.log` follows it either way.
52
43
 
53
- The project's `config.turri.yaml`, in the folder you run it from, can give the defaults, so a run can
54
- be just `turri regenerate`:
44
+ The project's `config.turri.yaml`, in the folder you run it from, can give the defaults, so a run can be just `turri regenerate`:
55
45
 
56
46
  ```yaml
57
47
  module: app.plain # what to build when no file is given
@@ -62,20 +52,7 @@ judge-model: anthropic/claude-opus-5-5 # the judge and the test review (defau
62
52
  conformance-tests: true # write and run conformance tests where there are test reqs (default: false)
63
53
  ```
64
54
 
65
- The same file names the [testing scripts](regenerating.md#the-testing-scripts); every key is in
66
- [the project config](config.md). `turri regenerate`, `spec-diff` and `conform` take five of its keys on
67
- the command line too - `--harness-model`, `--coding-model` (`--model` is the same), `--judge-model`,
68
- `--review` and, but for `conform`, `--conformance-tests`. Each is read as if the file had it, in its place:
69
- it takes that key's value, and every key keeps its place in the order the config resolves them in - a
70
- `models:` entry still wins over `harness-model`, `coding-model` or `judge-model`, the planner and the
71
- conformance chooser run on the harness model, the judge falls back on the coding model. So a run with
72
- the options is the same as one, or a chat, with those keys in `config.turri.yaml` - and a `config.yaml`
73
- the project has is left as it is.
74
- A project whose parts are on different stacks keeps each stack's settings in
75
- `config.turri.<stack>.yaml` beside it ([stacks](config.md#stacks)). Choose the one to build with
76
- `--stack <name>` - on any command: `regenerate`, `conform`, `spec-diff`, `glimpse`, `release`, `cultivate` -
77
- or `TURRI_STACK`: `turri regenerate backend.plain --stack backend`. With none chosen,
78
- `config.turri.yaml` alone is used; a stack the project has no file for is refused.
55
+ The same file names the [testing scripts](regenerating.md#the-testing-scripts); every key is in [the project config](config.md). `turri regenerate`, `spec-diff` and `conform` take five of its keys on the command line too - `--harness-model`, `--coding-model` (`--model` is the same), `--judge-model`, `--review` and, but for `conform`, `--conformance-tests`. Each is read as if the file had it, in its place: it takes that key's value, and every key keeps its place in the order the config resolves them in - a `models:` entry still wins over `harness-model`, `coding-model` or `judge-model`, the planner and the conformance chooser run on the harness model, the judge falls back on the coding model. So a run with the options is the same as one, or a chat, with those keys in `config.turri.yaml` - and a `config.yaml` the project has is left as it is. A project whose parts are on different stacks keeps each stack's settings in `config.turri.<stack>.yaml` beside it ([stacks](config.md#stacks)). Choose the one to build with `--stack <name>` - on any command: `regenerate`, `conform`, `spec-diff`, `glimpse`, `release`, `cultivate` - or `TURRI_STACK`: `turri regenerate backend.plain --stack backend`. With none chosen, `config.turri.yaml` alone is used; a stack the project has no file for is refused.
79
56
 
80
57
  Useful options - `turri regenerate --help` lists all of them:
81
58
 
@@ -96,20 +73,11 @@ Useful options - `turri regenerate --help` lists all of them:
96
73
 
97
74
  An option a command does not take is refused, not ignored.
98
75
 
99
- `turri regenerate` queues the jobs the chat's `regenerate` does - a spec diff that brings what
100
- is built in line, when it no longer matches the spec, then one render functionality job per
101
- functional spec not built, each with its conformance job - and runs them all to the end, the queue
102
- master reworking them as in the chat (see [a whole module](regenerating.md#a-whole-module)).
103
- Conformance tests follow `conformance-tests:` in `config.turri.yaml`, or `--conformance-tests`.
76
+ `turri regenerate` queues the jobs the chat's `regenerate` does - a spec diff that brings what is built in line, when it no longer matches the spec, then one render functionality job per functional spec not built, each with its conformance job - and runs them all to the end, the queue master reworking them as in the chat (see [a whole module](regenerating.md#a-whole-module)). Conformance tests follow `conformance-tests:` in `config.turri.yaml`, or `--conformance-tests`.
104
77
 
105
78
  ## In the morning
106
79
 
107
- The command exits `0` only when every module is complete: its code regenerated from the spec as it is
108
- now, and its conformance tests written for what is built when they are on - no pulse failed, nothing
109
- halted, nothing waiting for a start over. Anything else exits `1`, and the last lines say which
110
- module is not complete, and why. A behaviour the spec leaves open that an agent had to decide is printed
111
- as a `spec gap` under its pulse, with what was decided meanwhile. The log says, per functionality, what was built and whether its
112
- tests passed.
80
+ The command exits `0` only when every module is complete: its code regenerated from the spec as it is now, and its conformance tests written for what is built when they are on - no pulse failed, nothing halted, nothing waiting for a start over. Anything else exits `1`, and the last lines say which module is not complete, and why. A behaviour the spec leaves open that an agent had to decide is printed as a `spec gap` under its pulse, with what was decided meanwhile. The log says, per functionality, what was built and whether its tests passed.
113
81
 
114
82
  | | |
115
83
  | --- | --- |
@@ -127,24 +95,11 @@ git --git-dir=plain_modules/<module>/.plaingit --work-tree=plain_modules/<module
127
95
 
128
96
  ### Where the code is
129
97
 
130
- A module that `requires` others is built in its own codebase on top of theirs: the code of
131
- `plain_modules/<module>/code` holds every module below it. `--build-dir <dir>` exports it as the run
132
- ends, however it ends - done, failed, stopped by a signal: the last commit of the furthest-built
133
- codebase of the chain, with the harness's own files (`.turri.json`, the notes) left out, so what is in
134
- it is only what landed, its unit tests passing. The folder's contents are replaced each time; a
135
- folder that holds the project, or one inside `plain_modules`, is refused. Point an external test
136
- suite at it.
98
+ A module that `requires` others is built in its own codebase on top of theirs: the code of `plain_modules/<module>/code` holds every module below it. `--build-dir <dir>` exports it as the run ends, however it ends - done, failed, stopped by a signal: the last commit of the furthest-built codebase of the chain, with the harness's own files (`.turri.json`, the notes) left out, so what is in it is only what landed, its unit tests passing. The folder's contents are replaced each time; a folder that holds the project, or one inside `plain_modules`, is refused. Point an external test suite at it.
137
99
 
138
100
  ### The run report
139
101
 
140
- `--report <file.json>` writes one JSON summary of the run, for a benchmark or CI to read rather than
141
- the log. It is written as the run starts, again as each pulse ends, and last as the process exits -
142
- whether the run is done, a pulse failed, the provider failed, it crashed, or `SIGTERM` / `SIGINT` /
143
- `SIGHUP` stopped it. Only a process killed outright (`SIGKILL`) keeps the one written as its last pulse
144
- ended. Each write replaces the file whole (written beside it, then renamed), so a reader never sees
145
- half of one. Keys are snake_case, numbers are numbers, and `schema_version` changes only when a field
146
- changes meaning or goes; new fields may appear in the same version. Paths in `pulses`,
147
- `functionalities` and `logs` are relative to `project_dir`.
102
+ `--report <file.json>` writes one JSON summary of the run, for a benchmark or CI to read rather than the log. It is written as the run starts, again as each pulse ends, and last as the process exits - whether the run is done, a pulse failed, the provider failed, it crashed, or `SIGTERM` / `SIGINT` / `SIGHUP` stopped it. Only a process killed outright (`SIGKILL`) keeps the one written as its last pulse ended. Each write replaces the file whole (written beside it, then renamed), so a reader never sees half of one. Keys are snake_case, numbers are numbers, and `schema_version` changes only when a field changes meaning or goes; new fields may appear in the same version. Paths in `pulses`, `functionalities` and `logs` are relative to `project_dir`.
148
103
 
149
104
  ```jsonc
150
105
  {
@@ -211,53 +166,19 @@ changes meaning or goes; new fields may appear in the same version. Paths in `pu
211
166
  }
212
167
  ```
213
168
 
214
- **Usage.** Every model response an agent gets counts, in a pulse or not. `total` is `in_pulses` plus
215
- `outside_pulses`, and `in_pulses` is the pulses' own usage added up - the figures their lines in the
216
- notes and the pulse logs print - so the totals can be checked by hand against them. `llm_calls` counts
217
- model responses, a compaction's included; `agent_turns` the harness's requests to an agent, each as
218
- many responses as it takes; `model_seconds` the time from each request to its response's end, added
219
- up - several agents working at once each count theirs, so it can exceed the run's duration. Tokens and
220
- cost are as the provider reports them to pi. `by_model`, `by_role` and `by_pulse_kind` split the same
221
- calls; a role or model turri could not tell is `unknown`.
222
-
223
- **Pulses.** `landed`: its work is committed. `rolled_back`: it ended without landing - failed,
224
- stopped, or did not pass - and everything it wrote was undone. A pulse that failed is tried once more
225
- before it counts as failed; both tries are the one pulse.
226
-
227
- **Spec gaps.** A behaviour the spec leaves open that an agent met and decided on its own - reported with
228
- `report_spec_gap` (see [regenerating](regenerating.md)). In the chat they are put to you as the pulse ends; a
229
- headless run has no one to ask, so they are listed here, in the order the pulses started, and printed under the
230
- pulse that met them. `assumption` is what the agent decided meanwhile, also in the codebase's notes'
231
- assumptions while its pulse is `landed`; a pulse rolled back takes it out of the notes, but the question still
232
- stands. Settle the ones you care about in the spec and run again.
233
-
234
- **Turn incidents.** Each time the model provider failed for a moment and pi or turri waited it out before trying
235
- the request again, each time the provider sent nothing back on a request for two minutes, and each turn turri
236
- stopped itself - an agent going round in a circle, its calls failing again and again, its time up - after which the
237
- run carried on. The provider failing or stalling is printed as it happens, in yellow, under the pulse; a stop is
238
- the work's normal course and is only listed. They are counted in the usage as `provider_retries`,
239
- `provider_stalls` and `turns_stopped`. Many of any point at the model, not the spec: a provider at its limits, or a
240
- model too small for the role it was given.
241
-
242
- **Completed functional specs** - the pass rate, the figure to compare runs by.
243
- A functional spec is completed when the code record of the codebase the run delivers - the
244
- furthest-built of the chain, the one `--build-dir` exports - as last committed, holds it as the spec has
245
- it now: the same text, every file it links holding the same, no definition it uses changed, and not
246
- marked to regenerate again. A record moves only when a pulse lands, and a pulse lands only once the
247
- code's unit tests pass (its conformance tests too, when they are on), so completed means landed and
248
- passing. Each module counts its own functional specs: a module below the tip counts from what the
249
- tip's code was built on, and a module above the delivered codebase has none completed.
250
- `conformance_tested` is those of the completed ones whose conformance tests are recorded as written
251
- for them as they are now (`plain_modules/<module>/tests/.turri.json`).
169
+ **Usage.** Every model response an agent gets counts, in a pulse or not. `total` is `in_pulses` plus `outside_pulses`, and `in_pulses` is the pulses' own usage added up - the figures their lines in the notes and the pulse logs print - so the totals can be checked by hand against them. `llm_calls` counts model responses, a compaction's included; `agent_turns` the harness's requests to an agent, each as many responses as it takes; `model_seconds` the time from each request to its response's end, added up - several agents working at once each count theirs, so it can exceed the run's duration. Tokens and cost are as the provider reports them to pi. `by_model`, `by_role` and `by_pulse_kind` split the same calls; a role or model turri could not tell is `unknown`.
170
+
171
+ **Pulses.** `landed`: its work is committed. `rolled_back`: it ended without landing - failed, stopped, or did not pass - and everything it wrote was undone. A pulse that failed is tried once more before it counts as failed; both tries are the one pulse.
172
+
173
+ **Spec gaps.** A behaviour the spec leaves open that an agent met and decided on its own - reported with `report_spec_gap` (see [regenerating](regenerating.md)). In the chat they are put to you as the pulse ends; a headless run has no one to ask, so they are listed here, in the order the pulses started, and printed under the pulse that met them. `assumption` is what the agent decided meanwhile, also in the codebase's notes' assumptions while its pulse is `landed`; a pulse rolled back takes it out of the notes, but the question still stands. Settle the ones you care about in the spec and run again.
174
+
175
+ **Turn incidents.** Each time the model provider failed for a moment and pi or turri waited it out before trying the request again, each time the provider sent nothing back on a request for two minutes, and each turn turri stopped itself - an agent going round in a circle, its calls failing again and again, its time up - after which the run carried on. The provider failing or stalling is printed as it happens, in orange, under the pulse; a stop is the work's normal course and is only listed. They are counted in the usage as `provider_retries`, `provider_stalls` and `turns_stopped`. Many of any point at the model, not the spec: a provider at its limits, or a model too small for the role it was given.
176
+
177
+ **Completed functional specs** - the pass rate, the figure to compare runs by. A functional spec is completed when the code record of the codebase the run delivers - the furthest-built of the chain, the one `--build-dir` exports - as last committed, holds it as the spec has it now: the same text, every file it links holding the same, no definition it uses changed, and not marked to regenerate again. A record moves only when a pulse lands, and a pulse lands only once the code's unit tests pass (its conformance tests too, when they are on), so completed means landed and passing. Each module counts its own functional specs: a module below the tip counts from what the tip's code was built on, and a module above the delivered codebase has none completed. `conformance_tested` is those of the completed ones whose conformance tests are recorded as written for them as they are now (`plain_modules/<module>/tests/.turri.json`).
252
178
 
253
179
  ## If it stopped
254
180
 
255
- Run the same command again. Functionalities already built from the current spec are left alone,
256
- and modules that are complete are skipped, so it picks up where it stopped - after a crash, a
257
- provider outage, or a machine that went to sleep anyway. Whatever the stopped run left
258
- half-written is undone first: each step starts from the module's last commit. A functionality whose spec has changed
259
- since counts as not built, so editing the spec and re-running rebuilds what changed. `--from-scratch`
260
- rebuilds the module; `--from-scratch-chain` rebuilds every module of its chain.
181
+ Run the same command again. Functionalities already built from the current spec are left alone, and modules that are complete are skipped, so it picks up where it stopped - after a crash, a provider outage, or a machine that went to sleep anyway. Whatever the stopped run left half-written is undone first: each step starts from the module's last commit. A functionality whose spec has changed since counts as not built, so editing the spec and re-running rebuilds what changed. `--from-scratch` rebuilds the module; `--from-scratch-chain` rebuilds every module of its chain.
261
182
 
262
183
  ## Rebuilding one functionality
263
184
 
@@ -265,17 +186,9 @@ rebuilds the module; `--from-scratch-chain` rebuilds every module of its chain.
265
186
  turri regenerate app.plain 3
266
187
  ```
267
188
 
268
- It rebuilds just functionality 3 over the code that already exists - after its spec changed, or as
269
- it is - without rebuilding the ones before it. To change what it does, change its spec first: the
270
- spec is what the code is built from, and a change made there lasts. Then it updates the
271
- functionality's conformance tests and runs every conformance test in the codebase, since a change can break something built
272
- later as easily as something built earlier. It works in the codebase the functionality now lives
273
- in: when later modules are built on top of this one, the furthest-built of them. It exits `0` when
274
- everything passes, and refuses when there is no code yet - regenerate the module first.
189
+ It rebuilds just functionality 3 over the code that already exists - after its spec changed, or as it is - without rebuilding the ones before it. To change what it does, change its spec first: the spec is what the code is built from, and a change made there lasts. Then it updates the functionality's conformance tests and runs every conformance test in the codebase, since a change can break something built later as easily as something built earlier. It works in the codebase the functionality now lives in: when later modules are built on top of this one, the furthest-built of them. It exits `0` when everything passes, and refuses when there is no code yet - regenerate the module first.
275
190
 
276
- Several changes do not need one command each: after editing the spec, re-running `turri regenerate`
277
- brings the code up to it - one spec diff for what changed in what is built, then the functional
278
- specs not built yet.
191
+ Several changes do not need one command each: after editing the spec, re-running `turri regenerate` brings the code up to it - one spec diff for what changed in what is built, then the functional specs not built yet.
279
192
 
280
193
  ## In a container
281
194
 
@@ -288,12 +201,7 @@ turri regenerate app.plain --harness-model amazon-bedrock/eu.anthropic.claude-op
288
201
  --start-over --build-dir /out/build --report /out/report.json < /dev/null
289
202
  ```
290
203
 
291
- - **What it needs:** Node 22.19 or newer, `bash`, `git` (each module's history, and the spec diffs
292
- the work is planned from), `/bin/sh` with `kill` and `sleep`, a writable `HOME` (pi keeps
293
- `~/.pi/agent/auth.json` there, even with no login - or set `PI_CODING_AGENT_DIR`) and `TMPDIR`, and
294
- the toolchains the project's testing scripts and implementation reqs name. `turri doctor` in the project, given
295
- the run's models, checks them all and that each model answers - at the start of the task's log, not when a pulse
296
- fails:
204
+ - **What it needs:** Node 22.19 or newer, `bash`, `git` (each module's history, and the spec diffs the work is planned from), `/bin/sh` with `kill` and `sleep`, a writable `HOME` (pi keeps `~/.pi/agent/auth.json` there, even with no login - or set `PI_CODING_AGENT_DIR`) and `TMPDIR`, and the toolchains the project's testing scripts and implementation reqs name. `turri doctor` in the project, given the run's models, checks them all and that each model answers - at the start of the task's log, not when a pulse fails:
297
205
 
298
206
  ```bash
299
207
  turri doctor --harness-model amazon-bedrock/eu.anthropic.claude-opus-5-5 \
@@ -301,32 +209,18 @@ turri regenerate app.plain --harness-model amazon-bedrock/eu.anthropic.claude-op
301
209
  ```
302
210
 
303
211
  It exits 1 on anything marked `✗`.
304
- - **Without bubblewrap:** a container that cannot make user namespaces - no
305
- `--security-opt seccomp=unconfined` - has no OS sandbox. turri runs anyway, the command check and the
306
- audit holding alone under the agents' commands; `turri doctor` shows it as `!`, not `✗`. Installing
307
- the `bubblewrap` package is worth it where it can start.
308
- - **As another user:** run it as the user who will own `plain_modules/`. git refuses a repository
309
- another user made (`dubious ownership`), so do not let root create it first.
310
- - **Amazon Bedrock:** `AWS_BEARER_TOKEN_BEDROCK` (or the usual AWS credentials) and `AWS_REGION` are
311
- enough - no pi login. An `eu.` inference profile needs an EU region. A model id pi does not know
312
- stops the first pulse; `turri --list-models bedrock` lists the ones it does.
313
- - **A project with only a `config.yaml`:** it is read while there is no `config.turri*.yaml` -
314
- `unittests-script`, `conformance-tests-script` and `prepare-environment-script` included, relative to
315
- the project; the keys turri does not know are ignored. Do not write a `config.turri.yaml` beside it to
316
- turn on conformance tests or review: from then on `config.yaml` is not read. The options above do it instead.
317
- - **Long test suites:** a testing script run is stopped after 120 seconds; set `TURRI_COMMAND_TIMEOUT`
318
- (seconds) for more.
212
+ - **Without bubblewrap:** a container that cannot make user namespaces - no `--security-opt seccomp=unconfined` - has no OS sandbox. turri runs anyway, the command check and the audit holding alone under the agents' commands; `turri doctor` shows it as `!`, not `✗`. Installing the `bubblewrap` package is worth it where it can start.
213
+ - **As another user:** run it as the user who will own `plain_modules/`. git refuses a repository another user made (`dubious ownership`), so do not let root create it first.
214
+ - **Amazon Bedrock:** `AWS_BEARER_TOKEN_BEDROCK` (or the usual AWS credentials) and `AWS_REGION` are enough - no pi login. An `eu.` inference profile needs an EU region. A model id pi does not know stops the first pulse; `turri --list-models bedrock` lists the ones it does.
215
+ - **A project with only a `config.yaml`:** it is read while there is no `config.turri*.yaml` - `unittests-script`, `conformance-tests-script` and `prepare-environment-script` included, relative to the project; the keys turri does not know are ignored. Do not write a `config.turri.yaml` beside it to turn on conformance tests or review: from then on `config.yaml` is not read. The options above do it instead.
216
+ - **Long test suites:** a testing script run is stopped after 120 seconds; set `TURRI_COMMAND_TIMEOUT` (seconds) for more.
319
217
 
320
218
  ## Asking in words instead
321
219
 
322
- pi's print mode runs one request and exits, with the render tools waiting for their work rather
323
- than starting background jobs:
220
+ pi's print mode runs one request and exits, with the render tools waiting for their work rather than starting background jobs:
324
221
 
325
222
  ```bash
326
223
  nohup turri -p "Render app.plain as a whole module, with conformance tests" > render.log 2>&1 &
327
224
  ```
328
225
 
329
- It reports what it did at the end, and nothing streams in between, so for long runs
330
- `turri regenerate` is the better choice. The interview skill (`brood`) needs
331
- someone to answer; without one they decide and record their assumptions, and a gate that escalates
332
- to a human stops the run.
226
+ It reports what it did at the end, and nothing streams in between, so for long runs `turri regenerate` is the better choice. The interview skill (`brood`) needs someone to answer; without one they decide and record their assumptions, and a gate that escalates to a human stops the run.
@@ -1,12 +1,8 @@
1
1
  # Installation
2
2
 
3
- **turri** runs wherever pi runs - macOS, Linux, WSL - with git, bash and Node 22.19 or newer (a Linux
4
- distribution's own `nodejs` package is often older: take it from nodejs.org, nvm or NodeSource). Native Windows is
5
- best effort: it needs bash on the PATH (Git for Windows' Git Bash), and has no OS sandbox under the agents' commands.
3
+ **turri** runs wherever pi runs - macOS, Linux, WSL - with git, bash and Node 22.19 or newer (a Linux distribution's own `nodejs` package is often older: take it from nodejs.org, nvm or NodeSource). Native Windows is best effort: it needs bash on the PATH (Git for Windows' Git Bash), and has no OS sandbox under the agents' commands.
6
4
 
7
- The agents' commands run on this machine, with its toolchains - see [the sandbox](sandbox.md). Each is checked before
8
- it runs and audited after, on every platform; under them an OS sandbox fences it where the OS has one: Seatbelt on
9
- macOS, always there, and bubblewrap on Linux and WSL 2 - `apt install bubblewrap`, or your distribution's package.
5
+ The agents' commands run on this machine, with its toolchains - see [the sandbox](sandbox.md). Each is checked before it runs and audited after, on every platform; under them an OS sandbox fences it where the OS has one: Seatbelt on macOS, always there, and bubblewrap on Linux and WSL 2 - `apt install bubblewrap`, or your distribution's package.
10
6
 
11
7
  ```bash
12
8
  npm install -g @turri-dev/cli # puts `turri` on your PATH
@@ -21,9 +17,7 @@ turri init-config # a config.turri.yaml with the model you talk to and the one
21
17
  turri # pi, with turri's tools, skills and themes loaded
22
18
  ```
23
19
 
24
- The package is `@turri-dev/cli` - npm keeps the name `turri` back, as too like another
25
- package's - and the command it puts on your PATH is `turri`. `npm install -g @turri-dev/cli@next` gets the latest
26
- prerelease instead, built from the last commit on `main`.
20
+ The package is `@turri-dev/cli` - npm keeps the name `turri` back, as too like another package's - and the command it puts on your PATH is `turri`. `npm install -g @turri-dev/cli@next` gets the latest prerelease instead, built from the last commit on `main`.
27
21
 
28
22
  ## From a clone
29
23
 
@@ -35,22 +29,13 @@ npm install
35
29
  npm link # puts `turri` on your PATH, pointing at this clone
36
30
  ```
37
31
 
38
- A clone runs its TypeScript sources directly - Node strips the types - so edits take effect immediately. The package
39
- on npm runs JavaScript compiled from them instead (`npm run build`, into `dist/`, run by `npm pack` and `npm publish`
40
- before they pack), as Node runs no `.ts` file inside a `node_modules`. `bin/turri.mjs` runs `src/` where there is one,
41
- and `dist/` otherwise.
32
+ A clone runs its TypeScript sources directly - Node strips the types - so edits take effect immediately. The package on npm runs JavaScript compiled from them instead (`npm run build`, into `dist/`, run by `npm pack` and `npm publish` before they pack), as Node runs no `.ts` file inside a `node_modules`. `bin/turri.mjs` runs `src/` where there is one, and `dist/` otherwise.
42
33
 
43
- To update, `git pull` and `npm install` - the second picks up a dependency that changed, pi's say. `npm link` needs
44
- running only once. A **turri** already running keeps the code it started with: start it again to run what you pulled.
34
+ To update, `git pull` and `npm install` - the second picks up a dependency that changed, pi's say. `npm link` needs running only once. A **turri** already running keeps the code it started with: start it again to run what you pulled.
45
35
 
46
36
  ## In Docker
47
37
 
48
- `docker/Dockerfile` builds an image with Node, git, bash, bubblewrap and **turri** linked; the agents' commands and the
49
- testing scripts run inside it, so add the toolchains your projects need first. It runs **turri** as the user who owns
50
- the project folder mounted at `/work`, never as root - bubblewrap as root in a container lacks the capability to make
51
- its namespaces - and with seccomp and AppArmor unconfined the OS sandbox starts. `systempaths=unconfined` lets it mount
52
- a `/proc` of its own; without it, it binds the container's. The build, the run, Windows and the keys:
53
- [turri in Docker](../docker/README.md).
38
+ `docker/Dockerfile` builds an image with Node, git, bash, bubblewrap and **turri** linked; the agents' commands and the testing scripts run inside it, so add the toolchains your projects need first. It runs **turri** as the user who owns the project folder mounted at `/work`, never as root - bubblewrap as root in a container lacks the capability to make its namespaces - and with seccomp and AppArmor unconfined the OS sandbox starts. `systempaths=unconfined` lets it mount a `/proc` of its own; without it, it binds the container's. The build, the run, Windows and the keys: [turri in Docker](../docker/README.md).
54
39
 
55
40
  ## What `turri doctor` checks
56
41
 
@@ -73,23 +58,17 @@ In a project - a folder with `config.turri.yaml` (or the old `config.yaml`) or `
73
58
  | the testing scripts | what in them this machine does not have: a program named by its whole path that is not here (a macOS-only `/usr/libexec/java_home` on Linux), a toolchain they run that is not on the PATH |
74
59
  | the models | each model `config.turri.yaml` names - `harness-model`, `coding-model`, `judge-model`, every role under `models:` - sent one short request through pi, with the credentials turri's agents will use, for any provider pi supports. One that answers is ready; for one that does not, it says what the provider said and what to do: the credentials pi reads for that provider (`AWS_PROFILE` and `AWS_REGION` for Amazon Bedrock, `OPENAI_API_KEY`, `/login`, ...), model access in the Bedrock console, the closest model ids pi knows for one it does not. `--harness-model`, `--coding-model` (`--model`) and `--judge-model` name models as the config's keys, as `turri regenerate` reads them - checked in a project or not. `--no-models` skips it |
75
60
 
76
- Removing that file is the one thing `doctor` changes, and it asks first; `turri doctor --yes` says yes. Run without a
77
- terminal - piped, or in CI - it asks nothing and changes nothing. Run it again whenever something breaks;
78
- `turri doctor --no-models` leaves out the models' requests.
61
+ Removing that file is the one thing `doctor` changes, and it asks first; `turri doctor --yes` says yes. Run without a terminal - piped, or in CI - it asks nothing and changes nothing. Run it again whenever something breaks; `turri doctor --no-models` leaves out the models' requests.
79
62
 
80
63
  The chat is told every turn which wall is up and what the project's modules need of this machine that it lacks. What they need is worked out from every spec of the project - seconds, on a project of long requires chains - so it is worked out once the chat is up and again after a turn in which a spec changed, never while your message waits: a turn is told what was known last.
81
64
 
82
65
  ## turri and `pi`
83
66
 
84
- `turri` runs pi from this project's `node_modules`, with turri loaded, so no global pi install is needed. A plain
85
- `pi` is left as it is: starting pi does not start turri, and turri puts nothing into pi's own folders.
67
+ `turri` runs pi from this project's `node_modules`, with turri loaded, so no global pi install is needed. A plain `pi` is left as it is: starting pi does not start turri, and turri puts nothing into pi's own folders.
86
68
 
87
- An older `turri setup` could load turri into every `pi`, with a one-line file in `~/.pi/agent/extensions/` pointing at
88
- this clone. `turri doctor` says when it is there, and offers to remove it. Until it is gone, `turri`
89
- leaves out its own `-e`: pi treats a tool registered twice as an error.
69
+ An older `turri setup` could load turri into every `pi`, with a one-line file in `~/.pi/agent/extensions/` pointing at this clone. `turri doctor` says when it is there, and offers to remove it. Until it is gone, `turri` leaves out its own `-e`: pi treats a tool registered twice as an error.
90
70
 
91
- `turri` starts in the `turri-dark` theme. A plain `pi` keeps its own theme; to give it
92
- `turri-dark` too, in `~/.pi/agent/settings.json`:
71
+ `turri` starts in the `turri-dark` theme. A plain `pi` keeps its own theme; to give it `turri-dark` too, in `~/.pi/agent/settings.json`:
93
72
 
94
73
  ```json
95
74
  { "theme": "turri-dark" }
@@ -97,24 +76,18 @@ leaves out its own `-e`: pi treats a tool registered twice as an error.
97
76
 
98
77
  ### Project trust
99
78
 
100
- pi will not auto-discover a project-local extension until you trust the project, and `-p` has no
101
- way to ask - so `npm run chat` and `npm run ask` pass `-e` explicitly. Once you have run `pi`
102
- interactively in this directory and accepted the trust prompt, plain `pi` picks the extension up
103
- on its own. A `defaultProjectTrust` key in `.pi/settings.json` does *not* work - a project cannot
104
- vouch for itself; that setting only counts in `~/.pi/agent/settings.json`.
79
+ pi will not auto-discover a project-local extension until you trust the project, and `-p` has no way to ask - so `npm run chat` and `npm run ask` pass `-e` explicitly. Once you have run `pi` interactively in this directory and accepted the trust prompt, plain `pi` picks the extension up on its own. A `defaultProjectTrust` key in `.pi/settings.json` does *not* work - a project cannot vouch for itself; that setting only counts in `~/.pi/agent/settings.json`.
105
80
 
106
81
  ## Environment
107
82
 
108
- Models and credentials come from your pi configuration (`~/.pi/agent`) or provider environment
109
- variables - the same resolution `pi` itself uses. Put the rest in your shell profile:
83
+ Models and credentials come from your pi configuration (`~/.pi/agent`) or provider environment variables - the same resolution `pi` itself uses. Put the rest in your shell profile:
110
84
 
111
85
  ```bash
112
86
  export PLAIN_TEMPLATE_DIRS=~/path/to/standard_template_library # shared templates
113
87
  export GEMINI_API_KEY=... # and leave GOOGLE_API_KEY unset
114
88
  ```
115
89
 
116
- For Gemini, `GEMINI_API_KEY` is what makes pi *offer* the google provider, but when
117
- `GOOGLE_API_KEY` is also set pi uses that one for the calls and says so.
90
+ For Gemini, `GEMINI_API_KEY` is what makes pi *offer* the google provider, but when `GOOGLE_API_KEY` is also set pi uses that one for the calls and says so.
118
91
 
119
92
  | variable | |
120
93
  | --- | --- |
package/docs/parser.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # The \*\*\*plain parser
2
2
 
3
- `src/plain/` is a TypeScript port of the reference \*\*\*plain parser - `plain_file.py`,
4
- `plain_spec.py`, `concept_utils.py`, and the `file_utils.py` helpers they depend on.
3
+ `src/plain/` is a TypeScript port of the reference \*\*\*plain parser - `plain_file.py`, `plain_spec.py`, `concept_utils.py`, and the `file_utils.py` helpers they depend on.
5
4
 
6
5
  | module | ports |
7
6
  |---|---|
@@ -21,9 +20,7 @@ turri validate . # every .plain file in a directory
21
20
  turri validate <file.plain> --json
22
21
  ```
23
22
 
24
- It uses this repo's own parser, so it needs no external tool, no API key and no credits: it parses
25
- the file, resolves its imports and required modules, and reports every syntax, concept and
26
- structure error with the line it is on.
23
+ It uses this repo's own parser, so it needs no external tool, no API key and no credits: it parses the file, resolves its imports and required modules, and reports every syntax, concept and structure error with the line it is on.
27
24
 
28
25
  ```
29
26
  PASS d365_export.plain 6 functionalities (1, 2, 3, 4, 5, 6), requires d365_restore_api
@@ -37,14 +34,12 @@ The exit code is 0 only when every file passed. In a conversation the same check
37
34
 
38
35
  ## Two rules of inheritance
39
36
 
40
- - A module that **`requires`** another inherits its functional specs, transitively: it is built on
41
- top of that module's code.
37
+ - A module that **`requires`** another inherits its functional specs, transitively: it is built on top of that module's code.
42
38
  - A module that **`import`s** another inherits its definitions, implementation reqs and test reqs.
43
39
 
44
40
  ## The functional-spec iterator
45
41
 
46
- `getFrids` walks functionalities depth-first through nested sections; `getSpecificationsForFrid`
47
- builds what a single functionality is rendered with. `iterateFunctionalSpecs` pairs them:
42
+ `getFrids` walks functionalities depth-first through nested sections; `getSpecificationsForFrid` builds what a single functionality is rendered with. `iterateFunctionalSpecs` pairs them:
48
43
 
49
44
  ```ts
50
45
  import { plainFileParser, iterateFunctionalSpecs } from "./src/plain/index.ts";
@@ -60,49 +55,26 @@ for (const { frid, specifications, codeVariables } of iterateFunctionalSpecs(tre
60
55
 
61
56
  ## Parsing once
62
57
 
63
- A module's parse reads its imports and the whole of its `requires` chain, so a long chain asks for the same
64
- files over and over: bringing an 18-module chain up to its spec parsed for minutes. Two caches keep it to once.
65
- Inside `withParseCache`, one question about the specs, each file is parsed once. Between calls, a parse is kept
66
- with a fingerprint of every file directly in its template folders and the current one - name, size,
67
- modification time, and a `.plain` file's text - and handed out again, as a copy, while the fingerprint is the
68
- same; a spec that does not parse is kept as that too. What a parse returns is unchanged: the cache only decides
69
- whether to parse again.
58
+ A module's parse reads its imports and the whole of its `requires` chain, so a long chain asks for the same files over and over: bringing an 18-module chain up to its spec parsed for minutes. Two caches keep it to once. Inside `withParseCache`, one question about the specs, each file is parsed once. Between calls, a parse is kept with a fingerprint of every file directly in its template folders and the current one - name, size, modification time, and a `.plain` file's text - and handed out again, as a copy, while the fingerprint is the same; a spec that does not parse is kept as that too. What a parse returns is unchanged: the cache only decides whether to parse again.
70
59
 
71
60
  ## Fidelity
72
61
 
73
- The port is checked against the Python implementation directly, not only against the ported
74
- assertions. Both parsers are run over a corpus and their module name, marshalled tree, required
75
- modules, functionality ids and per-functionality specifications compared:
62
+ The port is checked against the Python implementation directly, not only against the ported assertions. Both parsers are run over a corpus and their module name, marshalled tree, required modules, functionality ids and per-functionality specifications compared:
76
63
 
77
64
  | corpus | files | identical | differences |
78
65
  |---|---|---|---|
79
66
  | `tests/data/` fixtures | 84 | 80 | 4 error strings (below) |
80
67
  | `hycu-dynamics-365-modules` | 47 | 47 | none |
81
68
 
82
- The 4 differences are all the same thing: template partials (`implement.plain`,
83
- `implement_2.plain`, `header.plain`, `pr_implement.plain`) are meant to be included with
84
- arguments, not parsed alone. Both implementations reject them for the same reason - an undefined
85
- Liquid variable - but the message text comes from the template engine, and liquidjs phrases it
86
- differently from liquid2.
87
-
88
- Real specs exposed three places where mdast and mistletoe disagree, each covered by
89
- `tests/plain/renderer.test.ts`:
90
-
91
- - **Inline decoding.** mdast resolves `\"` to `"` and forgets whether emphasis was written with `_`
92
- or `*`; mistletoe round-trips the source. Inline leaf nodes are pinned back to their original
93
- source slice at parse time.
94
- - **List geometry.** mistletoe indents an item's continuation lines by its `prepend`, measured in
95
- coordinates with the enclosing item's indentation stripped - not by the column gap between parent
96
- and child. An acceptance test rendered detached from its functionality keeps 6 and 10 spaces where
97
- the naive reading gives 4 and 8.
98
- - **Lazy continuation lines.** A paragraph's continuation lines carry leading whitespace that is not
99
- part of the text; keeping it in the raw slice indents the line twice.
69
+ The 4 differences are all the same thing: template partials (`implement.plain`, `implement_2.plain`, `header.plain`, `pr_implement.plain`) are meant to be included with arguments, not parsed alone. Both implementations reject them for the same reason - an undefined Liquid variable - but the message text comes from the template engine, and liquidjs phrases it differently from liquid2.
70
+
71
+ Real specs exposed three places where mdast and mistletoe disagree, each covered by `tests/plain/renderer.test.ts`:
72
+
73
+ - **Inline decoding.** mdast resolves `\"` to `"` and forgets whether emphasis was written with `_` or `*`; mistletoe round-trips the source. Inline leaf nodes are pinned back to their original source slice at parse time.
74
+ - **List geometry.** mistletoe indents an item's continuation lines by its `prepend`, measured in coordinates with the enclosing item's indentation stripped - not by the column gap between parent and child. An acceptance test rendered detached from its functionality keeps 6 and 10 spaces where the naive reading gives 4 and 8.
75
+ - **Lazy continuation lines.** A paragraph's continuation lines carry leading whitespace that is not part of the text; keeping it in the raw slice indents the line twice.
100
76
 
101
77
  Two deliberate differences from the Python source:
102
78
 
103
- - The `test_plainfile.py` tests drive the acceptance-test helpers with `MagicMock` tokens and patch
104
- the inner helper to count calls. ESM has no equivalent, so those tests build real ASTs from
105
- markdown and assert the same observable outcome.
106
- - `networkx`'s `topological_sort` is reimplemented rather than replaced with a generic one: it
107
- flattens `topological_generations`, so the ordering of independent concepts follows graph
108
- insertion order, and the Python tests assert that exact sequence.
79
+ - The `test_plainfile.py` tests drive the acceptance-test helpers with `MagicMock` tokens and patch the inner helper to count calls. ESM has no equivalent, so those tests build real ASTs from markdown and assert the same observable outcome.
80
+ - `networkx`'s `topological_sort` is reimplemented rather than replaced with a generic one: it flattens `topological_generations`, so the ordering of independent concepts follows graph insertion order, and the Python tests assert that exact sequence.