pi-gortex 0.2.0 → 0.4.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/README.md +13 -1
- package/docs/architecture.md +86 -14
- package/package.json +5 -4
- package/src/config.ts +15 -4
- package/src/diff.ts +404 -0
- package/src/index.ts +149 -49
- package/src/mcp-client.ts +24 -10
- package/src/native.ts +141 -0
- package/src/render.ts +65 -8
- package/src/runtime.ts +4 -0
- package/src/state.ts +10 -1
- package/src/tools.ts +48 -8
package/src/tools.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
// Tool registration: each tool the bridge lists becomes a native Pi tool whose
|
|
2
2
|
// execute() forwards to tools/call.
|
|
3
3
|
|
|
4
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
|
|
6
|
+
import { candidatePaths, editDetails, isFileEdit, withFileDiffs } from "./diff.ts";
|
|
7
|
+
import type { FileDiff } from "./diff.ts";
|
|
5
8
|
|
|
6
9
|
import type { ToolDescriptor } from "./mcp-client.ts";
|
|
7
10
|
import { callRenderer, readCallRenderer, renderResult, resultText } from "./render.ts";
|
|
8
11
|
import type { RenderCall, RenderResult } from "./render.ts";
|
|
9
|
-
import { getClient, gortexToolNames } from "./state.ts";
|
|
12
|
+
import { getClient, gortexToolNames, withdrawnToolNames } from "./state.ts";
|
|
10
13
|
import { toolNameMap } from "./hook.ts";
|
|
11
14
|
|
|
12
15
|
// Pi's reserved built-in names (mirrors toolNameMap's keys). A bridge tool that
|
|
@@ -44,7 +47,13 @@ interface BridgeToolDefinition {
|
|
|
44
47
|
label: string;
|
|
45
48
|
description: string;
|
|
46
49
|
parameters: unknown;
|
|
47
|
-
execute(
|
|
50
|
+
execute(
|
|
51
|
+
toolCallId: string,
|
|
52
|
+
params: Record<string, unknown>,
|
|
53
|
+
signal: AbortSignal | undefined,
|
|
54
|
+
onUpdate: unknown,
|
|
55
|
+
ctx: ExtensionContext | undefined,
|
|
56
|
+
): Promise<unknown>;
|
|
48
57
|
renderCall: RenderCall;
|
|
49
58
|
renderResult: RenderResult;
|
|
50
59
|
}
|
|
@@ -86,12 +95,20 @@ export function registerOneTool(pi: ExtensionAPI, desc: ToolDescriptor): void {
|
|
|
86
95
|
label: name,
|
|
87
96
|
description: piAliasedDescription(name, piAliasName(name), (desc.description || name).trim()),
|
|
88
97
|
parameters,
|
|
89
|
-
async execute(_id
|
|
98
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
90
99
|
const client = getClient();
|
|
91
100
|
if (!client) throw new Error(`gortex ${name}: MCP bridge is not connected`);
|
|
101
|
+
const args = params ?? {};
|
|
102
|
+
const callTool = () => client.callTool(name, args);
|
|
92
103
|
let result: unknown;
|
|
104
|
+
let diffs: FileDiff[] = [];
|
|
93
105
|
try {
|
|
94
|
-
|
|
106
|
+
if (ctx && isFileEdit(name, args)) {
|
|
107
|
+
const paths = candidatePaths(args, ctx.cwd);
|
|
108
|
+
({ result, diffs } = await withFileDiffs(paths, ctx.cwd, callTool, (r) => !(r as { isError?: boolean })?.isError));
|
|
109
|
+
} else {
|
|
110
|
+
result = await callTool();
|
|
111
|
+
}
|
|
95
112
|
} catch (err) {
|
|
96
113
|
throw new Error(`gortex ${name} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
97
114
|
}
|
|
@@ -103,7 +120,7 @@ export function registerOneTool(pi: ExtensionAPI, desc: ToolDescriptor): void {
|
|
|
103
120
|
}
|
|
104
121
|
const text = resultText(result);
|
|
105
122
|
if ((result as { isError?: boolean })?.isError) throw new Error(text || `gortex ${name} failed`);
|
|
106
|
-
return { content: [{ type: "text", text }], details:
|
|
123
|
+
return { content: [{ type: "text", text }], details: editDetails(diffs) };
|
|
107
124
|
},
|
|
108
125
|
renderCall: (name === "read" ? readCallRenderer : callRenderer)(piAliasName(name)),
|
|
109
126
|
renderResult,
|
|
@@ -115,13 +132,36 @@ export function registerOneTool(pi: ExtensionAPI, desc: ToolDescriptor): void {
|
|
|
115
132
|
export async function registerGortexTools(pi: ExtensionAPI): Promise<void> {
|
|
116
133
|
const client = getClient();
|
|
117
134
|
if (!client) return;
|
|
135
|
+
const listed = new Set<string>();
|
|
118
136
|
for (const desc of await client.listTools()) {
|
|
119
|
-
if (!desc) continue;
|
|
137
|
+
if (!desc?.name) continue;
|
|
120
138
|
registerOneTool(pi, desc);
|
|
139
|
+
listed.add(piAliasName(desc.name));
|
|
140
|
+
}
|
|
141
|
+
matchActiveTools(pi, listed);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Keeps the tools offered to the model in step with the daemon's list. */
|
|
145
|
+
function matchActiveTools(pi: ExtensionAPI, listed: Set<string>): void {
|
|
146
|
+
const dropped = [...gortexToolNames].filter((name) => !listed.has(name));
|
|
147
|
+
const relisted = [...withdrawnToolNames].filter((name) => listed.has(name));
|
|
148
|
+
if (dropped.length === 0 && relisted.length === 0) return;
|
|
149
|
+
for (const name of dropped) {
|
|
150
|
+
gortexToolNames.delete(name);
|
|
151
|
+
withdrawnToolNames.add(name);
|
|
152
|
+
}
|
|
153
|
+
for (const name of relisted) withdrawnToolNames.delete(name);
|
|
154
|
+
try {
|
|
155
|
+
const active = new Set(pi.getActiveTools());
|
|
156
|
+
for (const name of dropped) active.delete(name);
|
|
157
|
+
for (const name of relisted) active.add(name);
|
|
158
|
+
pi.setActiveTools([...active]);
|
|
159
|
+
} catch {
|
|
160
|
+
// an invalidated instance keeps its last tool set.
|
|
121
161
|
}
|
|
122
162
|
}
|
|
123
163
|
|
|
124
|
-
/** Re-fetches the tool list after a list_changed and
|
|
164
|
+
/** Re-fetches the tool list after a list_changed and applies the difference. */
|
|
125
165
|
export async function syncTools(pi: ExtensionAPI): Promise<void> {
|
|
126
166
|
try {
|
|
127
167
|
await registerGortexTools(pi);
|