@turri-dev/cli 0.1.27 → 0.2.0
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 +70 -14
- 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 -13
- package/dist/regenerate/conformance.js +119 -82
- 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 +62 -43
- package/dist/regenerate/loop.js +472 -341
- 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 +71 -28
- package/dist/regenerate/test-kinds.js +110 -63
- 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 +87 -52
- 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 +23 -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
package/dist/run-report.js
CHANGED
|
@@ -13,7 +13,7 @@ import { firstRegeneration } from "./regenerate/jobs.js";
|
|
|
13
13
|
* pulse failed, a crash, a signal. A process killed outright (SIGKILL) leaves the one written as the last pulse ended.
|
|
14
14
|
* Each write is atomic: the file is written beside it and renamed over it, so a reader never sees half of one.
|
|
15
15
|
*/
|
|
16
|
-
export const REPORT_SCHEMA_VERSION =
|
|
16
|
+
export const REPORT_SCHEMA_VERSION = 2;
|
|
17
17
|
const SIGNALS = { 129: "SIGHUP", 130: "SIGINT", 143: "SIGTERM" };
|
|
18
18
|
const emptyTotals = () => ({
|
|
19
19
|
input_tokens: 0,
|
|
@@ -229,13 +229,15 @@ export function startRunReport(options) {
|
|
|
229
229
|
const modules = counts.flatMap((chain) => chain.modules.map((module) => ({
|
|
230
230
|
module: module.module,
|
|
231
231
|
specified: module.specified,
|
|
232
|
+
with_scenarios: module.withScenarios,
|
|
232
233
|
completed: module.completed,
|
|
233
234
|
conformance_tested: module.conformanceTested,
|
|
234
|
-
|
|
235
|
+
incomplete_func_ids: module.incomplete,
|
|
235
236
|
})));
|
|
236
237
|
return {
|
|
237
238
|
total: {
|
|
238
239
|
specified: modules.reduce((sum, module) => sum + module.specified, 0),
|
|
240
|
+
with_scenarios: modules.reduce((sum, module) => sum + module.with_scenarios, 0),
|
|
239
241
|
completed: modules.reduce((sum, module) => sum + module.completed, 0),
|
|
240
242
|
conformance_tested: modules.reduce((sum, module) => sum + module.conformance_tested, 0),
|
|
241
243
|
},
|
|
@@ -268,7 +270,6 @@ export function startRunReport(options) {
|
|
|
268
270
|
spec_file: resolve(options.specFile),
|
|
269
271
|
project_dir: projectRoot,
|
|
270
272
|
build_dir: options.buildDir !== undefined ? resolve(options.buildDir) : null,
|
|
271
|
-
conformance_tests: options.conformanceTests,
|
|
272
273
|
review: options.review ?? null,
|
|
273
274
|
mutation_testing: options.mutationTesting ?? null,
|
|
274
275
|
models: {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
/**
|
|
12
12
|
* What a shell command would run of turri or pi itself, if anything: the subcommand,
|
|
13
|
-
* or "pi" for another pi. `turri
|
|
13
|
+
* or "pi" for another pi. `turri parse`, `drift` and `lineage` are not in it - they start no
|
|
14
14
|
* agent - and neither are setup and doctor.
|
|
15
15
|
*/
|
|
16
16
|
export declare function selfInvocation(command: string): string | undefined;
|
package/dist/self-invocation.js
CHANGED
|
@@ -39,7 +39,7 @@ const ENTRY = {
|
|
|
39
39
|
const AT_COMMAND = String.raw `(?:^|[\s;&|()\x60$])`;
|
|
40
40
|
/**
|
|
41
41
|
* What a shell command would run of turri or pi itself, if anything: the subcommand,
|
|
42
|
-
* or "pi" for another pi. `turri
|
|
42
|
+
* or "pi" for another pi. `turri parse`, `drift` and `lineage` are not in it - they start no
|
|
43
43
|
* agent - and neither are setup and doctor.
|
|
44
44
|
*/
|
|
45
45
|
export function selfInvocation(command) {
|
package/dist/sentinel.js
CHANGED
|
@@ -244,7 +244,7 @@ export function sentinelBrief(job, context) {
|
|
|
244
244
|
// ---------------------------------------------------------------------------------------------
|
|
245
245
|
// Its tools.
|
|
246
246
|
/** The tools the sentinel's agent may call: what reads, its notices and its guidance - and what a host gives it. */
|
|
247
|
-
export const SENTINEL_TOOLS = ["read", "ls", "grep", "find", "bash", "read_output", "ledger", "pulses", "diagnose", "
|
|
247
|
+
export const SENTINEL_TOOLS = ["read", "ls", "grep", "find", "bash", "read_output", "ledger", "pulses", "diagnose", "parse", "doctor", "web_search", "web_fetch", "notify_person", "conclude", "keep_guidance"];
|
|
248
248
|
/**
|
|
249
249
|
* Its file tools and shell: the whole project to read and nothing to write - the shell behind the command check, the OS
|
|
250
250
|
* sandbox where one is up, and the audit, with the network open. Its home and temporary folder are its own, in the
|
package/dist/setup.d.ts
CHANGED
|
@@ -55,6 +55,16 @@ export interface DoctorOptions {
|
|
|
55
55
|
* nothing, so a host can show them in its own way; `turri doctor` prints the same.
|
|
56
56
|
*/
|
|
57
57
|
export declare function doctor(options?: DoctorOptions): Promise<Check[]>;
|
|
58
|
+
/**
|
|
59
|
+
* The base folders the project's config names - its own, and each stack's - each there with its files, or not: a codebase
|
|
60
|
+
* that requires nothing starts from one, and its first pulse stops when it is not there. Nothing when none is named.
|
|
61
|
+
*/
|
|
62
|
+
export declare function checkBaseFolders(project: string): Check[];
|
|
63
|
+
/**
|
|
64
|
+
* The AWS region a project's Bedrock models are called in, when it uses any: turri's, the person's or the profile's - and
|
|
65
|
+
* what is wrong with it, when the models need two, or the person's is not in their geography. Nothing when it uses none.
|
|
66
|
+
*/
|
|
67
|
+
export declare function checkBedrockRegion(project: string, env?: NodeJS.ProcessEnv): Check[];
|
|
58
68
|
/** What the checks add up to: whether turri can run here, and a line saying so - with its marks, uncoloured. */
|
|
59
69
|
export declare function verdictOf(checks: Check[]): {
|
|
60
70
|
ready: boolean;
|
package/dist/setup.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { existsSync, rmSync } from "node:fs";
|
|
3
|
-
import { join, relative } from "node:path";
|
|
2
|
+
import { existsSync, rmSync, statSync } from "node:fs";
|
|
3
|
+
import { join, relative, resolve } from "node:path";
|
|
4
4
|
import { createInterface } from "node:readline/promises";
|
|
5
5
|
import { ModelRegistry, ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { globalExtensionFile, MIN_NODE, nodeVersionOk, PACKAGE_ROOT, turriOnPath } from "./environment.js";
|
|
7
7
|
import { sandboxAvailable, sandboxWith } from "./regenerate/os-sandbox.js";
|
|
8
8
|
import { findTestScripts, onThisMachine } from "./regenerate/test-scripts.js";
|
|
9
9
|
import { configKeyOptions, parseArgs } from "./cli-options.js";
|
|
10
|
-
import { hasProjectConfig, readProjectConfig, stackOf } from "./config.js";
|
|
10
|
+
import { CONFIG_FILE, hasProjectConfig, readProjectConfig, stackConfigFile, stackOf, stacksOf } from "./config.js";
|
|
11
11
|
import { checkModels, modelsInUse, piModelAccess } from "./model-check.js";
|
|
12
|
+
import { bedrockRegionFor, describeBedrockRegion, regionInGeography } from "./bedrock-region.js";
|
|
13
|
+
import { baseFiles } from "./regenerate/base-folder.js";
|
|
12
14
|
import { brand } from "./brand.js";
|
|
13
15
|
import { readScript, scriptAgainstMachine, stackOnMachine } from "./stack.js";
|
|
14
16
|
const MARK = { ok: "✓", missing: "✗", limited: "!", info: "·" };
|
|
@@ -156,7 +158,8 @@ export async function checkProjectModels(project, access) {
|
|
|
156
158
|
}
|
|
157
159
|
let verdicts;
|
|
158
160
|
try {
|
|
159
|
-
|
|
161
|
+
const region = describeBedrockRegion(bedrockRegionFor(project));
|
|
162
|
+
verdicts = await checkModels(used, access ?? (await piModelAccess()), region !== undefined ? { bedrockRegion: region } : {});
|
|
160
163
|
}
|
|
161
164
|
catch (error) {
|
|
162
165
|
return [{ level: "missing", name: "Models", detail: `pi's models could not be read: ${error instanceof Error ? error.message : String(error)}` }];
|
|
@@ -194,10 +197,55 @@ async function allChecks(options) {
|
|
|
194
197
|
return checks;
|
|
195
198
|
if (hasProjectConfig(project) || existsSync(join(project, "plain_modules")))
|
|
196
199
|
checks.push(...checkProject(project, { has: (command) => onThisMachine(command, env) !== undefined }));
|
|
200
|
+
if (hasProjectConfig(project))
|
|
201
|
+
checks.push(...checkBedrockRegion(project, env), ...checkBaseFolders(project));
|
|
197
202
|
if (asksModels(options))
|
|
198
203
|
checks.push(...(await checkProjectModels(project)));
|
|
199
204
|
return checks;
|
|
200
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* The base folders the project's config names - its own, and each stack's - each there with its files, or not: a codebase
|
|
208
|
+
* that requires nothing starts from one, and its first pulse stops when it is not there. Nothing when none is named.
|
|
209
|
+
*/
|
|
210
|
+
export function checkBaseFolders(project) {
|
|
211
|
+
const named = new Map();
|
|
212
|
+
for (const [stack, config] of [[undefined, readProjectConfig(project)], ...stacksOf(project).map((stack) => [stack, readProjectConfig(project, stack)])]) {
|
|
213
|
+
if (config.baseFolder === undefined)
|
|
214
|
+
continue;
|
|
215
|
+
named.set(config.baseFolder, [...(named.get(config.baseFolder) ?? []), stack === undefined ? CONFIG_FILE : stackConfigFile(stack)]);
|
|
216
|
+
}
|
|
217
|
+
return [...named].map(([folder, files]) => {
|
|
218
|
+
const path = resolve(project, folder);
|
|
219
|
+
const name = `base folder ${folder}`;
|
|
220
|
+
if (!existsSync(path) || !statSync(path).isDirectory()) {
|
|
221
|
+
return { level: "missing", name, detail: `${existsSync(path) ? "not a folder" : "not there"} - named in ${files.join(", ")}`, fix: "make the folder, or name the right one in base-folder:, by path from the project's folder" };
|
|
222
|
+
}
|
|
223
|
+
const count = baseFiles(path).length;
|
|
224
|
+
return { level: "ok", name, detail: `${count} file${count === 1 ? "" : "s"} to start a codebase from - named in ${files.join(", ")}` };
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The AWS region a project's Bedrock models are called in, when it uses any: turri's, the person's or the profile's - and
|
|
229
|
+
* what is wrong with it, when the models need two, or the person's is not in their geography. Nothing when it uses none.
|
|
230
|
+
*/
|
|
231
|
+
export function checkBedrockRegion(project, env = process.env) {
|
|
232
|
+
let decided;
|
|
233
|
+
try {
|
|
234
|
+
decided = bedrockRegionFor(project, { env });
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
const line = describeBedrockRegion(decided);
|
|
240
|
+
if (decided.models.length === 0 || line === undefined)
|
|
241
|
+
return [];
|
|
242
|
+
const outside = decided.source === "set" && decided.needs !== undefined && decided.region !== undefined && !regionInGeography(decided.region, decided.needs.geography);
|
|
243
|
+
if (decided.conflict !== undefined)
|
|
244
|
+
return [{ level: "missing", name: "AWS region", detail: "the Bedrock models need two regions", more: [line] }];
|
|
245
|
+
if (outside)
|
|
246
|
+
return [{ level: "missing", name: "AWS region", detail: `${decided.region}, which does not serve ${decided.needs.geography}'s models`, more: [line] }];
|
|
247
|
+
return [{ level: "ok", name: "AWS region", detail: line }];
|
|
248
|
+
}
|
|
201
249
|
/** What the checks add up to: whether turri can run here, and a line saying so - with its marks, uncoloured. */
|
|
202
250
|
export function verdictOf(checks) {
|
|
203
251
|
const missing = checks.filter((check) => check.level === "missing").length;
|
package/dist/skill.d.ts
CHANGED
|
@@ -90,6 +90,16 @@ export interface Skill {
|
|
|
90
90
|
* (`metadata.command-only: true`): cultivate runs it only when the person has just typed its command.
|
|
91
91
|
*/
|
|
92
92
|
commandOnly: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Run in the background (`metadata.background: true`): in the chat, cultivate starts it and returns at once, and what
|
|
95
|
+
* it did comes back to the chat once the chat is idle. Nobody is asked anything while it runs.
|
|
96
|
+
*/
|
|
97
|
+
background: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* The model it runs on (`metadata.model`): `coding`, the coding model of the module it is for. Omitted: the chat's
|
|
100
|
+
* model, or `harness-model` from the command line.
|
|
101
|
+
*/
|
|
102
|
+
model?: "coding";
|
|
93
103
|
}
|
|
94
104
|
export declare function loadSkill(skillDir: string): Skill;
|
|
95
105
|
export declare function readRubric(skill: Skill, review: {
|
package/dist/skill.js
CHANGED
|
@@ -121,6 +121,10 @@ export function loadSkill(skillDir) {
|
|
|
121
121
|
throw new Error(`${name}: gate references unknown step '${stepName}'`);
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
+
const rawModel = data.metadata?.model;
|
|
125
|
+
if (rawModel !== undefined && rawModel !== "coding")
|
|
126
|
+
throw new Error(`${name}: 'metadata.model' is coding, or left out for the chat's model`);
|
|
127
|
+
const model = rawModel === "coding" ? "coding" : undefined;
|
|
124
128
|
return {
|
|
125
129
|
name,
|
|
126
130
|
description,
|
|
@@ -132,6 +136,8 @@ export function loadSkill(skillDir) {
|
|
|
132
136
|
questioning: capped,
|
|
133
137
|
diffPerAnswer,
|
|
134
138
|
commandOnly: data.metadata?.["command-only"] === true,
|
|
139
|
+
background: data.metadata?.background === true,
|
|
140
|
+
...(model !== undefined ? { model } : {}),
|
|
135
141
|
};
|
|
136
142
|
}
|
|
137
143
|
export function readRubric(skill, review) {
|
package/dist/skills.js
CHANGED
|
@@ -63,7 +63,9 @@ export function catalogueLine(skill) {
|
|
|
63
63
|
const whose = skill.origin === "turri" ? "" : ` (this project's, ${skill.origin.project})`;
|
|
64
64
|
// One the person starts themselves - typed, or picked among the options the model offers: never run unasked.
|
|
65
65
|
const byCommand = skill.commandOnly ? ` Started only by the person, with /${skill.name} or by picking it when you offer it with ask_user: never run it unasked.` : "";
|
|
66
|
-
|
|
66
|
+
// One that runs in the background: the chat goes on, and its end comes back to it.
|
|
67
|
+
const background = skill.background ? " Runs in the background: cultivate returns at once, and its end comes back to this chat - pass plainFile, the module it is for." : "";
|
|
68
|
+
return `- ${skill.name}${whose}: ${skill.description}${byCommand}${background}`;
|
|
67
69
|
}
|
|
68
70
|
/** What is said under the list about the skills it leaves out. */
|
|
69
71
|
export function leftOutNote(catalogue) {
|
package/dist/spec-tools.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
2
|
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { resolveTemplateDirs } from "./regenerate/paths.js";
|
|
4
|
+
import { advisingNote } from "./advisor-chat.js";
|
|
5
|
+
import { checkTouchedPlain, formatCheckResults } from "./plain/check.js";
|
|
7
6
|
import { applySpecDiff, plainFilesOf, revertSpecDiff } from "./regenerate/regenerate.js";
|
|
8
|
-
import {
|
|
7
|
+
import { refuseTestPlainFile } from "./test-plain-files.js";
|
|
9
8
|
import { describeModules } from "./regenerate/loop.js";
|
|
10
9
|
import { reviewNote } from "./tools.js";
|
|
11
10
|
import { STACK_PARAM, stacklessBuild, withCallStack } from "./loop-tools.js";
|
|
@@ -33,10 +32,13 @@ export function createSpecDiffTools(options) {
|
|
|
33
32
|
description: "Apply a change to the ***plain specs, given as a git diff of the .plain files, and start building it in " +
|
|
34
33
|
"the background. The diff is applied with git; if the specs no longer parse, it is undone and nothing is " +
|
|
35
34
|
"built. Any section may change - definitions, implementation reqs, functional specs, added, changed or removed - and " +
|
|
36
|
-
"every change is built, whatever it touches."
|
|
35
|
+
"every change is built, whatever it touches. A diff may touch test plain files too - tests/<module>/<func-id>.plain, " +
|
|
36
|
+
"a functional spec's conformance and acceptance scenarios. A functional spec with none has no test plain file: " +
|
|
37
|
+
"write one only to give it scenarios.",
|
|
37
38
|
promptSnippet: "apply a git diff of the .plain specs and start building it",
|
|
38
39
|
promptGuidelines: [
|
|
39
40
|
"Every change to a .plain file in the product phase goes through change_plain, as a git diff with paths relative to the project root. It writes the change and starts its build in one step.",
|
|
41
|
+
"A functional spec is a `# <func-id>` heading and its text. One with scenarios has a test plain file, tests/<module>/<func-id>.plain: its `module:` and `func_id:`, then `***conformance tests***` and `***acceptance tests***`, each scenario a `# <scenario-id>` heading and its text - a section only when it has a scenario, an empty one being a syntax error. One with no scenarios has no test plain file. plain_file stays the module's .plain file when the diff touches only its test plain files.",
|
|
40
42
|
skillPaced
|
|
41
43
|
? "One change per answer, and a new one only after the user has answered a question about it. Set fixes_earlier to correct something already written."
|
|
42
44
|
: "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.",
|
|
@@ -62,31 +64,17 @@ export function createSpecDiffTools(options) {
|
|
|
62
64
|
const touched = plainFilesOf(params.diff);
|
|
63
65
|
if (touched.length === 0)
|
|
64
66
|
throw new Error("The diff changes no .plain file. Nothing was written.");
|
|
67
|
+
refuseTestPlainFile(resolve(cwd, params.plain_file));
|
|
65
68
|
const result = applySpecDiff(params.diff, cwd);
|
|
66
|
-
// A change that breaks a spec is not written at all, so nothing is ever built from a
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
})),
|
|
76
|
-
];
|
|
77
|
-
for (const path of toParse) {
|
|
78
|
-
const file = relative(cwd, path);
|
|
79
|
-
try {
|
|
80
|
-
plainFileParser(basename(path), resolveTemplateDirs(path), { allowNoFunctionalities: true });
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
if (error instanceof MissingFunctionalitiesError)
|
|
84
|
-
continue;
|
|
85
|
-
if (result === "applied")
|
|
86
|
-
revertSpecDiff(params.diff, cwd);
|
|
87
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
88
|
-
throw new Error(`Nothing was written: with this change ${file} no longer parses. ${reason}`);
|
|
89
|
-
}
|
|
69
|
+
// A change that breaks a spec is not written at all, so nothing is ever built from a file that cannot be parsed:
|
|
70
|
+
// each module it touched is parsed with its test plain files - an import module as the modules that import it
|
|
71
|
+
// read it, a test plain file as its module - as parse reads them. A module with no functional specs yet is
|
|
72
|
+
// not broken.
|
|
73
|
+
const failed = checkTouchedPlain([params.plain_file, ...touched].map((file) => resolve(cwd, file))).filter((check) => !check.ok);
|
|
74
|
+
if (failed.length > 0) {
|
|
75
|
+
if (result === "applied")
|
|
76
|
+
revertSpecDiff(params.diff, cwd);
|
|
77
|
+
throw new Error(`Nothing was written: with this change the ***plain no longer checks.\n${formatCheckResults(failed)}`);
|
|
90
78
|
}
|
|
91
79
|
if (params.fixes_earlier !== true)
|
|
92
80
|
answeredSinceLastDiff = false;
|
|
@@ -118,8 +106,10 @@ export function createSpecDiffTools(options) {
|
|
|
118
106
|
? "Carry on: write the next change you decided yourself, or ask the next question - every answer is written as a change before the question after it."
|
|
119
107
|
: "Write the next change now, without stopping to ask: this skill goes on until everything it is to write is written.",
|
|
120
108
|
];
|
|
109
|
+
// The functional specs the test advisor took up, for the agent to say so rather than offer their scenarios.
|
|
110
|
+
const advising = advisingNote(loop, building);
|
|
121
111
|
return {
|
|
122
|
-
content: [{ type: "text", text: lines.join(" ") + reviewNote(liveReview) }],
|
|
112
|
+
content: [{ type: "text", text: lines.join(" ") + reviewNote(liveReview) + (advising !== "" ? `\n\n${advising}` : "") }],
|
|
123
113
|
details: { jobIds: statuses.flatMap((status) => (status.running !== undefined ? [status.running.jobId] : [])), files: touched },
|
|
124
114
|
};
|
|
125
115
|
},
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** A module's test plain files: every .plain file under `<test-dir>/<module>/`, sorted, as absolute paths. */
|
|
2
|
+
export declare function testPlainFilesOf(plainFile: string): string[];
|
|
3
|
+
/** The modules of a project, by name: the .plain files in its folder and its `template/` folder. */
|
|
4
|
+
export declare function projectModules(projectDir: string): Map<string, string>;
|
|
5
|
+
/** What a path under a project's test folder is: the test folder itself, or a module's folder or file in it. */
|
|
6
|
+
export interface TestPlace {
|
|
7
|
+
projectDir: string;
|
|
8
|
+
testDir: string;
|
|
9
|
+
/** The module whose folder it is in - `tests/<module>/...` - when it is in one. */
|
|
10
|
+
folderModule?: string;
|
|
11
|
+
/** For a file: the `module:` and `func_id:` it names. */
|
|
12
|
+
module?: string;
|
|
13
|
+
funcId?: string;
|
|
14
|
+
directory: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Where a path is, when it is a test plain file - under the project's test folder, or a .plain file whose frontmatter
|
|
18
|
+
* binds it with `module:` and `func_id:` - or the test folder, or a folder in it. Undefined for anything else.
|
|
19
|
+
*/
|
|
20
|
+
export declare function testPlaceOf(path: string): TestPlace | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Why a tool refuses a path in place of a module: it is a test plain file - naming its module and the functional
|
|
23
|
+
* spec it binds to - or a test folder. Undefined for a path that is not one.
|
|
24
|
+
*/
|
|
25
|
+
export declare function testPlainFileRefusal(path: string): string | undefined;
|
|
26
|
+
/** Refuse a path that is a test plain file or a test folder, as every tool that takes a module does. */
|
|
27
|
+
export declare function refuseTestPlainFile(path: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* The test plain files of a project that are not where they belong, each with where it does: a file outside any
|
|
30
|
+
* module's folder, in the folder of a module there is not, or in a module's folder its `module:` does not name. Strays
|
|
31
|
+
* are not read when a module is parsed; this is where they are found.
|
|
32
|
+
*/
|
|
33
|
+
export declare function strayTestPlainFiles(projectDir: string): Array<{
|
|
34
|
+
file: string;
|
|
35
|
+
problem: string;
|
|
36
|
+
}>;
|
|
37
|
+
/** The module a test plain file belongs to, by its folder, and that module's .plain file when the project has one. */
|
|
38
|
+
export declare function moduleOfTestPlainFile(path: string): {
|
|
39
|
+
module: string;
|
|
40
|
+
plainFile: string | undefined;
|
|
41
|
+
} | undefined;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { DEFAULT_TEST_DIR, testDirOf } from "./config.js";
|
|
4
|
+
import { PLAIN_SOURCE_FILE_EXTENSION } from "./plain/parser.js";
|
|
5
|
+
import { FUNC_ID_KEY, loadFrontmatter, MODULE_KEY } from "./plain/test-plain-file.js";
|
|
6
|
+
import { isProjectDir, projectDirOf } from "./regenerate/paths.js";
|
|
7
|
+
/**
|
|
8
|
+
* Where a project's test plain files are, and what each belongs to: `<test-dir>/<module>/`, every `.plain` file in it
|
|
9
|
+
* a test plain file of that module - its functional specs' conformance and acceptance scenarios. turri finds modules
|
|
10
|
+
* only in the project's folder and its `template/` folder, so a test plain file is never taken for a module; and no
|
|
11
|
+
* tool takes one in place of its module.
|
|
12
|
+
*/
|
|
13
|
+
/** Every .plain file under a folder, at any depth, sorted by path; none when it is not there. */
|
|
14
|
+
function plainFilesUnder(dir) {
|
|
15
|
+
let names;
|
|
16
|
+
try {
|
|
17
|
+
names = readdirSync(dir).sort();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
const found = [];
|
|
23
|
+
for (const name of names) {
|
|
24
|
+
const path = join(dir, name);
|
|
25
|
+
let directory = false;
|
|
26
|
+
try {
|
|
27
|
+
directory = statSync(path).isDirectory();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (directory)
|
|
33
|
+
found.push(...plainFilesUnder(path));
|
|
34
|
+
else if (name.endsWith(PLAIN_SOURCE_FILE_EXTENSION))
|
|
35
|
+
found.push(path);
|
|
36
|
+
}
|
|
37
|
+
return found;
|
|
38
|
+
}
|
|
39
|
+
/** A module's test plain files: every .plain file under `<test-dir>/<module>/`, sorted, as absolute paths. */
|
|
40
|
+
export function testPlainFilesOf(plainFile) {
|
|
41
|
+
return plainFilesUnder(join(testDirOf(plainFile), basename(plainFile, PLAIN_SOURCE_FILE_EXTENSION)));
|
|
42
|
+
}
|
|
43
|
+
/** Whether a path is a folder, or below one. */
|
|
44
|
+
function within(path, folder) {
|
|
45
|
+
const from = relative(folder, path);
|
|
46
|
+
return from === "" || (!from.startsWith("..") && !from.startsWith(sep) && from !== "..");
|
|
47
|
+
}
|
|
48
|
+
/** The frontmatter of a .plain file, as a test plain file's is read; empty when it cannot be read. */
|
|
49
|
+
function frontmatterOf(path) {
|
|
50
|
+
try {
|
|
51
|
+
return loadFrontmatter(readFileSync(path, "utf8").split("\r\n").join("\n")).metadata;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** The modules of a project, by name: the .plain files in its folder and its `template/` folder. */
|
|
58
|
+
export function projectModules(projectDir) {
|
|
59
|
+
const modules = new Map();
|
|
60
|
+
for (const dir of [join(projectDir, "template"), projectDir]) {
|
|
61
|
+
let names = [];
|
|
62
|
+
try {
|
|
63
|
+
names = readdirSync(dir).filter((name) => name.endsWith(PLAIN_SOURCE_FILE_EXTENSION));
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
for (const name of names)
|
|
69
|
+
modules.set(basename(name, PLAIN_SOURCE_FILE_EXTENSION), join(dir, name));
|
|
70
|
+
}
|
|
71
|
+
return modules;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Where a path is, when it is a test plain file - under the project's test folder, or a .plain file whose frontmatter
|
|
75
|
+
* binds it with `module:` and `func_id:` - or the test folder, or a folder in it. Undefined for anything else.
|
|
76
|
+
*/
|
|
77
|
+
export function testPlaceOf(path) {
|
|
78
|
+
const absolute = resolve(path);
|
|
79
|
+
const directory = existsSync(absolute) && statSync(absolute).isDirectory();
|
|
80
|
+
const projectDir = projectOfTestPath(absolute, directory);
|
|
81
|
+
const testDir = testDirOf(join(projectDir, "x.plain"));
|
|
82
|
+
const metadata = directory || !existsSync(absolute) ? {} : frontmatterOf(absolute);
|
|
83
|
+
const bound = typeof metadata[MODULE_KEY] === "string" && typeof metadata[FUNC_ID_KEY] === "string";
|
|
84
|
+
if (!within(absolute, testDir) && !bound)
|
|
85
|
+
return undefined;
|
|
86
|
+
const parts = within(absolute, testDir) ? relative(testDir, absolute).split(sep).filter(Boolean) : [];
|
|
87
|
+
const folderModule = directory ? parts[0] : parts.length > 1 ? parts[0] : undefined;
|
|
88
|
+
return {
|
|
89
|
+
projectDir,
|
|
90
|
+
testDir,
|
|
91
|
+
directory,
|
|
92
|
+
...(folderModule !== undefined ? { folderModule } : {}),
|
|
93
|
+
...(bound ? { module: metadata[MODULE_KEY], funcId: metadata[FUNC_ID_KEY] } : {}),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The project a path in a test folder belongs to. One with no config yet has nothing above the path to say so: its
|
|
98
|
+
* test folder is `tests`, and the project the folder that holds it.
|
|
99
|
+
*/
|
|
100
|
+
function projectOfTestPath(absolute, directory) {
|
|
101
|
+
const found = projectDirOf(directory ? join(absolute, "x.plain") : absolute);
|
|
102
|
+
if (isProjectDir(found))
|
|
103
|
+
return found;
|
|
104
|
+
for (let dir = directory ? absolute : dirname(absolute); dirname(dir) !== dir; dir = dirname(dir)) {
|
|
105
|
+
if (basename(dir) === DEFAULT_TEST_DIR)
|
|
106
|
+
return dirname(dir);
|
|
107
|
+
}
|
|
108
|
+
return found;
|
|
109
|
+
}
|
|
110
|
+
/** A path as a person reads it in the project: from its folder. */
|
|
111
|
+
function shown(path, projectDir) {
|
|
112
|
+
return relative(projectDir, resolve(path)).split(sep).join("/");
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Why a tool refuses a path in place of a module: it is a test plain file - naming its module and the functional
|
|
116
|
+
* spec it binds to - or a test folder. Undefined for a path that is not one.
|
|
117
|
+
*/
|
|
118
|
+
export function testPlainFileRefusal(path) {
|
|
119
|
+
const place = testPlaceOf(path);
|
|
120
|
+
if (place === undefined)
|
|
121
|
+
return undefined;
|
|
122
|
+
const given = shown(path, place.projectDir) + (place.directory ? "/" : "");
|
|
123
|
+
const module = place.module ?? place.folderModule;
|
|
124
|
+
if (place.directory) {
|
|
125
|
+
return module === undefined
|
|
126
|
+
? `\`${given}\` is the test folder of the project, which holds the test plain files; name a module's .plain file.`
|
|
127
|
+
: `\`${given}\` is the test folder of \`${module}\`; name \`${module}.plain\`.`;
|
|
128
|
+
}
|
|
129
|
+
if (module === undefined)
|
|
130
|
+
return `\`${given}\` is in the test folder, where test plain files are; name a module's .plain file.`;
|
|
131
|
+
const funcId = place.funcId ?? basename(path, PLAIN_SOURCE_FILE_EXTENSION);
|
|
132
|
+
return `\`${given}\` is a test plain file of \`${module}\`; name \`${module}.plain\`, or \`${module}:${funcId}\` for that one functional spec.`;
|
|
133
|
+
}
|
|
134
|
+
/** Refuse a path that is a test plain file or a test folder, as every tool that takes a module does. */
|
|
135
|
+
export function refuseTestPlainFile(path) {
|
|
136
|
+
const refusal = testPlainFileRefusal(path);
|
|
137
|
+
if (refusal !== undefined)
|
|
138
|
+
throw new Error(refusal);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The test plain files of a project that are not where they belong, each with where it does: a file outside any
|
|
142
|
+
* module's folder, in the folder of a module there is not, or in a module's folder its `module:` does not name. Strays
|
|
143
|
+
* are not read when a module is parsed; this is where they are found.
|
|
144
|
+
*/
|
|
145
|
+
export function strayTestPlainFiles(projectDir) {
|
|
146
|
+
const testDir = testDirOf(join(projectDir, "x.plain"));
|
|
147
|
+
const modules = projectModules(projectDir);
|
|
148
|
+
const strays = [];
|
|
149
|
+
for (const path of plainFilesUnder(testDir)) {
|
|
150
|
+
const file = shown(path, projectDir);
|
|
151
|
+
const folder = relative(testDir, dirname(path)).split(sep).filter(Boolean)[0];
|
|
152
|
+
const metadata = frontmatterOf(path);
|
|
153
|
+
const named = typeof metadata[MODULE_KEY] === "string" ? metadata[MODULE_KEY] : undefined;
|
|
154
|
+
const home = (module) => `${shown(join(testDir, module), projectDir)}/`;
|
|
155
|
+
if (folder === undefined) {
|
|
156
|
+
strays.push({ file, problem: named === undefined ? "move it to the folder of its module, in the test folder" : `move it to ${home(named)} (its module: ${named})` });
|
|
157
|
+
}
|
|
158
|
+
else if (!modules.has(folder)) {
|
|
159
|
+
strays.push({
|
|
160
|
+
file,
|
|
161
|
+
problem: named === undefined ? `no module ${folder}` : `no module ${folder}; module: ${named} is in ${home(named)}`,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
else if (named !== undefined && named !== folder) {
|
|
165
|
+
strays.push({ file, problem: `its module: is ${named}, not ${folder}; move it to ${home(named)}` });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return strays;
|
|
169
|
+
}
|
|
170
|
+
/** The module a test plain file belongs to, by its folder, and that module's .plain file when the project has one. */
|
|
171
|
+
export function moduleOfTestPlainFile(path) {
|
|
172
|
+
const place = testPlaceOf(path);
|
|
173
|
+
const module = place?.folderModule;
|
|
174
|
+
if (place === undefined || module === undefined)
|
|
175
|
+
return undefined;
|
|
176
|
+
return { module, plainFile: projectModules(place.projectDir).get(module) };
|
|
177
|
+
}
|
package/docs/README.md
CHANGED
|
@@ -18,6 +18,7 @@ turri is the first coding agent with the power of regenerative software: [\*\*\*
|
|
|
18
18
|
| how to start a project, or add a feature, in a conversation | [using](using.md#in-a-conversation) |
|
|
19
19
|
| how to have several chats open at once, and reopen them | [chats](chats.md) |
|
|
20
20
|
| what checks each pulse as it finishes, and tells me what needs me | [the sentinel](sentinel.md) |
|
|
21
|
+
| why the chat wrote scenarios I did not ask for, and how to turn that off | [the test advisor](test-advisor.md) |
|
|
21
22
|
| which model codes, and how to change it | [using](using.md#choosing-a-model), [the project config](config.md#models) |
|
|
22
23
|
| what `config.turri.yaml` can set, and what happens without it | [the project config](config.md) |
|
|
23
24
|
| how a backend and a frontend on different stacks share one project | [the project config](config.md#stacks) |
|
|
@@ -44,6 +45,7 @@ turri is the first coding agent with the power of regenerative software: [\*\*\*
|
|
|
44
45
|
| [Using turri](using.md) | the conversation, the tools, background jobs, choosing a model, module history, the command line |
|
|
45
46
|
| [Chats](chats.md) | several chats in one session: what each keeps and what they share, `/chat` and its keys, `turri --continue` and `.turri/chats.json` |
|
|
46
47
|
| [The sentinel](sentinel.md) | the chat every finished pulse goes to: what it checks and tests, its notices and how to answer them, what it may do, closing it, its model, in a host |
|
|
48
|
+
| [The test advisor](test-advisor.md) | turri's background check of new and changed functional specs: when it runs, how it decides, what you see, its records, model and setting |
|
|
47
49
|
| [The project config](config.md) | `config.turri.yaml` and each stack's `config.turri.<stack>.yaml`: where they are read from, every key, models and their inheritance, conformance, the testing scripts, stacks and choosing one, the old `config.yaml` |
|
|
48
50
|
| [Deploy](deploy.md) | trying what is built: dist/<module>, the last commit, starting it on your machine, `turri release` |
|
|
49
51
|
| [Running unattended](headless.md) | regenerating overnight or in CI: before you leave it, the command, the results, resuming, rebuilding one functionality |
|
|
@@ -51,7 +53,7 @@ turri is the first coding agent with the power of regenerative software: [\*\*\*
|
|
|
51
53
|
| [Regenerating](regenerating.md) | where things go, one job, a whole module and how its work is cut into jobs, each job's cases, \*\*\*plain changes, conformance tests, the two records of what is built |
|
|
52
54
|
| [The sandbox](sandbox.md) | what an agent can reach, the file-tool fence, the command check, the OS sandbox, the audit |
|
|
53
55
|
| [Skills and gates](skills-and-gates.md) | the gate syntax, what the harness enforces, what a step can do, the rules, trying the gates |
|
|
54
|
-
| [The \*\*\*plain parser](parser.md) | the TypeScript port, `turri
|
|
56
|
+
| [The \*\*\*plain parser](parser.md) | the TypeScript port, `turri parse`, inheritance, the iterator, fidelity |
|
|
55
57
|
| [Theming](theming.md) | the turri themes, contrast, backgrounds, the font |
|
|
56
58
|
| [Running turri in a host](hosting.md) | `@turri-dev/cli/host`: the session and its chats in a host's own windows - their jobs, their questions - and what a host shows beside them |
|
|
57
59
|
| [Development](development.md) | tests, the source layout, known edges |
|