@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.
- 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/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/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 +21 -0
- package/dist/host.js +25 -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 +52 -0
- package/package.json +8 -1
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The project's `config.turri.yaml`, at its root - the folder that holds `plain_modules` - or, before it had one, its `config.yaml`.
|
|
3
|
+
*
|
|
4
|
+
* One file for what a project says about how it is built:
|
|
5
|
+
*
|
|
6
|
+
* ```yaml
|
|
7
|
+
* module: prism_cli.plain # what `turri regenerate` builds by default
|
|
8
|
+
* harness-model: # the model you chat with in `turri`
|
|
9
|
+
* model: anthropic/claude-opus-5-5
|
|
10
|
+
* thinking: high
|
|
11
|
+
* coding-model: # the rendering agents (default: harness-model)
|
|
12
|
+
* model: google/gemini-3.1-flash
|
|
13
|
+
* thinking: medium
|
|
14
|
+
* judge-model: # the judge and the test review (default: coding-model)
|
|
15
|
+
* model: google/gemini-3.1-pro
|
|
16
|
+
* conformance-tests: chosen # all, chosen (those with acceptance tests, and those the chooser picks after each pulse), module (the chooser picks, and plans, once the module is regenerated) or false (default: false)
|
|
17
|
+
* conformance-budget: 30% # under chosen or module: how many functional specs a module may have chosen, beside those with acceptance tests - a count or a share (default: no limit)
|
|
18
|
+
* review: false # review the code and its tests once they pass (default: true)
|
|
19
|
+
* mutation-testing: false # give the coding agent and the reviewer can_it_fail (default: true)
|
|
20
|
+
* allow-to-stop-jobs: false # stop a running job the spec moved on from (default: true)
|
|
21
|
+
* release-after-pulse: true # release what each pulse lands, as its last stage, before the next pulse (default: false)
|
|
22
|
+
* rules-off: [integrations] # ***plain rules to leave out of the prompts, by name (default: none)
|
|
23
|
+
* unittests-script: test_scripts/run_unittests.sh # the testing scripts - see test-scripts.ts
|
|
24
|
+
* conformance-tests-script: test_scripts/run_conformance_tests.sh
|
|
25
|
+
* prepare-environment-script: test_scripts/prepare_environment.sh # optional: run before the conformance testing script - see test-scripts.ts
|
|
26
|
+
* testing-scripts-run-on: in-place # what the testing scripts run on: in-place (default), or copy - see test-scripts.ts
|
|
27
|
+
* turn-limit: 150 # seconds each turn of an agent a render starts has (default: 120; 0 for none)
|
|
28
|
+
* models: # each agent role's own model, thinking and turn limit - see AGENT_ROLES
|
|
29
|
+
* default: amazon-bedrock/global.openai.gpt-6-luna # every role not named
|
|
30
|
+
* coding: { model: amazon-bedrock/eu.anthropic.claude-sonnet-5, thinking: high, turn-limit: 150 }
|
|
31
|
+
* explorer: google/gemini-3.1-flash-lite
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* `models:` names a model, a thinking level and a turn limit per agent role (AGENT_ROLES), each falling back as
|
|
35
|
+
* roleModel says: a role it does not name takes `default`, and what an entry leaves out it takes from there too.
|
|
36
|
+
* `harness-model`, `coding-model` and `judge-model` are the same as `models: chat`, `coding` and `judge`, which win
|
|
37
|
+
* over them. The turn limit is the role's own, else `default`'s, else `turn-limit:`, else two minutes - the
|
|
38
|
+
* config is the only place it is set.
|
|
39
|
+
*
|
|
40
|
+
* Each model is a model reference and a thinking level (off, minimal, low, medium, high, xhigh, max),
|
|
41
|
+
* either of which may be left out; a bare string is the model alone. What a model leaves out it takes
|
|
42
|
+
* from the one it inherits: judge-model from coding-model, coding-model from harness-model. They are
|
|
43
|
+
* defaults: `--harness-model` / `--coding-model` / `--judge-model` on the command line win over them,
|
|
44
|
+
* and so does a model chosen during the conversation (pi's /model, engine). `model:` is read
|
|
45
|
+
* as `coding-model`, which it was before there were two.
|
|
46
|
+
*
|
|
47
|
+
* Keys are written with dashes; underscores are read the same (`conformance_tests`). Keys it
|
|
48
|
+
* does not know - another tool's `config.yaml`, read while it is the old file, may carry several - are ignored, and so is a value of
|
|
49
|
+
* the wrong type. Everything is optional, and what is given on the command line wins.
|
|
50
|
+
*
|
|
51
|
+
* A project whose parts do not share a stack - a Python backend and a React frontend beside each other - keeps a
|
|
52
|
+
* stack's own settings in `config.turri.<stack>.yaml` beside `config.turri.yaml`: `config.turri.backend.yaml` for the
|
|
53
|
+
* stack `backend` - its testing scripts, its toolchains. A stack is chosen, never worked out: `/stack` in the chat,
|
|
54
|
+
* `--stack <name>` (or `TURRI_STACK`) on the command line. With one chosen, everything turri builds from then on is
|
|
55
|
+
* built with `config.turri.yaml` and the stack's file over it, key by key - a model it names takes what it leaves out
|
|
56
|
+
* from the project's - and a pulse already running keeps the stack it started with. With none, `config.turri.yaml`
|
|
57
|
+
* alone. `module:` and `harness-model:` are the project's alone - there is one conversation - and are not read from a
|
|
58
|
+
* stack's file.
|
|
59
|
+
*/
|
|
60
|
+
/** turri's settings for a project, in a file of its own: a `config.yaml` beside it may be another tool's. */
|
|
61
|
+
export declare const CONFIG_FILE = "config.turri.yaml";
|
|
62
|
+
/**
|
|
63
|
+
* Where turri's settings were before they had a file of their own. A project with no `config.turri*.yaml` yet is read
|
|
64
|
+
* from it - and a `config.<name>.yaml` beside it as the stack `<name>` - and turri never writes it: the first setting
|
|
65
|
+
* turri writes copies them to the new names (`adoptLegacyConfig`), and from then on the old files are not read.
|
|
66
|
+
*/
|
|
67
|
+
export declare const LEGACY_CONFIG_FILE = "config.yaml";
|
|
68
|
+
/** A stack's config file's name: `config.turri.backend.yaml` for the stack `backend`. */
|
|
69
|
+
export declare function stackConfigFile(stack: string): string;
|
|
70
|
+
/** Whether a folder's settings are still in the old files: a config.yaml or a config.<stack>.yaml, and no config.turri*.yaml. */
|
|
71
|
+
export declare function usesLegacyConfig(root: string): boolean;
|
|
72
|
+
/** The file the project's settings are read from, by name: config.turri.yaml, or config.yaml while the folder has only the old files. */
|
|
73
|
+
export declare function projectConfigFile(root: string): string;
|
|
74
|
+
/** The file a stack's settings are read from, by name - the old name while the folder has only the old files. */
|
|
75
|
+
export declare function stackConfigFileIn(root: string, stack: string): string;
|
|
76
|
+
/** Whether a folder holds a project's settings: a config.turri.yaml, a stack's file, or the old files. */
|
|
77
|
+
export declare function hasProjectConfig(root: string): boolean;
|
|
78
|
+
/** The stacks a project folder has files for, by name. */
|
|
79
|
+
export declare function stacksOf(root: string): string[];
|
|
80
|
+
/**
|
|
81
|
+
* Before turri first writes a setting in a folder whose settings are still in the old files: config.yaml copied to
|
|
82
|
+
* config.turri.yaml, and each config.<stack>.yaml to config.turri.<stack>.yaml, so nothing is lost when the old files
|
|
83
|
+
* stop being read. The old files are left as they are. Gives back the files written, by name.
|
|
84
|
+
*/
|
|
85
|
+
export declare function adoptLegacyConfig(root: string, options?: {
|
|
86
|
+
projectFile?: boolean;
|
|
87
|
+
}): string[];
|
|
88
|
+
/** The stack chosen now - `/stack`, `--stack`, `TURRI_STACK` - or, inside a pulse, the one it started with. */
|
|
89
|
+
export declare function chosenStack(): string | undefined;
|
|
90
|
+
/** Choose the stack everything is built with from now on - or none, the project's config alone. Kept in TURRI_STACK. */
|
|
91
|
+
export declare function chooseStack(stack: string | undefined): void;
|
|
92
|
+
/** Run `work` with `stack` as the one chosen, whatever is chosen meanwhile: a pulse keeps the stack it started with. */
|
|
93
|
+
export declare function withStack<T>(stack: string | undefined, work: () => T): T;
|
|
94
|
+
/** Whether a stack is pinned here - named by the call that asked for the work, or kept by the pulse it runs in. */
|
|
95
|
+
export declare function stackPinned(): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* The stack a module is built with: the one pinned - named by the call, or kept by the pulse - else the stack named
|
|
98
|
+
* after the module, config.turri.backend.yaml for backend.plain, else the one chosen for the session; none, when none
|
|
99
|
+
* of them is.
|
|
100
|
+
*/
|
|
101
|
+
export declare function stackForModule(plainFile: string): string | undefined;
|
|
102
|
+
/** The words that choose no stack: the project's config alone. */
|
|
103
|
+
export declare const NO_STACK: string[];
|
|
104
|
+
/**
|
|
105
|
+
* The stack `name` names for the project at `root` - undefined for none - refused when the project has no file for
|
|
106
|
+
* it, with the stacks it has.
|
|
107
|
+
*/
|
|
108
|
+
export declare function stackNamed(root: string, name: string): string | undefined;
|
|
109
|
+
/** The stack a project folder is built with now: the one chosen, when the folder has a file for it. */
|
|
110
|
+
export declare function stackOf(root: string): string | undefined;
|
|
111
|
+
export declare const THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
112
|
+
export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
113
|
+
/** A model, and how hard it thinks - either may be left to the one it inherits from. */
|
|
114
|
+
export interface ModelChoice {
|
|
115
|
+
/** "provider/model-id". */
|
|
116
|
+
model?: string;
|
|
117
|
+
thinking?: ThinkingLevel;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The agents turri starts, by what they do - each can be given its own model, thinking and turn limit (`models:`).
|
|
121
|
+
* The code fixer of a conformance run and the repair agent are coding agents; the unit-test and conformance arbiters
|
|
122
|
+
* are both `arbiter`.
|
|
123
|
+
*/
|
|
124
|
+
export declare const AGENT_ROLES: readonly ["chat", "chooser", "planner", "diagnosis", "release", "coding", "conformance", "explorer", "reviewer", "test-reviewer", "judge", "arbiter", "debugger"];
|
|
125
|
+
export type AgentRole = (typeof AGENT_ROLES)[number];
|
|
126
|
+
/**
|
|
127
|
+
* How hard each role thinks when nothing names a level - not the config's `models:`, nor a `:level` on the model it is
|
|
128
|
+
* given. The agents that do the work - write the code and the tests, explore, release - think little: they work from a
|
|
129
|
+
* spec that leaves little to guess, and every turn of theirs is many. The ones that judge - review, weigh, plan, find a
|
|
130
|
+
* cause - think more: a wrong call there costs pulses. The conversation keeps its own level, pi's.
|
|
131
|
+
*/
|
|
132
|
+
export declare const ROLE_THINKING: Partial<Record<AgentRole, ThinkingLevel>>;
|
|
133
|
+
/** A role's model and thinking, and how long each of its turns may take, in seconds - each may be left out. */
|
|
134
|
+
export interface RoleChoice extends ModelChoice {
|
|
135
|
+
turnLimit?: number;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* A model choice as the one string every agent is given, in pi's own form: "provider/id", with
|
|
139
|
+
* ":level" when it names how hard to think - ":level" alone for the default model thinking so.
|
|
140
|
+
*/
|
|
141
|
+
export declare function modelSpec(choice: ModelChoice | undefined): string | undefined;
|
|
142
|
+
/** A model spec back into its model and thinking level - "provider/id:high", "provider/id", ":high". */
|
|
143
|
+
export declare function splitModelSpec(spec: string | undefined): ModelChoice;
|
|
144
|
+
/**
|
|
145
|
+
* A model spec's model alone, its thinking left behind: what a judging role falls back on when it runs on the coding
|
|
146
|
+
* model - the model, not how hard the coding agent was told to think. Its role's level (ROLE_THINKING) applies instead.
|
|
147
|
+
*/
|
|
148
|
+
export declare function modelOnly(spec: string | undefined): ModelChoice;
|
|
149
|
+
/** A choice with what it leaves out taken from the one it inherits from - or that one, when it is not given at all. */
|
|
150
|
+
export declare function inheritModel(own: ModelChoice | undefined, from: ModelChoice | undefined): ModelChoice | undefined;
|
|
151
|
+
/**
|
|
152
|
+
* The three models as they hold: the judge inherits from the coding model, the coding model from the harness model -
|
|
153
|
+
* `models:` over them: `chat` over harness-model, `coding` over coding-model, `judge` over judge-model, and `default`
|
|
154
|
+
* between the harness model and the rest.
|
|
155
|
+
*/
|
|
156
|
+
export declare function projectModels(config: Pick<ProjectConfig, "harnessModel" | "codingModel" | "judgeModel" | "models">): {
|
|
157
|
+
harness?: ModelChoice;
|
|
158
|
+
coding?: ModelChoice;
|
|
159
|
+
judge?: ModelChoice;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* What the config itself says a role runs on - its own entry, the legacy key that names it (judge-model for the judge
|
|
163
|
+
* and the test review), then `default` (the chat's side: the chat's model instead) - or nothing when it says nothing
|
|
164
|
+
* of it, and the role runs on what it is given: the job's model, which the command line and the conversation choose.
|
|
165
|
+
* The coding role always runs on what it is given: its config is already in it (projectModels).
|
|
166
|
+
*/
|
|
167
|
+
export declare function roleModel(config: Pick<ProjectConfig, "harnessModel" | "judgeModel" | "models">, role: AgentRole): ModelChoice | undefined;
|
|
168
|
+
/** How long each turn of a role's agents may take, in milliseconds: its own, `default`'s, `turn-limit:`'s - or undefined, the harness's default. */
|
|
169
|
+
export declare function roleTurnLimitMs(config: Pick<ProjectConfig, "models" | "turnLimit">, role: AgentRole | undefined): number | undefined;
|
|
170
|
+
export interface ProjectConfig {
|
|
171
|
+
/** The .plain file the module command builds when it is given none. */
|
|
172
|
+
module?: string;
|
|
173
|
+
/** The model `turri` starts with: the one the person talks to. */
|
|
174
|
+
harnessModel?: ModelChoice;
|
|
175
|
+
/** The model the rendering agents run on - every render, conformance, repair and spec change. Unset: the harness model. */
|
|
176
|
+
codingModel?: ModelChoice;
|
|
177
|
+
/**
|
|
178
|
+
* The model of the agents that give a verdict in a conformance run - the judge, which says whether
|
|
179
|
+
* the code, the tests or the spec is wrong, and the test review. Unset: the coding model.
|
|
180
|
+
*/
|
|
181
|
+
judgeModel?: ModelChoice;
|
|
182
|
+
/**
|
|
183
|
+
* Whether conformance tests are written and run, and for what. true (`all`): every functional spec the code is built
|
|
184
|
+
* from. "chosen": the ones with acceptance tests, and those the chooser picks from what each code pulse regenerated, as
|
|
185
|
+
* the pulse ends. "module": the same, but the chooser is asked once, when the whole module is regenerated - what to test,
|
|
186
|
+
* and in which pulses. Under both, those the person asks for with conform too, and a choice is kept once its tests land.
|
|
187
|
+
* false: none, unless the person asks. Unset: none, as with false.
|
|
188
|
+
*/
|
|
189
|
+
conformanceTests?: boolean | "chosen" | "module";
|
|
190
|
+
/**
|
|
191
|
+
* Under `conformance-tests: chosen`: how many of a module's functional specs may be chosen for conformance tests, beside
|
|
192
|
+
* those with acceptance tests - a count, or a share of the module's functional specs (rounded up, at least one).
|
|
193
|
+
* Every choice for the module counts, whenever it was made; the person's own conform is not limited. Unset: no limit.
|
|
194
|
+
*/
|
|
195
|
+
conformanceBudget?: {
|
|
196
|
+
count: number;
|
|
197
|
+
} | {
|
|
198
|
+
share: number;
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Whether the code and its unit tests - and the conformance tests - are reviewed once they pass: two reviews at
|
|
202
|
+
* most, the second weighed by the arbiter. false: the work goes on once its tests pass. Unset: false.
|
|
203
|
+
*/
|
|
204
|
+
review?: boolean;
|
|
205
|
+
/**
|
|
206
|
+
* Whether the coding agent and the reviewer have can_it_fail: a bug put in the code for one run of the unit tests,
|
|
207
|
+
* to see that a test catches it - on a copy, or in place and put back after. Unset: true.
|
|
208
|
+
*/
|
|
209
|
+
mutationTesting?: boolean;
|
|
210
|
+
/**
|
|
211
|
+
* Whether turri stops a running job the spec has moved on from - a spec change, or a file it links edited - and
|
|
212
|
+
* regenerates the spec as it now is instead. false: the job runs to its end and lands what it was regenerating, and
|
|
213
|
+
* what changed since is regenerated on top of it by the next job: something lands as soon as it can, the software
|
|
214
|
+
* evolving job by job. The person's own halt stops a job either way. Unset: true.
|
|
215
|
+
*/
|
|
216
|
+
allowToStopJobs?: boolean;
|
|
217
|
+
/**
|
|
218
|
+
* Whether every pulse that lands code releases it as its last stage, before the module's next pulse starts - an agent
|
|
219
|
+
* looking for the project's release guidance as the chat does, and doing what it says. Unset: false.
|
|
220
|
+
*/
|
|
221
|
+
releaseAfterPulse?: boolean;
|
|
222
|
+
/** Path from the root to the unit testing script. */
|
|
223
|
+
unittestsScript?: string;
|
|
224
|
+
/** Path from the root to the conformance testing script. */
|
|
225
|
+
conformanceTestsScript?: string;
|
|
226
|
+
/** Run on the build before every conformance testing script run: `<script> <build folder>`. */
|
|
227
|
+
prepareEnvironmentScript?: string;
|
|
228
|
+
/**
|
|
229
|
+
* What the testing scripts run on, on this machine: the code in place (the default) - as for an integration whose
|
|
230
|
+
* scripts merge it into a host codebase - or a fresh copy of the code.
|
|
231
|
+
*/
|
|
232
|
+
testingScriptsRunOn?: "copy" | "in-place";
|
|
233
|
+
/** How many jobs run at once in the chat - modules that share nothing build side by side up to it. Unset: from the machine's memory. */
|
|
234
|
+
parallelJobs?: number;
|
|
235
|
+
/**
|
|
236
|
+
* The ***plain authoring rules (genome/rules/<name>.md) left out of the conversation's prompt and every spec-writing
|
|
237
|
+
* agent's, by name: a rule a project's own skills or AGENTS.md replace. The project's alone.
|
|
238
|
+
*/
|
|
239
|
+
rulesOff?: string[];
|
|
240
|
+
/**
|
|
241
|
+
* Tokens of conversation - what an agent has done, past its instructions - after which an agent that keeps its
|
|
242
|
+
* session through a job is compacted between turns. The project's alone. Unset: 100k.
|
|
243
|
+
*/
|
|
244
|
+
compactAt?: number;
|
|
245
|
+
/** The most functional specs the planner puts in one pulse. Unset: 3. A stack's file wins. */
|
|
246
|
+
maxSpecsPerPulse?: number;
|
|
247
|
+
/** The most lines of spec - its functional specs and the definitions they bring - in one pulse; past it, split. Unset: 120. */
|
|
248
|
+
maxLinesPerPulse?: number;
|
|
249
|
+
/**
|
|
250
|
+
* What passes through to the agents' commands beside the scrubbed environment: every variable of the dotenv `files`
|
|
251
|
+
* (paths from the project root), and the `vars` named, from the environment turri was started in. A module's own are
|
|
252
|
+
* added to the project's.
|
|
253
|
+
*/
|
|
254
|
+
agentEnv?: {
|
|
255
|
+
files: string[];
|
|
256
|
+
vars: string[];
|
|
257
|
+
};
|
|
258
|
+
/**
|
|
259
|
+
* Folders of this machine an agent's commands may read beside the system's and the toolchains turri finds itself: a
|
|
260
|
+
* toolchain installed somewhere unusual. A module's own are added to the project's.
|
|
261
|
+
*/
|
|
262
|
+
toolchains?: string[];
|
|
263
|
+
/** Each agent role's own model, thinking and turn limit; `default` for every role not named. */
|
|
264
|
+
models?: Partial<Record<AgentRole | "default", RoleChoice>>;
|
|
265
|
+
/** Seconds each turn of an agent a render starts has, for every role without its own. Unset: 120. 0: none. */
|
|
266
|
+
turnLimit?: number;
|
|
267
|
+
}
|
|
268
|
+
/** The config keys, as written in the file. */
|
|
269
|
+
export declare const CONFIG_KEYS: {
|
|
270
|
+
readonly module: "module";
|
|
271
|
+
readonly harnessModel: "harness-model";
|
|
272
|
+
readonly codingModel: "coding-model";
|
|
273
|
+
readonly judgeModel: "judge-model";
|
|
274
|
+
readonly conformanceTests: "conformance-tests";
|
|
275
|
+
readonly conformanceBudget: "conformance-budget";
|
|
276
|
+
readonly review: "review";
|
|
277
|
+
readonly mutationTesting: "mutation-testing";
|
|
278
|
+
readonly allowToStopJobs: "allow-to-stop-jobs";
|
|
279
|
+
readonly releaseAfterPulse: "release-after-pulse";
|
|
280
|
+
readonly unittestsScript: "unittests-script";
|
|
281
|
+
readonly conformanceTestsScript: "conformance-tests-script";
|
|
282
|
+
readonly prepareEnvironmentScript: "prepare-environment-script";
|
|
283
|
+
readonly testingScriptsRunOn: "testing-scripts-run-on";
|
|
284
|
+
readonly parallelJobs: "parallel-jobs";
|
|
285
|
+
readonly rulesOff: "rules-off";
|
|
286
|
+
readonly compactAt: "compact-at";
|
|
287
|
+
readonly maxSpecsPerPulse: "max-specs-per-pulse";
|
|
288
|
+
readonly maxLinesPerPulse: "max-lines-per-pulse";
|
|
289
|
+
readonly agentEnv: "agent-env";
|
|
290
|
+
readonly toolchains: "toolchains";
|
|
291
|
+
readonly models: "models";
|
|
292
|
+
readonly turnLimit: "turn-limit";
|
|
293
|
+
};
|
|
294
|
+
/** Whether `conformance-tests:` turns the conformance tests on: all (true), chosen or module. Not set, they are off, as with false. */
|
|
295
|
+
export declare function conformanceSwitchedOn(setting: ProjectConfig["conformanceTests"]): boolean;
|
|
296
|
+
/** When the chooser decides which functional specs get conformance tests: after each code pulse, once the module is regenerated, or never - every one, or none. */
|
|
297
|
+
export declare function choosingWhen(setting: ProjectConfig["conformanceTests"]): "pulse" | "module" | undefined;
|
|
298
|
+
/** How many functional specs a module of `count` may have chosen under a budget: the count, or the share rounded up - at least one. */
|
|
299
|
+
export declare function budgetFor(budget: NonNullable<ProjectConfig["conformanceBudget"]>, count: number): number;
|
|
300
|
+
/**
|
|
301
|
+
* The project's config - with a stack's over it, when one is named - or nothing of it when there is no file or it does
|
|
302
|
+
* not parse.
|
|
303
|
+
*/
|
|
304
|
+
export declare function readProjectConfig(root: string, stack?: string): ProjectConfig;
|
|
305
|
+
/**
|
|
306
|
+
* What a command's own options say of the config's keys - `turri regenerate --harness-model`, `--coding-model`,
|
|
307
|
+
* `--judge-model`, `--conformance-tests`, `--review`, `--mutation-testing` - read as if the file had them: each takes its key's value, and
|
|
308
|
+
* every key keeps its place in the order, so a run is the same as one with them in config.turri.yaml - a `models:`
|
|
309
|
+
* entry still winning over `harness-model`, `coding-model` or `judge-model`. A headless run needs no config file
|
|
310
|
+
* edited. Never set in the conversation.
|
|
311
|
+
*/
|
|
312
|
+
type RunOverrides = Partial<Pick<ProjectConfig, "harnessModel" | "codingModel" | "judgeModel" | "conformanceTests" | "review" | "mutationTesting">>;
|
|
313
|
+
/** Read a command's options as the config's keys for the rest of this process. */
|
|
314
|
+
export declare function overrideConfig(overrides: RunOverrides): void;
|
|
315
|
+
/** `--conformance-tests <value>`: the config's own values - all (or true), chosen, module, false. Undefined for anything else. */
|
|
316
|
+
export declare function conformanceOption(value: string): ProjectConfig["conformanceTests"] | undefined;
|
|
317
|
+
/** `--review <value>`, `--mutation-testing <value>`: the config's own, true or false. Undefined for anything else. */
|
|
318
|
+
export declare function booleanOption(value: string): boolean | undefined;
|
|
319
|
+
/** The config a spec is built with: its project's, with its module's stack over it (`stackForModule`). */
|
|
320
|
+
export declare function configForSpec(plainFile: string): ProjectConfig;
|
|
321
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What turri needs from the machine it runs on, found out without changing anything.
|
|
3
|
+
*
|
|
4
|
+
* Kept free of heavy imports so the CLI can ask these questions before it has loaded pi - and so `turri doctor` can report a missing piece instead of crashing on it.
|
|
5
|
+
*/
|
|
6
|
+
export declare const PACKAGE_ROOT: string;
|
|
7
|
+
export declare const AGENT_DIR: string;
|
|
8
|
+
/** The oldest Node that runs .ts files by itself, which is how turri runs. */
|
|
9
|
+
export declare const MIN_NODE: readonly [22, 19];
|
|
10
|
+
export declare function nodeVersionOk(version?: string): boolean;
|
|
11
|
+
/** Whether a command runs at all. */
|
|
12
|
+
export declare function runs(command: string, args?: string[]): boolean;
|
|
13
|
+
/** The global pi extension file that loads turri into every `pi`, if there is one. */
|
|
14
|
+
export declare function globalExtensionFile(): string | undefined;
|
|
15
|
+
export declare function extensionInstalledGlobally(): boolean;
|
|
16
|
+
export declare function themeFiles(): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Are this project's themes already installed globally?
|
|
19
|
+
*
|
|
20
|
+
* Passing --theme as well would make pi load each of them twice and report a collision
|
|
21
|
+
* on startup - the global copy wins and the project copy is skipped, which is noise
|
|
22
|
+
* about a duplicate we created ourselves.
|
|
23
|
+
*/
|
|
24
|
+
export declare function themesInstalledGlobally(): boolean;
|
|
25
|
+
/** Where `turri` on the PATH points, if it is on the PATH at all. */
|
|
26
|
+
export declare function turriOnPath(): string | undefined;
|
|
27
|
+
/** The version of the pi turri runs on, as Node finds it - undefined when it is not installed. */
|
|
28
|
+
export declare function piVersion(): string | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* What `turri --version` prints: turri's version - from a clone, its commit too, as the banner says it - and pi's
|
|
31
|
+
* beside it. pi's own `--version` says only pi's, which read as turri's.
|
|
32
|
+
*/
|
|
33
|
+
export declare function versionLine(identity?: ReturnType<typeof turriIdentity>, pi?: string | null): string;
|
|
34
|
+
/** turri's version, and the commit when it runs from a clone. */
|
|
35
|
+
export declare function turriIdentity(): {
|
|
36
|
+
version: string | null;
|
|
37
|
+
git_commit: string | null;
|
|
38
|
+
git_dirty: boolean | null;
|
|
39
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import "./terminal-images.ts";
|
|
2
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { stackOnMachine } from "./stack.ts";
|
|
4
|
+
import { createAskUserTool } from "./tools.ts";
|
|
5
|
+
import { fromWorkingDir } from "./loop-tools.ts";
|
|
6
|
+
import { type WorkLoop } from "./regenerate/loop.ts";
|
|
7
|
+
import { type AskUser, type AskUserIO } from "./ask.ts";
|
|
8
|
+
import { type Job, type JobRegistry } from "./regenerate/jobs.ts";
|
|
9
|
+
/**
|
|
10
|
+
* Puts a skill step's question to the person, when there is one to ask.
|
|
11
|
+
*
|
|
12
|
+
* A skill runs inside one tool call, in its own session, so its dialogs are not
|
|
13
|
+
* conversation turns and nothing it asks would otherwise reach the transcript. Every
|
|
14
|
+
* exchange is therefore reported back, so the interview is visible while it happens and
|
|
15
|
+
* survives in the tool's result afterwards.
|
|
16
|
+
*/
|
|
17
|
+
export declare function askUserVia(ctx: ExtensionContext, onExchange: (question: string, answer: string, said?: string) => void,
|
|
18
|
+
/** Given, "/btw ..." typed as an answer is a side question: answered with this, and the question asked again. */
|
|
19
|
+
aside?: (question: string) => Promise<string>,
|
|
20
|
+
/** A host's own dialogs (TurriHost.dialogs), the question put in them instead of the terminal's. */
|
|
21
|
+
dialogs?: AskUserIO): AskUser;
|
|
22
|
+
/**
|
|
23
|
+
* ask_user, for the conversation's agent too - the same dialog a skill's interview uses: options to
|
|
24
|
+
* pick from, several at once, "Something else…" to type instead, "Chat about this" to talk it over in
|
|
25
|
+
* the chat first - so a question with a handful of answers is picked, not typed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function chatAskUserTool(dialogs?: AskUserIO): ReturnType<typeof createAskUserTool>;
|
|
28
|
+
/**
|
|
29
|
+
* What a host that runs turri's conversation itself - a desktop app, an editor - is told and asked, where the terminal
|
|
30
|
+
* would draw: the conversation's jobs and its work loop as they are made and as they change, and the dialogs its
|
|
31
|
+
* questions are put to the person in. A host gives the extension `createTurriExtension(host)`; `turri` gives it none.
|
|
32
|
+
*/
|
|
33
|
+
export interface TurriHost {
|
|
34
|
+
/** The conversation's job registry, as the extension makes it: every pulse the conversation starts is in it. */
|
|
35
|
+
onJobs?(jobs: JobRegistry): void;
|
|
36
|
+
/** The conversation's work loop, once work is first asked for: what each codebase has planned, and why. */
|
|
37
|
+
onWorkLoop?(loop: WorkLoop): void;
|
|
38
|
+
/** A job started, moved to another stage, did something, or ended. */
|
|
39
|
+
onJobChange?(job: Job): void;
|
|
40
|
+
/** What is planned changed with no job - a module being planned, or planned. */
|
|
41
|
+
onPlanChange?(): void;
|
|
42
|
+
/** The dialogs ask_user puts its questions in - the conversation's and a skill's interview - instead of the terminal's. */
|
|
43
|
+
dialogs?: AskUserIO;
|
|
44
|
+
}
|
|
45
|
+
/** turri's conversation as an extension of pi's, for a host that runs it itself: see TurriHost. */
|
|
46
|
+
export declare function createTurriExtension(host?: TurriHost): (pi: ExtensionAPI) => void;
|
|
47
|
+
declare const _default: (pi: ExtensionAPI) => void;
|
|
48
|
+
export default _default;
|
|
49
|
+
/**
|
|
50
|
+
* What a slash command showed the person: kept in the chat, where they read it, and put in the conversation as well -
|
|
51
|
+
* unseen there - so that when they speak of it next ("fix it"), the agent knows what they saw. A command runs outside
|
|
52
|
+
* the agent's turns, and a notification is gone in seconds and reaches no model.
|
|
53
|
+
*/
|
|
54
|
+
export declare function shownAfterCommand(pi: ExtensionAPI, ctx: ExtensionContext, command: string, text: string, level: NonNullable<CommandOutcome["level"]>,
|
|
55
|
+
/** The command is in the chat already, above it (echoCommand): the text alone. */
|
|
56
|
+
echoed?: boolean): void;
|
|
57
|
+
export declare const COMMAND_OUTCOME = "turri_command_outcome";
|
|
58
|
+
interface CommandOutcome {
|
|
59
|
+
command: string;
|
|
60
|
+
/** None: the command itself, as the person typed it. */
|
|
61
|
+
text?: string;
|
|
62
|
+
level?: "info" | "warning" | "error";
|
|
63
|
+
echoed?: boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* What the person typed, put in the chat the moment they send it - before the command does anything - and drawn before
|
|
67
|
+
* it goes on: a command's work can hold the event loop (a long requires chain parsed), and the chat stays as it was
|
|
68
|
+
* until it lets go. What the command says comes under it.
|
|
69
|
+
*/
|
|
70
|
+
export declare function echoCommand(pi: ExtensionAPI, command: string): Promise<void>;
|
|
71
|
+
/** Until the TUI has drawn what was just put in the chat. */
|
|
72
|
+
export declare const nextFrame: () => Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* The module a /regenerate's first words name, and the words after it - all of them when none is named. A name typed
|
|
75
|
+
* with spaces for its underscores - "d365_function executor" - is the module d365_function_executor: the most words
|
|
76
|
+
* that name one win.
|
|
77
|
+
*/
|
|
78
|
+
export declare function moduleNamedFirst(cwd: string, words: string[]): {
|
|
79
|
+
named?: string;
|
|
80
|
+
rest: string[];
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* How many jobs the chat runs at once: `parallel-jobs:` in the session folder's config.turri.yaml - the only place it
|
|
84
|
+
* is set - else half this machine's cores, between one and four.
|
|
85
|
+
*/
|
|
86
|
+
export declare function parallelJobs(cwd: string): number;
|
|
87
|
+
/** The project's own files and a digest of each - not plain_modules, dependencies, or turri's and git's folders. */
|
|
88
|
+
export declare function projectFiles(root: string): Map<string, string>;
|
|
89
|
+
/** The files added, changed or removed between two digests of the project. */
|
|
90
|
+
export declare function changedFiles(before: Map<string, string>, after: Map<string, string>): string[];
|
|
91
|
+
/**
|
|
92
|
+
* This machine, for the chat's agent, read afresh every turn: which wall is up under the agents' commands, whether the
|
|
93
|
+
* machine has the toolchains the project's modules name, and what in its testing scripts it lacks - for the person to
|
|
94
|
+
* install or fix; the agents install nothing. `modules`: what the modules need, when it is known already - working it
|
|
95
|
+
* out parses every spec of the project.
|
|
96
|
+
*/
|
|
97
|
+
export declare function machineBlock(cwd: string, modules?: ReturnType<typeof stackOnMachine>): string;
|
|
98
|
+
/**
|
|
99
|
+
* What the project's modules need of this machine, kept between turns - working it out parses every spec, seconds on a
|
|
100
|
+
* project of long requires chains, and a message is never kept waiting for it. Worked out once the chat is up, and again
|
|
101
|
+
* when a turn has settled and a spec changed since: a turn is told what was known last, at most a turn behind.
|
|
102
|
+
*/
|
|
103
|
+
export declare function machineNeeds(cwd: string, work?: (cwd: string) => ReturnType<typeof stackOnMachine>): {
|
|
104
|
+
/** What is known - worked out now only when nothing is yet. */
|
|
105
|
+
now(): ReturnType<typeof stackOnMachine>;
|
|
106
|
+
/** Worked out again, if a spec changed - once the chat has drawn what it is drawing. */
|
|
107
|
+
refreshSoon(): void;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* The project's config.turri.yaml, for the chat's agent: what it says, and what that means for the
|
|
111
|
+
* tools - so a question about the project's settings is answered from the file, not from the
|
|
112
|
+
* tools' defaults.
|
|
113
|
+
*/
|
|
114
|
+
export declare function projectConfigBlock(root: string): string;
|
|
115
|
+
export { fromWorkingDir };
|
|
116
|
+
/**
|
|
117
|
+
* What the agent is to do with a job that just ended, unasked: what the project's guidance says to do then, or else say
|
|
118
|
+
* in a line or two what ended - the person saw it end already, in the transcript.
|
|
119
|
+
*/
|
|
120
|
+
export declare const JOB_FINISHED_REACTION: string;
|
|
121
|
+
/**
|
|
122
|
+
* A skill's question as the transcript keeps it: who asked, what it said before asking - a brief to confirm, the choices
|
|
123
|
+
* it weighed, shown in the dialog that is gone once answered - the question, and the answer.
|
|
124
|
+
*/
|
|
125
|
+
export declare function exchangeLines(data: Exchange, fg: (color: "muted" | "text" | "dim" | "accent", text: string) => string): string[];
|
|
126
|
+
export interface Exchange {
|
|
127
|
+
skill: string;
|
|
128
|
+
/** What the skill's agent said before asking - a brief to confirm, the choices it weighed: shown in the dialog, kept here. */
|
|
129
|
+
said?: string;
|
|
130
|
+
question: string;
|
|
131
|
+
answer: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The name on the door.
|
|
135
|
+
*
|
|
136
|
+
* Pi's own startup header is a built-in component an extension cannot replace, so this
|
|
137
|
+
* adds a wordmark of its own at the top of the session. `turri` starts pi without
|
|
138
|
+
* its header (see pi-launch.ts), leaving only this one.
|
|
139
|
+
*/
|
|
140
|
+
/**
|
|
141
|
+
* The ***plain rules as the conversation has them in its system prompt, every turn - the standard library's
|
|
142
|
+
* inventory among them, in its rule, so the conversation knows what a spec can import from the start.
|
|
143
|
+
*/
|
|
144
|
+
export declare function chatRulesBlock(rules?: {
|
|
145
|
+
name: string;
|
|
146
|
+
content: string;
|
|
147
|
+
}[]): string;
|
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())
|