@turri-dev/cli 0.1.6-next.336dd6e → 0.1.7-next.9aec40e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.pi/themes/turri-dark.json +1 -1
- package/README.md +1 -1
- package/dist/ask.d.ts +83 -0
- package/dist/brand.d.ts +13 -0
- package/dist/btw.d.ts +67 -0
- package/dist/chat-models.d.ts +22 -0
- package/dist/chat-skills.d.ts +17 -0
- package/dist/cli-options.d.ts +25 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.js +7 -1
- package/dist/config-command.d.ts +26 -0
- package/dist/config-edit.d.ts +69 -0
- package/dist/config.d.ts +321 -0
- package/dist/conform-cli.d.ts +1 -0
- package/dist/cultivate-cli.d.ts +1 -0
- package/dist/drift-cli.d.ts +1 -0
- package/dist/environment.d.ts +39 -0
- package/dist/environment.js +26 -0
- package/dist/extension.d.ts +147 -0
- package/dist/extension.js +36 -10
- package/dist/genome.d.ts +62 -0
- package/dist/graph.d.ts +52 -0
- package/dist/host.d.ts +20 -0
- package/dist/host.js +23 -0
- package/dist/identity.d.ts +2 -0
- package/dist/init-config.d.ts +106 -0
- package/dist/jellyfish.d.ts +25 -0
- package/dist/judge.d.ts +25 -0
- package/dist/lineage-cli.d.ts +1 -0
- package/dist/loop-cli.d.ts +34 -0
- package/dist/loop-tools.d.ts +80 -0
- package/dist/mature-tools.d.ts +53 -0
- package/dist/model-check.d.ts +42 -0
- package/dist/module-report.d.ts +9 -0
- package/dist/pi-launch.d.ts +1 -0
- package/dist/pi.d.ts +246 -0
- package/dist/picture-row.d.ts +10 -0
- package/dist/plain/check.d.ts +23 -0
- package/dist/plain/concepts.d.ts +11 -0
- package/dist/plain/errors.d.ts +22 -0
- package/dist/plain/files.d.ts +12 -0
- package/dist/plain/highlight.d.ts +42 -0
- package/dist/plain/index.d.ts +15 -0
- package/dist/plain/liquid.d.ts +22 -0
- package/dist/plain/markdown.d.ts +74 -0
- package/dist/plain/parser.d.ts +87 -0
- package/dist/plain/spec.d.ts +90 -0
- package/dist/plain/stdlib.d.ts +14 -0
- package/dist/question-dialog.d.ts +51 -0
- package/dist/questioning.d.ts +55 -0
- package/dist/quiet.d.ts +34 -0
- package/dist/regenerate/activity.d.ts +207 -0
- package/dist/regenerate/agent-env.d.ts +44 -0
- package/dist/regenerate/agent-shell.d.ts +49 -0
- package/dist/regenerate/arbiter.d.ts +68 -0
- package/dist/regenerate/audit.d.ts +69 -0
- package/dist/regenerate/cases.d.ts +9 -0
- package/dist/regenerate/change-view.d.ts +18 -0
- package/dist/regenerate/choose-conformance.d.ts +32 -0
- package/dist/regenerate/chunk-plan.d.ts +164 -0
- package/dist/regenerate/codebase.d.ts +21 -0
- package/dist/regenerate/command-check.d.ts +40 -0
- package/dist/regenerate/compaction.d.ts +13 -0
- package/dist/regenerate/confine.d.ts +62 -0
- package/dist/regenerate/conform.d.ts +63 -0
- package/dist/regenerate/conformance-agents.d.ts +405 -0
- package/dist/regenerate/conformance.d.ts +189 -0
- package/dist/regenerate/conversation-writes.d.ts +10 -0
- package/dist/regenerate/credentials.d.ts +18 -0
- package/dist/regenerate/debug-log.d.ts +54 -0
- package/dist/regenerate/debugger.d.ts +31 -0
- package/dist/regenerate/delivery.d.ts +6 -0
- package/dist/regenerate/diagnose-job.d.ts +2 -0
- package/dist/regenerate/diagnose.d.ts +46 -0
- package/dist/regenerate/diff-view.d.ts +61 -0
- package/dist/regenerate/drift.d.ts +84 -0
- package/dist/regenerate/engine.d.ts +52 -0
- package/dist/regenerate/environment-error.d.ts +136 -0
- package/dist/regenerate/job-log.d.ts +28 -0
- package/dist/regenerate/job-panel.d.ts +122 -0
- package/dist/regenerate/jobs-tree.d.ts +71 -0
- package/dist/regenerate/jobs.d.ts +186 -0
- package/dist/regenerate/lanes.d.ts +16 -0
- package/dist/regenerate/lineage.d.ts +95 -0
- package/dist/regenerate/list-arguments.d.ts +21 -0
- package/dist/regenerate/loop.d.ts +392 -0
- package/dist/regenerate/masking.d.ts +19 -0
- package/dist/regenerate/notes.d.ts +159 -0
- package/dist/regenerate/os-sandbox.d.ts +89 -0
- package/dist/regenerate/panel-dock.d.ts +23 -0
- package/dist/regenerate/paths.d.ts +92 -0
- package/dist/regenerate/plain-palette.d.ts +34 -0
- package/dist/regenerate/prompt.d.ts +192 -0
- package/dist/regenerate/records.d.ts +88 -0
- package/dist/regenerate/regenerate.d.ts +108 -0
- package/dist/regenerate/regression.d.ts +58 -0
- package/dist/regenerate/release.d.ts +123 -0
- package/dist/regenerate/requires.d.ts +96 -0
- package/dist/regenerate/resources.d.ts +53 -0
- package/dist/regenerate/self-check.d.ts +29 -0
- package/dist/regenerate/services.d.ts +28 -0
- package/dist/regenerate/shell.d.ts +91 -0
- package/dist/regenerate/side-panel.d.ts +57 -0
- package/dist/regenerate/spec-map-view.d.ts +139 -0
- package/dist/regenerate/spec-map.d.ts +138 -0
- package/dist/regenerate/spec-steps.d.ts +137 -0
- package/dist/regenerate/state.d.ts +20 -0
- package/dist/regenerate/steer.d.ts +34 -0
- package/dist/regenerate/steering.d.ts +56 -0
- package/dist/regenerate/stop-report.d.ts +43 -0
- package/dist/regenerate/submit-work.d.ts +42 -0
- package/dist/regenerate/test-kinds.d.ts +42 -0
- package/dist/regenerate/test-scripts.d.ts +221 -0
- package/dist/regenerate/testing.d.ts +141 -0
- package/dist/regenerate/transcript-view.d.ts +21 -0
- package/dist/regenerate/unit-gate.d.ts +27 -0
- package/dist/regenerate/unit-review.d.ts +152 -0
- package/dist/regenerate/web.d.ts +61 -0
- package/dist/regenerate/workspace.d.ts +12 -0
- package/dist/regenerate-cli.d.ts +1 -0
- package/dist/release-agent.d.ts +26 -0
- package/dist/release-cli.d.ts +1 -0
- package/dist/release-tools.d.ts +38 -0
- package/dist/resume-line.d.ts +2 -0
- package/dist/review/live.d.ts +72 -0
- package/dist/rules.d.ts +20 -0
- package/dist/run-report.d.ts +88 -0
- package/dist/screen-padding.d.ts +28 -0
- package/dist/self-invocation.d.ts +25 -0
- package/dist/setup.d.ts +43 -0
- package/dist/skill-progress.d.ts +46 -0
- package/dist/skill.d.ts +98 -0
- package/dist/skills.d.ts +30 -0
- package/dist/spec-diff-cli.d.ts +1 -0
- package/dist/spec-tools.d.ts +36 -0
- package/dist/stack.d.ts +58 -0
- package/dist/stacks.d.ts +12 -0
- package/dist/stdlib.d.ts +27 -0
- package/dist/terminal-images.d.ts +2 -0
- package/dist/tools.d.ts +101 -0
- package/dist/validate-cli.d.ts +1 -0
- package/docs/README.md +2 -0
- package/docs/hosting.md +51 -0
- package/docs/installation.md +1 -1
- package/docs/theming.md +2 -2
- package/package.json +8 -1
package/dist/extension.js
CHANGED
|
@@ -80,8 +80,17 @@ const PACKAGE_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
|
80
80
|
*/
|
|
81
81
|
export function askUserVia(ctx, onExchange,
|
|
82
82
|
/** Given, "/btw ..." typed as an answer is a side question: answered with this, and the question asked again. */
|
|
83
|
-
aside
|
|
83
|
+
aside,
|
|
84
|
+
/** A host's own dialogs (TurriHost.dialogs), the question put in them instead of the terminal's. */
|
|
85
|
+
dialogs) {
|
|
84
86
|
return async (request) => {
|
|
87
|
+
if (dialogs !== undefined) {
|
|
88
|
+
const asking = (asked) => runAsk(asked, dialogs);
|
|
89
|
+
const bare = { ...request, said: undefined };
|
|
90
|
+
const answer = aside !== undefined ? await askWithAsides(bare, asking, aside) : await asking(bare);
|
|
91
|
+
onExchange(request.question, answer, [request.context, request.said].map((part) => part?.trim() ?? "").filter((part) => part !== "").join("\n\n") || undefined);
|
|
92
|
+
return answer;
|
|
93
|
+
}
|
|
85
94
|
if (!ctx.hasUI) {
|
|
86
95
|
return "No user is available to answer. Decide yourself, and say in your report what you assumed.";
|
|
87
96
|
}
|
|
@@ -156,9 +165,9 @@ function hookPeekKey(ctx) {
|
|
|
156
165
|
* pick from, several at once, "Something else…" to type instead, "Chat about this" to talk it over in
|
|
157
166
|
* the chat first - so a question with a handful of answers is picked, not typed.
|
|
158
167
|
*/
|
|
159
|
-
export function chatAskUserTool() {
|
|
168
|
+
export function chatAskUserTool(dialogs) {
|
|
160
169
|
let conversation;
|
|
161
|
-
const tool = createAskUserTool((request) => (conversation !== undefined ? askUserVia(conversation, () => { })(request) : Promise.resolve("No user is available to answer.")), undefined, undefined, undefined, "reply");
|
|
170
|
+
const tool = createAskUserTool((request) => (conversation !== undefined ? askUserVia(conversation, () => { }, undefined, dialogs)(request) : Promise.resolve("No user is available to answer.")), undefined, undefined, undefined, "reply");
|
|
162
171
|
return {
|
|
163
172
|
...tool,
|
|
164
173
|
promptGuidelines: [
|
|
@@ -171,7 +180,14 @@ export function chatAskUserTool() {
|
|
|
171
180
|
},
|
|
172
181
|
};
|
|
173
182
|
}
|
|
174
|
-
|
|
183
|
+
/** The host each conversation's jobs report to, for the work loop made for them later. */
|
|
184
|
+
const hosts = new WeakMap();
|
|
185
|
+
/** turri's conversation as an extension of pi's, for a host that runs it itself: see TurriHost. */
|
|
186
|
+
export function createTurriExtension(host = {}) {
|
|
187
|
+
return (pi) => turriExtension(pi, host);
|
|
188
|
+
}
|
|
189
|
+
export default createTurriExtension();
|
|
190
|
+
function turriExtension(pi, host) {
|
|
175
191
|
dropNoise();
|
|
176
192
|
const cwd = process.cwd();
|
|
177
193
|
// turri's own skills, and the project's - .turri/skills, .agents/skills (skills.ts).
|
|
@@ -189,7 +205,10 @@ export default function (pi) {
|
|
|
189
205
|
const deployState = createDeployState();
|
|
190
206
|
const jobs = createJobRegistry({
|
|
191
207
|
// The loop's plan changed - a module being planned, or planned: the live panel shows it at once.
|
|
192
|
-
onNotify: () =>
|
|
208
|
+
onNotify: () => {
|
|
209
|
+
panel.refresh();
|
|
210
|
+
host.onPlanChange?.();
|
|
211
|
+
},
|
|
193
212
|
// A job that fails is run once more; failing again, it and every job behind it fail, and the
|
|
194
213
|
// harness - which can read the skills, the rules, the code, the tests and turri itself -
|
|
195
214
|
// works out why, and what has to be done.
|
|
@@ -208,9 +227,13 @@ export default function (pi) {
|
|
|
208
227
|
});
|
|
209
228
|
return diagnosis !== undefined ? describeDiagnosis(diagnosis) : undefined;
|
|
210
229
|
},
|
|
211
|
-
onChange: () =>
|
|
230
|
+
onChange: (job) => {
|
|
231
|
+
panel.refresh();
|
|
232
|
+
host.onJobChange?.(job);
|
|
233
|
+
},
|
|
212
234
|
onFinish: (job) => {
|
|
213
235
|
panel.refresh();
|
|
236
|
+
host.onJobChange?.(job);
|
|
214
237
|
// Queued again as another job: that one says how it went.
|
|
215
238
|
if (job.replacedBy !== undefined)
|
|
216
239
|
return;
|
|
@@ -236,6 +259,8 @@ export default function (pi) {
|
|
|
236
259
|
}, { triggerTurn: true, deliverAs: "followUp" });
|
|
237
260
|
},
|
|
238
261
|
});
|
|
262
|
+
hosts.set(jobs, host);
|
|
263
|
+
host.onJobs?.(jobs);
|
|
239
264
|
// The ***plain rules go into the system prompt in full, every turn. The block is
|
|
240
265
|
// identical each time, so it sits inside the cached prefix rather than invalidating it.
|
|
241
266
|
// With them, the instruction to do turri's work with its tools rather than its command
|
|
@@ -375,7 +400,7 @@ export default function (pi) {
|
|
|
375
400
|
registerBranding(pi, discoveredSkills);
|
|
376
401
|
registerInterviewLog(pi);
|
|
377
402
|
registerJobTools(pi, jobs, cwd);
|
|
378
|
-
registerCheckPlain(pi, cwd);
|
|
403
|
+
registerCheckPlain(pi, cwd, host.dialogs);
|
|
379
404
|
registerRegenerate(pi, cwd, jobs);
|
|
380
405
|
registerDrift(pi, cwd, jobs);
|
|
381
406
|
registerGlimpse(pi, cwd, jobs);
|
|
@@ -549,7 +574,7 @@ export default function (pi) {
|
|
|
549
574
|
push(` Q: ${question}`);
|
|
550
575
|
push(` A: ${answer}`);
|
|
551
576
|
recordExchange(pi, { skill: skill.name, question, answer, ...(said !== undefined && said.trim() !== "" ? { said: said.trim() } : {}) });
|
|
552
|
-
}, (question) => askOnTheSide(pi, ctx, cwd, jobs, question)),
|
|
577
|
+
}, (question) => askOnTheSide(pi, ctx, cwd, jobs, question), host.dialogs),
|
|
553
578
|
model: conversationModel(ctx),
|
|
554
579
|
// Renders queued by a step land in the same registry as everything else, so
|
|
555
580
|
// pulses sees them without knowing a skill started them.
|
|
@@ -1326,6 +1351,7 @@ function workLoopFor(jobs, cwd, ctx) {
|
|
|
1326
1351
|
});
|
|
1327
1352
|
shared = { loop, from };
|
|
1328
1353
|
workLoops.set(jobs, shared);
|
|
1354
|
+
hosts.get(jobs)?.onWorkLoop?.(loop);
|
|
1329
1355
|
}
|
|
1330
1356
|
shared.from.ctx = ctx;
|
|
1331
1357
|
return shared.loop;
|
|
@@ -1932,9 +1958,9 @@ function jobLines(all) {
|
|
|
1932
1958
|
* so it needs no external tool, no API key and no credits - and it is the only honest way
|
|
1933
1959
|
* to answer "is this spec valid?" without generating anything.
|
|
1934
1960
|
*/
|
|
1935
|
-
function registerCheckPlain(pi, cwd) {
|
|
1961
|
+
function registerCheckPlain(pi, cwd, dialogs) {
|
|
1936
1962
|
pi.registerTool(checkPlainTool(cwd));
|
|
1937
|
-
pi.registerTool(chatAskUserTool());
|
|
1963
|
+
pi.registerTool(chatAskUserTool(dialogs));
|
|
1938
1964
|
// The web, for the conversation too - a library's documentation, an API, an error; curl is its
|
|
1939
1965
|
// bash. Run here, like the rendering agents' (see render/web.ts).
|
|
1940
1966
|
for (const tool of createWebTools())
|
package/dist/genome.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type Reporter } from "./graph.ts";
|
|
2
|
+
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { type Skill } from "./skill.ts";
|
|
4
|
+
import { type AskUser } from "./tools.ts";
|
|
5
|
+
import type { JobRegistry } from "./regenerate/jobs.ts";
|
|
6
|
+
import type { WorkLoop } from "./regenerate/loop.ts";
|
|
7
|
+
export interface EscalationRequest {
|
|
8
|
+
step: string;
|
|
9
|
+
artifact: string;
|
|
10
|
+
critique: string;
|
|
11
|
+
question: string;
|
|
12
|
+
}
|
|
13
|
+
export interface RunSkillResult {
|
|
14
|
+
skill: Skill;
|
|
15
|
+
ok: boolean;
|
|
16
|
+
artifacts: Record<string, string>;
|
|
17
|
+
/** The last step's artifact - what the caller usually wants. */
|
|
18
|
+
output: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Run one skill to completion. The only entry point: the CLI and the Pi extension
|
|
22
|
+
* both go through here, so a skill behaves identically whether a human launched it
|
|
23
|
+
* or a conversational agent decided to.
|
|
24
|
+
*/
|
|
25
|
+
export declare function runSkill(opts: {
|
|
26
|
+
skillDir: string;
|
|
27
|
+
cwd: string;
|
|
28
|
+
runDir: string;
|
|
29
|
+
report: Reporter;
|
|
30
|
+
/** Called when a gate cannot be satisfied. Return true to accept the artifact anyway. */
|
|
31
|
+
escalate: (request: EscalationRequest) => Promise<boolean>;
|
|
32
|
+
/** Lets a step interview the user. Omitted: the skill runs with nobody to ask. */
|
|
33
|
+
askUser?: AskUser;
|
|
34
|
+
/** "provider/model-id" for the executor and the judges. Omitted: pi's default. */
|
|
35
|
+
model?: string;
|
|
36
|
+
/** Lets steps build functionalities in the background while the interview continues. */
|
|
37
|
+
renderQueue?: WorkLoop;
|
|
38
|
+
/** What the user has already said, so the skill does not re-ask it. */
|
|
39
|
+
context?: string;
|
|
40
|
+
/** Pick up the run of this skill that was interrupted here, rather than start a new one. */
|
|
41
|
+
resume?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Tools the conversation's agent has that the skill's agent needs as well, because the person
|
|
44
|
+
* talks to the skill's agent while it runs - choosing the render model, for one.
|
|
45
|
+
*/
|
|
46
|
+
sharedTools?: ToolDefinition[];
|
|
47
|
+
/**
|
|
48
|
+
* The conversation's tools that act on the work loop (loop-tools.ts): drift, for every skill with the loop - how its
|
|
49
|
+
* builds stand - and regenerate and halt for a skill that names them in its allowed-tools.
|
|
50
|
+
*/
|
|
51
|
+
loopTools?: ToolDefinition[];
|
|
52
|
+
/** The jobs the render queue runs, for a skill that waits for its builds and reads what they recorded. */
|
|
53
|
+
jobs?: JobRegistry;
|
|
54
|
+
}): Promise<RunSkillResult>;
|
|
55
|
+
/** Whether there is a run of this skill here that was interrupted, and could be resumed. */
|
|
56
|
+
export declare function interruptedRun(runDir: string, skillName: string): {
|
|
57
|
+
step?: string;
|
|
58
|
+
updatedAt: string;
|
|
59
|
+
} | undefined;
|
|
60
|
+
/** Human-readable one-liner per step, for logs and tool output. */
|
|
61
|
+
export declare function describeGates(skill: Skill): string[];
|
|
62
|
+
export type { Reporter };
|
package/dist/graph.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type LiveReviewer } from "./review/live.ts";
|
|
3
|
+
import { type Skill } from "./skill.ts";
|
|
4
|
+
export type Decision = "advance" | "retry" | "escalate" | "abort";
|
|
5
|
+
export declare const GenomeState: import("@langchain/langgraph").AnnotationRoot<{
|
|
6
|
+
artifacts: import("@langchain/langgraph").BaseChannel<Record<string, string>, Record<string, string> | import("@langchain/langgraph").OverwriteValue<Record<string, string>>, unknown>;
|
|
7
|
+
attempts: import("@langchain/langgraph").BaseChannel<Record<string, number>, Record<string, number> | import("@langchain/langgraph").OverwriteValue<Record<string, number>>, unknown>;
|
|
8
|
+
/** Judge feedback for the step currently being retried. */
|
|
9
|
+
critique: import("@langchain/langgraph").BaseChannel<string, string | import("@langchain/langgraph").OverwriteValue<string>, unknown>;
|
|
10
|
+
decision: import("@langchain/langgraph").BaseChannel<Decision, Decision | import("@langchain/langgraph").OverwriteValue<Decision>, unknown>;
|
|
11
|
+
}>;
|
|
12
|
+
export type GenomeStateType = typeof GenomeState.State;
|
|
13
|
+
export interface Reporter {
|
|
14
|
+
step(name: string, attempt: number): void;
|
|
15
|
+
artifact(path: string, body: string): void;
|
|
16
|
+
gate(name: string, judges: number): void;
|
|
17
|
+
/** One continuous-review pass finished while the step was still running. */
|
|
18
|
+
livePass?(step: string, scope: string, findings: number): void;
|
|
19
|
+
/** The continuous reviewer's closing word, read at the gate. */
|
|
20
|
+
liveReview?(name: string, passes: number, findings: number): void;
|
|
21
|
+
judge(index: number, model: string, verdict: string, violations: number): void;
|
|
22
|
+
outcome(name: string, decision: Decision, critique: string): void;
|
|
23
|
+
/** One thing the skill's agent just did - a tool call, as one short line - for a view of it while it works. */
|
|
24
|
+
activity?(line: string): void;
|
|
25
|
+
/** The skill's agent's words as they stream, from the start of each reply: `fresh` when a new one begins. */
|
|
26
|
+
said?(delta: string, fresh: boolean): void;
|
|
27
|
+
}
|
|
28
|
+
/** How many times a step whose turn ends on a question in its reply is sent back to carry on. */
|
|
29
|
+
export declare const QUESTION_NUDGES = 2;
|
|
30
|
+
/** Whether a reply ends on a question: its last line, its markdown left aside, ends with a question mark. */
|
|
31
|
+
export declare function endsOnQuestion(reply: string): boolean;
|
|
32
|
+
export declare function compileSkill(opts: {
|
|
33
|
+
skill: Skill;
|
|
34
|
+
cwd: string;
|
|
35
|
+
runDir: string;
|
|
36
|
+
executor: AgentSession;
|
|
37
|
+
report: Reporter;
|
|
38
|
+
/** Default model for the judges, when a gate does not name one of its own. */
|
|
39
|
+
model?: string;
|
|
40
|
+
/** What the user has already said, carried in from the conversation that started this. */
|
|
41
|
+
context?: string;
|
|
42
|
+
/** Reviewers already reading along, by step name. Their findings gate the step too. */
|
|
43
|
+
liveReviewers?: Map<string, LiveReviewer>;
|
|
44
|
+
/** Start at this step instead of the first: a resumed run, picking up where it stopped. */
|
|
45
|
+
startAt?: string;
|
|
46
|
+
/** Called as each step starts, with the state it starts from - what a resumed run restores. */
|
|
47
|
+
onStepStart?: (step: string, state: GenomeStateType) => void;
|
|
48
|
+
/** Called once a step is reported started, before its agent is prompted: the harness's own question goes here. */
|
|
49
|
+
beforeStep?: (step: string) => Promise<void>;
|
|
50
|
+
/** What the harness tells the agent as a step starts, beside its instructions - the questioning level, say. */
|
|
51
|
+
stepNote?: (step: string) => string | undefined;
|
|
52
|
+
}): any;
|
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turri for a host that runs it itself - a desktop app, an editor - imported as `@turri-dev/cli/host`: the
|
|
3
|
+
* conversation as pi's extension, with the host told of its jobs and asking its questions (createTurriExtension), and
|
|
4
|
+
* what a host shows beside it - a spec parsed, checked and coloured, how far each functional spec is built, the work a
|
|
5
|
+
* spec change would plan. It is turri's own code, as the terminal runs it; a 0.x release may change any of it.
|
|
6
|
+
*/
|
|
7
|
+
export { createTurriExtension, type TurriHost } from "./extension.ts";
|
|
8
|
+
export type { AskUserIO } from "./ask.ts";
|
|
9
|
+
/** pi itself, as turri's extension is built against it: a host makes its session with this one, not another copy. */
|
|
10
|
+
export * as pi from "@earendil-works/pi-coding-agent";
|
|
11
|
+
export { configForSpec, hasProjectConfig, modelSpec, projectModels, readProjectConfig, stacksOf, type ProjectConfig } from "./config.ts";
|
|
12
|
+
export { MODULES_DIR, projectDirOf, resolveTemplateDirs } from "./regenerate/paths.ts";
|
|
13
|
+
export { checkPlainFile, type CheckResult } from "./plain/check.ts";
|
|
14
|
+
export { plainFileParser } from "./plain/parser.ts";
|
|
15
|
+
export { FUNCTIONAL_REQUIREMENTS, iterateFunctionalSpecs } from "./plain/spec.ts";
|
|
16
|
+
export { highlightLines, roleOf, type PlainRole } from "./plain/highlight.ts";
|
|
17
|
+
export { createWorkLoop, describeModules, functionalityCompletion, promptOf, type DriftReport, type ModuleStatus, type WorkLoop } from "./regenerate/loop.ts";
|
|
18
|
+
export { createJobRegistry, defaultParallelJobs, type Job, type JobRegistry } from "./regenerate/jobs.ts";
|
|
19
|
+
export { describeTurnIncident, onModelEvent, type TranscriptEntry } from "./regenerate/activity.ts";
|
|
20
|
+
export { sweepOrphanedGroups } from "./regenerate/shell.ts";
|
package/dist/host.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turri for a host that runs it itself - a desktop app, an editor - imported as `@turri-dev/cli/host`: the
|
|
3
|
+
* conversation as pi's extension, with the host told of its jobs and asking its questions (createTurriExtension), and
|
|
4
|
+
* what a host shows beside it - a spec parsed, checked and coloured, how far each functional spec is built, the work a
|
|
5
|
+
* spec change would plan. It is turri's own code, as the terminal runs it; a 0.x release may change any of it.
|
|
6
|
+
*/
|
|
7
|
+
// The conversation.
|
|
8
|
+
export { createTurriExtension } from "./extension.js";
|
|
9
|
+
/** pi itself, as turri's extension is built against it: a host makes its session with this one, not another copy. */
|
|
10
|
+
export * as pi from "@earendil-works/pi-coding-agent";
|
|
11
|
+
// The project and its settings.
|
|
12
|
+
export { configForSpec, hasProjectConfig, modelSpec, projectModels, readProjectConfig, stacksOf } from "./config.js";
|
|
13
|
+
export { MODULES_DIR, projectDirOf, resolveTemplateDirs } from "./regenerate/paths.js";
|
|
14
|
+
// A spec: parsed, checked, coloured.
|
|
15
|
+
export { checkPlainFile } from "./plain/check.js";
|
|
16
|
+
export { plainFileParser } from "./plain/parser.js";
|
|
17
|
+
export { FUNCTIONAL_REQUIREMENTS, iterateFunctionalSpecs } from "./plain/spec.js";
|
|
18
|
+
export { highlightLines, roleOf } from "./plain/highlight.js";
|
|
19
|
+
// The work: what is built, what is left, and the jobs that build it.
|
|
20
|
+
export { createWorkLoop, describeModules, functionalityCompletion, promptOf } from "./regenerate/loop.js";
|
|
21
|
+
export { createJobRegistry, defaultParallelJobs } from "./regenerate/jobs.js";
|
|
22
|
+
export { describeTurnIncident, onModelEvent } from "./regenerate/activity.js";
|
|
23
|
+
export { sweepOrphanedGroups } from "./regenerate/shell.js";
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `turri init-config`: a project's first config.turri.yaml - the two models, a strong one to talk to and a smaller one to
|
|
3
|
+
* write the code - so a new project starts with a choice that works rather than an empty file. It asks which vendor's
|
|
4
|
+
* models you use and fills in theirs. Every other setting is written out with the value turri uses for it (DEFAULTS),
|
|
5
|
+
* so the file says how the project is built; the optional ones, which have no value of their own - the testing
|
|
6
|
+
* scripts, `module`, `judge-model`, `models` - are there commented out, with an example. docs/config.md has them all.
|
|
7
|
+
*
|
|
8
|
+
* Then it says what to do next: where the file is - turri reads it from that folder and the folders below it, nowhere
|
|
9
|
+
* else - and how to sign in to the vendor so pi, which turri runs on, can reach the models.
|
|
10
|
+
*
|
|
11
|
+
* Amazon Bedrock serves Anthropic's and OpenAI's models in a geography each - an inference profile, `eu.`, `us.`,
|
|
12
|
+
* `global.` - so it asks which of the two, and where. A config.turri.yaml already there keeps everything but the two models; a new one takes
|
|
13
|
+
* from a config.yaml beside it the keys turri reads too.
|
|
14
|
+
*/
|
|
15
|
+
/** A vendor whose models the quick start config is filled in with. */
|
|
16
|
+
export type Vendor = "anthropic" | "openai" | "google" | "amazon-bedrock" | "other";
|
|
17
|
+
/** Whose models Amazon Bedrock serves here. */
|
|
18
|
+
export type BedrockMaker = "anthropic" | "openai";
|
|
19
|
+
/** Amazon Bedrock's choice: whose models, and the geography of the inference profile they run in. */
|
|
20
|
+
export interface BedrockChoice {
|
|
21
|
+
maker: BedrockMaker;
|
|
22
|
+
region: string;
|
|
23
|
+
}
|
|
24
|
+
/** The geographies Bedrock serves each maker's two models in, as pi knows them - the first the default. */
|
|
25
|
+
export declare const BEDROCK_REGIONS: Record<BedrockMaker, string[]>;
|
|
26
|
+
/** Bedrock's models for a maker in a geography: Opus 5.5 and Haiku 5.5, or GPT-6 Astra and Luna. */
|
|
27
|
+
export declare function bedrockModels(choice: BedrockChoice): {
|
|
28
|
+
harness: string;
|
|
29
|
+
coding: string;
|
|
30
|
+
strong: string;
|
|
31
|
+
};
|
|
32
|
+
/** Bedrock's default: Anthropic's models, anywhere. */
|
|
33
|
+
export declare const BEDROCK_DEFAULT: BedrockChoice;
|
|
34
|
+
interface VendorPreset {
|
|
35
|
+
label: string;
|
|
36
|
+
/** The model you talk to: it writes the specs and plans the work. */
|
|
37
|
+
harness: string;
|
|
38
|
+
/** The smaller, cheaper one that writes the code. */
|
|
39
|
+
coding: string;
|
|
40
|
+
/** A strong model for the commented-out judge, reviewer and debugger examples. */
|
|
41
|
+
strong: string;
|
|
42
|
+
/** The environment variables pi reads the vendor's keys from - pi's own names (pi.dev, providers). */
|
|
43
|
+
keys: Array<{
|
|
44
|
+
vendor: string;
|
|
45
|
+
variable: string;
|
|
46
|
+
}>;
|
|
47
|
+
}
|
|
48
|
+
export declare const VENDORS: Record<Vendor, VendorPreset>;
|
|
49
|
+
/**
|
|
50
|
+
* A config.turri.yaml with its two models set to these lines, and nothing else changed: each key's line - and the lines
|
|
51
|
+
* indented under it, when it is written as a block - replaced where it is, or put at the top when it is not there.
|
|
52
|
+
*/
|
|
53
|
+
export declare function withModels(text: string, lines: [string, string]): string;
|
|
54
|
+
/**
|
|
55
|
+
* What turri does with each setting the config leaves out, written into the quick start config so it says so. They
|
|
56
|
+
* must stay what the code falls back to - tests/init-config.test.ts holds them to the constants they mirror.
|
|
57
|
+
*/
|
|
58
|
+
export declare const DEFAULTS: {
|
|
59
|
+
readonly conformanceTests: false;
|
|
60
|
+
readonly review: false;
|
|
61
|
+
readonly mutationTesting: true;
|
|
62
|
+
readonly allowToStopJobs: true;
|
|
63
|
+
readonly releaseAfterPulse: false;
|
|
64
|
+
readonly testingScriptsRunOn: "in-place";
|
|
65
|
+
readonly turnLimit: 120;
|
|
66
|
+
readonly compactAt: 100000;
|
|
67
|
+
readonly maxSpecsPerPulse: 3;
|
|
68
|
+
readonly maxLinesPerPulse: 120;
|
|
69
|
+
};
|
|
70
|
+
/** How many pulses run at once without a setting: half this machine's cores, one to four - as regenerate/jobs.ts has it. */
|
|
71
|
+
export declare function defaultParallel(cores?: number): number;
|
|
72
|
+
/** The quick start config.turri.yaml for a vendor's models - for Bedrock, the maker and geography chosen. */
|
|
73
|
+
export declare function quickStartConfig(vendor?: Vendor, bedrock?: BedrockChoice): string;
|
|
74
|
+
/** The quick start config for the mix of vendors: what turri wrote before it asked. */
|
|
75
|
+
export declare const QUICK_START_CONFIG: string;
|
|
76
|
+
/** A vendor from what was typed: its number in the list, or its name. */
|
|
77
|
+
export declare function vendorOf(answer: string): Vendor | undefined;
|
|
78
|
+
/** Bedrock's maker from what was typed: its number - 1 Anthropic, 2 OpenAI - or its name. */
|
|
79
|
+
export declare function bedrockMakerOf(answer: string): BedrockMaker | undefined;
|
|
80
|
+
/** A geography the maker's models are served in, from what was typed: its number in the list, or its name. */
|
|
81
|
+
export declare function bedrockRegionOf(maker: BedrockMaker, answer: string): string | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* What a config.yaml in `folder` says that turri reads too - another tool's testing scripts, say - by turri's names
|
|
84
|
+
* (`template_dir` read as `template-dir`), in the file's order; and the keys it leaves, turri reading none of them. The
|
|
85
|
+
* two models are the vendor's, so they are not taken.
|
|
86
|
+
*/
|
|
87
|
+
export declare function sharedWithLegacy(folder: string): {
|
|
88
|
+
carried: Array<[string, unknown]>;
|
|
89
|
+
left: string[];
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Write the quick start config.turri.yaml in `dir`, with what a config.yaml beside it says that turri reads too. One already
|
|
93
|
+
* there keeps everything but its two models, which are set to the vendor's - `force` writes the whole of it again. Says
|
|
94
|
+
* what it did.
|
|
95
|
+
*/
|
|
96
|
+
export declare function initConfig(dir: string, options?: {
|
|
97
|
+
force?: boolean;
|
|
98
|
+
vendor?: Vendor;
|
|
99
|
+
bedrock?: BedrockChoice;
|
|
100
|
+
}): {
|
|
101
|
+
written: boolean;
|
|
102
|
+
message: string;
|
|
103
|
+
};
|
|
104
|
+
/** `turri init-config [--vendor <vendor>] [--maker <maker>] [--region <geography>] [--force] [folder]`. */
|
|
105
|
+
export declare function main(args: string[]): Promise<number>;
|
|
106
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The jellyfish turri is named after, in pixels: a pink bell, an orange gut, neon tentacles coming apart into code at
|
|
3
|
+
* their tips - the design system's colours, swimming a stroke a loop. Drawn at two sizes from one design: the banner's
|
|
4
|
+
* pictures are rendered from the large one (scripts/jellyfish-frames.mjs writes assets/jellyfish/), and where a terminal
|
|
5
|
+
* shows no images the small one is drawn here in half blocks, two pixels a cell - drawn at its size, not shrunk, so no
|
|
6
|
+
* pixel of it is lost.
|
|
7
|
+
*/
|
|
8
|
+
/** What a pixel is: the bell, its glint and shine, the gut, the underside, a tentacle, an oral arm, a tip fading. */
|
|
9
|
+
type Pixel = "p" | "h" | "w" | "o" | "e" | "N" | "n" | "l" | "g";
|
|
10
|
+
/** The pixels' colours on a dark background and a light one - the tentacles darker on light, where neon does not show. */
|
|
11
|
+
export declare const JELLYFISH_COLOURS: Record<"dark" | "light", Record<Pixel, string>>;
|
|
12
|
+
export declare const JELLYFISH_WIDTH: number;
|
|
13
|
+
export declare const JELLYFISH_HEIGHT: number;
|
|
14
|
+
/** The large jellyfish's frames, a stroke: what the pictures are rendered from. */
|
|
15
|
+
export declare const JELLYFISH_FRAMES: string[][];
|
|
16
|
+
/** The small jellyfish's frames, a stroke: what a terminal without images draws, in half blocks. */
|
|
17
|
+
export declare const JELLYFISH_TEXT_FRAMES: string[][];
|
|
18
|
+
/** How long each frame shows. */
|
|
19
|
+
export declare const JELLYFISH_FRAME_MS = 250;
|
|
20
|
+
/**
|
|
21
|
+
* A frame drawn in half blocks for a terminal that shows no images: two pixels a cell, the upper one the text's colour
|
|
22
|
+
* and the lower one the background's, in as many colours as the terminal has: a column a pixel, a row every two.
|
|
23
|
+
*/
|
|
24
|
+
export declare function halfBlocks(pixels: string[], appearance: "dark" | "light", truecolor: boolean): string[];
|
|
25
|
+
export {};
|
package/dist/judge.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type Verdict } from "./tools.ts";
|
|
3
|
+
import type { ReviewSpec } from "./skill.ts";
|
|
4
|
+
export interface JudgeResult {
|
|
5
|
+
index: number;
|
|
6
|
+
model: string;
|
|
7
|
+
verdict: Verdict | undefined;
|
|
8
|
+
}
|
|
9
|
+
export interface GateOutcome {
|
|
10
|
+
passed: boolean;
|
|
11
|
+
results: JudgeResult[];
|
|
12
|
+
/** Feedback handed back to the executor on retry. Empty when the gate passed. */
|
|
13
|
+
critique: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function runGate(opts: {
|
|
16
|
+
cwd: string;
|
|
17
|
+
review: ReviewSpec;
|
|
18
|
+
rubric: string;
|
|
19
|
+
artifactPath: string;
|
|
20
|
+
artifact: string;
|
|
21
|
+
executor: AgentSession;
|
|
22
|
+
/** Used when the gate does not name a model - normally the conversation's. */
|
|
23
|
+
defaultModel?: string;
|
|
24
|
+
onJudgeStart?: (index: number, model: string) => void;
|
|
25
|
+
}): Promise<GateOutcome>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type DriftReport, type ModuleStatus, type WorkLoop } from "./regenerate/loop.ts";
|
|
2
|
+
import { type ModuleState } from "./run-report.ts";
|
|
3
|
+
/**
|
|
4
|
+
* What every command that builds - `turri regenerate`, `conform`, `spec-diff` - does around its own
|
|
5
|
+
* request of the work loop, as the chat does it: the project's models, a job that fails run once more and, failing again,
|
|
6
|
+
* diagnosed, a line as each job starts and ends; then the loop run until nothing more can start, how each job ended,
|
|
7
|
+
* and exit 1 when any failed or a module is not complete. With `dryRun`, the work is planned and the prompt of the first job each codebase would
|
|
8
|
+
* start printed, and nothing runs.
|
|
9
|
+
*/
|
|
10
|
+
export declare function runLoop(options: {
|
|
11
|
+
plainFile: string;
|
|
12
|
+
/** Where the code is exported to as the run ends, however it ends: the furthest-built codebase's last commit. */
|
|
13
|
+
buildDir?: string;
|
|
14
|
+
/** The run report's file (`--report`): see run-report.ts. */
|
|
15
|
+
report?: string;
|
|
16
|
+
/** The command, as the report gives it: its name and its arguments. */
|
|
17
|
+
command?: string[];
|
|
18
|
+
dryRun?: boolean;
|
|
19
|
+
/** Start over, unasked, a codebase that has to be on a module it now requires (`--start-over`). */
|
|
20
|
+
startOver?: boolean;
|
|
21
|
+
/** The request: bring up, regenerate, conformance. */
|
|
22
|
+
ask: (loop: WorkLoop) => Promise<ModuleStatus[]>;
|
|
23
|
+
}): Promise<never>;
|
|
24
|
+
/** How each codebase stands, from what is left to regenerate there. */
|
|
25
|
+
export declare function moduleStates(reports: DriftReport[]): ModuleState[];
|
|
26
|
+
/**
|
|
27
|
+
* The options every command that regenerates takes for a headless run: `--report` and `--build-dir`, and the config's
|
|
28
|
+
* own keys (configKeyOptions) - `--harness-model`, `--coding-model` (`--model`), `--judge-model`, `--review`, `--mutation-testing` and, where
|
|
29
|
+
* `conformance` is true, `--conformance-tests` - read as if config.turri.yaml had them. Exits on a value it cannot read.
|
|
30
|
+
*/
|
|
31
|
+
export declare function headlessOptions(options: Record<string, string | undefined>, conformance: boolean): {
|
|
32
|
+
report?: string;
|
|
33
|
+
buildDir?: string;
|
|
34
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type ExtensionContext, type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import { type ModuleStatus, type WorkLoop } from "./regenerate/loop.ts";
|
|
4
|
+
import type { RenderWork } from "./regenerate/engine.ts";
|
|
5
|
+
import { type JobRegistry } from "./regenerate/jobs.ts";
|
|
6
|
+
/**
|
|
7
|
+
* The tools that act on the work loop - `regenerate`, `drift` and `halt` - defined once, for the conversation and for the
|
|
8
|
+
* skills alike: a skill that regenerates what a spec holds, or reads how far it is regenerated, uses the conversation's
|
|
9
|
+
* own tools, on the conversation's loop, never copies of them.
|
|
10
|
+
*/
|
|
11
|
+
type AnyTool = ToolDefinition<any, any, any>;
|
|
12
|
+
/** How the queue is to be planned, for the work this call queues - see regenerate/steer.ts. */
|
|
13
|
+
export declare const GUIDANCE_PARAM: Type.TOptional<Type.TString>;
|
|
14
|
+
export declare const WORKING_DIR_PARAM: Type.TOptional<Type.TString>;
|
|
15
|
+
export declare const MODEL_PARAM: Type.TOptional<Type.TString>;
|
|
16
|
+
export declare const STACK_PARAM: Type.TOptional<Type.TString>;
|
|
17
|
+
/**
|
|
18
|
+
* Why a module asked for with no stack named cannot be built: a project of stacks where it has no stack of its own and
|
|
19
|
+
* none is chosen, and config.turri.yaml alone names no unit testing script - every pulse would stop for want of one.
|
|
20
|
+
* The stacks and the script each names, and what to do. Undefined when it can be built.
|
|
21
|
+
*/
|
|
22
|
+
export declare function stacklessBuild(plainFile: string): string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* `work` with the stack a call names chosen for it - or, none named, as it is: each module is then built with its own
|
|
25
|
+
* (`stackForModule`). A module that could not be built that way is refused before anything is planned (`stacklessBuild`).
|
|
26
|
+
*/
|
|
27
|
+
export declare function withCallStack<T>(plainFile: string, named: string | undefined, work: () => T): T;
|
|
28
|
+
export declare const WAIT_PARAM: Type.TOptional<Type.TBoolean>;
|
|
29
|
+
/**
|
|
30
|
+
* A render tool's arguments as if turri had been started in `working_dir`: the .plain file
|
|
31
|
+
* and the build folder made absolute from there, and that folder as `base`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function fromWorkingDir<T extends {
|
|
34
|
+
working_dir?: string;
|
|
35
|
+
plain_file?: string;
|
|
36
|
+
build_dir?: string;
|
|
37
|
+
}>(cwd: string, params: T): T & {
|
|
38
|
+
base: string;
|
|
39
|
+
};
|
|
40
|
+
/** What the loop is doing for a spec's modules, for the conversation - and, waiting, how each module ended. */
|
|
41
|
+
export declare function loopReply(loop: WorkLoop, statuses: ModuleStatus[], wait: boolean): Promise<{
|
|
42
|
+
content: {
|
|
43
|
+
type: "text";
|
|
44
|
+
text: string;
|
|
45
|
+
}[];
|
|
46
|
+
details: {
|
|
47
|
+
jobIds: string[];
|
|
48
|
+
};
|
|
49
|
+
}>;
|
|
50
|
+
/** Halt a pulse by its id, or the start of it: what it was, and what moves up behind it. `reason`: why, kept with it. */
|
|
51
|
+
export declare function stopJob(jobs: JobRegistry, named: string, reason?: string, loop?: WorkLoop): string;
|
|
52
|
+
export interface LoopToolsOptions {
|
|
53
|
+
/** Where turri was started: the folder plain_file and working_dir are relative to. */
|
|
54
|
+
cwd: string;
|
|
55
|
+
/** The pulses `halt` stops. Without them - the release agent's - it stops only what activate started. */
|
|
56
|
+
jobs?: JobRegistry;
|
|
57
|
+
/** The work loop the tools act on - the conversation's, for a call in it. */
|
|
58
|
+
loop?: (ctx: ExtensionContext) => WorkLoop;
|
|
59
|
+
/** The model a pulse of this call regenerates on: the one named, or the one chosen for the work. Default: the one named. */
|
|
60
|
+
model?: (ctx: ExtensionContext, plainFile: string, named?: string, work?: RenderWork) => string | undefined;
|
|
61
|
+
/** Whether a call waits for its work to finish, given what it asked. Default: only when it asked to. */
|
|
62
|
+
wait?: (ctx: ExtensionContext, asked?: boolean) => boolean;
|
|
63
|
+
}
|
|
64
|
+
/** Whether a /regenerate's words ask for the module from scratch: its code deleted, and all of it regenerated again. */
|
|
65
|
+
export declare function asksFromScratch(words: string): boolean;
|
|
66
|
+
/** Whether a /regenerate asking for its module from scratch asks for its whole requires chain with it. */
|
|
67
|
+
export declare function asksWholeChain(words: string): boolean;
|
|
68
|
+
/** regenerate: regenerate a module's code up to its spec as it is now. */
|
|
69
|
+
export declare function regenerateTool(options: LoopToolsOptions & {
|
|
70
|
+
loop: (ctx: ExtensionContext) => WorkLoop;
|
|
71
|
+
}): AnyTool;
|
|
72
|
+
/** drift: what is left to regenerate - planning nothing, starting nothing. */
|
|
73
|
+
export declare function driftTool(options: LoopToolsOptions & {
|
|
74
|
+
loop: (ctx: ExtensionContext) => WorkLoop;
|
|
75
|
+
}): AnyTool;
|
|
76
|
+
/** halt: stop a pulse, or what activate started. */
|
|
77
|
+
export declare function haltTool(options: Pick<LoopToolsOptions, "jobs"> & {
|
|
78
|
+
workLoop?: () => WorkLoop | undefined;
|
|
79
|
+
}): AnyTool;
|
|
80
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type Job, type JobRegistry } from "./regenerate/jobs.ts";
|
|
3
|
+
/**
|
|
4
|
+
* The tools a skill that matures a spec works with, besides the conversation's regenerate, drift and halt: wait for
|
|
5
|
+
* the builds, and read back where the spec left the agents to decide.
|
|
6
|
+
*
|
|
7
|
+
* A render that passes can still have been built on a guess: where the spec is silent, the agent
|
|
8
|
+
* decides, and says so in the working notes' assumptions. Those, the pitfalls, a failed job's
|
|
9
|
+
* diagnosis and a spec that contradicts itself are what the spec should have said - and a skill
|
|
10
|
+
* reading them can write it in, so the next build does not have to guess.
|
|
11
|
+
*/
|
|
12
|
+
type AnyTool = ToolDefinition<any, any, any>;
|
|
13
|
+
/** What a maturing skill found: where it came from, and what it says. */
|
|
14
|
+
export interface SpecFinding {
|
|
15
|
+
id: string;
|
|
16
|
+
kind: "assumption" | "pitfall" | "diagnosis" | "conflict";
|
|
17
|
+
/** The module whose codebase it came from, or the job it came from. */
|
|
18
|
+
where: string;
|
|
19
|
+
text: string;
|
|
20
|
+
}
|
|
21
|
+
/** A time as the notes stamp their lines: "2026-09-28 17:40". */
|
|
22
|
+
export declare function noteStamp(date: Date): string;
|
|
23
|
+
/**
|
|
24
|
+
* What the agents and the harness recorded since `since`: the assumptions and pitfalls in every
|
|
25
|
+
* codebase's working notes, the diagnosis of every job that failed for good, and every spec that
|
|
26
|
+
* contradicts itself.
|
|
27
|
+
*/
|
|
28
|
+
export declare function collectFindings(options: {
|
|
29
|
+
project: string;
|
|
30
|
+
jobs: Job[];
|
|
31
|
+
since: Date;
|
|
32
|
+
}): Omit<SpecFinding, "id">[];
|
|
33
|
+
/** Why a build is struggling: a key for what kind of struggle it is - handed out once each - and what it says. */
|
|
34
|
+
export interface Struggle {
|
|
35
|
+
key: string;
|
|
36
|
+
why: string;
|
|
37
|
+
}
|
|
38
|
+
/** How long a build may run before it counts as struggling on its length alone. */
|
|
39
|
+
export declare const LONG_BUILD_MS: number;
|
|
40
|
+
/**
|
|
41
|
+
* Whether a running build is struggling, and how: sent back four times or more in one loop, five
|
|
42
|
+
* setbacks or more, a spec conflict raised, a failure being run again, or running far longer than a
|
|
43
|
+
* build should. Each kind comes once per build - a round count again only every four rounds more.
|
|
44
|
+
*/
|
|
45
|
+
export declare function struggling(job: Job, now?: number): Struggle | undefined;
|
|
46
|
+
export declare function createMatureTools(options: {
|
|
47
|
+
jobs: JobRegistry;
|
|
48
|
+
cwd: string;
|
|
49
|
+
startedAt?: Date;
|
|
50
|
+
}): AnyTool[];
|
|
51
|
+
/** The names of the maturing tools, as a skill's allowed-tools names them: its own - it regenerates, halts and reads drift with the conversation's (loop-tools.ts). */
|
|
52
|
+
export declare const MATURE_TOOLS: string[];
|
|
53
|
+
export {};
|