@turri-dev/cli 0.1.11-next.3495500 → 0.1.11-next.509411a
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 +2 -2
- package/dist/cli.js +1 -1
- package/dist/extension.js +4 -4
- package/dist/genome.js +5 -5
- package/dist/identity.js +1 -1
- 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/{change-plain-cli.js → spec-diff-cli.js} +3 -3
- package/dist/spec-tools.js +2 -2
- package/dist/stacks.js +1 -1
- package/dist/tools.js +1 -1
- package/docs/README.md +2 -2
- package/docs/authoring-plain.md +32 -32
- package/docs/config.md +18 -18
- package/docs/deploy.md +4 -4
- package/docs/development.md +5 -5
- package/docs/headless.md +14 -14
- package/docs/hosting.md +6 -6
- package/docs/images/how-turri-works.svg +1 -1
- package/docs/installation.md +2 -2
- package/docs/parser.md +5 -5
- package/docs/regenerating.md +75 -75
- package/docs/sandbox.md +6 -6
- package/docs/skills-and-gates.md +14 -14
- package/docs/theming.md +9 -9
- package/docs/using.md +42 -42
- 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/{change-plain-cli.d.ts → spec-diff-cli.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -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 render \*\*\*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) | rendering \*\*\*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 |
|
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
|
+
"spec-diff": { module: "./spec-diff-cli.ts", blurb: "apply a change to the specs, 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("write_spec_diff")
|
|
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("write_spec_diff") ? 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 write_spec_diff. 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 write_spec_diff 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 WRITE_SPEC_DIFF = "write_spec_diff";
|
|
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 write_spec_diff 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 write_spec_diff: 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(WRITE_SPEC_DIFF)
|
|
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 !== WRITE_SPEC_DIFF && !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
|
@@ -55,7 +55,7 @@ const IDENTITY_BEFORE = [
|
|
|
55
55
|
" allow-to-stop-jobs in config.turri.yaml are about pulses. \"Job\" in what you read is a pulse; say pulse.",
|
|
56
56
|
];
|
|
57
57
|
const IDENTITY_AFTER = [
|
|
58
|
-
"- mutate, in a skill's activity, is
|
|
58
|
+
"- mutate, in a skill's activity, is write_spec_diff: a spec change written and regenerated in one step.",
|
|
59
59
|
"- Regenerate is the word for what you do to the code: never grow, grew or grown - older text, a tool's description",
|
|
60
60
|
" or a report may still say it, and means regenerate.",
|
|
61
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`, `spec-diff` - 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`, `spec-diff` - 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 - `write_spec_diff` takes a diff of the spec - and the shown one speaks turri's language.
|
|
177
177
|
*/
|
|
178
178
|
const SHOWN_AS = {
|
|
179
|
-
|
|
179
|
+
write_spec_diff: "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
|
+
"spec-diff": "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
|
+
"spec-diff-cli": "spec-diff",
|
|
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|spec-diff)\\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|spec-diff)\\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|spec-diff|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, spec-diff, 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 `write_spec_diff` 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.spec-diff-per-answer`): the next question waits until the
|
|
85
|
+
* last answer is written with `write_spec_diff`, 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?.["spec-diff-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.spec-diff-per-answer' names unknown step '${stepName}'`);
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
for (const stepName of gatesByStep.keys()) {
|
|
@@ -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 spec-diff <file.plain> <diff-file> [options]
|
|
8
8
|
|
|
9
|
-
Applies a change to
|
|
9
|
+
Applies a change to the specs, 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: ["spec-diff", ...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/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: "write_spec_diff",
|
|
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 write_spec_diff, 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 write_spec_diff 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
|
+
"write_spec_diff 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,6 +1,6 @@
|
|
|
1
1
|
# turri documentation
|
|
2
2
|
|
|
3
|
-
turri
|
|
3
|
+
turri implements 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
|
|
|
@@ -14,7 +14,7 @@ turri is the first coding agent with the power of regenerative software: [\*\*\*
|
|
|
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-setup-checks) |
|
|
18
18
|
| how to start a project, or add a feature, in a conversation | [using](using.md#in-a-conversation) |
|
|
19
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) |
|
package/docs/authoring-plain.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Authoring \*\*\*plain
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A spec is written in [\*\*\*plain](https://plainlang.org/docs/) - its documentation is the language's whole reference. Rendering turns a spec into code; authoring is the half that decides what gets built. The code is only ever as good as the spec - a fact in the wrong section is silently ignored, a functional spec that is too big comes out half-built, two specs that contradict each other break each other's tests. So this repo carries as much for writing specs as for rendering them: the authoring rules, a library of skills that apply them, and independent review of what they write.
|
|
4
4
|
|
|
5
|
-
The rule underneath all of it:
|
|
5
|
+
The rule underneath all of it: **the specs are the source of truth**. The generated code in `plain_modules/` is never edited by hand. A bug is fixed in the spec, and a change that is wanted goes into the spec - that is what persists and what the next render builds from.
|
|
6
6
|
|
|
7
7
|
## A \*\*\*plain project
|
|
8
8
|
|
|
@@ -39,13 +39,13 @@ The frontmatter connects modules:
|
|
|
39
39
|
| `exported_concepts` | which concepts a module that `requires` this one may use |
|
|
40
40
|
| `required_concepts` | concepts an import module expects the importing module to define |
|
|
41
41
|
|
|
42
|
-
Beyond that:
|
|
42
|
+
Beyond that: a spec links to files under `resources/` with markdown link syntax, and the file's contents go to the renderer with the spec; lines starting with `>` are comments for people; and files are processed with Liquid first, so a template can be included with parameters.
|
|
43
43
|
|
|
44
|
-
**Section ownership is the rule that bites.**
|
|
44
|
+
**Section ownership is the rule that bites.** The renderer reads each kind of fact only from the section that owns it. A tech choice written into a functional spec, or a behaviour written into the implementation reqs, is not flagged - it is simply not read.
|
|
45
45
|
|
|
46
46
|
## The standard library
|
|
47
47
|
|
|
48
|
-
turri ships a standard library of import modules in `stdlib/` that settle everything about a generic piece of software on one stack except what it does: its language and framework, layout, configuration, error handling, logging, shutdown, and how it is unit and conformance tested. A
|
|
48
|
+
turri ships a standard library of import modules in `stdlib/` that settle everything about a generic piece of software on one stack except what it does: its language and framework, layout, configuration, error handling, logging, shutdown, and how it is unit and conformance tested. A spec imports one and writes only its own concepts and functional specs:
|
|
49
49
|
|
|
50
50
|
```plain
|
|
51
51
|
---
|
|
@@ -82,19 +82,19 @@ It is built in three layers, and a stack imports the other two:
|
|
|
82
82
|
|
|
83
83
|
Each conformance template imports its kind's `conformance/common`: a backend's tests start the built service on a free port and reach it over HTTP only, checking a failure by status and `error.code`; a frontend's run the served build in Chromium through Playwright with the backend stood in; a command-line tool's run the built tool as a process; a library's go through its public entry point.
|
|
84
84
|
|
|
85
|
-
- **Conformance tests apart.**
|
|
86
|
-
- **A reserved name.** An import that starts with `stdlib/` is always turri's template, never a file of the project's. A project with a module at such a path - `stdlib/backend/python-fastapi.plain` beside the
|
|
87
|
-
- **No concepts.** Its requirements are on the predefined `:Implementation:`, `:UnitTests:` and `:ConformanceTests:` only, so it never collides with
|
|
85
|
+
- **Conformance tests apart.** A spec without the conformance template has no test reqs, so no conformance tests are written or run for it. Turning them on later is adding that one import.
|
|
86
|
+
- **A reserved name.** An import that starts with `stdlib/` is always turri's template, never a file of the project's. A project with a module at such a path - `stdlib/backend/python-fastapi.plain` beside the spec, in its `template/` folder or in `PLAIN_TEMPLATE_DIRS` - gets an error saying the name is reserved, so which one a spec builds on never depends on what is on disk beside it.
|
|
87
|
+
- **No concepts.** Its requirements are on the predefined `:Implementation:`, `:UnitTests:` and `:ConformanceTests:` only, so it never collides with a spec's own definitions.
|
|
88
88
|
- **Pinned to a toolchain.** It names its stack's toolchain - Python 3.12, Node.js 24, Java 21, Go 1.25, Rust - which the agents use from this machine; `turri doctor` says when the machine lacks it.
|
|
89
89
|
- **Not storage, not the testing scripts.** A module with a database says which on top of its stack. It still needs its unit testing script, and a conformance testing script when it has conformance tests.
|
|
90
|
-
- **Known to the agents.** The `stdlib` rule, in
|
|
91
|
-
- **Only what this machine can build.** The agents use this machine's toolchains and install none, so a Go stack on a laptop without Go stops at its first build. Every agent
|
|
90
|
+
- **Known to the agents.** The `stdlib` rule, in every spec-writing agent's system prompt, says when to import which, and lists every template with what it is and the folder it is in, read from `stdlib/` when the rules load. `spawn` and `brood` import a stack when the one chosen is in it, and its conformance template when conformance tests are on.
|
|
91
|
+
- **Only what this machine can build.** The agents use this machine's toolchains and install none, so a Go stack on a laptop without Go stops at its first build. Every spec-writing agent and the conversation are told which of the standard library's languages the machine can build now - Python needs `uv`, TypeScript and every frontend `node` and `npm`, Go `go`, Rust `cargo`, Java a JDK (`src/stack.ts`, `toolchainsBlock`) - and `spawn` and `brood` offer, and choose, only stacks in those. A stack the person names in a language the machine lacks is not refused: they are told what to install first, and that `turri doctor` checks it.
|
|
92
92
|
|
|
93
93
|
A requirement beyond the stack - storage, an outside service - is added on top of it. A module that differs from its stack on something the stack settles - another framework, another layout - writes its own instead: an import cannot be taken back in part.
|
|
94
94
|
|
|
95
95
|
## The rules
|
|
96
96
|
|
|
97
|
-
`genome/rules/` holds the authoring rules. They are in the system prompt of every agent that writes
|
|
97
|
+
`genome/rules/` holds the authoring rules. They are in the system prompt of every agent that writes specs - the conversation, the interview skills, the reviewers - so they are not something an agent has to decide to go and read.
|
|
98
98
|
|
|
99
99
|
| rule | covers |
|
|
100
100
|
| --- | --- |
|
|
@@ -103,40 +103,40 @@ A requirement beyond the stack - storage, an outside service - is added on top o
|
|
|
103
103
|
| `func-specs.md` | voice, the complexity limit, chronological order, no conflicts, language agnosticism, testability, acceptance tests |
|
|
104
104
|
| `impl-reqs.md` | HOW not WHAT, unit tests live here, what belongs and what does not |
|
|
105
105
|
| `test-reqs.md` | conformance tests only, what belongs, test types |
|
|
106
|
-
| `concise-specs.md` (+ two example banks) | keeping
|
|
106
|
+
| `concise-specs.md` (+ two example banks) | keeping specs short enough for a person to review: no obvious lines, plain words, no uncheckable adjectives, no hedging |
|
|
107
107
|
| `bullet-continuation.md` | bullets and continuation lines in every section |
|
|
108
108
|
| `linked-resources.md` | a linked resource is one text file on disk, or a PNG or JPEG image for a model that reads images; protocol artifacts are linked, never transcribed |
|
|
109
109
|
| `import-modules.md`, `requires-modules.md` | the two ways modules build on each other |
|
|
110
110
|
| `exported-concepts.md`, `required-concepts.md` | which concepts cross module boundaries |
|
|
111
|
-
| `integrations.md` (+ standalone, embedded, embedded testing) |
|
|
112
|
-
| `turri-regeneration.md` | turri's model of itself: a
|
|
111
|
+
| `integrations.md` (+ standalone, embedded, embedded testing) | specs for REST API integrations, deployed standalone or embedded in an existing codebase |
|
|
112
|
+
| `turri-regeneration.md` | turri's model of itself: a spec change regenerated as a spec diff, wherever it sits, in the chain's tip; what each kind of change sets off; what a coding agent sees; how bad code is fixed; where the cost is - so a spec is placed by what it means, never to spare regeneration |
|
|
113
113
|
| `stdlib.md` | the standard library: when to import which template, and every template with what it is and where it is, read from `stdlib/` |
|
|
114
114
|
|
|
115
|
-
The ones that shape
|
|
115
|
+
The ones that shape a spec most:
|
|
116
116
|
|
|
117
117
|
- **One functionality is at most ~200 lines of code.** A bigger one is split before it is written (`analyze-if-func-spec-too-complex`, `break-down-func-spec`).
|
|
118
|
-
- **Functional specs are in build order.** Each is
|
|
118
|
+
- **Functional specs are in build order.** Each is rendered on top of the ones before it - which is what a spec may assume, not what a change costs: a spec inserted anywhere is regenerated alone.
|
|
119
119
|
- **They state facts, with a concrete subject.** "A :User: can add a :Task:" - not "Implement adding tasks", not "The system should…".
|
|
120
120
|
- **Nothing a test cannot check.** No `robust`, `seamless`, `as appropriate`; state the checkable fact or cut the word.
|
|
121
|
-
- **No two
|
|
121
|
+
- **No two specs contradict each other.** A conflict is found and resolved when a spec is added, not when its tests fail.
|
|
122
122
|
|
|
123
|
-
## Writing
|
|
123
|
+
## Writing specs with turri
|
|
124
124
|
|
|
125
|
-
In a conversation (`turri`), say what you want and the matching skill runs - but the three that start a project or a feature you start yourself, by their commands: `/brood`, `/spawn` or `/bud`, with what you want after it. Two build a project from scratch, and write each
|
|
125
|
+
In a conversation (`turri`), say what you want and the matching skill runs - but the three that start a project or a feature you start yourself, by their commands: `/brood`, `/spawn` or `/bud`, with what you want after it. Two build a project from scratch, and write each spec change as a git diff that is built in the background the moment it is written. `brood` interviews you one question at a time and writes the specs as you answer; `spawn`, its quick twin, gets something running as soon as it can - the smallest thing that runs first, then the product a slice at a time - asking only what your message leaves open:
|
|
126
126
|
|
|
127
|
-
| skill | phases | how a
|
|
127
|
+
| skill | phases | how a spec change is written |
|
|
128
128
|
| --- | --- | --- |
|
|
129
|
-
| `brood` | intent → stack → testing → product, feature by feature | first, how many questions the stack and product phases may ask - light 3, medium 5, thorough 10 or best judgement, broader with fewer, covering the same ground - and what they leave open turri decides itself, each decision a
|
|
130
|
-
| `spawn` | start → skeleton → regenerate | built for the time to first regeneration: what to build is asked only when the message does not say, and the stack only when the user named one this machine cannot build - otherwise turri chooses one; the stack and the unit tests are written as files, with conformance tests off unless the user asks for them; then a
|
|
129
|
+
| `brood` | intent → stack → testing → product, feature by feature | first, how many questions the stack and product phases may ask - light 3, medium 5, thorough 10 or best judgement, broader with fewer, covering the same ground - and what they leave open turri decides itself, each decision a spec change of its own; every product answer is still a spec change, built before the next question; in testing, whether there are conformance tests, their `***test reqs***` and script - written as files, building nothing; then one spec change per answer, across whatever sections it needs, as a git diff with `write_spec_diff`, rendered as a render spec diff job and conformance tested as it is built |
|
|
130
|
+
| `spawn` | start → skeleton → regenerate | built for the time to first regeneration: what to build is asked only when the message does not say, and the stack only when the user named one this machine cannot build - otherwise turri chooses one; the stack and the unit tests are written as files, with conformance tests off unless the user asks for them; then a spec change for the smallest thing that runs starts the first build - a module's first pulse of one functional spec is not reviewed - and the main slice of what the user described and at most one more follow - none when they described no feature, and never one of turri's own - each a functional spec on its happy path, as git diffs with `write_spec_diff`, one after another with no answer between them. The rest - the next slices, then validation and edge cases as changes to those specs - is asked for in the conversation once the user has tried what landed: `go on` for the next slice it proposed |
|
|
131
131
|
|
|
132
|
-
`mature` starts from
|
|
132
|
+
`mature` starts from a spec you already have, rough or hand-written, and runs on its own until it is fully rendered - every functionality built from the spec as it stands, its tests passing. It renders what is not built and watches the agents building it; when one struggles on something the spec leaves open or unclear - sent back again and again, the judge going back and forth, a spec conflict - it writes what the spec should say as a spec diff, stops that build, and renders it again. What the builds had to decide themselves - their assumptions, pitfalls, a failed build's diagnosis - goes into the spec the same way, when a user, a caller or a test can see it. It never asks: it takes the reading that fits the rest of the spec, and marks each such decision in its report. It leaves code, environment and turri problems to the builds, and stops when a functionality keeps struggling after the spec was clarified, when two cycles change nothing, or after twenty.
|
|
133
133
|
|
|
134
|
-
For an existing project, `bud` works the same way, scoped to one feature: intent → scope → product. It first asks how many questions the product phase may ask - light, medium, thorough or best judgement, as `brood` does - then a short feature-intent interview, then product questions - a trigger and its outcome, an edge case, a rule - and every answer is a
|
|
134
|
+
For an existing project, `bud` works the same way, scoped to one feature: intent → scope → product. It first asks how many questions the product phase may ask - light, medium, thorough or best judgement, as `brood` does - then a short feature-intent interview, then product questions - a trigger and its outcome, an edge case, a rule - and every answer is a spec change, a git diff written with `write_spec_diff` and built the moment it is written, before the next question. What the questions leave open it decides itself, a spec change each. A change to something already specified, built or still to build, is put to the user first and is a spec diff too: what is left to build is worked out again from the spec. It ends on the person's confirmation of a short summary of the feature's changes.
|
|
135
135
|
|
|
136
136
|
What keeps the interview honest is the harness, not the interviewer:
|
|
137
137
|
|
|
138
|
-
- **Every question offers choices** (`ask_user` refuses a question without them), and each answer maps to one
|
|
139
|
-
- **Every answer is written before the next question.** In the product phase of `brood` and `bud`, `ask_user` refuses a new question until the last answer is a
|
|
138
|
+
- **Every question offers choices** (`ask_user` refuses a question without them), and each answer maps to one spec change.
|
|
139
|
+
- **Every answer is written before the next question.** In the product phase of `brood` and `bud`, `ask_user` refuses a new question until the last answer is a spec change - see [skills and gates](skills-and-gates.md).
|
|
140
140
|
- **Each phase ends on the person's confirmation** of a short summary, not on the interviewer's word.
|
|
141
141
|
|
|
142
142
|
## The skills
|
|
@@ -146,21 +146,21 @@ Every skill in `genome/skills/` can be run from a conversation; most are also us
|
|
|
146
146
|
| to… | skills |
|
|
147
147
|
| --- | --- |
|
|
148
148
|
| start a project | `brood`, `spawn`, `init-plain-project` |
|
|
149
|
-
| make rough
|
|
150
|
-
| add to
|
|
149
|
+
| make a rough spec build | `mature` |
|
|
150
|
+
| add to a spec | `bud`, `add-functional-spec`, `add-functional-specs`, `add-concept`, `add-implementation-requirement`, `add-test-requirement`, `add-acceptance-test`, `add-resource`, `add-template` |
|
|
151
151
|
| check what is written | `analyze-if-func-spec-too-complex`, `analyze-2-func-specs`, `analyze-func-specs`, `resolve-section-ownership` |
|
|
152
152
|
| fix what is written | `break-down-func-spec`, `resolve-spec-conflict`, `debug-specs` |
|
|
153
153
|
| restructure modules | `create-import-module`, `create-requires-module`, `refactor-module`, `consolidate-concepts` |
|
|
154
154
|
| set up the project to build | `implement-unit-testing-script`, `implement-conformance-testing-script`, `check-plain-env` |
|
|
155
155
|
|
|
156
|
-
`debug-specs` is the one for a running application that misbehaves: it reads the generated code to find the cause, traces it back to
|
|
156
|
+
`debug-specs` is the one for a running application that misbehaves: it reads the generated code to find the cause, traces it back to the spec, and fixes only the `.plain` files.
|
|
157
157
|
|
|
158
|
-
## Checking
|
|
158
|
+
## Checking a spec
|
|
159
159
|
|
|
160
160
|
```bash
|
|
161
161
|
turri validate app.plain # or validate in a conversation
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
-
It parses
|
|
164
|
+
It parses the spec with this repo's own parser, resolves its imports and required modules, and reports every syntax, concept and structure error with its line - no model, no API key. The interview skills run it before a phase ends, and `write_spec_diff` refuses a change that leaves a file that does not parse. Pass the project directory to check every module at once.
|
|
165
165
|
|
|
166
|
-
What `check` cannot see - a fact in the wrong section, a
|
|
166
|
+
What `check` cannot see - a fact in the wrong section, a spec that is too big, two that conflict, wording no test can check - is what the rules, the skills and the reviewers are for.
|