@turri-dev/cli 0.1.4-next.fa9223b → 0.1.5-next.6df6879
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.pi/themes/turri-dark.json +67 -66
- package/.pi/themes/turri-light.json +71 -73
- package/README.md +41 -127
- package/assets/jellyfish/dark-00.png +0 -0
- package/assets/jellyfish/dark-01.png +0 -0
- package/assets/jellyfish/dark-02.png +0 -0
- package/assets/jellyfish/dark-03.png +0 -0
- package/assets/jellyfish/dark-04.png +0 -0
- package/assets/jellyfish/dark-05.png +0 -0
- package/assets/jellyfish/dark-06.png +0 -0
- package/assets/jellyfish/dark-07.png +0 -0
- package/assets/jellyfish/dark-08.png +0 -0
- package/assets/jellyfish/dark-09.png +0 -0
- package/assets/jellyfish/dark-10.png +0 -0
- package/assets/jellyfish/dark-11.png +0 -0
- package/assets/jellyfish/light-00.png +0 -0
- package/assets/jellyfish/light-01.png +0 -0
- package/assets/jellyfish/light-02.png +0 -0
- package/assets/jellyfish/light-03.png +0 -0
- package/assets/jellyfish/light-04.png +0 -0
- package/assets/jellyfish/light-05.png +0 -0
- package/assets/jellyfish/light-06.png +0 -0
- package/assets/jellyfish/light-07.png +0 -0
- package/assets/jellyfish/light-08.png +0 -0
- package/assets/jellyfish/light-09.png +0 -0
- package/assets/jellyfish/light-10.png +0 -0
- package/assets/jellyfish/light-11.png +0 -0
- package/dist/brand.js +7 -7
- package/dist/cli-options.js +12 -6
- package/dist/config-edit.js +1 -0
- package/dist/config.js +22 -2
- package/dist/cultivate-cli.js +16 -23
- package/dist/extension.js +128 -108
- package/dist/init-config.js +5 -0
- package/dist/jellyfish.js +130 -0
- package/dist/loop-cli.js +16 -8
- package/dist/loop-tools.js +60 -21
- package/dist/module-report.js +5 -4
- package/dist/pi.js +76 -19
- package/dist/picture-row.js +3 -2
- package/dist/regenerate/activity.js +21 -0
- package/dist/regenerate/jobs.js +10 -5
- package/dist/regenerate/list-arguments.js +121 -0
- package/dist/regenerate/loop.js +66 -43
- package/dist/regenerate/panel-dock.js +7 -5
- package/dist/regenerate/plain-palette.js +52 -37
- package/dist/regenerate/regenerate.js +4 -2
- package/dist/regenerate/self-check.js +13 -6
- package/dist/regenerate/side-panel.js +1 -1
- package/dist/regenerate/spec-map-view.js +9 -5
- package/dist/regenerate/test-scripts.js +79 -13
- package/dist/regenerate/unit-review.js +1 -1
- package/dist/regenerate-cli.js +11 -5
- package/dist/release-cli.js +3 -8
- package/dist/review/live.js +2 -1
- package/dist/run-report.js +26 -1
- package/dist/screen-padding.js +72 -0
- package/dist/stacks.js +3 -5
- package/dist/validate-cli.js +3 -8
- package/docs/README.md +5 -12
- package/docs/authoring-plain.md +37 -119
- package/docs/config.md +55 -178
- package/docs/deploy.md +21 -80
- package/docs/development.md +14 -41
- package/docs/headless.md +40 -129
- package/docs/images/how-turri-works.svg +29 -31
- package/docs/images/turri-banner.png +0 -0
- package/docs/images/turri-screenshot.png +0 -0
- package/docs/installation.md +13 -40
- package/docs/parser.md +15 -43
- package/docs/regenerating.md +231 -942
- package/docs/sandbox.md +38 -152
- package/docs/skills-and-gates.md +50 -173
- package/docs/theming.md +45 -46
- package/docs/using.md +68 -344
- package/genome/rules/bullet-continuation.md +5 -13
- package/genome/rules/concise-specs-examples.md +2 -4
- package/genome/rules/concise-specs-integration-examples.md +2 -6
- package/genome/rules/concise-specs.md +1 -2
- package/genome/rules/func-specs.md +1 -0
- package/genome/rules/integration-embedded-testing.md +1 -4
- package/genome/rules/integrations.md +1 -2
- package/genome/rules/project-model.md +14 -35
- package/genome/rules/stdlib.md +14 -49
- package/genome/rules/turri-regeneration.md +48 -0
- package/genome/skills/add-implementation-requirement/SKILL.md +1 -2
- package/genome/skills/analyze-if-func-spec-too-complex/SKILL.md +3 -8
- package/genome/skills/brood/SKILL.md +37 -127
- package/genome/skills/brood/references/intent.md +4 -12
- package/genome/skills/brood/references/product.md +24 -76
- package/genome/skills/brood/references/stack.md +7 -29
- package/genome/skills/brood/references/testing.md +11 -42
- package/genome/skills/bud/SKILL.md +32 -109
- package/genome/skills/bud/references/intent.md +4 -11
- package/genome/skills/implement-conformance-testing-script/SKILL.md +4 -7
- package/genome/skills/implement-unit-testing-script/SKILL.md +2 -3
- package/genome/skills/init-plain-project/SKILL.md +1 -1
- package/genome/skills/mature/SKILL.md +21 -69
- package/genome/skills/refactor-module/SKILL.md +24 -2
- package/genome/skills/spawn/SKILL.md +28 -113
- package/package.json +1 -1
- package/assets/jellyfish/dark-12.png +0 -0
- package/assets/jellyfish/dark-13.png +0 -0
- package/assets/jellyfish/dark-14.png +0 -0
- package/assets/jellyfish/fallback.txt +0 -252
- package/assets/jellyfish/light-12.png +0 -0
- package/assets/jellyfish/light-13.png +0 -0
- package/assets/jellyfish/light-14.png +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/brand.js
CHANGED
|
@@ -2,8 +2,8 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
/**
|
|
5
|
-
* turri's colours for what its command line prints - the
|
|
6
|
-
*
|
|
5
|
+
* turri's colours for what its command line prints - the design system's, as its themes use them (.pi/themes): on a
|
|
6
|
+
* dark terminal neon and pink, on a light one blue whale and eggplant. Nothing is coloured when the output is not a terminal, or NO_COLOR is set;
|
|
7
7
|
* a terminal without 24-bit colour gets the nearest of its sixteen.
|
|
8
8
|
*/
|
|
9
9
|
/** Whether the terminal is light: COLORFGBG says so (a light background, 7 or 15), or pi's theme is a light one. */
|
|
@@ -35,15 +35,15 @@ function paint(stream = process.stdout) {
|
|
|
35
35
|
};
|
|
36
36
|
return {
|
|
37
37
|
/** turri's accent: what to look at - a heading, a choice's number, the command to run. */
|
|
38
|
-
accent: light ? ink("#
|
|
38
|
+
accent: light ? ink("#1e3442", 34) : ink("#5ce1e6", 96),
|
|
39
39
|
/** Done, and what was written. */
|
|
40
|
-
success: light ? ink("#
|
|
40
|
+
success: light ? ink("#732553", 35) : ink("#ff5c8d", 95),
|
|
41
41
|
/** What is wrong: what stops turri. */
|
|
42
|
-
error:
|
|
42
|
+
error: ink("#ff7a3d", 33),
|
|
43
43
|
/** What works, with limits. */
|
|
44
|
-
warning: ink("#
|
|
44
|
+
warning: ink("#ff7a3d", 33),
|
|
45
45
|
/** What goes with it, and can be skimmed. */
|
|
46
|
-
muted: ink("#
|
|
46
|
+
muted: light ? ink("#3e5a6b", 90) : ink("#85a3b2", 37),
|
|
47
47
|
bold: (text) => (on ? `\x1b[1m${text}\x1b[22m` : text),
|
|
48
48
|
};
|
|
49
49
|
}
|
package/dist/cli-options.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { conformanceOption, overrideConfig,
|
|
1
|
+
import { booleanOption, conformanceOption, overrideConfig, splitModelSpec } from "./config.js";
|
|
2
2
|
/**
|
|
3
3
|
* The command line of turri's commands: their words, and the options that stand for the config's own keys - kept apart
|
|
4
4
|
* from the commands that use them, so a light one (doctor) reads them as a regenerate does without loading it.
|
|
@@ -51,19 +51,24 @@ export function optionValue(options, name) {
|
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* The options that stand for the config's own keys - `--harness-model`, `--coding-model` (`--model`), `--judge-model`
|
|
54
|
-
* and, as asked, `--review` and `--conformance-tests` - read as if config.turri.yaml had them, in their place in the
|
|
54
|
+
* and, as asked, `--review`, `--mutation-testing` and `--conformance-tests` - read as if config.turri.yaml had them, in their place in the
|
|
55
55
|
* order the config resolves them in (overrideConfig): a run, or doctor, with them is the same as one with them in the
|
|
56
56
|
* file. Exits on a value it cannot read.
|
|
57
57
|
*/
|
|
58
58
|
export function configKeyOptions(options, take = {}) {
|
|
59
|
+
const flag = (name) => {
|
|
60
|
+
const given = take.review === true ? optionValue(options, name) : undefined;
|
|
61
|
+
const value = given !== undefined ? booleanOption(given) : undefined;
|
|
62
|
+
if (given !== undefined && value === undefined)
|
|
63
|
+
failOption(`--${name} takes true or false, not ${given}`);
|
|
64
|
+
return value;
|
|
65
|
+
};
|
|
59
66
|
const tests = take.conformance === true ? optionValue(options, "conformance-tests") : undefined;
|
|
60
67
|
const setting = tests !== undefined ? conformanceOption(tests) : undefined;
|
|
61
68
|
if (tests !== undefined && setting === undefined)
|
|
62
69
|
failOption(`--conformance-tests takes all, chosen, module or false, not ${tests}`);
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
if (reviewed !== undefined && review === undefined)
|
|
66
|
-
failOption(`--review takes true or false, not ${reviewed}`);
|
|
70
|
+
const review = flag("review");
|
|
71
|
+
const mutationTesting = flag("mutation-testing");
|
|
67
72
|
const model = (name, key) => {
|
|
68
73
|
const spec = optionValue(options, name);
|
|
69
74
|
return spec !== undefined ? { [key]: splitModelSpec(spec) } : {};
|
|
@@ -74,6 +79,7 @@ export function configKeyOptions(options, take = {}) {
|
|
|
74
79
|
...model("judge-model", "judgeModel"),
|
|
75
80
|
...(setting !== undefined ? { conformanceTests: setting } : {}),
|
|
76
81
|
...(review !== undefined ? { review } : {}),
|
|
82
|
+
...(mutationTesting !== undefined ? { mutationTesting } : {}),
|
|
77
83
|
});
|
|
78
84
|
}
|
|
79
85
|
function failOption(message) {
|
package/dist/config-edit.js
CHANGED
|
@@ -59,6 +59,7 @@ export function settings(rules = []) {
|
|
|
59
59
|
{ key: "conformance-tests", about: "which functional specs get conformance tests", kind: "choice", options: ["all", "chosen", "module", "false"], unset: "all, where a module has test reqs", effect: "next-plan" },
|
|
60
60
|
{ key: "conformance-budget", about: "under conformance-tests: chosen or module, how many may be chosen - a count or a share", kind: "budget", options: ["10%", "30%", "50%", "5", "10"], unset: "no limit", effect: "next-plan" },
|
|
61
61
|
{ key: "review", about: "whether the code and its tests are reviewed once they pass", kind: "boolean", unset: "true", effect: "next-pulse" },
|
|
62
|
+
{ key: "mutation-testing", about: "whether the coding agent and the reviewer can put a bug in the code for one test run (can_it_fail)", kind: "boolean", unset: "true", effect: "next-pulse" },
|
|
62
63
|
{ key: "allow-to-stop-jobs", about: "whether turri stops a running pulse the spec has moved on from", kind: "boolean", unset: "true", effect: "now" },
|
|
63
64
|
{ key: "release-after-pulse", about: "whether every pulse that lands code releases it", kind: "boolean", unset: "false", effect: "next-landing" },
|
|
64
65
|
{ key: "unittests-script", about: "the unit testing script, by path from the project root", kind: "path", unset: "the one run_unittests*.sh found", effect: "next-test-run" },
|
package/dist/config.js
CHANGED
|
@@ -21,6 +21,7 @@ import { projectDirOf } from "./regenerate/paths.js";
|
|
|
21
21
|
* conformance-tests: chosen # all, chosen (those with acceptance tests, and those the chooser picks after each pulse), module (the chooser picks, and plans, once the module is regenerated) or false (default: false)
|
|
22
22
|
* conformance-budget: 30% # under chosen or module: how many functional specs a module may have chosen, beside those with acceptance tests - a count or a share (default: no limit)
|
|
23
23
|
* review: false # review the code and its tests once they pass (default: true)
|
|
24
|
+
* mutation-testing: false # give the coding agent and the reviewer can_it_fail (default: true)
|
|
24
25
|
* allow-to-stop-jobs: false # stop a running job the spec moved on from (default: true)
|
|
25
26
|
* release-after-pulse: true # release what each pulse lands, as its last stage, before the next pulse (default: false)
|
|
26
27
|
* rules-off: [integrations] # ***plain rules to leave out of the prompts, by name (default: none)
|
|
@@ -156,6 +157,23 @@ export function chooseStack(stack) {
|
|
|
156
157
|
export function withStack(stack, work) {
|
|
157
158
|
return pinnedStack.run({ stack }, work);
|
|
158
159
|
}
|
|
160
|
+
/** The words that choose no stack: the project's config alone. */
|
|
161
|
+
export const NO_STACK = ["none", "off", "base", "-"];
|
|
162
|
+
/**
|
|
163
|
+
* The stack `name` names for the project at `root` - undefined for none - refused when the project has no file for
|
|
164
|
+
* it, with the stacks it has.
|
|
165
|
+
*/
|
|
166
|
+
export function stackNamed(root, name) {
|
|
167
|
+
const wanted = name.trim();
|
|
168
|
+
if (wanted === "" || NO_STACK.includes(wanted.toLowerCase()))
|
|
169
|
+
return undefined;
|
|
170
|
+
const stacks = stacksOf(root);
|
|
171
|
+
if (stacks.includes(wanted))
|
|
172
|
+
return wanted;
|
|
173
|
+
throw new Error(stacks.length > 0
|
|
174
|
+
? `There is no stack ${wanted} in ${root}. Its stacks: ${stacks.join(", ")} - or none.`
|
|
175
|
+
: `${root} has no stacks: a stack is a ${stackConfigFile("<stack>")} beside config.turri.yaml.`);
|
|
176
|
+
}
|
|
159
177
|
/** The stack a project folder is built with now: the one chosen, when the folder has a file for it. */
|
|
160
178
|
export function stackOf(root) {
|
|
161
179
|
const stack = chosenStack();
|
|
@@ -331,6 +349,7 @@ export const CONFIG_KEYS = {
|
|
|
331
349
|
conformanceTests: "conformance-tests",
|
|
332
350
|
conformanceBudget: "conformance-budget",
|
|
333
351
|
review: "review",
|
|
352
|
+
mutationTesting: "mutation-testing",
|
|
334
353
|
allowToStopJobs: "allow-to-stop-jobs",
|
|
335
354
|
releaseAfterPulse: "release-after-pulse",
|
|
336
355
|
unittestsScript: "unittests-script",
|
|
@@ -416,8 +435,8 @@ export function overrideConfig(overrides) {
|
|
|
416
435
|
export function conformanceOption(value) {
|
|
417
436
|
return conformanceSetting(value);
|
|
418
437
|
}
|
|
419
|
-
/** `--review <value>`: the config's own, true or false. Undefined for anything else. */
|
|
420
|
-
export function
|
|
438
|
+
/** `--review <value>`, `--mutation-testing <value>`: the config's own, true or false. Undefined for anything else. */
|
|
439
|
+
export function booleanOption(value) {
|
|
421
440
|
const word = value.trim().toLowerCase();
|
|
422
441
|
return word === "true" ? true : word === "false" ? false : undefined;
|
|
423
442
|
}
|
|
@@ -487,6 +506,7 @@ function readConfigFile(path) {
|
|
|
487
506
|
conformanceTests: conformanceSetting(conformance),
|
|
488
507
|
conformanceBudget: budgetSetting(raw[CONFIG_KEYS.conformanceBudget]),
|
|
489
508
|
review: typeof raw[CONFIG_KEYS.review] === "boolean" ? raw[CONFIG_KEYS.review] : undefined,
|
|
509
|
+
mutationTesting: typeof raw[CONFIG_KEYS.mutationTesting] === "boolean" ? raw[CONFIG_KEYS.mutationTesting] : undefined,
|
|
490
510
|
allowToStopJobs: typeof raw[CONFIG_KEYS.allowToStopJobs] === "boolean" ? raw[CONFIG_KEYS.allowToStopJobs] : undefined,
|
|
491
511
|
releaseAfterPulse: typeof raw[CONFIG_KEYS.releaseAfterPulse] === "boolean" ? raw[CONFIG_KEYS.releaseAfterPulse] : undefined,
|
|
492
512
|
unittestsScript: text(raw[CONFIG_KEYS.unittestsScript]),
|
package/dist/cultivate-cli.js
CHANGED
|
@@ -7,40 +7,33 @@ import { skillRunDir } from "./regenerate/paths.js";
|
|
|
7
7
|
import { loadSkill } from "./skill.js";
|
|
8
8
|
import { discoverSkills } from "./skills.js";
|
|
9
9
|
import { PACKAGE_ROOT } from "./environment.js";
|
|
10
|
-
|
|
11
|
-
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
12
|
-
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
13
|
-
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
14
|
-
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
15
|
-
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
16
|
-
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
17
|
-
};
|
|
10
|
+
import { c } from "./module-report.js";
|
|
18
11
|
const reporter = {
|
|
19
|
-
step: (name, attempt) => console.log(`\n${c.bold(c.
|
|
12
|
+
step: (name, attempt) => console.log(`\n${c.bold(c.accent(`> step ${name}`))} ${c.dim(`attempt ${attempt}`)}`),
|
|
20
13
|
artifact: (path, body) => {
|
|
21
14
|
console.log(c.dim(` ${path}:`));
|
|
22
15
|
for (const line of body.split("\n"))
|
|
23
16
|
console.log(c.dim(` | ${line}`));
|
|
24
17
|
},
|
|
25
18
|
gate: (name, judges) => console.log(judges === 0
|
|
26
|
-
? `${c.bold(c.
|
|
27
|
-
: `${c.bold(c.
|
|
19
|
+
? `${c.bold(c.warning(`> gate ${name}`))} ${c.dim("no artifact - failing without spending judges")}`
|
|
20
|
+
: `${c.bold(c.warning(`> gate ${name}`))} ${c.dim(`${judges} independent judges`)}`),
|
|
28
21
|
livePass: (step, scope, findings) => console.log(findings === 0
|
|
29
22
|
? c.dim(` review ${step}: ${scope} - clean`)
|
|
30
|
-
: ` ${c.
|
|
23
|
+
: ` ${c.warning(`review ${step}`)}: ${scope} ${c.dim(`- ${findings} to fix`)}`),
|
|
31
24
|
liveReview: (name, passes, findings) => console.log(findings === 0
|
|
32
|
-
? `${c.bold(c.
|
|
33
|
-
: `${c.bold(c.
|
|
25
|
+
? `${c.bold(c.warning(`> gate ${name}`))} ${c.dim(`reviewer read along in ${passes} passes, nothing outstanding`)}`
|
|
26
|
+
: `${c.bold(c.warning(`> gate ${name}`))} ${c.dim(`${findings} unresolved from the reviewer - failing without spending judges`)}`),
|
|
34
27
|
judge: (index, model, verdict, violations) => {
|
|
35
|
-
const mark = verdict === "pass" ? c.
|
|
28
|
+
const mark = verdict === "pass" ? c.success("pass") : c.error(verdict);
|
|
36
29
|
console.log(` judge ${index + 1} ${c.dim(`(${model})`)}: ${mark}${violations ? c.dim(` (${violations})`) : ""}`);
|
|
37
30
|
},
|
|
38
31
|
outcome: (name, decision, critique) => {
|
|
39
32
|
const colour = {
|
|
40
|
-
advance: c.
|
|
41
|
-
retry: c.
|
|
42
|
-
escalate: c.
|
|
43
|
-
abort: c.
|
|
33
|
+
advance: c.success,
|
|
34
|
+
retry: c.warning,
|
|
35
|
+
escalate: c.warning,
|
|
36
|
+
abort: c.error,
|
|
44
37
|
};
|
|
45
38
|
console.log(` ${c.bold(colour[decision](decision))} ${c.dim(`<- gate ${name}`)}`);
|
|
46
39
|
if (critique)
|
|
@@ -78,7 +71,7 @@ async function main() {
|
|
|
78
71
|
select: async (title, options, context) => {
|
|
79
72
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
80
73
|
const list = options.map((option, i) => ` ${c.bold(String(i + 1))}. ${option}`).join("\n");
|
|
81
|
-
const reply = (await rl.question(`\n${context !== undefined ? `${context}\n\n` : ""}${c.bold(c.
|
|
74
|
+
const reply = (await rl.question(`\n${context !== undefined ? `${context}\n\n` : ""}${c.bold(c.accent("? "))}${title}\n${list}\n> `)).trim();
|
|
82
75
|
rl.close();
|
|
83
76
|
const index = Number.parseInt(reply, 10);
|
|
84
77
|
if (Number.isInteger(index) && index >= 1 && index <= options.length)
|
|
@@ -87,13 +80,13 @@ async function main() {
|
|
|
87
80
|
},
|
|
88
81
|
input: async (title, context) => {
|
|
89
82
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
90
|
-
const reply = (await rl.question(`\n${context !== undefined ? `${context}\n\n` : ""}${c.bold(c.
|
|
83
|
+
const reply = (await rl.question(`\n${context !== undefined ? `${context}\n\n` : ""}${c.bold(c.accent("? "))}${title}\n> `)).trim();
|
|
91
84
|
rl.close();
|
|
92
85
|
return reply === "" ? undefined : reply;
|
|
93
86
|
},
|
|
94
87
|
}),
|
|
95
88
|
escalate: async ({ question, critique }) => {
|
|
96
|
-
console.log(`\n${c.bold(c.
|
|
89
|
+
console.log(`\n${c.bold(c.warning("! human review required"))}`);
|
|
97
90
|
console.log(c.dim(critique));
|
|
98
91
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
99
92
|
const answer = (await rl.question(`${question} [y/N] `)).trim().toLowerCase();
|
|
@@ -101,7 +94,7 @@ async function main() {
|
|
|
101
94
|
return answer === "y";
|
|
102
95
|
},
|
|
103
96
|
});
|
|
104
|
-
console.log(`\n${result.ok ? c.
|
|
97
|
+
console.log(`\n${result.ok ? c.success(c.bold("run complete")) : c.error(c.bold("run aborted at a gate"))} ${c.dim(`artifacts in ${relative(cwd, skillRunDir(cwd, skill.name))}`)}`);
|
|
105
98
|
process.exit(result.ok ? 0 : 1);
|
|
106
99
|
}
|
|
107
100
|
main().catch((err) => {
|