@topce/pizx 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -9
- package/dist/cli.js +85 -43
- package/dist/cli.js.map +3 -3
- package/dist/index.js +85 -43
- package/dist/index.js.map +3 -3
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export * from "zx";
|
|
|
4
4
|
// src/patterns/types.ts
|
|
5
5
|
import { createInterface } from "node:readline";
|
|
6
6
|
import { completeSimple } from "@earendil-works/pi-ai";
|
|
7
|
+
import { createAgentSession } from "@earendil-works/pi-coding-agent";
|
|
7
8
|
|
|
8
9
|
// src/model-picker.ts
|
|
9
10
|
import {
|
|
@@ -327,6 +328,40 @@ ${skillContext}` : skillContext;
|
|
|
327
328
|
}
|
|
328
329
|
return text.trim();
|
|
329
330
|
}
|
|
331
|
+
async function executeTask(prompt, opts = {}) {
|
|
332
|
+
if (opts.mode === "agent") {
|
|
333
|
+
return runAgentTask(prompt, opts);
|
|
334
|
+
}
|
|
335
|
+
return ask(prompt, opts);
|
|
336
|
+
}
|
|
337
|
+
async function runAgentTask(prompt, opts) {
|
|
338
|
+
const model = pickModel(opts.model);
|
|
339
|
+
if (!model) throw new Error("pizx/patterns: No AI models configured. Run `pi auth login` first.");
|
|
340
|
+
const tools = ["read", "bash", "edit", "write", "grep", "ls"];
|
|
341
|
+
const { session } = await createAgentSession({
|
|
342
|
+
tools,
|
|
343
|
+
...model ? { model } : {}
|
|
344
|
+
});
|
|
345
|
+
try {
|
|
346
|
+
await session.sendUserMessage(prompt);
|
|
347
|
+
const messages = session.messages;
|
|
348
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
349
|
+
const msg = messages[i];
|
|
350
|
+
if (msg?.role !== "assistant") continue;
|
|
351
|
+
const c = "content" in msg ? msg.content : void 0;
|
|
352
|
+
if (typeof c === "string") return c.trim();
|
|
353
|
+
if (Array.isArray(c)) {
|
|
354
|
+
const texts = c.filter(
|
|
355
|
+
(block) => typeof block === "object" && block !== null && "type" in block && "text" in block
|
|
356
|
+
).map((block) => block.text);
|
|
357
|
+
if (texts.length > 0) return texts.join("\n").trim();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return "";
|
|
361
|
+
} finally {
|
|
362
|
+
session.dispose();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
330
365
|
var QUALITY_REVIEW_SYSTEM = `You are a quality assurance reviewer. Evaluate the final deliverable against the original request.
|
|
331
366
|
|
|
332
367
|
Output format:
|
|
@@ -430,7 +465,7 @@ async function execute(pieces, args, opts) {
|
|
|
430
465
|
`);
|
|
431
466
|
}
|
|
432
467
|
if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
|
|
433
|
-
const planText = await
|
|
468
|
+
const planText = await executeTask(goal, {
|
|
434
469
|
...opts,
|
|
435
470
|
model: plannerModel,
|
|
436
471
|
thinkingLevel: "high",
|
|
@@ -463,12 +498,12 @@ async function execute(pieces, args, opts) {
|
|
|
463
498
|
if (!opts.quiet)
|
|
464
499
|
process.stderr.write(` \u2192 Step ${executionStep}: ${currentStep.slice(0, 60)}...
|
|
465
500
|
`);
|
|
466
|
-
const result = await
|
|
501
|
+
const result = await executeTask(currentStep, {
|
|
467
502
|
...opts,
|
|
468
503
|
model: workerModel,
|
|
469
504
|
system: mergeSystem(opts.system, EXECUTE_SYSTEM)
|
|
470
505
|
});
|
|
471
|
-
const evaluation = await
|
|
506
|
+
const evaluation = await executeTask(
|
|
472
507
|
`Goal: ${goal}
|
|
473
508
|
Step executed: ${currentStep}
|
|
474
509
|
Result: ${result}
|
|
@@ -672,7 +707,7 @@ async function execute2(pieces, args, opts) {
|
|
|
672
707
|
const broadcastResults = await Promise.allSettled(
|
|
673
708
|
roles.map(async (role) => {
|
|
674
709
|
const prompt = WORKER_PROMPT.replace("{role}", role).replace("{question}", question);
|
|
675
|
-
const text = await
|
|
710
|
+
const text = await executeTask(prompt, { ...opts, model: workerModel });
|
|
676
711
|
return new BroadcastResponse(role, text, true);
|
|
677
712
|
})
|
|
678
713
|
);
|
|
@@ -688,7 +723,7 @@ async function execute2(pieces, args, opts) {
|
|
|
688
723
|
if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing responses...\n");
|
|
689
724
|
const responsesText = responses.map((wr) => `--- ${wr.role} ---
|
|
690
725
|
${wr.response}`).join("\n\n");
|
|
691
|
-
const synthesis = await
|
|
726
|
+
const synthesis = await executeTask(
|
|
692
727
|
`Original question:
|
|
693
728
|
${question}
|
|
694
729
|
|
|
@@ -809,7 +844,7 @@ async function execute3(pieces, args, opts) {
|
|
|
809
844
|
process.stderr.write(`\u03A7: Cross-Agent Learning \u2014 analyzing ${label}
|
|
810
845
|
`);
|
|
811
846
|
}
|
|
812
|
-
const response = await
|
|
847
|
+
const response = await executeTask(input, {
|
|
813
848
|
...opts,
|
|
814
849
|
model: plannerModel,
|
|
815
850
|
system: mergeSystem(opts.system, ANALYSIS_SYSTEM)
|
|
@@ -885,12 +920,16 @@ async function execute4(pieces, args, opts) {
|
|
|
885
920
|
for (let r = 0; r < rounds; r++) {
|
|
886
921
|
if (r === 0) {
|
|
887
922
|
if (!opts.quiet) process.stderr.write(" \u2192 Generating initial content...\n");
|
|
888
|
-
currentContent = await
|
|
923
|
+
currentContent = await executeTask(prompt, {
|
|
924
|
+
...opts,
|
|
925
|
+
model: workerModel,
|
|
926
|
+
system: opts.system
|
|
927
|
+
});
|
|
889
928
|
} else {
|
|
890
929
|
if (!opts.quiet) process.stderr.write(` \u2192 Improving (round ${r + 1})...
|
|
891
930
|
`);
|
|
892
931
|
const prevCritique = critiqueRounds[r - 1]?.critique ?? "";
|
|
893
|
-
currentContent = await
|
|
932
|
+
currentContent = await executeTask(
|
|
894
933
|
`Original request: ${prompt}
|
|
895
934
|
|
|
896
935
|
Critique:
|
|
@@ -905,7 +944,7 @@ Revise the content based on the critique.`,
|
|
|
905
944
|
}
|
|
906
945
|
if (!opts.quiet) process.stderr.write(` \u2192 Critiquing (round ${r + 1})...
|
|
907
946
|
`);
|
|
908
|
-
const critique2 = await
|
|
947
|
+
const critique2 = await executeTask(currentContent, {
|
|
909
948
|
...opts,
|
|
910
949
|
model: plannerModel,
|
|
911
950
|
system: mergeSystem(opts.system, CRITIQUE_SYSTEM)
|
|
@@ -1036,7 +1075,7 @@ Refine your position. Address the counter-arguments directly. Strengthen your ar
|
|
|
1036
1075
|
`;
|
|
1037
1076
|
}
|
|
1038
1077
|
if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing perspectives...\n");
|
|
1039
|
-
const conclusion = await
|
|
1078
|
+
const conclusion = await executeTask(
|
|
1040
1079
|
`${debateHistory}
|
|
1041
1080
|
|
|
1042
1081
|
Synthesize a balanced conclusion from the full debate above. Weigh the evidence from all rounds.`,
|
|
@@ -1117,7 +1156,7 @@ function describeTask(task) {
|
|
|
1117
1156
|
return task;
|
|
1118
1157
|
}
|
|
1119
1158
|
var FLEET_SYSTEM = `You are a focused task specialist. Complete the assigned task concisely and accurately. Output only the result \u2014 no commentary about being an AI.`;
|
|
1120
|
-
async function
|
|
1159
|
+
async function executeFleetTask(task, opts, workerModel) {
|
|
1121
1160
|
if (typeof task === "function") {
|
|
1122
1161
|
try {
|
|
1123
1162
|
const text = await task("");
|
|
@@ -1128,7 +1167,7 @@ async function executeTask(task, opts, workerModel) {
|
|
|
1128
1167
|
}
|
|
1129
1168
|
const model = workerModel ?? opts.model;
|
|
1130
1169
|
try {
|
|
1131
|
-
const text = await
|
|
1170
|
+
const text = await executeTask(task, {
|
|
1132
1171
|
...opts,
|
|
1133
1172
|
model,
|
|
1134
1173
|
system: mergeSystem(opts.system, FLEET_SYSTEM)
|
|
@@ -1162,7 +1201,7 @@ async function execute6(pieces, args, opts) {
|
|
|
1162
1201
|
for (let i = 0; i < tasks.length; i += concurrency) {
|
|
1163
1202
|
const batch = tasks.slice(i, i + concurrency);
|
|
1164
1203
|
const batchResults = await Promise.allSettled(
|
|
1165
|
-
batch.map((task) =>
|
|
1204
|
+
batch.map((task) => executeFleetTask(task, opts, workerModel))
|
|
1166
1205
|
);
|
|
1167
1206
|
batchResults.forEach((r, idx) => {
|
|
1168
1207
|
if (r.status === "fulfilled") {
|
|
@@ -1309,7 +1348,7 @@ ${depResults}
|
|
|
1309
1348
|
Your task: ${node.task}`;
|
|
1310
1349
|
}
|
|
1311
1350
|
}
|
|
1312
|
-
const text = await
|
|
1351
|
+
const text = await executeTask(context, {
|
|
1313
1352
|
...opts,
|
|
1314
1353
|
model: workerModel,
|
|
1315
1354
|
system: mergeSystem(opts.system, NODE_SYSTEM)
|
|
@@ -1404,7 +1443,7 @@ async function execute8(pieces, args, opts) {
|
|
|
1404
1443
|
const roundResults = await Promise.allSettled(
|
|
1405
1444
|
roles.map(async (role) => {
|
|
1406
1445
|
const prompt = buildWriterPrompt(role, topic, blackboard);
|
|
1407
|
-
const text = await
|
|
1446
|
+
const text = await executeTask(prompt, { ...opts, model: workerModel });
|
|
1408
1447
|
return { role, text };
|
|
1409
1448
|
})
|
|
1410
1449
|
);
|
|
@@ -1418,7 +1457,7 @@ async function execute8(pieces, args, opts) {
|
|
|
1418
1457
|
}
|
|
1419
1458
|
}
|
|
1420
1459
|
if (!opts.quiet) process.stderr.write(" \u2192 Consolidating findings...\n");
|
|
1421
|
-
const synthesis = await
|
|
1460
|
+
const synthesis = await executeTask(
|
|
1422
1461
|
`Topic: ${topic}
|
|
1423
1462
|
|
|
1424
1463
|
Blackboard findings:
|
|
@@ -1509,7 +1548,7 @@ async function negotiateRoles(task, opts) {
|
|
|
1509
1548
|
const min = opts.minAgents ?? 2;
|
|
1510
1549
|
const max = opts.maxAgents ?? 5;
|
|
1511
1550
|
const prompt = NEGOTIATE_SYSTEM.replace("{min}", String(min)).replace("{max}", String(max));
|
|
1512
|
-
const response = await
|
|
1551
|
+
const response = await executeTask(`Task: ${task}
|
|
1513
1552
|
|
|
1514
1553
|
${prompt}`, {
|
|
1515
1554
|
...opts,
|
|
@@ -1541,7 +1580,7 @@ async function decideWorkflow(roles, task, opts) {
|
|
|
1541
1580
|
if (roles.length <= 1)
|
|
1542
1581
|
return { workflow: "parallel", reasoning: "Single role \u2014 no dependencies." };
|
|
1543
1582
|
const rolesText = roles.map((r, i) => `${i + 1}. ${r.name}: ${r.goal}`).join("\n");
|
|
1544
|
-
const response = await
|
|
1583
|
+
const response = await executeTask(
|
|
1545
1584
|
`Task: ${task}
|
|
1546
1585
|
|
|
1547
1586
|
Roles:
|
|
@@ -1571,7 +1610,7 @@ async function executeRoles(roles, task, workflow, opts) {
|
|
|
1571
1610
|
if (workflow === "sequential") {
|
|
1572
1611
|
let context = task;
|
|
1573
1612
|
for (const role of roles) {
|
|
1574
|
-
const output = await
|
|
1613
|
+
const output = await executeTask(context, {
|
|
1575
1614
|
...opts,
|
|
1576
1615
|
model: workerModel,
|
|
1577
1616
|
system: mergeSystem(opts.system, EXECUTE_SYSTEM2(role))
|
|
@@ -1739,7 +1778,7 @@ async function execute10(pieces, args, opts) {
|
|
|
1739
1778
|
}
|
|
1740
1779
|
if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
|
|
1741
1780
|
const planStart = Date.now();
|
|
1742
|
-
const planText = await
|
|
1781
|
+
const planText = await executeTask(request, {
|
|
1743
1782
|
...opts,
|
|
1744
1783
|
model: plannerModel,
|
|
1745
1784
|
thinkingLevel: "high",
|
|
@@ -1804,7 +1843,7 @@ async function execute10(pieces, args, opts) {
|
|
|
1804
1843
|
const workerTexts = workerResults.map((wr, i) => `Task ${i + 1}: ${wr.task}
|
|
1805
1844
|
Result: ${wr.output}`).join("\n\n");
|
|
1806
1845
|
const synthStart = Date.now();
|
|
1807
|
-
const synthesis = await
|
|
1846
|
+
const synthesis = await executeTask(
|
|
1808
1847
|
`Original request:
|
|
1809
1848
|
${request}
|
|
1810
1849
|
|
|
@@ -1955,7 +1994,7 @@ async function execute11(pieces, args, opts) {
|
|
|
1955
1994
|
} else {
|
|
1956
1995
|
const prompt = customPrompt ?? generateStagePrompt(stage, currentInput, i === 0);
|
|
1957
1996
|
const systemMessage = i === 0 ? `You are a specialist executing stage ${i + 1}: ${stage}. Focus only on this stage's output.` : `You are a specialist executing stage ${i + 1}: ${stage}. Process the previous stage's output according to your instructions. Maintain all important information from previous stages.`;
|
|
1958
|
-
output = await
|
|
1997
|
+
output = await executeTask(prompt, {
|
|
1959
1998
|
...opts,
|
|
1960
1999
|
model: workerModel,
|
|
1961
2000
|
system: mergeSystem(opts.system, systemMessage)
|
|
@@ -1977,7 +2016,7 @@ ${sr.output.slice(0, 200)}${sr.output.length > 200 ? "..." : ""}`
|
|
|
1977
2016
|
var \u039B = createPatternTag(defaults11, execute11);
|
|
1978
2017
|
|
|
1979
2018
|
// src/patterns/ralph.ts
|
|
1980
|
-
import { createAgentSession } from "@earendil-works/pi-coding-agent";
|
|
2019
|
+
import { createAgentSession as createAgentSession2 } from "@earendil-works/pi-coding-agent";
|
|
1981
2020
|
var defaults12 = {
|
|
1982
2021
|
maxIterations: 5,
|
|
1983
2022
|
useTools: true,
|
|
@@ -2003,7 +2042,7 @@ async function executeWithTools(goal, opts) {
|
|
|
2003
2042
|
`pizx/\u03A1: model not found: "${opts.model}". Run \`pi models\` to see available models.`
|
|
2004
2043
|
);
|
|
2005
2044
|
}
|
|
2006
|
-
const { session } = await
|
|
2045
|
+
const { session } = await createAgentSession2({
|
|
2007
2046
|
tools: ["read", "bash", "edit", "write", "grep", "ls"],
|
|
2008
2047
|
...agentModel ? { model: agentModel } : {}
|
|
2009
2048
|
});
|
|
@@ -2053,13 +2092,13 @@ async function execute12(pieces, args, opts) {
|
|
|
2053
2092
|
`);
|
|
2054
2093
|
}
|
|
2055
2094
|
if (!opts.quiet) process.stderr.write(" \u2192 Analyzing...\n");
|
|
2056
|
-
const analysis = await
|
|
2095
|
+
const analysis = await executeTask(currentGoal, {
|
|
2057
2096
|
...opts,
|
|
2058
2097
|
model: plannerModel,
|
|
2059
2098
|
system: mergeSystem(opts.system, ANALYSIS_SYSTEM2)
|
|
2060
2099
|
});
|
|
2061
2100
|
if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
|
|
2062
|
-
const plan = await
|
|
2101
|
+
const plan = await executeTask(
|
|
2063
2102
|
`Goal: ${currentGoal}
|
|
2064
2103
|
|
|
2065
2104
|
Analysis: ${analysis}
|
|
@@ -2074,7 +2113,7 @@ ${plan}
|
|
|
2074
2113
|
Goal: ${currentGoal}`, {
|
|
2075
2114
|
...opts,
|
|
2076
2115
|
model: workerModel
|
|
2077
|
-
}) : await
|
|
2116
|
+
}) : await executeTask(`Implement this plan:
|
|
2078
2117
|
${plan}
|
|
2079
2118
|
|
|
2080
2119
|
Goal: ${currentGoal}`, {
|
|
@@ -2082,19 +2121,22 @@ Goal: ${currentGoal}`, {
|
|
|
2082
2121
|
model: workerModel
|
|
2083
2122
|
});
|
|
2084
2123
|
if (!opts.quiet) process.stderr.write(" \u2192 Reviewing...\n");
|
|
2085
|
-
const review = await
|
|
2124
|
+
const review = await executeTask(
|
|
2125
|
+
`Plan:
|
|
2086
2126
|
${plan}
|
|
2087
2127
|
|
|
2088
2128
|
Result:
|
|
2089
2129
|
${result}
|
|
2090
2130
|
|
|
2091
|
-
Review the implementation.`,
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2131
|
+
Review the implementation.`,
|
|
2132
|
+
{
|
|
2133
|
+
...opts,
|
|
2134
|
+
model: plannerModel,
|
|
2135
|
+
maxTokens: 1024,
|
|
2136
|
+
thinkingLevel: "high",
|
|
2137
|
+
system: mergeSystem(opts.system, REVIEW_SYSTEM)
|
|
2138
|
+
}
|
|
2139
|
+
);
|
|
2098
2140
|
const shouldContinue = review.includes("ITERATE") && !review.includes("DONE");
|
|
2099
2141
|
iterations.push({
|
|
2100
2142
|
iteration,
|
|
@@ -2163,7 +2205,7 @@ var DECOMPOSE_SYSTEM = `You are a task decomposition specialist. Break down comp
|
|
|
2163
2205
|
var SYNTHESIS_SYSTEM5 = `You are a synthesis specialist. Combine the results from multiple sub-agent analyses into a coherent, comprehensive answer. Identify patterns, conflicts, and gaps.`;
|
|
2164
2206
|
async function decomposeTask(task, opts) {
|
|
2165
2207
|
if (opts.subdomains && opts.subdomains.length > 0) return opts.subdomains;
|
|
2166
|
-
const result = await
|
|
2208
|
+
const result = await executeTask(
|
|
2167
2209
|
`Decompose this task into ${opts.maxSubTasks ?? 4} independent sub-tasks that can be worked on in parallel:
|
|
2168
2210
|
|
|
2169
2211
|
${task}
|
|
@@ -2253,7 +2295,7 @@ async function execute13(pieces, args, opts) {
|
|
|
2253
2295
|
const subResultsText = subResults.map((sr, i) => `Sub-task ${i + 1}: ${sr.subTask}
|
|
2254
2296
|
Result: ${sr.text}`).join("\n\n");
|
|
2255
2297
|
const synthStart = Date.now();
|
|
2256
|
-
const synthesis = await
|
|
2298
|
+
const synthesis = await executeTask(
|
|
2257
2299
|
`Original task:
|
|
2258
2300
|
${task}
|
|
2259
2301
|
|
|
@@ -2371,7 +2413,7 @@ var CONSOLIDATE_SYSTEM = `You are a research director. Consolidate the structure
|
|
|
2371
2413
|
async function defineSchema(task, opts) {
|
|
2372
2414
|
const agentCount = opts.agents ?? 3;
|
|
2373
2415
|
const prompt = SCHEMA_SYSTEM.replace("{agentCount}", String(agentCount));
|
|
2374
|
-
const response = await
|
|
2416
|
+
const response = await executeTask(`Task: ${task}
|
|
2375
2417
|
|
|
2376
2418
|
${prompt}`, {
|
|
2377
2419
|
...opts,
|
|
@@ -2426,7 +2468,7 @@ async function executeRound(roles, assignments, store2, round, opts) {
|
|
|
2426
2468
|
const storeText = formatStore(store2);
|
|
2427
2469
|
const systemPrompt = isWrite ? WRITE_SYSTEM(role, keysStr).replace("{store}", storeText) : UPDATE_SYSTEM(role, keysStr).replace("{store}", storeText);
|
|
2428
2470
|
const task = isWrite ? `Write your initial findings to your assigned keys: ${keysStr}` : `Review the shared context and update your entries for keys: ${keysStr}`;
|
|
2429
|
-
const response = await
|
|
2471
|
+
const response = await executeTask(task, {
|
|
2430
2472
|
...opts,
|
|
2431
2473
|
model: workerModel,
|
|
2432
2474
|
system: mergeSystem(opts.system, systemPrompt)
|
|
@@ -2597,7 +2639,7 @@ async function execute15(pieces, args, opts) {
|
|
|
2597
2639
|
for (let a = 0; a < roles.length; a++) {
|
|
2598
2640
|
const role = roles[a] ?? `Agent ${a + 1}`;
|
|
2599
2641
|
const prompt = buildThreadPrompt(role, thread2);
|
|
2600
|
-
const response = await
|
|
2642
|
+
const response = await executeTask(prompt, { ...opts, model: workerModel });
|
|
2601
2643
|
messages.push(new ThreadMessage(role, turn, response));
|
|
2602
2644
|
thread2 += `
|
|
2603
2645
|
[${role}] (Turn ${turn}): ${response}
|
|
@@ -2605,7 +2647,7 @@ async function execute15(pieces, args, opts) {
|
|
|
2605
2647
|
}
|
|
2606
2648
|
}
|
|
2607
2649
|
if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing conclusion...\n");
|
|
2608
|
-
const conclusion = await
|
|
2650
|
+
const conclusion = await executeTask(
|
|
2609
2651
|
`Topic: ${topic}
|
|
2610
2652
|
|
|
2611
2653
|
Conversation thread:
|
|
@@ -2823,7 +2865,7 @@ function configurePi(opts) {
|
|
|
2823
2865
|
|
|
2824
2866
|
// src/pi-agent.ts
|
|
2825
2867
|
import {
|
|
2826
|
-
createAgentSession as
|
|
2868
|
+
createAgentSession as createAgentSession3,
|
|
2827
2869
|
DefaultResourceLoader
|
|
2828
2870
|
} from "@earendil-works/pi-coding-agent";
|
|
2829
2871
|
var _agentDefaults = {
|
|
@@ -2888,7 +2930,7 @@ async function getSession(opts) {
|
|
|
2888
2930
|
loader.extendResources({ skillPaths });
|
|
2889
2931
|
}
|
|
2890
2932
|
}
|
|
2891
|
-
const result = await
|
|
2933
|
+
const result = await createAgentSession3({
|
|
2892
2934
|
cwd: opts.cwd,
|
|
2893
2935
|
thinkingLevel: opts.thinkingLevel,
|
|
2894
2936
|
tools: opts.tools,
|