@turri-dev/cli 0.1.6 → 0.1.7-next.eb57c34

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.
Files changed (140) hide show
  1. package/dist/ask.d.ts +83 -0
  2. package/dist/brand.d.ts +13 -0
  3. package/dist/btw.d.ts +67 -0
  4. package/dist/chat-models.d.ts +22 -0
  5. package/dist/chat-skills.d.ts +17 -0
  6. package/dist/cli-options.d.ts +25 -0
  7. package/dist/cli.d.ts +9 -0
  8. package/dist/config-command.d.ts +26 -0
  9. package/dist/config-edit.d.ts +69 -0
  10. package/dist/config.d.ts +321 -0
  11. package/dist/conform-cli.d.ts +1 -0
  12. package/dist/cultivate-cli.d.ts +1 -0
  13. package/dist/drift-cli.d.ts +1 -0
  14. package/dist/environment.d.ts +39 -0
  15. package/dist/extension.d.ts +147 -0
  16. package/dist/extension.js +36 -10
  17. package/dist/genome.d.ts +62 -0
  18. package/dist/graph.d.ts +52 -0
  19. package/dist/host.d.ts +21 -0
  20. package/dist/host.js +25 -0
  21. package/dist/identity.d.ts +2 -0
  22. package/dist/init-config.d.ts +106 -0
  23. package/dist/jellyfish.d.ts +25 -0
  24. package/dist/judge.d.ts +25 -0
  25. package/dist/lineage-cli.d.ts +1 -0
  26. package/dist/loop-cli.d.ts +34 -0
  27. package/dist/loop-tools.d.ts +80 -0
  28. package/dist/mature-tools.d.ts +53 -0
  29. package/dist/model-check.d.ts +42 -0
  30. package/dist/module-report.d.ts +9 -0
  31. package/dist/pi-launch.d.ts +1 -0
  32. package/dist/pi.d.ts +246 -0
  33. package/dist/picture-row.d.ts +10 -0
  34. package/dist/plain/check.d.ts +23 -0
  35. package/dist/plain/concepts.d.ts +11 -0
  36. package/dist/plain/errors.d.ts +22 -0
  37. package/dist/plain/files.d.ts +12 -0
  38. package/dist/plain/highlight.d.ts +42 -0
  39. package/dist/plain/index.d.ts +15 -0
  40. package/dist/plain/liquid.d.ts +22 -0
  41. package/dist/plain/markdown.d.ts +74 -0
  42. package/dist/plain/parser.d.ts +87 -0
  43. package/dist/plain/spec.d.ts +90 -0
  44. package/dist/plain/stdlib.d.ts +14 -0
  45. package/dist/question-dialog.d.ts +51 -0
  46. package/dist/questioning.d.ts +55 -0
  47. package/dist/quiet.d.ts +34 -0
  48. package/dist/regenerate/activity.d.ts +207 -0
  49. package/dist/regenerate/agent-env.d.ts +44 -0
  50. package/dist/regenerate/agent-shell.d.ts +49 -0
  51. package/dist/regenerate/arbiter.d.ts +68 -0
  52. package/dist/regenerate/audit.d.ts +69 -0
  53. package/dist/regenerate/cases.d.ts +9 -0
  54. package/dist/regenerate/change-view.d.ts +18 -0
  55. package/dist/regenerate/choose-conformance.d.ts +32 -0
  56. package/dist/regenerate/chunk-plan.d.ts +164 -0
  57. package/dist/regenerate/codebase.d.ts +21 -0
  58. package/dist/regenerate/command-check.d.ts +40 -0
  59. package/dist/regenerate/compaction.d.ts +13 -0
  60. package/dist/regenerate/confine.d.ts +62 -0
  61. package/dist/regenerate/conform.d.ts +63 -0
  62. package/dist/regenerate/conformance-agents.d.ts +405 -0
  63. package/dist/regenerate/conformance.d.ts +189 -0
  64. package/dist/regenerate/conversation-writes.d.ts +10 -0
  65. package/dist/regenerate/credentials.d.ts +18 -0
  66. package/dist/regenerate/debug-log.d.ts +54 -0
  67. package/dist/regenerate/debugger.d.ts +31 -0
  68. package/dist/regenerate/delivery.d.ts +6 -0
  69. package/dist/regenerate/diagnose-job.d.ts +2 -0
  70. package/dist/regenerate/diagnose.d.ts +46 -0
  71. package/dist/regenerate/diff-view.d.ts +61 -0
  72. package/dist/regenerate/drift.d.ts +84 -0
  73. package/dist/regenerate/engine.d.ts +52 -0
  74. package/dist/regenerate/environment-error.d.ts +136 -0
  75. package/dist/regenerate/job-log.d.ts +28 -0
  76. package/dist/regenerate/job-panel.d.ts +122 -0
  77. package/dist/regenerate/jobs-tree.d.ts +71 -0
  78. package/dist/regenerate/jobs.d.ts +186 -0
  79. package/dist/regenerate/lanes.d.ts +16 -0
  80. package/dist/regenerate/lineage.d.ts +95 -0
  81. package/dist/regenerate/list-arguments.d.ts +21 -0
  82. package/dist/regenerate/loop.d.ts +392 -0
  83. package/dist/regenerate/masking.d.ts +19 -0
  84. package/dist/regenerate/notes.d.ts +159 -0
  85. package/dist/regenerate/os-sandbox.d.ts +89 -0
  86. package/dist/regenerate/panel-dock.d.ts +23 -0
  87. package/dist/regenerate/paths.d.ts +92 -0
  88. package/dist/regenerate/plain-palette.d.ts +34 -0
  89. package/dist/regenerate/prompt.d.ts +192 -0
  90. package/dist/regenerate/records.d.ts +88 -0
  91. package/dist/regenerate/regenerate.d.ts +108 -0
  92. package/dist/regenerate/regression.d.ts +58 -0
  93. package/dist/regenerate/release.d.ts +123 -0
  94. package/dist/regenerate/requires.d.ts +96 -0
  95. package/dist/regenerate/resources.d.ts +53 -0
  96. package/dist/regenerate/self-check.d.ts +29 -0
  97. package/dist/regenerate/services.d.ts +28 -0
  98. package/dist/regenerate/shell.d.ts +91 -0
  99. package/dist/regenerate/side-panel.d.ts +57 -0
  100. package/dist/regenerate/spec-map-view.d.ts +139 -0
  101. package/dist/regenerate/spec-map.d.ts +138 -0
  102. package/dist/regenerate/spec-steps.d.ts +137 -0
  103. package/dist/regenerate/state.d.ts +20 -0
  104. package/dist/regenerate/steer.d.ts +34 -0
  105. package/dist/regenerate/steering.d.ts +56 -0
  106. package/dist/regenerate/stop-report.d.ts +43 -0
  107. package/dist/regenerate/submit-work.d.ts +42 -0
  108. package/dist/regenerate/test-kinds.d.ts +42 -0
  109. package/dist/regenerate/test-scripts.d.ts +221 -0
  110. package/dist/regenerate/testing.d.ts +141 -0
  111. package/dist/regenerate/transcript-view.d.ts +21 -0
  112. package/dist/regenerate/unit-gate.d.ts +27 -0
  113. package/dist/regenerate/unit-review.d.ts +152 -0
  114. package/dist/regenerate/web.d.ts +61 -0
  115. package/dist/regenerate/workspace.d.ts +12 -0
  116. package/dist/regenerate-cli.d.ts +1 -0
  117. package/dist/release-agent.d.ts +26 -0
  118. package/dist/release-cli.d.ts +1 -0
  119. package/dist/release-tools.d.ts +38 -0
  120. package/dist/resume-line.d.ts +2 -0
  121. package/dist/review/live.d.ts +72 -0
  122. package/dist/rules.d.ts +20 -0
  123. package/dist/run-report.d.ts +88 -0
  124. package/dist/screen-padding.d.ts +28 -0
  125. package/dist/self-invocation.d.ts +25 -0
  126. package/dist/setup.d.ts +43 -0
  127. package/dist/skill-progress.d.ts +46 -0
  128. package/dist/skill.d.ts +98 -0
  129. package/dist/skills.d.ts +30 -0
  130. package/dist/spec-diff-cli.d.ts +1 -0
  131. package/dist/spec-tools.d.ts +36 -0
  132. package/dist/stack.d.ts +58 -0
  133. package/dist/stacks.d.ts +12 -0
  134. package/dist/stdlib.d.ts +27 -0
  135. package/dist/terminal-images.d.ts +2 -0
  136. package/dist/tools.d.ts +101 -0
  137. package/dist/validate-cli.d.ts +1 -0
  138. package/docs/README.md +2 -0
  139. package/docs/hosting.md +52 -0
  140. package/package.json +8 -1
@@ -0,0 +1,26 @@
1
+ import { type DeployState } from "./release-tools.ts";
2
+ /**
3
+ * What the project says of itself, for the agent to find release guidance in: its AGENTS.md and its skills
4
+ * (`.agents/skills`, `.pi/skills`, `.claude/skills`), where turri runs and in the host codebase - once each, whole.
5
+ */
6
+ export declare function projectNotes(dirs: string[]): string[];
7
+ export interface ReleaseAgentInput {
8
+ plainFile: string;
9
+ /** Where turri runs. */
10
+ cwd: string;
11
+ /** The agent's model: the harness model. */
12
+ model?: string;
13
+ /** What release has shipped and been told this conversation - shared with the chat's release. */
14
+ state: DeployState;
15
+ inModule?: string;
16
+ /** What the person said of how to release it. */
17
+ guidance?: string;
18
+ /** Released as a pulse's last stage: what the pulse landed. The agent releases only when the guidance says to. */
19
+ afterPulse?: {
20
+ moduleName: string;
21
+ subject: string;
22
+ };
23
+ onDelta?: (delta: string) => void;
24
+ }
25
+ /** Run the release agent to its end; its last reply. */
26
+ export declare function runReleaseAgent(input: ReleaseAgentInput): Promise<string>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,38 @@
1
+ import { type Deployment } from "./regenerate/release.ts";
2
+ import type { JobRegistry } from "./regenerate/jobs.ts";
3
+ import type { WorkLoop } from "./regenerate/loop.ts";
4
+ /**
5
+ * The tools that put a module out for the person to try, and start it: the conversation's, and the
6
+ * one-off agent's of `turri release`. The harness does the parts that must hold - exporting the
7
+ * last commit, owning what it starts - and the agent the rest: reading the code to see how it runs,
8
+ * installing what it needs, starting it, and telling the person where to find it. Nothing is put to
9
+ * the person: a release does everything itself.
10
+ */
11
+ export interface DeployState {
12
+ /** The last deploy: where what is started runs from. */
13
+ current?: Deployment;
14
+ /** The last release of each module in this conversation, by module: the commit it shipped. */
15
+ released: Map<string, Deployment>;
16
+ /** The guidance each module was last released with, by module: what a release after a pulse is told the person said. */
17
+ guidance: Map<string, string>;
18
+ }
19
+ export declare function createDeployState(): DeployState;
20
+ /** What the agent is to do once the code is in dist - in the deploy tool's answer, and the CLI agent's prompt. */
21
+ export declare function deployGuide(dir: string): string;
22
+ /**
23
+ * What the agent is to do on a guided release: the code is in dist, as on any release, and the guidance takes it on from
24
+ * there. The rules of a release to dist hold here too: what keeps running started with activate, and the built code never
25
+ * edited - and nothing put to the person.
26
+ */
27
+ export declare function guidedGuide(deployment: Deployment, guidance: string): string;
28
+ export declare function createDeployTools(options: {
29
+ cwd: string;
30
+ state: DeployState;
31
+ /** The spec deploy takes when it is given none: the project's module. */
32
+ defaultPlainFile: () => string | undefined;
33
+ /** The pulses halt stops as well - the conversation's. */
34
+ jobs?: JobRegistry;
35
+ /** The work loop whose modules halt halts by a planned row or a name - the conversation's, once it has one. */
36
+ workLoop?: () => WorkLoop | undefined;
37
+ }): import("@earendil-works/pi-coding-agent").ToolDefinition<any, any, any>[];
38
+ export declare const DEPLOY_TOOLS: string[];
@@ -0,0 +1,2 @@
1
+ /** pi's line on the way out that says how to resume the session, with turri in place of pi as the command to run. */
2
+ export declare function resumeLine(text: string): string;
@@ -0,0 +1,72 @@
1
+ import type { AgentSession } from "@earendil-works/pi-coding-agent";
2
+ import type { ContinuousSpec } from "../skill.ts";
3
+ import { type Verdict } from "../tools.ts";
4
+ /**
5
+ * Review that runs beside the interview instead of after it.
6
+ *
7
+ * The interviewer writes a functional spec, queues it, and asks the next question. A
8
+ * reviewer wakes on that same tick, reads what is now on disk, and either says nothing or
9
+ * reports what is wrong with it. Findings come back through the next tool result the
10
+ * interviewer gets, so they arrive between questions - the user is never left waiting on a
11
+ * review, and a spec that is wrong is caught before four more are built on top of it.
12
+ *
13
+ * Two rules keep this honest:
14
+ *
15
+ * - The reviewer is read-only. It reports; the interviewer owns the file. Two agents
16
+ * editing the same `.plain` mid-interview would produce a file neither of them wrote.
17
+ * - Passes are serial and coalesced. Ten specs written while one pass runs produce one
18
+ * further pass, not ten, and no two reviewers ever read the file at once.
19
+ */
20
+ export interface LiveFinding {
21
+ /** What was under review when this was found. */
22
+ scope: string;
23
+ criterion: string;
24
+ evidence: string;
25
+ fix: string;
26
+ }
27
+ /** Something the reviewer cannot settle from the files alone. */
28
+ export interface LiveQuestion {
29
+ id: string;
30
+ scope: string;
31
+ question: string;
32
+ }
33
+ export interface LiveReviewer {
34
+ /** Something was written. Schedules a pass; returns immediately. */
35
+ notice(scope: string): void;
36
+ /** Findings not yet handed to the interviewer. Draining marks them delivered. */
37
+ take(): LiveFinding[];
38
+ /** Questions the reviewer wants the user to answer, if any. */
39
+ takeQuestions(): LiveQuestion[];
40
+ /** Hands the user's answer back, to be carried into the next pass. */
41
+ reply(questionId: string, answer: string): boolean;
42
+ /** Waits for any pass in flight plus one final pass. Used by the phase gate. */
43
+ settle(): Promise<{
44
+ clean: boolean;
45
+ findings: LiveFinding[];
46
+ }>;
47
+ /** Everything found this phase, delivered or not. */
48
+ history(): LiveFinding[];
49
+ passes(): number;
50
+ }
51
+ interface LiveVerdict extends Verdict {
52
+ question?: string;
53
+ }
54
+ /** One review pass. Replaceable so the loop around it can be tested without a model. */
55
+ export type JudgePass = (prompt: string) => Promise<LiveVerdict | undefined>;
56
+ export declare function createLiveReviewer(opts: {
57
+ cwd: string;
58
+ spec: ContinuousSpec;
59
+ rubric: string;
60
+ /** Auth and the default model come from the session running the interview. */
61
+ executor?: AgentSession;
62
+ defaultModel?: string;
63
+ /** Called when a pass finishes, for the progress display. */
64
+ onPass?: (result: {
65
+ scope: string;
66
+ findings: number;
67
+ }) => void;
68
+ judge?: JudgePass;
69
+ }): LiveReviewer;
70
+ export declare function describeFindings(findings: LiveFinding[]): string;
71
+ export declare function describeQuestions(questions: LiveQuestion[]): string;
72
+ export {};
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The ***plain authoring rules.
3
+ *
4
+ * These are loaded in full into every session's system prompt. They are large - around
5
+ * 40k tokens - and that is the deliberate trade: a rule the model has to decide to go and
6
+ * read is a rule it can skip, and these govern every spec this project writes.
7
+ */
8
+ export declare function loadRules(rulesDir: string, off?: readonly string[]): Array<{
9
+ name: string;
10
+ content: string;
11
+ }>;
12
+ /** The block appended to the system prompt. Identical every turn, so it stays cacheable. */
13
+ export declare function renderRulesBlock(rules: Array<{
14
+ name: string;
15
+ content: string;
16
+ }>): string;
17
+ export declare function rulesSummary(rules: Array<{
18
+ name: string;
19
+ content: string;
20
+ }>): string;
@@ -0,0 +1,88 @@
1
+ import type { Job } from "./regenerate/jobs.ts";
2
+ /**
3
+ * A headless run's report: one JSON file, `turri regenerate --report <file>`, saying what the run did - how it ended,
4
+ * what it spent, which pulses landed, how many functional specs are completed - for a benchmark or CI to read rather
5
+ * than the log. See docs/headless.md for every field.
6
+ *
7
+ * Written when the run starts, again as each pulse ends, and last as the process exits - however it exits: done, a
8
+ * pulse failed, a crash, a signal. A process killed outright (SIGKILL) leaves the one written as the last pulse ended.
9
+ * Each write is atomic: the file is written beside it and renamed over it, so a reader never sees half of one.
10
+ */
11
+ export declare const REPORT_SCHEMA_VERSION = 1;
12
+ /** What the usage of some model calls adds up to, as the report gives it. */
13
+ export interface UsageTotals {
14
+ input_tokens: number;
15
+ output_tokens: number;
16
+ cache_read_tokens: number;
17
+ cache_write_tokens: number;
18
+ cost_usd: number;
19
+ /** Model responses. */
20
+ llm_calls: number;
21
+ /** Requests of the harness's to an agent, each as many calls as it takes. */
22
+ agent_turns: number;
23
+ compactions: number;
24
+ /** Turns the provider failed for a moment, waited out and tried again. */
25
+ provider_retries: number;
26
+ /** Requests the provider sent nothing back on for a long while. */
27
+ provider_stalls: number;
28
+ /** Turns turri ended itself - the agent going round in a circle, its time up - and carried on from. */
29
+ turns_stopped: number;
30
+ /** How long the models took to answer, added up - from each request to its response's end. */
31
+ model_seconds: number;
32
+ }
33
+ export type RunStatus = "running" | "completed" | "incomplete" | "failed" | "interrupted" | "crashed" | "dry_run";
34
+ /** How each codebase the run regenerates in stands, as the run ends. */
35
+ export interface ModuleState {
36
+ module: string;
37
+ state: "complete" | "incomplete" | "halted" | "start_over_needed" | "running";
38
+ why?: string;
39
+ }
40
+ export interface CompletionCounts {
41
+ codebase: string;
42
+ modules: Array<{
43
+ module: string;
44
+ specified: number;
45
+ completed: number;
46
+ conformanceTested: number;
47
+ incomplete: string[];
48
+ }>;
49
+ }
50
+ /** What the report is made from, read each time it is written. Each is asked in a try: one that fails leaves its part out. */
51
+ export interface ReportSources {
52
+ jobs: () => Job[];
53
+ modules?: () => ModuleState[];
54
+ completion?: () => CompletionCounts[];
55
+ /** Done once, as the process exits, before the last write: the code exported into --build-dir. */
56
+ atExit?: () => Record<string, unknown> | undefined;
57
+ }
58
+ export interface RunReporter {
59
+ attach(sources: ReportSources): void;
60
+ /** How the run ended, as it decides: written as the process exits. A signal or a crash after it wins. */
61
+ end(status: Exclude<RunStatus, "running">, error?: string): void;
62
+ /** The run crashed: written as the process exits. */
63
+ crashed(error: unknown): void;
64
+ write(): void;
65
+ /** The report as it would be written now. */
66
+ build(exitCode?: number): Record<string, unknown>;
67
+ }
68
+ export interface RunReportOptions {
69
+ file: string;
70
+ command: string[];
71
+ specFile: string;
72
+ projectDir: string;
73
+ buildDir?: string;
74
+ conformanceTests: string;
75
+ /** Whether the code and its tests are reviewed once they pass. */
76
+ review?: boolean;
77
+ /** Whether the coding agent and the reviewer had can_it_fail. */
78
+ mutationTesting?: boolean;
79
+ models: Record<"harness" | "coding" | "judge", {
80
+ model?: string;
81
+ thinking?: string;
82
+ }>;
83
+ /** Not given: the process's exit is listened for. Tests write the last report themselves. */
84
+ onExit?: false;
85
+ }
86
+ /** Write a file whole or not at all: beside it first, then renamed over it. */
87
+ export declare function writeAtomically(file: string, text: string): void;
88
+ export declare function startRunReport(options: RunReportOptions): RunReporter;
@@ -0,0 +1,28 @@
1
+ import { type Component } from "@earendil-works/pi-tui";
2
+ /**
3
+ * Room around the whole of turri's screen: a row above and below, two columns either side - a cell is about twice as
4
+ * tall as it is wide.
5
+ *
6
+ * pi draws its screen one of two ways, and each is padded as it is drawn: full screen (pi's default), a layout - its
7
+ * root put in the middle of blank rows and columns; the regular screen, the lines its render gives - asked for in fewer
8
+ * columns, indented, with blank lines above and below. Overlays are laid over the whole terminal, so one that should
9
+ * keep to the padding is given it as its margin.
10
+ */
11
+ export declare const SCREEN_PADDING: {
12
+ readonly top: 1;
13
+ readonly right: 2;
14
+ readonly bottom: 1;
15
+ readonly left: 2;
16
+ };
17
+ /** The columns inside the padding. */
18
+ export declare function paddedColumns(columns: number): number;
19
+ /** The rows inside the padding. */
20
+ export declare function paddedRows(rows: number): number;
21
+ /** What a layout root pads - the root itself when it is not padded. */
22
+ export declare function unpadded(root: Component): Component;
23
+ /**
24
+ * pi's screen drawn inside the padding, from its next frame - and every layout root it is given after, so the chat
25
+ * narrowed beside the docked panel (`narrowChat`) is padded too. Once a screen: pi starts a new one when it changes how
26
+ * it draws, which is padded when it is passed in.
27
+ */
28
+ export declare function padScreen(tui: object): void;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Keeping the conversation's agent on its own tools.
3
+ *
4
+ * The agent has a shell, and turri has a command line - so it can build a module by
5
+ * running `turri regenerate` through bash, or start another pi with `pi -p`. That run is a
6
+ * second, headless turri: outside this conversation's job queue, its panel and
7
+ * `/halt`, on whatever model the command line picks, fighting the conversation's own jobs for
8
+ * the same build directories. It is never the right call unless the person asked for the
9
+ * command line, so the harness refuses it and names the tool to use instead.
10
+ */
11
+ /**
12
+ * What a shell command would run of turri or pi itself, if anything: the subcommand,
13
+ * or "pi" for another pi. `turri validate`, `drift` and `lineage` are not in it - they start no
14
+ * agent - and neither are setup and doctor.
15
+ */
16
+ export declare function selfInvocation(command: string): string | undefined;
17
+ /**
18
+ * Whether the person's message asks for the command line: it says so, or names the command.
19
+ * Read from their own words only - an agent that decided on its own does not get through.
20
+ */
21
+ export declare function askedForCommandLine(message: string): boolean;
22
+ /** What the agent is told when the call is refused. */
23
+ export declare function refusal(what: string): string;
24
+ /** The instruction that goes into the conversation's system prompt. */
25
+ export declare const TOOLS_NOT_CLI: string;
@@ -0,0 +1,43 @@
1
+ import { type Wall } from "./regenerate/os-sandbox.ts";
2
+ import { type ModelAccess } from "./model-check.ts";
3
+ /**
4
+ * `turri doctor`: whether this machine is ready for turri.
5
+ *
6
+ * It looks: Node, a bash to run commands with, which wall is up under the agents' commands - Seatbelt, bubblewrap, or
7
+ * the command check and the audit alone - and, in a project, whether this machine has the toolchains its modules'
8
+ * implementation reqs name and what its testing scripts run, and whether each model its config names answers with this
9
+ * machine's credentials - one short request a model (model-check.ts). It changes one thing, asked first: a plain `pi`
10
+ * that an older turri made load turri too is put back to plain pi.
11
+ */
12
+ type Level = "ok" | "missing" | "limited" | "info";
13
+ export interface Check {
14
+ level: Level;
15
+ name: string;
16
+ detail: string;
17
+ /** More about it, a line each, shown under the detail. */
18
+ more?: string[];
19
+ /** What to run or do about it. */
20
+ fix?: string;
21
+ }
22
+ /** bash: every agent command and every testing script runs with it, on every platform. */
23
+ export declare function checkShell(found?: boolean, platform?: NodeJS.Platform): Check;
24
+ /** git: each module's history, which every pulse starts from and lands in, and the spec diffs the work is planned from. */
25
+ export declare function checkGit(found?: boolean): Check;
26
+ /** Which wall is up under the agents' commands, and - where none starts - why, and how to give it one. */
27
+ export declare function checkSandbox(wall?: Wall, platform?: NodeJS.Platform): Check;
28
+ /**
29
+ * In a project: each module's toolchains, as its implementation reqs name them, against this machine's PATH; and each
30
+ * testing script against this machine - a program it names that is not here, a toolchain it runs that is not on the
31
+ * PATH. Nothing outside a project.
32
+ */
33
+ export declare function checkProject(project: string, machine?: {
34
+ has?: (command: string) => boolean;
35
+ exists?: (path: string) => boolean;
36
+ }): Check[];
37
+ /**
38
+ * In a project: each model its config names - with the stack chosen, that stack's over it - tried with one short
39
+ * request. A model that answers is ready; one that does not says what the provider said, and what to do about it.
40
+ */
41
+ export declare function checkProjectModels(project: string, access?: ModelAccess): Promise<Check[]>;
42
+ export declare function main(args: string[]): Promise<number>;
43
+ export {};
@@ -0,0 +1,46 @@
1
+ /**
2
+ * A skill's run as one line above the input: every phase by name, in order - the ones done ticked, the one it is in
3
+ * marked, the rest to come - and where that is, phase 2 of 4. `brood` and `spawn` take many turns of
4
+ * questions and spec changes; without it, nothing on the screen says how far through they are.
5
+ *
6
+ * brood ✓ intent ━━━━ ⠋ stack ──── ○ testing ──── ○ product phase 2 of 4
7
+ * stack - the language, frameworks, storage and architecture
8
+ *
9
+ * The second line says what the phase it is in does: a phase's name alone - `groundwork` - says little to someone
10
+ * who has not read the skill.
11
+ *
12
+ * A skill of one phase, or none, has nothing to be partway through: its line is the spinner and its name alone.
13
+ *
14
+ * ⠋ add-test-requirement
15
+ */
16
+ export interface SkillProgress {
17
+ /** The skill's name. */
18
+ skill: string;
19
+ /** Its phases, in order: the `###` headings under its `## Steps`. */
20
+ phases: string[];
21
+ /** The phase it is in, by its place in `phases`; -1 before the first has started. */
22
+ current: number;
23
+ /** The attempt at the current phase - more than 1 after a gate sent it back. */
24
+ attempt: number;
25
+ /** The spinner's frame, for the phase it is in. */
26
+ frame: string;
27
+ /** What the phase it is in does, in a line - from the skill's `metadata.phases`. */
28
+ about?: string;
29
+ }
30
+ export interface ProgressStyle {
31
+ heading(text: string): string;
32
+ done(text: string): string;
33
+ now(text: string): string;
34
+ muted(text: string): string;
35
+ }
36
+ /**
37
+ * The line across `width`: the skill's name at the left edge, where it is at the right edge, and between them its
38
+ * phases, centred in the whole width across its share of it, the joins stretched to fill it - clear of both ends. Too
39
+ * narrow for that, compact from the left, the phases first, as many as fit.
40
+ */
41
+ export declare function skillProgressLine(progress: SkillProgress, width: number, style?: ProgressStyle): string;
42
+ /**
43
+ * The line under it: the phase it is in, and what that phase does - centred under the phases, cut short where it
44
+ * does not fit. Nothing before the first phase starts, or for a phase the skill does not describe.
45
+ */
46
+ export declare function skillAboutLine(progress: SkillProgress, width: number, style?: ProgressStyle): string | undefined;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * The gate syntax.
3
+ *
4
+ * Everything below lives under `metadata:` in SKILL.md frontmatter, which the
5
+ * Agent Skills spec reserves for arbitrary key-values. A harness that does not
6
+ * understand it ignores it and the skill degrades to plain prose. Ours parses
7
+ * it and turns each gate into an edge in the graph, so the gate is enforced by
8
+ * code rather than by the model deciding to honour a paragraph.
9
+ */
10
+ export type OnFail = "retry" | "escalate" | "abort";
11
+ export type Quorum = "all" | "majority" | "any";
12
+ export interface ReviewSpec {
13
+ /** Path to the rubric, relative to the skill directory. */
14
+ rubric: string;
15
+ /** Independent judge sessions. Separate sessions, not n samples of one. */
16
+ judges: number;
17
+ /** "provider/model-id". Omitted: same model as the executor. */
18
+ model?: string;
19
+ /**
20
+ * Read-only tools the judges may use.
21
+ *
22
+ * A rubric that checks what is on disk needs to look at the disk. Judges get nothing by
23
+ * default, so a gate has to ask for this explicitly, and should ask only for tools that
24
+ * cannot change anything.
25
+ */
26
+ judgeTools: string[];
27
+ quorum: Quorum;
28
+ onFail: OnFail;
29
+ maxRetries: number;
30
+ }
31
+ /**
32
+ * A reviewer that runs while the step is still running.
33
+ *
34
+ * A gate at the end of a phase only finds a problem once the whole phase is written, and
35
+ * in a phase that builds as it goes that is far too late: functionality 7 is already built
36
+ * on top of the mistake in functionality 3. A continuous reviewer watches the work as it
37
+ * lands and reports between questions, so the interview never blocks on it and the final
38
+ * gate normally has nothing left to find.
39
+ *
40
+ * It only works for a step that *adds*: every pass reviews what appeared since the last
41
+ * clean one, and earlier work is not rewritten underneath it.
42
+ */
43
+ export interface ContinuousSpec {
44
+ /** Rubric for the live pass. Checks what is on disk, not the phase record. */
45
+ rubric: string;
46
+ /** Judges per pass. One by default: this runs many times, the final gate runs once. */
47
+ judges: number;
48
+ model?: string;
49
+ judgeTools: string[];
50
+ }
51
+ export interface GateSpec {
52
+ /** Name of the step this gate guards. */
53
+ after: string;
54
+ review: ReviewSpec;
55
+ /** Reviews the work as it is written, rather than waiting for the step to end. */
56
+ continuous?: ContinuousSpec;
57
+ }
58
+ export interface StepSpec {
59
+ name: string;
60
+ /** Prose under the step's heading - this is what the executor is told to do. */
61
+ instructions: string;
62
+ /** What the step does, in a short line for the person - shown under the skill's progress while it runs. */
63
+ about?: string;
64
+ gate?: GateSpec;
65
+ }
66
+ export interface Skill {
67
+ name: string;
68
+ description: string;
69
+ dir: string;
70
+ allowedTools: string[];
71
+ steps: StepSpec[];
72
+ /**
73
+ * Whether `write_spec_diff` needs an answer from the user between two new changes - one answer,
74
+ * one change, as an interview writes them. `metadata.paced: false` turns it off, for a skill that
75
+ * writes the specs on its own.
76
+ */
77
+ paced: boolean;
78
+ /**
79
+ * The phases the questioning level caps (`metadata.questioning`, questioning.ts): when there are any, the harness asks
80
+ * the level first, and each of them asks at most as many questions as it allows. Empty: no level is asked.
81
+ */
82
+ questioning: string[];
83
+ /**
84
+ * The steps where each answer is a spec change (`metadata.spec-diff-per-answer`): the next question waits until the
85
+ * last answer is written with `write_spec_diff`, and changes the agent decides itself need no answer before them.
86
+ */
87
+ diffPerAnswer: string[];
88
+ }
89
+ export declare function loadSkill(skillDir: string): Skill;
90
+ export declare function readRubric(skill: Skill, review: {
91
+ rubric: string;
92
+ }): string;
93
+ /**
94
+ * The status line while a skill runs: which phase, and how far through the skill it is.
95
+ * The phase is counted from 1 and includes the one in progress, so "3 of 6" means two are
96
+ * done. A retry after a failed gate says so - the same phase twice is not progress.
97
+ */
98
+ export declare function skillStatus(skill: Pick<Skill, "name" | "steps">, step: string, attempt: number): string;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Where skills are found: turri's own, in `genome/skills/` of this package, and the project's - `.turri/skills/` and
3
+ * `.agents/skills/` in the project's folder, a folder a skill with its SKILL.md, in the format of turri's own. A
4
+ * project's skill is run the same way: its steps, its gates and judges, the tools its frontmatter names.
5
+ *
6
+ * A name is one skill: turri's own come first, then `.turri/skills/`, then `.agents/skills/`, and a skill whose name is
7
+ * taken already is left out and said to be - a project's `brood` never quietly stands in for turri's.
8
+ */
9
+ import { type Skill } from "./skill.ts";
10
+ /** A project's skill folders, in the order they are read, relative to the project. */
11
+ export declare const PROJECT_SKILL_DIRS: string[];
12
+ export interface FoundSkill extends Skill {
13
+ /** turri's own, or the project's - then where in it, relative to the project: ".agents/skills/release-notes". */
14
+ origin: "turri" | {
15
+ project: string;
16
+ };
17
+ }
18
+ export interface SkillCatalogue {
19
+ skills: FoundSkill[];
20
+ /** Skills that did not parse, by where they are: a name for turri's own, the folder for a project's. */
21
+ pending: string[];
22
+ /** A project's skills left out because the name is taken, and by what. */
23
+ shadowed: string[];
24
+ }
25
+ /** Every skill turri can run in `projectDir`: its own from `packageRoot`, then the project's. */
26
+ export declare function discoverSkills(packageRoot: string, projectDir: string | undefined): SkillCatalogue;
27
+ /** A skill's line in cultivate's list: a project's says so, and where it is. */
28
+ export declare function catalogueLine(skill: FoundSkill): string;
29
+ /** What is said under the list about the skills it leaves out. */
30
+ export declare function leftOutNote(catalogue: SkillCatalogue): string;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ import { type ToolDefinition } from "@earendil-works/pi-coding-agent";
2
+ import { type WorkLoop } from "./regenerate/loop.ts";
3
+ import { type LiveReviewer } from "./tools.ts";
4
+ /** Whether a question has been answered since the last new spec change was written. */
5
+ export interface SpecPace {
6
+ /** A question was answered; `owesChange` false for one no change comes from - a phase's confirmation, a dismissal. */
7
+ answered(owesChange?: boolean): void;
8
+ /** Whether an answer has come since the last change, and is not written yet. */
9
+ owed(): boolean;
10
+ }
11
+ type AnyTool = ToolDefinition<any, any, any>;
12
+ /**
13
+ * Writing a spec change as a diff, and building it, as one step.
14
+ *
15
+ * In an interview whose unit of work is a spec change, every change is meant to be built the
16
+ * moment it is written. Left to two tool calls - change the file, then start the render - the
17
+ * second is the one that gets forgotten, and nothing notices. So the diff is applied and its
18
+ * build queued in the same call: a change cannot reach the specs without its render being
19
+ * started. A new change needs an answer since the last one; a correction does not.
20
+ */
21
+ export declare function createSpecDiffTools(options: {
22
+ loop: WorkLoop;
23
+ cwd: string;
24
+ liveReview?: LiveReviewer;
25
+ /** One new change per answer (the default); false, for a skill that writes the specs on its own. */
26
+ paced?: boolean;
27
+ /**
28
+ * Whether the phase the interview is in lets a change come without an answer before it: one where each answer owes a
29
+ * change instead (`owed`, refused a next question until it is written), and the agent writes what it decides itself.
30
+ */
31
+ free?: () => boolean;
32
+ }): {
33
+ tools: AnyTool[];
34
+ pace: SpecPace;
35
+ };
36
+ export {};
@@ -0,0 +1,58 @@
1
+ /**
2
+ * What a project's specs need of this machine: the toolchains its implementation reqs name, and whether the machine
3
+ * has them - the agents use this machine's toolchains and install none.
4
+ *
5
+ * The implementation reqs say what the code is written in and built with - "Python 3.12 and FastAPI", "a React app
6
+ * built with Vite", "Java 21 with Spring Boot". This reads them, and says which toolchain each needs and by which word -
7
+ * a hint for the person, not a verdict: a req can name a technology it does not use.
8
+ */
9
+ /** A toolchain the reqs name: its name, the commands any one of which shows it is there, and the words that named it. */
10
+ export interface StackNeed {
11
+ toolchain: string;
12
+ commands: string[];
13
+ words: string[];
14
+ }
15
+ /** The toolchains a text of implementation reqs names. */
16
+ export declare function stackOf(text: string): StackNeed[];
17
+ /** The implementation reqs of every module in a project's folder, as the parser has them - imports in. */
18
+ export declare function projectImplementationReqs(project: string): Array<{
19
+ module: string;
20
+ reqs: string[];
21
+ }>;
22
+ /** What a project's modules need of this machine, and which of it the machine lacks. */
23
+ export declare function stackOnMachine(project: string, has?: (command: string) => boolean): Array<{
24
+ module: string;
25
+ needs: StackNeed[];
26
+ missing: StackNeed[];
27
+ }>;
28
+ /**
29
+ * The standard library's languages, and what builds and tests their stacks here - every command needed. Python's stacks
30
+ * run through uv, which fetches Python itself; TypeScript's through npm; Java's through the Maven or Gradle wrapper,
31
+ * which needs only Java.
32
+ */
33
+ export declare const STDLIB_LANGUAGES: Array<{
34
+ language: string;
35
+ commands: string[];
36
+ }>;
37
+ /** Which of the standard library's languages this machine can build and test now, and which it lacks a command of. */
38
+ export declare function languagesOnMachine(has?: (command: string) => boolean): {
39
+ ready: typeof STDLIB_LANGUAGES;
40
+ missing: typeof STDLIB_LANGUAGES;
41
+ };
42
+ /**
43
+ * What a spec-writing agent and the conversation are told of this machine before a stack is chosen: the languages it
44
+ * can build now. The agents install nothing, so a stack in a language the machine lacks stops at its first build -
45
+ * offering one is offering a project that cannot be regenerated here until the person installs it.
46
+ */
47
+ export declare function toolchainsBlock(has?: (command: string) => boolean): string;
48
+ /**
49
+ * What in a testing script this machine does not have: a program named by a whole path that is not there - a
50
+ * macOS-only `/usr/libexec/java_home` on Linux - and a toolchain it runs that is not on the PATH. The script is the
51
+ * person's: this is for them to fix, before an agent ever sees it fail.
52
+ */
53
+ export declare function scriptAgainstMachine(text: string, machine?: {
54
+ has?: (command: string) => boolean;
55
+ exists?: (path: string) => boolean;
56
+ }): string[];
57
+ /** A testing script's text, or nothing when it cannot be read. */
58
+ export declare function readScript(path: string): string | undefined;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Choosing a stack: `config.turri.<stack>.yaml` laid over `config.turri.yaml` for everything built from then on - a
3
+ * backend's testing scripts, then a frontend's. `/stack` and the `stack` tool in the chat, `--stack <name>` on the
4
+ * command line (cli.ts). The choice itself is kept in config.ts (`chooseStack`), for the session only.
5
+ */
6
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
7
+ /** The stacks of the projects here: the folder's own, and those of the projects one folder down (specs kept in plain/). */
8
+ export declare function knownStacks(root: string): string[];
9
+ /** Choose `name` - or none - among the stacks here, and say what is chosen now; a stack the project has no file for is refused. */
10
+ export declare function switchStack(root: string, name: string | undefined): string;
11
+ /** /stack and the chat's stack tool. */
12
+ export declare function registerStacks(pi: ExtensionAPI, cwd: string): void;