@oh-my-pi/pi-coding-agent 13.19.0 → 14.0.2
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 +266 -1
- package/package.json +86 -20
- package/scripts/format-prompts.ts +2 -2
- package/src/autoresearch/apply-contract-to-state.ts +24 -0
- package/src/autoresearch/contract.ts +0 -44
- package/src/autoresearch/dashboard.ts +1 -2
- package/src/autoresearch/git.ts +91 -0
- package/src/autoresearch/helpers.ts +49 -0
- package/src/autoresearch/index.ts +28 -187
- package/src/autoresearch/prompt.md +26 -9
- package/src/autoresearch/state.ts +0 -6
- package/src/autoresearch/tools/init-experiment.ts +202 -117
- package/src/autoresearch/tools/log-experiment.ts +83 -125
- package/src/autoresearch/tools/run-experiment.ts +48 -10
- package/src/autoresearch/types.ts +2 -2
- package/src/capability/index.ts +4 -2
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/grep-cli.ts +8 -8
- package/src/cli/grievances-cli.ts +78 -0
- package/src/cli/read-cli.ts +67 -0
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/update-cli.ts +3 -3
- package/src/cli.ts +2 -0
- package/src/commands/grep.ts +6 -1
- package/src/commands/grievances.ts +20 -0
- package/src/commands/read.ts +33 -0
- package/src/commit/agentic/agent.ts +5 -5
- package/src/commit/agentic/index.ts +3 -4
- package/src/commit/agentic/tools/analyze-file.ts +3 -3
- package/src/commit/agentic/validation.ts +1 -1
- package/src/commit/analysis/conventional.ts +4 -4
- package/src/commit/analysis/summary.ts +3 -3
- package/src/commit/changelog/generate.ts +4 -4
- package/src/commit/map-reduce/map-phase.ts +4 -4
- package/src/commit/map-reduce/reduce-phase.ts +4 -4
- package/src/commit/pipeline.ts +3 -4
- package/src/config/prompt-templates.ts +44 -226
- package/src/config/resolve-config-value.ts +4 -2
- package/src/config/settings-schema.ts +54 -2
- package/src/config/settings.ts +25 -26
- package/src/dap/client.ts +674 -0
- package/src/dap/config.ts +150 -0
- package/src/dap/defaults.json +211 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1255 -0
- package/src/dap/types.ts +600 -0
- package/src/debug/log-viewer.ts +3 -2
- package/src/discovery/builtin.ts +1 -2
- package/src/discovery/codex.ts +2 -2
- package/src/discovery/github.ts +2 -1
- package/src/discovery/helpers.ts +2 -2
- package/src/discovery/opencode.ts +2 -2
- package/src/edit/diff.ts +818 -0
- package/src/edit/index.ts +309 -0
- package/src/edit/line-hash.ts +67 -0
- package/src/edit/modes/chunk.ts +454 -0
- package/src/{patch → edit/modes}/hashline.ts +741 -361
- package/src/{patch/applicator.ts → edit/modes/patch.ts} +420 -117
- package/src/{patch/fuzzy.ts → edit/modes/replace.ts} +519 -197
- package/src/{patch → edit}/normalize.ts +97 -76
- package/src/{patch/shared.ts → edit/renderer.ts} +181 -108
- package/src/exec/bash-executor.ts +4 -2
- package/src/exec/idle-timeout-watchdog.ts +126 -0
- package/src/exec/non-interactive-env.ts +5 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +2 -2
- package/src/extensibility/custom-commands/bundled/review/index.ts +2 -2
- package/src/extensibility/custom-commands/loader.ts +1 -2
- package/src/extensibility/custom-tools/loader.ts +34 -11
- package/src/extensibility/extensions/loader.ts +9 -4
- package/src/extensibility/extensions/runner.ts +24 -1
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/hooks/loader.ts +5 -6
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +2 -1
- package/src/extensibility/slash-commands.ts +3 -7
- package/src/index.ts +2 -1
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/ipy/executor.ts +58 -17
- package/src/ipy/gateway-coordinator.ts +6 -4
- package/src/ipy/kernel.ts +45 -22
- package/src/ipy/runtime.ts +2 -2
- package/src/lsp/client.ts +7 -4
- package/src/lsp/clients/lsp-linter-client.ts +4 -4
- package/src/lsp/config.ts +2 -2
- package/src/lsp/defaults.json +688 -154
- package/src/lsp/index.ts +234 -45
- package/src/lsp/lspmux.ts +2 -2
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +12 -1
- package/src/lsp/utils.ts +8 -1
- package/src/main.ts +102 -46
- package/src/memories/index.ts +4 -5
- package/src/modes/acp/acp-agent.ts +563 -163
- package/src/modes/acp/acp-event-mapper.ts +9 -1
- package/src/modes/acp/acp-mode.ts +4 -2
- package/src/modes/components/agent-dashboard.ts +3 -4
- package/src/modes/components/diff.ts +6 -7
- package/src/modes/components/read-tool-group.ts +6 -12
- package/src/modes/components/settings-defs.ts +5 -0
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/btw-controller.ts +2 -2
- package/src/modes/controllers/command-controller.ts +3 -2
- package/src/modes/controllers/input-controller.ts +12 -8
- package/src/modes/index.ts +20 -2
- package/src/modes/interactive-mode.ts +94 -37
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/rpc-client.ts +178 -13
- package/src/modes/rpc/rpc-mode.ts +73 -3
- package/src/modes/rpc/rpc-types.ts +53 -1
- package/src/modes/theme/theme.ts +80 -8
- package/src/modes/types.ts +2 -2
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/chunk-edit.md +219 -0
- package/src/prompts/tools/debug.md +43 -0
- package/src/prompts/tools/grep.md +3 -0
- package/src/prompts/tools/lsp.md +5 -5
- package/src/prompts/tools/read-chunk.md +17 -0
- package/src/prompts/tools/read.md +19 -5
- package/src/sdk.ts +190 -154
- package/src/secrets/obfuscator.ts +1 -1
- package/src/session/agent-session.ts +306 -256
- package/src/session/agent-storage.ts +12 -12
- package/src/session/compaction/branch-summarization.ts +3 -3
- package/src/session/compaction/compaction.ts +5 -6
- package/src/session/compaction/utils.ts +3 -3
- package/src/session/history-storage.ts +62 -19
- package/src/session/messages.ts +3 -3
- package/src/session/session-dump-format.ts +203 -0
- package/src/session/session-storage.ts +4 -2
- package/src/session/streaming-output.ts +1 -1
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +56 -8
- package/src/ssh/connection-manager.ts +2 -2
- package/src/ssh/sshfs-mount.ts +5 -5
- package/src/stt/downloader.ts +4 -4
- package/src/stt/recorder.ts +4 -4
- package/src/stt/transcriber.ts +2 -2
- package/src/system-prompt.ts +21 -13
- package/src/task/agents.ts +5 -6
- package/src/task/commands.ts +2 -5
- package/src/task/executor.ts +4 -4
- package/src/task/index.ts +3 -4
- package/src/task/template.ts +2 -2
- package/src/task/worktree.ts +4 -4
- package/src/tools/ask.ts +2 -3
- package/src/tools/ast-edit.ts +7 -7
- package/src/tools/ast-grep.ts +7 -7
- package/src/tools/auto-generated-guard.ts +36 -41
- package/src/tools/await-tool.ts +2 -2
- package/src/tools/bash.ts +5 -23
- package/src/tools/browser.ts +4 -5
- package/src/tools/calculator.ts +2 -3
- package/src/tools/cancel-job.ts +2 -2
- package/src/tools/checkpoint.ts +3 -3
- package/src/tools/debug.ts +1007 -0
- package/src/tools/exit-plan-mode.ts +2 -3
- package/src/tools/fetch.ts +67 -3
- package/src/tools/find.ts +4 -5
- package/src/tools/fs-cache-invalidation.ts +5 -0
- package/src/tools/gemini-image.ts +13 -5
- package/src/tools/gh.ts +10 -11
- package/src/tools/grep.ts +57 -9
- package/src/tools/index.ts +44 -22
- package/src/tools/inspect-image.ts +4 -4
- package/src/tools/output-meta.ts +1 -1
- package/src/tools/python.ts +19 -6
- package/src/tools/read.ts +198 -67
- package/src/tools/render-mermaid.ts +2 -3
- package/src/tools/render-utils.ts +20 -6
- package/src/tools/renderers.ts +3 -1
- package/src/tools/report-tool-issue.ts +80 -0
- package/src/tools/resolve.ts +70 -39
- package/src/tools/search-tool-bm25.ts +2 -2
- package/src/tools/ssh.ts +2 -2
- package/src/tools/todo-write.ts +2 -2
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/write.ts +5 -6
- package/src/tui/tree-list.ts +3 -1
- package/src/utils/clipboard.ts +80 -0
- package/src/utils/commit-message-generator.ts +2 -3
- package/src/utils/edit-mode.ts +49 -0
- package/src/utils/file-display-mode.ts +6 -5
- package/src/utils/file-mentions.ts +8 -7
- package/src/utils/git.ts +4 -4
- package/src/utils/image-loading.ts +98 -0
- package/src/utils/title-generator.ts +2 -3
- package/src/utils/tools-manager.ts +6 -6
- package/src/web/scrapers/choosealicense.ts +1 -1
- package/src/web/search/index.ts +3 -3
- package/src/autoresearch/command-initialize.md +0 -34
- package/src/patch/diff.ts +0 -433
- package/src/patch/index.ts +0 -888
- package/src/patch/parser.ts +0 -532
- package/src/patch/types.ts +0 -292
- package/src/prompts/agents/oracle.md +0 -77
- package/src/tools/pending-action.ts +0 -49
- package/src/utils/child-process.ts +0 -88
- package/src/utils/frontmatter.ts +0 -117
- package/src/utils/image-input.ts +0 -274
- package/src/utils/mime.ts +0 -53
- package/src/utils/prompt-format.ts +0 -170
package/src/tools/output-meta.ts
CHANGED
|
@@ -337,7 +337,7 @@ export function formatTruncationMetaNotice(truncation: TruncationMeta): string {
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
if (truncation.nextOffset != null) {
|
|
340
|
-
notice += `. Use
|
|
340
|
+
notice += `. Use sel=L${truncation.nextOffset} to continue`;
|
|
341
341
|
}
|
|
342
342
|
|
|
343
343
|
if (truncation.artifactId != null) {
|
package/src/tools/python.ts
CHANGED
|
@@ -4,11 +4,10 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
4
4
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
5
5
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
6
6
|
import { Markdown, Text } from "@oh-my-pi/pi-tui";
|
|
7
|
-
import { getProjectDir } from "@oh-my-pi/pi-utils";
|
|
7
|
+
import { getProjectDir, prompt } from "@oh-my-pi/pi-utils";
|
|
8
8
|
import { type Static, Type } from "@sinclair/typebox";
|
|
9
|
-
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
10
9
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
11
|
-
import { executePython, getPreludeDocs, type PythonExecutorOptions } from "../ipy/executor";
|
|
10
|
+
import { executePython, getPreludeDocs, type PythonExecutorOptions, warmPythonEnvironment } from "../ipy/executor";
|
|
12
11
|
import type { PreludeHelper, PythonStatusEvent } from "../ipy/kernel";
|
|
13
12
|
import { truncateToVisualLines } from "../modes/components/visual-truncate";
|
|
14
13
|
import { getMarkdownTheme, type Theme } from "../modes/theme/theme";
|
|
@@ -136,7 +135,7 @@ function renderJsonTree(value: unknown, theme: Theme, expanded: boolean, maxDept
|
|
|
136
135
|
export function getPythonToolDescription(): string {
|
|
137
136
|
const helpers = getPreludeDocs();
|
|
138
137
|
const categories = groupPreludeHelpers(helpers);
|
|
139
|
-
return
|
|
138
|
+
return prompt.render(pythonDescription, { categories });
|
|
140
139
|
}
|
|
141
140
|
|
|
142
141
|
export interface PythonToolOptions {
|
|
@@ -146,7 +145,9 @@ export interface PythonToolOptions {
|
|
|
146
145
|
export class PythonTool implements AgentTool<typeof pythonSchema> {
|
|
147
146
|
readonly name = "python";
|
|
148
147
|
readonly label = "Python";
|
|
149
|
-
|
|
148
|
+
get description(): string {
|
|
149
|
+
return getPythonToolDescription();
|
|
150
|
+
}
|
|
150
151
|
readonly parameters = pythonSchema;
|
|
151
152
|
readonly concurrency = "exclusive";
|
|
152
153
|
readonly strict = true;
|
|
@@ -158,7 +159,6 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
|
|
|
158
159
|
options?: PythonToolOptions,
|
|
159
160
|
) {
|
|
160
161
|
this.#proxyExecutor = options?.proxyExecutor;
|
|
161
|
-
this.description = getPythonToolDescription();
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
async execute(
|
|
@@ -266,6 +266,19 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
|
|
|
266
266
|
},
|
|
267
267
|
});
|
|
268
268
|
const sessionId = sessionFile ? `session:${sessionFile}:cwd:${commandCwd}` : `cwd:${commandCwd}`;
|
|
269
|
+
|
|
270
|
+
if (getPreludeDocs().length === 0) {
|
|
271
|
+
const warmup = await warmPythonEnvironment(
|
|
272
|
+
commandCwd,
|
|
273
|
+
sessionId,
|
|
274
|
+
this.session.settings.get("python.sharedGateway"),
|
|
275
|
+
sessionFile ?? undefined,
|
|
276
|
+
);
|
|
277
|
+
if (!warmup.ok) {
|
|
278
|
+
throw new ToolError(warmup.reason ?? "Python prelude helpers unavailable");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
269
282
|
const baseExecutorOptions: Omit<PythonExecutorOptions, "reset"> = {
|
|
270
283
|
cwd: commandCwd,
|
|
271
284
|
deadlineMs,
|
package/src/tools/read.ts
CHANGED
|
@@ -5,15 +5,22 @@ import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
|
|
5
5
|
import { glob } from "@oh-my-pi/pi-natives";
|
|
6
6
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
7
7
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
8
|
-
import { getRemoteDir, untilAborted } from "@oh-my-pi/pi-utils";
|
|
8
|
+
import { getRemoteDir, prompt, readImageMetadata, untilAborted } from "@oh-my-pi/pi-utils";
|
|
9
9
|
import { type Static, Type } from "@sinclair/typebox";
|
|
10
|
-
import {
|
|
10
|
+
import { computeLineHash } from "../edit/line-hash";
|
|
11
|
+
import {
|
|
12
|
+
type ChunkReadTarget,
|
|
13
|
+
formatChunkedRead,
|
|
14
|
+
parseChunkReadPath,
|
|
15
|
+
parseChunkSelector,
|
|
16
|
+
resolveAnchorStyle,
|
|
17
|
+
} from "../edit/modes/chunk";
|
|
11
18
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
12
19
|
import { parseInternalUrl } from "../internal-urls/parse";
|
|
13
20
|
import type { InternalUrl } from "../internal-urls/types";
|
|
14
21
|
import { getLanguageFromPath, type Theme } from "../modes/theme/theme";
|
|
15
|
-
import { computeLineHash } from "../patch/hashline";
|
|
16
22
|
import readDescription from "../prompts/tools/read.md" with { type: "text" };
|
|
23
|
+
import readChunkDescription from "../prompts/tools/read-chunk.md" with { type: "text" };
|
|
17
24
|
import type { ToolSession } from "../sdk";
|
|
18
25
|
import {
|
|
19
26
|
DEFAULT_MAX_BYTES,
|
|
@@ -25,20 +32,17 @@ import {
|
|
|
25
32
|
} from "../session/streaming-output";
|
|
26
33
|
import { renderCodeCell, renderStatusLine } from "../tui";
|
|
27
34
|
import { CachedOutputBlock } from "../tui/output-block";
|
|
35
|
+
import { resolveEditMode } from "../utils/edit-mode";
|
|
28
36
|
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
29
|
-
import {
|
|
30
|
-
ImageInputTooLargeError,
|
|
31
|
-
loadImageInput,
|
|
32
|
-
MAX_IMAGE_INPUT_BYTES,
|
|
33
|
-
readImageMetadata,
|
|
34
|
-
} from "../utils/image-input";
|
|
37
|
+
import { ImageInputTooLargeError, loadImageInput, MAX_IMAGE_INPUT_BYTES } from "../utils/image-loading";
|
|
35
38
|
import { convertFileWithMarkit } from "../utils/markit";
|
|
36
|
-
import { detectSupportedImageMimeTypeFromFile } from "../utils/mime";
|
|
37
39
|
import { type ArchiveReader, openArchive, parseArchivePathCandidates } from "./archive-reader";
|
|
40
|
+
|
|
38
41
|
import {
|
|
39
42
|
executeReadUrl,
|
|
40
43
|
isReadableUrlPath,
|
|
41
44
|
loadReadUrlCacheEntry,
|
|
45
|
+
parseReadUrlTarget,
|
|
42
46
|
type ReadUrlToolDetails,
|
|
43
47
|
renderReadUrlCall,
|
|
44
48
|
renderReadUrlResult,
|
|
@@ -50,19 +54,14 @@ import { formatAge, formatBytes, shortenPath, wrapBrackets } from "./render-util
|
|
|
50
54
|
import { ToolAbortError, ToolError, throwIfAborted } from "./tool-errors";
|
|
51
55
|
import { toolResult } from "./tool-result";
|
|
52
56
|
|
|
57
|
+
const PROSE_LANGUAGES = new Set(["markdown", "text", "log", "asciidoc", "restructuredtext"]);
|
|
58
|
+
|
|
59
|
+
function isProseLanguage(language: string | undefined): boolean {
|
|
60
|
+
return language !== undefined && PROSE_LANGUAGES.has(language);
|
|
61
|
+
}
|
|
62
|
+
|
|
53
63
|
// Document types converted to markdown via markit.
|
|
54
|
-
const CONVERTIBLE_EXTENSIONS = new Set([
|
|
55
|
-
".pdf",
|
|
56
|
-
".doc",
|
|
57
|
-
".docx",
|
|
58
|
-
".ppt",
|
|
59
|
-
".pptx",
|
|
60
|
-
".xls",
|
|
61
|
-
".xlsx",
|
|
62
|
-
".rtf",
|
|
63
|
-
".epub",
|
|
64
|
-
".ipynb",
|
|
65
|
-
]);
|
|
64
|
+
const CONVERTIBLE_EXTENSIONS = new Set([".pdf", ".doc", ".docx", ".ppt", ".pptx", ".xls", ".xlsx", ".rtf", ".epub"]);
|
|
66
65
|
|
|
67
66
|
// Remote mount path prefix (sshfs mounts) - skip fuzzy matching to avoid hangs
|
|
68
67
|
const REMOTE_MOUNT_PREFIX = getRemoteDir() + path.sep;
|
|
@@ -354,10 +353,8 @@ function prependSuffixResolutionNotice(text: string, suffixResolution?: { from:
|
|
|
354
353
|
|
|
355
354
|
const readSchema = Type.Object({
|
|
356
355
|
path: Type.String({ description: "Path or URL to read" }),
|
|
357
|
-
|
|
358
|
-
limit: Type.Optional(Type.Number({ description: "Maximum number of lines" })),
|
|
356
|
+
sel: Type.Optional(Type.String({ description: "Selector: chunk path, L10-L50, or raw" })),
|
|
359
357
|
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (default: 20)" })),
|
|
360
|
-
raw: Type.Optional(Type.Boolean({ description: "If set, returns raw content without transformations" })),
|
|
361
358
|
});
|
|
362
359
|
|
|
363
360
|
export type ReadToolInput = Static<typeof readSchema>;
|
|
@@ -368,6 +365,7 @@ export interface ReadToolDetails {
|
|
|
368
365
|
isDirectory?: boolean;
|
|
369
366
|
resolvedPath?: string;
|
|
370
367
|
suffixResolution?: { from: string; to: string };
|
|
368
|
+
chunk?: ChunkReadTarget;
|
|
371
369
|
url?: string;
|
|
372
370
|
finalUrl?: string;
|
|
373
371
|
contentType?: string;
|
|
@@ -378,6 +376,43 @@ export interface ReadToolDetails {
|
|
|
378
376
|
|
|
379
377
|
type ReadParams = ReadToolInput;
|
|
380
378
|
|
|
379
|
+
/** Parsed representation of the `sel` parameter. */
|
|
380
|
+
type ParsedSelector =
|
|
381
|
+
| { kind: "none" }
|
|
382
|
+
| { kind: "raw" }
|
|
383
|
+
| { kind: "lines"; startLine: number; endLine: number | undefined }
|
|
384
|
+
| { kind: "chunk"; selector: string };
|
|
385
|
+
|
|
386
|
+
const LINE_RANGE_RE = /^L(\d+)(?:-L?(\d+))?$/i;
|
|
387
|
+
|
|
388
|
+
function parseSel(sel: string | undefined): ParsedSelector {
|
|
389
|
+
if (!sel || sel.length === 0) return { kind: "none" };
|
|
390
|
+
const normalizedSelector = parseChunkSelector(sel).selector ?? sel;
|
|
391
|
+
if (normalizedSelector === "raw") return { kind: "raw" };
|
|
392
|
+
const lineMatch = LINE_RANGE_RE.exec(normalizedSelector);
|
|
393
|
+
if (lineMatch) {
|
|
394
|
+
const rawStart = Number.parseInt(lineMatch[1]!, 10);
|
|
395
|
+
if (rawStart < 1) {
|
|
396
|
+
throw new ToolError("L0 is invalid; lines are 1-indexed. Use sel=L1.");
|
|
397
|
+
}
|
|
398
|
+
const rawEnd = lineMatch[2] ? Number.parseInt(lineMatch[2], 10) : undefined;
|
|
399
|
+
if (rawEnd !== undefined && rawEnd < rawStart) {
|
|
400
|
+
throw new ToolError(`Invalid range L${rawStart}-L${rawEnd}: end must be >= start.`);
|
|
401
|
+
}
|
|
402
|
+
return { kind: "lines", startLine: rawStart, endLine: rawEnd };
|
|
403
|
+
}
|
|
404
|
+
return { kind: "chunk", selector: normalizedSelector };
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/** Convert a line-range selector to the offset/limit pair used by internal pagination. */
|
|
408
|
+
function selToOffsetLimit(parsed: ParsedSelector): { offset?: number; limit?: number } {
|
|
409
|
+
if (parsed.kind === "lines") {
|
|
410
|
+
const limit = parsed.endLine !== undefined ? parsed.endLine - parsed.startLine + 1 : undefined;
|
|
411
|
+
return { offset: parsed.startLine, limit };
|
|
412
|
+
}
|
|
413
|
+
return {};
|
|
414
|
+
}
|
|
415
|
+
|
|
381
416
|
interface ResolvedArchiveReadPath {
|
|
382
417
|
absolutePath: string;
|
|
383
418
|
archiveSubPath: string;
|
|
@@ -410,12 +445,17 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
410
445
|
Math.min(session.settings.get("read.defaultLimit") ?? DEFAULT_MAX_LINES, DEFAULT_MAX_LINES),
|
|
411
446
|
);
|
|
412
447
|
this.#inspectImageEnabled = session.settings.get("inspect_image.enabled");
|
|
413
|
-
this.description =
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
448
|
+
this.description =
|
|
449
|
+
resolveEditMode(session) === "chunk"
|
|
450
|
+
? prompt.render(readChunkDescription, {
|
|
451
|
+
anchorStyle: resolveAnchorStyle(session.settings),
|
|
452
|
+
})
|
|
453
|
+
: prompt.render(readDescription, {
|
|
454
|
+
DEFAULT_LIMIT: String(this.#defaultLimit),
|
|
455
|
+
DEFAULT_MAX_LINES: String(DEFAULT_MAX_LINES),
|
|
456
|
+
IS_HASHLINE_MODE: displayMode.hashLines,
|
|
457
|
+
IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
|
|
458
|
+
});
|
|
419
459
|
}
|
|
420
460
|
|
|
421
461
|
async #resolveArchiveReadPath(readPath: string, signal?: AbortSignal): Promise<ResolvedArchiveReadPath | null> {
|
|
@@ -496,10 +536,10 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
496
536
|
const suggestion =
|
|
497
537
|
allLines.length === 0
|
|
498
538
|
? `The ${options.entityLabel} is empty.`
|
|
499
|
-
: `Use
|
|
539
|
+
: `Use sel=L1 to read from the start, or sel=L${allLines.length} to read the last line.`;
|
|
500
540
|
return resultBuilder
|
|
501
541
|
.text(
|
|
502
|
-
`
|
|
542
|
+
`Line ${startLineDisplay} is beyond end of ${options.entityLabel} (${allLines.length} lines total). ${suggestion}`,
|
|
503
543
|
)
|
|
504
544
|
.done();
|
|
505
545
|
}
|
|
@@ -556,7 +596,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
556
596
|
const nextOffset = startLine + userLimitedLines + 1;
|
|
557
597
|
|
|
558
598
|
outputText = formatText(selectedContent, startLineDisplay);
|
|
559
|
-
outputText += `\n\n[${remaining} more lines in ${options.entityLabel}. Use
|
|
599
|
+
outputText += `\n\n[${remaining} more lines in ${options.entityLabel}. Use sel=L${nextOffset} to continue]`;
|
|
560
600
|
} else {
|
|
561
601
|
outputText = formatText(truncation.content, startLineDisplay);
|
|
562
602
|
}
|
|
@@ -674,42 +714,60 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
674
714
|
_onUpdate?: AgentToolUpdateCallback<ReadToolDetails>,
|
|
675
715
|
_toolContext?: AgentToolContext,
|
|
676
716
|
): Promise<AgentToolResult<ReadToolDetails>> {
|
|
677
|
-
let { path: readPath,
|
|
678
|
-
const displayMode = resolveFileDisplayMode(this.session);
|
|
717
|
+
let { path: readPath, sel, timeout } = params;
|
|
679
718
|
if (readPath.startsWith("file://")) {
|
|
680
719
|
readPath = expandPath(readPath);
|
|
681
720
|
}
|
|
721
|
+
const displayMode = resolveFileDisplayMode(this.session);
|
|
722
|
+
const chunkMode = resolveEditMode(this.session) === "chunk";
|
|
682
723
|
|
|
683
724
|
// Handle internal URLs (agent://, artifact://, memory://, skill://, rule://, local://, mcp://)
|
|
684
725
|
const internalRouter = this.session.internalRouter;
|
|
685
726
|
if (internalRouter?.canHandle(readPath)) {
|
|
727
|
+
const parsed = parseSel(sel);
|
|
728
|
+
const { offset, limit } = selToOffsetLimit(parsed);
|
|
686
729
|
return this.#handleInternalUrl(readPath, offset, limit);
|
|
687
730
|
}
|
|
688
731
|
|
|
689
|
-
|
|
732
|
+
const parsedUrlTarget = parseReadUrlTarget(readPath, sel);
|
|
733
|
+
if (parsedUrlTarget) {
|
|
690
734
|
if (!this.session.settings.get("fetch.enabled")) {
|
|
691
735
|
throw new ToolError("URL reads are disabled by settings.");
|
|
692
736
|
}
|
|
693
|
-
if (offset !== undefined || limit !== undefined) {
|
|
694
|
-
const cached = await loadReadUrlCacheEntry(
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
737
|
+
if (parsedUrlTarget.offset !== undefined || parsedUrlTarget.limit !== undefined) {
|
|
738
|
+
const cached = await loadReadUrlCacheEntry(
|
|
739
|
+
this.session,
|
|
740
|
+
{ path: parsedUrlTarget.path, timeout, raw: parsedUrlTarget.raw },
|
|
741
|
+
signal,
|
|
742
|
+
{
|
|
743
|
+
ensureArtifact: true,
|
|
744
|
+
preferCached: true,
|
|
745
|
+
},
|
|
746
|
+
);
|
|
747
|
+
return this.#buildInMemoryTextResult(cached.output, parsedUrlTarget.offset, parsedUrlTarget.limit, {
|
|
699
748
|
details: { ...cached.details },
|
|
700
749
|
sourceUrl: cached.details.finalUrl,
|
|
701
750
|
entityLabel: "URL output",
|
|
702
751
|
});
|
|
703
752
|
}
|
|
704
|
-
return executeReadUrl(this.session, { path:
|
|
753
|
+
return executeReadUrl(this.session, { path: parsedUrlTarget.path, timeout, raw: parsedUrlTarget.raw }, signal);
|
|
705
754
|
}
|
|
706
755
|
|
|
707
|
-
const
|
|
756
|
+
const parsedReadPath = chunkMode ? parseChunkReadPath(readPath) : { filePath: readPath };
|
|
757
|
+
const localReadPath = parsedReadPath.filePath;
|
|
758
|
+
const pathSelectorParsed = chunkMode ? parseSel(parsedReadPath.selector) : { kind: "none" as const };
|
|
759
|
+
const pathChunkSelector = pathSelectorParsed.kind === "chunk" ? pathSelectorParsed.selector : undefined;
|
|
760
|
+
const selectorInput = sel ?? parsedReadPath.selector;
|
|
761
|
+
const rawSelectorInput = sel ?? parsedReadPath.selector;
|
|
762
|
+
const parsed = parseSel(selectorInput);
|
|
763
|
+
|
|
764
|
+
const archivePath = await this.#resolveArchiveReadPath(localReadPath, signal);
|
|
708
765
|
if (archivePath) {
|
|
766
|
+
const { offset, limit } = selToOffsetLimit(parsed);
|
|
709
767
|
return this.#readArchive(readPath, offset, limit, archivePath, signal);
|
|
710
768
|
}
|
|
711
769
|
|
|
712
|
-
let absolutePath = resolveReadPath(
|
|
770
|
+
let absolutePath = resolveReadPath(localReadPath, this.session.cwd);
|
|
713
771
|
let suffixResolution: { from: string; to: string } | undefined;
|
|
714
772
|
|
|
715
773
|
let isDirectory = false;
|
|
@@ -722,14 +780,14 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
722
780
|
if (isNotFoundError(error)) {
|
|
723
781
|
// Attempt unique suffix resolution before falling back to fuzzy suggestions
|
|
724
782
|
if (!isRemoteMountPath(absolutePath)) {
|
|
725
|
-
const suffixMatch = await findUniqueSuffixMatch(
|
|
783
|
+
const suffixMatch = await findUniqueSuffixMatch(localReadPath, this.session.cwd, signal);
|
|
726
784
|
if (suffixMatch) {
|
|
727
785
|
try {
|
|
728
786
|
const retryStat = await Bun.file(suffixMatch.absolutePath).stat();
|
|
729
787
|
absolutePath = suffixMatch.absolutePath;
|
|
730
788
|
fileSize = retryStat.size;
|
|
731
789
|
isDirectory = retryStat.isDirectory();
|
|
732
|
-
suffixResolution = { from:
|
|
790
|
+
suffixResolution = { from: localReadPath, to: suffixMatch.displayPath };
|
|
733
791
|
} catch {
|
|
734
792
|
// Suffix match candidate no longer stats — fall through to error path
|
|
735
793
|
}
|
|
@@ -737,7 +795,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
737
795
|
}
|
|
738
796
|
|
|
739
797
|
if (!suffixResolution) {
|
|
740
|
-
throw new ToolError(`Path '${
|
|
798
|
+
throw new ToolError(`Path '${localReadPath}' not found`);
|
|
741
799
|
}
|
|
742
800
|
} else {
|
|
743
801
|
throw error;
|
|
@@ -745,7 +803,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
745
803
|
}
|
|
746
804
|
|
|
747
805
|
if (isDirectory) {
|
|
748
|
-
const dirResult = await this.#readDirectory(absolutePath, limit, signal);
|
|
806
|
+
const dirResult = await this.#readDirectory(absolutePath, selToOffsetLimit(parsed).limit, signal);
|
|
749
807
|
if (suffixResolution) {
|
|
750
808
|
dirResult.details ??= {};
|
|
751
809
|
dirResult.details.suffixResolution = suffixResolution;
|
|
@@ -753,11 +811,59 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
753
811
|
return dirResult;
|
|
754
812
|
}
|
|
755
813
|
|
|
756
|
-
const
|
|
814
|
+
const imageMetadata = await readImageMetadata(absolutePath);
|
|
815
|
+
const mimeType = imageMetadata?.mimeType;
|
|
757
816
|
const ext = path.extname(absolutePath).toLowerCase();
|
|
817
|
+
const hasEditTool = this.session.hasEditTool ?? true;
|
|
818
|
+
const language = getLanguageFromPath(absolutePath);
|
|
819
|
+
const skipChunksForExplore = !hasEditTool && !this.session.settings.get("read.explorechunks");
|
|
820
|
+
const skipChunksForProse = isProseLanguage(language) && !this.session.settings.get("read.prosechunks");
|
|
821
|
+
const shouldConvertWithMarkit =
|
|
822
|
+
CONVERTIBLE_EXTENSIONS.has(ext) || (ext === ".ipynb" && (parsed.kind === "raw" || !chunkMode));
|
|
823
|
+
|
|
824
|
+
if (chunkMode && parsed.kind !== "raw" && !skipChunksForExplore && !skipChunksForProse) {
|
|
825
|
+
const absoluteLineRange =
|
|
826
|
+
pathChunkSelector && parsed.kind === "lines"
|
|
827
|
+
? { startLine: parsed.startLine, endLine: parsed.endLine }
|
|
828
|
+
: undefined;
|
|
829
|
+
// sel= wins over path:chunk when both are provided (explicit param > embedded path).
|
|
830
|
+
const effectiveSelector = sel ? selectorInput : (pathChunkSelector ?? selectorInput);
|
|
831
|
+
const rawEffectiveSelector = sel ? selectorInput : (rawSelectorInput ?? effectiveSelector);
|
|
832
|
+
const chunkReadPath =
|
|
833
|
+
parsed.kind === "chunk" || (pathChunkSelector && !sel)
|
|
834
|
+
? rawEffectiveSelector
|
|
835
|
+
? `${localReadPath}:${rawEffectiveSelector}`
|
|
836
|
+
: localReadPath
|
|
837
|
+
: parsed.kind === "lines"
|
|
838
|
+
? parsed.endLine !== undefined
|
|
839
|
+
? `${localReadPath}:L${parsed.startLine}-L${parsed.endLine}`
|
|
840
|
+
: `${localReadPath}:L${parsed.startLine}`
|
|
841
|
+
: localReadPath;
|
|
842
|
+
const chunkResult = await formatChunkedRead({
|
|
843
|
+
filePath: absolutePath,
|
|
844
|
+
readPath: chunkReadPath,
|
|
845
|
+
cwd: this.session.cwd,
|
|
846
|
+
language,
|
|
847
|
+
omitChecksum: !hasEditTool,
|
|
848
|
+
anchorStyle: resolveAnchorStyle(this.session.settings),
|
|
849
|
+
absoluteLineRange,
|
|
850
|
+
});
|
|
851
|
+
let text = chunkResult.text;
|
|
852
|
+
if (suffixResolution) {
|
|
853
|
+
text = prependSuffixResolutionNotice(text, suffixResolution);
|
|
854
|
+
}
|
|
855
|
+
return toolResult<ReadToolDetails>({
|
|
856
|
+
resolvedPath: absolutePath,
|
|
857
|
+
suffixResolution,
|
|
858
|
+
chunk: chunkResult.chunk,
|
|
859
|
+
})
|
|
860
|
+
.text(text)
|
|
861
|
+
.sourcePath(absolutePath)
|
|
862
|
+
.done();
|
|
863
|
+
}
|
|
758
864
|
|
|
759
865
|
// Read the file based on type
|
|
760
|
-
let content:
|
|
866
|
+
let content: Array<TextContent | ImageContent>;
|
|
761
867
|
let details: ReadToolDetails = {};
|
|
762
868
|
let sourcePath: string | undefined;
|
|
763
869
|
let truncationInfo:
|
|
@@ -766,14 +872,9 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
766
872
|
|
|
767
873
|
if (mimeType) {
|
|
768
874
|
if (this.#inspectImageEnabled) {
|
|
769
|
-
const metadata =
|
|
770
|
-
path: readPath,
|
|
771
|
-
cwd: this.session.cwd,
|
|
772
|
-
resolvedPath: absolutePath,
|
|
773
|
-
detectedMimeType: mimeType,
|
|
774
|
-
});
|
|
875
|
+
const metadata = imageMetadata;
|
|
775
876
|
const outputMime = metadata?.mimeType ?? mimeType;
|
|
776
|
-
const outputBytes =
|
|
877
|
+
const outputBytes = fileSize;
|
|
777
878
|
const metadataLines = [
|
|
778
879
|
"Image metadata:",
|
|
779
880
|
`- MIME: ${outputMime}`,
|
|
@@ -824,7 +925,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
824
925
|
throw error;
|
|
825
926
|
}
|
|
826
927
|
}
|
|
827
|
-
} else if (
|
|
928
|
+
} else if (shouldConvertWithMarkit) {
|
|
828
929
|
// Convert document or notebook via markit.
|
|
829
930
|
const result = await convertFileWithMarkit(absolutePath, signal);
|
|
830
931
|
if (result.ok) {
|
|
@@ -843,13 +944,41 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
843
944
|
content = [{ type: "text", text: `[Cannot read ${ext} file: conversion failed]` }];
|
|
844
945
|
}
|
|
845
946
|
} else {
|
|
846
|
-
//
|
|
947
|
+
// Chunk mode: dispatch to chunk tree unless raw or line range requested
|
|
948
|
+
if (chunkMode && parsed.kind !== "raw" && parsed.kind !== "lines") {
|
|
949
|
+
const chunkSel = parsed.kind === "chunk" ? parsed.selector : undefined;
|
|
950
|
+
const chunkResult = await formatChunkedRead({
|
|
951
|
+
filePath: absolutePath,
|
|
952
|
+
readPath: chunkSel ? `${localReadPath}:${chunkSel}` : localReadPath,
|
|
953
|
+
cwd: this.session.cwd,
|
|
954
|
+
language: getLanguageFromPath(absolutePath),
|
|
955
|
+
omitChecksum: !(this.session.hasEditTool ?? true),
|
|
956
|
+
anchorStyle: resolveAnchorStyle(this.session.settings),
|
|
957
|
+
});
|
|
958
|
+
let text = chunkResult.text;
|
|
959
|
+
if (suffixResolution) {
|
|
960
|
+
text = prependSuffixResolutionNotice(text, suffixResolution);
|
|
961
|
+
}
|
|
962
|
+
return toolResult<ReadToolDetails>({
|
|
963
|
+
resolvedPath: absolutePath,
|
|
964
|
+
suffixResolution,
|
|
965
|
+
chunk: chunkResult.chunk,
|
|
966
|
+
})
|
|
967
|
+
.text(text)
|
|
968
|
+
.sourcePath(absolutePath)
|
|
969
|
+
.done();
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// Raw text or line-range mode
|
|
973
|
+
const { offset, limit } = selToOffsetLimit(parsed);
|
|
847
974
|
const startLine = offset ? Math.max(0, offset - 1) : 0;
|
|
848
|
-
const startLineDisplay = startLine + 1;
|
|
975
|
+
const startLineDisplay = startLine + 1;
|
|
849
976
|
|
|
850
|
-
const
|
|
977
|
+
const DEFAULT_LIMIT = this.#defaultLimit;
|
|
978
|
+
const effectiveLimit = limit ?? DEFAULT_LIMIT;
|
|
851
979
|
const maxLinesToCollect = Math.min(effectiveLimit, DEFAULT_MAX_LINES);
|
|
852
980
|
const selectedLineLimit = effectiveLimit;
|
|
981
|
+
|
|
853
982
|
const streamResult = await streamLinesFromFile(
|
|
854
983
|
absolutePath,
|
|
855
984
|
startLine,
|
|
@@ -873,9 +1002,9 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
873
1002
|
const suggestion =
|
|
874
1003
|
totalFileLines === 0
|
|
875
1004
|
? "The file is empty."
|
|
876
|
-
: `Use
|
|
1005
|
+
: `Use sel=L1 to read from the start, or sel=L${totalFileLines} to read the last line.`;
|
|
877
1006
|
return toolResult<ReadToolDetails>({ resolvedPath: absolutePath, suffixResolution })
|
|
878
|
-
.text(`
|
|
1007
|
+
.text(`Line ${startLineDisplay} is beyond end of file (${totalFileLines} lines total). ${suggestion}`)
|
|
879
1008
|
.done();
|
|
880
1009
|
}
|
|
881
1010
|
|
|
@@ -942,7 +1071,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
942
1071
|
const nextOffset = startLine + userLimitedLines + 1;
|
|
943
1072
|
|
|
944
1073
|
outputText = formatText(truncation.content, startLineDisplay);
|
|
945
|
-
outputText += `\n\n[${remaining} more lines in file. Use
|
|
1074
|
+
outputText += `\n\n[${remaining} more lines in file. Use sel=L${nextOffset} to continue]`;
|
|
946
1075
|
details = {};
|
|
947
1076
|
sourcePath = absolutePath;
|
|
948
1077
|
} else {
|
|
@@ -1100,9 +1229,11 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1100
1229
|
interface ReadRenderArgs {
|
|
1101
1230
|
path?: string;
|
|
1102
1231
|
file_path?: string;
|
|
1232
|
+
sel?: string;
|
|
1233
|
+
timeout?: number;
|
|
1234
|
+
// Legacy fields from old schema — tolerated for in-flight tool calls during transition
|
|
1103
1235
|
offset?: number;
|
|
1104
1236
|
limit?: number;
|
|
1105
|
-
timeout?: number;
|
|
1106
1237
|
raw?: boolean;
|
|
1107
1238
|
}
|
|
1108
1239
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import { type MermaidAsciiRenderOptions, renderMermaidAscii } from "@oh-my-pi/pi-utils";
|
|
2
|
+
import { type MermaidAsciiRenderOptions, prompt, renderMermaidAscii } from "@oh-my-pi/pi-utils";
|
|
3
3
|
import { type Static, Type } from "@sinclair/typebox";
|
|
4
|
-
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
5
4
|
import renderMermaidDescription from "../prompts/tools/render-mermaid.md" with { type: "text" };
|
|
6
5
|
import type { ToolSession } from "./index";
|
|
7
6
|
|
|
@@ -41,7 +40,7 @@ export class RenderMermaidTool implements AgentTool<typeof renderMermaidSchema,
|
|
|
41
40
|
readonly strict = true;
|
|
42
41
|
|
|
43
42
|
constructor(private readonly session: ToolSession) {
|
|
44
|
-
this.description =
|
|
43
|
+
this.description = prompt.render(renderMermaidDescription);
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
async execute(
|
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
* tool renderers to ensure a unified TUI experience.
|
|
6
6
|
*/
|
|
7
7
|
import * as os from "node:os";
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import type { Ellipsis } from "@oh-my-pi/pi-natives";
|
|
10
|
+
import { replaceTabs, truncateToWidth } from "@oh-my-pi/pi-tui";
|
|
11
|
+
import { pluralize } from "@oh-my-pi/pi-utils";
|
|
10
12
|
import { settings } from "../config/settings";
|
|
11
13
|
import type { Theme } from "../modes/theme/theme";
|
|
12
14
|
import { formatDimensionNote, type ResizedImage } from "../utils/image-resize";
|
|
13
15
|
|
|
14
|
-
export { Ellipsis
|
|
16
|
+
export { Ellipsis } from "@oh-my-pi/pi-natives";
|
|
17
|
+
export { replaceTabs, truncateToWidth } from "@oh-my-pi/pi-tui";
|
|
15
18
|
|
|
16
|
-
export function replaceTabs(text: string, file?: string): string {
|
|
17
|
-
return text.replaceAll("\t", getIndentation(file));
|
|
18
|
-
}
|
|
19
19
|
// =============================================================================
|
|
20
20
|
// Standardized Display Constants
|
|
21
21
|
// =============================================================================
|
|
@@ -548,6 +548,20 @@ export function shortenPath(filePath: string, homeDir?: string): string {
|
|
|
548
548
|
return filePath;
|
|
549
549
|
}
|
|
550
550
|
|
|
551
|
+
export function formatToolWorkingDirectory(workdir: string | undefined, projectDir: string): string | undefined {
|
|
552
|
+
if (!workdir) return undefined;
|
|
553
|
+
const resolvedProjectDir = path.resolve(projectDir);
|
|
554
|
+
const resolvedWorkdir = path.resolve(projectDir, workdir);
|
|
555
|
+
if (resolvedWorkdir === resolvedProjectDir) {
|
|
556
|
+
return undefined;
|
|
557
|
+
}
|
|
558
|
+
const relativePath = path.relative(resolvedProjectDir, resolvedWorkdir);
|
|
559
|
+
const isWithinProject =
|
|
560
|
+
relativePath.length > 0 && !relativePath.startsWith("..") && !relativePath.startsWith(`..${path.sep}`);
|
|
561
|
+
const displayWorkdir = isWithinProject ? relativePath : shortenPath(resolvedWorkdir);
|
|
562
|
+
return replaceTabs(displayWorkdir);
|
|
563
|
+
}
|
|
564
|
+
|
|
551
565
|
export function formatScreenshot(opts: {
|
|
552
566
|
saveFullRes: boolean;
|
|
553
567
|
savedMimeType: string;
|
package/src/tools/renderers.ts
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* These provide rich visualization for tool calls and results in the TUI.
|
|
5
5
|
*/
|
|
6
6
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
7
|
+
import { editToolRenderer } from "../edit/renderer";
|
|
7
8
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
8
9
|
import { lspToolRenderer } from "../lsp/render";
|
|
9
10
|
import type { Theme } from "../modes/theme/theme";
|
|
10
|
-
import { editToolRenderer } from "../patch";
|
|
11
11
|
import { taskToolRenderer } from "../task/render";
|
|
12
12
|
import { webSearchToolRenderer } from "../web/search/render";
|
|
13
13
|
import { askToolRenderer } from "./ask";
|
|
@@ -15,6 +15,7 @@ import { astEditToolRenderer } from "./ast-edit";
|
|
|
15
15
|
import { astGrepToolRenderer } from "./ast-grep";
|
|
16
16
|
import { bashToolRenderer } from "./bash";
|
|
17
17
|
import { calculatorToolRenderer } from "./calculator";
|
|
18
|
+
import { debugToolRenderer } from "./debug";
|
|
18
19
|
import { findToolRenderer } from "./find";
|
|
19
20
|
import { ghRunWatchToolRenderer } from "./gh-renderer";
|
|
20
21
|
import { grepToolRenderer } from "./grep";
|
|
@@ -46,6 +47,7 @@ export const toolRenderers: Record<string, ToolRenderer> = {
|
|
|
46
47
|
ast_grep: astGrepToolRenderer as ToolRenderer,
|
|
47
48
|
ast_edit: astEditToolRenderer as ToolRenderer,
|
|
48
49
|
bash: bashToolRenderer as ToolRenderer,
|
|
50
|
+
debug: debugToolRenderer as ToolRenderer,
|
|
49
51
|
python: pythonToolRenderer as ToolRenderer,
|
|
50
52
|
calc: calculatorToolRenderer as ToolRenderer,
|
|
51
53
|
edit: editToolRenderer as ToolRenderer,
|