@turri-dev/cli 0.1.27-next.763d091 → 0.1.28-next.d5f2680
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 +6 -6
- package/dist/advisor-chat.d.ts +63 -0
- package/dist/advisor-chat.js +290 -0
- package/dist/background-skills.d.ts +53 -0
- package/dist/background-skills.js +121 -0
- package/dist/bedrock-region.d.ts +66 -0
- package/dist/bedrock-region.js +144 -0
- package/dist/btw.d.ts +1 -1
- package/dist/btw.js +1 -1
- package/dist/change-plain-cli.js +20 -13
- package/dist/chat-idle.d.ts +7 -0
- package/dist/chat-idle.js +20 -0
- package/dist/chats.d.ts +10 -0
- package/dist/chats.js +23 -0
- package/dist/cli-options.d.ts +1 -2
- package/dist/cli-options.js +2 -7
- package/dist/cli.js +11 -3
- package/dist/config-command.d.ts +14 -0
- package/dist/config-command.js +35 -15
- package/dist/config-edit.d.ts +3 -3
- package/dist/config-edit.js +16 -12
- package/dist/config.d.ts +54 -34
- package/dist/config.js +40 -44
- package/dist/conform-cli.js +12 -10
- package/dist/conformance-switch.d.ts +34 -0
- package/dist/conformance-switch.js +108 -0
- package/dist/cultivate-cli.js +15 -5
- package/dist/drift-cli.js +2 -0
- package/dist/extension.d.ts +15 -0
- package/dist/extension.js +260 -83
- package/dist/host-session.d.ts +26 -0
- package/dist/host-session.js +30 -2
- package/dist/host.d.ts +5 -2
- package/dist/host.js +7 -1
- package/dist/init-config.d.ts +1 -1
- package/dist/init-config.js +20 -11
- package/dist/loop-cli.d.ts +3 -3
- package/dist/loop-cli.js +13 -16
- package/dist/loop-tools.js +18 -10
- package/dist/model-check.d.ts +8 -3
- package/dist/model-check.js +13 -8
- package/dist/new-project.js +12 -8
- package/dist/{validate-cli.js → parse-cli.js} +11 -5
- package/dist/parse-tool.d.ts +9 -0
- package/dist/parse-tool.js +33 -0
- package/dist/pi-launch.js +1 -1
- package/dist/plain/check.d.ts +53 -6
- package/dist/plain/check.js +217 -24
- package/dist/plain/concepts.d.ts +8 -0
- package/dist/plain/concepts.js +66 -23
- package/dist/plain/cycles.d.ts +21 -0
- package/dist/plain/cycles.js +190 -0
- package/dist/plain/errors.d.ts +4 -0
- package/dist/plain/errors.js +7 -0
- package/dist/plain/files.d.ts +18 -1
- package/dist/plain/files.js +39 -2
- package/dist/plain/graph.d.ts +33 -4
- package/dist/plain/graph.js +60 -12
- package/dist/plain/highlight.d.ts +3 -2
- package/dist/plain/highlight.js +4 -3
- package/dist/plain/index.d.ts +8 -5
- package/dist/plain/index.js +8 -5
- package/dist/plain/liquid.js +1 -6
- package/dist/plain/markdown.d.ts +32 -3
- package/dist/plain/markdown.js +146 -24
- package/dist/plain/module-concepts.js +2 -2
- package/dist/plain/parser.d.ts +83 -28
- package/dist/plain/parser.js +381 -260
- package/dist/plain/plain.tmLanguage.json +29 -3
- package/dist/plain/project.d.ts +48 -0
- package/dist/plain/project.js +155 -0
- package/dist/plain/spec.d.ts +90 -28
- package/dist/plain/spec.js +107 -133
- package/dist/plain/test-plain-file.d.ts +59 -0
- package/dist/plain/test-plain-file.js +232 -0
- package/dist/regenerate/activity.d.ts +20 -9
- package/dist/regenerate/activity.js +11 -4
- package/dist/regenerate/advisor-records.d.ts +93 -0
- package/dist/regenerate/advisor-records.js +110 -0
- package/dist/regenerate/advisor-runs.d.ts +87 -0
- package/dist/regenerate/advisor-runs.js +327 -0
- package/dist/regenerate/base-folder.d.ts +68 -0
- package/dist/regenerate/base-folder.js +168 -0
- package/dist/regenerate/cases.d.ts +9 -6
- package/dist/regenerate/cases.js +71 -15
- package/dist/regenerate/change-view.d.ts +8 -3
- package/dist/regenerate/change-view.js +71 -22
- package/dist/regenerate/chunk-plan.d.ts +36 -15
- package/dist/regenerate/chunk-plan.js +165 -59
- package/dist/regenerate/conform.d.ts +3 -0
- package/dist/regenerate/conform.js +6 -5
- package/dist/regenerate/conformance-agents.d.ts +18 -39
- package/dist/regenerate/conformance-agents.js +60 -88
- package/dist/regenerate/conformance.d.ts +23 -20
- package/dist/regenerate/conformance.js +118 -121
- package/dist/regenerate/diff-view.d.ts +1 -1
- package/dist/regenerate/diff-view.js +20 -4
- package/dist/regenerate/drift.d.ts +47 -15
- package/dist/regenerate/drift.js +101 -30
- package/dist/regenerate/jobs.d.ts +2 -2
- package/dist/regenerate/jobs.js +1 -1
- package/dist/regenerate/ledger.d.ts +19 -0
- package/dist/regenerate/loop.d.ts +61 -49
- package/dist/regenerate/loop.js +473 -340
- package/dist/regenerate/paths.d.ts +4 -0
- package/dist/regenerate/paths.js +7 -1
- package/dist/regenerate/plain-palette.js +2 -0
- package/dist/regenerate/prompt.d.ts +57 -19
- package/dist/regenerate/prompt.js +157 -122
- package/dist/regenerate/records.d.ts +43 -12
- package/dist/regenerate/records.js +102 -88
- package/dist/regenerate/regenerate.js +29 -11
- package/dist/regenerate/regression.d.ts +4 -4
- package/dist/regenerate/regression.js +14 -10
- package/dist/regenerate/renames.d.ts +42 -0
- package/dist/regenerate/renames.js +185 -0
- package/dist/regenerate/requires.d.ts +1 -1
- package/dist/regenerate/requires.js +10 -3
- package/dist/regenerate/resources.d.ts +5 -3
- package/dist/regenerate/resources.js +4 -4
- package/dist/regenerate/scripts-being-written.d.ts +19 -0
- package/dist/regenerate/scripts-being-written.js +30 -0
- package/dist/regenerate/spec-map-view.d.ts +2 -2
- package/dist/regenerate/spec-map-view.js +46 -15
- package/dist/regenerate/spec-map.d.ts +27 -5
- package/dist/regenerate/spec-map.js +92 -60
- package/dist/regenerate/spec-steps.d.ts +48 -5
- package/dist/regenerate/spec-steps.js +213 -28
- package/dist/regenerate/state.d.ts +4 -3
- package/dist/regenerate/stop-report.d.ts +1 -1
- package/dist/regenerate/stop-report.js +10 -10
- package/dist/regenerate/test-advisor.d.ts +101 -0
- package/dist/regenerate/test-advisor.js +379 -0
- package/dist/regenerate/test-kinds.d.ts +69 -31
- package/dist/regenerate/test-kinds.js +109 -80
- package/dist/regenerate/test-scripts.d.ts +2 -0
- package/dist/regenerate/test-scripts.js +4 -0
- package/dist/regenerate/testing-steps.d.ts +41 -0
- package/dist/regenerate/testing-steps.js +110 -0
- package/dist/regenerate/testing.d.ts +1 -1
- package/dist/regenerate/testing.js +5 -4
- package/dist/regenerate/unit-review.d.ts +1 -1
- package/dist/regenerate/unit-review.js +3 -3
- package/dist/regenerate-cli.js +11 -12
- package/dist/release-agent.js +4 -0
- package/dist/release-cli.js +2 -0
- package/dist/release-tools.js +2 -0
- package/dist/run-report.d.ts +2 -2
- package/dist/run-report.js +4 -3
- package/dist/self-invocation.d.ts +1 -1
- package/dist/self-invocation.js +1 -1
- package/dist/sentinel.js +1 -1
- package/dist/setup.d.ts +10 -0
- package/dist/setup.js +52 -4
- package/dist/skill.d.ts +10 -0
- package/dist/skill.js +6 -0
- package/dist/skills.js +3 -1
- package/dist/spec-tools.js +21 -31
- package/dist/test-plain-files.d.ts +41 -0
- package/dist/test-plain-files.js +177 -0
- package/docs/README.md +3 -1
- package/docs/authoring-plain.md +46 -13
- package/docs/chats.md +3 -1
- package/docs/config.md +20 -15
- package/docs/deploy.md +1 -1
- package/docs/development.md +2 -1
- package/docs/headless.md +15 -18
- package/docs/hosting.md +28 -12
- package/docs/installation.md +1 -0
- package/docs/parser.md +88 -27
- package/docs/regenerating.md +89 -54
- package/docs/sentinel.md +1 -1
- package/docs/skills-and-gates.md +3 -1
- package/docs/test-advisor.md +110 -0
- package/docs/theming.md +1 -1
- package/docs/using.md +17 -15
- package/genome/rules/bullet-continuation.md +8 -8
- package/genome/rules/concise-specs-examples.md +111 -80
- package/genome/rules/concise-specs-integration-examples.md +56 -41
- package/genome/rules/concise-specs.md +17 -16
- package/genome/rules/definitions.md +5 -5
- package/genome/rules/examples/integration-embedded-testing/run_conformance_tests_java.sh +1 -1
- package/genome/rules/func-specs.md +69 -74
- package/genome/rules/impl-reqs.md +4 -4
- package/genome/rules/integration-embedded-testing.md +20 -20
- package/genome/rules/integration-embedded.md +31 -31
- package/genome/rules/integration-standalone.md +9 -9
- package/genome/rules/integrations.md +18 -18
- package/genome/rules/linked-resources.md +19 -16
- package/genome/rules/module-structure.md +21 -14
- package/genome/rules/project-model.md +18 -17
- package/genome/rules/required-concepts.md +1 -1
- package/genome/rules/requires-modules.md +6 -5
- package/genome/rules/semantic-ids.md +49 -0
- package/genome/rules/stdlib.md +4 -5
- package/genome/rules/test-plain-files.md +130 -0
- package/genome/rules/test-reqs.md +8 -8
- package/genome/rules/turri-regeneration.md +24 -8
- package/genome/skills/analyze-2-func-specs/SKILL.md +36 -21
- package/genome/skills/analyze-func-specs/SKILL.md +46 -32
- package/genome/skills/break-down-func-spec/SKILL.md +91 -67
- package/genome/skills/consolidate-concepts/SKILL.md +10 -10
- package/genome/skills/convert-old-syntax/SKILL.md +68 -0
- package/genome/skills/convert-old-syntax/references/rubric.md +11 -0
- package/genome/skills/debug-specs/SKILL.md +62 -56
- package/genome/skills/implement-conformance-testing-script/SKILL.md +19 -15
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_java.sh +2 -2
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.sh +2 -2
- package/genome/skills/implement-unit-testing-script/SKILL.md +14 -10
- package/genome/skills/refactor-module/SKILL.md +35 -35
- package/genome/skills/resolve-section-ownership/SKILL.md +9 -9
- package/genome/skills/resolve-spec-conflict/SKILL.md +35 -29
- package/package.json +7 -6
- package/dist/regenerate/choose-conformance.d.ts +0 -32
- package/dist/regenerate/choose-conformance.js +0 -117
- /package/dist/{validate-cli.d.ts → parse-cli.d.ts} +0 -0
|
@@ -1,38 +1,32 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { ACCEPTANCE_TESTS, CONFORMANCE_TESTS, FUNCTIONAL_REQUIREMENTS, REQUIRED_FUNCTIONAL_SPECS } from "../plain/spec.js";
|
|
3
4
|
import { listFiles } from "./testing.js";
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* Conformance tests are written from the functional spec itself: what the functionality does,
|
|
8
|
-
* its edge cases, its errors - as if no acceptance tests had been given. Acceptance tests are the
|
|
9
|
-
* ones the spec's author wrote, each implemented as a test of its own, doing what it says. The
|
|
10
|
-
* two are kept apart even when the acceptance tests would seem to cover the functionality: tests
|
|
11
|
-
* derived only from the acceptance tests check what the author thought to check, and nothing
|
|
12
|
-
* the spec says beyond it.
|
|
13
|
-
*
|
|
14
|
-
* Each kind is labelled where anyone reading the tests can see it - in its file names, and in a
|
|
15
|
-
* comment the harness looks for - so which test is which, and which acceptance test a test
|
|
16
|
-
* implements, never has to be guessed.
|
|
17
|
-
*/
|
|
18
|
-
export function conformanceLabel(frid) {
|
|
19
|
-
return `conformance tests of functionality ${frid}`;
|
|
5
|
+
/** The label of a file of a functional spec's conformance tests. */
|
|
6
|
+
export function conformanceLabel(module, funcId) {
|
|
7
|
+
return `conformance tests of ${module}:${funcId}`;
|
|
20
8
|
}
|
|
21
|
-
|
|
22
|
-
|
|
9
|
+
/** The label right above a test that carries out a scenario. */
|
|
10
|
+
export function scenarioLabel(kind, scenarioId, module, funcId) {
|
|
11
|
+
return `${kind} scenario ${scenarioId} of ${module}:${funcId}`;
|
|
23
12
|
}
|
|
24
|
-
/**
|
|
25
|
-
export function
|
|
26
|
-
return
|
|
13
|
+
/** Every scenario of a functional spec, each with its kind, in its file's order: the conformance ones, then the acceptance ones. */
|
|
14
|
+
export function scenariosOf(spec) {
|
|
15
|
+
return [...spec.conformance.map((scenario) => ({ ...scenario, kind: "conformance" })), ...spec.acceptance.map((scenario) => ({ ...scenario, kind: "acceptance" }))];
|
|
16
|
+
}
|
|
17
|
+
/** A scenario as the agents are given it: its label, and its text under it. */
|
|
18
|
+
export function scenarioText(module, funcId, scenario) {
|
|
19
|
+
return `${scenarioLabel(scenario.kind, scenario.id, module, funcId)}:\n${scenario.text.trim()}`;
|
|
27
20
|
}
|
|
28
21
|
function normalise(text) {
|
|
29
22
|
return text.toLowerCase().replace(/\s+/g, " ");
|
|
30
23
|
}
|
|
31
24
|
/**
|
|
32
|
-
* What is missing from the labelling of a
|
|
33
|
-
*
|
|
25
|
+
* What is missing from the labelling of a functional spec's tests: the file label, when no test file carries it, and each
|
|
26
|
+
* scenario no test is labelled as carrying out.
|
|
34
27
|
*/
|
|
35
|
-
export function missingTestLabels(testsDir,
|
|
28
|
+
export function missingTestLabels(testsDir, codebase, spec) {
|
|
29
|
+
const module = spec.module ?? codebase;
|
|
36
30
|
const text = normalise(listFiles(testsDir)
|
|
37
31
|
.map((file) => {
|
|
38
32
|
try {
|
|
@@ -43,74 +37,110 @@ export function missingTestLabels(testsDir, frid, acceptanceTests) {
|
|
|
43
37
|
}
|
|
44
38
|
})
|
|
45
39
|
.join("\n"));
|
|
46
|
-
//
|
|
47
|
-
const has = (label) => new RegExp(`${normalise(label).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![
|
|
40
|
+
// Matched on the whole key: the label of `todo:log-in` must not be found in that of `todo:log-in-2`.
|
|
41
|
+
const has = (label) => new RegExp(`${normalise(label).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![a-z0-9_]|-[a-z0-9])`).test(text);
|
|
48
42
|
const missing = [];
|
|
49
|
-
if (!has(conformanceLabel(
|
|
50
|
-
missing.push(`No test file is labelled "${conformanceLabel(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
43
|
+
if (!has(conformanceLabel(module, spec.funcId)))
|
|
44
|
+
missing.push(`No test file is labelled "${conformanceLabel(module, spec.funcId)}".`);
|
|
45
|
+
for (const scenario of scenariosOf(spec)) {
|
|
46
|
+
const label = scenarioLabel(scenario.kind, scenario.id, module, spec.funcId);
|
|
47
|
+
if (!has(label))
|
|
48
|
+
missing.push(`No test is labelled "${label}": ${scenario.text.trim().split("\n")[0]}`);
|
|
49
|
+
}
|
|
55
50
|
return missing;
|
|
56
51
|
}
|
|
57
52
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
53
|
+
* Sent back when the labels of functional specs are missing - one, or every one a spec change touches: every one that
|
|
54
|
+
* is missing, in one request, so none is asked for in place of another.
|
|
60
55
|
*/
|
|
61
|
-
export function
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
56
|
+
export function labelsPromptFor(codebase, items, empty = false) {
|
|
57
|
+
const pending = items.filter((item) => item.missing.length > 0);
|
|
58
|
+
const names = pending.map((item) => `${item.module ?? codebase}:${item.funcId}`);
|
|
59
|
+
const module = pending[0]?.module ?? codebase;
|
|
60
|
+
const which = names.length === 1 ? `functional spec ${names[0]}` : `functional specs ${names.slice(0, -1).join(", ")} and ${names.at(-1)}`;
|
|
61
|
+
const first = pending[0]?.funcId ?? "<func-id>";
|
|
62
|
+
return [
|
|
63
|
+
empty
|
|
64
|
+
? "There are no tests in your tests folder - nothing ran. Whatever a build folder or an earlier report there says, write " +
|
|
65
|
+
`the conformance tests of ${which} now, as files in your tests folder, labelled as below:`
|
|
66
|
+
: `The tests are not labelled yet as the scenarios they carry out. Missing, for ${which}:`,
|
|
67
|
+
"",
|
|
68
|
+
...pending.flatMap((item) => item.missing.map((line) => `- ${line}`)),
|
|
69
|
+
"",
|
|
70
|
+
`Every file of a functional spec's tests carries its label, in a comment of its own line: "${conformanceLabel(module, first)}". ` +
|
|
71
|
+
"A file that tests several functional specs has one such line for each.",
|
|
72
|
+
`Every scenario is carried out by at least one test, with a comment right above it: "${scenarioLabel("conformance", "<scenario-id>", module, first)}" or ` +
|
|
73
|
+
`"${scenarioLabel("acceptance", "<scenario-id>", module, first)}" - one not carried out yet is written as a test of its own.`,
|
|
74
|
+
"Add every label missing above. Keep every label and every test that is there already: adding one scenario's label never takes another's away. Then run the tests again.",
|
|
75
|
+
].join("\n");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The functional specs of a tree of what conformance tests are written for, with their scenarios - those of the modules
|
|
79
|
+
* below with their module, the codebase's own after them.
|
|
80
|
+
*/
|
|
81
|
+
export function testedSpecsOf(tree) {
|
|
82
|
+
const of = (section, below) => (tree?.[section] ?? []).map((item) => ({
|
|
83
|
+
funcId: item.id ?? "",
|
|
84
|
+
...(below && item.module !== undefined ? { module: item.module } : {}),
|
|
85
|
+
conformance: (item[CONFORMANCE_TESTS] ?? []).map(({ id, text }) => ({ id, text })),
|
|
86
|
+
acceptance: (item[ACCEPTANCE_TESTS] ?? []).map(({ id, text }) => ({ id, text })),
|
|
87
|
+
}));
|
|
88
|
+
return [...of(REQUIRED_FUNCTIONAL_SPECS, true), ...of(FUNCTIONAL_REQUIREMENTS, false)];
|
|
89
|
+
}
|
|
90
|
+
const ID = "[a-z0-9]+(?:-[a-z0-9]+)*";
|
|
91
|
+
const END = "(?![a-z0-9_]|-[a-z0-9])";
|
|
92
|
+
/**
|
|
93
|
+
* The labels in the tests folder that name nothing the tests are written for: a functional spec, or a scenario of one,
|
|
94
|
+
* that `specs` does not have - left over from a scenario or a functional spec taken out or renamed, mistyped, or
|
|
95
|
+
* invented. Each with the file it is in.
|
|
96
|
+
*/
|
|
97
|
+
export function orphanedLabels(testsDir, codebase, specs) {
|
|
98
|
+
const known = new Set();
|
|
99
|
+
for (const spec of specs) {
|
|
100
|
+
const module = spec.module ?? codebase;
|
|
101
|
+
known.add(conformanceLabel(module, spec.funcId).toLowerCase());
|
|
102
|
+
for (const scenario of scenariosOf(spec))
|
|
103
|
+
known.add(scenarioLabel(scenario.kind, scenario.id, module, spec.funcId).toLowerCase());
|
|
104
|
+
}
|
|
105
|
+
const pattern = new RegExp(`\\b(?:conformance tests of|(?:conformance|acceptance) scenario ${ID} of) [\\w./-]+:${ID}${END}`, "gi");
|
|
106
|
+
const found = [];
|
|
107
|
+
for (const file of listFiles(testsDir)) {
|
|
108
|
+
let text;
|
|
67
109
|
try {
|
|
68
|
-
|
|
110
|
+
text = readFileSync(join(testsDir, file), "utf8");
|
|
69
111
|
}
|
|
70
112
|
catch {
|
|
71
|
-
|
|
113
|
+
continue;
|
|
72
114
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return
|
|
115
|
+
for (const match of new Set(text.match(pattern) ?? []))
|
|
116
|
+
if (!known.has(match.replace(/\s+/g, " ").toLowerCase()))
|
|
117
|
+
found.push({ file, label: match });
|
|
118
|
+
}
|
|
119
|
+
return found;
|
|
78
120
|
}
|
|
79
|
-
/**
|
|
80
|
-
|
|
81
|
-
* missing, in one request, so none is asked for in place of another.
|
|
82
|
-
*/
|
|
83
|
-
export function labelsPromptFor(items, empty = false, hasAcceptance = true) {
|
|
84
|
-
const pending = items.filter((item) => item.missing.length > 0);
|
|
85
|
-
const frids = pending.map((item) => item.frid);
|
|
86
|
-
const which = frids.length === 1 ? `functionality ${frids[0]}` : `functionalities ${frids.slice(0, -1).join(", ")} and ${frids.at(-1)}`;
|
|
121
|
+
/** Sent back when labels name nothing the tests are written for. */
|
|
122
|
+
export function orphansPrompt(orphans) {
|
|
87
123
|
return [
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
`the conformance tests of ${which} now, as files in your tests folder, labelled as the kinds they are:`
|
|
91
|
-
: `The tests are not labelled yet as the kinds they must be kept apart as. Missing, for ${which}:`,
|
|
124
|
+
"These labels name no functional spec, or no scenario of one, that the tests are written for - a scenario or a functional spec",
|
|
125
|
+
"taken out or renamed, a label mistyped, or a test of your own beside the scenarios:",
|
|
92
126
|
"",
|
|
93
|
-
...
|
|
127
|
+
...orphans.map((orphan) => `- ${orphan.file}: "${orphan.label}"`),
|
|
94
128
|
"",
|
|
95
|
-
"
|
|
96
|
-
|
|
97
|
-
`A file that tests several functionalities has one such line for each. A label naming several at once - "conformance tests of functionality 1, 2 and 3" - counts for the first alone.`,
|
|
98
|
-
...(hasAcceptance
|
|
99
|
-
? [
|
|
100
|
-
`Every acceptance test is its own test, with a comment right above it: "${acceptanceLabel(frids[0] ?? "1", 1)}:" and the acceptance test's text - one not implemented yet is written as a test of its own, not folded into a conformance test.`,
|
|
101
|
-
]
|
|
102
|
-
: []),
|
|
103
|
-
"Add every label missing above. Keep every label and every test that is there already: adding one functionality's label never takes another's away. Then run the tests again.",
|
|
129
|
+
"Remove each such test and its label - or, where it carries out a scenario that is there under another label, relabel it with",
|
|
130
|
+
"that scenario's label. Every test carries out a scenario, labelled as it is now. Then run the tests again.",
|
|
104
131
|
].join("\n");
|
|
105
132
|
}
|
|
133
|
+
/** Sent back when the labels of one functional spec are missing. */
|
|
134
|
+
export function labelsPrompt(module, funcId, missing, empty = false) {
|
|
135
|
+
return labelsPromptFor(module, [{ funcId, missing }], empty);
|
|
136
|
+
}
|
|
106
137
|
/**
|
|
107
|
-
* Conformance
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* a label is misplaced all the same.
|
|
138
|
+
* Conformance tests in the code folder, where they do not belong: they live in the module's tests folder, which the
|
|
139
|
+
* harness runs as the conformance suite. Found by their label, or by a test file under a name or folder that says
|
|
140
|
+
* conformance or acceptance - one written without a label is misplaced all the same.
|
|
111
141
|
*/
|
|
112
142
|
export function misplacedConformanceTests(codeDir) {
|
|
113
|
-
const label = /conformance tests of
|
|
143
|
+
const label = /conformance tests of [a-z0-9_]+:[a-z0-9]|(conformance|acceptance) scenario \S+ of [a-z0-9_]+:[a-z0-9]/i;
|
|
114
144
|
return listFiles(codeDir).filter((file) => {
|
|
115
145
|
const parts = file.toLowerCase().split("/");
|
|
116
146
|
const named = parts.some((part) => /conformance|acceptance/.test(part)) && /test/.test(parts.at(-1));
|
|
@@ -128,15 +158,14 @@ export function misplacedConformanceTests(codeDir) {
|
|
|
128
158
|
/** Sent back when there are: to the agent writing the code, or to the one writing the tests. */
|
|
129
159
|
export function misplacedTestsPrompt(files, codeDir, testsDir) {
|
|
130
160
|
return [
|
|
131
|
-
`These are conformance
|
|
161
|
+
`These are conformance tests, in the code folder (${codeDir}), where they do not belong:`,
|
|
132
162
|
"",
|
|
133
163
|
...files.map((file) => `- ${file}`),
|
|
134
164
|
"",
|
|
135
165
|
testsDir === undefined
|
|
136
|
-
? "Conformance
|
|
137
|
-
"
|
|
138
|
-
: `Conformance
|
|
139
|
-
"
|
|
140
|
-
"the conformance suite.",
|
|
166
|
+
? "Conformance tests are not yours to write: the conformance step writes them, in the module's tests folder. " +
|
|
167
|
+
"Remove these from the code folder. Your tests are the unit tests - write them as unit tests."
|
|
168
|
+
: `Conformance tests live in your working directory, ${testsDir}, and nowhere else. Move these there - labelled as ` +
|
|
169
|
+
"they must be - and remove them from the code folder; the harness runs only that folder as the conformance suite.",
|
|
141
170
|
].join("\n");
|
|
142
171
|
}
|
|
@@ -84,6 +84,8 @@ export declare function staticChecksOnly(run: {
|
|
|
84
84
|
exitCode: number;
|
|
85
85
|
timedOut?: boolean;
|
|
86
86
|
}): boolean;
|
|
87
|
+
/** The kind of testing script a skill writes, by its name - one that writes none, undefined. */
|
|
88
|
+
export declare function scriptKindOfSkill(skill: string): ScriptKind | undefined;
|
|
87
89
|
/** Raised before a build starts when the script that would decide it is done is missing. */
|
|
88
90
|
export declare class MissingTestScriptError extends Error {
|
|
89
91
|
readonly moduleName: string;
|
|
@@ -181,6 +181,10 @@ const SKILL = {
|
|
|
181
181
|
unit: { skill: "implement-unit-testing-script", file: "run_unittests", what: "unit testing script" },
|
|
182
182
|
conformance: { skill: "implement-conformance-testing-script", file: "run_conformance_tests", what: "conformance testing script" },
|
|
183
183
|
};
|
|
184
|
+
/** The kind of testing script a skill writes, by its name - one that writes none, undefined. */
|
|
185
|
+
export function scriptKindOfSkill(skill) {
|
|
186
|
+
return Object.keys(SKILL).find((kind) => SKILL[kind].skill === skill);
|
|
187
|
+
}
|
|
184
188
|
/** Raised before a build starts when the script that would decide it is done is missing. */
|
|
185
189
|
export class MissingTestScriptError extends Error {
|
|
186
190
|
// Declared fields, not parameter properties: node's own type stripping cannot run those.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type MarshalledPlainSource } from "../plain/spec.ts";
|
|
2
|
+
/**
|
|
3
|
+
* The steps of adding tests to a module, and the one it skipped. Nothing about testing exists until the person asks
|
|
4
|
+
* for it; then, in order: its test reqs - the stack's conformance template, or its own - the conformance testing script
|
|
5
|
+
* for its stack, and the scenarios in its test plain files. A module whose functional specs have scenarios but which
|
|
6
|
+
* skipped one of the first two has its conformance pulses refused - by the work loop and by `conform` alike - and
|
|
7
|
+
* `parse` and the map say what is missing. A module with no scenarios skipped nothing: it is not tested.
|
|
8
|
+
*/
|
|
9
|
+
export interface TestingGap {
|
|
10
|
+
/** The first step skipped. */
|
|
11
|
+
step: "test reqs" | "conformance testing script";
|
|
12
|
+
/** What is missing, and the next step - a refusal's, a warning's and the loop's why. */
|
|
13
|
+
text: string;
|
|
14
|
+
/** In a few words, for the map's row. */
|
|
15
|
+
short: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The standard library's conformance template for a module's stack, `stdlib/<kind>/conformance/<stack>`, when the module
|
|
19
|
+
* imports a stack template that has one - nothing otherwise.
|
|
20
|
+
*/
|
|
21
|
+
export declare function conformanceTemplateOf(plainFile: string): string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* The conformance testing script a module's tests would run with, looked for under its stack - the one pinned, else
|
|
24
|
+
* the one `stackForModule` gives it - or nothing, with why when there is a why.
|
|
25
|
+
*/
|
|
26
|
+
export declare function conformanceScriptOf(plainFile: string, buildDir?: string): {
|
|
27
|
+
script?: string;
|
|
28
|
+
note?: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* The step of adding tests a module skipped, when its functional specs have scenarios: no test reqs - its own or
|
|
32
|
+
* imported - or no conformance testing script for its stack. `tree` is what its conformance tests would be written from:
|
|
33
|
+
* the codebase's spec, the module's own sections with its imports in, and the functional specs it inherits. The script
|
|
34
|
+
* is looked for under the module's stack - the one pinned, else the one `stackForModule` gives it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function testingGap(input: {
|
|
37
|
+
moduleName: string;
|
|
38
|
+
plainFile: string;
|
|
39
|
+
tree: MarshalledPlainSource;
|
|
40
|
+
buildDir?: string;
|
|
41
|
+
}): TestingGap | undefined;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { conformanceTestsOn } from "../conformance-switch.js";
|
|
4
|
+
import { stackForModule, stackPinned, withStack } from "../config.js";
|
|
5
|
+
import { IMPORT_DIRECTIVE, readPlainSourceMetadata } from "../plain/parser.js";
|
|
6
|
+
import { FUNCTIONAL_REQUIREMENTS, REQUIRED_FUNCTIONAL_SPECS, TEST_REQUIREMENTS } from "../plain/spec.js";
|
|
7
|
+
import { isStdlibModule, STDLIB_ROOT } from "../plain/stdlib.js";
|
|
8
|
+
import { projectDirOf, resolveTemplateDirs } from "./paths.js";
|
|
9
|
+
import { hasScenarios } from "./records.js";
|
|
10
|
+
import { findTestScripts, projectRootFor } from "./test-scripts.js";
|
|
11
|
+
/** The stack template a module imports, `stdlib/<kind>/<stack>`, through its imports - none, when it imports none. */
|
|
12
|
+
function stdlibStackOf(plainFile) {
|
|
13
|
+
const dirs = resolveTemplateDirs(resolve(plainFile));
|
|
14
|
+
const seen = new Set();
|
|
15
|
+
const visit = (path) => {
|
|
16
|
+
if (seen.has(path) || !existsSync(path))
|
|
17
|
+
return undefined;
|
|
18
|
+
seen.add(path);
|
|
19
|
+
let imports;
|
|
20
|
+
try {
|
|
21
|
+
imports = readPlainSourceMetadata(readFileSync(path, "utf8")).metadata[IMPORT_DIRECTIVE] ?? [];
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
for (const name of imports) {
|
|
27
|
+
const bare = name.replace(/^\.\//, "");
|
|
28
|
+
if (isStdlibModule(bare)) {
|
|
29
|
+
const [, kind, stack, more] = bare.split("/");
|
|
30
|
+
if (kind !== undefined && stack !== undefined && more === undefined && kind !== "lang" && stack !== "common")
|
|
31
|
+
return { kind, stack };
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const found = [dirname(path), ...dirs].map((dir) => join(dir, `${bare}.plain`)).find((candidate) => existsSync(candidate));
|
|
35
|
+
const stack = found !== undefined ? visit(resolve(found)) : undefined;
|
|
36
|
+
if (stack !== undefined)
|
|
37
|
+
return stack;
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
};
|
|
41
|
+
return visit(resolve(plainFile));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The standard library's conformance template for a module's stack, `stdlib/<kind>/conformance/<stack>`, when the module
|
|
45
|
+
* imports a stack template that has one - nothing otherwise.
|
|
46
|
+
*/
|
|
47
|
+
export function conformanceTemplateOf(plainFile) {
|
|
48
|
+
const template = stdlibStackOf(plainFile);
|
|
49
|
+
const conformance = template !== undefined ? `stdlib/${template.kind}/conformance/${template.stack}` : undefined;
|
|
50
|
+
return conformance !== undefined && existsSync(join(STDLIB_ROOT, `${conformance}.plain`)) ? conformance : undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The conformance testing script a module's tests would run with, looked for under its stack - the one pinned, else
|
|
54
|
+
* the one `stackForModule` gives it - or nothing, with why when there is a why.
|
|
55
|
+
*/
|
|
56
|
+
export function conformanceScriptOf(plainFile, buildDir) {
|
|
57
|
+
const stack = stackPinned() ? undefined : stackForModule(plainFile);
|
|
58
|
+
const scripts = () => {
|
|
59
|
+
const root = buildDir !== undefined ? projectRootFor(plainFile, buildDir) : projectDirOf(plainFile);
|
|
60
|
+
return findTestScripts(root, plainFile, buildDir);
|
|
61
|
+
};
|
|
62
|
+
const found = stackPinned() ? scripts() : withStack(stack, scripts);
|
|
63
|
+
return { ...(found.conformance !== undefined ? { script: found.conformance } : {}), ...(found.notes?.conformance !== undefined ? { note: found.notes.conformance } : {}) };
|
|
64
|
+
}
|
|
65
|
+
/** The functional specs of a codebase's tree - its own, or ones it inherits - that have scenarios, as `module:func-id`, a module below's first. */
|
|
66
|
+
function withScenarios(tree, moduleName) {
|
|
67
|
+
const items = (section) => (tree[section] ?? []);
|
|
68
|
+
return [...items(REQUIRED_FUNCTIONAL_SPECS), ...items(FUNCTIONAL_REQUIREMENTS)].filter(hasScenarios).map((item) => `${item.module ?? moduleName}:${item.id}`);
|
|
69
|
+
}
|
|
70
|
+
/** Specs named in a line: the first three, and how many more. */
|
|
71
|
+
function named(specs) {
|
|
72
|
+
return specs.length <= 3 ? specs.join(", ") : `${specs.slice(0, 3).join(", ")} and ${specs.length - 3} more`;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The step of adding tests a module skipped, when its functional specs have scenarios: no test reqs - its own or
|
|
76
|
+
* imported - or no conformance testing script for its stack. `tree` is what its conformance tests would be written from:
|
|
77
|
+
* the codebase's spec, the module's own sections with its imports in, and the functional specs it inherits. The script
|
|
78
|
+
* is looked for under the module's stack - the one pinned, else the one `stackForModule` gives it.
|
|
79
|
+
*/
|
|
80
|
+
export function testingGap(input) {
|
|
81
|
+
const { moduleName, plainFile } = input;
|
|
82
|
+
// Conformance tests off: none is written, so no step of adding them is skipped.
|
|
83
|
+
if (!conformanceTestsOn(plainFile))
|
|
84
|
+
return undefined;
|
|
85
|
+
const tested = withScenarios(input.tree, moduleName);
|
|
86
|
+
if (tested.length === 0)
|
|
87
|
+
return undefined;
|
|
88
|
+
const found = conformanceScriptOf(plainFile, input.buildDir);
|
|
89
|
+
const scriptStep = "write the conformance testing script with the implement-conformance-testing-script skill";
|
|
90
|
+
if ((input.tree[TEST_REQUIREMENTS] ?? []).length === 0) {
|
|
91
|
+
const conformance = conformanceTemplateOf(plainFile);
|
|
92
|
+
const reqs = conformance !== undefined ? `import the stack's conformance template, ${conformance}` : "write the module's own ***test reqs***";
|
|
93
|
+
return {
|
|
94
|
+
step: "test reqs",
|
|
95
|
+
text: `${moduleName} has scenarios to test - ${named(tested)} - but no ***test reqs***, so its conformance tests cannot be written - the test reqs are ` +
|
|
96
|
+
`the first step of adding tests. Next step: ${reqs}, with change_plain${found.script === undefined ? `; then ${scriptStep}` : ""}.`,
|
|
97
|
+
short: "scenarios not tested: no test reqs",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (found.script === undefined) {
|
|
101
|
+
const note = found.note;
|
|
102
|
+
return {
|
|
103
|
+
step: "conformance testing script",
|
|
104
|
+
text: `${moduleName} has scenarios to test - ${named(tested)} - and ***test reqs*** but no conformance testing script, so nothing can run its conformance ` +
|
|
105
|
+
`tests - the script is the second step of adding tests. Next step: ${scriptStep}.${note !== undefined ? ` ${note}` : ""}`,
|
|
106
|
+
short: "scenarios not tested: no conformance testing script",
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
@@ -93,7 +93,7 @@ export declare function draftPrompt(): string;
|
|
|
93
93
|
export declare const AGAIN_ASKS = 2;
|
|
94
94
|
/** For a coding agent regenerating functional specs again that has written nothing: no change is no answer. */
|
|
95
95
|
export declare function againPrompt(again: Array<{
|
|
96
|
-
|
|
96
|
+
funcId: string;
|
|
97
97
|
module?: string;
|
|
98
98
|
}>): string;
|
|
99
99
|
export type UnitVerdict = {
|
|
@@ -6,7 +6,7 @@ import { reportActivity } from "./activity.js";
|
|
|
6
6
|
import { maskSecrets } from "./masking.js";
|
|
7
7
|
import { NOTES_FILE } from "./notes.js";
|
|
8
8
|
import { modulesScratchDir } from "./paths.js";
|
|
9
|
-
import { RECORD_FILE } from "./paths.js";
|
|
9
|
+
import { BASE_RECORD_FILE, RECORD_FILE } from "./paths.js";
|
|
10
10
|
import { missingToolchain, runUnitScript, scriptFailure } from "./test-scripts.js";
|
|
11
11
|
import { COMMAND_TIMEOUT_MS } from "./shell.js";
|
|
12
12
|
import { pulseWrote } from "./pulse-files.js";
|
|
@@ -210,6 +210,7 @@ export const NOT_CODE = new Set([
|
|
|
210
210
|
// Working notes for the agents: not part of the codebase either (see notes.ts).
|
|
211
211
|
NOTES_FILE,
|
|
212
212
|
RECORD_FILE,
|
|
213
|
+
BASE_RECORD_FILE,
|
|
213
214
|
]);
|
|
214
215
|
export function listFiles(root) {
|
|
215
216
|
const found = [];
|
|
@@ -305,8 +306,8 @@ export function draftPrompt() {
|
|
|
305
306
|
return [
|
|
306
307
|
"You have not written anything yet: the code folder is as it was when you started.",
|
|
307
308
|
"",
|
|
308
|
-
"If the change needs code - anything it adds to or changes in a functional spec,
|
|
309
|
-
"
|
|
309
|
+
"If the change needs code - anything it adds to or changes in a functional spec, a definition or an implementation",
|
|
310
|
+
"req - stop planning and write it now: your best guess at what the change asks, the code and its",
|
|
310
311
|
"unit tests, as a draft. If you are sure it needs no code at all - it only rewords, describes or renames - say so",
|
|
311
312
|
"in one line, and why.",
|
|
312
313
|
"Where the specification leaves something open, pick the most likely reading, write it, and note the assumption",
|
|
@@ -317,7 +318,7 @@ export function draftPrompt() {
|
|
|
317
318
|
export const AGAIN_ASKS = 2;
|
|
318
319
|
/** For a coding agent regenerating functional specs again that has written nothing: no change is no answer. */
|
|
319
320
|
export function againPrompt(again) {
|
|
320
|
-
const named = again.map((spec) =>
|
|
321
|
+
const named = again.map((spec) => (spec.module !== undefined ? `\`${spec.module}:${spec.funcId}\`` : `\`${spec.funcId}\``)).join(", ");
|
|
321
322
|
return [
|
|
322
323
|
"You have not written anything yet: the code folder is as it was when you started.",
|
|
323
324
|
"",
|
|
@@ -47,7 +47,7 @@ export declare function changedUnitTests(buildDir: string, atStart: Map<string,
|
|
|
47
47
|
*/
|
|
48
48
|
export declare function unitReviewTask(options: {
|
|
49
49
|
buildDir: string;
|
|
50
|
-
/** What the work built: "
|
|
50
|
+
/** What the work built: "functional spec `m:add-task`", "the spec change to module `m`". */
|
|
51
51
|
what: string;
|
|
52
52
|
/** The specification it built, as the coding agent had it, what is new in this work apart (`reviewedSpec`). */
|
|
53
53
|
spec: string;
|
|
@@ -111,7 +111,7 @@ export function unitReviewTask(options) {
|
|
|
111
111
|
"This work is the <spec change> in <new in this work>, section by section: what it adds or changes is what needs",
|
|
112
112
|
"testing, and the tests it changed still have to test the rest as they did. A change that adds no behaviour - a",
|
|
113
113
|
"description, a renamed concept, a reworded spec - needs no new test.",
|
|
114
|
-
"Behaviour is what a functional spec
|
|
114
|
+
"Behaviour is what a functional spec or an implementation req states. A definition says what",
|
|
115
115
|
"a word means: a page, a component or a record that only <definitions> name, and no functional spec or",
|
|
116
116
|
"implementation req of this codebase asks for, has no code yet, and a test for it is not missing.",
|
|
117
117
|
]
|
|
@@ -303,8 +303,8 @@ async function reviewLoop(options, reviewer) {
|
|
|
303
303
|
workdir: options.buildDir,
|
|
304
304
|
read: [options.buildDir],
|
|
305
305
|
uphold: [
|
|
306
|
-
"- uphold it when because of it a behaviour this work's specification states - in a functional spec
|
|
307
|
-
"
|
|
306
|
+
"- uphold it when because of it a behaviour this work's specification states - in a functional spec or an",
|
|
307
|
+
" implementation req - is not implemented, is implemented other than it says, is not tested, or a",
|
|
308
308
|
" test cannot fail when that behaviour breaks. Then the reviewer is right, and the coding agent fixes it.",
|
|
309
309
|
],
|
|
310
310
|
changed,
|
package/dist/regenerate-cli.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { CONFIG_FILE, readProjectConfig } from "./config.js";
|
|
2
2
|
import { parseArgs } from "./cli-options.js";
|
|
3
3
|
import { headlessOptions, runLoop } from "./loop-cli.js";
|
|
4
|
-
const USAGE = `usage: turri regenerate [file.plain] [
|
|
4
|
+
const USAGE = `usage: turri regenerate [file.plain] [module:func-id ...] [options]
|
|
5
5
|
|
|
6
6
|
Regenerates the module's code from its spec as it is now, and everything it requires, bottom first, as
|
|
7
7
|
the chat does: what is left to regenerate - from what each module's code was regenerated from to its
|
|
8
8
|
spec now - is planned into jobs, and they run one after the other until the code is regenerated from
|
|
9
|
-
the spec,
|
|
10
|
-
how each went. With functional specs named
|
|
11
|
-
does in the chat with
|
|
9
|
+
the spec, then the conformance tests of every functional spec with scenarios whose tests are behind. Then it says
|
|
10
|
+
how each went. With functional specs named - each as module:func-id - those are regenerated though their spec did
|
|
11
|
+
not change, as regenerate does in the chat with funcIds named: their code taken out and built anew, doing what the
|
|
12
|
+
spec says.
|
|
12
13
|
|
|
13
14
|
The project's config.turri.yaml, in the current directory, gives the defaults: module (the file to
|
|
14
|
-
build when none is given), coding-model (the agents that build)
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
build when none is given), coding-model (the agents that build) and harness-model (the planner). It exits
|
|
16
|
+
0 only when every module is complete: regenerated from the spec as it is, and its conformance tests written
|
|
17
|
+
for every functional spec's scenarios.
|
|
17
18
|
|
|
18
19
|
--dry-run plan the work and print the prompt of the first job, and run nothing (turri glimpse)
|
|
19
20
|
--start-over start over, without asking, a codebase regenerated without a module it now requires -
|
|
@@ -21,23 +22,21 @@ its conformance tests too when they are on.
|
|
|
21
22
|
--from-scratch delete the module's code and conformance tests (its lineage keeps them) and regenerate all of it
|
|
22
23
|
--from-scratch-chain the same for every module of its requires chain, built again from the bottom - after
|
|
23
24
|
modules were split or moved
|
|
24
|
-
--harness-model <p/id> config.turri.yaml's harness-model for this run: the planner
|
|
25
|
+
--harness-model <p/id> config.turri.yaml's harness-model for this run: the planner
|
|
25
26
|
--coding-model <p/id> its coding-model, --model too: the agents that build; p/id:level for how hard a model thinks
|
|
26
27
|
--judge-model <p/id> its judge-model: the judge and the test review
|
|
27
28
|
--review <true|false> its review: whether the code and its tests are reviewed once they pass
|
|
28
29
|
--mutation-testing <true|false>
|
|
29
30
|
its mutation-testing: whether the coding agent and the reviewer can put a bug in the code
|
|
30
31
|
for one test run (can_it_fail), to see that a test catches it
|
|
31
|
-
--conformance-tests <all|chosen|module|false>
|
|
32
|
-
its conformance-tests: which conformance tests are written and run
|
|
33
32
|
--build-dir <dir> as the run ends, however it ends, export the code there: the furthest-built codebase's
|
|
34
33
|
last commit, which holds every module it requires - the folder's contents are replaced
|
|
35
34
|
--report <file.json> write a report of the run there - outcome, usage, pulses, functional specs completed -
|
|
36
35
|
as it starts, as each pulse ends and as it exits (docs/headless.md)
|
|
37
36
|
|
|
38
37
|
PLAIN_TEMPLATE_DIRS colon-separated template directories`;
|
|
39
|
-
const { positional, options } = parseArgs(process.argv.slice(2), ["--dry-run", "--start-over", "--from-scratch", "--from-scratch-chain"], USAGE, ["--build-dir", "--report", "--
|
|
40
|
-
const { report, buildDir } = headlessOptions(options
|
|
38
|
+
const { positional, options } = parseArgs(process.argv.slice(2), ["--dry-run", "--start-over", "--from-scratch", "--from-scratch-chain"], USAGE, ["--build-dir", "--report", "--harness-model", "--judge-model", "--review", "--mutation-testing"]);
|
|
39
|
+
const { report, buildDir } = headlessOptions(options);
|
|
41
40
|
const named = positional[0] ?? readProjectConfig(process.cwd()).module;
|
|
42
41
|
// Functional specs named after the file: regenerated again though their spec did not change.
|
|
43
42
|
const again = positional.slice(1);
|
package/dist/release-agent.js
CHANGED
|
@@ -22,6 +22,10 @@ function outsideBuiltCode(tool, dir) {
|
|
|
22
22
|
if (relative(dir, path).split(sep).includes("plain_modules")) {
|
|
23
23
|
throw new Error(`Not written: ${shortPath(path)} is built code. A release never changes it - a fix belongs in the spec.`);
|
|
24
24
|
}
|
|
25
|
+
// The ***plain - a module, a test plain file - is the person's: a release that finds it wrong says so, never rewrites it.
|
|
26
|
+
if (path.endsWith(".plain")) {
|
|
27
|
+
throw new Error(`Not written: ${shortPath(path)} is ***plain. A release never changes it - say what is wrong with it instead.`);
|
|
28
|
+
}
|
|
25
29
|
return tool.execute(toolCallId, params, signal, onUpdate, ctx);
|
|
26
30
|
},
|
|
27
31
|
};
|
package/dist/release-cli.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { closeSync, openSync, readSync, statSync } from "node:fs";
|
|
2
|
+
import { refuseTestPlainFile } from "./test-plain-files.js";
|
|
2
3
|
import { resolve } from "node:path";
|
|
3
4
|
import { configForSpec, modelSpec, projectModels, readProjectConfig } from "./config.js";
|
|
4
5
|
import { createDeployState } from "./release-tools.js";
|
|
@@ -90,6 +91,7 @@ async function main() {
|
|
|
90
91
|
process.exit(1);
|
|
91
92
|
}
|
|
92
93
|
const plainFile = resolve(named);
|
|
94
|
+
refuseTestPlainFile(plainFile);
|
|
93
95
|
// A release follows steps - a script, a start command: the coding model does that well, thinking low.
|
|
94
96
|
const model = args.model ?? modelSpec(projectModels(configForSpec(plainFile)).coding);
|
|
95
97
|
const state = createDeployState();
|
package/dist/release-tools.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
2
|
+
import { refuseTestPlainFile } from "./test-plain-files.js";
|
|
2
3
|
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
3
4
|
import { Type } from "typebox";
|
|
4
5
|
import { deployLogDir, deployTarget, describeDeployment, exportModule, hostDirOf, shortPath, startDeployed, stopDeployed, } from "./regenerate/release.js";
|
|
@@ -88,6 +89,7 @@ export function createDeployTools(options) {
|
|
|
88
89
|
if (named === undefined)
|
|
89
90
|
throw new Error("Which spec? config.turri.yaml names no module: pass plain_file.");
|
|
90
91
|
const plainFile = resolve(options.cwd, named);
|
|
92
|
+
refuseTestPlainFile(plainFile);
|
|
91
93
|
const target = deployTarget(plainFile, params.in_module);
|
|
92
94
|
const guidance = params.guidance?.trim() || undefined;
|
|
93
95
|
// Released already: the module's last commit is what the last release shipped. Nothing is exported or stopped -
|
package/dist/run-report.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { type Job } from "./regenerate/jobs.ts";
|
|
|
8
8
|
* pulse failed, a crash, a signal. A process killed outright (SIGKILL) leaves the one written as the last pulse ended.
|
|
9
9
|
* Each write is atomic: the file is written beside it and renamed over it, so a reader never sees half of one.
|
|
10
10
|
*/
|
|
11
|
-
export declare const REPORT_SCHEMA_VERSION =
|
|
11
|
+
export declare const REPORT_SCHEMA_VERSION = 2;
|
|
12
12
|
/** What the usage of some model calls adds up to, as the report gives it. */
|
|
13
13
|
export interface UsageTotals {
|
|
14
14
|
input_tokens: number;
|
|
@@ -42,6 +42,7 @@ export interface CompletionCounts {
|
|
|
42
42
|
modules: Array<{
|
|
43
43
|
module: string;
|
|
44
44
|
specified: number;
|
|
45
|
+
withScenarios: number;
|
|
45
46
|
completed: number;
|
|
46
47
|
conformanceTested: number;
|
|
47
48
|
incomplete: string[];
|
|
@@ -71,7 +72,6 @@ export interface RunReportOptions {
|
|
|
71
72
|
specFile: string;
|
|
72
73
|
projectDir: string;
|
|
73
74
|
buildDir?: string;
|
|
74
|
-
conformanceTests: string;
|
|
75
75
|
/** Whether the code and its tests are reviewed once they pass. */
|
|
76
76
|
review?: boolean;
|
|
77
77
|
/** Whether the coding agent and the reviewer had can_it_fail. */
|