@oh-my-pi/pi-coding-agent 3.25.0 → 3.31.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/CHANGELOG.md +90 -0
- package/package.json +5 -5
- package/src/cli/args.ts +4 -0
- package/src/core/agent-session.ts +29 -2
- package/src/core/bash-executor.ts +2 -1
- package/src/core/custom-commands/bundled/review/index.ts +369 -14
- package/src/core/custom-commands/bundled/wt/index.ts +1 -1
- package/src/core/session-manager.ts +158 -246
- package/src/core/session-storage.ts +379 -0
- package/src/core/settings-manager.ts +155 -4
- package/src/core/system-prompt.ts +62 -64
- package/src/core/tools/ask.ts +5 -4
- package/src/core/tools/bash-interceptor.ts +26 -61
- package/src/core/tools/bash.ts +13 -8
- package/src/core/tools/complete.ts +2 -4
- package/src/core/tools/edit-diff.ts +11 -4
- package/src/core/tools/edit.ts +7 -13
- package/src/core/tools/find.ts +111 -50
- package/src/core/tools/gemini-image.ts +128 -147
- package/src/core/tools/grep.ts +397 -415
- package/src/core/tools/index.test.ts +5 -1
- package/src/core/tools/index.ts +6 -8
- package/src/core/tools/jtd-to-json-schema.ts +174 -196
- package/src/core/tools/ls.ts +12 -10
- package/src/core/tools/lsp/client.ts +58 -9
- package/src/core/tools/lsp/config.ts +205 -656
- package/src/core/tools/lsp/defaults.json +465 -0
- package/src/core/tools/lsp/index.ts +55 -32
- package/src/core/tools/lsp/rust-analyzer.ts +49 -10
- package/src/core/tools/lsp/types.ts +1 -0
- package/src/core/tools/lsp/utils.ts +1 -1
- package/src/core/tools/read.ts +152 -76
- package/src/core/tools/render-utils.ts +70 -10
- package/src/core/tools/review.ts +38 -126
- package/src/core/tools/task/artifacts.ts +5 -4
- package/src/core/tools/task/executor.ts +204 -67
- package/src/core/tools/task/index.ts +129 -92
- package/src/core/tools/task/name-generator.ts +1544 -214
- package/src/core/tools/task/parallel.ts +30 -3
- package/src/core/tools/task/render.ts +85 -39
- package/src/core/tools/task/types.ts +34 -11
- package/src/core/tools/task/worker.ts +152 -27
- package/src/core/tools/web-fetch.ts +220 -1657
- package/src/core/tools/web-scrapers/academic.test.ts +239 -0
- package/src/core/tools/web-scrapers/artifacthub.ts +215 -0
- package/src/core/tools/web-scrapers/arxiv.ts +88 -0
- package/src/core/tools/web-scrapers/aur.ts +175 -0
- package/src/core/tools/web-scrapers/biorxiv.ts +141 -0
- package/src/core/tools/web-scrapers/bluesky.ts +284 -0
- package/src/core/tools/web-scrapers/brew.ts +177 -0
- package/src/core/tools/web-scrapers/business.test.ts +82 -0
- package/src/core/tools/web-scrapers/cheatsh.ts +78 -0
- package/src/core/tools/web-scrapers/chocolatey.ts +158 -0
- package/src/core/tools/web-scrapers/choosealicense.ts +110 -0
- package/src/core/tools/web-scrapers/cisa-kev.ts +100 -0
- package/src/core/tools/web-scrapers/clojars.ts +180 -0
- package/src/core/tools/web-scrapers/coingecko.ts +184 -0
- package/src/core/tools/web-scrapers/crates-io.ts +128 -0
- package/src/core/tools/web-scrapers/crossref.ts +149 -0
- package/src/core/tools/web-scrapers/dev-platforms.test.ts +254 -0
- package/src/core/tools/web-scrapers/devto.ts +177 -0
- package/src/core/tools/web-scrapers/discogs.ts +308 -0
- package/src/core/tools/web-scrapers/discourse.ts +221 -0
- package/src/core/tools/web-scrapers/dockerhub.ts +160 -0
- package/src/core/tools/web-scrapers/documentation.test.ts +85 -0
- package/src/core/tools/web-scrapers/fdroid.ts +158 -0
- package/src/core/tools/web-scrapers/finance-media.test.ts +144 -0
- package/src/core/tools/web-scrapers/firefox-addons.ts +214 -0
- package/src/core/tools/web-scrapers/flathub.ts +239 -0
- package/src/core/tools/web-scrapers/git-hosting.test.ts +272 -0
- package/src/core/tools/web-scrapers/github-gist.ts +68 -0
- package/src/core/tools/web-scrapers/github.ts +455 -0
- package/src/core/tools/web-scrapers/gitlab.ts +456 -0
- package/src/core/tools/web-scrapers/go-pkg.ts +275 -0
- package/src/core/tools/web-scrapers/hackage.ts +94 -0
- package/src/core/tools/web-scrapers/hackernews.ts +208 -0
- package/src/core/tools/web-scrapers/hex.ts +121 -0
- package/src/core/tools/web-scrapers/huggingface.ts +385 -0
- package/src/core/tools/web-scrapers/iacr.ts +86 -0
- package/src/core/tools/web-scrapers/index.ts +250 -0
- package/src/core/tools/web-scrapers/jetbrains-marketplace.ts +169 -0
- package/src/core/tools/web-scrapers/lemmy.ts +220 -0
- package/src/core/tools/web-scrapers/lobsters.ts +186 -0
- package/src/core/tools/web-scrapers/mastodon.ts +310 -0
- package/src/core/tools/web-scrapers/maven.ts +152 -0
- package/src/core/tools/web-scrapers/mdn.ts +174 -0
- package/src/core/tools/web-scrapers/media.test.ts +138 -0
- package/src/core/tools/web-scrapers/metacpan.ts +253 -0
- package/src/core/tools/web-scrapers/musicbrainz.ts +273 -0
- package/src/core/tools/web-scrapers/npm.ts +114 -0
- package/src/core/tools/web-scrapers/nuget.ts +205 -0
- package/src/core/tools/web-scrapers/nvd.ts +243 -0
- package/src/core/tools/web-scrapers/ollama.ts +267 -0
- package/src/core/tools/web-scrapers/open-vsx.ts +119 -0
- package/src/core/tools/web-scrapers/opencorporates.ts +275 -0
- package/src/core/tools/web-scrapers/openlibrary.ts +319 -0
- package/src/core/tools/web-scrapers/orcid.ts +299 -0
- package/src/core/tools/web-scrapers/osv.ts +189 -0
- package/src/core/tools/web-scrapers/package-managers-2.test.ts +199 -0
- package/src/core/tools/web-scrapers/package-managers.test.ts +171 -0
- package/src/core/tools/web-scrapers/package-registries.test.ts +259 -0
- package/src/core/tools/web-scrapers/packagist.ts +174 -0
- package/src/core/tools/web-scrapers/pub-dev.ts +185 -0
- package/src/core/tools/web-scrapers/pubmed.ts +178 -0
- package/src/core/tools/web-scrapers/pypi.ts +129 -0
- package/src/core/tools/web-scrapers/rawg.ts +124 -0
- package/src/core/tools/web-scrapers/readthedocs.ts +126 -0
- package/src/core/tools/web-scrapers/reddit.ts +104 -0
- package/src/core/tools/web-scrapers/repology.ts +262 -0
- package/src/core/tools/web-scrapers/research.test.ts +107 -0
- package/src/core/tools/web-scrapers/rfc.ts +209 -0
- package/src/core/tools/web-scrapers/rubygems.ts +117 -0
- package/src/core/tools/web-scrapers/searchcode.ts +217 -0
- package/src/core/tools/web-scrapers/sec-edgar.ts +274 -0
- package/src/core/tools/web-scrapers/security.test.ts +103 -0
- package/src/core/tools/web-scrapers/semantic-scholar.ts +190 -0
- package/src/core/tools/web-scrapers/snapcraft.ts +200 -0
- package/src/core/tools/web-scrapers/social-extended.test.ts +192 -0
- package/src/core/tools/web-scrapers/social.test.ts +259 -0
- package/src/core/tools/web-scrapers/sourcegraph.ts +373 -0
- package/src/core/tools/web-scrapers/spdx.ts +121 -0
- package/src/core/tools/web-scrapers/spotify.ts +218 -0
- package/src/core/tools/web-scrapers/stackexchange.test.ts +120 -0
- package/src/core/tools/web-scrapers/stackoverflow.ts +124 -0
- package/src/core/tools/web-scrapers/standards.test.ts +122 -0
- package/src/core/tools/web-scrapers/terraform.ts +304 -0
- package/src/core/tools/web-scrapers/tldr.ts +51 -0
- package/src/core/tools/web-scrapers/twitter.ts +96 -0
- package/src/core/tools/web-scrapers/types.ts +234 -0
- package/src/core/tools/web-scrapers/utils.ts +162 -0
- package/src/core/tools/web-scrapers/vimeo.ts +152 -0
- package/src/core/tools/web-scrapers/vscode-marketplace.ts +195 -0
- package/src/core/tools/web-scrapers/w3c.ts +163 -0
- package/src/core/tools/web-scrapers/wikidata.ts +357 -0
- package/src/core/tools/web-scrapers/wikipedia.test.ts +73 -0
- package/src/core/tools/web-scrapers/wikipedia.ts +95 -0
- package/src/core/tools/web-scrapers/youtube.test.ts +198 -0
- package/src/core/tools/web-scrapers/youtube.ts +371 -0
- package/src/core/tools/write.ts +21 -18
- package/src/core/voice.ts +3 -2
- package/src/lib/worktree/collapse.ts +2 -1
- package/src/lib/worktree/git.ts +2 -18
- package/src/main.ts +59 -3
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +33 -19
- package/src/modes/interactive/components/extensions/extension-list.ts +15 -8
- package/src/modes/interactive/components/hook-editor.ts +2 -1
- package/src/modes/interactive/components/model-selector.ts +19 -4
- package/src/modes/interactive/interactive-mode.ts +41 -38
- package/src/modes/interactive/theme/theme.ts +58 -58
- package/src/modes/rpc/rpc-mode.ts +10 -9
- package/src/prompts/review-request.md +27 -0
- package/src/prompts/reviewer.md +64 -68
- package/src/prompts/tools/output.md +22 -3
- package/src/prompts/tools/task.md +32 -33
- package/src/utils/clipboard.ts +2 -1
- package/src/utils/tools-manager.ts +110 -8
- package/examples/extensions/subagent/agents/reviewer.md +0 -35
|
@@ -21,7 +21,6 @@ import { formatDuration } from "../render-utils";
|
|
|
21
21
|
import { cleanupTempDir, createTempArtifactsDir, getArtifactsDir } from "./artifacts";
|
|
22
22
|
import { discoverAgents, getAgent } from "./discovery";
|
|
23
23
|
import { runSubprocess } from "./executor";
|
|
24
|
-
import { generateTaskName } from "./name-generator";
|
|
25
24
|
import { mapWithConcurrencyLimit } from "./parallel";
|
|
26
25
|
import { renderCall, renderResult } from "./render";
|
|
27
26
|
import {
|
|
@@ -135,17 +134,34 @@ export async function createTaskTool(
|
|
|
135
134
|
execute: async (_toolCallId, params, signal, onUpdate) => {
|
|
136
135
|
const startTime = Date.now();
|
|
137
136
|
const { agents, projectAgentsDir } = await discoverAgents(session.cwd);
|
|
138
|
-
const context = params
|
|
139
|
-
|
|
137
|
+
const { agent: agentName, context, model, output: outputSchema } = params;
|
|
138
|
+
|
|
139
|
+
// Validate agent exists
|
|
140
|
+
const agent = getAgent(agents, agentName);
|
|
141
|
+
if (!agent) {
|
|
142
|
+
const available = agents.map((a) => a.name).join(", ") || "none";
|
|
143
|
+
return {
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: "text",
|
|
147
|
+
text: `Unknown agent "${agentName}". Available: ${available}`,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
details: {
|
|
151
|
+
projectAgentsDir,
|
|
152
|
+
results: [],
|
|
153
|
+
totalDurationMs: 0,
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
140
157
|
|
|
141
158
|
// Handle empty or missing tasks
|
|
142
159
|
if (!params.tasks || params.tasks.length === 0) {
|
|
143
|
-
const available = agents.map((a) => a.name).join(", ") || "none";
|
|
144
160
|
return {
|
|
145
161
|
content: [
|
|
146
162
|
{
|
|
147
163
|
type: "text",
|
|
148
|
-
text: `No tasks provided. Use: { tasks: [{
|
|
164
|
+
text: `No tasks provided. Use: { agent, context, tasks: [{id, task, description}, ...] }`,
|
|
149
165
|
},
|
|
150
166
|
],
|
|
151
167
|
details: {
|
|
@@ -173,6 +189,56 @@ export async function createTaskTool(
|
|
|
173
189
|
};
|
|
174
190
|
}
|
|
175
191
|
|
|
192
|
+
const tasks = params.tasks;
|
|
193
|
+
const missingTaskIndexes: number[] = [];
|
|
194
|
+
const idIndexes = new Map<string, number[]>();
|
|
195
|
+
|
|
196
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
197
|
+
const id = tasks[i]?.id;
|
|
198
|
+
if (typeof id !== "string" || id.trim() === "") {
|
|
199
|
+
missingTaskIndexes.push(i);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
const normalizedId = id.toLowerCase();
|
|
203
|
+
const indexes = idIndexes.get(normalizedId);
|
|
204
|
+
if (indexes) {
|
|
205
|
+
indexes.push(i);
|
|
206
|
+
} else {
|
|
207
|
+
idIndexes.set(normalizedId, [i]);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const duplicateIds: Array<{ id: string; indexes: number[] }> = [];
|
|
212
|
+
for (const [normalizedId, indexes] of idIndexes.entries()) {
|
|
213
|
+
if (indexes.length > 1) {
|
|
214
|
+
duplicateIds.push({
|
|
215
|
+
id: tasks[indexes[0]]?.id ?? normalizedId,
|
|
216
|
+
indexes,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (missingTaskIndexes.length > 0 || duplicateIds.length > 0) {
|
|
222
|
+
const problems: string[] = [];
|
|
223
|
+
if (missingTaskIndexes.length > 0) {
|
|
224
|
+
problems.push(`Missing task ids at indexes: ${missingTaskIndexes.join(", ")}`);
|
|
225
|
+
}
|
|
226
|
+
if (duplicateIds.length > 0) {
|
|
227
|
+
const details = duplicateIds
|
|
228
|
+
.map((entry) => `${entry.id} (indexes ${entry.indexes.join(", ")})`)
|
|
229
|
+
.join("; ");
|
|
230
|
+
problems.push(`Duplicate task ids detected (case-insensitive): ${details}`);
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
content: [{ type: "text", text: `Invalid tasks: ${problems.join(". ")}` }],
|
|
234
|
+
details: {
|
|
235
|
+
projectAgentsDir,
|
|
236
|
+
results: [],
|
|
237
|
+
totalDurationMs: 0,
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
176
242
|
// Derive artifacts directory
|
|
177
243
|
const sessionFile = session.getSessionFile();
|
|
178
244
|
const artifactsDir = sessionFile ? getArtifactsDir(sessionFile) : null;
|
|
@@ -197,88 +263,59 @@ export async function createTaskTool(
|
|
|
197
263
|
};
|
|
198
264
|
|
|
199
265
|
try {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
skippedSelfRecursion = blockedTasks.length;
|
|
208
|
-
|
|
209
|
-
if (skippedSelfRecursion > 0 && tasks.length === 0) {
|
|
210
|
-
return {
|
|
211
|
-
content: [
|
|
212
|
-
{
|
|
213
|
-
type: "text",
|
|
214
|
-
text: `Cannot spawn ${blockedAgent} agent from within itself (recursion prevention). Use a different agent type.`,
|
|
215
|
-
},
|
|
216
|
-
],
|
|
217
|
-
details: {
|
|
218
|
-
projectAgentsDir,
|
|
219
|
-
results: [],
|
|
220
|
-
totalDurationMs: Date.now() - startTime,
|
|
266
|
+
// Check self-recursion prevention
|
|
267
|
+
if (blockedAgent && agentName === blockedAgent) {
|
|
268
|
+
return {
|
|
269
|
+
content: [
|
|
270
|
+
{
|
|
271
|
+
type: "text",
|
|
272
|
+
text: `Cannot spawn ${blockedAgent} agent from within itself (recursion prevention). Use a different agent type.`,
|
|
221
273
|
},
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const available = agents.map((a) => a.name).join(", ");
|
|
230
|
-
return {
|
|
231
|
-
content: [{ type: "text", text: `Unknown agent: ${task.agent}. Available: ${available}` }],
|
|
232
|
-
details: {
|
|
233
|
-
projectAgentsDir,
|
|
234
|
-
results: [],
|
|
235
|
-
totalDurationMs: Date.now() - startTime,
|
|
236
|
-
},
|
|
237
|
-
};
|
|
238
|
-
}
|
|
274
|
+
],
|
|
275
|
+
details: {
|
|
276
|
+
projectAgentsDir,
|
|
277
|
+
results: [],
|
|
278
|
+
totalDurationMs: Date.now() - startTime,
|
|
279
|
+
},
|
|
280
|
+
};
|
|
239
281
|
}
|
|
240
282
|
|
|
241
283
|
// Check spawn restrictions from parent
|
|
242
284
|
const parentSpawns = session.getSessionSpawns() ?? "*";
|
|
243
285
|
const allowedSpawns = parentSpawns.split(",").map((s) => s.trim());
|
|
244
|
-
const isSpawnAllowed = (
|
|
286
|
+
const isSpawnAllowed = (): boolean => {
|
|
245
287
|
if (parentSpawns === "") return false; // Empty = deny all
|
|
246
288
|
if (parentSpawns === "*") return true; // Wildcard = allow all
|
|
247
289
|
return allowedSpawns.includes(agentName);
|
|
248
290
|
};
|
|
249
291
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
};
|
|
261
|
-
}
|
|
292
|
+
if (!isSpawnAllowed()) {
|
|
293
|
+
const allowed = parentSpawns === "" ? "none (spawns disabled for this agent)" : parentSpawns;
|
|
294
|
+
return {
|
|
295
|
+
content: [{ type: "text", text: `Cannot spawn '${agentName}'. Allowed: ${allowed}` }],
|
|
296
|
+
details: {
|
|
297
|
+
projectAgentsDir,
|
|
298
|
+
results: [],
|
|
299
|
+
totalDurationMs: Date.now() - startTime,
|
|
300
|
+
},
|
|
301
|
+
};
|
|
262
302
|
}
|
|
263
303
|
|
|
264
|
-
// Build full prompts with context prepended
|
|
304
|
+
// Build full prompts with context prepended
|
|
265
305
|
const tasksWithContext = tasks.map((t) => ({
|
|
266
|
-
agent: t.agent,
|
|
267
306
|
task: context ? `${context}\n\n${t.task}` : t.task,
|
|
268
|
-
model: t.model,
|
|
269
307
|
description: t.description,
|
|
270
|
-
taskId:
|
|
308
|
+
taskId: t.id,
|
|
271
309
|
}));
|
|
272
310
|
|
|
273
311
|
// Initialize progress for all tasks
|
|
274
312
|
for (let i = 0; i < tasksWithContext.length; i++) {
|
|
275
313
|
const t = tasksWithContext[i];
|
|
276
|
-
const agentCfg = getAgent(agents, t.agent);
|
|
277
314
|
progressMap.set(i, {
|
|
278
315
|
index: i,
|
|
279
316
|
taskId: t.taskId,
|
|
280
|
-
agent:
|
|
281
|
-
agentSource:
|
|
317
|
+
agent: agentName,
|
|
318
|
+
agentSource: agent.source,
|
|
282
319
|
status: "pending",
|
|
283
320
|
task: t.task,
|
|
284
321
|
recentTools: [],
|
|
@@ -286,36 +323,40 @@ export async function createTaskTool(
|
|
|
286
323
|
toolCount: 0,
|
|
287
324
|
tokens: 0,
|
|
288
325
|
durationMs: 0,
|
|
289
|
-
modelOverride:
|
|
326
|
+
modelOverride: model,
|
|
290
327
|
description: t.description,
|
|
291
328
|
});
|
|
292
329
|
}
|
|
293
330
|
emitProgress();
|
|
294
331
|
|
|
295
332
|
// Execute in parallel with concurrency limit
|
|
296
|
-
const results = await mapWithConcurrencyLimit(
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
333
|
+
const results = await mapWithConcurrencyLimit(
|
|
334
|
+
tasksWithContext,
|
|
335
|
+
MAX_CONCURRENCY,
|
|
336
|
+
async (task, index) => {
|
|
337
|
+
return runSubprocess({
|
|
338
|
+
cwd: session.cwd,
|
|
339
|
+
agent,
|
|
340
|
+
task: task.task,
|
|
341
|
+
description: task.description,
|
|
342
|
+
index,
|
|
343
|
+
taskId: task.taskId,
|
|
344
|
+
context: undefined, // Already prepended above
|
|
345
|
+
modelOverride: model,
|
|
346
|
+
outputSchema,
|
|
347
|
+
sessionFile,
|
|
348
|
+
persistArtifacts: !!artifactsDir,
|
|
349
|
+
artifactsDir: effectiveArtifactsDir,
|
|
350
|
+
signal,
|
|
351
|
+
eventBus: undefined,
|
|
352
|
+
onProgress: (progress) => {
|
|
353
|
+
progressMap.set(index, structuredClone(progress));
|
|
354
|
+
emitProgress();
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
},
|
|
358
|
+
signal,
|
|
359
|
+
);
|
|
319
360
|
|
|
320
361
|
// Aggregate usage from executor results (already accumulated incrementally)
|
|
321
362
|
const aggregatedUsage = createUsageTotals();
|
|
@@ -349,14 +390,10 @@ export async function createTaskTool(
|
|
|
349
390
|
return `[${r.agent}] ${status}${meta} ${r.taskId}\n${preview}`;
|
|
350
391
|
});
|
|
351
392
|
|
|
352
|
-
const skippedNote =
|
|
353
|
-
skippedSelfRecursion > 0
|
|
354
|
-
? ` (${skippedSelfRecursion} ${blockedAgent} task${skippedSelfRecursion > 1 ? "s" : ""} skipped - self-recursion blocked)`
|
|
355
|
-
: "";
|
|
356
393
|
const outputIds = results.map((r) => r.taskId);
|
|
357
394
|
const outputHint =
|
|
358
395
|
outputIds.length > 0 ? `\n\nUse output tool for full logs: output ids ${outputIds.join(", ")}` : "";
|
|
359
|
-
const summary = `${successCount}/${results.length} succeeded
|
|
396
|
+
const summary = `${successCount}/${results.length} succeeded [${formatDuration(
|
|
360
397
|
totalDuration,
|
|
361
398
|
)}]\n\n${summaries.join("\n\n---\n\n")}${outputHint}`;
|
|
362
399
|
|