@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/README.md +7 -7
- package/dist/{spec-diff-cli.js → change-plain-cli.js} +3 -3
- package/dist/cli.js +1 -1
- package/dist/extension.js +4 -4
- package/dist/genome.js +5 -5
- package/dist/identity.js +8 -6
- package/dist/loop-cli.d.ts +1 -1
- package/dist/loop-cli.js +1 -1
- package/dist/regenerate/activity.js +2 -2
- package/dist/self-invocation.js +6 -6
- package/dist/skill.d.ts +3 -3
- package/dist/skill.js +2 -2
- package/dist/spec-tools.js +2 -2
- package/dist/stacks.js +1 -1
- package/dist/tools.js +1 -1
- package/docs/README.md +13 -13
- package/docs/authoring-plain.md +33 -33
- package/docs/config.md +25 -25
- package/docs/deploy.md +5 -5
- package/docs/development.md +12 -12
- package/docs/headless.md +18 -18
- package/docs/hosting.md +14 -14
- package/docs/images/how-turri-works.svg +1 -1
- package/docs/installation.md +13 -13
- package/docs/parser.md +6 -6
- package/docs/regenerating.md +78 -78
- package/docs/sandbox.md +7 -7
- package/docs/skills-and-gates.md +19 -19
- package/docs/theming.md +12 -12
- package/docs/using.md +50 -50
- package/genome/skills/brood/SKILL.md +11 -11
- package/genome/skills/brood/references/product.md +4 -4
- package/genome/skills/brood/references/testing.md +1 -1
- package/genome/skills/bud/SKILL.md +6 -6
- package/genome/skills/mature/SKILL.md +4 -4
- package/genome/skills/spawn/SKILL.md +4 -4
- package/package.json +1 -1
- /package/dist/{spec-diff-cli.d.ts → change-plain-cli.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
**turri** is named after *Turritopsis dohrnii*, the immortal jellyfish that regenerates itself from its young form, again and again - as **turri** regenerates the code from the durable intent - see [the name](#the-name).
|
|
12
12
|
|
|
13
|
-
**turri** has a desktop app too: [turri
|
|
13
|
+
**turri** has a desktop app too: [turri desktop](https://github.com/turri-oss/turri-desktop).
|
|
14
14
|
|
|
15
15
|
## Quick start
|
|
16
16
|
|
|
17
|
-
**turri** is built on [pi](https://pi.dev), the coding agent, and runs wherever pi runs: macOS, Linux, WSL; native Windows best effort, with Git Bash. You need git, bash and [Node](https://nodejs.org/en/download) 22.19 or newer - a Linux distribution's own `nodejs` package is often older. The agents use this machine's toolchains, fenced by a command check and an audit everywhere, and an OS sandbox where the OS has one - Seatbelt on macOS, bubblewrap on Linux (`apt install bubblewrap`) - see [installation](docs/installation.md#installation).
|
|
17
|
+
**turri** is built on [pi](https://pi.dev), the coding agent, and runs wherever [pi](https://pi.dev) runs: macOS, Linux, WSL; native Windows best effort, with Git Bash. You need git, bash and [Node](https://nodejs.org/en/download) 22.19 or newer - a Linux distribution's own `nodejs` package is often older. The agents use this machine's toolchains, fenced by a command check and an audit everywhere, and an OS sandbox where the OS has one - Seatbelt on macOS, bubblewrap on Linux (`apt install bubblewrap`) - see [installation](docs/installation.md#installation).
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
20
|
npm install -g @turri-dev/cli # puts `turri` on your PATH
|
|
@@ -23,7 +23,7 @@ turri doctor # checks the machine
|
|
|
23
23
|
|
|
24
24
|
To follow `main` as it moves, or work on turri itself, run it [from a clone](#from-a-clone) instead.
|
|
25
25
|
|
|
26
|
-
`doctor` checks for Node 22.19+, bash, the OS sandbox and a model provider for pi; in a project, the toolchains its modules need, what its testing scripts run against this machine, and that each model `config.turri.yaml` names answers with your credentials. It changes nothing without asking: when a check fails - the OS sandbox does not start, say - it says why, and the command for you to run. Run it again whenever something breaks. What each check means: [installation](docs/installation.md).
|
|
26
|
+
`doctor` checks for Node 22.19+, bash, the OS sandbox and a model provider for [pi](https://pi.dev); in a project, the toolchains its modules need, what its testing scripts run against this machine, and that each model `config.turri.yaml` names answers with your credentials. It changes nothing without asking: when a check fails - the OS sandbox does not start, say - it says why, and the command for you to run. Run it again whenever something breaks. What each check means: [installation](docs/installation.md).
|
|
27
27
|
|
|
28
28
|
Then start it in your project, signed in to your model providers:
|
|
29
29
|
|
|
@@ -101,7 +101,7 @@ With **turri** you work on the durable intent. You say what the software must do
|
|
|
101
101
|
|
|
102
102
|
## How it works
|
|
103
103
|
|
|
104
|
-
**turri** captures the durable intent in [\*\*\*plain](https://plainlang.org/), a language for saying what software should do. \*\*\*plain breaks the intent down into functionalities - small units, each a single thing the software does - and **turri** regenerates the code from them piece by piece.
|
|
104
|
+
**turri** captures the durable intent in [\*\*\*plain](https://plainlang.org/docs/), a language for saying what software should do. \*\*\*plain breaks the intent down into functionalities - small units, each a single thing the software does - and **turri** regenerates the code from them piece by piece.
|
|
105
105
|
|
|
106
106
|
1. **Define.** You say what the software should do; **turri** writes it down as \*\*\*plain.
|
|
107
107
|
2. **Review and refine.** You review \*\*\*plain and change it with **turri** until it says what you mean.
|
|
@@ -127,7 +127,7 @@ So the tools speak its language: **`regenerate`** regenerates the code from \*\*
|
|
|
127
127
|
|
|
128
128
|
## Models
|
|
129
129
|
|
|
130
|
-
`init-config`'s models are only a starting point. **turri** is built on [pi](https://
|
|
130
|
+
`init-config`'s models are only a starting point. **turri** is built on [pi](https://pi.dev), so any model [pi](https://pi.dev) supports works - Anthropic, OpenAI, Google, Amazon Bedrock, OpenRouter, a local one. A model is reached as [pi](https://pi.dev) reaches it - `/login`, in `turri` as in `pi`, the provider's API key in the environment (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, ...), or a cloud account - and named as [pi](https://pi.dev) names it, `provider/model-id`, with `:high`, `:low` and so on for how hard it thinks. It uses three: the **harness** model you talk to, which writes \*\*\*plain and plans the work - worth a capable one - and the **coding** and **judge** models its agents build and judge with, which can be smaller and cheaper, such as GPT-6 Luna or Gemini 3.1 Flash Lite. `turri --harness-model` and `--coding-model` choose them for a session, and [pi](https://pi.dev)'s `/model` switches the one you talk to as you go; to keep them for the project, set them with `/turri-config harness-model`, `/turri-config coding-model` and so on - each role's too, as `models.<role>` - or write them in its `config.turri.yaml`:
|
|
131
131
|
|
|
132
132
|
```yaml
|
|
133
133
|
harness-model: anthropic/claude-opus-5-5 # the model you talk to: a capable one
|
|
@@ -179,7 +179,7 @@ turri regenerate app.plain # regenerate the code from ***plain: planned int
|
|
|
179
179
|
turri regenerate app.plain 3 # regenerate functionality 3, changed or not
|
|
180
180
|
```
|
|
181
181
|
|
|
182
|
-
More: [using **turri**](docs/using.md); to
|
|
182
|
+
More: [using **turri**](docs/using.md); to regenerate \*\*\*plain overnight, see [running unattended](docs/headless.md).
|
|
183
183
|
|
|
184
184
|
## Documentation
|
|
185
185
|
|
|
@@ -192,7 +192,7 @@ Start at the [documentation index](docs/README.md), or go straight to a page:
|
|
|
192
192
|
| [Using **turri**](docs/using.md) | the conversation, skills, tools, pulses and regen, choosing models, the command line |
|
|
193
193
|
| [The project config](docs/config.md) | `config.turri.yaml` and each stack's `config.turri.<stack>.yaml`: every key, models and their inheritance, conformance, the testing scripts, stacks and choosing one, the old `config.yaml` |
|
|
194
194
|
| [Deploy](docs/deploy.md) | trying what is built: the last commit in `dist/<module>`, started on your machine, nothing installed without asking |
|
|
195
|
-
| [Running unattended](docs/headless.md) |
|
|
195
|
+
| [Running unattended](docs/headless.md) | regenerating \*\*\*plain overnight or in CI: the command, keeping it running, the results, resuming, rebuilding one functionality |
|
|
196
196
|
| [Authoring \*\*\*plain](docs/authoring-plain.md) | what \*\*\*plain is made of, the authoring rules, the skills that write and fix it, checking it |
|
|
197
197
|
| [Regenerating](docs/regenerating.md) | what each build does and in which order: functionalities, modules, regeneration, changes to \*\*\*plain, conformance and acceptance tests, working notes, what the code is built from |
|
|
198
198
|
| [The sandbox](docs/sandbox.md) | what agents can reach, the walls every command passes, time limits, the web tools |
|
|
@@ -4,9 +4,9 @@ import { checkPlainFile } from "./plain/check.js";
|
|
|
4
4
|
import { applySpecDiff, revertSpecDiff } from "./regenerate/regenerate.js";
|
|
5
5
|
import { parseArgs } from "./cli-options.js";
|
|
6
6
|
import { headlessOptions, runLoop } from "./loop-cli.js";
|
|
7
|
-
const USAGE = `usage: turri
|
|
7
|
+
const USAGE = `usage: turri change-plain <file.plain> <diff-file> [options]
|
|
8
8
|
|
|
9
|
-
Applies a change to
|
|
9
|
+
Applies a change to ***plain, given as a git diff of the .plain files, and builds it: the headless
|
|
10
10
|
counterpart of regenerate, given a diff. The diff is applied first, unless it is already in; then, as
|
|
11
11
|
turri regenerate does, what is left to build is planned into jobs and built.
|
|
12
12
|
|
|
@@ -40,7 +40,7 @@ runLoop({
|
|
|
40
40
|
startOver: options["start-over"] !== undefined,
|
|
41
41
|
...(buildDir !== undefined ? { buildDir } : {}),
|
|
42
42
|
...(report !== undefined ? { report } : {}),
|
|
43
|
-
command: ["
|
|
43
|
+
command: ["change-plain", ...process.argv.slice(2)],
|
|
44
44
|
ask: (loop) => loop.bringUp(plainFile),
|
|
45
45
|
}).catch((error) => {
|
|
46
46
|
console.error(error instanceof Error ? error.message : error);
|
package/dist/cli.js
CHANGED
|
@@ -78,7 +78,7 @@ const COMMANDS = {
|
|
|
78
78
|
glimpse: { module: "./regenerate-cli.ts", blurb: "plan the work and print the first job's prompt, starting nothing", args: ["--dry-run"] },
|
|
79
79
|
drift: { module: "./drift-cli.ts", blurb: "what is left to regenerate: how far the code has drifted from its spec" },
|
|
80
80
|
conform: { module: "./conform-cli.ts", blurb: "write and run a module's conformance tests" },
|
|
81
|
-
"
|
|
81
|
+
"change-plain": { module: "./change-plain-cli.ts", blurb: "apply a change to ***plain, given as a git diff, and regenerate it" },
|
|
82
82
|
validate: { module: "./validate-cli.ts", blurb: "validate a ***plain spec, regenerating nothing" },
|
|
83
83
|
lineage: { module: "./lineage-cli.ts", blurb: "a module's lineage: what was regenerated into it, and when" },
|
|
84
84
|
ledger: { module: "./ledger-cli.ts", blurb: "what turri did here: the last runs and their pulses, or one pulse, from the ledger" },
|
package/dist/extension.js
CHANGED
|
@@ -669,11 +669,11 @@ function turriExtension(pi, host) {
|
|
|
669
669
|
: "It changed no file in the project - whatever it said it wrote is not there. Say so to the user; do not report the work as done.";
|
|
670
670
|
// A skill that edits the specs with the file tools builds nothing: the change is the conversation's to render.
|
|
671
671
|
const specsChanged = changes.filter((path) => path.endsWith(".plain"));
|
|
672
|
-
const unrendered = specsChanged.length > 0 && !skill.allowedTools.includes("
|
|
672
|
+
const unrendered = specsChanged.length > 0 && !skill.allowedTools.includes("change_plain")
|
|
673
673
|
? `${specsChanged.join(", ")} changed, and nothing is regenerated from that yet: call regenerate for it, and the code is brought up to the spec.`
|
|
674
674
|
: "";
|
|
675
675
|
const gated = skill.steps.some((step) => step.gate !== undefined);
|
|
676
|
-
const first = skill.allowedTools.includes("
|
|
676
|
+
const first = skill.allowedTools.includes("change_plain") ? firstRegeneration(jobs.list(), began) : {};
|
|
677
677
|
const ttfr = first.started === undefined
|
|
678
678
|
? ""
|
|
679
679
|
: `Time to first regeneration: ${formatElapsed(first.started)} - its first pulse started that long after ${skill.name} began` +
|
|
@@ -1125,7 +1125,7 @@ export function projectConfigBlock(root) {
|
|
|
1125
1125
|
return [
|
|
1126
1126
|
...settings(readProjectConfig(dir), withStack(undefined, () => findTestScripts(dir)), dir === root ? `${projectConfigFile(dir)}, for the specs here:` : `${relative(root, dir)}/${projectConfigFile(dir)}, for the specs in ${relative(root, dir)}/ - built in ${relative(root, dir)}/plain_modules:`, where, stacks.length > 0 ? "project of stacks" : "project"),
|
|
1127
1127
|
...(stacks.length > 0
|
|
1128
|
-
? ["", `Stacks: ${stacks.join(", ")} - chosen now: ${chosen ?? "none, so the project's file alone"}. Each module is built with the stack of its part, pinned to its pulses whatever is chosen after: a stack named after the module is its own - backend.plain is built with config.turri.backend.yaml - with nothing to choose. For a module whose stack has another name, pass it as stack to regenerate, conform, glimpse and
|
|
1128
|
+
? ["", `Stacks: ${stacks.join(", ")} - chosen now: ${chosen ?? "none, so the project's file alone"}. Each module is built with the stack of its part, pinned to its pulses whatever is chosen after: a stack named after the module is its own - backend.plain is built with config.turri.backend.yaml - with nothing to choose. For a module whose stack has another name, pass it as stack to regenerate, conform, glimpse and change_plain. Choose one for the whole session with the stack tool only when the user asks.`]
|
|
1129
1129
|
: []),
|
|
1130
1130
|
// A stack: its file over the project's, for everything built while it is chosen.
|
|
1131
1131
|
...stacks.flatMap((stack) => [
|
|
@@ -1298,7 +1298,7 @@ function setRenderModelTool(pi, conversation) {
|
|
|
1298
1298
|
label: "Engine",
|
|
1299
1299
|
description: "The engine: which model the agents that regenerate the code run on. With no model, say which it is. With one, choose it " +
|
|
1300
1300
|
"for the rest of this session: for spec changes " +
|
|
1301
|
-
"(render spec diff pulses, and the ones
|
|
1301
|
+
"(render spec diff pulses, and the ones change_plain queues), for functionalities (render, module, " +
|
|
1302
1302
|
"regenerate with functionalities named, conformance), or for everything. By default " +
|
|
1303
1303
|
"each runs on the model the project's config.turri.yaml names, or else the conversation's model. The model may be given as provider/model-id or by name as " +
|
|
1304
1304
|
"the user said it; a name that fits more than one model, or none with credentials, comes back as a " +
|
package/dist/genome.js
CHANGED
|
@@ -12,7 +12,7 @@ import { DISMISSED } from "./ask.js";
|
|
|
12
12
|
import { createQuestioning, levelOf, questioningQuestion } from "./questioning.js";
|
|
13
13
|
import { createSpecDiffTools } from "./spec-tools.js";
|
|
14
14
|
import { createMatureTools, MATURE_TOOLS } from "./mature-tools.js";
|
|
15
|
-
const
|
|
15
|
+
const CHANGE_PLAIN = "change_plain";
|
|
16
16
|
/** The conversation's tools a skill acts on the work loop with - never pi built-ins. */
|
|
17
17
|
const LOOP_TOOLS = ["regenerate", "drift", "halt", "conform"];
|
|
18
18
|
/** How much of what the agent said before a question is shown with it: its end, where the brief is. */
|
|
@@ -77,14 +77,14 @@ export async function runSkill(opts) {
|
|
|
77
77
|
const questions = {
|
|
78
78
|
refusal: (confirmsPhase) => diffPerAnswer() && diffs?.pace.owed() === true
|
|
79
79
|
? "This question was not asked: the last answer is not in the spec yet. Every answer in this phase is a spec " +
|
|
80
|
-
"change, built at once - write it with
|
|
80
|
+
"change, built at once - write it with change_plain first, then ask."
|
|
81
81
|
: questioning?.refusal(confirmsPhase),
|
|
82
82
|
answered: (confirmsPhase) => questioning?.answered(confirmsPhase),
|
|
83
83
|
end: () => questioning?.end() ?? "No questioning level was chosen: ask what the phase needs.",
|
|
84
84
|
};
|
|
85
|
-
// A skill whose unit of work is a spec change gets
|
|
85
|
+
// A skill whose unit of work is a spec change gets change_plain: the change is applied
|
|
86
86
|
// and its build queued in one step.
|
|
87
|
-
const diffs = opts.renderQueue !== undefined && skill.allowedTools.includes(
|
|
87
|
+
const diffs = opts.renderQueue !== undefined && skill.allowedTools.includes(CHANGE_PLAIN)
|
|
88
88
|
? createSpecDiffTools({
|
|
89
89
|
loop: opts.renderQueue,
|
|
90
90
|
cwd: opts.cwd,
|
|
@@ -125,7 +125,7 @@ export async function runSkill(opts) {
|
|
|
125
125
|
customTools.push(...(opts.sharedTools ?? []));
|
|
126
126
|
const names = customTools.map((tool) => tool.name);
|
|
127
127
|
// Tools this harness provides are never pi built-ins, even when it did not provide them.
|
|
128
|
-
const builtins = skill.allowedTools.filter((t) => !names.includes(t) && t !==
|
|
128
|
+
const builtins = skill.allowedTools.filter((t) => !names.includes(t) && t !== CHANGE_PLAIN && !MATURE_TOOLS.includes(t) && !LOOP_TOOLS.includes(t));
|
|
129
129
|
const executor = await createExecutor({
|
|
130
130
|
cwd: opts.cwd,
|
|
131
131
|
customTools,
|
package/dist/identity.js
CHANGED
|
@@ -27,8 +27,9 @@ export function conversationIdentity(surface) {
|
|
|
27
27
|
}
|
|
28
28
|
const IDENTITY_BEFORE = [
|
|
29
29
|
"<turri>",
|
|
30
|
-
"You are turri - always lowercase -
|
|
31
|
-
"***plain; the code is regenerated from it. You
|
|
30
|
+
"You are turri - always lowercase - the first coding agent with the power of regenerative software. The software is",
|
|
31
|
+
"its ***plain; the code is regenerated from it. You write the ***plain with the person - they say what the software",
|
|
32
|
+
"should do, and you write it down as ***plain, shaping it with them - and you regenerate the code from it a",
|
|
32
33
|
"change at a time - with agents that write the code and tests, while you decide what runs next and when the work is",
|
|
33
34
|
"done - record what each piece was regenerated from, and prove it against conformance tests written from the ***plain",
|
|
34
35
|
"alone. When the code goes bad, you regenerate it from the ***plain. You are named after Turritopsis dohrnii, the",
|
|
@@ -37,9 +38,10 @@ const IDENTITY_BEFORE = [
|
|
|
37
38
|
"gone bad is regenerated from it, whole, rather than patched. Say it as that likeness, not as a fact about a",
|
|
38
39
|
"jellyfish.",
|
|
39
40
|
"",
|
|
40
|
-
"Asked who or what you are, say it in those words: you are turri,
|
|
41
|
-
"software - you
|
|
42
|
-
"
|
|
41
|
+
"Asked who or what you are, say it in those words: you are turri, the first coding agent with the power of",
|
|
42
|
+
"regenerative software - you write ***plain with them and regenerate the code from it. Never say they write the",
|
|
43
|
+
"***plain and you help: you write it, together. Not an assistant, and not a helper with specifications: ***plain is",
|
|
44
|
+
"the language the software is written in, and naming it is the point. Your replies are Markdown, where",
|
|
43
45
|
"three asterisks start bold italics: write the name escaped, \\*\\*\\*plain, so it shows as ***plain.",
|
|
44
46
|
"",
|
|
45
47
|
"Speak as turri, in the first person - I, my pulses, my agents - to the person, in whole sentences. Never speak of",
|
|
@@ -53,7 +55,7 @@ const IDENTITY_BEFORE = [
|
|
|
53
55
|
" allow-to-stop-jobs in config.turri.yaml are about pulses. \"Job\" in what you read is a pulse; say pulse.",
|
|
54
56
|
];
|
|
55
57
|
const IDENTITY_AFTER = [
|
|
56
|
-
"- mutate, in a skill's activity, is
|
|
58
|
+
"- mutate, in a skill's activity, is change_plain: a spec change written and regenerated in one step.",
|
|
57
59
|
"- Regenerate is the word for what you do to the code: never grow, grew or grown - older text, a tool's description",
|
|
58
60
|
" or a report may still say it, and means regenerate.",
|
|
59
61
|
];
|
package/dist/loop-cli.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type DriftReport, type ModuleStatus, type WorkLoop } from "./regenerate/loop.ts";
|
|
2
2
|
import { type ModuleState } from "./run-report.ts";
|
|
3
3
|
/**
|
|
4
|
-
* What every command that builds - `turri regenerate`, `conform`, `
|
|
4
|
+
* What every command that builds - `turri regenerate`, `conform`, `change-plain` - does around its own
|
|
5
5
|
* request of the work loop, as the chat does it: the project's models, a job that fails run once more and, failing again,
|
|
6
6
|
* diagnosed, a line as each job starts and ends; then the loop run until nothing more can start, how each job ended,
|
|
7
7
|
* and exit 1 when any failed or a module is not complete. With `dryRun`, the work is planned and the prompt of the first job each codebase would
|
package/dist/loop-cli.js
CHANGED
|
@@ -17,7 +17,7 @@ import { listFiles } from "./regenerate/testing.js";
|
|
|
17
17
|
import { startRunReport } from "./run-report.js";
|
|
18
18
|
import { configKeyOptions, optionValue } from "./cli-options.js";
|
|
19
19
|
/**
|
|
20
|
-
* What every command that builds - `turri regenerate`, `conform`, `
|
|
20
|
+
* What every command that builds - `turri regenerate`, `conform`, `change-plain` - does around its own
|
|
21
21
|
* request of the work loop, as the chat does it: the project's models, a job that fails run once more and, failing again,
|
|
22
22
|
* diagnosed, a line as each job starts and ends; then the loop run until nothing more can start, how each job ended,
|
|
23
23
|
* and exit 1 when any failed or a module is not complete. With `dryRun`, the work is planned and the prompt of the first job each codebase would
|
|
@@ -173,10 +173,10 @@ export function withinModules(text) {
|
|
|
173
173
|
}
|
|
174
174
|
/**
|
|
175
175
|
* What the person is shown a tool as, where that is not the name its agent calls it by. The agent's name says
|
|
176
|
-
* what the call takes - `
|
|
176
|
+
* what the call takes - `change_plain` takes a diff of the spec - and the shown one speaks turri's language.
|
|
177
177
|
*/
|
|
178
178
|
const SHOWN_AS = {
|
|
179
|
-
|
|
179
|
+
change_plain: "mutate",
|
|
180
180
|
};
|
|
181
181
|
/** A tool's name as the person is shown it. */
|
|
182
182
|
export function shownName(name) {
|
package/dist/self-invocation.js
CHANGED
|
@@ -15,7 +15,7 @@ const TOOL_FOR = {
|
|
|
15
15
|
conform: "conform",
|
|
16
16
|
cultivate: "cultivate",
|
|
17
17
|
release: "release",
|
|
18
|
-
"
|
|
18
|
+
"change-plain": "regenerate",
|
|
19
19
|
chat: "nothing - you are the conversation already",
|
|
20
20
|
ask: "nothing - you are the conversation already",
|
|
21
21
|
};
|
|
@@ -31,7 +31,7 @@ const NPM_SCRIPT = {
|
|
|
31
31
|
const ENTRY = {
|
|
32
32
|
"regenerate-cli": "regenerate",
|
|
33
33
|
"conform-cli": "conform",
|
|
34
|
-
"
|
|
34
|
+
"change-plain-cli": "change-plain",
|
|
35
35
|
"cultivate-cli": "cultivate",
|
|
36
36
|
"release-cli": "release",
|
|
37
37
|
};
|
|
@@ -43,10 +43,10 @@ const AT_COMMAND = String.raw `(?:^|[\s;&|()\x60$])`;
|
|
|
43
43
|
* agent - and neither are setup and doctor.
|
|
44
44
|
*/
|
|
45
45
|
export function selfInvocation(command) {
|
|
46
|
-
const cli = new RegExp(`${AT_COMMAND}(?:npx\\s+)?turri\\s+(regenerate|glimpse|conform|cultivate|release|
|
|
46
|
+
const cli = new RegExp(`${AT_COMMAND}(?:npx\\s+)?turri\\s+(regenerate|glimpse|conform|cultivate|release|change-plain)\\b`).exec(command);
|
|
47
47
|
if (cli)
|
|
48
48
|
return cli[1];
|
|
49
|
-
const launcher = new RegExp(`(?:bin/turri\\.mjs|src/cli\\.ts)\\s+(regenerate|glimpse|conform|cultivate|release|
|
|
49
|
+
const launcher = new RegExp(`(?:bin/turri\\.mjs|src/cli\\.ts)\\s+(regenerate|glimpse|conform|cultivate|release|change-plain)\\b`).exec(command);
|
|
50
50
|
if (launcher)
|
|
51
51
|
return launcher[1];
|
|
52
52
|
// turri alone, or with options, is the conversation - where a command starts, not a word an argument names (`which turri`).
|
|
@@ -83,7 +83,7 @@ const PI_CONFIGURES = new Set([
|
|
|
83
83
|
export function askedForCommandLine(message) {
|
|
84
84
|
const text = message.toLowerCase();
|
|
85
85
|
return (/\b(headless|command[ -]line|cli|terminal|shell|in bash|via bash|through bash|npm run|nohup)\b/.test(text) ||
|
|
86
|
-
/\bturri\s+(regenerate|glimpse|conform|
|
|
86
|
+
/\bturri\s+(regenerate|glimpse|conform|change-plain|cultivate|release)\b/.test(text) ||
|
|
87
87
|
/(^|\s)turri\s+-p\b/.test(text) ||
|
|
88
88
|
/(^|\s)pi\s+-p\b/.test(text));
|
|
89
89
|
}
|
|
@@ -102,7 +102,7 @@ export const TOOLS_NOT_CLI = [
|
|
|
102
102
|
"<turri-tools>",
|
|
103
103
|
"In this conversation, you do your work with your tools - regenerate, conform, cultivate and the rest - which run as",
|
|
104
104
|
"background jobs the user can watch and halt. To try what is regenerated, release it - never by hand in plain_modules. Never",
|
|
105
|
-
"run your own command line (turri regenerate, glimpse, conform,
|
|
105
|
+
"run your own command line (turri regenerate, glimpse, conform, change-plain, cultivate, release, turri itself; npm run",
|
|
106
106
|
"regenerate and the like) or start another pi through bash:",
|
|
107
107
|
"that is a separate headless run the user cannot see or stop. Only when the user explicitly asks for the",
|
|
108
108
|
"command line, run it. Configuring pi itself - pi install, update, list, config, auth, or its settings files -",
|
package/dist/skill.d.ts
CHANGED
|
@@ -70,7 +70,7 @@ export interface Skill {
|
|
|
70
70
|
allowedTools: string[];
|
|
71
71
|
steps: StepSpec[];
|
|
72
72
|
/**
|
|
73
|
-
* Whether `
|
|
73
|
+
* Whether `change_plain` needs an answer from the user between two new changes - one answer,
|
|
74
74
|
* one change, as an interview writes them. `metadata.paced: false` turns it off, for a skill that
|
|
75
75
|
* writes the specs on its own.
|
|
76
76
|
*/
|
|
@@ -81,8 +81,8 @@ export interface Skill {
|
|
|
81
81
|
*/
|
|
82
82
|
questioning: string[];
|
|
83
83
|
/**
|
|
84
|
-
* The steps where each answer is a spec change (`metadata.
|
|
85
|
-
* last answer is written with `
|
|
84
|
+
* The steps where each answer is a spec change (`metadata.change-plain-per-answer`): the next question waits until the
|
|
85
|
+
* last answer is written with `change_plain`, and changes the agent decides itself need no answer before them.
|
|
86
86
|
*/
|
|
87
87
|
diffPerAnswer: string[];
|
|
88
88
|
/**
|
package/dist/skill.js
CHANGED
|
@@ -109,11 +109,11 @@ export function loadSkill(skillDir) {
|
|
|
109
109
|
throw new Error(`${name}: 'metadata.questioning' names unknown step '${stepName}'`);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
-
const perAnswer = data.metadata?.["
|
|
112
|
+
const perAnswer = data.metadata?.["change-plain-per-answer"];
|
|
113
113
|
const diffPerAnswer = Array.isArray(perAnswer) ? perAnswer.map(String) : [];
|
|
114
114
|
for (const stepName of diffPerAnswer) {
|
|
115
115
|
if (!steps.some((s) => s.name === stepName)) {
|
|
116
|
-
throw new Error(`${name}: 'metadata.
|
|
116
|
+
throw new Error(`${name}: 'metadata.change-plain-per-answer' names unknown step '${stepName}'`);
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
for (const stepName of gatesByStep.keys()) {
|
package/dist/spec-tools.js
CHANGED
|
@@ -28,7 +28,7 @@ export function createSpecDiffTools(options) {
|
|
|
28
28
|
let owed = false;
|
|
29
29
|
let answeredSinceLastDiff = true;
|
|
30
30
|
const writeDiff = defineTool({
|
|
31
|
-
name: "
|
|
31
|
+
name: "change_plain",
|
|
32
32
|
label: "Write spec diff",
|
|
33
33
|
description: "Apply a change to the ***plain specs, given as a git diff of the .plain files, and start building it in " +
|
|
34
34
|
"the background. The diff is applied with git; if the specs no longer parse, it is undone and nothing is " +
|
|
@@ -36,7 +36,7 @@ export function createSpecDiffTools(options) {
|
|
|
36
36
|
"every change is built, whatever it touches.",
|
|
37
37
|
promptSnippet: "apply a git diff of the .plain specs and start building it",
|
|
38
38
|
promptGuidelines: [
|
|
39
|
-
"Every change to a .plain file in the product phase goes through
|
|
39
|
+
"Every change to a .plain file in the product phase goes through change_plain, as a git diff with paths relative to the project root. It writes the change and starts its build in one step.",
|
|
40
40
|
skillPaced
|
|
41
41
|
? "One change per answer, and a new one only after the user has answered a question about it. Set fixes_earlier to correct something already written."
|
|
42
42
|
: "Write the changes one after another, on your own: no answer is needed between them, and none is asked for. Set fixes_earlier to correct something already written.",
|
package/dist/stacks.js
CHANGED
|
@@ -77,7 +77,7 @@ export function registerStacks(pi, cwd) {
|
|
|
77
77
|
"For the session; a pulse already running keeps the stack it started with. Without a name, says the stacks and the one chosen.",
|
|
78
78
|
promptSnippet: "choose the stack - its testing scripts and settings - that everything is built with",
|
|
79
79
|
promptGuidelines: [
|
|
80
|
-
"Switch the stack only when the user asks for it, and say which you chose. A module is built with the stack named after it by itself; to build one whose stack has another name, pass that stack as stack to regenerate, conform, glimpse or
|
|
80
|
+
"Switch the stack only when the user asks for it, and say which you chose. A module is built with the stack named after it by itself; to build one whose stack has another name, pass that stack as stack to regenerate, conform, glimpse or change_plain instead: it is pinned to that module's pulses, and the session's choice stays as it is.",
|
|
81
81
|
],
|
|
82
82
|
parameters: Type.Object({
|
|
83
83
|
name: Type.Optional(Type.String({ description: "The stack to choose, or none. Omitted: the stacks there are, and the one chosen" })),
|
package/dist/tools.js
CHANGED
|
@@ -164,7 +164,7 @@ export function createEndQuestionsTool(questioning) {
|
|
|
164
164
|
label: "End questions",
|
|
165
165
|
description: "End this phase's questions: you have asked what the person must decide, and you decide what is left " +
|
|
166
166
|
"yourself, saying what you chose. Afterwards ask_user asks only the phase's closing confirmation, and " +
|
|
167
|
-
"
|
|
167
|
+
"change_plain needs no answer between changes.",
|
|
168
168
|
promptSnippet: "end this phase's questions and decide what is left yourself",
|
|
169
169
|
parameters: Type.Object({}),
|
|
170
170
|
async execute() {
|
package/docs/README.md
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
# turri documentation
|
|
2
2
|
|
|
3
|
-
turri
|
|
3
|
+
turri is the first coding agent with the power of regenerative software: [\*\*\*plain](https://plainlang.org/docs/) captures the durable definition of what a system does, and the code is regenerated from it and validated against. The [project README](../README.md) says why, and how to install and start; these pages go into each part.
|
|
4
4
|
|
|
5
5
|
## Start here
|
|
6
6
|
|
|
7
7
|
1. [Installation](installation.md) - get a machine ready: `turri doctor`, and what turri needs of this machine.
|
|
8
|
-
2. [Using turri](using.md) - the conversation, the skills and tools, background jobs and their logs, choosing the model that
|
|
9
|
-
3. [Authoring \*\*\*plain](authoring-plain.md) - writing
|
|
10
|
-
4. [
|
|
8
|
+
2. [Using turri](using.md) - the conversation, the skills and tools, background jobs and their logs, choosing the model that codes, the command line.
|
|
9
|
+
3. [Authoring \*\*\*plain](authoring-plain.md) - writing \*\*\*plain: what is it made out of, the authoring rules, the skills that write and fix it.
|
|
10
|
+
4. [Regenerating](regenerating.md) - turning \*\*\*plain into code: what each build does, in which order, and what has to pass before it moves on.
|
|
11
11
|
|
|
12
12
|
## By question
|
|
13
13
|
|
|
14
14
|
| to find out… | read |
|
|
15
15
|
| --- | --- |
|
|
16
16
|
| what my machine needs, and why a check failed | [installation](installation.md) |
|
|
17
|
-
| what turri needs of this machine, and what `turri doctor` checks | [installation](installation.md#what-turri-
|
|
17
|
+
| what turri needs of this machine, and what `turri doctor` checks | [installation](installation.md#what-turri-doctor-checks) |
|
|
18
18
|
| how to start a project, or add a feature, in a conversation | [using](using.md#in-a-conversation) |
|
|
19
|
-
| which model
|
|
19
|
+
| which model codes, and how to change it | [using](using.md#choosing-a-model), [the project config](config.md#models) |
|
|
20
20
|
| what `config.turri.yaml` can set, and what happens without it | [the project config](config.md) |
|
|
21
21
|
| how a backend and a frontend on different stacks share one project | [the project config](config.md#stacks) |
|
|
22
|
-
| which ready-made stacks
|
|
22
|
+
| which ready-made stacks \*\*\*plain can import - a FastAPI backend, a React frontend | [the standard library](authoring-plain.md#the-standard-library) |
|
|
23
23
|
| everything an agent was sent and did, to debug it | [using](using.md#agent-logs) |
|
|
24
|
-
| how to
|
|
24
|
+
| how to regenerate \*\*\*plain overnight, and resume it | [running unattended](headless.md) |
|
|
25
25
|
| how to rebuild one functionality | [running unattended](headless.md#rebuilding-one-functionality) |
|
|
26
|
-
| what goes in which section of
|
|
26
|
+
| what goes in which section of \*\*\*plain | [authoring \*\*\*plain](authoring-plain.md#what-plain-is-made-of) |
|
|
27
27
|
| which skill does what | [authoring \*\*\*plain](authoring-plain.md#the-skills) |
|
|
28
|
-
| what happens when
|
|
28
|
+
| what happens when \*\*\*plain changes | [regenerating the code](regenerating.md#when-plain-changes) |
|
|
29
29
|
| why a test was not allowed to be deleted | [regenerating the code](regenerating.md#the-tests-must-actually-pass) |
|
|
30
30
|
| how to try what is built, and what deploy may install | [deploy](deploy.md) |
|
|
31
31
|
| what the agents can and cannot reach | [the sandbox](sandbox.md) |
|
|
@@ -42,9 +42,9 @@ turri implements regenerative software: the \*\*\*plain specs are the durable de
|
|
|
42
42
|
| [Using turri](using.md) | the conversation, the tools, background jobs, choosing a model, module history, the command line |
|
|
43
43
|
| [The project config](config.md) | `config.turri.yaml` and each stack's `config.turri.<stack>.yaml`: where they are read from, every key, models and their inheritance, conformance, the testing scripts, stacks and choosing one, the old `config.yaml` |
|
|
44
44
|
| [Deploy](deploy.md) | trying what is built: dist/<module>, the last commit, starting it on your machine, `turri release` |
|
|
45
|
-
| [Running unattended](headless.md) |
|
|
46
|
-
| [Authoring \*\*\*plain](authoring-plain.md) | a \*\*\*plain project, what
|
|
47
|
-
| [
|
|
45
|
+
| [Running unattended](headless.md) | regenerating overnight or in CI: before you leave it, the command, the results, resuming, rebuilding one functionality |
|
|
46
|
+
| [Authoring \*\*\*plain](authoring-plain.md) | a \*\*\*plain project, what \*\*\*plain is made of, the rules, writing \*\*\*plain with the agent, the skills, checking \*\*\*plain |
|
|
47
|
+
| [Regenerating](regenerating.md) | where things go, one job, a whole module and how its work is cut into jobs, each job's cases, \*\*\*plain changes, conformance tests, the two records of what is built |
|
|
48
48
|
| [The sandbox](sandbox.md) | what an agent can reach, the file-tool fence, the command check, the OS sandbox, the audit |
|
|
49
49
|
| [Skills and gates](skills-and-gates.md) | the gate syntax, what the harness enforces, what a step can do, the rules, trying the gates |
|
|
50
50
|
| [The \*\*\*plain parser](parser.md) | the TypeScript port, `turri validate`, inheritance, the iterator, fidelity |
|