@oh-my-pi/pi-coding-agent 16.0.4 → 16.0.6
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 +94 -0
- package/dist/cli.js +2027 -1396
- package/dist/types/advisor/advise-tool.d.ts +31 -19
- package/dist/types/autoresearch/tools/init-experiment.d.ts +13 -17
- package/dist/types/autoresearch/tools/log-experiment.d.ts +17 -19
- package/dist/types/autoresearch/tools/run-experiment.d.ts +3 -4
- package/dist/types/autoresearch/tools/update-notes.d.ts +4 -5
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/bench-cli.d.ts +6 -0
- package/dist/types/cli/ttsr-cli.d.ts +39 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/commands/ttsr.d.ts +57 -0
- package/dist/types/commit/agentic/tools/analyze-file.d.ts +4 -5
- package/dist/types/commit/agentic/tools/git-file-diff.d.ts +4 -5
- package/dist/types/commit/agentic/tools/git-hunk.d.ts +5 -6
- package/dist/types/commit/agentic/tools/git-overview.d.ts +4 -5
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +23 -24
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +11 -32
- package/dist/types/commit/agentic/tools/recent-commits.d.ts +3 -4
- package/dist/types/commit/agentic/tools/schemas.d.ts +6 -27
- package/dist/types/commit/agentic/tools/split-commit.d.ts +28 -49
- package/dist/types/commit/changelog/generate.d.ts +12 -13
- package/dist/types/commit/shared-llm.d.ts +10 -37
- package/dist/types/config/config-file.d.ts +4 -4
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/models-config-schema.d.ts +625 -990
- package/dist/types/config/models-config.d.ts +229 -217
- package/dist/types/config/settings-schema.d.ts +144 -25
- package/dist/types/edit/hashline/params.d.ts +7 -11
- package/dist/types/edit/index.d.ts +2 -1
- package/dist/types/edit/modes/apply-patch.d.ts +4 -5
- package/dist/types/edit/modes/patch.d.ts +15 -24
- package/dist/types/edit/modes/replace.d.ts +16 -17
- package/dist/types/eval/js/index.d.ts +1 -0
- package/dist/types/extensibility/custom-commands/types.d.ts +6 -3
- package/dist/types/extensibility/custom-tools/types.d.ts +8 -5
- package/dist/types/extensibility/extensions/runner.d.ts +5 -2
- package/dist/types/extensibility/extensions/types.d.ts +14 -10
- package/dist/types/extensibility/hooks/types.d.ts +7 -4
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +13 -5
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +17 -0
- package/dist/types/extensibility/shared-events.d.ts +22 -1
- package/dist/types/extensibility/typebox.d.ts +80 -58
- package/dist/types/goals/tools/goal-tool.d.ts +11 -24
- package/dist/types/index.d.ts +2 -0
- package/dist/types/lsp/index.d.ts +11 -26
- package/dist/types/lsp/types.d.ts +12 -28
- package/dist/types/main.d.ts +1 -0
- package/dist/types/mcp/client.d.ts +8 -0
- package/dist/types/modes/components/btw-panel.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +3 -1
- package/dist/types/modes/components/status-line/component.d.ts +1 -1
- package/dist/types/modes/components/status-line/context-thresholds.d.ts +0 -1
- package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +1 -1
- package/dist/types/modes/setup-wizard/index.d.ts +1 -0
- package/dist/types/modes/setup-wizard/startup-splash.d.ts +7 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +3 -0
- package/dist/types/modes/utils/context-usage.d.ts +12 -0
- package/dist/types/sdk.d.ts +8 -1
- package/dist/types/session/agent-session.d.ts +24 -0
- package/dist/types/session/session-persistence.d.ts +4 -0
- package/dist/types/startup-splash.d.ts +12 -0
- package/dist/types/task/types.d.ts +47 -48
- package/dist/types/tools/ask.d.ts +26 -27
- package/dist/types/tools/ast-edit.d.ts +17 -17
- package/dist/types/tools/ast-grep.d.ts +12 -13
- package/dist/types/tools/bash.d.ts +20 -17
- package/dist/types/tools/browser.d.ts +46 -71
- package/dist/types/tools/checkpoint.d.ts +14 -15
- package/dist/types/tools/debug.d.ts +82 -145
- package/dist/types/tools/eval.d.ts +30 -40
- package/dist/types/tools/find.d.ts +17 -18
- package/dist/types/tools/gh.d.ts +49 -78
- package/dist/types/tools/image-gen.d.ts +20 -36
- package/dist/types/tools/inspect-image.d.ts +10 -11
- package/dist/types/tools/irc.d.ts +22 -33
- package/dist/types/tools/job.d.ts +11 -12
- package/dist/types/tools/learn.d.ts +21 -28
- package/dist/types/tools/manage-skill.d.ts +13 -22
- package/dist/types/tools/memory-edit.d.ts +15 -24
- package/dist/types/tools/memory-recall.d.ts +7 -8
- package/dist/types/tools/memory-reflect.d.ts +9 -10
- package/dist/types/tools/memory-retain.d.ts +13 -14
- package/dist/types/tools/read.d.ts +8 -8
- package/dist/types/tools/resolve.d.ts +11 -18
- package/dist/types/tools/review.d.ts +9 -15
- package/dist/types/tools/search-tool-bm25.d.ts +9 -10
- package/dist/types/tools/search.d.ts +16 -17
- package/dist/types/tools/ssh.d.ts +14 -15
- package/dist/types/tools/todo.d.ts +27 -43
- package/dist/types/tools/tts.d.ts +8 -9
- package/dist/types/tools/write.d.ts +9 -10
- package/dist/types/tui/code-cell.d.ts +2 -0
- package/dist/types/tui/index.d.ts +1 -0
- package/dist/types/tui/width-aware-text.d.ts +23 -0
- package/dist/types/utils/image-vision-fallback.d.ts +28 -0
- package/dist/types/utils/markit.d.ts +10 -1
- package/dist/types/web/search/index.d.ts +17 -28
- package/dist/types/web/search/providers/base.d.ts +1 -0
- package/dist/types/web/search/providers/gemini.d.ts +1 -0
- package/dist/types/web/search/providers/perplexity.d.ts +0 -2
- package/dist/types/web/search/types.d.ts +32 -26
- package/package.json +14 -13
- package/scripts/omp +1 -1
- package/src/advisor/__tests__/advisor.test.ts +103 -1
- package/src/advisor/advise-tool.ts +47 -11
- package/src/autoresearch/tools/init-experiment.ts +13 -16
- package/src/autoresearch/tools/log-experiment.ts +15 -18
- package/src/autoresearch/tools/run-experiment.ts +3 -3
- package/src/autoresearch/tools/update-notes.ts +4 -4
- package/src/cli/args.ts +1 -0
- package/src/cli/bench-cli.ts +30 -7
- package/src/cli/flag-tables.ts +8 -0
- package/src/cli/ttsr-cli.ts +995 -0
- package/src/cli-commands.ts +1 -0
- package/src/cli.ts +7 -1
- package/src/collab/host.ts +2 -2
- package/src/commands/launch.ts +3 -0
- package/src/commands/ttsr.ts +125 -0
- package/src/commit/agentic/tools/analyze-file.ts +4 -4
- package/src/commit/agentic/tools/git-file-diff.ts +4 -4
- package/src/commit/agentic/tools/git-hunk.ts +7 -5
- package/src/commit/agentic/tools/git-overview.ts +4 -4
- package/src/commit/agentic/tools/propose-changelog.ts +18 -15
- package/src/commit/agentic/tools/propose-commit.ts +6 -6
- package/src/commit/agentic/tools/recent-commits.ts +3 -3
- package/src/commit/agentic/tools/schemas.ts +8 -20
- package/src/commit/agentic/tools/split-commit.ts +19 -23
- package/src/commit/analysis/summary.ts +7 -5
- package/src/commit/changelog/generate.ts +15 -11
- package/src/commit/shared-llm.ts +17 -24
- package/src/config/config-file.ts +13 -15
- package/src/config/keybindings.ts +6 -0
- package/src/config/models-config-schema.ts +206 -179
- package/src/config/settings-schema.ts +118 -2
- package/src/discovery/builtin-rules/index.ts +2 -0
- package/src/discovery/builtin-rules/ts-import-type.md +2 -2
- package/src/discovery/builtin-rules/ts-no-any.md +11 -2
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +55 -0
- package/src/edit/hashline/params.ts +12 -11
- package/src/edit/index.ts +5 -4
- package/src/edit/modes/apply-patch.ts +4 -4
- package/src/edit/modes/patch.ts +15 -18
- package/src/edit/modes/replace.ts +13 -17
- package/src/edit/renderer.ts +0 -1
- package/src/eval/agent-bridge.ts +11 -13
- package/src/eval/completion-bridge.ts +25 -17
- package/src/eval/js/context-manager.ts +17 -2
- package/src/eval/js/index.ts +1 -1
- package/src/eval/py/executor.ts +2 -2
- package/src/eval/py/runner.py +44 -0
- package/src/extensibility/custom-commands/loader.ts +5 -3
- package/src/extensibility/custom-commands/types.ts +6 -3
- package/src/extensibility/custom-tools/loader.ts +4 -2
- package/src/extensibility/custom-tools/types.ts +8 -5
- package/src/extensibility/extensions/loader.ts +4 -2
- package/src/extensibility/extensions/runner.ts +20 -2
- package/src/extensibility/extensions/types.ts +22 -8
- package/src/extensibility/hooks/loader.ts +5 -2
- package/src/extensibility/hooks/types.ts +7 -4
- package/src/extensibility/legacy-pi-ai-shim.ts +42 -5
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +113 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +13 -13
- package/src/extensibility/shared-events.ts +24 -0
- package/src/extensibility/tool-proxy.ts +4 -1
- package/src/extensibility/typebox.ts +778 -251
- package/src/goals/guided-setup.ts +12 -3
- package/src/goals/tools/goal-tool.ts +6 -6
- package/src/index.ts +2 -0
- package/src/internal-urls/docs-index.generated.ts +15 -13
- package/src/lsp/types.ts +13 -27
- package/src/main.ts +29 -21
- package/src/mcp/client.ts +38 -13
- package/src/mcp/render.ts +102 -89
- package/src/modes/components/agent-hub.ts +11 -4
- package/src/modes/components/branch-summary-message.ts +1 -0
- package/src/modes/components/btw-panel.ts +5 -1
- package/src/modes/components/collab-prompt-message.ts +9 -7
- package/src/modes/components/compaction-summary-message.ts +1 -0
- package/src/modes/components/custom-editor.ts +18 -0
- package/src/modes/components/custom-message.ts +1 -0
- package/src/modes/components/footer.ts +6 -5
- package/src/modes/components/hook-message.ts +1 -0
- package/src/modes/components/read-tool-group.ts +9 -3
- package/src/modes/components/skill-message.ts +1 -0
- package/src/modes/components/status-line/component.ts +139 -15
- package/src/modes/components/status-line/context-thresholds.ts +0 -1
- package/src/modes/components/todo-reminder.ts +1 -0
- package/src/modes/components/tool-execution.ts +17 -10
- package/src/modes/components/ttsr-notification.ts +1 -0
- package/src/modes/components/user-message.ts +6 -6
- package/src/modes/controllers/btw-controller.ts +69 -1
- package/src/modes/controllers/event-controller.ts +2 -7
- package/src/modes/controllers/input-controller.ts +29 -0
- package/src/modes/controllers/selector-controller.ts +10 -3
- package/src/modes/interactive-mode.ts +42 -10
- package/src/modes/rpc/rpc-types.ts +1 -1
- package/src/modes/setup-wizard/index.ts +1 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +77 -5
- package/src/modes/setup-wizard/startup-splash.ts +107 -0
- package/src/modes/theme/theme.ts +133 -143
- package/src/modes/types.ts +3 -0
- package/src/modes/utils/context-usage.ts +37 -20
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/tools/image-attachment-describe-system.md +8 -0
- package/src/prompts/tools/image-attachment-describe.md +10 -0
- package/src/sdk.ts +35 -22
- package/src/session/agent-session.ts +715 -255
- package/src/session/session-history-format.ts +11 -2
- package/src/session/session-loader.ts +19 -32
- package/src/session/session-persistence.ts +27 -11
- package/src/session/snapcompact-inline.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +4 -11
- package/src/ssh/connection-manager.ts +3 -2
- package/src/startup-splash.ts +19 -0
- package/src/task/executor.ts +12 -7
- package/src/task/types.ts +44 -41
- package/src/tool-discovery/tool-index.ts +17 -4
- package/src/tools/ask.ts +14 -14
- package/src/tools/ast-edit.ts +17 -14
- package/src/tools/ast-grep.ts +10 -9
- package/src/tools/bash.ts +15 -10
- package/src/tools/browser/launch.ts +13 -0
- package/src/tools/browser.ts +26 -32
- package/src/tools/checkpoint.ts +7 -7
- package/src/tools/debug.ts +72 -69
- package/src/tools/eval.ts +18 -19
- package/src/tools/find.ts +20 -13
- package/src/tools/gh.ts +29 -49
- package/src/tools/image-gen.ts +94 -57
- package/src/tools/inspect-image.ts +8 -9
- package/src/tools/irc.ts +12 -12
- package/src/tools/job.ts +6 -6
- package/src/tools/learn.ts +11 -14
- package/src/tools/manage-skill.ts +19 -23
- package/src/tools/memory-edit.ts +8 -8
- package/src/tools/memory-recall.ts +4 -4
- package/src/tools/memory-reflect.ts +5 -5
- package/src/tools/memory-retain.ts +9 -11
- package/src/tools/puppeteer/02_stealth_hairline.txt +1 -1
- package/src/tools/puppeteer/04_stealth_iframe.txt +4 -4
- package/src/tools/puppeteer/05_stealth_webgl.txt +1 -1
- package/src/tools/puppeteer/10_stealth_plugins.txt +6 -4
- package/src/tools/puppeteer/12_stealth_codecs.txt +2 -2
- package/src/tools/puppeteer/13_stealth_worker.txt +1 -1
- package/src/tools/read.ts +197 -19
- package/src/tools/report-tool-issue.ts +6 -6
- package/src/tools/resolve.ts +6 -6
- package/src/tools/review.ts +10 -12
- package/src/tools/search-tool-bm25.ts +5 -5
- package/src/tools/search.ts +20 -29
- package/src/tools/ssh.ts +8 -8
- package/src/tools/todo.ts +16 -19
- package/src/tools/tts.ts +16 -15
- package/src/tools/write.ts +5 -5
- package/src/tui/code-cell.ts +44 -3
- package/src/tui/index.ts +1 -0
- package/src/tui/width-aware-text.ts +58 -0
- package/src/utils/image-vision-fallback.ts +197 -0
- package/src/utils/markit.ts +17 -2
- package/src/web/search/index.ts +21 -9
- package/src/web/search/providers/base.ts +1 -0
- package/src/web/search/providers/gemini.ts +56 -18
- package/src/web/search/providers/perplexity.ts +373 -126
- package/src/web/search/types.ts +28 -48
package/src/tools/read.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as os from "node:os";
|
|
3
4
|
import * as path from "node:path";
|
|
4
5
|
import { formatHashlineHeader, formatNumberedLine, formatNumberedLines } from "@oh-my-pi/hashline";
|
|
5
6
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
@@ -8,8 +9,8 @@ import { glob, type SummaryResult, summarizeCode } from "@oh-my-pi/pi-natives";
|
|
|
8
9
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
9
10
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
10
11
|
import { getRemoteDir, logger, prompt, readImageMetadata, untilAborted } from "@oh-my-pi/pi-utils";
|
|
12
|
+
import { type } from "arktype";
|
|
11
13
|
import { LRUCache } from "lru-cache/raw";
|
|
12
|
-
import { z } from "zod/v4";
|
|
13
14
|
import {
|
|
14
15
|
canonicalSnapshotKey,
|
|
15
16
|
getFileSnapshotStore,
|
|
@@ -661,18 +662,40 @@ function prependSuffixResolutionNotice(text: string, suffixResolution?: { from:
|
|
|
661
662
|
const notice = `[Path '${suffixResolution.from}' not found; resolved to '${suffixResolution.to}' via suffix match]`;
|
|
662
663
|
return text ? `${notice}\n${text}` : notice;
|
|
663
664
|
}
|
|
665
|
+
const PDF_IMAGE_PLACEHOLDER_RE = /<!--\s*image:\s*([^\s<>]+)(.*?)-->/g;
|
|
666
|
+
const PDF_IMAGE_MEMBER_RE = /^(.*\.pdf):(.*)$/i;
|
|
667
|
+
const PDF_IMAGE_MEMBER_EXTENSION_RE = /\.png$/i;
|
|
664
668
|
|
|
665
|
-
|
|
666
|
-
.
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
669
|
+
function pdfImageMemberPath(pdfPath: string, imageId: string): string {
|
|
670
|
+
const member = PDF_IMAGE_MEMBER_EXTENSION_RE.test(imageId) ? imageId : `${imageId}.png`;
|
|
671
|
+
return `${pdfPath}:${member}`;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function rewritePdfImagePlaceholders(markdown: string, pdfPath: string): string {
|
|
675
|
+
return markdown.replace(PDF_IMAGE_PLACEHOLDER_RE, (_match: string, imageId: string, metadataText: string) => {
|
|
676
|
+
const metadata = metadataText.trim();
|
|
677
|
+
const suffix = metadata.length > 0 ? ` (${metadata})` : "";
|
|
678
|
+
return `Image ${imageId}${suffix}: read \`${pdfImageMemberPath(pdfPath, imageId)}\``;
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function splitPdfImageMemberReadPath(readPath: string): { pdfPath: string; member: string } | null {
|
|
683
|
+
const match = PDF_IMAGE_MEMBER_RE.exec(readPath);
|
|
684
|
+
if (!match) return null;
|
|
685
|
+
const pdfPath = match[1];
|
|
686
|
+
const member = match[2];
|
|
687
|
+
if (pdfPath === undefined || member === undefined) return null;
|
|
688
|
+
if (member.length !== 0 && !PDF_IMAGE_MEMBER_EXTENSION_RE.test(member)) return null;
|
|
689
|
+
return { pdfPath, member };
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
const readSchema = type({
|
|
693
|
+
path: type("string").describe(
|
|
694
|
+
'Local path, internal URI (e.g. "omp://", "issue://123", "pr://123"), or URL; append :<sel> for line ranges or raw mode (e.g. "src/foo.ts:50-100")',
|
|
695
|
+
),
|
|
696
|
+
});
|
|
674
697
|
|
|
675
|
-
export type ReadToolInput =
|
|
698
|
+
export type ReadToolInput = typeof readSchema.infer;
|
|
676
699
|
|
|
677
700
|
export interface ReadToolDetails {
|
|
678
701
|
kind?: "file" | "url";
|
|
@@ -689,7 +712,11 @@ export interface ReadToolDetails {
|
|
|
689
712
|
/** Raw text + start line for user-visible TUI rendering, set when content is text-like.
|
|
690
713
|
* Mirrors the same lines the model receives but without hashline/line-number prefixes,
|
|
691
714
|
* so the TUI can render the file content with its own gutter without re-parsing the formatted text. */
|
|
692
|
-
displayContent?: {
|
|
715
|
+
displayContent?: {
|
|
716
|
+
text: string;
|
|
717
|
+
startLine: number;
|
|
718
|
+
lineNumbers?: Array<number | null>;
|
|
719
|
+
};
|
|
693
720
|
summary?: { lines: number; elidedSpans: number; elidedLines: number };
|
|
694
721
|
/** Number of unresolved git conflicts surfaced by this read (TUI uses for inline `⚠ N` badge). */
|
|
695
722
|
conflictCount?: number;
|
|
@@ -979,6 +1006,113 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
979
1006
|
return null;
|
|
980
1007
|
}
|
|
981
1008
|
|
|
1009
|
+
#pdfImageCacheDir(absolutePdfPath: string): string {
|
|
1010
|
+
const artifactsDir = this.session.getArtifactsDir?.();
|
|
1011
|
+
let root = artifactsDir ?? undefined;
|
|
1012
|
+
if (root === undefined) {
|
|
1013
|
+
const sessionFile = this.session.getSessionFile();
|
|
1014
|
+
root = sessionFile?.endsWith(".jsonl")
|
|
1015
|
+
? sessionFile.slice(0, -6)
|
|
1016
|
+
: path.join(os.tmpdir(), "omp-read-pdf-images");
|
|
1017
|
+
}
|
|
1018
|
+
const basename = path.basename(absolutePdfPath).replace(/[^A-Za-z0-9._-]/g, "_");
|
|
1019
|
+
return path.join(root, "read-pdf-images", `${basename}-${Bun.hash(absolutePdfPath).toString(36)}`);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
async #listPdfImageMembers(imageDir: string): Promise<string[]> {
|
|
1023
|
+
try {
|
|
1024
|
+
const entries = await fs.readdir(imageDir, { withFileTypes: true });
|
|
1025
|
+
const members: string[] = [];
|
|
1026
|
+
for (const entry of entries) {
|
|
1027
|
+
if (entry.isFile() && PDF_IMAGE_MEMBER_EXTENSION_RE.test(entry.name)) members.push(entry.name);
|
|
1028
|
+
}
|
|
1029
|
+
return members.sort();
|
|
1030
|
+
} catch (error) {
|
|
1031
|
+
if (isNotFoundError(error)) return [];
|
|
1032
|
+
throw error;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
async #ensurePdfImageCache(absolutePdfPath: string, signal?: AbortSignal): Promise<string> {
|
|
1037
|
+
const imageDir = this.#pdfImageCacheDir(absolutePdfPath);
|
|
1038
|
+
const markerPath = path.join(imageDir, ".extracted");
|
|
1039
|
+
try {
|
|
1040
|
+
await fs.stat(markerPath);
|
|
1041
|
+
return imageDir;
|
|
1042
|
+
} catch (error) {
|
|
1043
|
+
if (!isNotFoundError(error)) throw error;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
await fs.rm(imageDir, { recursive: true, force: true });
|
|
1047
|
+
await fs.mkdir(imageDir, { recursive: true });
|
|
1048
|
+
const result = await convertFileWithMarkit(absolutePdfPath, signal, { imageDir });
|
|
1049
|
+
if (!result.ok) {
|
|
1050
|
+
await fs.rm(imageDir, { recursive: true, force: true });
|
|
1051
|
+
throw new ToolError(`Cannot extract images from PDF: ${result.error ?? "conversion failed"}`);
|
|
1052
|
+
}
|
|
1053
|
+
await Bun.write(markerPath, "ok");
|
|
1054
|
+
return imageDir;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
async #readPdfImageMember(
|
|
1058
|
+
absolutePdfPath: string,
|
|
1059
|
+
pdfDisplayPath: string,
|
|
1060
|
+
member: string,
|
|
1061
|
+
suffixResolution: { from: string; to: string } | undefined,
|
|
1062
|
+
signal?: AbortSignal,
|
|
1063
|
+
): Promise<AgentToolResult<ReadToolDetails>> {
|
|
1064
|
+
const imageDir = await this.#ensurePdfImageCache(absolutePdfPath, signal);
|
|
1065
|
+
const members = await this.#listPdfImageMembers(imageDir);
|
|
1066
|
+
if (member.length === 0) {
|
|
1067
|
+
const text =
|
|
1068
|
+
members.length === 0
|
|
1069
|
+
? "No extractable PDF image members found."
|
|
1070
|
+
: `Extractable PDF image members:\n${members
|
|
1071
|
+
.map(imageMember => `- read \`${pdfDisplayPath}:${imageMember}\``)
|
|
1072
|
+
.join("\n")}`;
|
|
1073
|
+
return toolResult<ReadToolDetails>({ resolvedPath: absolutePdfPath, suffixResolution })
|
|
1074
|
+
.text(prependSuffixResolutionNotice(text, suffixResolution))
|
|
1075
|
+
.sourcePath(absolutePdfPath)
|
|
1076
|
+
.done();
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
if (!members.includes(member)) {
|
|
1080
|
+
const available = members.length === 0 ? "(none)" : members.join(", ");
|
|
1081
|
+
throw new ToolError(`PDF image member '${member}' not found. Available members: ${available}`);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
const imagePath = path.join(imageDir, member);
|
|
1085
|
+
const imageStat = await Bun.file(imagePath).stat();
|
|
1086
|
+
if (imageStat.size > MAX_IMAGE_SIZE) {
|
|
1087
|
+
const sizeStr = formatBytes(imageStat.size);
|
|
1088
|
+
const maxStr = formatBytes(MAX_IMAGE_SIZE);
|
|
1089
|
+
throw new ToolError(`Image file too large: ${sizeStr} exceeds ${maxStr} limit.`);
|
|
1090
|
+
}
|
|
1091
|
+
const metadata = await readImageMetadata(imagePath);
|
|
1092
|
+
const mimeType = metadata?.mimeType;
|
|
1093
|
+
if (!mimeType) throw new ToolError(`PDF image member '${member}' is not a supported image.`);
|
|
1094
|
+
const imageInput = await loadImageInput({
|
|
1095
|
+
path: `${pdfDisplayPath}:${member}`,
|
|
1096
|
+
cwd: this.session.cwd,
|
|
1097
|
+
autoResize: this.#autoResizeImages,
|
|
1098
|
+
maxBytes: MAX_IMAGE_SIZE,
|
|
1099
|
+
resolvedPath: imagePath,
|
|
1100
|
+
detectedMimeType: mimeType,
|
|
1101
|
+
excludeWebP: webpExclusionForModel(this.session.getActiveModel?.()),
|
|
1102
|
+
});
|
|
1103
|
+
if (!imageInput) {
|
|
1104
|
+
throw new ToolError(`Read image file [${mimeType}] failed: unsupported image format.`);
|
|
1105
|
+
}
|
|
1106
|
+
const textNote = prependSuffixResolutionNotice(imageInput.textNote, suffixResolution);
|
|
1107
|
+
return toolResult<ReadToolDetails>({ resolvedPath: absolutePdfPath, suffixResolution })
|
|
1108
|
+
.content([
|
|
1109
|
+
{ type: "text", text: textNote },
|
|
1110
|
+
{ type: "image", data: imageInput.data, mimeType: imageInput.mimeType },
|
|
1111
|
+
])
|
|
1112
|
+
.sourcePath(imageInput.resolvedPath)
|
|
1113
|
+
.done();
|
|
1114
|
+
}
|
|
1115
|
+
|
|
982
1116
|
#buildInMemoryTextResult(
|
|
983
1117
|
text: string,
|
|
984
1118
|
offset: number | undefined,
|
|
@@ -1058,7 +1192,12 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1058
1192
|
let emittedHashlineHeader = false;
|
|
1059
1193
|
let seenLines: number[] | undefined;
|
|
1060
1194
|
const formatText = (content: string, startNum: number): string => {
|
|
1061
|
-
|
|
1195
|
+
const lineCount = countTextLines(content);
|
|
1196
|
+
details.displayContent = {
|
|
1197
|
+
text: content,
|
|
1198
|
+
startLine: startNum,
|
|
1199
|
+
lineNumbers: Array.from({ length: lineCount }, (_, i) => startNum + i),
|
|
1200
|
+
};
|
|
1062
1201
|
if (shouldAddHashLines) seenLines = contiguousLineNumbers(startNum, countTextLines(content));
|
|
1063
1202
|
const formatted = formatTextWithMode(content, startNum, shouldAddHashLines, shouldAddLineNumbers);
|
|
1064
1203
|
if (!hashContext || emittedHashlineHeader) return formatted;
|
|
@@ -1070,6 +1209,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1070
1209
|
details.displayContent = {
|
|
1071
1210
|
text: lineEntriesToPlainText(entries, BRACKET_CONTEXT_ELLIPSIS),
|
|
1072
1211
|
startLine: firstLine?.kind === "line" ? firstLine.lineNumber : startNum,
|
|
1212
|
+
lineNumbers: entries.map(entry => (entry.kind === "line" ? entry.lineNumber : null)),
|
|
1073
1213
|
};
|
|
1074
1214
|
if (shouldAddHashLines) seenLines = lineNumbersFromEntries(entries);
|
|
1075
1215
|
const formatted = formatLineEntriesWithMode(entries, shouldAddHashLines, shouldAddLineNumbers);
|
|
@@ -1218,6 +1358,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1218
1358
|
details.displayContent = {
|
|
1219
1359
|
text: lineEntriesToPlainText(entries, BRACKET_CONTEXT_ELLIPSIS),
|
|
1220
1360
|
startLine: firstLine.lineNumber,
|
|
1361
|
+
lineNumbers: entries.map(entry => (entry.kind === "line" ? entry.lineNumber : null)),
|
|
1221
1362
|
};
|
|
1222
1363
|
}
|
|
1223
1364
|
const formatted = formatLineEntriesWithMode(entries, shouldAddHashLines, shouldAddLineNumbers);
|
|
@@ -1255,7 +1396,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1255
1396
|
): Promise<{
|
|
1256
1397
|
outputText: string;
|
|
1257
1398
|
columnTruncated: number;
|
|
1258
|
-
displayContent?: { text: string; startLine: number };
|
|
1399
|
+
displayContent?: { text: string; startLine: number; lineNumbers?: Array<number | null> };
|
|
1259
1400
|
bridgeResult?: AgentToolResult<ReadToolDetails>;
|
|
1260
1401
|
}> {
|
|
1261
1402
|
const rawSelector = isRawSelector(parsed);
|
|
@@ -1292,7 +1433,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1292
1433
|
const displayLineByNumber = new Map<number, string>();
|
|
1293
1434
|
const fullLines = rawSelector ? undefined : await readBracketContextFullLines(absolutePath, fileSize);
|
|
1294
1435
|
let columnTruncated = 0;
|
|
1295
|
-
let displayContent: { text: string; startLine: number } | undefined;
|
|
1436
|
+
let displayContent: { text: string; startLine: number; lineNumbers?: Array<number | null> } | undefined;
|
|
1296
1437
|
|
|
1297
1438
|
for (const range of ranges) {
|
|
1298
1439
|
const rangeStart = range.startLine - 1; // 0-indexed
|
|
@@ -1375,6 +1516,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1375
1516
|
displayContent = {
|
|
1376
1517
|
text: lineEntriesToPlainText(entries, BRACKET_CONTEXT_ELLIPSIS),
|
|
1377
1518
|
startLine: firstLine?.kind === "line" ? firstLine.lineNumber : (visibleSpans[0]?.startLine ?? 1),
|
|
1519
|
+
lineNumbers: entries.map(entry => (entry.kind === "line" ? entry.lineNumber : null)),
|
|
1378
1520
|
};
|
|
1379
1521
|
outputText = formatLineEntriesWithMode(entries, shouldAddHashLines, shouldAddLineNumbers);
|
|
1380
1522
|
} else {
|
|
@@ -1893,6 +2035,30 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1893
2035
|
return this.#readSqlite(sqlitePath, signal);
|
|
1894
2036
|
}
|
|
1895
2037
|
|
|
2038
|
+
const pdfImageMemberPath = splitPdfImageMemberReadPath(readPath);
|
|
2039
|
+
if (pdfImageMemberPath) {
|
|
2040
|
+
let absolutePdfPath = resolveReadPath(pdfImageMemberPath.pdfPath, this.session.cwd);
|
|
2041
|
+
let suffixResolution: { from: string; to: string } | undefined;
|
|
2042
|
+
try {
|
|
2043
|
+
const stat = await Bun.file(absolutePdfPath).stat();
|
|
2044
|
+
if (stat.isDirectory())
|
|
2045
|
+
throw new ToolError(`Path '${pdfImageMemberPath.pdfPath}' is a directory, not a PDF file`);
|
|
2046
|
+
} catch (error) {
|
|
2047
|
+
if (!isNotFoundError(error) || isRemoteMountPath(absolutePdfPath)) throw error;
|
|
2048
|
+
const suffixMatch = await this.#findSuffixMatchCached(suffixCache, pdfImageMemberPath.pdfPath, signal);
|
|
2049
|
+
if (!suffixMatch) throw new ToolError(`Path '${pdfImageMemberPath.pdfPath}' not found`);
|
|
2050
|
+
absolutePdfPath = suffixMatch.absolutePath;
|
|
2051
|
+
suffixResolution = { from: pdfImageMemberPath.pdfPath, to: suffixMatch.displayPath };
|
|
2052
|
+
}
|
|
2053
|
+
return this.#readPdfImageMember(
|
|
2054
|
+
absolutePdfPath,
|
|
2055
|
+
pdfImageMemberPath.pdfPath,
|
|
2056
|
+
pdfImageMemberPath.member,
|
|
2057
|
+
suffixResolution,
|
|
2058
|
+
signal,
|
|
2059
|
+
);
|
|
2060
|
+
}
|
|
2061
|
+
|
|
1896
2062
|
const localTarget = splitPathAndSel(readPath);
|
|
1897
2063
|
const localReadPath = localTarget.path;
|
|
1898
2064
|
const parsed = parseSel(localTarget.sel);
|
|
@@ -2044,20 +2210,22 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2044
2210
|
// Convert document via markit.
|
|
2045
2211
|
const result = await convertFileWithMarkit(absolutePath, signal);
|
|
2046
2212
|
if (result.ok) {
|
|
2213
|
+
const renderedContent =
|
|
2214
|
+
ext === ".pdf" ? rewritePdfImagePlaceholders(result.content, localReadPath) : result.content;
|
|
2047
2215
|
// Route the converted markdown through the in-memory text builder
|
|
2048
2216
|
// so line-range selectors (`file.pdf:50-100`, `:5-16,40-80`) and
|
|
2049
2217
|
// raw mode apply against the converted output. Without this,
|
|
2050
2218
|
// `file.pdf:50-100` silently returned the head of the document
|
|
2051
2219
|
// because only `truncateHead` was being applied.
|
|
2052
2220
|
if (isMultiRange(parsed) && parsed.kind === "lines") {
|
|
2053
|
-
return this.#buildInMemoryMultiRangeResult(
|
|
2221
|
+
return this.#buildInMemoryMultiRangeResult(renderedContent, parsed.ranges, {
|
|
2054
2222
|
details: { resolvedPath: absolutePath },
|
|
2055
2223
|
sourcePath: absolutePath,
|
|
2056
2224
|
entityLabel: "document",
|
|
2057
2225
|
});
|
|
2058
2226
|
}
|
|
2059
2227
|
const { offset, limit } = selToOffsetLimit(parsed);
|
|
2060
|
-
return this.#buildInMemoryTextResult(
|
|
2228
|
+
return this.#buildInMemoryTextResult(renderedContent, offset, limit, {
|
|
2061
2229
|
details: { resolvedPath: absolutePath },
|
|
2062
2230
|
sourcePath: absolutePath,
|
|
2063
2231
|
entityLabel: "document",
|
|
@@ -2292,10 +2460,17 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2292
2460
|
}
|
|
2293
2461
|
}
|
|
2294
2462
|
|
|
2295
|
-
let capturedDisplayContent:
|
|
2463
|
+
let capturedDisplayContent:
|
|
2464
|
+
| { text: string; startLine: number; lineNumbers?: Array<number | null> }
|
|
2465
|
+
| undefined;
|
|
2296
2466
|
let emittedHashlineHeader = false;
|
|
2297
2467
|
const formatText = (text: string, startNum: number): string => {
|
|
2298
|
-
|
|
2468
|
+
const lineCount = countTextLines(text);
|
|
2469
|
+
capturedDisplayContent = {
|
|
2470
|
+
text,
|
|
2471
|
+
startLine: startNum,
|
|
2472
|
+
lineNumbers: Array.from({ length: lineCount }, (_, i) => startNum + i),
|
|
2473
|
+
};
|
|
2299
2474
|
const formatted = formatTextWithMode(text, startNum, shouldAddHashLines, shouldAddLineNumbers);
|
|
2300
2475
|
if (!hashContext || emittedHashlineHeader) return formatted;
|
|
2301
2476
|
emittedHashlineHeader = true;
|
|
@@ -2322,6 +2497,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2322
2497
|
capturedDisplayContent = {
|
|
2323
2498
|
text: lineEntriesToPlainText(entries, BRACKET_CONTEXT_ELLIPSIS),
|
|
2324
2499
|
startLine: firstLine?.kind === "line" ? firstLine.lineNumber : startLineDisplay,
|
|
2500
|
+
lineNumbers: entries.map(entry => (entry.kind === "line" ? entry.lineNumber : null)),
|
|
2325
2501
|
};
|
|
2326
2502
|
const formatted = formatLineEntriesWithMode(entries, shouldAddHashLines, shouldAddLineNumbers);
|
|
2327
2503
|
if (!hashContext || emittedHashlineHeader) return formatted;
|
|
@@ -2928,6 +3104,8 @@ export const readToolRenderer = {
|
|
|
2928
3104
|
status: "complete",
|
|
2929
3105
|
output: warningLines.length > 0 ? warningLines.join("\n") : undefined,
|
|
2930
3106
|
expanded,
|
|
3107
|
+
codeStartLine: details?.displayContent?.startLine,
|
|
3108
|
+
codeLineNumbers: details?.displayContent?.lineNumbers,
|
|
2931
3109
|
width,
|
|
2932
3110
|
},
|
|
2933
3111
|
uiTheme,
|
|
@@ -23,7 +23,7 @@ import { Database } from "bun:sqlite";
|
|
|
23
23
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
24
24
|
import type { FetchImpl } from "@oh-my-pi/pi-ai";
|
|
25
25
|
import { $env, $flag, getAutoQaDbDir, getInstallId, logger, VERSION } from "@oh-my-pi/pi-utils";
|
|
26
|
-
import {
|
|
26
|
+
import { type } from "arktype";
|
|
27
27
|
import type { Settings } from "..";
|
|
28
28
|
import type { ToolSession } from "./index";
|
|
29
29
|
|
|
@@ -31,12 +31,12 @@ function buildReportToolIssueParams(activeBuiltinNames: readonly string[]) {
|
|
|
31
31
|
// Enum gives the model a tight schema; the runtime check in `execute` is the
|
|
32
32
|
// source of truth (handles models that ignore the enum and the empty-list
|
|
33
33
|
// fallback used by call sites that don't know the active set yet).
|
|
34
|
-
const toolSchema = activeBuiltinNames.length > 0 ?
|
|
35
|
-
return
|
|
34
|
+
const toolSchema = activeBuiltinNames.length > 0 ? type.enumerated(...activeBuiltinNames) : type("string");
|
|
35
|
+
return type({
|
|
36
36
|
tool: toolSchema.describe("tool name"),
|
|
37
|
-
report:
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
report: type("string").describe(
|
|
38
|
+
"unexpected behavior; generic, NEVER PII (paths, file contents, identifiers, prompt text)",
|
|
39
|
+
),
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
|
package/src/tools/resolve.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
2
2
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
3
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import {
|
|
5
|
+
import { type } from "arktype";
|
|
6
6
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
7
7
|
import type { Theme } from "../modes/theme/theme";
|
|
8
8
|
import resolveDescription from "../prompts/tools/resolve.md" with { type: "text" };
|
|
@@ -11,13 +11,13 @@ import type { ToolSession } from ".";
|
|
|
11
11
|
import { replaceTabs } from "./render-utils";
|
|
12
12
|
import { ToolError } from "./tool-errors";
|
|
13
13
|
|
|
14
|
-
const resolveSchema =
|
|
15
|
-
action:
|
|
16
|
-
reason:
|
|
17
|
-
extra:
|
|
14
|
+
const resolveSchema = type({
|
|
15
|
+
action: "'apply' | 'discard'",
|
|
16
|
+
reason: type("string").describe("reason for action"),
|
|
17
|
+
"extra?": type("Record<string, unknown>").describe("free-form metadata"),
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
type ResolveParams =
|
|
20
|
+
type ResolveParams = typeof resolveSchema.infer;
|
|
21
21
|
|
|
22
22
|
export interface ResolveToolDetails {
|
|
23
23
|
action: "apply" | "discard";
|
package/src/tools/review.ts
CHANGED
|
@@ -12,7 +12,7 @@ import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
|
12
12
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
13
13
|
import { Container, Text } from "@oh-my-pi/pi-tui";
|
|
14
14
|
import { isRecord } from "@oh-my-pi/pi-utils";
|
|
15
|
-
import {
|
|
15
|
+
import { type } from "arktype";
|
|
16
16
|
import type { Theme, ThemeColor } from "../modes/theme/theme";
|
|
17
17
|
import { subprocessToolRegistry } from "../task/subprocess-tool-registry";
|
|
18
18
|
import type { ReviewFinding } from "../task/types";
|
|
@@ -52,17 +52,15 @@ function getPriorityDisplay(
|
|
|
52
52
|
|
|
53
53
|
// report_finding schema
|
|
54
54
|
// report_finding schema
|
|
55
|
-
const ReportFindingParams =
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
})
|
|
65
|
-
.strict();
|
|
55
|
+
const ReportFindingParams = type({
|
|
56
|
+
title: type("string").describe("prefixed imperative title"),
|
|
57
|
+
body: type("string").describe("problem explanation"),
|
|
58
|
+
priority: type("'P0' | 'P1' | 'P2' | 'P3'").describe("priority 0-3"),
|
|
59
|
+
confidence: type("number >= 0 & number <= 1").describe("confidence score"),
|
|
60
|
+
file_path: type("string").describe("file path"),
|
|
61
|
+
line_start: type("number").describe("start line"),
|
|
62
|
+
line_end: type("number").describe("end line"),
|
|
63
|
+
});
|
|
66
64
|
|
|
67
65
|
interface ReportFindingDetails {
|
|
68
66
|
title: string;
|
|
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
2
2
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
3
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import {
|
|
5
|
+
import { type } from "arktype";
|
|
6
6
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
7
7
|
import type { Theme } from "../modes/theme/theme";
|
|
8
8
|
import searchToolBm25Description from "../prompts/tools/search-tool-bm25.md" with { type: "text" };
|
|
@@ -27,12 +27,12 @@ const COLLAPSED_MATCH_LIMIT = 5;
|
|
|
27
27
|
const MATCH_LABEL_LEN = 72;
|
|
28
28
|
const MATCH_DESCRIPTION_LEN = 96;
|
|
29
29
|
|
|
30
|
-
const searchToolBm25Schema =
|
|
31
|
-
query:
|
|
32
|
-
limit:
|
|
30
|
+
const searchToolBm25Schema = type({
|
|
31
|
+
query: type("string").describe("tool search query"),
|
|
32
|
+
"limit?": type("number>0").describe("max matches"),
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
type SearchToolBm25Params =
|
|
35
|
+
type SearchToolBm25Params = typeof searchToolBm25Schema.infer;
|
|
36
36
|
|
|
37
37
|
interface SearchToolBm25Match {
|
|
38
38
|
name: string;
|
package/src/tools/search.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { type GrepMatch, GrepOutputMode, type GrepResult, grep } from "@oh-my-pi
|
|
|
7
7
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
8
8
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
9
9
|
import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
10
|
-
import {
|
|
10
|
+
import { type } from "arktype";
|
|
11
11
|
import { recordFileSnapshot, recordSeenLinesFromBody } from "../edit/file-snapshot-store";
|
|
12
12
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
13
13
|
import type { LocalProtocolOptions } from "../internal-urls/local-protocol";
|
|
@@ -65,31 +65,24 @@ import {
|
|
|
65
65
|
import { ToolError } from "./tool-errors";
|
|
66
66
|
import { toolResult } from "./tool-result";
|
|
67
67
|
|
|
68
|
-
const
|
|
69
|
-
.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
.nullable()
|
|
87
|
-
.optional()
|
|
88
|
-
.describe("files to skip before collecting results — use to paginate when the prior call hit the file limit"),
|
|
89
|
-
})
|
|
90
|
-
.strict();
|
|
91
|
-
|
|
92
|
-
export type SearchToolInput = z.infer<typeof searchSchema>;
|
|
68
|
+
const searchPathEntry = type("string").describe(
|
|
69
|
+
'file, directory, glob, internal URL, or "<file>:<lines>" selector (e.g. "src/foo.ts:50-100", "src/foo.ts:50+10", "src/foo.ts:50-100,200-300")',
|
|
70
|
+
);
|
|
71
|
+
const searchSchema = type({
|
|
72
|
+
pattern: type("string").describe("regex pattern"),
|
|
73
|
+
"paths?": searchPathEntry
|
|
74
|
+
.or(searchPathEntry.array())
|
|
75
|
+
.describe(
|
|
76
|
+
'file, directory, glob, internal URL, or array of those to search; append `:<lines>` to scope a file to specific line ranges. Omitted or empty -> searches the workspace root (".")',
|
|
77
|
+
),
|
|
78
|
+
"i?": type("boolean").describe("case-insensitive search"),
|
|
79
|
+
"gitignore?": type("boolean").describe("respect gitignore"),
|
|
80
|
+
"skip?": type("number")
|
|
81
|
+
.or("null")
|
|
82
|
+
.describe("files to skip before collecting results — use to paginate when the prior call hit the file limit"),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
export type SearchToolInput = typeof searchSchema.infer;
|
|
93
86
|
export function toPathList(input: string | string[] | undefined): string[] {
|
|
94
87
|
return typeof input === "string" ? [input] : (input ?? []);
|
|
95
88
|
}
|
|
@@ -665,7 +658,7 @@ export interface SearchToolDetails {
|
|
|
665
658
|
missingPaths?: string[];
|
|
666
659
|
}
|
|
667
660
|
|
|
668
|
-
type SearchParams =
|
|
661
|
+
type SearchParams = typeof searchSchema.infer;
|
|
669
662
|
|
|
670
663
|
export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDetails> {
|
|
671
664
|
readonly name = "search";
|
|
@@ -884,7 +877,6 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
884
877
|
multiline: effectiveMultiline,
|
|
885
878
|
hidden: true,
|
|
886
879
|
gitignore: useGitignore,
|
|
887
|
-
cache: false,
|
|
888
880
|
maxCount: INTERNAL_TOTAL_CAP,
|
|
889
881
|
contextBefore: normalizedContextBefore,
|
|
890
882
|
contextAfter: normalizedContextAfter,
|
|
@@ -932,7 +924,6 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
932
924
|
multiline: effectiveMultiline,
|
|
933
925
|
hidden: true,
|
|
934
926
|
gitignore: useGitignore,
|
|
935
|
-
cache: false,
|
|
936
927
|
maxCount: INTERNAL_TOTAL_CAP,
|
|
937
928
|
contextBefore: normalizedContextBefore,
|
|
938
929
|
contextAfter: normalizedContextAfter,
|
package/src/tools/ssh.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
2
2
|
import type { ToolExample } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import {
|
|
5
|
+
import { type } from "arktype";
|
|
6
6
|
import type { SSHHost } from "../capability/ssh";
|
|
7
7
|
import { sshCapability } from "../capability/ssh";
|
|
8
8
|
import { loadCapability } from "../discovery";
|
|
@@ -23,11 +23,11 @@ import { ToolError } from "./tool-errors";
|
|
|
23
23
|
import { toolResult } from "./tool-result";
|
|
24
24
|
import { clampTimeout } from "./tool-timeouts";
|
|
25
25
|
|
|
26
|
-
const sshSchema =
|
|
27
|
-
host:
|
|
28
|
-
command:
|
|
29
|
-
cwd:
|
|
30
|
-
timeout:
|
|
26
|
+
const sshSchema = type({
|
|
27
|
+
host: type("string").describe("ssh host"),
|
|
28
|
+
command: type("string").describe("remote command"),
|
|
29
|
+
"cwd?": type("string").describe("remote working directory"),
|
|
30
|
+
"timeout?": type("number").describe("timeout in seconds"),
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
export interface SSHToolDetails {
|
|
@@ -118,7 +118,7 @@ async function loadHosts(session: ToolSession): Promise<{
|
|
|
118
118
|
return { hostNames, hostsByName };
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
type SshToolParams =
|
|
121
|
+
type SshToolParams = typeof sshSchema.infer;
|
|
122
122
|
|
|
123
123
|
export class SshTool implements AgentTool<typeof sshSchema, SSHToolDetails> {
|
|
124
124
|
readonly name = "ssh";
|
|
@@ -136,7 +136,7 @@ export class SshTool implements AgentTool<typeof sshSchema, SSHToolDetails> {
|
|
|
136
136
|
readonly concurrency = "exclusive";
|
|
137
137
|
readonly strict = true;
|
|
138
138
|
|
|
139
|
-
readonly examples: readonly ToolExample<
|
|
139
|
+
readonly examples: readonly ToolExample<SshToolParams>[] = [
|
|
140
140
|
{
|
|
141
141
|
caption: "List files: Linux (on server1 (10.0.0.1) | linux/bash)",
|
|
142
142
|
call: { host: "server1", command: "ls -la /home/user" },
|
package/src/tools/todo.ts
CHANGED
|
@@ -3,8 +3,8 @@ import type { ToolExample } from "@oh-my-pi/pi-ai";
|
|
|
3
3
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
5
5
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
6
|
+
import { type } from "arktype";
|
|
6
7
|
import chalk from "chalk";
|
|
7
|
-
import { z } from "zod/v4";
|
|
8
8
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
9
9
|
import type { Theme } from "../modes/theme/theme";
|
|
10
10
|
import todoDescription from "../prompts/tools/todo.md" with { type: "text" };
|
|
@@ -44,30 +44,27 @@ export interface TodoToolDetails {
|
|
|
44
44
|
// Schema
|
|
45
45
|
// =============================================================================
|
|
46
46
|
|
|
47
|
-
const TodoOp =
|
|
48
|
-
.enum(["init", "start", "done", "rm", "drop", "append", "view"] as const)
|
|
49
|
-
.describe("operation to apply");
|
|
47
|
+
const TodoOp = type('"init" | "start" | "done" | "rm" | "drop" | "append" | "view"').describe("operation to apply");
|
|
50
48
|
|
|
51
|
-
const InitListEntry =
|
|
52
|
-
phase:
|
|
53
|
-
items:
|
|
49
|
+
const InitListEntry = type({
|
|
50
|
+
phase: type("string").describe("phase name"),
|
|
51
|
+
items: type("string").describe("task content").array().atLeastLength(1).describe("tasks for this phase"),
|
|
54
52
|
});
|
|
55
53
|
|
|
56
|
-
const TodoOpEntry =
|
|
54
|
+
const TodoOpEntry = type({
|
|
57
55
|
op: TodoOp,
|
|
58
|
-
list:
|
|
59
|
-
task:
|
|
60
|
-
phase:
|
|
61
|
-
items:
|
|
56
|
+
"list?": InitListEntry.array().describe("phased task list (init)"),
|
|
57
|
+
"task?": type("string").describe("task content"),
|
|
58
|
+
"phase?": type("string").describe("phase name"),
|
|
59
|
+
"items?": type("string").describe("task content").array().atLeastLength(1).describe("tasks to append"),
|
|
62
60
|
});
|
|
63
61
|
|
|
64
|
-
const todoSchema =
|
|
65
|
-
.
|
|
66
|
-
|
|
67
|
-
})
|
|
68
|
-
.describe("apply ordered todo operations");
|
|
62
|
+
const todoSchema = type({
|
|
63
|
+
ops: TodoOpEntry.array().atLeastLength(1).describe("ordered todo operations"),
|
|
64
|
+
}).describe("apply ordered todo operations");
|
|
69
65
|
|
|
70
|
-
type TodoParams =
|
|
66
|
+
type TodoParams = TodoSchema;
|
|
67
|
+
type TodoSchema = typeof todoSchema.infer;
|
|
71
68
|
type TodoOpEntryValue = TodoParams["ops"][number];
|
|
72
69
|
|
|
73
70
|
// =============================================================================
|
|
@@ -565,7 +562,7 @@ export class TodoTool implements AgentTool<typeof todoSchema, TodoToolDetails> {
|
|
|
565
562
|
readonly concurrency = "exclusive";
|
|
566
563
|
readonly strict = true;
|
|
567
564
|
|
|
568
|
-
readonly examples: readonly ToolExample<
|
|
565
|
+
readonly examples: readonly ToolExample<typeof todoSchema.infer>[] = [
|
|
569
566
|
{
|
|
570
567
|
caption: "Initial setup (multi-phase)",
|
|
571
568
|
call: {
|