@turri-dev/cli 0.1.5-next.6df6879 → 0.1.6-next.056c770

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.
@@ -45,7 +45,7 @@
45
45
  "mdHeading": "white",
46
46
  "mdLink": "neon",
47
47
  "mdLinkUrl": "path",
48
- "mdCode": "siestaTan",
48
+ "mdCode": "neonHover",
49
49
  "mdCodeBlock": "white",
50
50
  "mdCodeBlockBorder": "lines",
51
51
  "mdQuote": "grauzone",
package/README.md CHANGED
@@ -190,7 +190,7 @@ Start at the [documentation index](docs/README.md), or go straight to a page:
190
190
  | | |
191
191
  | --- | --- |
192
192
  | [Installation](docs/installation.md) | doctor, what turri needs of this machine, **turri** and `pi`, environment variables |
193
- | [Examples](examples/README.md) | projects to build with turri step by step: cognio from an empty folder, a CLI, a backend service with conformance tests, a REST API integration |
193
+ | [Examples](examples/README.md) | projects to build with turri step by step: cognio from an empty folder, guestbook - a backend and a frontend in minutes - a CLI, a backend service with conformance tests, a REST API integration |
194
194
  | [Using **turri**](docs/using.md) | the conversation, skills, tools, pulses and regen, choosing models, the command line |
195
195
  | [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` |
196
196
  | [Deploy](docs/deploy.md) | trying what is built: the last commit in `dist/<module>`, started on your machine, nothing installed without asking |
package/dist/cli.js CHANGED
@@ -19,7 +19,7 @@ import { dirname, join } from "node:path";
19
19
  import { fileURLToPath } from "node:url";
20
20
  import { CODING_MODEL_ENV, chatModels } from "./chat-models.js";
21
21
  import { readProjectConfig } from "./config.js";
22
- import { extensionInstalledGlobally, PACKAGE_ROOT, themesInstalledGlobally } from "./environment.js";
22
+ import { extensionInstalledGlobally, PACKAGE_ROOT, themesInstalledGlobally, versionLine } from "./environment.js";
23
23
  import { dropNoise } from "./quiet.js";
24
24
  /**
25
25
  * Start pi with this project's extension and themes already wired in.
@@ -92,6 +92,7 @@ const SETUP_COMMANDS = {
92
92
  const USAGE = [
93
93
  "usage: turri [options] open the conversation here: pi, with turri's tools and themes loaded",
94
94
  " turri <command> [args]",
95
+ " turri --version turri's version, and pi's",
95
96
  "",
96
97
  ` ${"options".padEnd(12)} --harness-model <m> the model you chat with --coding-model <m> the model renders run on`,
97
98
  ` ${"".padEnd(12)} --stack <name> build with config.turri.<name>.yaml over config.turri.yaml - here and in every command`,
@@ -139,6 +140,11 @@ if (command === "-h" || command === "--help") {
139
140
  console.log(USAGE);
140
141
  process.exit(0);
141
142
  }
143
+ // turri's version, not pi's alone: pi's --version would answer for it.
144
+ if (command === "-v" || command === "--version") {
145
+ console.log(versionLine());
146
+ process.exit(0);
147
+ }
142
148
  // turri alone is the conversation, and so is turri with pi's options - `turri -c`, `turri --harness-model <m>`. A word
143
149
  // turri does not know is an error below, not a conversation started on it.
144
150
  if (command === undefined || command.startsWith("-"))
package/dist/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { copyFileSync, existsSync, readdirSync, readFileSync, statSync } from "node:fs";
3
- import { join, resolve } from "node:path";
3
+ import { basename, join, resolve } from "node:path";
4
4
  import { load as loadYaml } from "js-yaml";
5
5
  import { projectDirOf } from "./regenerate/paths.js";
6
6
  /**
@@ -157,6 +157,22 @@ export function chooseStack(stack) {
157
157
  export function withStack(stack, work) {
158
158
  return pinnedStack.run({ stack }, work);
159
159
  }
160
+ /** Whether a stack is pinned here - named by the call that asked for the work, or kept by the pulse it runs in. */
161
+ export function stackPinned() {
162
+ return pinnedStack.getStore() !== undefined;
163
+ }
164
+ /**
165
+ * The stack a module is built with: the one pinned - named by the call, or kept by the pulse - else the stack named
166
+ * after the module, config.turri.backend.yaml for backend.plain, else the one chosen for the session; none, when none
167
+ * of them is.
168
+ */
169
+ export function stackForModule(plainFile) {
170
+ const root = projectDirOf(resolve(plainFile));
171
+ if (stackPinned())
172
+ return stackOf(root);
173
+ const own = basename(plainFile).replace(/\.plain$/, "");
174
+ return stacksOf(root).includes(own) ? own : stackOf(root);
175
+ }
160
176
  /** The words that choose no stack: the project's config alone. */
161
177
  export const NO_STACK = ["none", "off", "base", "-"];
162
178
  /**
@@ -478,10 +494,10 @@ function agentEnvSetting(value) {
478
494
  function withoutUndefined(config) {
479
495
  return Object.fromEntries(Object.entries(config).filter(([, value]) => value !== undefined));
480
496
  }
481
- /** The config a spec is built with: its project's, with the stack chosen over it. */
497
+ /** The config a spec is built with: its project's, with its module's stack over it (`stackForModule`). */
482
498
  export function configForSpec(plainFile) {
483
499
  const root = projectDirOf(resolve(plainFile));
484
- return readProjectConfig(root, stackOf(root));
500
+ return readProjectConfig(root, stackForModule(plainFile));
485
501
  }
486
502
  /** One config file, read, or nothing of it when it is not there or does not parse. */
487
503
  function readConfigFile(path) {
@@ -68,6 +68,32 @@ export function turriOnPath() {
68
68
  const found = which.status === 0 ? which.stdout.trim().split("\n")[0] : undefined;
69
69
  return found === "" ? undefined : found;
70
70
  }
71
+ /** The version of the pi turri runs on, as Node finds it - undefined when it is not installed. */
72
+ export function piVersion() {
73
+ try {
74
+ // Its entry point, then up to the package.json that names it.
75
+ for (let dir = dirname(fileURLToPath(import.meta.resolve("@earendil-works/pi-coding-agent"))); dirname(dir) !== dir; dir = dirname(dir)) {
76
+ const file = join(dir, "package.json");
77
+ if (!existsSync(file))
78
+ continue;
79
+ const pkg = JSON.parse(readFileSync(file, "utf8"));
80
+ if (pkg.name === "@earendil-works/pi-coding-agent")
81
+ return pkg.version;
82
+ }
83
+ }
84
+ catch {
85
+ /* not installed: left out */
86
+ }
87
+ return undefined;
88
+ }
89
+ /**
90
+ * What `turri --version` prints: turri's version - from a clone, its commit too, as the banner says it - and pi's
91
+ * beside it. pi's own `--version` says only pi's, which read as turri's.
92
+ */
93
+ export function versionLine(identity = turriIdentity(), pi = piVersion() ?? null) {
94
+ const clone = identity.git_commit !== null ? `, from a clone, ${identity.git_commit.slice(0, 7)}${identity.git_dirty === true ? " with changes" : ""}` : "";
95
+ return `turri ${identity.version ?? "0.0.0"}${clone}${pi !== null ? ` (pi ${pi})` : ""}`;
96
+ }
71
97
  /** turri's version, and the commit when it runs from a clone. */
72
98
  export function turriIdentity() {
73
99
  let version = null;
package/dist/extension.js CHANGED
@@ -1041,7 +1041,7 @@ export function projectConfigBlock(root) {
1041
1041
  return [
1042
1042
  ...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"),
1043
1043
  ...(stacks.length > 0
1044
- ? ["", `Stacks: ${stacks.join(", ")} - chosen now: ${chosen ?? "none, so the project's file alone"}. Each module is built with the stack of its part - a backend's spec with the backend stack: pass it as stack to regenerate, conform and glimpse, and it is pinned to that module's pulses, whatever is chosen after. Choose one for the whole session with the stack tool when the user asks.`]
1044
+ ? ["", `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.`]
1045
1045
  : []),
1046
1046
  // A stack: its file over the project's, for everything built while it is chosen.
1047
1047
  ...stacks.flatMap((stack) => [
@@ -2,7 +2,7 @@ import { existsSync } from "node:fs";
2
2
  import { basename, relative, resolve } from "node:path";
3
3
  import { defineTool } from "@earendil-works/pi-coding-agent";
4
4
  import { Type } from "typebox";
5
- import { chosenStack, CONFIG_FILE, readProjectConfig, stackConfigFile, stackNamed, stackOf, stacksOf, withStack } from "./config.js";
5
+ import { CONFIG_FILE, readProjectConfig, stackConfigFile, stackForModule, stackNamed, stacksOf, withStack } from "./config.js";
6
6
  import { checkPlainFile } from "./plain/check.js";
7
7
  import { describeDrift, describeModules } from "./regenerate/loop.js";
8
8
  import { pulseName } from "./regenerate/jobs.js";
@@ -28,29 +28,38 @@ export const MODEL_PARAM = Type.Optional(Type.String({
28
28
  export const STACK_PARAM = Type.Optional(Type.String({
29
29
  description: "In a project of stacks: the stack that builds this module - its config.turri.<stack>.yaml over config.turri.yaml, " +
30
30
  "its testing scripts, toolchains and models - for every pulse of it, whatever stack is chosen later. \"none\": " +
31
- "config.turri.yaml alone. Omitted: the stack chosen now.",
31
+ "config.turri.yaml alone. Omitted: the stack named after the module - config.turri.backend.yaml for backend.plain - " +
32
+ "else the one chosen now.",
32
33
  }));
33
34
  /**
34
- * `work` with the stack a call names - or, none named, the one chosen now - chosen for it: what it asks for is built with
35
- * that stack. A project of stacks that would be built with none, when config.turri.yaml alone names no unit testing
36
- * script, is refused before anything is planned: its pulses would all stop for want of one.
35
+ * Why a module asked for with no stack named cannot be built: a project of stacks where it has no stack of its own and
36
+ * none is chosen, and config.turri.yaml alone names no unit testing script - every pulse would stop for want of one.
37
+ * The stacks and the script each names, and what to do. Undefined when it can be built.
37
38
  */
38
- export function withCallStack(plainFile, named, work) {
39
+ export function stacklessBuild(plainFile) {
39
40
  const root = projectDirOf(plainFile);
40
- const stack = named !== undefined ? stackNamed(root, named) : chosenStack();
41
- return withStack(stack, () => {
42
- const stacks = stacksOf(root);
43
- if (named === undefined && stackOf(root) === undefined && stacks.length > 0 && findTestScripts(root).unit === undefined) {
44
- const scripts = stacks.map((each) => {
45
- const unit = withStack(each, () => findTestScripts(root).unit);
46
- return `${each}${unit !== undefined ? ` (${stackConfigFile(each)}: ${relative(root, unit)})` : ""}`;
47
- });
48
- throw new Error(`Nothing was started: ${basename(plainFile)} would be built with no stack - ${CONFIG_FILE} alone - and that names no unit testing ` +
49
- `script, so every pulse would stop. The project's stacks: ${scripts.join(", ")}. Pass the stack that builds ` +
50
- `${basename(plainFile)} as stack - or ask the user which, when the spec does not make it plain.`);
51
- }
52
- return work();
41
+ const stacks = stacksOf(root);
42
+ if (stacks.length === 0 || stackForModule(plainFile) !== undefined || findTestScripts(root).unit !== undefined)
43
+ return undefined;
44
+ const scripts = stacks.map((each) => {
45
+ const unit = withStack(each, () => findTestScripts(root).unit);
46
+ return `${each}${unit !== undefined ? ` (${stackConfigFile(each)}: ${relative(root, unit)})` : ""}`;
53
47
  });
48
+ return (`${basename(plainFile)} would be built with no stack - ${CONFIG_FILE} alone - and that names no unit testing script, so every ` +
49
+ `pulse would stop. No stack is named after it, and none is chosen. The project's stacks: ${scripts.join(", ")}. Pass the ` +
50
+ `stack that builds ${basename(plainFile)} as stack - or ask the user which, when the spec does not make it plain.`);
51
+ }
52
+ /**
53
+ * `work` with the stack a call names chosen for it - or, none named, as it is: each module is then built with its own
54
+ * (`stackForModule`). A module that could not be built that way is refused before anything is planned (`stacklessBuild`).
55
+ */
56
+ export function withCallStack(plainFile, named, work) {
57
+ if (named !== undefined)
58
+ return withStack(stackNamed(projectDirOf(plainFile), named), work);
59
+ const stackless = stacklessBuild(plainFile);
60
+ if (stackless !== undefined)
61
+ throw new Error(`Nothing was started: ${stackless}`);
62
+ return work();
54
63
  }
55
64
  export const WAIT_PARAM = Type.Optional(Type.Boolean({
56
65
  description: "Block until the work finishes instead of returning a pulse id. Defaults to false in the " +
@@ -164,7 +173,7 @@ export function regenerateTool(options) {
164
173
  "A change to a file the spec links - an API surface, a mapping document - is a change to the spec: regenerate sees it by itself, and regenerates every functional spec that uses what links it.",
165
174
  "Never stop pulses and start them again yourself to follow a spec change: regenerate does it.",
166
175
  "Set from_scratch only when the user asks for the module to be regenerated from scratch - started over, its code deleted - in this conversation, or agrees to it when a module's status says it has to start over: never to fix a pulse that did not land.",
167
- "In a project of stacks - a backend and a frontend, each with its config.turri.<stack>.yaml - pass stack: the one that builds this module's part, the backend's spec with the backend stack. It is pinned to the module's pulses: regenerate a backend and a frontend side by side, each with its own, without switching the stack chosen. What it says back names the stack each module is built with.",
176
+ "In a project of stacks - a backend and a frontend, each with its config.turri.<stack>.yaml - a module is built with the stack named after it by itself: backend.plain with config.turri.backend.yaml. Pass stack only for a module whose stack has another name. It is pinned to the module's pulses: a backend and a frontend regenerate side by side, each with its own, without switching the stack chosen. What it says back names the stack each module is built with.",
168
177
  'from_scratch "module" takes out the code where the module is built; the modules below keep theirs, and the module starts from a copy of the code below it. "chain" takes out every codebase of its requires chain, the base\'s too, and builds all of it again from the bottom: use it after modules were split, merged or moved (refactor-module), or when the user asks for the whole chain.',
169
178
  ],
170
179
  parameters: Type.Object({
@@ -13,7 +13,7 @@ import { adoptDigests, chainTree, codeTree, conformanceTarget, REGENERATE_AGAIN,
13
13
  import { describeSpecDiffConformance, renderSpecDiffConformance } from "./conform.js";
14
14
  import { conformanceDirFor } from "./conformance.js";
15
15
  import { modulesRoot } from "./paths.js";
16
- import { budgetFor, chosenStack, choosingWhen, configForSpec, conformanceSwitchedOn, withStack } from "../config.js";
16
+ import { budgetFor, choosingWhen, configForSpec, conformanceSwitchedOn, stackForModule, stackPinned, withStack } from "../config.js";
17
17
  import { describeSpecChange } from "./prompt.js";
18
18
  import { resolveTemplateDirs } from "./paths.js";
19
19
  import { builtTips, copyRequiredCode, importingModules, isImportModule, newlyRequired, resolveRequiresChain, startFromScratch } from "./requires.js";
@@ -1032,8 +1032,8 @@ export function createWorkLoop(options) {
1032
1032
  return { status, jobs: (state.running !== undefined ? listed.slice(1) : listed).map((item) => item.job), scheduled: (state.running !== undefined ? listed.slice(1) : listed).map((item) => (item.entry.waitsFor !== undefined ? { title: item.entry.title, specs: item.entry.cases.add.map((touched) => touched.text) } : undefined)) };
1033
1033
  };
1034
1034
  /**
1035
- * Look after a codebase - its chain up to it - from now on; says its state. `pin`: the stack it is built with from now
1036
- * on - kept, when `keep` and it has one already.
1035
+ * Look after a codebase - its chain up to it - from now on; says its state. `pin`: its stack is worked out again - its
1036
+ * tip's (`stackForModule`) - unless `keep`, and it has one: a spec change keeps the stack it was asked for with.
1037
1037
  */
1038
1038
  const track = (chain, askedFile, request, pin) => {
1039
1039
  const tip = tipOf(chain);
@@ -1050,8 +1050,10 @@ export function createWorkLoop(options) {
1050
1050
  }
1051
1051
  state.chain = chain;
1052
1052
  state.askedFile = askedFile;
1053
- if (pin !== undefined && (pin.keep !== true || state.stack === undefined))
1054
- state.stack = { name: pin.name };
1053
+ // Kept only when it is a stack: a module pinned to none - built before its stack's file was there - takes its own.
1054
+ const keep = pin?.keep === true && !stackPinned() && state.stack?.name !== undefined;
1055
+ if (pin !== undefined && !keep)
1056
+ state.stack = { name: stackForModule(tip.plainFile) };
1055
1057
  // Its model as its stack has it: a stack's file can name a coding model of its own.
1056
1058
  const model = request.model ?? inStack(state, () => options.modelFor?.(tip.plainFile));
1057
1059
  if (model !== undefined)
@@ -1066,9 +1068,9 @@ export function createWorkLoop(options) {
1066
1068
  const files = codebasesOf(resolve(options.cwd, plainFile));
1067
1069
  for (const file of files)
1068
1070
  asked.set(file, { ...asked.get(file), ...request });
1069
- // Asked for, it is built with the stack chosen now - pinned, whatever is chosen later; a spec change keeps the one
1070
- // it was asked for with.
1071
- const pin = { name: chosenStack(), keep: specChange === true };
1071
+ // Asked for, it is built with its stack - the one the call names, its own, or the one chosen now - pinned, whatever
1072
+ // is chosen later; a spec change keeps the one it was asked for with.
1073
+ const pin = { keep: specChange === true };
1072
1074
  const states = withParseCache(() => files.flatMap((file) => chainsFor(file).flatMap((chain) => toBuild(chain).map((entry) => track(chain.slice(0, chain.indexOf(entry) + 1), file, request, pin)))));
1073
1075
  // Asked again, a halted codebase is tried again - one the person halted too, unless only its spec changed.
1074
1076
  for (const state of states) {
@@ -1191,7 +1193,7 @@ export function createWorkLoop(options) {
1191
1193
  }
1192
1194
  }
1193
1195
  }
1194
- const tracked = withParseCache(() => (wentBack ? [own] : chainsFor(file)).flatMap((chain) => toBuild(chain).map((entry) => track(chain.slice(0, chain.indexOf(entry) + 1), file, request, { name: chosenStack() }))));
1196
+ const tracked = withParseCache(() => (wentBack ? [own] : chainsFor(file)).flatMap((chain) => toBuild(chain).map((entry) => track(chain.slice(0, chain.indexOf(entry) + 1), file, request, {}))));
1195
1197
  // The module asked for, in its own codebase - and those below it: never the codebases built on it.
1196
1198
  const asking = tracked.find((state) => tipEntry(state).buildDir === self.buildDir);
1197
1199
  const states = asking !== undefined ? tracked.filter((state) => own.some((entry) => entry.buildDir === tipEntry(state).buildDir)) : tracked;
@@ -8,6 +8,9 @@ import { applySpecDiff, plainFilesOf, revertSpecDiff } from "./regenerate/regene
8
8
  import { MissingFunctionalitiesError } from "./plain/errors.js";
9
9
  import { describeModules } from "./regenerate/loop.js";
10
10
  import { reviewNote } from "./tools.js";
11
+ import { STACK_PARAM, stacklessBuild, withCallStack } from "./loop-tools.js";
12
+ import { stackNamed } from "./config.js";
13
+ import { projectDirOf } from "./regenerate/paths.js";
11
14
  /**
12
15
  * Writing a spec change as a diff, and building it, as one step.
13
16
  *
@@ -37,12 +40,14 @@ export function createSpecDiffTools(options) {
37
40
  skillPaced
38
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."
39
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.",
43
+ "In a project of stacks, the change is built with the stack named after the module - backend.plain with config.turri.backend.yaml - by itself. For a module whose stack has another name, pass it as stack. A change that cannot be built yet for want of a stack is written all the same: carry on, and pass the stack with the next change.",
40
44
  "To take something out of what is already written and queued - the user drops a page or a feature - write the diff that removes it from every spec that names it, with fixes_earlier. What is left to build is worked out from the spec each time, and a build the change undoes is stopped by itself; never stop builds to drop a change - it stays in the files.",
41
45
  ],
42
46
  parameters: Type.Object({
43
47
  plain_file: Type.String({ description: "The .plain file of the module the change is built for" }),
44
48
  diff: Type.String({ description: "The change, as a git diff (diff --git a/<file> b/<file> ...)" }),
45
49
  fixes_earlier: Type.Optional(Type.Boolean({ description: "This corrects something already written - a contradiction, a review finding" })),
50
+ stack: STACK_PARAM,
46
51
  guidance: Type.Optional(Type.String({
47
52
  description: "How to plan the queue for this change, in a sentence or two - which functional specs go together, what first - as the " +
48
53
  "user said it or as the interview settled it. A strong preference for the planner. Omit when there is none.",
@@ -87,12 +92,25 @@ export function createSpecDiffTools(options) {
87
92
  answeredSinceLastDiff = false;
88
93
  owed = false;
89
94
  liveReview?.notice(`${params.plain_file}, after the change to ${touched.join(", ")}`);
90
- // In the files, the change is work: the loop works out what is left to build and plans it.
91
- // A spec change, not a request: a module the person halted stays halted.
92
- const statuses = await loop.bringUp(params.plain_file, { specChange: true, ...(params.guidance !== undefined ? { guidance: params.guidance } : {}) });
95
+ // In the files, the change is work: the loop works out what is left to build and plans it - with the stack the call
96
+ // names, else the module's own. A spec change, not a request: a module the person halted stays halted. The change
97
+ // is written whatever the stack: one that cannot be built yet is said, and built once it can - nothing is lost.
98
+ const building = resolve(cwd, params.plain_file);
99
+ let stackless;
100
+ try {
101
+ stackless = params.stack === undefined ? stacklessBuild(building) : (stackNamed(projectDirOf(building), params.stack), undefined);
102
+ }
103
+ catch (error) {
104
+ stackless = error instanceof Error ? error.message : String(error);
105
+ }
106
+ const statuses = stackless === undefined
107
+ ? await withCallStack(building, params.stack, () => loop.bringUp(params.plain_file, { specChange: true, ...(params.guidance !== undefined ? { guidance: params.guidance } : {}) }))
108
+ : [];
93
109
  const lines = [
94
110
  result === "applied" ? `Applied the change to ${touched.join(", ")}.` : `The change to ${touched.join(", ")} was already applied.`,
95
- `Building in the background:\n${describeModules(statuses)}\n`,
111
+ stackless === undefined
112
+ ? `Building in the background:\n${describeModules(statuses)}\n`
113
+ : `Not built yet - ${stackless} The change is in the spec: pass stack with the next change, or call regenerate with it, and everything written so far is built.\n`,
96
114
  // An interview goes back to its questions; a skill that writes on its own, straight to its next change - a stop to ask ends it.
97
115
  paced
98
116
  ? "Carry on with the interview."
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. To build a part of the project another stack builds - a backend's, a frontend's - pass that stack as stack to regenerate, conform or glimpse instead: it is pinned to that module's pulses, and the session's choice stays as it is.",
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/docs/config.md CHANGED
@@ -216,22 +216,22 @@ coding-model:
216
216
  thinking: high
217
217
  ```
218
218
 
219
- A stack is chosen, never worked out from the spec:
219
+ Which stack builds a module, first match wins:
220
220
 
221
- - **In the chat, for a module**, by the chat's model: `regenerate`, `conform` and `glimpse` take a `stack`, and it builds each module with the stack of its part - `backend.plain` with `backend` - leaving the session's choice as it is. A backend and a frontend can be regenerated side by side this way, each with its own.
222
- - **In the chat, for the session**, with `/stack` - a list of the project's stacks and none - or `/stack backend`, `/stack none`. The chat's model has a `stack` tool too, and switches the stack when you ask it to. `/stack` alone says which is chosen now; a module asked for without a stack of its own is built with it.
223
- - **On the command line**, with `--stack <name>` on any command - `turri regenerate`, `conform`, `spec-diff`, `glimpse`, `release`, `cultivate` - and on `turri --stack backend` for the chat; or with `TURRI_STACK` in the environment.
221
+ - **The one a call names.** `regenerate`, `conform`, `glimpse` and `write_spec_diff` take a `stack`; the chat's model passes it for a module whose stack has a name of its own, leaving the session's choice as it is.
222
+ - **The stack named after the module** - `config.turri.backend.yaml` builds `backend.plain` - with nothing to choose or pass. Name each stack after the module it builds, and a backend and a frontend are each built with their own, side by side - in the chat, in a brood as each change is written, and on the command line.
223
+ - **The one chosen for the session** - in the chat, with `/stack` - a list of the project's stacks and none - or `/stack backend`, `/stack none`. The chat's model has a `stack` tool too, and switches the stack when you ask it to. `/stack` alone says which is chosen now. On the command line, with `--stack <name>` on any command - `turri regenerate`, `conform`, `spec-diff`, `glimpse`, `release`, `cultivate` - and on `turri --stack backend` for the chat; or with `TURRI_STACK` in the environment.
224
224
 
225
225
  The choice lasts for the session only: nothing remembers it, and turri started again has none chosen. A stack the project has no file for is refused, with the stacks it has. With none chosen, `config.turri.yaml` alone is used.
226
226
 
227
227
  How a stack's config is made:
228
228
 
229
229
  - **Laid over `config.turri.yaml`, key by key.** What the stack's file gives wins; what it leaves out is the project's. A model it names takes what it leaves out from the project's same model - above, with the stack frontend chosen, everything renders on the project's coding model, thinking `high`.
230
- - **Pinned to a module when it is asked for.** A module is built with the stack chosen - or named in the call - when it is asked for, from its first pulse to its last: the pulses planned after it, its test runs, the scripts found, its plans and its conformance tests, whatever stack is chosen meanwhile. A pulse waiting for a free slot keeps the stack it was queued with. A change to its spec keeps its stack; asked for again, it takes the one chosen then. What `regenerate` says back names each module's stack, and so does a pulse's report.
231
- - **Refused with none, when that cannot build.** In a project of stacks, a module asked for with no stack - none named, none chosen - is refused before anything is planned when `config.turri.yaml` alone names no unit testing script: every pulse would stop for want of one. The refusal names the stacks and the script each names. `stack: none` builds with `config.turri.yaml` alone all the same.
230
+ - **Pinned to a module when it is asked for.** A module is built with the stack it was asked for with, from its first pulse to its last: the pulses planned after it, its test runs, the scripts found, its plans and its conformance tests, whatever stack is chosen meanwhile. A pulse waiting for a free slot keeps the stack it was queued with. A change to its spec keeps its stack - unless it was built with none and has a stack of its own now; asked for again, its stack is worked out again. What `regenerate` says back names each module's stack, and so does a pulse's report.
231
+ - **Refused with none, when that cannot build.** In a project of stacks, a module asked for with no stack - none named, none named after it, none chosen - is refused before anything is planned when `config.turri.yaml` alone names no unit testing script: every pulse would stop for want of one. The refusal names the stacks and the script each names. A spec change `write_spec_diff` writes is never refused: it is written, and said not to be built yet - passed the stack, everything written is built. `stack: none` builds with `config.turri.yaml` alone all the same.
232
232
  - **The project's alone.** `module:`, `harness-model:`, `parallel-jobs:`, `rules-off:` and `compact-at:` are read only from `config.turri.yaml` - there is one conversation, one module built when none is named, one machine. `agent-env:` and `toolchains:` in a stack's file add to the project's.
233
233
 
234
- The chat is told each stack's config and the one chosen. The skills that write testing scripts put each stack's in its own file once a project has two stacks, and say which stack to choose before building each part.
234
+ The chat is told each stack's config and the one chosen. The skills that write testing scripts put each stack's in its own file once a project has two stacks, each stack named after the module it builds.
235
235
 
236
236
  ## The old config.yaml
237
237
 
@@ -17,7 +17,7 @@ turri init-config # a config.turri.yaml with the model you talk to and the one
17
17
  turri # pi, with turri's tools, skills and themes loaded
18
18
  ```
19
19
 
20
- The package is `@turri-dev/cli` - npm keeps the name `turri` back, as too like another package's - and the command it puts on your PATH is `turri`. `npm install -g @turri-dev/cli@next` gets the latest prerelease instead, built from the last commit on `main`.
20
+ The package is `@turri-dev/cli` - npm keeps the name `turri` back, as too like another package's - and the command it puts on your PATH is `turri`. `npm install -g @turri-dev/cli@next` gets the latest prerelease instead, built from the last commit on `main`. Installed again - `@latest` or `@next` - it replaces the one there. `turri --version` says which turri runs, and the pi under it: `turri 0.1.6 (pi 1.3.0)`, and from a clone its commit too.
21
21
 
22
22
  ## From a clone
23
23
 
package/docs/theming.md CHANGED
@@ -26,10 +26,10 @@ Both themes, the command line's colours, the spec diff's, the banner's jellyfish
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, the jellyfish's shine |
29
- | siesta tan | `#e9d8c8` | inline code, \*\*\*plain's text in quotes or backticks |
29
+ | siesta tan | `#e9d8c8` | strings in code, \*\*\*plain's text in quotes or backticks |
30
30
  | eggplant | `#732553` | a removed line's fill in a spec diff |
31
31
  | neon | `#5ce1e6` | links, focus, the selection, section labels, \*\*\*plain's concepts in a spec diff: `accent`, `mdLink`, regen's `◆` |
32
- | neon hover | `#8eebee` | types in code, an added line's fill in a light theme's spec diff |
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
33
  | pink | `#ff5c8d` | the wordmark, the jellyfish's bell, and what is done - its `✓`, the spec map's `◇` and `◆`: `success` |
34
34
  | pink hover | `#ff7fa6` | \*\*\*plain's section headers, a removed line's `-`, keywords in code |
35
35
  | 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` |
package/docs/using.md CHANGED
@@ -102,7 +102,7 @@ In `print`, `json` and `rpc` modes the process ends when the answer is written,
102
102
 
103
103
  The chat reads the project's `config.turri.yaml` afresh every turn and follows it: its agent is told what it sets, and the render tools default to it - `module` when you ask to build without naming one, `conformance-tests: false` to render without conformance tests (none written, none of the existing ones run) unless you ask for them, `coding-model` for what renders run on, and the testing scripts. `harness-model` is what `turri` starts on. What you ask for in the conversation wins. Every key, what happens without it, and how the models inherit: [the project config](config.md).
104
104
 
105
- A project whose parts are on different stacks gives each stack its own `config.turri.<stack>.yaml` beside `config.turri.yaml` - `config.turri.backend.yaml`, `config.turri.frontend.yaml` - with its testing scripts, models and conformance over the project's. A stack is chosen, never worked out: ask for the backend and the chat's model regenerates it with `stack: backend`, the frontend with `stack: frontend` - each pinned to its module's pulses, so both can be built side by side - or choose one for the session with `/stack backend`. The chat is told each stack's and which is chosen, and the status bar shows it. See [stacks](config.md#stacks).
105
+ A project whose parts are on different stacks gives each stack its own `config.turri.<stack>.yaml` beside `config.turri.yaml` - `config.turri.backend.yaml`, `config.turri.frontend.yaml` - with its testing scripts, models and conformance over the project's. A stack named after its module builds it - `config.turri.backend.yaml` builds `backend.plain` - with nothing to choose, so a backend and a frontend are each built with their own, side by side, a brood's changes too; for a stack named otherwise the chat's model passes it as `stack`, or choose one for the session with `/stack backend`. The chat is told each stack's and which is chosen, and the status bar shows it. See [stacks](config.md#stacks).
106
106
 
107
107
  A project with only a `config.yaml` - turri's file before it had its own name, or another tool's - is read from it until turri first writes a setting, which copies it to `config.turri.yaml`; see [the old config.yaml](config.md#the-old-configyaml).
108
108
 
@@ -102,7 +102,7 @@ Create the module structure and frontmatter so there is a `.plain` file to build
102
102
 
103
103
  Write no definitions, no functional specs, no `***test reqs***` and no `***acceptance tests***`.
104
104
 
105
- End the phase with the unit tests: their framework, as a `:UnitTests:` requirement, and the unit testing script, which every build needs from the first one on. If turri already finds a script - named by `unittests-script:` in `config.turri.yaml` at the project root, or the one `run_unittests*.sh` in the root or in `test_scripts/` - keep it. Otherwise write `test_scripts/run_unittests_<language>.sh` with the `implement-unit-testing-script` skill, for the language and framework just settled. Do not link it from the specs. Without it, every build stops before it starts. When this module sits in a project beside another on a different stack - a frontend beside a backend, sharing only an imported module - its script is its stack's: name it in that stack's `config.turri.<stack>.yaml` beside `config.turri.yaml` (`unittests-script:`) - `config.turri.frontend.yaml` for the stack `frontend` - not in `config.turri.yaml`, which is the project's and holds what the stacks share. A stack is chosen, never worked out: this module is built with its own - in the chat, `regenerate` with `stack: <stack>`, pinned to its pulses whatever stack is chosen after; `--stack <stack>` on the command line - so say which stack builds it.
105
+ End the phase with the unit tests: their framework, as a `:UnitTests:` requirement, and the unit testing script, which every build needs from the first one on. If turri already finds a script - named by `unittests-script:` in `config.turri.yaml` at the project root, or the one `run_unittests*.sh` in the root or in `test_scripts/` - keep it. Otherwise write `test_scripts/run_unittests_<language>.sh` with the `implement-unit-testing-script` skill, for the language and framework just settled. Do not link it from the specs. Without it, every build stops before it starts. When this module sits in a project beside another on a different stack - a frontend beside a backend, sharing only an imported module - its script is its stack's: name it in that stack's `config.turri.<stack>.yaml` beside `config.turri.yaml` (`unittests-script:`) - `config.turri.frontend.yaml` for the stack `frontend` - not in `config.turri.yaml`, which is the project's and holds what the stacks share. Name the stack after this module - `config.turri.frontend.yaml` for `frontend.plain` - and it is built with it by itself, with nothing to choose. A stack the project already has under another name is passed as `stack` to `write_spec_diff`.
106
106
 
107
107
  The unit testing script runs the static checks too - the formatter, linter and type checker the stack names, which count as the unit tests do; `implement-unit-testing-script` says which for each language. There is no separate static checks script.
108
108
 
@@ -19,7 +19,7 @@ Write no `***acceptance tests***`, no functional specs and no `***implementation
19
19
  3. **How the tests reach the software** - as a subprocess of the CLI, over HTTP against a server the tests start, through the public entry point it exports. Author it.
20
20
  4. **Setup and outside services** - fixtures, test data, databases, third-party APIs: started for real, faked, or mocked at the boundary. Author each decision as its own requirement.
21
21
  5. **How the whole suite is run** - the one command that discovers every test. Author it.
22
- 6. **The conformance testing script** - if turri already finds one (named by `conformance-tests-script:` in `config.turri.yaml`, or the one `run_conformance_tests*.sh` in the project root or in `test_scripts/`), check it runs the suite the way the test reqs now say, and keep it. Otherwise write `test_scripts/run_conformance_tests_<language>.sh` with `implement-conformance-testing-script`, to match the test reqs. Never link it from the specs. If `test_scripts/` would hold two of a kind, name the one to use in `config.turri.yaml`. In a project whose parts are on different stacks, name each stack's script in its own `config.turri.<stack>.yaml` (`conformance-tests-script:`), and build each part with its own stack - in the chat, `regenerate` with `stack: <stack>`, pinned to that part's pulses; `--stack <stack>` on the command line.
22
+ 6. **The conformance testing script** - if turri already finds one (named by `conformance-tests-script:` in `config.turri.yaml`, or the one `run_conformance_tests*.sh` in the project root or in `test_scripts/`), check it runs the suite the way the test reqs now say, and keep it. Otherwise write `test_scripts/run_conformance_tests_<language>.sh` with `implement-conformance-testing-script`, to match the test reqs. Never link it from the specs. If `test_scripts/` would hold two of a kind, name the one to use in `config.turri.yaml`. In a project whose parts are on different stacks, name each stack's script in its own `config.turri.<stack>.yaml` (`conformance-tests-script:`), each stack named after the module it builds - `config.turri.backend.yaml` for `backend.plain` - so each part is built with its own by itself.
23
23
  7. **Anything else** - anything about testing the user wants to add or change.
24
24
 
25
25
  Run `validate` on every module you changed. The product phase's first build needs the test reqs and the conformance testing script both there: without them it stops before it starts.
@@ -208,7 +208,7 @@ The harness never runs `.ps1` scripts. These notes apply only to an optional cop
208
208
  4. Pick the right row from the [Dependency isolation](#dependency-isolation) table — use the same flag/env var in steps 7, 7b and 8 — and the checks from [Static checks of the tests](#static-checks-of-the-tests).
209
209
  5. Add the language-appropriate "no tests discovered" guard from [No tests discovered detection](#no-tests-discovered-detection), and the guard against conformance tests in the build folder from [Tests in the build folder](#tests-in-the-build-folder).
210
210
  6. Save the new script as `test_scripts/run_conformance_tests_<lang>.sh` and `chmod +x` it. Write a `.ps1` copy only if the user wants to run it by hand on Windows.
211
- 7. **Make sure turri finds the script.** Do not link it from the specs - turri finds the conformance testing script in the project root (the folder that holds `plain_modules/`) and puts it, whole, into the prompts of the agents that write and review the conformance tests itself, whenever conformance tests are written for a build; a link to it from a spec is ignored. It looks, in order, at `config.turri.yaml` in the root (`conformance-tests-script: <path relative to the root>`) - with the chosen stack's `config.turri.<stack>.yaml` over it, then for the one `run_conformance_tests*.sh` in the root, then for the one in `test_scripts/`. Saving the script in `test_scripts/` (or the root) is enough when it is the only one there. If the project keeps it elsewhere, name it in `config.turri.yaml` as `conformance-tests-script:` - and when the project has only a `config.yaml`, copy it to `config.turri.yaml` first (and each `config.<name>.yaml` to `config.turri.<name>.yaml`) and edit the copy; `config.yaml` may be another tool's, and is never written. If the project has modules on different stacks - e.g. a Python backend and a React frontend side by side, sharing only an imported module - each part is on a stack of its own, and each stack names its own script in its own file beside `config.turri.yaml`: `conformance-tests-script:` in `config.turri.backend.yaml` for the stack `backend`, in `config.turri.frontend.yaml` for the stack `frontend`. A stack is chosen, never worked out: each part is built with its own - in the chat, `regenerate` with `stack: <stack>`, pinned to that part's pulses whatever stack is chosen after; `--stack <stack>` on the command line - so say which stack builds which part. turri never guesses between two. The script is required: with conformance testing on, a build without one stops before it starts. The `***test reqs***` still describe the conformance framework and command in words; do not paraphrase the script into them.
211
+ 7. **Make sure turri finds the script.** Do not link it from the specs - turri finds the conformance testing script in the project root (the folder that holds `plain_modules/`) and puts it, whole, into the prompts of the agents that write and review the conformance tests itself, whenever conformance tests are written for a build; a link to it from a spec is ignored. It looks, in order, at `config.turri.yaml` in the root (`conformance-tests-script: <path relative to the root>`) - with the chosen stack's `config.turri.<stack>.yaml` over it, then for the one `run_conformance_tests*.sh` in the root, then for the one in `test_scripts/`. Saving the script in `test_scripts/` (or the root) is enough when it is the only one there. If the project keeps it elsewhere, name it in `config.turri.yaml` as `conformance-tests-script:` - and when the project has only a `config.yaml`, copy it to `config.turri.yaml` first (and each `config.<name>.yaml` to `config.turri.<name>.yaml`) and edit the copy; `config.yaml` may be another tool's, and is never written. If the project has modules on different stacks - e.g. a Python backend and a React frontend side by side, sharing only an imported module - each part is on a stack of its own, and each stack names its own script in its own file beside `config.turri.yaml`: `conformance-tests-script:` in `config.turri.backend.yaml` for the stack `backend`, in `config.turri.frontend.yaml` for the stack `frontend`. Name each stack after the module it builds - `config.turri.backend.yaml` for `backend.plain` - and that module is built with it by itself, with nothing to choose; a stack named otherwise is passed as `stack` to `regenerate` in the chat, or `--stack <stack>` on the command line. turri never guesses between two. The script is required: with conformance testing on, a build without one stops before it starts. The `***test reqs***` still describe the conformance framework and command in words; do not paraphrase the script into them.
212
212
 
213
213
  ## Anti-Patterns
214
214
 
@@ -139,7 +139,7 @@ Notes:
139
139
  3. Translate each of the eight steps above into the equivalent commands for the target language **and** shell. The toolchain check, dependency install, static checks and test invocation are the language-specific parts; the rest is mechanical translation between Bash and PowerShell syntax.
140
140
  4. Pick the dependency-isolation mechanism from the [Dependency isolation](#dependency-isolation) table and use it consistently in steps 6, 7 and 8, and the checks from the [Static checks](#static-checks) table.
141
141
  5. Save the new script as `test_scripts/run_unittests_<lang>.sh` (a `.ps1` copy next to it only if the user wants to run it by hand on Windows). For Bash, `chmod +x` it.
142
- 6. **Make sure turri finds the script.** Do not link it from the specs - turri finds the unit testing script in the project root (the folder that holds `plain_modules/`) and puts it, whole, into the prompt of the coding agent itself; a link to it from a spec is ignored. It looks, in order, at `config.turri.yaml` in the root (`unittests-script: <path relative to the root>`) - with the chosen stack's `config.turri.<stack>.yaml` over it, then for the one `run_unittests*.sh` in the root, then for the one in `test_scripts/`. Saving the script in `test_scripts/` (or the root) is enough when it is the only one there. If the project keeps it elsewhere, name it in `config.turri.yaml` as `unittests-script:` - and when the project has only a `config.yaml`, copy it to `config.turri.yaml` first (and each `config.<name>.yaml` to `config.turri.<name>.yaml`) and edit the copy; `config.yaml` may be another tool's, and is never written. If the project has modules on different stacks - e.g. a Python backend and a React frontend side by side, sharing only an imported module - each part is on a stack of its own, and each stack names its own script in its own file beside `config.turri.yaml`: `unittests-script:` in `config.turri.backend.yaml` for the stack `backend`, in `config.turri.frontend.yaml` for the stack `frontend`. A stack is chosen, never worked out: each part is built with its own - in the chat, `regenerate` with `stack: <stack>`, pinned to that part's pulses whatever stack is chosen after; `--stack <stack>` on the command line - so say which stack builds which part. turri never guesses between two. The script is required: a build without one stops before it starts.
142
+ 6. **Make sure turri finds the script.** Do not link it from the specs - turri finds the unit testing script in the project root (the folder that holds `plain_modules/`) and puts it, whole, into the prompt of the coding agent itself; a link to it from a spec is ignored. It looks, in order, at `config.turri.yaml` in the root (`unittests-script: <path relative to the root>`) - with the chosen stack's `config.turri.<stack>.yaml` over it, then for the one `run_unittests*.sh` in the root, then for the one in `test_scripts/`. Saving the script in `test_scripts/` (or the root) is enough when it is the only one there. If the project keeps it elsewhere, name it in `config.turri.yaml` as `unittests-script:` - and when the project has only a `config.yaml`, copy it to `config.turri.yaml` first (and each `config.<name>.yaml` to `config.turri.<name>.yaml`) and edit the copy; `config.yaml` may be another tool's, and is never written. If the project has modules on different stacks - e.g. a Python backend and a React frontend side by side, sharing only an imported module - each part is on a stack of its own, and each stack names its own script in its own file beside `config.turri.yaml`: `unittests-script:` in `config.turri.backend.yaml` for the stack `backend`, in `config.turri.frontend.yaml` for the stack `frontend`. Name each stack after the module it builds - `config.turri.backend.yaml` for `backend.plain` - and that module is built with it by itself, with nothing to choose; a stack named otherwise is passed as `stack` to `regenerate` in the chat, or `--stack <stack>` on the command line. turri never guesses between two. The script is required: a build without one stops before it starts.
143
143
 
144
144
  ## Anti-Patterns
145
145
 
@@ -96,7 +96,7 @@ Call the corresponding implementation skills in order. Each skill writes a Bash
96
96
 
97
97
  ### 6. Check turri finds the scripts
98
98
 
99
- turri finds the testing scripts in the project root, not through the specs: first in `config.turri.yaml` (`unittests-script:`, `conformance-tests-script:`, paths relative to the root), then the one `run_unittests*.sh` / `run_conformance_tests*.sh` in the root, then the one of each kind in `test_scripts/`. It puts them into the prompts of the agents that write and run the tests itself, so do not link them from `template/base.plain` (a link to one is ignored). Confirm every generated script is in `test_scripts/` and is the only one of its kind there - or, if the project wants it, that a `config.turri.yaml` at the root names the one to use and the path resolves. A project with parts on different stacks (e.g. a Python backend and a React frontend sharing an imported `base`) keeps one `config.turri.yaml` for what they share and gives each stack its own `config.turri.<stack>.yaml` beside it - `config.turri.backend.yaml`, `config.turri.frontend.yaml` - naming that stack's `unittests-script:` and `conformance-tests-script:`. A stack is chosen, never worked out: each part is built with its own - in the chat, `regenerate` with `stack: <stack>`, pinned to that part's pulses whatever stack is chosen after; `--stack <stack>` on the command line - so say which stack builds which part. When the project has only a `config.yaml`, copy it to `config.turri.yaml` first - and each `config.<name>.yaml` to `config.turri.<name>.yaml` - and write the copies; never write `config.yaml`.
99
+ turri finds the testing scripts in the project root, not through the specs: first in `config.turri.yaml` (`unittests-script:`, `conformance-tests-script:`, paths relative to the root), then the one `run_unittests*.sh` / `run_conformance_tests*.sh` in the root, then the one of each kind in `test_scripts/`. It puts them into the prompts of the agents that write and run the tests itself, so do not link them from `template/base.plain` (a link to one is ignored). Confirm every generated script is in `test_scripts/` and is the only one of its kind there - or, if the project wants it, that a `config.turri.yaml` at the root names the one to use and the path resolves. A project with parts on different stacks (e.g. a Python backend and a React frontend sharing an imported `base`) keeps one `config.turri.yaml` for what they share and gives each stack its own `config.turri.<stack>.yaml` beside it - `config.turri.backend.yaml`, `config.turri.frontend.yaml` - naming that stack's `unittests-script:` and `conformance-tests-script:`. Name each stack after the module it builds - `config.turri.backend.yaml` for `backend.plain` - and that module is built with it by itself, with nothing to choose; a stack named otherwise is passed as `stack` to `regenerate` in the chat, or `--stack <stack>` on the command line. When the project has only a `config.yaml`, copy it to `config.turri.yaml` first - and each `config.<name>.yaml` to `config.turri.<name>.yaml` - and write the copies; never write `config.yaml`.
100
100
 
101
101
  Do **not** run `validate` here — keeping this skill light is the whole point. The user can run it when they want validation.
102
102
 
@@ -38,7 +38,7 @@ Read what is already there first: an existing `.plain` file, `config.turri.yaml`
38
38
 
39
39
  - Templates go in `template/`; import paths omit the `template/` prefix. Resources go in `resources/`.
40
40
  - Generated code lands in `plain_modules/<module>/code/`, conformance tests in `plain_modules/<module>/tests/` (both read-only).
41
- - One module, unless the intent needs two stacks - a web frontend and an API backend. Then two modules side by side, neither requiring the other, sharing what they both mean through one imported module in `template/`. Each is on a stack of its own, whose testing scripts are named in `config.turri.<stack>.yaml` beside `config.turri.yaml` (`unittests-script:`, `conformance-tests-script:`) - `config.turri.backend.yaml`, `config.turri.frontend.yaml`. A stack is chosen, never worked out: each part is built with its own - in the chat, `regenerate` with `stack: <stack>`, pinned to that part's pulses whatever stack is chosen after; `--stack <stack>` on the command line - so say which stack builds which part.
41
+ - One module, unless the intent needs two stacks - a web frontend and an API backend. Then two modules side by side, neither requiring the other, sharing what they both mean through one imported module in `template/`. Each is on a stack of its own, whose testing scripts are named in `config.turri.<stack>.yaml` beside `config.turri.yaml` (`unittests-script:`, `conformance-tests-script:`) - `config.turri.backend.yaml`, `config.turri.frontend.yaml`. Name each stack after the module it builds - `config.turri.backend.yaml` for `backend.plain` - and that module is built with it by itself, with nothing to choose; a stack named otherwise is passed as `stack` to `regenerate` in the chat, or `--stack <stack>` on the command line.
42
42
 
43
43
  ## Steps
44
44
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turri-dev/cli",
3
- "version": "0.1.5-next.6df6879",
3
+ "version": "0.1.6-next.056c770",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {