@mrclrchtr/supi-web 5.0.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/docs.ts +5 -328
- package/src/tool/tool-specs.ts +74 -81
- package/src/tool/web_docs_fetch/execute.ts +41 -0
- package/src/tool/web_docs_fetch/guidance.ts +9 -0
- package/src/tool/web_docs_fetch/register.ts +16 -0
- package/src/tool/web_docs_fetch/render.ts +70 -0
- package/src/tool/web_docs_fetch/result.ts +31 -0
- package/src/tool/web_docs_fetch/spec.ts +32 -0
- package/src/tool/web_docs_search/execute.ts +50 -0
- package/src/tool/web_docs_search/guidance.ts +7 -0
- package/src/tool/web_docs_search/register.ts +16 -0
- package/src/tool/web_docs_search/render.ts +75 -0
- package/src/tool/web_docs_search/result.ts +103 -0
- package/src/tool/web_docs_search/spec.ts +28 -0
- package/src/tool/web_fetch_md/execute.ts +69 -0
- package/src/tool/web_fetch_md/guidance.ts +32 -0
- package/src/tool/web_fetch_md/input.ts +39 -0
- package/src/tool/web_fetch_md/register.ts +17 -0
- package/src/tool/web_fetch_md/render.ts +74 -0
- package/src/tool/web_fetch_md/result.ts +52 -0
- package/src/tool/web_fetch_md/spec.ts +21 -0
- package/src/web.ts +3 -178
- package/src/tool/guidance.ts +0 -34
- /package/src/tool/{output.ts → result.ts} +0 -0
package/src/web.ts
CHANGED
|
@@ -2,184 +2,9 @@
|
|
|
2
2
|
* SuPi Web extension entry point — registers the `web_fetch_md` tool with pi.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type {
|
|
6
|
-
|
|
7
|
-
AgentToolUpdateCallback,
|
|
8
|
-
ExtensionAPI,
|
|
9
|
-
ExtensionContext,
|
|
10
|
-
TruncationResult,
|
|
11
|
-
} from "@earendil-works/pi-coding-agent";
|
|
12
|
-
import { htmlToMarkdown, wrapAsCodeBlock } from "./convert.ts";
|
|
13
|
-
import { fetchWithNegotiation, isValidHttpUrl } from "./fetch.ts";
|
|
14
|
-
import { writeTempFile } from "./temp-file.ts";
|
|
15
|
-
import { getWebToolPromptSurface } from "./tool/guidance.ts";
|
|
16
|
-
import { limitModelVisibleOutput } from "./tool/output.ts";
|
|
17
|
-
import { renderCollapsibleTextResult, renderToolCall } from "./tool/render.ts";
|
|
18
|
-
import {
|
|
19
|
-
getWebToolSpec,
|
|
20
|
-
WEB_FETCH_INLINE_MAX_CHARS,
|
|
21
|
-
WEB_FETCH_MD_TOOL_NAME,
|
|
22
|
-
type WebFetchMdInput,
|
|
23
|
-
type WebFetchOutputMode,
|
|
24
|
-
} from "./tool/tool-specs.ts";
|
|
25
|
-
|
|
26
|
-
interface WebFetchDetails extends Record<string, unknown> {
|
|
27
|
-
chars: number;
|
|
28
|
-
lines: number;
|
|
29
|
-
url: string;
|
|
30
|
-
outputMode: WebFetchOutputMode;
|
|
31
|
-
filePath?: string;
|
|
32
|
-
truncation?: TruncationResult;
|
|
33
|
-
fullOutputPath?: string;
|
|
34
|
-
}
|
|
5
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { registerWebFetchMdTool } from "./tool/web_fetch_md/register.ts";
|
|
35
7
|
|
|
36
8
|
export default function webExtension(pi: ExtensionAPI): void {
|
|
37
|
-
|
|
38
|
-
const surface = getWebToolPromptSurface(WEB_FETCH_MD_TOOL_NAME);
|
|
39
|
-
|
|
40
|
-
pi.registerTool({
|
|
41
|
-
name: spec.name,
|
|
42
|
-
label: spec.label,
|
|
43
|
-
description: surface.description,
|
|
44
|
-
promptSnippet: surface.promptSnippet,
|
|
45
|
-
promptGuidelines: surface.promptGuidelines,
|
|
46
|
-
parameters: spec.parameters,
|
|
47
|
-
execute: runWebFetch,
|
|
48
|
-
renderCall(args, theme) {
|
|
49
|
-
const input = (args ?? {}) as WebFetchMdInput;
|
|
50
|
-
const url = typeof input.url === "string" ? input.url : "";
|
|
51
|
-
const outputMode = typeof input.output_mode === "string" ? input.output_mode : undefined;
|
|
52
|
-
return renderToolCall(spec.name, url, theme, outputMode);
|
|
53
|
-
},
|
|
54
|
-
renderResult(result, { expanded, isPartial }, theme) {
|
|
55
|
-
if (isPartial) {
|
|
56
|
-
return renderCollapsibleTextResult({
|
|
57
|
-
summary: theme.fg("warning", "Fetching web content..."),
|
|
58
|
-
expanded,
|
|
59
|
-
theme,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const details = result.details as WebFetchDetails | undefined;
|
|
64
|
-
const summary = buildWebFetchSummary(details, theme);
|
|
65
|
-
const content = result.content.find((item) => item.type === "text");
|
|
66
|
-
const body = details?.filePath
|
|
67
|
-
? undefined
|
|
68
|
-
: content?.type === "text"
|
|
69
|
-
? content.text
|
|
70
|
-
: undefined;
|
|
71
|
-
|
|
72
|
-
return renderCollapsibleTextResult({
|
|
73
|
-
summary,
|
|
74
|
-
body,
|
|
75
|
-
expanded,
|
|
76
|
-
theme,
|
|
77
|
-
fullOutputPath: details?.fullOutputPath,
|
|
78
|
-
});
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// biome-ignore lint/complexity/useMaxParams: pi ToolDefinition.execute signature
|
|
84
|
-
async function runWebFetch(
|
|
85
|
-
_toolCallId: string,
|
|
86
|
-
params: unknown,
|
|
87
|
-
signal: AbortSignal | undefined,
|
|
88
|
-
onUpdate: AgentToolUpdateCallback<Record<string, unknown>> | undefined,
|
|
89
|
-
_ctx: ExtensionContext,
|
|
90
|
-
): Promise<AgentToolResult<WebFetchDetails>> {
|
|
91
|
-
const input = (params ?? {}) as WebFetchMdInput;
|
|
92
|
-
const url = String(input.url || "").trim();
|
|
93
|
-
if (!isValidHttpUrl(url)) {
|
|
94
|
-
throw new Error(`URL must be http(s): ${url}`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const outputMode = input.output_mode ?? "auto";
|
|
98
|
-
const absLinks = input.abs_links ?? true;
|
|
99
|
-
const timeoutMs = typeof input.timeout_ms === "number" ? input.timeout_ms : 30_000;
|
|
100
|
-
|
|
101
|
-
onUpdate?.({
|
|
102
|
-
content: [{ type: "text", text: `Fetching ${url}...` }],
|
|
103
|
-
details: { url, outputMode },
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const result = await fetchWithNegotiation(url, { timeoutMs, signal });
|
|
107
|
-
const markdown = await resolveMarkdown(result, absLinks);
|
|
108
|
-
const lines = markdown.split("\n").length;
|
|
109
|
-
const chars = markdown.length;
|
|
110
|
-
const details: WebFetchDetails = { chars, lines, url: result.url, outputMode };
|
|
111
|
-
|
|
112
|
-
if (shouldReturnFile(outputMode, chars)) {
|
|
113
|
-
const filePath = await writeTempFile(markdown, "web-fetch-md", ".md");
|
|
114
|
-
return {
|
|
115
|
-
content: [
|
|
116
|
-
{
|
|
117
|
-
type: "text",
|
|
118
|
-
text: `Content written to ${filePath} (${chars.toLocaleString()} chars, ${lines.toLocaleString()} lines). Use the read tool to access it.`,
|
|
119
|
-
},
|
|
120
|
-
],
|
|
121
|
-
details: { ...details, filePath },
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const output = await limitModelVisibleOutput(markdown, {
|
|
126
|
-
tempPrefix: "web-fetch-md",
|
|
127
|
-
suffix: ".md",
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
content: [{ type: "text", text: output.text }],
|
|
132
|
-
details: {
|
|
133
|
-
...details,
|
|
134
|
-
truncation: output.truncation,
|
|
135
|
-
fullOutputPath: output.fullOutputPath,
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function shouldReturnFile(outputMode: WebFetchOutputMode, chars: number): boolean {
|
|
141
|
-
return outputMode === "file" || (outputMode === "auto" && chars > WEB_FETCH_INLINE_MAX_CHARS);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
async function resolveMarkdown(
|
|
145
|
-
result: { isMarkdown: boolean; isPlainText: boolean; text: string; url: string },
|
|
146
|
-
absLinks: boolean,
|
|
147
|
-
): Promise<string> {
|
|
148
|
-
if (result.isMarkdown) return result.text;
|
|
149
|
-
if (result.isPlainText) return wrapAsCodeBlock(result.text, result.url);
|
|
150
|
-
return htmlToMarkdown(result.text, result.url, { absLinks });
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function buildWebFetchSummary(
|
|
154
|
-
details: WebFetchDetails | undefined,
|
|
155
|
-
theme: { fg: (color: "success" | "warning" | "dim", text: string) => string },
|
|
156
|
-
): string {
|
|
157
|
-
if (!details) {
|
|
158
|
-
return theme.fg("success", "Fetched web content");
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (details.filePath) {
|
|
162
|
-
return [
|
|
163
|
-
theme.fg("success", "Saved Markdown to "),
|
|
164
|
-
theme.fg("dim", details.filePath),
|
|
165
|
-
theme.fg(
|
|
166
|
-
"dim",
|
|
167
|
-
` (${details.chars.toLocaleString()} chars, ${details.lines.toLocaleString()} lines)`,
|
|
168
|
-
),
|
|
169
|
-
].join("");
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
let summary = [
|
|
173
|
-
theme.fg("success", "Fetched Markdown"),
|
|
174
|
-
theme.fg(
|
|
175
|
-
"dim",
|
|
176
|
-
` (${details.chars.toLocaleString()} chars, ${details.lines.toLocaleString()} lines)`,
|
|
177
|
-
),
|
|
178
|
-
].join("");
|
|
179
|
-
|
|
180
|
-
if (details.truncation?.truncated) {
|
|
181
|
-
summary += theme.fg("warning", " [truncated]");
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return summary;
|
|
9
|
+
registerWebFetchMdTool(pi);
|
|
185
10
|
}
|
package/src/tool/guidance.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { getWebToolSpec, WEB_FETCH_MD_TOOL_NAME, type WebToolName } from "./tool-specs.ts";
|
|
3
|
-
|
|
4
|
-
/** Prompt metadata sent to pi for a single web tool. */
|
|
5
|
-
export interface WebToolPromptSurface {
|
|
6
|
-
description: string;
|
|
7
|
-
promptSnippet: string;
|
|
8
|
-
promptGuidelines: string[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Build prompt metadata from the shared tool specs, adding runtime-specific guidance when useful. */
|
|
12
|
-
export function getWebToolPromptSurface(name: WebToolName): WebToolPromptSurface {
|
|
13
|
-
const spec = getWebToolSpec(name);
|
|
14
|
-
const promptGuidelines = [...spec.promptGuidelines];
|
|
15
|
-
|
|
16
|
-
if (name === WEB_FETCH_MD_TOOL_NAME && isGhAvailable()) {
|
|
17
|
-
promptGuidelines.push("Use `gh` CLI instead of web_fetch_md for GitHub URLs.");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
description: spec.description,
|
|
22
|
-
promptSnippet: spec.promptSnippet,
|
|
23
|
-
promptGuidelines,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function isGhAvailable(): boolean {
|
|
28
|
-
try {
|
|
29
|
-
const result = spawnSync("gh", ["--version"], { stdio: "ignore" });
|
|
30
|
-
return result.status === 0;
|
|
31
|
-
} catch {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
File without changes
|