@turri-dev/cli 0.1.4-next.5a94e68 → 0.1.4-next.8254fbc
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/.pi/themes/turri-dark.json +67 -66
- package/.pi/themes/turri-light.json +71 -73
- package/README.md +40 -128
- package/assets/jellyfish/dark-00.png +0 -0
- package/assets/jellyfish/dark-01.png +0 -0
- package/assets/jellyfish/dark-02.png +0 -0
- package/assets/jellyfish/dark-03.png +0 -0
- package/assets/jellyfish/dark-04.png +0 -0
- package/assets/jellyfish/dark-05.png +0 -0
- package/assets/jellyfish/dark-06.png +0 -0
- package/assets/jellyfish/dark-07.png +0 -0
- package/assets/jellyfish/dark-08.png +0 -0
- package/assets/jellyfish/dark-09.png +0 -0
- package/assets/jellyfish/dark-10.png +0 -0
- package/assets/jellyfish/dark-11.png +0 -0
- package/assets/jellyfish/dark-12.png +0 -0
- package/assets/jellyfish/dark-13.png +0 -0
- package/assets/jellyfish/dark-14.png +0 -0
- package/assets/jellyfish/light-00.png +0 -0
- package/assets/jellyfish/light-01.png +0 -0
- package/assets/jellyfish/light-02.png +0 -0
- package/assets/jellyfish/light-03.png +0 -0
- package/assets/jellyfish/light-04.png +0 -0
- package/assets/jellyfish/light-05.png +0 -0
- package/assets/jellyfish/light-06.png +0 -0
- package/assets/jellyfish/light-07.png +0 -0
- package/assets/jellyfish/light-08.png +0 -0
- package/assets/jellyfish/light-09.png +0 -0
- package/assets/jellyfish/light-10.png +0 -0
- package/assets/jellyfish/light-11.png +0 -0
- package/assets/jellyfish/light-12.png +0 -0
- package/assets/jellyfish/light-13.png +0 -0
- package/assets/jellyfish/light-14.png +0 -0
- package/dist/brand.js +7 -7
- package/dist/cultivate-cli.js +16 -23
- package/dist/extension.js +15 -21
- package/dist/loop-cli.js +8 -8
- package/dist/module-report.js +5 -4
- package/dist/regenerate/plain-palette.js +51 -36
- package/dist/regenerate/side-panel.js +1 -1
- package/dist/regenerate/spec-map-view.js +9 -5
- package/dist/release-cli.js +3 -8
- package/dist/validate-cli.js +3 -8
- package/docs/README.md +5 -12
- package/docs/authoring-plain.md +37 -119
- package/docs/config.md +50 -176
- package/docs/deploy.md +21 -80
- package/docs/development.md +13 -41
- package/docs/headless.md +30 -136
- package/docs/images/how-turri-works.svg +29 -31
- package/docs/images/turri-banner.png +0 -0
- package/docs/installation.md +13 -40
- package/docs/parser.md +15 -43
- package/docs/regenerating.md +231 -964
- package/docs/sandbox.md +38 -152
- package/docs/skills-and-gates.md +50 -173
- package/docs/theming.md +45 -46
- package/docs/using.md +66 -356
- package/genome/rules/bullet-continuation.md +5 -13
- package/genome/rules/concise-specs-examples.md +2 -4
- package/genome/rules/concise-specs-integration-examples.md +2 -6
- package/genome/rules/concise-specs.md +1 -2
- package/genome/rules/func-specs.md +1 -0
- package/genome/rules/integration-embedded-testing.md +1 -4
- package/genome/rules/integrations.md +1 -2
- package/genome/rules/project-model.md +14 -35
- package/genome/rules/stdlib.md +14 -49
- package/genome/rules/turri-regeneration.md +48 -0
- package/genome/skills/add-implementation-requirement/SKILL.md +1 -2
- package/genome/skills/analyze-if-func-spec-too-complex/SKILL.md +3 -8
- package/genome/skills/brood/SKILL.md +37 -127
- package/genome/skills/brood/references/intent.md +4 -12
- package/genome/skills/brood/references/product.md +24 -76
- package/genome/skills/brood/references/stack.md +7 -29
- package/genome/skills/brood/references/testing.md +11 -42
- package/genome/skills/bud/SKILL.md +32 -109
- package/genome/skills/bud/references/intent.md +4 -11
- package/genome/skills/implement-conformance-testing-script/SKILL.md +3 -6
- package/genome/skills/implement-unit-testing-script/SKILL.md +1 -2
- package/genome/skills/mature/SKILL.md +21 -69
- package/genome/skills/spawn/SKILL.md +28 -113
- 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
|
-
|
|
15
|
-
- **
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
- **
|
|
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.
|
|
@@ -2,49 +2,47 @@
|
|
|
2
2
|
<title id="d-title">How turri works</title>
|
|
3
3
|
<desc id="d-desc">You describe changes to turri; turri asks and reports back. turri writes the ***plain specs, which you read and review with turri. Then turri loops without end: it reads the specs, regenerates the code from them, and tests the code against them.</desc>
|
|
4
4
|
<style>
|
|
5
|
-
@font-face { font-family: "Archivo"; font-weight: 900; src: url(data:font/woff2;base64,d09GMgABAAAAAAN0ABAAAAAABuQAAAMZAAIAQgAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbehwqBmA/U1RBVFoARBEICoIAgV8BNgIkAxQLDAAEIAWESgcgDAcbnQUgrgN4MoaXUIIRo4bSsCiga71j9UcNccO+yjyC+P1os19OzJJmreeIVRLJ+4WIehOXTKRJJRSJ8Pyncf+de2ce5wNCgxqZAC0ct6/blrChgUdrbfZNUdmKmpz5LBKSlaKpfEIsfsMjiZDIAa9FmMiRBO5xu8oTYkgzRXzs+9+lkO3UTW0sDfC/Eg80wzWI0sVrASYYaPgX/QUUUZwVWGASpe6a4qnU8vN++BgB6gBkCJEIgRo+ovDC4Pg8xPbtoA4xEkgQEHs5W4cACBAkpkf0mAcJRkVgEgAAgTGBCIQQJjCRXOsngM9xiaHI0vMFJWTvskM4oi4XP9X6lXQAfsTmjZMjkCBh+cB/UGwghwgga+BnGCIQoidAGwEm8do+ioSAtZ0iEiB2445KnDAtK24IZGGvBwgEUSMWkGMczoAAZBA8JHWfFAGq6DHYzcYSIRCVLqQT6bgAkGIzEQeUgBqc3whsB6D43F9FhbTpcmwPhZZSSaVRteGjj59qv27LeOjVk2PYcSj5hTCSqg9DMZq5r2qx/exN7XkyKJ65eHCjDr99TrmSxrrQ/ddvbRP77/bfbnTXBClNV3Wb/V8OzhdlfN+zmyYuvQ78yq+N29b1NhUoOpktvThzPnRiPpgdaR7/8x/4q21F2Fy8K2X997rzFrMa3zXdVG/2h9xezABAgEzWr9dWPOVvgPgG+P6P4MdMl+X/1lRRbBQToAIDAqr30kH43z8jcK3MVqyLdCnxtj+kNzsyh0CpnxcQsFXvUo4KA+AWpkJC3CvMzFdFyPA9lG6KhlN4yVpBkZIhoC76oBCMMUIa2QnfHuyS7wR203cG/ql5BtMxm0QgQlSUiOkoDzVEHCqTbY9IcsBAVbAZZJw96CIyCAw2JwwIhkImR6tw4fpJ462YXZW6GTRVyml0kl2Mo0KNYyZOVdLpkkNE1TjaAtmLq9pHZVHt2KOYxREwuYutVpgIESIlKzdh0oi2kNVNXUEsmhfLSQw6qnQVhlhynlg3gWQfui5Ikww2zQ7VCFSTkj2zjI2LdG6ANOUYfxXpj+RRAAA=) format("woff2"); }
|
|
6
|
-
@font-face { font-family: "Archivo"; font-weight: 800; src: url(data:font/woff2;base64,d09GMgABAAAAAARoABAAAAAACJwAAAQNAAIAQgAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbgRAcKgZgP1NUQVRaAEwRCAqEcIQUATYCJAMcCxAABCAFhGoHIAwHGxgHKB6FccNdgjLFEIv53uLyOp7///3+N9e+57sm0yTgybwE8f4rdCIhkRhdNGRC4VMilffzedb7TYcZyRkQmwJOF4CK41InIf9hXSYupz+WPr61fmGAXUCBJToX/m9+bNFHYwImlGY9dw/0QE2Pto0FGJB3TbabIke9urYiGAWUiH4RDNuQg3atvrUXPF6zdQrw0ODXaHrF1yjAmY3SFD8GlZ0L4lAlTLPL0UnJ/fIYmVhii1q6qEK3JOFBTgBoVdFFcaB+0WYB77Aof+fwXxCQP/HwfnsTXAAuqlP8D7R7aBUQqHHaPmAFJmJQh1kYDtBOeDoRQuDkmUiHQF7o2DIUgqbftAgIlUA8IEKGsCigUyqhAAGF/VwQSXc+N1xZ2/KEkh7VAzxCmfp/naUWmJuTGJHdgwkwAMYxhN8AGQCgAaKUEtmc9w139ZoOjmDPstnQRLsdn221WsH17b5TrrNOHABvDt7EZ5/ej/AJFoulj7zk6slgHOF0137XPZb+2ecPIzRxk+LQSZsywKgp5fju1U3FMD5x3a5jGsN7hCMCUv/s2/bKh24R+AZxs8+EMJw2YeXOrcHkMM/CjjsWtnQtADFmhTLwvqUbNwbjy9fvuxc4gA/usTywFOADFFFpJskqM0X0FzGC6/Pju4o3xkiPRid8ITPi0smIxJgrY2JWFcZ31+UzQ+DvSA2byKWYZJ6JSVRFzr371fH3Fss0vNFpV08yjN+40JocFHFQyw2Nv1ucGptSfAcqRobHllTrLa6KGk0M9BuKy4zMWHMKFZ4SPLLsid/6rJjmmhxW8IG2RyHXZqYFxn1fC5dHBscQ1WaSrKbGEINDFJACzdaG5ExZeny6PCM5KUPe/wZZJnzE+0aR7FFb6vz357/zCT+1Lixvi+d0l2an4GfVfrDwStzwZlFdmIlTdd9jf9tr5vOXtY5l75dtLqt+s/noT/ekh+bWKAAIypVR2eOTTM/Sf84Y9hbw52/YQcBpUYX3/7hedptMjwL6FQifI/1JhvhvHAOxWw3ULLhbPIgvagCl4Gh7PRBVjiYY8IyDzTa73ZgP0ElzAT4CCh3HdtBl3CndueX9YJOk+ywnx9fvMK0NCIxmHKQRDGcW7QLTWZIJhOi2TAhEDzkL5ZpzcJWSCkVDTEhEJ0qOrIsYpZcO8o0qGlyKIWagnMFOs5IEH5dOitTTuZU0tLkke/N0VLRKZMokJF6+lR5HBi4luUwd5BTEOPS00vWiqPBl+h2dHIfRg09OA6tFGd5C1jHpGmyOfzc+IT3Zp2jkyJAlSzZChzbtmhDWW7fB0sH+aK6TxJQUolCvLQIDyepHKC4xHl076Vx7fblybDJNsUetGFc3Je9KChjQk0mzV39D/6dOnwsA) format("woff2"); }
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
.
|
|
11
|
-
.
|
|
12
|
-
.
|
|
13
|
-
.flow { fill: none; stroke: #0f0f0f; stroke-width: 2.25; stroke-linecap: round; }
|
|
14
|
-
.review { stroke-dasharray: 9 7; }
|
|
5
|
+
@font-face { font-family: "Geist Mono"; font-weight: 400; src: url(data:font/woff2;base64,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) format("woff2"); }
|
|
6
|
+
@font-face { font-family: "Geist Mono"; font-weight: 500; src: url(data:font/woff2;base64,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) format("woff2"); }
|
|
7
|
+
text { font-family: "Geist Mono", ui-monospace, Menlo, monospace; font-variant-ligatures: none; font-feature-settings: "calt" 0, "liga" 0; }
|
|
8
|
+
.lbl { font-family: "Geist Mono", ui-monospace, Menlo, monospace; font-size: 15px; font-weight: 400; fill: #85a3b2; }
|
|
9
|
+
.node { font-family: "Geist Mono", ui-monospace, Menlo, monospace; font-weight: 500; font-size: 22px; letter-spacing: -0.01em; }
|
|
10
|
+
.sub { font-family: "Geist Mono", ui-monospace, Menlo, monospace; font-weight: 400; font-size: 12px; letter-spacing: 0.04em; fill: #85a3b2; }
|
|
11
|
+
.flow { fill: none; stroke: #85a3b2; stroke-width: 2; }
|
|
12
|
+
.review { stroke: #3e5a6b; stroke-width: 4; stroke-dasharray: 8 8; }
|
|
15
13
|
@media (prefers-reduced-motion: no-preference) {
|
|
16
|
-
.spin { transform-box: fill-box; transform-origin: 50% 58%; animation: turn 6s
|
|
14
|
+
.spin { transform-box: fill-box; transform-origin: 50% 58%; animation: turn 6s steps(8) infinite; }
|
|
17
15
|
@keyframes turn { to { transform: rotate(360deg); } }
|
|
18
16
|
}
|
|
19
17
|
</style>
|
|
20
|
-
<rect x="16" y="-5" width="1100" height="490"
|
|
18
|
+
<rect x="16" y="-5" width="1100" height="490" fill="#0a0f1a"/>
|
|
21
19
|
<defs>
|
|
22
20
|
<marker id="head" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
23
|
-
<path d="M0 0 L10 5 L0 10 z" fill="#
|
|
21
|
+
<path d="M0 0 L10 5 L0 10 z" fill="#85a3b2"/>
|
|
24
22
|
</marker>
|
|
25
23
|
</defs>
|
|
26
|
-
<path class="flow review" d="M120
|
|
27
|
-
<text class="lbl" x="552" y="
|
|
28
|
-
<path class="flow" d="
|
|
24
|
+
<path class="flow review" d="M120 140 V 40 H 985 V 155"/>
|
|
25
|
+
<text class="lbl" x="552" y="24" text-anchor="middle">review loop</text>
|
|
26
|
+
<path class="flow" d="M180 180 L 466 180" marker-end="url(#head)"/>
|
|
29
27
|
<text class="lbl" x="324" y="166" text-anchor="middle">describe changes</text>
|
|
30
|
-
<path class="flow" d="
|
|
28
|
+
<path class="flow" d="M466 220 L 180 220" marker-end="url(#head)"/>
|
|
31
29
|
<text class="lbl" x="324" y="246" text-anchor="middle">asks · reports</text>
|
|
32
|
-
<path class="flow" d="
|
|
30
|
+
<path class="flow" d="M650 180 L 896 180" marker-end="url(#head)"/>
|
|
33
31
|
<text class="lbl" x="771" y="166" text-anchor="middle">writes</text>
|
|
34
|
-
<path class="flow" d="
|
|
32
|
+
<path class="flow" d="M896 220 L 650 220" marker-end="url(#head)"/>
|
|
35
33
|
<text class="lbl" x="771" y="246" text-anchor="middle">reads</text>
|
|
36
|
-
<path class="flow" d="M538
|
|
34
|
+
<path class="flow" d="M538 247 L 538 376" marker-end="url(#head)"/>
|
|
37
35
|
<text class="lbl" x="524" y="318" text-anchor="end">regenerates</text>
|
|
38
|
-
<path class="flow" d="M578
|
|
36
|
+
<path class="flow" d="M578 376 L 578 247" marker-end="url(#head)"/>
|
|
39
37
|
<text class="lbl" x="592" y="318" text-anchor="start">tests against ***plain</text>
|
|
40
|
-
<text class="spin" x="880" y="372" text-anchor="middle" font-
|
|
41
|
-
<
|
|
42
|
-
<text class="node" x="120" y="208" text-anchor="middle" fill="#
|
|
43
|
-
<rect x="470" y="156" width="176" height="88"
|
|
44
|
-
<text x="558" y="
|
|
45
|
-
<rect x="900" y="158" width="170" height="84"
|
|
46
|
-
<text x="985" y="208" text-anchor="middle" font-
|
|
47
|
-
<rect x="473" y="380" width="170" height="84"
|
|
48
|
-
<text class="node" x="558" y="420" text-anchor="middle" fill="#
|
|
49
|
-
<text class="sub" x="558" y="444" text-anchor="middle"
|
|
38
|
+
<text class="spin" x="880" y="372" text-anchor="middle" font-weight="500" font-size="44" fill="#5ce1e6">↻</text>
|
|
39
|
+
<rect x="66" y="146" width="108" height="108" fill="#142030"/><rect x="66" y="143" width="108" height="3" fill="#85a3b2"/><rect x="66" y="254" width="108" height="3" fill="#85a3b2"/><rect x="63" y="146" width="3" height="108" fill="#85a3b2"/><rect x="174" y="146" width="3" height="108" fill="#85a3b2"/>
|
|
40
|
+
<text class="node" x="120" y="208" text-anchor="middle" fill="#f4f7f9">you</text>
|
|
41
|
+
<rect x="470" y="156" width="176" height="88" fill="#142030"/><rect x="470" y="153" width="176" height="3" fill="#85a3b2"/><rect x="470" y="244" width="176" height="3" fill="#85a3b2"/><rect x="467" y="156" width="3" height="88" fill="#85a3b2"/><rect x="646" y="156" width="3" height="88" fill="#85a3b2"/>
|
|
42
|
+
<text x="558" y="211" text-anchor="middle" font-weight="500" font-size="34" letter-spacing="-0.02em" fill="#ff5c8d">turri</text>
|
|
43
|
+
<rect x="900" y="158" width="170" height="84" fill="#142030"/><rect x="900" y="155" width="170" height="3" fill="#85a3b2"/><rect x="900" y="242" width="170" height="3" fill="#85a3b2"/><rect x="897" y="158" width="3" height="84" fill="#85a3b2"/><rect x="1070" y="158" width="3" height="84" fill="#85a3b2"/>
|
|
44
|
+
<text x="985" y="208" text-anchor="middle" font-weight="500" font-size="24" fill="#e9d8c8">***plain</text>
|
|
45
|
+
<rect x="473" y="380" width="170" height="84" fill="none" stroke="#3e5a6b" stroke-width="4" stroke-dasharray="8 8"/>
|
|
46
|
+
<text class="node" x="558" y="420" text-anchor="middle" fill="#f4f7f9">code</text>
|
|
47
|
+
<text class="sub" x="558" y="444" text-anchor="middle">disposable</text>
|
|
50
48
|
</svg>
|
|
Binary file
|
package/docs/installation.md
CHANGED
|
@@ -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
|
| --- | --- |
|