@oh-my-pi/pi-coding-agent 8.0.20 → 8.1.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 +105 -0
- package/package.json +14 -11
- package/scripts/generate-wasm-b64.ts +24 -0
- package/src/capability/context-file.ts +1 -1
- package/src/capability/extension-module.ts +1 -1
- package/src/capability/extension.ts +1 -1
- package/src/capability/hook.ts +1 -1
- package/src/capability/instruction.ts +1 -1
- package/src/capability/mcp.ts +1 -1
- package/src/capability/prompt.ts +1 -1
- package/src/capability/rule.ts +1 -1
- package/src/capability/settings.ts +1 -1
- package/src/capability/skill.ts +1 -1
- package/src/capability/slash-command.ts +1 -1
- package/src/capability/ssh.ts +1 -1
- package/src/capability/system-prompt.ts +1 -1
- package/src/capability/tool.ts +1 -1
- package/src/cli/args.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -5
- package/src/commit/agentic/agent.ts +309 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +359 -0
- package/src/commit/agentic/prompts/analyze-file.md +22 -0
- package/src/commit/agentic/prompts/session-user.md +26 -0
- package/src/commit/agentic/prompts/split-confirm.md +1 -0
- package/src/commit/agentic/prompts/system.md +40 -0
- package/src/commit/agentic/state.ts +74 -0
- package/src/commit/agentic/tools/analyze-file.ts +131 -0
- package/src/commit/agentic/tools/git-file-diff.ts +194 -0
- package/src/commit/agentic/tools/git-hunk.ts +50 -0
- package/src/commit/agentic/tools/git-overview.ts +84 -0
- package/src/commit/agentic/tools/index.ts +56 -0
- package/src/commit/agentic/tools/propose-changelog.ts +128 -0
- package/src/commit/agentic/tools/propose-commit.ts +154 -0
- package/src/commit/agentic/tools/recent-commits.ts +81 -0
- package/src/commit/agentic/tools/split-commit.ts +284 -0
- package/src/commit/agentic/topo-sort.ts +44 -0
- package/src/commit/agentic/trivial.ts +51 -0
- package/src/commit/agentic/validation.ts +200 -0
- package/src/commit/analysis/conventional.ts +169 -0
- package/src/commit/analysis/index.ts +4 -0
- package/src/commit/analysis/scope.ts +242 -0
- package/src/commit/analysis/summary.ts +114 -0
- package/src/commit/analysis/validation.ts +66 -0
- package/src/commit/changelog/detect.ts +36 -0
- package/src/commit/changelog/generate.ts +112 -0
- package/src/commit/changelog/index.ts +233 -0
- package/src/commit/changelog/parse.ts +44 -0
- package/src/commit/cli.ts +93 -0
- package/src/commit/git/diff.ts +148 -0
- package/src/commit/git/errors.ts +11 -0
- package/src/commit/git/index.ts +217 -0
- package/src/commit/git/operations.ts +53 -0
- package/src/commit/index.ts +5 -0
- package/src/commit/map-reduce/.map-phase.ts.kate-swp +0 -0
- package/src/commit/map-reduce/index.ts +63 -0
- package/src/commit/map-reduce/map-phase.ts +193 -0
- package/src/commit/map-reduce/reduce-phase.ts +147 -0
- package/src/commit/map-reduce/utils.ts +9 -0
- package/src/commit/message.ts +11 -0
- package/src/commit/model-selection.ts +84 -0
- package/src/commit/pipeline.ts +242 -0
- package/src/commit/prompts/analysis-system.md +155 -0
- package/src/commit/prompts/analysis-user.md +41 -0
- package/src/commit/prompts/changelog-system.md +56 -0
- package/src/commit/prompts/changelog-user.md +19 -0
- package/src/commit/prompts/file-observer-system.md +26 -0
- package/src/commit/prompts/file-observer-user.md +9 -0
- package/src/commit/prompts/reduce-system.md +60 -0
- package/src/commit/prompts/reduce-user.md +17 -0
- package/src/commit/prompts/summary-retry.md +4 -0
- package/src/commit/prompts/summary-system.md +52 -0
- package/src/commit/prompts/summary-user.md +13 -0
- package/src/commit/prompts/types-description.md +2 -0
- package/src/commit/types.ts +109 -0
- package/src/commit/utils/exclusions.ts +42 -0
- package/src/config/file-lock.ts +111 -0
- package/src/config/model-registry.ts +16 -7
- package/src/config/settings-manager.ts +115 -40
- package/src/config.ts +5 -5
- package/src/discovery/agents-md.ts +1 -1
- package/src/discovery/builtin.ts +1 -1
- package/src/discovery/claude.ts +1 -1
- package/src/discovery/cline.ts +1 -1
- package/src/discovery/codex.ts +1 -1
- package/src/discovery/cursor.ts +1 -1
- package/src/discovery/gemini.ts +1 -1
- package/src/discovery/github.ts +1 -1
- package/src/discovery/index.ts +11 -11
- package/src/discovery/mcp-json.ts +1 -1
- package/src/discovery/ssh.ts +1 -1
- package/src/discovery/vscode.ts +1 -1
- package/src/discovery/windsurf.ts +1 -1
- package/src/extensibility/custom-commands/loader.ts +1 -1
- package/src/extensibility/custom-commands/types.ts +1 -1
- package/src/extensibility/custom-tools/loader.ts +1 -1
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/loader.ts +1 -1
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/hooks/loader.ts +1 -1
- package/src/extensibility/hooks/types.ts +3 -3
- package/src/index.ts +10 -10
- package/src/ipy/executor.ts +97 -1
- package/src/lsp/index.ts +1 -1
- package/src/lsp/render.ts +90 -46
- package/src/main.ts +16 -3
- package/src/mcp/loader.ts +3 -3
- package/src/migrations.ts +3 -3
- package/src/modes/components/assistant-message.ts +29 -1
- package/src/modes/components/tool-execution.ts +5 -3
- package/src/modes/components/tree-selector.ts +1 -1
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +5 -3
- package/src/modes/rpc/rpc-client.ts +1 -1
- package/src/modes/rpc/rpc-mode.ts +1 -4
- package/src/modes/rpc/rpc-types.ts +1 -1
- package/src/modes/theme/mermaid-cache.ts +89 -0
- package/src/modes/theme/theme.ts +2 -0
- package/src/modes/types.ts +2 -2
- package/src/patch/index.ts +3 -9
- package/src/patch/shared.ts +33 -5
- package/src/prompts/tools/task.md +2 -0
- package/src/sdk.ts +60 -22
- package/src/session/agent-session.ts +3 -3
- package/src/session/agent-storage.ts +32 -28
- package/src/session/artifacts.ts +24 -1
- package/src/session/auth-storage.ts +25 -10
- package/src/session/storage-migration.ts +12 -53
- package/src/system-prompt.ts +2 -2
- package/src/task/.executor.ts.kate-swp +0 -0
- package/src/task/executor.ts +1 -1
- package/src/task/index.ts +10 -1
- package/src/task/output-manager.ts +94 -0
- package/src/task/render.ts +7 -12
- package/src/task/worker.ts +1 -1
- package/src/tools/ask.ts +35 -13
- package/src/tools/bash.ts +80 -87
- package/src/tools/calculator.ts +42 -40
- package/src/tools/complete.ts +1 -1
- package/src/tools/fetch.ts +67 -104
- package/src/tools/find.ts +83 -86
- package/src/tools/grep.ts +80 -96
- package/src/tools/index.ts +10 -7
- package/src/tools/ls.ts +39 -65
- package/src/tools/notebook.ts +48 -64
- package/src/tools/output-utils.ts +1 -1
- package/src/tools/python.ts +71 -183
- package/src/tools/read.ts +74 -15
- package/src/tools/render-utils.ts +1 -15
- package/src/tools/ssh.ts +43 -24
- package/src/tools/todo-write.ts +27 -15
- package/src/tools/write.ts +93 -64
- package/src/tui/code-cell.ts +115 -0
- package/src/tui/file-list.ts +48 -0
- package/src/tui/index.ts +11 -0
- package/src/tui/output-block.ts +73 -0
- package/src/tui/status-line.ts +40 -0
- package/src/tui/tree-list.ts +56 -0
- package/src/tui/types.ts +17 -0
- package/src/tui/utils.ts +49 -0
- package/src/vendor/photon/photon_rs_bg.wasm.b64.js +1 -0
- package/src/web/search/auth.ts +1 -1
- package/src/web/search/index.ts +1 -1
- package/src/web/search/render.ts +119 -163
- package/tsconfig.json +0 -42
package/src/tools/find.ts
CHANGED
|
@@ -3,10 +3,11 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
3
3
|
import { StringEnum } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
5
5
|
import type { RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
|
|
6
|
-
import {
|
|
6
|
+
import type { Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
7
7
|
import findDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/find.md" with { type: "text" };
|
|
8
8
|
import type { OutputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
|
|
9
9
|
import { ToolAbortError, ToolError, throwIfAborted } from "@oh-my-pi/pi-coding-agent/tools/tool-errors";
|
|
10
|
+
import { renderFileList, renderStatusLine, renderTreeList } from "@oh-my-pi/pi-coding-agent/tui";
|
|
10
11
|
import { ensureTool } from "@oh-my-pi/pi-coding-agent/utils/tools-manager";
|
|
11
12
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
12
13
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
@@ -14,10 +15,10 @@ import { ptree, untilAborted } from "@oh-my-pi/pi-utils";
|
|
|
14
15
|
import type { Static } from "@sinclair/typebox";
|
|
15
16
|
import { Type } from "@sinclair/typebox";
|
|
16
17
|
|
|
17
|
-
import type { ToolSession } from "
|
|
18
|
+
import type { ToolSession } from ".";
|
|
18
19
|
import { applyListLimit } from "./list-limit";
|
|
19
20
|
import { resolveToCwd } from "./path-utils";
|
|
20
|
-
import {
|
|
21
|
+
import { formatCount, formatEmptyMessage, formatErrorMessage, PREVIEW_LIMITS } from "./render-utils";
|
|
21
22
|
import { toolResult } from "./tool-result";
|
|
22
23
|
import { type TruncationResult, truncateHead } from "./truncate";
|
|
23
24
|
|
|
@@ -34,6 +35,7 @@ const findSchema = Type.Object({
|
|
|
34
35
|
});
|
|
35
36
|
|
|
36
37
|
const DEFAULT_LIMIT = 1000;
|
|
38
|
+
const FD_TIMEOUT_MS = 5000;
|
|
37
39
|
|
|
38
40
|
export interface FindToolDetails {
|
|
39
41
|
truncation?: TruncationResult;
|
|
@@ -132,6 +134,11 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
132
134
|
|
|
133
135
|
return untilAborted(signal, async () => {
|
|
134
136
|
const searchPath = resolveToCwd(searchDir || ".", this.session.cwd);
|
|
137
|
+
|
|
138
|
+
if (searchPath === "/") {
|
|
139
|
+
throw new ToolError("Searching from root directory '/' is not allowed");
|
|
140
|
+
}
|
|
141
|
+
|
|
135
142
|
const scopePath = (() => {
|
|
136
143
|
const relative = path.relative(this.session.cwd, searchPath).replace(/\\/g, "/");
|
|
137
144
|
return relative.length === 0 ? "." : relative;
|
|
@@ -246,7 +253,9 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
246
253
|
"--absolute-path",
|
|
247
254
|
searchPath,
|
|
248
255
|
];
|
|
249
|
-
const
|
|
256
|
+
const timeoutSignal = AbortSignal.timeout(FD_TIMEOUT_MS);
|
|
257
|
+
const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
258
|
+
const { stdout: gitignoreStdout } = await runFd(fdPath, gitignoreArgs, combinedSignal);
|
|
250
259
|
for (const rawLine of gitignoreStdout.split("\n")) {
|
|
251
260
|
const file = rawLine.trim();
|
|
252
261
|
if (!file) continue;
|
|
@@ -266,8 +275,10 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
266
275
|
// Pattern and path
|
|
267
276
|
args.push(effectivePattern, searchPath);
|
|
268
277
|
|
|
269
|
-
// Run fd
|
|
270
|
-
const
|
|
278
|
+
// Run fd with timeout
|
|
279
|
+
const mainTimeoutSignal = AbortSignal.timeout(FD_TIMEOUT_MS);
|
|
280
|
+
const mainCombinedSignal = signal ? AbortSignal.any([signal, mainTimeoutSignal]) : mainTimeoutSignal;
|
|
281
|
+
const { stdout, stderr, exitCode } = await runFd(fdPath, args, mainCombinedSignal);
|
|
271
282
|
const output = stdout.trim();
|
|
272
283
|
|
|
273
284
|
// fd exit codes: 0 = found files, 1 = no matches, other = error
|
|
@@ -371,10 +382,6 @@ const COLLAPSED_LIST_LIMIT = PREVIEW_LIMITS.COLLAPSED_ITEMS;
|
|
|
371
382
|
export const findToolRenderer = {
|
|
372
383
|
inline: true,
|
|
373
384
|
renderCall(args: FindRenderArgs, uiTheme: Theme): Component {
|
|
374
|
-
const ui = new ToolUIKit(uiTheme);
|
|
375
|
-
const label = ui.title("Find");
|
|
376
|
-
let text = `${uiTheme.format.bullet} ${label} ${uiTheme.fg("accent", args.pattern || "*")}`;
|
|
377
|
-
|
|
378
385
|
const meta: string[] = [];
|
|
379
386
|
if (args.path) meta.push(`in ${args.path}`);
|
|
380
387
|
if (args.type && args.type !== "all") meta.push(`type:${args.type}`);
|
|
@@ -382,8 +389,10 @@ export const findToolRenderer = {
|
|
|
382
389
|
if (args.sortByMtime) meta.push("sort:mtime");
|
|
383
390
|
if (args.limit !== undefined) meta.push(`limit:${args.limit}`);
|
|
384
391
|
|
|
385
|
-
text
|
|
386
|
-
|
|
392
|
+
const text = renderStatusLine(
|
|
393
|
+
{ icon: "pending", title: "Find", description: args.pattern || "*", meta },
|
|
394
|
+
uiTheme,
|
|
395
|
+
);
|
|
387
396
|
return new Text(text, 0, 0);
|
|
388
397
|
},
|
|
389
398
|
|
|
@@ -391,106 +400,94 @@ export const findToolRenderer = {
|
|
|
391
400
|
result: { content: Array<{ type: string; text?: string }>; details?: FindToolDetails; isError?: boolean },
|
|
392
401
|
{ expanded }: RenderResultOptions,
|
|
393
402
|
uiTheme: Theme,
|
|
403
|
+
args?: FindRenderArgs,
|
|
394
404
|
): Component {
|
|
395
|
-
const ui = new ToolUIKit(uiTheme);
|
|
396
405
|
const details = result.details;
|
|
397
406
|
|
|
398
407
|
if (result.isError || details?.error) {
|
|
399
408
|
const errorText = details?.error || result.content?.find((c) => c.type === "text")?.text || "Unknown error";
|
|
400
|
-
return new Text(
|
|
409
|
+
return new Text(formatErrorMessage(errorText, uiTheme), 0, 0);
|
|
401
410
|
}
|
|
402
411
|
|
|
403
412
|
const hasDetailedData = details?.fileCount !== undefined;
|
|
404
413
|
const textContent = result.content?.find((c) => c.type === "text")?.text;
|
|
405
414
|
|
|
406
415
|
if (!hasDetailedData) {
|
|
407
|
-
if (
|
|
408
|
-
|
|
416
|
+
if (
|
|
417
|
+
!textContent ||
|
|
418
|
+
textContent.includes("No files matching") ||
|
|
419
|
+
textContent.includes("No files found") ||
|
|
420
|
+
textContent.trim() === ""
|
|
421
|
+
) {
|
|
422
|
+
return new Text(formatEmptyMessage("No files found", uiTheme), 0, 0);
|
|
409
423
|
}
|
|
410
424
|
|
|
411
425
|
const lines = textContent.split("\n").filter((l) => l.trim());
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
426
|
+
const header = renderStatusLine(
|
|
427
|
+
{
|
|
428
|
+
icon: "success",
|
|
429
|
+
title: "Find",
|
|
430
|
+
description: args?.pattern,
|
|
431
|
+
meta: [formatCount("file", lines.length)],
|
|
432
|
+
},
|
|
433
|
+
uiTheme,
|
|
434
|
+
);
|
|
435
|
+
const listLines = renderTreeList(
|
|
436
|
+
{
|
|
437
|
+
items: lines,
|
|
438
|
+
expanded,
|
|
439
|
+
maxCollapsed: COLLAPSED_LIST_LIMIT,
|
|
440
|
+
itemType: "file",
|
|
441
|
+
renderItem: (line) => uiTheme.fg("accent", line),
|
|
442
|
+
},
|
|
443
|
+
uiTheme,
|
|
444
|
+
);
|
|
445
|
+
return new Text([header, ...listLines].join("\n"), 0, 0);
|
|
431
446
|
}
|
|
432
447
|
|
|
433
448
|
const fileCount = details?.fileCount ?? 0;
|
|
434
|
-
const truncation = details?.meta?.truncation;
|
|
449
|
+
const truncation = details?.truncation ?? details?.meta?.truncation;
|
|
435
450
|
const limits = details?.meta?.limits;
|
|
436
|
-
const truncated = Boolean(
|
|
437
|
-
details?.truncated || truncation || limits?.resultLimit || limits?.headLimit || limits?.matchLimit,
|
|
438
|
-
);
|
|
451
|
+
const truncated = Boolean(details?.truncated || truncation || details?.resultLimitReached || limits?.resultLimit);
|
|
439
452
|
const files = details?.files ?? [];
|
|
440
453
|
|
|
441
454
|
if (fileCount === 0) {
|
|
442
|
-
|
|
455
|
+
const header = renderStatusLine(
|
|
456
|
+
{ icon: "warning", title: "Find", description: args?.pattern, meta: ["0 files"] },
|
|
457
|
+
uiTheme,
|
|
458
|
+
);
|
|
459
|
+
return new Text([header, formatEmptyMessage("No files found", uiTheme)].join("\n"), 0, 0);
|
|
443
460
|
}
|
|
461
|
+
const meta: string[] = [formatCount("file", fileCount)];
|
|
462
|
+
if (details?.scopePath) meta.push(`in ${details.scopePath}`);
|
|
463
|
+
if (truncated) meta.push(uiTheme.fg("warning", "truncated"));
|
|
464
|
+
const header = renderStatusLine(
|
|
465
|
+
{ icon: truncated ? "warning" : "success", title: "Find", description: args?.pattern, meta },
|
|
466
|
+
uiTheme,
|
|
467
|
+
);
|
|
444
468
|
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
469
|
+
const fileLines = renderFileList(
|
|
470
|
+
{
|
|
471
|
+
files: files.map((entry) => ({ path: entry, isDirectory: entry.endsWith("/") })),
|
|
472
|
+
expanded,
|
|
473
|
+
maxCollapsed: COLLAPSED_LIST_LIMIT,
|
|
474
|
+
},
|
|
475
|
+
uiTheme,
|
|
476
|
+
);
|
|
453
477
|
|
|
454
478
|
const truncationReasons: string[] = [];
|
|
455
|
-
if (
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
479
|
+
if (details?.resultLimitReached) truncationReasons.push(`limit ${details.resultLimitReached} results`);
|
|
480
|
+
if (limits?.resultLimit) truncationReasons.push(`limit ${limits.resultLimit.reached} results`);
|
|
481
|
+
if (truncation) truncationReasons.push(truncation.truncatedBy === "lines" ? "line limit" : "size limit");
|
|
482
|
+
const artifactId = truncation && "artifactId" in truncation ? truncation.artifactId : undefined;
|
|
483
|
+
if (artifactId) truncationReasons.push(`full output: artifact://${artifactId}`);
|
|
484
|
+
|
|
485
|
+
const extraLines: string[] = [];
|
|
486
|
+
if (truncationReasons.length > 0) {
|
|
487
|
+
extraLines.push(uiTheme.fg("warning", `truncated: ${truncationReasons.join(", ")}`));
|
|
463
488
|
}
|
|
464
489
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
if (files.length > 0) {
|
|
468
|
-
for (let i = 0; i < maxFiles; i++) {
|
|
469
|
-
const isLast = i === maxFiles - 1 && !hasMoreFiles && !hasTruncation;
|
|
470
|
-
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
471
|
-
const entry = files[i];
|
|
472
|
-
const isDir = entry.endsWith("/");
|
|
473
|
-
const entryPath = isDir ? entry.slice(0, -1) : entry;
|
|
474
|
-
const lang = isDir ? undefined : getLanguageFromPath(entryPath);
|
|
475
|
-
const entryIcon = isDir
|
|
476
|
-
? uiTheme.fg("accent", uiTheme.icon.folder)
|
|
477
|
-
: uiTheme.fg("muted", uiTheme.getLangIcon(lang));
|
|
478
|
-
text += `\n ${uiTheme.fg("dim", branch)} ${entryIcon} ${uiTheme.fg("accent", entry)}`;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
if (hasMoreFiles) {
|
|
482
|
-
const moreFilesBranch = hasTruncation ? uiTheme.tree.branch : uiTheme.tree.last;
|
|
483
|
-
text += `\n ${uiTheme.fg("dim", moreFilesBranch)} ${uiTheme.fg(
|
|
484
|
-
"muted",
|
|
485
|
-
ui.moreItems(files.length - maxFiles, "file"),
|
|
486
|
-
)}`;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
if (hasTruncation) {
|
|
491
|
-
text += `\n ${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("warning", `truncated: ${truncationReasons.join(", ")}`)}`;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
return new Text(text, 0, 0);
|
|
490
|
+
return new Text([header, ...fileLines, ...extraLines].join("\n"), 0, 0);
|
|
495
491
|
},
|
|
492
|
+
mergeCallAndResult: true,
|
|
496
493
|
};
|
package/src/tools/grep.ts
CHANGED
|
@@ -3,10 +3,11 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
3
3
|
import { StringEnum } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
5
5
|
import type { RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
|
|
6
|
-
import {
|
|
6
|
+
import type { Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
7
7
|
import grepDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/grep.md" with { type: "text" };
|
|
8
8
|
import type { OutputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
|
|
9
9
|
import { ToolAbortError, ToolError } from "@oh-my-pi/pi-coding-agent/tools/tool-errors";
|
|
10
|
+
import { renderFileList, renderStatusLine, renderTreeList } from "@oh-my-pi/pi-coding-agent/tui";
|
|
10
11
|
import { ensureTool } from "@oh-my-pi/pi-coding-agent/utils/tools-manager";
|
|
11
12
|
import { untilAborted } from "@oh-my-pi/pi-coding-agent/utils/utils";
|
|
12
13
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
@@ -14,10 +15,10 @@ import { Text } from "@oh-my-pi/pi-tui";
|
|
|
14
15
|
import { ptree, readLines } from "@oh-my-pi/pi-utils";
|
|
15
16
|
import { Type } from "@sinclair/typebox";
|
|
16
17
|
import { $ } from "bun";
|
|
17
|
-
import type { ToolSession } from "
|
|
18
|
+
import type { ToolSession } from ".";
|
|
18
19
|
import { applyListLimit } from "./list-limit";
|
|
19
20
|
import { resolveToCwd } from "./path-utils";
|
|
20
|
-
import {
|
|
21
|
+
import { formatCount, formatEmptyMessage, formatErrorMessage, PREVIEW_LIMITS } from "./render-utils";
|
|
21
22
|
import { toolResult } from "./tool-result";
|
|
22
23
|
import { DEFAULT_MAX_COLUMN, type TruncationResult, truncateHead, truncateLine } from "./truncate";
|
|
23
24
|
|
|
@@ -600,10 +601,6 @@ const COLLAPSED_TEXT_LIMIT = PREVIEW_LIMITS.COLLAPSED_LINES * 2;
|
|
|
600
601
|
export const grepToolRenderer = {
|
|
601
602
|
inline: true,
|
|
602
603
|
renderCall(args: GrepRenderArgs, uiTheme: Theme): Component {
|
|
603
|
-
const ui = new ToolUIKit(uiTheme);
|
|
604
|
-
const label = ui.title("Grep");
|
|
605
|
-
let text = `${uiTheme.format.bullet} ${label} ${uiTheme.fg("accent", args.pattern || "?")}`;
|
|
606
|
-
|
|
607
604
|
const meta: string[] = [];
|
|
608
605
|
if (args.path) meta.push(`in ${args.path}`);
|
|
609
606
|
if (args.glob) meta.push(`glob:${args.glob}`);
|
|
@@ -619,8 +616,10 @@ export const grepToolRenderer = {
|
|
|
619
616
|
if (args.context !== undefined) meta.push(`context:${args.context}`);
|
|
620
617
|
if (args.limit !== undefined) meta.push(`limit:${args.limit}`);
|
|
621
618
|
|
|
622
|
-
text
|
|
623
|
-
|
|
619
|
+
const text = renderStatusLine(
|
|
620
|
+
{ icon: "pending", title: "Grep", description: args.pattern || "?", meta },
|
|
621
|
+
uiTheme,
|
|
622
|
+
);
|
|
624
623
|
return new Text(text, 0, 0);
|
|
625
624
|
},
|
|
626
625
|
|
|
@@ -628,13 +627,13 @@ export const grepToolRenderer = {
|
|
|
628
627
|
result: { content: Array<{ type: string; text?: string }>; details?: GrepToolDetails; isError?: boolean },
|
|
629
628
|
{ expanded }: RenderResultOptions,
|
|
630
629
|
uiTheme: Theme,
|
|
630
|
+
args?: GrepRenderArgs,
|
|
631
631
|
): Component {
|
|
632
|
-
const ui = new ToolUIKit(uiTheme);
|
|
633
632
|
const details = result.details;
|
|
634
633
|
|
|
635
634
|
if (result.isError || details?.error) {
|
|
636
635
|
const errorText = details?.error || result.content?.find((c) => c.type === "text")?.text || "Unknown error";
|
|
637
|
-
return new Text(
|
|
636
|
+
return new Text(formatErrorMessage(errorText, uiTheme), 0, 0);
|
|
638
637
|
}
|
|
639
638
|
|
|
640
639
|
const hasDetailedData = details?.matchCount !== undefined || details?.fileCount !== undefined;
|
|
@@ -642,31 +641,25 @@ export const grepToolRenderer = {
|
|
|
642
641
|
if (!hasDetailedData) {
|
|
643
642
|
const textContent = result.content?.find((c) => c.type === "text")?.text;
|
|
644
643
|
if (!textContent || textContent === "No matches found") {
|
|
645
|
-
return new Text(
|
|
644
|
+
return new Text(formatEmptyMessage("No matches found", uiTheme), 0, 0);
|
|
646
645
|
}
|
|
647
|
-
|
|
648
646
|
const lines = textContent.split("\n").filter((line) => line.trim() !== "");
|
|
649
|
-
const
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
if (remaining > 0) {
|
|
666
|
-
text += `\n ${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("muted", ui.moreItems(remaining, "item"))}`;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
return new Text(text, 0, 0);
|
|
647
|
+
const description = args?.pattern ?? undefined;
|
|
648
|
+
const header = renderStatusLine(
|
|
649
|
+
{ icon: "success", title: "Grep", description, meta: [formatCount("item", lines.length)] },
|
|
650
|
+
uiTheme,
|
|
651
|
+
);
|
|
652
|
+
const listLines = renderTreeList(
|
|
653
|
+
{
|
|
654
|
+
items: lines,
|
|
655
|
+
expanded,
|
|
656
|
+
maxCollapsed: COLLAPSED_TEXT_LIMIT,
|
|
657
|
+
itemType: "item",
|
|
658
|
+
renderItem: (line) => uiTheme.fg("toolOutput", line),
|
|
659
|
+
},
|
|
660
|
+
uiTheme,
|
|
661
|
+
);
|
|
662
|
+
return new Text([header, ...listLines].join("\n"), 0, 0);
|
|
670
663
|
}
|
|
671
664
|
|
|
672
665
|
const matchCount = details?.matchCount ?? 0;
|
|
@@ -685,79 +678,70 @@ export const grepToolRenderer = {
|
|
|
685
678
|
const files = details?.files ?? [];
|
|
686
679
|
|
|
687
680
|
if (matchCount === 0) {
|
|
688
|
-
|
|
681
|
+
const header = renderStatusLine(
|
|
682
|
+
{ icon: "warning", title: "Grep", description: args?.pattern, meta: ["0 matches"] },
|
|
683
|
+
uiTheme,
|
|
684
|
+
);
|
|
685
|
+
return new Text([header, formatEmptyMessage("No matches found", uiTheme)].join("\n"), 0, 0);
|
|
689
686
|
}
|
|
690
687
|
|
|
691
|
-
const icon = uiTheme.styledSymbol("status.success", "success");
|
|
692
688
|
const summaryParts =
|
|
693
689
|
mode === "files_with_matches"
|
|
694
|
-
? [
|
|
695
|
-
: [
|
|
696
|
-
const
|
|
697
|
-
|
|
690
|
+
? [formatCount("file", fileCount)]
|
|
691
|
+
: [formatCount("match", matchCount), formatCount("file", fileCount)];
|
|
692
|
+
const meta = [...summaryParts];
|
|
693
|
+
if (details?.scopePath) meta.push(`in ${details.scopePath}`);
|
|
694
|
+
if (truncated) meta.push(uiTheme.fg("warning", "truncated"));
|
|
695
|
+
const description = args?.pattern ?? undefined;
|
|
696
|
+
const header = renderStatusLine(
|
|
697
|
+
{ icon: truncated ? "warning" : "success", title: "Grep", description, meta },
|
|
698
|
+
uiTheme,
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
if (mode === "content") {
|
|
702
|
+
const textContent = result.content?.find((c) => c.type === "text")?.text ?? "";
|
|
703
|
+
const contentLines = textContent.split("\n").filter((line) => line.trim().length > 0);
|
|
704
|
+
const matchLines = renderTreeList(
|
|
705
|
+
{
|
|
706
|
+
items: contentLines,
|
|
707
|
+
expanded,
|
|
708
|
+
maxCollapsed: COLLAPSED_TEXT_LIMIT,
|
|
709
|
+
itemType: "match",
|
|
710
|
+
renderItem: (line) => uiTheme.fg("toolOutput", line),
|
|
711
|
+
},
|
|
712
|
+
uiTheme,
|
|
713
|
+
);
|
|
714
|
+
return new Text([header, ...matchLines].join("\n"), 0, 0);
|
|
715
|
+
}
|
|
698
716
|
|
|
699
717
|
const fileEntries: Array<{ path: string; count?: number }> = details?.fileMatches?.length
|
|
700
718
|
? details.fileMatches.map((entry) => ({ path: entry.path, count: entry.count }))
|
|
701
719
|
: files.map((path) => ({ path }));
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
720
|
+
const fileLines = renderFileList(
|
|
721
|
+
{
|
|
722
|
+
files: fileEntries.map((entry) => ({
|
|
723
|
+
path: entry.path,
|
|
724
|
+
isDirectory: entry.path.endsWith("/"),
|
|
725
|
+
meta: entry.count !== undefined ? `(${entry.count} match${entry.count !== 1 ? "es" : ""})` : undefined,
|
|
726
|
+
})),
|
|
727
|
+
expanded,
|
|
728
|
+
maxCollapsed: COLLAPSED_LIST_LIMIT,
|
|
729
|
+
},
|
|
730
|
+
uiTheme,
|
|
731
|
+
);
|
|
707
732
|
|
|
708
733
|
const truncationReasons: string[] = [];
|
|
709
|
-
if (limits?.matchLimit) {
|
|
710
|
-
|
|
711
|
-
}
|
|
712
|
-
if (
|
|
713
|
-
|
|
714
|
-
}
|
|
715
|
-
if (limits?.headLimit) {
|
|
716
|
-
truncationReasons.push(`head limit ${limits.headLimit.reached}`);
|
|
717
|
-
}
|
|
718
|
-
if (truncation) {
|
|
719
|
-
truncationReasons.push(truncation.truncatedBy === "lines" ? "line limit" : "size limit");
|
|
720
|
-
}
|
|
721
|
-
if (limits?.columnTruncated) {
|
|
722
|
-
truncationReasons.push(`line length ${limits.columnTruncated.maxColumn}`);
|
|
723
|
-
}
|
|
724
|
-
if (truncation?.artifactId) {
|
|
725
|
-
truncationReasons.push(`full output: artifact://${truncation.artifactId}`);
|
|
726
|
-
}
|
|
734
|
+
if (limits?.matchLimit) truncationReasons.push(`limit ${limits.matchLimit.reached} matches`);
|
|
735
|
+
if (limits?.resultLimit) truncationReasons.push(`limit ${limits.resultLimit.reached} results`);
|
|
736
|
+
if (limits?.headLimit) truncationReasons.push(`head limit ${limits.headLimit.reached}`);
|
|
737
|
+
if (truncation) truncationReasons.push(truncation.truncatedBy === "lines" ? "line limit" : "size limit");
|
|
738
|
+
if (limits?.columnTruncated) truncationReasons.push(`line length ${limits.columnTruncated.maxColumn}`);
|
|
739
|
+
if (truncation?.artifactId) truncationReasons.push(`full output: artifact://${truncation.artifactId}`);
|
|
727
740
|
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
if (fileEntries.length > 0) {
|
|
731
|
-
for (let i = 0; i < maxFiles; i++) {
|
|
732
|
-
const entry = fileEntries[i];
|
|
733
|
-
const isLast = i === maxFiles - 1 && !hasMoreFiles && !hasTruncation;
|
|
734
|
-
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
735
|
-
const isDir = entry.path.endsWith("/");
|
|
736
|
-
const entryPath = isDir ? entry.path.slice(0, -1) : entry.path;
|
|
737
|
-
const lang = isDir ? undefined : getLanguageFromPath(entryPath);
|
|
738
|
-
const entryIcon = isDir
|
|
739
|
-
? uiTheme.fg("accent", uiTheme.icon.folder)
|
|
740
|
-
: uiTheme.fg("muted", uiTheme.getLangIcon(lang));
|
|
741
|
-
const countLabel =
|
|
742
|
-
entry.count !== undefined
|
|
743
|
-
? ` ${uiTheme.fg("dim", `(${entry.count} match${entry.count !== 1 ? "es" : ""})`)}`
|
|
744
|
-
: "";
|
|
745
|
-
text += `\n ${uiTheme.fg("dim", branch)} ${entryIcon} ${uiTheme.fg("accent", entry.path)}${countLabel}`;
|
|
746
|
-
}
|
|
741
|
+
const extraLines =
|
|
742
|
+
truncationReasons.length > 0 ? [uiTheme.fg("warning", `truncated: ${truncationReasons.join(", ")}`)] : [];
|
|
747
743
|
|
|
748
|
-
|
|
749
|
-
const moreFilesBranch = hasTruncation ? uiTheme.tree.branch : uiTheme.tree.last;
|
|
750
|
-
text += `\n ${uiTheme.fg("dim", moreFilesBranch)} ${uiTheme.fg(
|
|
751
|
-
"muted",
|
|
752
|
-
ui.moreItems(fileEntries.length - maxFiles, "file"),
|
|
753
|
-
)}`;
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
if (hasTruncation) {
|
|
758
|
-
text += `\n ${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("warning", `truncated: ${truncationReasons.join(", ")}`)}`;
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
return new Text(text, 0, 0);
|
|
744
|
+
return new Text([header, ...fileLines, ...extraLines].join("\n"), 0, 0);
|
|
762
745
|
},
|
|
746
|
+
mergeCallAndResult: true,
|
|
763
747
|
};
|
package/src/tools/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Exa MCP tools (22 tools)
|
|
2
|
-
export { exaTools } from "@oh-my-pi/pi-coding-agent/exa
|
|
2
|
+
export { exaTools } from "@oh-my-pi/pi-coding-agent/exa";
|
|
3
3
|
export type { ExaRenderDetails, ExaSearchResponse, ExaSearchResult } from "@oh-my-pi/pi-coding-agent/exa/types";
|
|
4
4
|
export {
|
|
5
5
|
type FileDiagnosticsResult,
|
|
@@ -11,9 +11,9 @@ export {
|
|
|
11
11
|
type LspWarmupOptions,
|
|
12
12
|
type LspWarmupResult,
|
|
13
13
|
warmupLspServers,
|
|
14
|
-
} from "@oh-my-pi/pi-coding-agent/lsp
|
|
14
|
+
} from "@oh-my-pi/pi-coding-agent/lsp";
|
|
15
15
|
export { EditTool, type EditToolDetails } from "@oh-my-pi/pi-coding-agent/patch";
|
|
16
|
-
export { BUNDLED_AGENTS, TaskTool } from "@oh-my-pi/pi-coding-agent/task
|
|
16
|
+
export { BUNDLED_AGENTS, TaskTool } from "@oh-my-pi/pi-coding-agent/task";
|
|
17
17
|
export {
|
|
18
18
|
companyWebSearchTools,
|
|
19
19
|
exaWebSearchTools,
|
|
@@ -31,7 +31,7 @@ export {
|
|
|
31
31
|
webSearchCustomTool,
|
|
32
32
|
webSearchDeepTool,
|
|
33
33
|
webSearchLinkedinTool,
|
|
34
|
-
} from "@oh-my-pi/pi-coding-agent/web/search
|
|
34
|
+
} from "@oh-my-pi/pi-coding-agent/web/search";
|
|
35
35
|
export { AskTool, type AskToolDetails } from "./ask";
|
|
36
36
|
export { BashTool, type BashToolDetails, type BashToolOptions } from "./bash";
|
|
37
37
|
export { CalculatorTool, type CalculatorToolDetails } from "./calculator";
|
|
@@ -64,12 +64,13 @@ import type { BashInterceptorRule } from "@oh-my-pi/pi-coding-agent/config/setti
|
|
|
64
64
|
import type { InternalUrlRouter } from "@oh-my-pi/pi-coding-agent/internal-urls";
|
|
65
65
|
import { getPreludeDocs, warmPythonEnvironment } from "@oh-my-pi/pi-coding-agent/ipy/executor";
|
|
66
66
|
import { checkPythonKernelAvailability } from "@oh-my-pi/pi-coding-agent/ipy/kernel";
|
|
67
|
-
import { LspTool } from "@oh-my-pi/pi-coding-agent/lsp
|
|
67
|
+
import { LspTool } from "@oh-my-pi/pi-coding-agent/lsp";
|
|
68
68
|
import { EditTool } from "@oh-my-pi/pi-coding-agent/patch";
|
|
69
69
|
import type { ArtifactManager } from "@oh-my-pi/pi-coding-agent/session/artifacts";
|
|
70
|
-
import { TaskTool } from "@oh-my-pi/pi-coding-agent/task
|
|
70
|
+
import { TaskTool } from "@oh-my-pi/pi-coding-agent/task";
|
|
71
|
+
import type { AgentOutputManager } from "@oh-my-pi/pi-coding-agent/task/output-manager";
|
|
71
72
|
import type { EventBus } from "@oh-my-pi/pi-coding-agent/utils/event-bus";
|
|
72
|
-
import { WebSearchTool } from "@oh-my-pi/pi-coding-agent/web/search
|
|
73
|
+
import { WebSearchTool } from "@oh-my-pi/pi-coding-agent/web/search";
|
|
73
74
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
74
75
|
import { AskTool } from "./ask";
|
|
75
76
|
import { BashTool } from "./bash";
|
|
@@ -125,6 +126,8 @@ export interface ToolSession {
|
|
|
125
126
|
mcpManager?: import("../mcp/manager").MCPManager;
|
|
126
127
|
/** Internal URL router for agent:// and skill:// URLs */
|
|
127
128
|
internalRouter?: InternalUrlRouter;
|
|
129
|
+
/** Agent output manager for unique agent:// IDs across task invocations */
|
|
130
|
+
agentOutputManager?: AgentOutputManager;
|
|
128
131
|
/** Settings manager for passing to subagents (avoids SQLite access in workers) */
|
|
129
132
|
settingsManager?: { serialize: () => import("@oh-my-pi/pi-coding-agent/config/settings-manager").Settings };
|
|
130
133
|
/** Settings manager (optional) */
|