@oh-my-pi/pi-coding-agent 14.5.3 → 14.5.5
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 +44 -0
- package/examples/extensions/plan-mode.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/package.json +7 -7
- package/src/config/prompt-templates.ts +103 -8
- package/src/config/settings-schema.ts +14 -13
- package/src/config/settings.ts +1 -1
- package/src/cursor.ts +4 -4
- package/src/edit/index.ts +111 -109
- package/src/edit/line-hash.ts +33 -3
- package/src/edit/modes/apply-patch.ts +6 -4
- package/src/edit/modes/atom.lark +27 -0
- package/src/edit/modes/atom.ts +1057 -841
- package/src/edit/modes/hashline.ts +9 -10
- package/src/edit/modes/patch.ts +23 -19
- package/src/edit/modes/replace.ts +19 -15
- package/src/edit/renderer.ts +65 -8
- package/src/edit/streaming.ts +47 -77
- package/src/extensibility/extensions/types.ts +11 -11
- package/src/extensibility/hooks/types.ts +6 -6
- package/src/lsp/edits.ts +8 -5
- package/src/lsp/index.ts +4 -4
- package/src/lsp/utils.ts +7 -7
- package/src/mcp/discoverable-tool-metadata.ts +1 -1
- package/src/mcp/manager.ts +3 -3
- package/src/mcp/tool-bridge.ts +4 -4
- package/src/memories/index.ts +1 -1
- package/src/modes/acp/acp-event-mapper.ts +1 -1
- package/src/modes/components/session-observer-overlay.ts +1 -1
- package/src/modes/components/settings-defs.ts +3 -3
- package/src/modes/components/tree-selector.ts +2 -2
- package/src/modes/utils/ui-helpers.ts +31 -7
- package/src/prompts/agents/explore.md +1 -1
- package/src/prompts/agents/librarian.md +2 -2
- package/src/prompts/agents/plan.md +2 -2
- package/src/prompts/agents/reviewer.md +1 -1
- package/src/prompts/agents/task.md +2 -2
- package/src/prompts/system/plan-mode-active.md +1 -1
- package/src/prompts/system/system-prompt.md +34 -31
- package/src/prompts/tools/apply-patch.md +0 -2
- package/src/prompts/tools/atom.md +81 -63
- package/src/prompts/tools/bash.md +7 -4
- package/src/prompts/tools/checkpoint.md +1 -1
- package/src/prompts/tools/find.md +6 -1
- package/src/prompts/tools/hashline.md +10 -11
- package/src/prompts/tools/patch.md +13 -13
- package/src/prompts/tools/read.md +4 -4
- package/src/prompts/tools/replace.md +3 -3
- package/src/prompts/tools/{grep.md → search.md} +4 -4
- package/src/sdk.ts +19 -9
- package/src/session/agent-session.ts +65 -0
- package/src/system-prompt.ts +15 -5
- package/src/task/executor.ts +5 -0
- package/src/task/index.ts +10 -1
- package/src/tools/ast-edit.ts +4 -6
- package/src/tools/ast-grep.ts +4 -6
- package/src/tools/bash.ts +1 -1
- package/src/tools/file-recorder.ts +6 -6
- package/src/tools/find.ts +11 -13
- package/src/tools/index.ts +7 -7
- package/src/tools/path-utils.ts +31 -4
- package/src/tools/read.ts +12 -6
- package/src/tools/renderers.ts +2 -2
- package/src/tools/{grep.ts → search.ts} +32 -40
- package/src/tools/write.ts +8 -4
- package/src/web/search/index.ts +1 -1
- package/src/edit/block.ts +0 -308
- package/src/edit/indent.ts +0 -150
package/src/task/executor.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Runs each subagent on the main thread and forwards AgentEvents for progress tracking.
|
|
5
5
|
*/
|
|
6
|
+
|
|
6
7
|
import path from "node:path";
|
|
7
8
|
import type { AgentEvent, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
8
9
|
import { logger, prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
@@ -16,6 +17,7 @@ import { SETTINGS_SCHEMA, type SettingPath } from "../config/settings-schema";
|
|
|
16
17
|
import type { CustomTool } from "../extensibility/custom-tools/types";
|
|
17
18
|
import { runExtensionCompact, runExtensionSetModel } from "../extensibility/extensions/compact-handler";
|
|
18
19
|
import type { Skill } from "../extensibility/skills";
|
|
20
|
+
import type { LocalProtocolOptions } from "../internal-urls";
|
|
19
21
|
import { callTool } from "../mcp/client";
|
|
20
22
|
import type { MCPManager } from "../mcp/manager";
|
|
21
23
|
import subagentSystemPromptTemplate from "../prompts/system/subagent-system-prompt.md" with { type: "text" };
|
|
@@ -159,6 +161,8 @@ export interface ExecutorOptions {
|
|
|
159
161
|
authStorage?: AuthStorage;
|
|
160
162
|
modelRegistry?: ModelRegistry;
|
|
161
163
|
settings?: Settings;
|
|
164
|
+
/** Override local:// protocol options so subagent shares parent's local:// root */
|
|
165
|
+
localProtocolOptions?: LocalProtocolOptions;
|
|
162
166
|
}
|
|
163
167
|
|
|
164
168
|
function parseStringifiedJson(value: unknown): unknown {
|
|
@@ -987,6 +991,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
987
991
|
enableMCP,
|
|
988
992
|
mcpManager: options.mcpManager,
|
|
989
993
|
customTools: mcpProxyTools.length > 0 ? mcpProxyTools : undefined,
|
|
994
|
+
localProtocolOptions: options.localProtocolOptions,
|
|
990
995
|
});
|
|
991
996
|
|
|
992
997
|
activeSession = session;
|
package/src/task/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ import taskSummaryTemplate from "../prompts/tools/task-summary.md" with { type:
|
|
|
28
28
|
import { formatBytes, formatDuration } from "../tools/render-utils";
|
|
29
29
|
// Import review tools for side effects (registers subagent tool handlers)
|
|
30
30
|
import "../tools/review";
|
|
31
|
+
import type { LocalProtocolOptions } from "../internal-urls";
|
|
31
32
|
import { generateCommitMessage } from "../utils/commit-message-generator";
|
|
32
33
|
import * as git from "../utils/git";
|
|
33
34
|
import { discoverAgents, getAgent } from "./discovery";
|
|
@@ -567,7 +568,7 @@ export class TaskTool implements AgentTool<TSchema, TaskToolDetails, Theme> {
|
|
|
567
568
|
}
|
|
568
569
|
|
|
569
570
|
const planModeState = this.session.getPlanModeState?.();
|
|
570
|
-
const planModeTools = ["read", "
|
|
571
|
+
const planModeTools = ["read", "search", "find", "lsp", "web_search"];
|
|
571
572
|
const effectiveAgent: typeof agent = planModeState?.enabled
|
|
572
573
|
? {
|
|
573
574
|
...agent,
|
|
@@ -715,6 +716,12 @@ export class TaskTool implements AgentTool<TSchema, TaskToolDetails, Theme> {
|
|
|
715
716
|
const tempArtifactsDir = artifactsDir ? null : path.join(os.tmpdir(), `omp-task-${Snowflake.next()}`);
|
|
716
717
|
const effectiveArtifactsDir = artifactsDir || tempArtifactsDir!;
|
|
717
718
|
|
|
719
|
+
// Share the parent session's local:// root with subagents so they read/write the same scratch space
|
|
720
|
+
const localProtocolOptions: LocalProtocolOptions = {
|
|
721
|
+
getArtifactsDir: this.session.getArtifactsDir ?? (() => null),
|
|
722
|
+
getSessionId: this.session.getSessionId ?? (() => null),
|
|
723
|
+
};
|
|
724
|
+
|
|
718
725
|
// Initialize progress tracking
|
|
719
726
|
const progressMap = new Map<number, AgentProgress>();
|
|
720
727
|
|
|
@@ -856,6 +863,7 @@ export class TaskTool implements AgentTool<TSchema, TaskToolDetails, Theme> {
|
|
|
856
863
|
contextFiles,
|
|
857
864
|
skills: availableSkills,
|
|
858
865
|
promptTemplates,
|
|
866
|
+
localProtocolOptions,
|
|
859
867
|
});
|
|
860
868
|
}
|
|
861
869
|
|
|
@@ -909,6 +917,7 @@ export class TaskTool implements AgentTool<TSchema, TaskToolDetails, Theme> {
|
|
|
909
917
|
contextFiles,
|
|
910
918
|
skills: availableSkills,
|
|
911
919
|
promptTemplates,
|
|
920
|
+
localProtocolOptions,
|
|
912
921
|
});
|
|
913
922
|
if (mergeMode === "branch" && result.exitCode === 0) {
|
|
914
923
|
try {
|
package/src/tools/ast-edit.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as path from "node:path";
|
|
2
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
3
2
|
import { type AstReplaceChange, astEdit } from "@oh-my-pi/pi-natives";
|
|
4
3
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
@@ -16,6 +15,7 @@ import { createFileRecorder, formatResultPath } from "./file-recorder";
|
|
|
16
15
|
import { formatGroupedFiles } from "./grouped-file-output";
|
|
17
16
|
import type { OutputMeta } from "./output-meta";
|
|
18
17
|
import {
|
|
18
|
+
formatPathRelativeToCwd,
|
|
19
19
|
hasGlobPathChars,
|
|
20
20
|
normalizePathLikeInput,
|
|
21
21
|
parseSearchPath,
|
|
@@ -106,10 +106,7 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
106
106
|
const normalizedRewrites = Object.fromEntries(ops);
|
|
107
107
|
const maxFiles = $envpos("PI_MAX_AST_FILES", 1000);
|
|
108
108
|
|
|
109
|
-
const formatScopePath = (targetPath: string): string =>
|
|
110
|
-
const relative = path.relative(this.session.cwd, targetPath).replace(/\\/g, "/");
|
|
111
|
-
return relative.length === 0 ? "." : relative;
|
|
112
|
-
};
|
|
109
|
+
const formatScopePath = (targetPath: string): string => formatPathRelativeToCwd(targetPath, this.session.cwd);
|
|
113
110
|
let searchPath: string | undefined;
|
|
114
111
|
let scopePath: string | undefined;
|
|
115
112
|
let globFilter: string | undefined;
|
|
@@ -164,7 +161,8 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
164
161
|
});
|
|
165
162
|
|
|
166
163
|
const dedupedParseErrors = dedupeParseErrors(result.parseErrors);
|
|
167
|
-
const formatPath = (filePath: string): string =>
|
|
164
|
+
const formatPath = (filePath: string): string =>
|
|
165
|
+
formatResultPath(filePath, isDirectory, resolvedSearchPath, this.session.cwd);
|
|
168
166
|
|
|
169
167
|
const { record: recordFile, list: fileList } = createFileRecorder();
|
|
170
168
|
const fileReplacementCounts = new Map<string, number>();
|
package/src/tools/ast-grep.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as path from "node:path";
|
|
2
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
3
2
|
import { type AstFindMatch, astGrep } from "@oh-my-pi/pi-natives";
|
|
4
3
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
@@ -16,6 +15,7 @@ import { formatGroupedFiles } from "./grouped-file-output";
|
|
|
16
15
|
import { formatMatchLine } from "./match-line-format";
|
|
17
16
|
import type { OutputMeta } from "./output-meta";
|
|
18
17
|
import {
|
|
18
|
+
formatPathRelativeToCwd,
|
|
19
19
|
hasGlobPathChars,
|
|
20
20
|
normalizePathLikeInput,
|
|
21
21
|
parseSearchPath,
|
|
@@ -87,10 +87,7 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
|
|
|
87
87
|
if (!Number.isFinite(skip) || skip < 0) {
|
|
88
88
|
throw new ToolError("skip must be a non-negative number");
|
|
89
89
|
}
|
|
90
|
-
const formatScopePath = (targetPath: string): string =>
|
|
91
|
-
const relative = path.relative(this.session.cwd, targetPath).replace(/\\/g, "/");
|
|
92
|
-
return relative.length === 0 ? "." : relative;
|
|
93
|
-
};
|
|
90
|
+
const formatScopePath = (targetPath: string): string => formatPathRelativeToCwd(targetPath, this.session.cwd);
|
|
94
91
|
let searchPath: string | undefined;
|
|
95
92
|
let scopePath: string | undefined;
|
|
96
93
|
let globFilter: string | undefined;
|
|
@@ -147,7 +144,8 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
|
|
|
147
144
|
return parseError?.[1] ?? error;
|
|
148
145
|
});
|
|
149
146
|
const dedupedParseErrors = dedupeParseErrors(normalizedParseErrors);
|
|
150
|
-
const formatPath = (filePath: string): string =>
|
|
147
|
+
const formatPath = (filePath: string): string =>
|
|
148
|
+
formatResultPath(filePath, isDirectory, resolvedSearchPath, this.session.cwd);
|
|
151
149
|
|
|
152
150
|
const { record: recordFile, list: fileList } = createFileRecorder();
|
|
153
151
|
const fileMatchCounts = new Map<string, number>();
|
package/src/tools/bash.ts
CHANGED
|
@@ -269,7 +269,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
269
269
|
autoBackgroundThresholdSeconds: Math.max(0, Math.floor(this.#autoBackgroundThresholdMs / 1000)),
|
|
270
270
|
hasAstGrep: this.session.settings.get("astGrep.enabled"),
|
|
271
271
|
hasAstEdit: this.session.settings.get("astEdit.enabled"),
|
|
272
|
-
|
|
272
|
+
hasSearch: this.session.settings.get("search.enabled"),
|
|
273
273
|
hasFind: this.session.settings.get("find.enabled"),
|
|
274
274
|
});
|
|
275
275
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { formatPathRelativeToCwd } from "./path-utils";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Creates a deduplicating recorder for relative file paths.
|
|
@@ -22,14 +23,13 @@ export function createFileRecorder(): {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
|
-
* Strip
|
|
26
|
-
*
|
|
27
|
-
* so tool output does not leak absolute paths.
|
|
26
|
+
* Strip native virtual-root prefixes and format file paths relative to cwd when
|
|
27
|
+
* they are inside cwd. Paths outside cwd remain absolute.
|
|
28
28
|
*/
|
|
29
|
-
export function formatResultPath(filePath: string, isDirectory: boolean): string {
|
|
29
|
+
export function formatResultPath(filePath: string, isDirectory: boolean, basePath: string, cwd: string): string {
|
|
30
30
|
const cleanPath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
|
|
31
31
|
if (isDirectory) {
|
|
32
|
-
return
|
|
32
|
+
return formatPathRelativeToCwd(path.resolve(basePath, cleanPath), cwd);
|
|
33
33
|
}
|
|
34
|
-
return
|
|
34
|
+
return formatPathRelativeToCwd(basePath, cwd);
|
|
35
35
|
}
|
package/src/tools/find.ts
CHANGED
|
@@ -23,7 +23,13 @@ import {
|
|
|
23
23
|
import type { ToolSession } from ".";
|
|
24
24
|
import { applyListLimit } from "./list-limit";
|
|
25
25
|
import { formatFullOutputReference, type OutputMeta } from "./output-meta";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
formatPathRelativeToCwd,
|
|
28
|
+
normalizePathLikeInput,
|
|
29
|
+
parseFindPattern,
|
|
30
|
+
resolveMultiFindPattern,
|
|
31
|
+
resolveToCwd,
|
|
32
|
+
} from "./path-utils";
|
|
27
33
|
import { formatCount, formatEmptyMessage, formatErrorMessage, PREVIEW_LIMITS } from "./render-utils";
|
|
28
34
|
import { ToolAbortError, ToolError, throwIfAborted } from "./tool-errors";
|
|
29
35
|
import { toolResult } from "./tool-result";
|
|
@@ -101,10 +107,7 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
101
107
|
const { pattern, limit, hidden } = params;
|
|
102
108
|
|
|
103
109
|
return untilAborted(signal, async () => {
|
|
104
|
-
const formatScopePath = (targetPath: string): string =>
|
|
105
|
-
const relative = path.relative(this.session.cwd, targetPath).replace(/\\/g, "/");
|
|
106
|
-
return relative.length === 0 ? "." : relative;
|
|
107
|
-
};
|
|
110
|
+
const formatScopePath = (targetPath: string): string => formatPathRelativeToCwd(targetPath, this.session.cwd);
|
|
108
111
|
const normalizedPattern = normalizePathLikeInput(pattern).replace(/\\/g, "/");
|
|
109
112
|
if (!normalizedPattern) {
|
|
110
113
|
throw new ToolError("Pattern must not be empty");
|
|
@@ -132,14 +135,9 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
132
135
|
const formatMatchPath = (matchPath: string, fileType?: natives.FileType): string => {
|
|
133
136
|
const hadTrailingSlash = matchPath.endsWith("/") || matchPath.endsWith("\\");
|
|
134
137
|
const absolutePath = path.isAbsolute(matchPath) ? matchPath : path.resolve(searchPath, matchPath);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
if ((fileType === natives.FileType.Dir || hadTrailingSlash) && !relativePath.endsWith("/")) {
|
|
140
|
-
relativePath += "/";
|
|
141
|
-
}
|
|
142
|
-
return relativePath;
|
|
138
|
+
return formatPathRelativeToCwd(absolutePath, this.session.cwd, {
|
|
139
|
+
trailingSlash: fileType === natives.FileType.Dir || hadTrailingSlash,
|
|
140
|
+
});
|
|
143
141
|
};
|
|
144
142
|
|
|
145
143
|
const buildResult = (files: string[]): AgentToolResult<FindToolDetails> => {
|
package/src/tools/index.ts
CHANGED
|
@@ -18,7 +18,7 @@ import type { ToolChoiceQueue } from "../session/tool-choice-queue";
|
|
|
18
18
|
import { TaskTool } from "../task";
|
|
19
19
|
import type { AgentOutputManager } from "../task/output-manager";
|
|
20
20
|
import type { EventBus } from "../utils/event-bus";
|
|
21
|
-
import {
|
|
21
|
+
import { WebSearchTool } from "../web/search";
|
|
22
22
|
import { AskTool } from "./ask";
|
|
23
23
|
import { AstEditTool } from "./ast-edit";
|
|
24
24
|
import { AstGrepTool } from "./ast-grep";
|
|
@@ -30,7 +30,6 @@ import { DebugTool } from "./debug";
|
|
|
30
30
|
import { ExitPlanModeTool } from "./exit-plan-mode";
|
|
31
31
|
import { FindTool } from "./find";
|
|
32
32
|
import { GithubTool } from "./gh";
|
|
33
|
-
import { GrepTool } from "./grep";
|
|
34
33
|
import { InspectImageTool } from "./inspect-image";
|
|
35
34
|
import { IrcTool } from "./irc";
|
|
36
35
|
import { JobTool } from "./job";
|
|
@@ -42,6 +41,7 @@ import { RenderMermaidTool } from "./render-mermaid";
|
|
|
42
41
|
import { createReportToolIssueTool, isAutoQaEnabled } from "./report-tool-issue";
|
|
43
42
|
import { ResolveTool } from "./resolve";
|
|
44
43
|
import { reportFindingTool } from "./review";
|
|
44
|
+
import { SearchTool } from "./search";
|
|
45
45
|
import { SearchToolBm25Tool } from "./search-tool-bm25";
|
|
46
46
|
import { loadSshTool } from "./ssh";
|
|
47
47
|
import { type TodoPhase, TodoWriteTool } from "./todo-write";
|
|
@@ -68,7 +68,6 @@ export * from "./debug";
|
|
|
68
68
|
export * from "./exit-plan-mode";
|
|
69
69
|
export * from "./find";
|
|
70
70
|
export * from "./gh";
|
|
71
|
-
export * from "./grep";
|
|
72
71
|
export * from "./image-gen";
|
|
73
72
|
export * from "./inspect-image";
|
|
74
73
|
export * from "./irc";
|
|
@@ -80,6 +79,7 @@ export * from "./render-mermaid";
|
|
|
80
79
|
export * from "./report-tool-issue";
|
|
81
80
|
export * from "./resolve";
|
|
82
81
|
export * from "./review";
|
|
82
|
+
export * from "./search";
|
|
83
83
|
export * from "./search-tool-bm25";
|
|
84
84
|
export * from "./ssh";
|
|
85
85
|
export * from "./todo-write";
|
|
@@ -214,7 +214,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
|
214
214
|
edit: s => new EditTool(s),
|
|
215
215
|
github: GithubTool.createIf,
|
|
216
216
|
find: s => new FindTool(s),
|
|
217
|
-
|
|
217
|
+
search: s => new SearchTool(s),
|
|
218
218
|
lsp: LspTool.createIf,
|
|
219
219
|
notebook: s => new NotebookTool(s),
|
|
220
220
|
read: s => new ReadTool(s),
|
|
@@ -226,7 +226,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
|
226
226
|
job: JobTool.createIf,
|
|
227
227
|
irc: IrcTool.createIf,
|
|
228
228
|
todo_write: s => new TodoWriteTool(s),
|
|
229
|
-
web_search: s => new
|
|
229
|
+
web_search: s => new WebSearchTool(s),
|
|
230
230
|
search_tool_bm25: SearchToolBm25Tool.createIf,
|
|
231
231
|
write: s => new WriteTool(s),
|
|
232
232
|
};
|
|
@@ -357,7 +357,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
357
357
|
// Auto-include AST counterparts when their text-based sibling is present
|
|
358
358
|
if (requestedTools) {
|
|
359
359
|
if (
|
|
360
|
-
requestedTools.includes("
|
|
360
|
+
requestedTools.includes("search") &&
|
|
361
361
|
!requestedTools.includes("ast_grep") &&
|
|
362
362
|
session.settings.get("astGrep.enabled")
|
|
363
363
|
) {
|
|
@@ -379,7 +379,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
379
379
|
if (name === "debug") return session.settings.get("debug.enabled");
|
|
380
380
|
if (name === "todo_write") return !includeYield && session.settings.get("todo.enabled");
|
|
381
381
|
if (name === "find") return session.settings.get("find.enabled");
|
|
382
|
-
if (name === "
|
|
382
|
+
if (name === "search") return session.settings.get("search.enabled");
|
|
383
383
|
if (name === "github") return session.settings.get("github.enabled");
|
|
384
384
|
if (name === "ast_grep") return session.settings.get("astGrep.enabled");
|
|
385
385
|
if (name === "ast_edit") return session.settings.get("astEdit.enabled");
|
package/src/tools/path-utils.ts
CHANGED
|
@@ -157,6 +157,27 @@ export function resolveToCwd(filePath: string, cwd: string): string {
|
|
|
157
157
|
return path.resolve(cwd, expanded);
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
export function formatPathRelativeToCwd(
|
|
161
|
+
filePath: string,
|
|
162
|
+
cwd: string,
|
|
163
|
+
options: { trailingSlash?: boolean } = {},
|
|
164
|
+
): string {
|
|
165
|
+
const resolvedCwd = path.resolve(cwd);
|
|
166
|
+
const normalized = normalizeLocalScheme(filePath);
|
|
167
|
+
if (isInternalUrlPath(normalized)) {
|
|
168
|
+
return normalized;
|
|
169
|
+
}
|
|
170
|
+
const expanded = expandPath(normalized);
|
|
171
|
+
const resolvedPath = path.isAbsolute(expanded) ? path.resolve(expanded) : path.resolve(cwd, expanded);
|
|
172
|
+
const relative = path.relative(resolvedCwd, resolvedPath);
|
|
173
|
+
const isWithinCwd = relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
174
|
+
let displayPath = normalizePosixPath(isWithinCwd ? relative || "." : resolvedPath);
|
|
175
|
+
if (options.trailingSlash && displayPath !== "." && !displayPath.endsWith("/")) {
|
|
176
|
+
displayPath += "/";
|
|
177
|
+
}
|
|
178
|
+
return displayPath;
|
|
179
|
+
}
|
|
180
|
+
|
|
160
181
|
/**
|
|
161
182
|
* Strip matching surrounding double quotes from a path string.
|
|
162
183
|
* Common when users paste quoted paths from Windows Explorer or shell copy-paste.
|
|
@@ -381,8 +402,14 @@ function findCommonBasePath(paths: string[]): string {
|
|
|
381
402
|
return joined || path.parse(path.resolve(paths[0])).root;
|
|
382
403
|
}
|
|
383
404
|
|
|
384
|
-
function toScopeDisplay(items: string[]): string {
|
|
385
|
-
return items
|
|
405
|
+
function toScopeDisplay(items: string[], cwd: string): string {
|
|
406
|
+
return items
|
|
407
|
+
.map(item =>
|
|
408
|
+
formatPathRelativeToCwd(item, cwd, {
|
|
409
|
+
trailingSlash: item.endsWith("/") || item.endsWith("\\"),
|
|
410
|
+
}),
|
|
411
|
+
)
|
|
412
|
+
.join(", ");
|
|
386
413
|
}
|
|
387
414
|
|
|
388
415
|
function looksLikeDelimitedPathToken(token: string): boolean {
|
|
@@ -533,7 +560,7 @@ export async function resolveMultiSearchPath(
|
|
|
533
560
|
return {
|
|
534
561
|
basePath: commonBasePath,
|
|
535
562
|
glob: buildBraceUnion(combinedPatterns),
|
|
536
|
-
scopePath: toScopeDisplay(pathItems),
|
|
563
|
+
scopePath: toScopeDisplay(pathItems, cwd),
|
|
537
564
|
exactFilePaths: allExactFiles ? parsedItems.map(item => item.absoluteBasePath) : undefined,
|
|
538
565
|
};
|
|
539
566
|
}
|
|
@@ -571,7 +598,7 @@ export async function resolveMultiFindPattern(
|
|
|
571
598
|
return {
|
|
572
599
|
basePath: commonBasePath,
|
|
573
600
|
globPattern: buildBraceUnion(combinedPatterns) ?? "**/*",
|
|
574
|
-
scopePath: toScopeDisplay(patternItems),
|
|
601
|
+
scopePath: toScopeDisplay(patternItems, cwd),
|
|
575
602
|
};
|
|
576
603
|
}
|
|
577
604
|
|
package/src/tools/read.ts
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
} from "./fetch";
|
|
41
41
|
import { applyListLimit } from "./list-limit";
|
|
42
42
|
import { formatFullOutputReference, formatStyledTruncationWarning, type OutputMeta } from "./output-meta";
|
|
43
|
-
import { expandPath, resolveReadPath } from "./path-utils";
|
|
43
|
+
import { expandPath, formatPathRelativeToCwd, resolveReadPath } from "./path-utils";
|
|
44
44
|
import { formatAge, formatBytes, shortenPath, wrapBrackets } from "./render-utils";
|
|
45
45
|
import {
|
|
46
46
|
executeReadQuery,
|
|
@@ -1046,7 +1046,10 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1046
1046
|
? "- Alpha: no"
|
|
1047
1047
|
: "- Alpha: unknown",
|
|
1048
1048
|
"",
|
|
1049
|
-
`If you want to analyze the image, call inspect_image with path="${
|
|
1049
|
+
`If you want to analyze the image, call inspect_image with path="${formatPathRelativeToCwd(
|
|
1050
|
+
absolutePath,
|
|
1051
|
+
this.session.cwd,
|
|
1052
|
+
)}" and a question describing what to inspect and the desired output format.`,
|
|
1050
1053
|
];
|
|
1051
1054
|
content = [{ type: "text", text: metadataLines.join("\n") }];
|
|
1052
1055
|
details = {};
|
|
@@ -1110,12 +1113,15 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1110
1113
|
const effectiveLimit = limit ?? DEFAULT_LIMIT;
|
|
1111
1114
|
const maxLinesToCollect = Math.min(effectiveLimit, DEFAULT_MAX_LINES);
|
|
1112
1115
|
const selectedLineLimit = effectiveLimit;
|
|
1116
|
+
// Scale byte budget with line limit so the configured line count actually fits.
|
|
1117
|
+
// Assume ~512 bytes/line average; never go below the shared default.
|
|
1118
|
+
const maxBytesForRead = Math.max(DEFAULT_MAX_BYTES, maxLinesToCollect * 512);
|
|
1113
1119
|
|
|
1114
1120
|
const streamResult = await streamLinesFromFile(
|
|
1115
1121
|
absolutePath,
|
|
1116
1122
|
startLine,
|
|
1117
1123
|
maxLinesToCollect,
|
|
1118
|
-
|
|
1124
|
+
maxBytesForRead,
|
|
1119
1125
|
selectedLineLimit,
|
|
1120
1126
|
signal,
|
|
1121
1127
|
);
|
|
@@ -1146,7 +1152,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1146
1152
|
const totalSelectedLines = totalFileLines - startLine;
|
|
1147
1153
|
const totalSelectedBytes = collectedBytes;
|
|
1148
1154
|
const wasTruncated = collectedLines.length < totalSelectedLines || stoppedByByteLimit;
|
|
1149
|
-
const firstLineExceedsLimit = firstLineByteLength !== undefined && firstLineByteLength >
|
|
1155
|
+
const firstLineExceedsLimit = firstLineByteLength !== undefined && firstLineByteLength > maxBytesForRead;
|
|
1150
1156
|
|
|
1151
1157
|
const truncation: TruncationResult = {
|
|
1152
1158
|
content: selectedContent,
|
|
@@ -1178,14 +1184,14 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1178
1184
|
if (shouldAddHashLines) {
|
|
1179
1185
|
outputText = `[Line ${startLineDisplay} is ${formatBytes(
|
|
1180
1186
|
firstLineBytes,
|
|
1181
|
-
)}, exceeds ${formatBytes(
|
|
1187
|
+
)}, exceeds ${formatBytes(maxBytesForRead)} limit. Hashline output requires full lines; cannot compute hashes for a truncated preview.]`;
|
|
1182
1188
|
} else {
|
|
1183
1189
|
outputText = formatText(snippet.text, startLineDisplay);
|
|
1184
1190
|
}
|
|
1185
1191
|
if (snippet.text.length === 0) {
|
|
1186
1192
|
outputText = `[Line ${startLineDisplay} is ${formatBytes(
|
|
1187
1193
|
firstLineBytes,
|
|
1188
|
-
)}, exceeds ${formatBytes(
|
|
1194
|
+
)}, exceeds ${formatBytes(maxBytesForRead)} limit. Unable to display a valid UTF-8 snippet.]`;
|
|
1189
1195
|
}
|
|
1190
1196
|
details = { truncation };
|
|
1191
1197
|
sourcePath = absolutePath;
|
package/src/tools/renderers.ts
CHANGED
|
@@ -18,13 +18,13 @@ import { calculatorToolRenderer } from "./calculator";
|
|
|
18
18
|
import { debugToolRenderer } from "./debug";
|
|
19
19
|
import { findToolRenderer } from "./find";
|
|
20
20
|
import { githubToolRenderer } from "./gh-renderer";
|
|
21
|
-
import { grepToolRenderer } from "./grep";
|
|
22
21
|
import { inspectImageToolRenderer } from "./inspect-image-renderer";
|
|
23
22
|
import { jobToolRenderer } from "./job";
|
|
24
23
|
import { notebookToolRenderer } from "./notebook";
|
|
25
24
|
import { pythonToolRenderer } from "./python";
|
|
26
25
|
import { readToolRenderer } from "./read";
|
|
27
26
|
import { resolveToolRenderer } from "./resolve";
|
|
27
|
+
import { searchToolRenderer } from "./search";
|
|
28
28
|
import { searchToolBm25Renderer } from "./search-tool-bm25";
|
|
29
29
|
import { sshToolRenderer } from "./ssh";
|
|
30
30
|
import { todoWriteToolRenderer } from "./todo-write";
|
|
@@ -54,7 +54,7 @@ export const toolRenderers: Record<string, ToolRenderer> = {
|
|
|
54
54
|
edit: editToolRenderer as ToolRenderer,
|
|
55
55
|
apply_patch: editToolRenderer as ToolRenderer,
|
|
56
56
|
find: findToolRenderer as ToolRenderer,
|
|
57
|
-
|
|
57
|
+
search: searchToolRenderer as ToolRenderer,
|
|
58
58
|
lsp: lspToolRenderer as ToolRenderer,
|
|
59
59
|
notebook: notebookToolRenderer as ToolRenderer,
|
|
60
60
|
inspect_image: inspectImageToolRenderer as ToolRenderer,
|