@turri-dev/cli 0.1.11-next.be75b46 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/sandbox.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # The sandbox
2
2
 
3
- A rendering agent is building one module. It has no business reading the `.plain` specs it was not given, the rest of the project, or anything else on the machine - and pi's `cwd` is only where relative paths start: an absolute path resolves to itself and `bash` can `cd` anywhere. So turri fences its agents: their file tools check every path, and every command they run on this machine passes a command check, runs in an environment of the module's own behind an OS sandbox where the OS has one, and is audited afterwards. The rules live in code that runs the same on every platform; the OS sandbox is an extra wall under them.
3
+ A regenerating agent is building one module. It has no business reading \*\*\*plain it was not given, the rest of the project, or anything else on the machine - and [pi](https://pi.dev)'s `cwd` is only where relative paths start: an absolute path resolves to itself and `bash` can `cd` anywhere. So turri fences its agents: their file tools check every path, and every command they run on this machine passes a command check, runs in an environment of the module's own behind an OS sandbox where the OS has one, and is audited afterwards. The rules live in code that runs the same on every platform; the OS sandbox is an extra wall under them.
4
4
 
5
5
  ## What an agent can reach
6
6
 
@@ -14,14 +14,14 @@ Each agent's file tools reach its own module and only the parts its role needs:
14
14
  | Explorers | its module's `code/` | nothing |
15
15
  | Test review | its module's `tests/` and `code/` | nothing |
16
16
 
17
- Nothing else exists for them: not another module, not turri's scratch area `plain_modules/.turri` (a test run's whole output reaches them through `read_output`, not by path), not the module's lineage `.plaingit`, and nothing above `plain_modules`. The specs it is building from reach it only through the prompt the harness writes. Nor does any context file: pi puts every `AGENTS.md` / `CLAUDE.md` from an agent's folder up into its system prompt, and turri turns that off for every agent it starts - so the project's own `AGENTS.md`, or yours in `~/.pi/agent`, never reaches a rendering agent, a judge or a skill's agent. Nor do pi's skills, which pi would list for an agent to load: each agent turri starts has one job, and turri runs its own gated skills itself. The conversation you talk to still loads both, as pi does - but for the skills turri has already, its own and any about \*\*\*plain (see [a project's own skills](skills-and-gates.md#a-projects-own-skills)).
17
+ Nothing else exists for them: not another module, not turri's scratch area `plain_modules/.turri` (a test run's whole output reaches them through `read_output`, not by path), not the module's lineage `.plaingit`, and nothing above `plain_modules`. The \*\*\*plain description it is building from reaches it only through the prompt the harness writes. Nor does any context file: [pi](https://pi.dev) puts every `AGENTS.md` / `CLAUDE.md` from an agent's folder up into its system prompt, and turri turns that off for every agent it starts - so the project's own `AGENTS.md`, or yours in `~/.pi/agent`, never reaches a regenerating agent, a judge or a skill's agent. Nor do [pi](https://pi.dev)'s skills, which [pi](https://pi.dev) would list for an agent to load: each agent turri starts has one job, and turri runs its own gated skills itself. The conversation you talk to still loads both, as [pi](https://pi.dev) does - but for the skills turri has already, its own and any about [\*\*\*plain](https://plainlang.org/docs/) (see [a project's own skills](skills-and-gates.md#a-projects-own-skills)).
18
18
 
19
19
  ## Every agent knows its walls
20
20
 
21
21
  Each agent's system prompt says where its walls are, in words generated from its fence, so they say exactly what is enforced and are the same on every platform:
22
22
 
23
23
  - its own folder or folders, by absolute path, and what its file tools read and write there - a role that only reads is told it writes nothing, "not with your file tools, not with your shell"; the conformance agent that it reads the code under test but writes tests, not code; the code fixer that it reads the conformance tests but fixes the code;
24
- - that its shell runs on this machine, in its folder, with this machine's toolchains, a home folder and a temporary folder of its own (`$HOME`, `$TMPDIR`), the package caches and an open network, and that nothing else exists for it - not the specs, not the project's other folders or modules, not your home;
24
+ - that its shell runs on this machine, in its folder, with this machine's toolchains, a home folder and a temporary folder of its own (`$HOME`, `$TMPDIR`), the package caches and an open network, and that nothing else exists for it - not \*\*\*plain, not the project's other folders or modules, not your home;
25
25
  - what happens on a reach outside, matter-of-fact: a command that names a path outside is refused before it runs, and anything a command changes outside is put back, and it is told;
26
26
  - that the project's dependencies are its own, through the build manifest, and installing anything else is not: what the machine lacks it reports with `report_environment_error`, naming what is missing, and goes no further with what depends on it (see [when the environment lacks something](regenerating.md#when-the-environment-lacks-something));
27
27
  - that it may stop only what it started, and that a port something else holds means another port; that `ps` may show nothing but what it started, so a server is checked with `kill -0 $!`, its log or a request;
@@ -33,7 +33,7 @@ No other path is named - nothing into `plain_modules/.turri`. A test run or a co
33
33
 
34
34
  ## File tools
35
35
 
36
- `src/regenerate/confine.ts` rebuilds `read`, `write`, `edit`, `ls`, `grep` and `find` from pi's own factories with a check in front of every path. The check runs twice: once on the argument the tool was called with, and once on the absolute path the tool resolved it to, because the two can differ. Symlinks are resolved before comparing, so a link cannot walk out, and a glob containing `..` is refused outright.
36
+ `src/regenerate/confine.ts` rebuilds `read`, `write`, `edit`, `ls`, `grep` and `find` from [pi](https://pi.dev)'s own factories with a check in front of every path. The check runs twice: once on the argument the tool was called with, and once on the absolute path the tool resolved it to, because the two can differ. Symlinks are resolved before comparing, so a link cannot walk out, and a glob containing `..` is refused outright.
37
37
 
38
38
  The record of what the code is built from, `code/.turri.json`, is the harness's: no agent reads or writes it. `read` refuses it, and `ls`, `grep` and `find` leave it out of what they list - as the command check, the OS sandbox and the audit keep it from commands.
39
39
 
@@ -82,11 +82,11 @@ Under the check, where the OS offers one, the command runs behind an OS sandbox
82
82
 
83
83
  ### The audit
84
84
 
85
- After every agent command, on every platform - a sandbox up or not - turri puts back what the command's role may not change inside the project (`src/regenerate/audit.ts`): the specs, `config*.yaml`, the testing scripts, every credential file (the agent's own folder included), the code record, the module's lineage `.plaingit` (its refs and settings), the other modules, the folders the role only reads, and the part of its own module it does not write - the coding agent's conformance tests. A changed file is put back, a new one removed, a deleted one brought back: from a copy kept before the command, or - another module - as its last commit has it, unless a pulse is at work in that module, whose writes are not this command's to undo (left, and logged). Nor is a change the conversation made while the command ran: the chat's `edit` and `write`, a spec diff it applied, or anything while a command of its own ran - the chat editing a spec beside a pulse is the person's change, not the agent's, so it is left and logged (`src/regenerate/conversation-writes.ts`). The agents of a pulse start without extensions, so nothing of theirs is marked so. The agent is told in its command's result what was put back; the job's log gets a line; you are not told, and the pulse goes on. It compares sizes and modification times, skipping dependencies and build output, and contents only where those differ - about 35 ms on a project of 3000 files. Outside the project nothing can be put back: that is the OS sandbox's, and on Windows an accepted gap.
85
+ After every agent command, on every platform - a sandbox up or not - turri puts back what the command's role may not change inside the project (`src/regenerate/audit.ts`): \*\*\*plain, `config*.yaml`, the testing scripts, every credential file (the agent's own folder included), the code record, the module's lineage `.plaingit` (its refs and settings), the other modules, the folders the role only reads, and the part of its own module it does not write - the coding agent's conformance tests. A changed file is put back, a new one removed, a deleted one brought back: from a copy kept before the command, or - another module - as its last commit has it, unless a pulse is at work in that module, whose writes are not this command's to undo (left, and logged). Nor is a change the conversation made while the command ran: the chat's `edit` and `write`, a \*\*\*plain diff it applied, or anything while a command of its own ran - the chat editing \*\*\*plain beside a pulse is the person's change, not the agent's, so it is left and logged (`src/regenerate/conversation-writes.ts`). The agents of a pulse start without extensions, so nothing of theirs is marked so. The agent is told in its command's result what was put back; the job's log gets a line; you are not told, and the pulse goes on. It compares sizes and modification times, skipping dependencies and build output, and contents only where those differ - about 35 ms on a project of 3000 files. Outside the project nothing can be put back: that is the OS sandbox's, and on Windows an accepted gap.
86
86
 
87
87
  ## The web
88
88
 
89
- Every rendering agent - render, spec change, conformance, regression repair - can also search the web and read a page, for a library's documentation or an error it has not seen:
89
+ Every regenerating agent - regenerate, \*\*\*plain change, conformance, regression repair - can also search the web and read a page, for a library's documentation or an error it has not seen:
90
90
 
91
91
  | Tool | What it does |
92
92
  | --- | --- |
@@ -99,7 +99,7 @@ DuckDuckGo is used without a key through its Lite page, which is not an official
99
99
 
100
100
  ## Outside the sandbox: deploy
101
101
 
102
- What `deploy` starts runs on your machine unfenced: you asked to try the software, with your toolchains, in your browser. It runs from `dist/<module>`, a copy of the last commit outside `plain_modules`, so no rendering agent reaches it. Nothing is installed there without your say-so - every command that would install or download is put to you first, and the chat's shell refuses the rest. See [deploy](deploy.md).
102
+ What `deploy` starts runs on your machine unfenced: you asked to try the software, with your toolchains, in your browser. It runs from `dist/<module>`, a copy of the last commit outside `plain_modules`, so no regenerating agent reaches it. Nothing is installed there without your say-so - every command that would install or download is put to you first, and the chat's shell refuses the rest. See [deploy](deploy.md).
103
103
 
104
104
  ## What turri keeps beside the code
105
105
 
@@ -2,16 +2,16 @@
2
2
 
3
3
  A skill's review step is normally a paragraph asking the agent to check its own work. Agents talk their way past paragraphs. turri makes the review an edge in a graph instead: the harness decides when a gate runs, spawns the judges itself, and branches on a structured verdict. The model's only job is producing the artifact under review.
4
4
 
5
- pi supplies the agent runtime (sessions, tools, multi-provider models); LangGraph supplies the control flow. The gate syntax lives in `SKILL.md`, so a gated skill still loads in any spec-compliant harness - it just runs ungated there.
5
+ [pi](https://pi.dev) supplies the agent runtime (sessions, tools, multi-provider models); LangGraph supplies the control flow. The gate syntax lives in `SKILL.md`, so a gated skill still loads in any harness that follows the Agent Skills standard - it just runs ungated there.
6
6
 
7
7
  ## The syntax
8
8
 
9
- Under `metadata:`, which the Agent Skills spec reserves for arbitrary key-values:
9
+ Under `metadata:`, which the Agent Skills standard reserves for arbitrary key-values:
10
10
 
11
11
  ```yaml
12
12
  ---
13
- name: draft-spec
14
- description: Drafts a ***plain spec, judged before it is kept.
13
+ name: draft-plain
14
+ description: Drafts ***plain, judged before it is kept.
15
15
  allowed-tools: [read, write, edit, bash, ls, grep, find]
16
16
  metadata:
17
17
  gates:
@@ -39,21 +39,21 @@ A skill declares its steps with a `## Steps` section whose `###` headings are th
39
39
 
40
40
  `command-only: true` under `metadata:` keeps a skill from the model: it is started only by the person, with its slash command - `/brood`, `/spawn` and `/bud` are. turri registers the command for every such skill, the project's own too. Typed, the command puts what was typed in the chat as the person's message, and lets `cultivate` start the skill once; the run starting uses that up, so the model cannot start it again later on its own. Without the command, `cultivate` refuses it and the model is told to name the command to the person instead - the skill's line in `cultivate`'s list says so too.
41
41
 
42
- `paced: false` under `metadata:` lets a skill write spec changes one after another on its own: `write_spec_diff` otherwise refuses a new change until the user has answered a question since the last one - one answer, one change, as an interview writes them. `spawn` is unpaced: the tool tells it so, and after each change to write the next at once, not to carry on with an interview it does not have.
42
+ `paced: false` under `metadata:` lets a skill write \*\*\*plain changes one after another on its own: `change_plain` otherwise refuses a new change until the user has answered a question since the last one - one answer, one change, as an interview writes them. `spawn` is unpaced: the tool tells it so, and after each change to write the next at once, not to carry on with an interview it does not have.
43
43
 
44
44
  `questioning:` under `metadata:` names the steps the questioning level caps (`src/questioning.ts`) - `brood`'s `[stack, product]`, `bud`'s `[product]`. When there are any and someone to ask, the harness itself asks the first question of the first step: how many questions those steps may ask - light (3), medium (5), thorough (10) or best judgement - and tells the agent the answer beside each step's instructions, with how broad each question is to be: broad at light, a whole area at once, narrow at thorough, an edge case at a time. Every level covers the same ground, in less or more depth. In a capped step `ask_user` refuses a question past the cap; the step's closing confirmation, asked with `confirms_phase: true`, is never counted. `end_questions` ends the step's questions sooner - best judgement's only end. What the questions leave open, the agent decides itself and says what it chose. The level, and how many questions each step has asked, are kept in the run's `run.json`, so a resumed run keeps them. A step it does not name, or a run with nobody to ask, is not capped.
45
45
 
46
- `spec-diff-per-answer:` under `metadata:` names the steps where every answer is a spec change - `brood`'s and `bud`'s `[product]`, at every questioning level. There `ask_user` refuses the next question until the last answer is written with `write_spec_diff` (a confirmation, or a question dismissed, owes nothing), and `write_spec_diff` no longer waits for an answer before a change: the agent writes as many as the step needs, those it decides itself included. The level limits the questions, never the spec changes.
46
+ `change-plain-per-answer:` under `metadata:` names the steps where every answer is a \*\*\*plain change - `brood`'s and `bud`'s `[product]`, at every questioning level. There `ask_user` refuses the next question until the last answer is written with `change_plain` (a confirmation, or a question dismissed, owes nothing), and `change_plain` no longer waits for an answer before a change: the agent writes as many as the step needs, those it decides itself included. The level limits the questions, never the \*\*\*plain changes.
47
47
 
48
48
  A step ends when its agent's turn does. A turn that ends on a question in the reply would end the step there, half done, on a question no one reads - the person sees only what `ask_user` asks. So such a step is sent back, twice at most: to ask with `ask_user` if the question is truly the person's, or, in an unpaced skill, to decide it itself - then to carry on until the step is done.
49
49
 
50
50
  `phases:` under `metadata:` says what each step does, in a short line a step by its name - for the person, not the agent: while the skill runs, the line above the input shows its phases, and under them the one it is in with its line (`skeleton - sets up the project and writes the smallest spec that runs, so the first build can start`). A phase's name alone says little to someone who has not read the skill. Every skill with more than one step describes each; a line for a step the skill does not have is an error, as a gate after one is.
51
51
 
52
- A skill gets the tools its `allowed-tools` names, and nothing else: one that names none has none. So every skill here names its own - the file tools (`read`, `write`, `edit`, `bash`, `ls`, `grep`, `find`) for the ones that write specs, resources or scripts, only the reading ones for those that analyse and report, and turri's own (`write_spec_diff` and the ones below) where a skill uses them.
52
+ A skill gets the tools its `allowed-tools` names, and nothing else: one that names none has none. So every skill here names its own - the file tools (`read`, `write`, `edit`, `bash`, `ls`, `grep`, `find`) for the ones that write \*\*\*plain, resources or scripts, only the reading ones for those that analyse and report, and turri's own (`change_plain` and the ones below) where a skill uses them.
53
53
 
54
- What a run changed is part of its result: every file of the project it added, changed or removed - its specs, resources, scripts; not the built code - or that it changed none, which is said as such, so a run that wrote nothing is never reported as done. A skill without gates says it was not reviewed. A `.plain` file a skill edited with the file tools is not rendered by it: the result says so, for the conversation to render the change.
54
+ What a run changed is part of its result: every file of the project it added, changed or removed - its \*\*\*plain, resources, scripts; not the built code - or that it changed none, which is said as such, so a run that wrote nothing is never reported as done. A skill without gates says it was not reviewed. A `.plain` file a skill edited with the file tools is not regenerated by it: the result says so, for the conversation to regenerate the change.
55
55
 
56
- A skill acts on the work loop with the conversation's own tools, on the conversation's loop - never copies of them (`src/loop-tools.ts`). Every skill with the loop has `drift`: what is left to regenerate, what is regenerating, and "Fully regenerated" once nothing is. One that lists `regenerate` and `halt` in `allowed-tools` - as `mature` does - can regenerate what a module's spec holds and halt a pulse, with why. `mature` lists two tools of its own as well: `watch_jobs` waits for its pulses, coming back as soon as one struggles - sent back four times or more, five setbacks, a spec conflict, a failure run again, over half an hour - with what it went through; `spec_findings` reads back, numbered and each once, what the pulses recorded since the run started: the assumptions and pitfalls in every codebase's working notes, a failed pulse's diagnosis, a spec that contradicts itself.
56
+ A skill acts on the work loop with the conversation's own tools, on the conversation's loop - never copies of them (`src/loop-tools.ts`). Every skill with the loop has `drift`: what is left to regenerate, what is regenerating, and "Fully regenerated" once nothing is. One that lists `regenerate` and `halt` in `allowed-tools` - as `mature` does - can regenerate what a module's \*\*\*plain holds and halt a pulse, with why. `mature` lists two tools of its own as well: `watch_jobs` waits for its pulses, coming back as soon as one struggles - sent back four times or more, five setbacks, a \*\*\*plain conflict, a failure run again, over half an hour - with what it went through; `spec_findings` reads back, numbered and each once, what the pulses recorded since the run started: the assumptions and pitfalls in every codebase's working notes, a failed pulse's diagnosis, \*\*\*plain that contradicts itself.
57
57
 
58
58
  Each step is told where its skill's files are, so a path it names - `references/intent.md` - and a skill it says to follow - `implement-unit-testing-script` - are found without searching.
59
59
 
@@ -73,17 +73,17 @@ A gate can also have a `continuous` reviewer: a read-only agent that re-reads th
73
73
 
74
74
  ## What a step can do
75
75
 
76
- - **Interview the user.** `ask_user` puts one question to the person. In the TUI it is a dialog; from the CLI the options are numbered. Offering choices is required, not optional - a call with neither `options` nor `free_form: true` is refused - and every list carries "Something else…". `multi_select: true` gives every option a box, ticked and unticked as it is chosen - a pick made by mistake is undone by choosing it again - until they choose Done; what they type is one more box, ticked. "Something else…", "Chat about this" and Done are no boxes. The skill's agent talks in a session of its own that the person never sees, so what the person must read to answer - the brief to confirm, the plan to approve - it passes as `ask_user`'s `context`, shown above the question in the dialog; `brood`, `spawn` and `bud` pass their briefs so. The agents are told to keep it to what can be taken in at a glance, about fifteen lines; one longer scrolls in a window of its own above the question (`src/question-dialog.ts`) - PgUp/PgDn, Shift+Up/Down or the mouse wheel - so the question and its options always show below it. pi's own select, which turri used before, put it all in its title, and a long brief pushed the options off the screen. Nothing else it wrote is shown there: the last thing it said before asking - its account of a probe, a regeneration - is kept with the answer, in the run's trace and the transcript, never in the dialog.
76
+ - **Interview the user.** `ask_user` puts one question to the person. In the TUI it is a dialog; from the CLI the options are numbered. Offering choices is required, not optional - a call with neither `options` nor `free_form: true` is refused - and every list carries "Something else…". `multi_select: true` gives every option a box, ticked and unticked as it is chosen - a pick made by mistake is undone by choosing it again - until they choose Done; what they type is one more box, ticked. "Something else…", "Chat about this" and Done are no boxes. The skill's agent talks in a session of its own that the person never sees, so what the person must read to answer - the brief to confirm, the plan to approve - it passes as `ask_user`'s `context`, shown above the question in the dialog; `brood`, `spawn` and `bud` pass their briefs so. The agents are told to keep it to what can be taken in at a glance, about fifteen lines; one longer scrolls in a window of its own above the question (`src/question-dialog.ts`) - PgUp/PgDn, Shift+Up/Down or the mouse wheel - so the question and its options always show below it. [pi](https://pi.dev)'s own select, which turri used before, put it all in its title, and a long brief pushed the options off the screen. Nothing else it wrote is shown there: the last thing it said before asking - its account of a probe, a regeneration - is kept with the answer, in the run's trace and the transcript, never in the dialog.
77
77
 
78
- Beside "Something else…", every list an agent's question shows carries "Chat about this": the person says what they want to talk over - why it is asked, what an option means for later - and it goes back to the agent that asked, as no answer. A skill's agent answers in the `context` of the question asked again, since nothing else it writes reaches them; the conversation's agent answers in its reply and ends its turn, the person going on in the chat. It is not counted against the questioning level, and owes no spec change. The harness's own first question - how many the phases may ask - has no agent to answer, and does not offer it. Either choice is backed out of with Esc, or by typing nothing: the options come back, so one picked by accident costs nothing. Esc on the options themselves dismisses the question.
79
- - **Be asked something on the side.** A skill runs inside one `cultivate` call, so the conversation can answer nothing until it returns. `/btw <question>` (`btw.ts`) can: pi runs a command at once, even mid-turn, and it starts an agent of its own - fresh, on the chat's model, reading the project and turri and changing nothing - shown the conversation, the running skill's trace and the end of its agent's session, and the pulses. It is told it is turri, as the conversation's agent is (`identity.ts`), and answers as turri, in the first person - never as an onlooker who sends the person to turri with a message to pass on. Its answer goes in the transcript as an entry, in no model's context: the conversation and the skill go on as if nothing was asked. While a question of the skill's has the keys, `/btw <question>` typed as the answer (under "Something else…") is the same: answered, and the question asked again with the answer above it - the skill's agent never sees it.
80
- - **Build while it interviews.** `write_spec_diff` (`brood`, `spawn`) writes a change to the spec and queues its render in one call; the interview carries on while the builds run in the background, one after another. `drift` shows how they stand. The person sees its calls as `mutate`: a tool can be shown by a name of its own (`SHOWN_AS` in `regenerate/activity.ts`), while its agent calls it by the one that says what it takes.
78
+ Beside "Something else…", every list an agent's question shows carries "Chat about this": the person says what they want to talk over - why it is asked, what an option means for later - and it goes back to the agent that asked, as no answer. A skill's agent answers in the `context` of the question asked again, since nothing else it writes reaches them; the conversation's agent answers in its reply and ends its turn, the person going on in the chat. It is not counted against the questioning level, and owes no \*\*\*plain change. The harness's own first question - how many the phases may ask - has no agent to answer, and does not offer it. Either choice is backed out of with Esc, or by typing nothing: the options come back, so one picked by accident costs nothing. Esc on the options themselves dismisses the question.
79
+ - **Be asked something on the side.** A skill runs inside one `cultivate` call, so the conversation can answer nothing until it returns. `/btw <question>` (`btw.ts`) can: [pi](https://pi.dev) runs a command at once, even mid-turn, and it starts an agent of its own - fresh, on the chat's model, reading the project and turri and changing nothing - shown the conversation, the running skill's trace and the end of its agent's session, and the pulses. It is told it is turri, as the conversation's agent is (`identity.ts`), and answers as turri, in the first person - never as an onlooker who sends the person to turri with a message to pass on. Its answer goes in the transcript as an entry, in no model's context: the conversation and the skill go on as if nothing was asked. While a question of the skill's has the keys, `/btw <question>` typed as the answer (under "Something else…") is the same: answered, and the question asked again with the answer above it - the skill's agent never sees it.
80
+ - **Build while it interviews.** `change_plain` (`brood`, `spawn`) writes a change to \*\*\*plain and queues its regeneration in one call; the interview carries on while the builds run in the background, one after another. `drift` shows how they stand. The person sees its calls as `mutate`: a tool can be shown by a name of its own (`SHOWN_AS` in `regenerate/activity.ts`), while its agent calls it by the one that says what it takes.
81
81
  - **Regenerate, and conform.** A skill that names them in `allowed-tools` gets the conversation's own `regenerate`, `drift`, `halt` and `conform`, on its loop: `conform` schedules a conformance pulse for functional specs not built yet, as it does from the chat. The skill never waits for them.
82
- - **Choose the render model**, when the user says so - `engine`, the same tool the conversation has.
82
+ - **Choose the model that regenerates**, when the user says so - `engine`, the same tool the conversation has.
83
83
 
84
84
  The harness still owns the sequence. A step chooses what to ask; it does not choose whether the gate afterwards runs. A run is saved as it goes, so an interrupted one can be resumed where it stopped.
85
85
 
86
- While a skill runs in the chat, `cultivate`'s output leads with what its agent is doing now: the skill, its step and how long each has taken, its last few tool calls, and the end of what it is saying, refreshed every few seconds. pi shows only a running tool's first lines, and a step can take many minutes without a new line in the trace. The result the conversation gets back keeps the trace alone.
86
+ While a skill runs in the chat, `cultivate`'s output leads with what its agent is doing now: the skill, its step and how long each has taken, its last few tool calls, and the end of what it is saying, refreshed every few seconds. [pi](https://pi.dev) shows only a running tool's first lines, and a step can take many minutes without a new line in the trace. The result the conversation gets back keeps the trace alone.
87
87
 
88
88
  ## A project's own skills
89
89
 
@@ -91,15 +91,15 @@ A project can have skills of its own, in the same format: a folder a skill, with
91
91
 
92
92
  A name is one skill. turri's own come first, then `.turri/skills/`, then `.agents/skills/`; a skill whose name is taken already is left out, and the list says so - a project's `brood` never quietly stands in for turri's. A skill that does not parse is said to have failed to load, by its folder.
93
93
 
94
- `.turri/` is turri's scratch folder and ignores itself, all but `skills/`: its `.gitignore` is `*`, `!skills/`, `!skills/**`, and one written before is brought up to date. pi reads `.agents/skills/` too, as skills of its own the chat could follow by hand - the chat is told to run any `cultivate` lists with `cultivate`, so its gates still run.
94
+ `.turri/` is turri's scratch folder and ignores itself, all but `skills/`: its `.gitignore` is `*`, `!skills/`, `!skills/**`, and one written before is brought up to date. [pi](https://pi.dev) reads `.agents/skills/` too, as skills of its own the chat could follow by hand - the chat is told to run any `cultivate` lists with `cultivate`, so its gates still run.
95
95
 
96
- The skills pi offers the chat leave out what turri has already (`src/chat-skills.ts`): a skill of a name turri runs itself - its own, or the project's - and any skill about \*\*\*plain, its specs or their testing scripts. pi gathers skills from `~/.pi/agent/skills`, `~/.agents/skills` and the `.agents/skills` of every folder from where turri started up to its git root, so a folder above the project can hold another tool's \*\*\*plain skills - a `load-plain-reference` the chat would load at the start of every session, though the whole \*\*\*plain reference, turri's [rules](#rules), is in its system prompt already. The person is told once which were left out. A skill about anything else is offered as pi offers it.
96
+ The skills [pi](https://pi.dev) offers the chat leave out what turri has already (`src/chat-skills.ts`): a skill of a name turri runs itself - its own, or the project's - and any skill about [\*\*\*plain](https://plainlang.org/docs/) or its testing scripts. [pi](https://pi.dev) gathers skills from `~/.pi/agent/skills`, `~/.agents/skills` and the `.agents/skills` of every folder from where turri started up to its git root, so a folder above the project can hold another tool's \*\*\*plain skills - a `load-plain-reference` the chat would load at the start of every session, though the whole \*\*\*plain reference, turri's [rules](#rules), is in its system prompt already. The person is told once which were left out. A skill about anything else is offered as [pi](https://pi.dev) offers it.
97
97
 
98
- A skill is found once, as turri starts: one added while it runs is there the next time. A project's skill agent runs as turri's do - pi's file tools and `bash` on the machine, no fence - so a project's skills are as trusted as the project.
98
+ A skill is found once, as turri starts: one added while it runs is there the next time. A project's skill agent runs as turri's do - [pi](https://pi.dev)'s file tools and `bash` on the machine, no fence - so a project's skills are as trusted as the project.
99
99
 
100
100
  ## Rules
101
101
 
102
- `genome/rules/` holds the \*\*\*plain authoring rules. Every rule, in full, is appended to the system prompt of the conversation and of the agents that write specs - never of the ones that write code. The block is byte-identical every time, so it sits inside the cached prefix - but it is ~40k tokens. The trade is deliberate: a rule the model has to decide to go and read is a rule it can skip. `src/rules.ts` is where to change it.
102
+ `genome/rules/` holds the \*\*\*plain authoring rules. Every rule, in full, is appended to the system prompt of the conversation and of the agents that write \*\*\*plain - never of the ones that write code. The block is byte-identical every time, so it sits inside the cached prefix - but it is ~40k tokens. The trade is deliberate: a rule the model has to decide to go and read is a rule it can skip. `src/rules.ts` is where to change it.
103
103
 
104
104
  ## Trying the gate semantics
105
105
 
package/docs/theming.md CHANGED
@@ -8,33 +8,33 @@ turri --use-theme turri-light/turri-dark # follows terminal appearance
8
8
  turri --use-theme dark # pi's own
9
9
  ```
10
10
 
11
- The second form follows your terminal's light/dark appearance. The default is for `turri` only: pi's settings are left alone, so a plain `pi` keeps its theme. With the themes copied to `~/.pi/agent/themes/` (`cp .pi/themes/*.json ~/.pi/agent/themes/`), `{"theme": "turri-dark"}` in `~/.pi/agent/settings.json` gives a plain `pi` the same one.
11
+ The second form follows your terminal's light/dark appearance. The default is for `turri` only: [pi](https://pi.dev)'s settings are left alone, so a plain `pi` keeps its theme. With the themes copied to `~/.pi/agent/themes/` (`cp .pi/themes/*.json ~/.pi/agent/themes/`), `{"theme": "turri-dark"}` in `~/.pi/agent/settings.json` gives a plain `pi` the same one.
12
12
 
13
13
  Bad input fails quietly - an unknown `--use-theme` name and a malformed theme file both run without complaint - so if the colours do not change, suspect a typo rather than the mechanism.
14
14
 
15
15
  ## The palette
16
16
 
17
- Both themes, the command line's colours, the spec diff's, the banner's jellyfish and the docs' diagram are drawn from turri's design system and nothing else: dark, quiet surfaces, one pink thing to press, neon for anything you can follow. These are all the colours turri has - none is mixed, tinted or added, but for the jellyfish's tentacles, every other pixel of which the design draws half seen.
17
+ Both themes, the command line's colours, the \*\*\*plain diff's, the banner's jellyfish and the docs' diagram are drawn from turri's design system and nothing else: dark, quiet surfaces, one pink thing to press, neon for anything you can follow. These are all the colours turri has - none is mixed, tinted or added, but for the jellyfish's tentacles, every other pixel of which the design draws half seen.
18
18
 
19
19
  | name | value | in `turri-dark` |
20
20
  |---|---|---|
21
21
  | abyss | `#0a0f1a` | the page - the HTML export's background, and the surface the contrast below is measured on |
22
22
  | tile | `#0d1420` | the HTML export's cards |
23
23
  | hēi sè | `#142030` | boxes, the HTML export's callouts |
24
- | blue whale | `#1e3442` | a highlighted line: `selectedBg`; an added line's fill in a spec diff |
24
+ | blue whale | `#1e3442` | a highlighted line: `selectedBg`; an added line's fill in a \*\*\*plain diff |
25
25
  | lines | `#22344a` | rules, dividers, frames: `borderMuted`, `mdHr` |
26
26
  | path | `#3e5a6b` | the editor's border, what is dim: `dim`, line numbers |
27
27
  | grauzone | `#85a3b2` | body text, and what can be skimmed: `text`, `muted` |
28
28
  | white | `#f4f7f9` | headings, tool titles, what you typed |
29
- | siesta tan | `#e9d8c8` | strings in code, \*\*\*plain's text in quotes or backticks |
30
- | eggplant | `#732553` | a removed line's fill in a spec diff, the jellyfish's rim |
31
- | neon | `#5ce1e6` | links, focus, the selection, section labels, \*\*\*plain's concepts in a spec diff: `accent`, `mdLink`, regen's `◆` |
32
- | neon hover | `#8eebee` | inline code - siesta tan beside white text was too close to tell apart, and tiring to read - types in code, an added line's fill in a light theme's spec diff: `mdCode` |
33
- | pink | `#ff5c8d` | the wordmark, the jellyfish's bell, and what is done - its `✓`, the spec map's `◇` and `◆`: `success` |
29
+ | siesta tan | `#e9d8c8` | strings in code, [\*\*\*plain](https://plainlang.org/docs/)'s text in quotes or backticks |
30
+ | eggplant | `#732553` | a removed line's fill in a \*\*\*plain diff, the jellyfish's rim |
31
+ | neon | `#5ce1e6` | links, focus, the selection, section labels, \*\*\*plain's concepts in a \*\*\*plain diff: `accent`, `mdLink`, regen's `◆` |
32
+ | neon hover | `#8eebee` | inline code - siesta tan beside white text was too close to tell apart, and tiring to read - types in code, an added line's fill in a light theme's \*\*\*plain diff: `mdCode` |
33
+ | pink | `#ff5c8d` | the wordmark, the jellyfish's bell, and what is done - its `✓`, the \*\*\*plain map's `◇` and `◆`: `success` |
34
34
  | pink hover | `#ff7fa6` | the glint on the jellyfish's bell, \*\*\*plain's section headers, a removed line's `-`, keywords in code |
35
35
  | orchid | `#a9479a` | the jellyfish's bell, between its pink and its indigo - nowhere else |
36
36
  | indigo | `#5b57d9` | the jellyfish's bell, at its rim - nowhere else |
37
- | orange | `#ff7a3d` | the jellyfish's gut, the `$` of bash mode, and everything that asks for you - what failed (`✗`), what works with limits, what needs you (the spec map's `!`): `error`, `warning` |
37
+ | orange | `#ff7a3d` | the jellyfish's gut, the `$` of bash mode, and everything that asks for you - what failed (`✗`), what works with limits, what needs you (the \*\*\*plain map's `!`): `error`, `warning` |
38
38
 
39
39
  The design system keeps pink for the one thing to press and orange for the `$` prompt alone. A terminal has no buttons, but it needs status colours, so pink carries what is done and orange everything that asks for you - what failed, what works with limits, what waits on you - the mark (`✗`, `!`, `■`) telling them apart. The palette has no red.
40
40
 
@@ -68,10 +68,10 @@ With tool rows carrying no background, their state is signalled by the title and
68
68
 
69
69
  **The window's background is your terminal's setting**, and the transcript is drawn on it. For the brand look, set it to abyss, `#0a0f1a`, in your terminal profile.
70
70
 
71
- ## Spec diffs
71
+ ## \*\*\*plain diffs
72
72
 
73
- A spec diff in the panels is not painted with the theme's colours for code: those are made for code, and the turri themes' backgrounds are all the surface colour, so a diff drawn with them would have no fills and hardly any highlighting. It has a palette of its own (`src/regenerate/plain-palette.ts`), one for a dark theme and one for a light, picked by the theme's appearance: a hue for each piece of \*\*\*plain - a section's header, a concept, a link, text in quotes or backticks, a front matter key, a module, a comment - at least 7:1 on the surface, and a fill behind each added and removed line: on a dark theme blue whale and eggplant, on a light one neon hover and siesta tan. Every colour reads at least 4.8:1 on blue whale and 5:1 on the light fills; eggplant, the design system's only warm dark fill, takes the pinks and grauzone down to 3.7:1.
73
+ A \*\*\*plain diff in the panels is not painted with the theme's colours for code: those are made for code, and the turri themes' backgrounds are all the surface colour, so a diff drawn with them would have no fills and hardly any highlighting. It has a palette of its own (`src/regenerate/plain-palette.ts`), one for a dark theme and one for a light, picked by the theme's appearance: a hue for each piece of \*\*\*plain - a section's header, a concept, a link, text in quotes or backticks, a front matter key, a module, a comment - at least 7:1 on the surface, and a fill behind each added and removed line: on a dark theme blue whale and eggplant, on a light one neon hover and siesta tan. Every colour reads at least 4.8:1 on blue whale and 5:1 on the light fills; eggplant, the design system's only warm dark fill, takes the pinks and grauzone down to 3.7:1.
74
74
 
75
75
  ## The font
76
76
 
77
- The design system's fonts cannot be applied. pi draws into a terminal, so the typeface is your terminal emulator's setting. Its monospaced face is Geist Mono - set it in iTerm2 / Ghostty / Alacritty for the brand look; Neue Montreal, for headings, is proportional, and a terminal needs a monospaced face.
77
+ The design system's fonts cannot be applied. [pi](https://pi.dev) draws into a terminal, so the typeface is your terminal emulator's setting. Its monospaced face is Geist Mono - set it in iTerm2 / Ghostty / Alacritty for the brand look; Neue Montreal, for headings, is proportional, and a terminal needs a monospaced face.