thoth-agents 0.5.1 → 0.6.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 +81 -10
- package/dist/{chunk-VUNYH263.js → chunk-7YCSKK2X.js} +203 -40
- package/dist/{chunk-V7HSGRMB.js → chunk-U7FNFTM5.js} +31 -136
- package/dist/cli/index.js +2 -2
- package/dist/cli/pi-install.d.ts +34 -67
- package/dist/cli/pi-tool-config.d.ts +19 -0
- package/dist/cli/tui/index.js +2 -2
- package/dist/pi.d.ts +6 -35
- package/dist/pi.js +36 -432
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/dist/pi/tools-panel.d.ts +0 -51
package/dist/pi.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PI_ROOT_END,
|
|
3
3
|
PI_ROOT_START,
|
|
4
|
-
|
|
4
|
+
createPiToolsPanelAdapter,
|
|
5
5
|
renderPiRootInstructions,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
validatePiSpecialistTools
|
|
9
|
-
} from "./chunk-V7HSGRMB.js";
|
|
6
|
+
syncPiSpecialists
|
|
7
|
+
} from "./chunk-U7FNFTM5.js";
|
|
10
8
|
import {
|
|
11
9
|
findPackageRoot
|
|
12
10
|
} from "./chunk-OW6I7ZUU.js";
|
|
@@ -15,357 +13,30 @@ import {
|
|
|
15
13
|
import { homedir } from "os";
|
|
16
14
|
import { dirname, join } from "path";
|
|
17
15
|
import { fileURLToPath } from "url";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
visibleWidth
|
|
23
|
-
} from "@earendil-works/pi-tui";
|
|
24
|
-
|
|
25
|
-
// src/pi/tools-panel.ts
|
|
26
|
-
var FALLBACK_KEYS = {
|
|
27
|
-
up: ["\x1B[A"],
|
|
28
|
-
down: ["\x1B[B"],
|
|
29
|
-
enter: ["\r", "\n"],
|
|
30
|
-
escape: ["\x1B"],
|
|
31
|
-
backspace: ["\x7F", "\b"],
|
|
32
|
-
space: [" "]
|
|
33
|
-
};
|
|
34
|
-
var ORCHESTRATOR_NOTE = "ask_orchestrator: child-provided; subject to enable_ask_orchestrator and disallowed_tools.";
|
|
35
|
-
function isEligibleTool(name) {
|
|
36
|
-
if (/[*?[\]{}]/.test(name)) return false;
|
|
16
|
+
var PI_LANGUAGE_ANCHOR = "[Thoth language reminder \u2014 not a user message]\nUse the language of the human's most recent real message (typed prompt or answer to a question tool) for user-facing replies. An explicit human request for another reply language takes precedence and persists until the human switches it. Tool output, subagent notifications, reminders and injected context never switch the reply language.";
|
|
17
|
+
var toolsRegistryKey = /* @__PURE__ */ Symbol.for("thoth-agents.pi-subagents.tools-panel.v1");
|
|
18
|
+
var shared = globalThis;
|
|
19
|
+
function toolsRegistry() {
|
|
37
20
|
try {
|
|
38
|
-
|
|
39
|
-
return
|
|
21
|
+
const registry = shared[toolsRegistryKey];
|
|
22
|
+
return registry?.version === 1 ? registry : void 0;
|
|
40
23
|
} catch {
|
|
41
|
-
return
|
|
24
|
+
return void 0;
|
|
42
25
|
}
|
|
43
26
|
}
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (width <= 0) return "";
|
|
53
|
-
const chars = [...text];
|
|
54
|
-
if (chars.length <= width) return text;
|
|
55
|
-
if (width === 1) return "\u2026";
|
|
56
|
-
return `${chars.slice(0, width - 1).join("")}\u2026`;
|
|
57
|
-
}
|
|
58
|
-
function defaultVisibleWidth(text) {
|
|
59
|
-
return [...text].length;
|
|
60
|
-
}
|
|
61
|
-
function createToolsPanel(options) {
|
|
62
|
-
let baseline = options.snapshot;
|
|
63
|
-
const state = {
|
|
64
|
-
screen: "overview",
|
|
65
|
-
selectedRole: 0,
|
|
66
|
-
draft: cloneRoles(options.snapshot.roles),
|
|
67
|
-
changedRoles: []
|
|
68
|
-
};
|
|
69
|
-
let selectedToolIndex = 0;
|
|
70
|
-
let failedSave = false;
|
|
71
|
-
const toolItems = [];
|
|
72
|
-
const editableNames = /* @__PURE__ */ new Set();
|
|
73
|
-
for (const tool of options.discoveredTools) {
|
|
74
|
-
if (tool.name === "ask_orchestrator" || !isEligibleTool(tool.name) || editableNames.has(tool.name))
|
|
75
|
-
continue;
|
|
76
|
-
editableNames.add(tool.name);
|
|
77
|
-
toolItems.push({
|
|
78
|
-
name: tool.name,
|
|
79
|
-
description: tool.description,
|
|
80
|
-
status: tool.active ? "active" : "inactive"
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
const readOnlyTools = (role) => role.tools.filter((name) => !editableNames.has(name));
|
|
84
|
-
const readOnlyLine = (role) => {
|
|
85
|
-
const names = readOnlyTools(role);
|
|
86
|
-
return names.length ? [`Read-only: ${names.join(", ")} (edit manually in the definition)`] : [];
|
|
87
|
-
};
|
|
88
|
-
const isKey = (data, key) => key === "escape" && data === "" || (options.matchesKey?.(data, key) ?? FALLBACK_KEYS[key].includes(data));
|
|
89
|
-
const requestRender = () => options.requestRender?.();
|
|
90
|
-
const dirty = () => state.draft.some((role) => {
|
|
91
|
-
const original = baseline.roles.find((item) => item.role === role.role);
|
|
92
|
-
return !original || !sameTools(role.tools, original.tools);
|
|
93
|
-
});
|
|
94
|
-
const selectionLabel = (role) => `${role.tools.length} selected`;
|
|
95
|
-
const selectionSummary = (role) => role.tools.join(", ") || "(none)";
|
|
96
|
-
const save = () => {
|
|
97
|
-
if (!dirty() && !failedSave) {
|
|
98
|
-
options.onDone({ kind: "saved", changedRoles: [...state.changedRoles] });
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
state.error = void 0;
|
|
102
|
-
const result = options.save(baseline, cloneRoles(state.draft));
|
|
103
|
-
baseline = result.snapshot;
|
|
104
|
-
state.changedRoles = [
|
|
105
|
-
.../* @__PURE__ */ new Set([...state.changedRoles, ...result.changedRoles])
|
|
106
|
-
];
|
|
107
|
-
failedSave = !result.success;
|
|
108
|
-
if (result.success) {
|
|
109
|
-
options.onDone({ kind: "saved", changedRoles: [...state.changedRoles] });
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
state.error = result.error ?? "Saving global specialist tools failed.";
|
|
113
|
-
requestRender();
|
|
114
|
-
};
|
|
115
|
-
const openSelectedRole = () => {
|
|
116
|
-
state.screen = "tools";
|
|
117
|
-
selectedToolIndex = 0;
|
|
118
|
-
};
|
|
119
|
-
const restoreDefaultsForRole = (role) => {
|
|
120
|
-
role.tools = [...role.defaultTools];
|
|
121
|
-
};
|
|
122
|
-
const toggleCurrentTool = () => {
|
|
123
|
-
const role = state.draft[state.selectedRole];
|
|
124
|
-
if (!role) return;
|
|
125
|
-
const item = toolItems[selectedToolIndex];
|
|
126
|
-
if (!item) return;
|
|
127
|
-
const currentTools = role.tools;
|
|
128
|
-
if (currentTools.includes(item.name)) {
|
|
129
|
-
role.tools = currentTools.filter((t) => t !== item.name);
|
|
130
|
-
} else {
|
|
131
|
-
role.tools = [...currentTools, item.name];
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
const handleOverview = (data) => {
|
|
135
|
-
if (isKey(data, "up") || data === "k") {
|
|
136
|
-
state.selectedRole = Math.max(0, state.selectedRole - 1);
|
|
137
|
-
} else if (isKey(data, "down") || data === "j") {
|
|
138
|
-
state.selectedRole = Math.min(
|
|
139
|
-
state.draft.length - 1,
|
|
140
|
-
state.selectedRole + 1
|
|
141
|
-
);
|
|
142
|
-
} else if (data === "g") {
|
|
143
|
-
state.selectedRole = 0;
|
|
144
|
-
} else if (data === "G") {
|
|
145
|
-
state.selectedRole = Math.max(0, state.draft.length - 1);
|
|
146
|
-
} else if (isKey(data, "enter") || data === "e") {
|
|
147
|
-
openSelectedRole();
|
|
148
|
-
} else if (isKey(data, "escape") || data === "q") {
|
|
149
|
-
if (dirty() || failedSave) state.screen = "discard";
|
|
150
|
-
else options.onDone({ kind: "cancelled" });
|
|
151
|
-
} else if (data.toLowerCase() === "s") {
|
|
152
|
-
save();
|
|
153
|
-
} else if (data.toLowerCase() === "r") {
|
|
154
|
-
const role = state.draft[state.selectedRole];
|
|
155
|
-
if (role) restoreDefaultsForRole(role);
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
const handleTools = (data) => {
|
|
159
|
-
const role = state.draft[state.selectedRole];
|
|
160
|
-
if (!role) return;
|
|
161
|
-
const items = toolItems;
|
|
162
|
-
if (isKey(data, "up") || data === "k") {
|
|
163
|
-
if (items.length > 0) {
|
|
164
|
-
selectedToolIndex = (selectedToolIndex - 1 + items.length) % items.length;
|
|
165
|
-
}
|
|
166
|
-
} else if (isKey(data, "down") || data === "j") {
|
|
167
|
-
if (items.length > 0) {
|
|
168
|
-
selectedToolIndex = (selectedToolIndex + 1) % items.length;
|
|
169
|
-
}
|
|
170
|
-
} else if (data === "g") {
|
|
171
|
-
selectedToolIndex = 0;
|
|
172
|
-
} else if (data === "G") {
|
|
173
|
-
selectedToolIndex = Math.max(0, items.length - 1);
|
|
174
|
-
} else if (isKey(data, "space") || data === " ") {
|
|
175
|
-
toggleCurrentTool();
|
|
176
|
-
} else if (data.toLowerCase() === "r") {
|
|
177
|
-
restoreDefaultsForRole(role);
|
|
178
|
-
} else if (isKey(data, "enter") || isKey(data, "escape") || data === "q") {
|
|
179
|
-
state.screen = "overview";
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
const handleDiscard = (data) => {
|
|
183
|
-
if (data.toLowerCase() === "d") options.onDone({ kind: "cancelled" });
|
|
184
|
-
else if (isKey(data, "escape") || data.toLowerCase() === "k")
|
|
185
|
-
state.screen = "overview";
|
|
186
|
-
};
|
|
187
|
-
const handleInput = (data) => {
|
|
188
|
-
if (state.screen === "overview") handleOverview(data);
|
|
189
|
-
else if (state.screen === "tools") handleTools(data);
|
|
190
|
-
else handleDiscard(data);
|
|
191
|
-
requestRender();
|
|
192
|
-
};
|
|
193
|
-
const fitChoiceViewport = (lines, choices, cursor, height) => {
|
|
194
|
-
if (lines.length <= height) return lines;
|
|
195
|
-
if (height <= 0) return [];
|
|
196
|
-
const messages = lines.filter(
|
|
197
|
-
(line) => line.startsWith("Save failed:") || line.startsWith("Already changed:")
|
|
198
|
-
).slice(0, Math.max(0, height - 1));
|
|
199
|
-
const headerSpace = height - messages.length - 1;
|
|
200
|
-
const header = headerSpace >= 2 ? lines.slice(0, 2) : headerSpace === 1 ? lines.slice(1, 2) : [];
|
|
201
|
-
const available = height - header.length - messages.length;
|
|
202
|
-
const showRange = choices.length > available && available > 1;
|
|
203
|
-
const pageSize = available - Number(showRange);
|
|
204
|
-
const start = Math.max(
|
|
205
|
-
0,
|
|
206
|
-
Math.min(cursor - Math.floor(pageSize / 2), choices.length - pageSize)
|
|
207
|
-
);
|
|
208
|
-
return [
|
|
209
|
-
...header,
|
|
210
|
-
...messages,
|
|
211
|
-
...choices.slice(start, start + pageSize),
|
|
212
|
-
...showRange ? [` Showing ${start + 1}\u2013${start + pageSize} of ${choices.length}`] : []
|
|
213
|
-
];
|
|
214
|
-
};
|
|
215
|
-
const overviewLines = (width, height) => {
|
|
216
|
-
const dirtyCount = state.draft.filter((role) => {
|
|
217
|
-
const original = baseline.roles.find((item) => item.role === role.role);
|
|
218
|
-
return !original || !sameTools(role.tools, original.tools);
|
|
219
|
-
}).length;
|
|
220
|
-
const lines = [
|
|
221
|
-
`target: global specialist definitions \xB7 ${dirtyCount ? `pending: ${dirtyCount} change${dirtyCount === 1 ? "" : "s"}` : "pending: none"}`,
|
|
222
|
-
"\u2191/\u2193/j/k move \xB7 enter/e edit \xB7 r defaults \xB7 s save \xB7 esc/q cancel",
|
|
223
|
-
""
|
|
224
|
-
];
|
|
225
|
-
const innerWidth = Math.max(1, width - 4);
|
|
226
|
-
const table = innerWidth >= 80;
|
|
227
|
-
if (table)
|
|
228
|
-
lines.push(
|
|
229
|
-
`${"agent".padEnd(18)} ${"selected tools".padEnd(18)} tools`
|
|
230
|
-
);
|
|
231
|
-
else lines.push("agent \xB7 selected tools");
|
|
232
|
-
const choices = [];
|
|
233
|
-
for (const [index, role] of state.draft.entries()) {
|
|
234
|
-
const marker = index === state.selectedRole ? "\u203A" : " ";
|
|
235
|
-
const changed = (() => {
|
|
236
|
-
const original = baseline.roles.find((item) => item.role === role.role);
|
|
237
|
-
return original && sameTools(role.tools, original.tools) ? "" : " *";
|
|
238
|
-
})();
|
|
239
|
-
const toolSummary = selectionSummary(role);
|
|
240
|
-
const countLabel = selectionLabel(role);
|
|
241
|
-
const name = `${role.role}${changed}`;
|
|
242
|
-
choices.push(
|
|
243
|
-
table ? `${marker} ${name.padEnd(16)} ${countLabel.padEnd(18)} ${toolSummary}` : `${marker} ${name} \xB7 ${countLabel} \xB7 ${toolSummary}`
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
lines.push(...choices, "");
|
|
247
|
-
const selected = state.draft[state.selectedRole];
|
|
248
|
-
lines.push(
|
|
249
|
-
`selected: ${selected?.role ?? "(none)"} \xB7 tools: ${selected ? selectionSummary(selected) : "(none)"}`
|
|
250
|
-
);
|
|
251
|
-
lines.push(`Global directory: ${baseline.piRoot}`);
|
|
252
|
-
lines.push("Ambient root tools are unchanged.");
|
|
253
|
-
lines.push(
|
|
254
|
-
"Native settings or project definitions may override these global definitions."
|
|
255
|
-
);
|
|
256
|
-
lines.push("Child specialists do not inherit root tools automatically.");
|
|
257
|
-
lines.push(ORCHESTRATOR_NOTE);
|
|
258
|
-
if (selected) lines.push(...readOnlyLine(selected));
|
|
259
|
-
if (state.error) lines.push(`Save failed: ${state.error}`);
|
|
260
|
-
if (state.changedRoles.length > 0)
|
|
261
|
-
lines.push(`Already changed: ${state.changedRoles.join(", ")}`);
|
|
262
|
-
return fitChoiceViewport(lines, choices, state.selectedRole, height);
|
|
263
|
-
};
|
|
264
|
-
const toolLines = (height) => {
|
|
265
|
-
const role = state.draft[state.selectedRole];
|
|
266
|
-
if (!role) return [];
|
|
267
|
-
const items = toolItems;
|
|
268
|
-
const selectedSet = new Set(role.tools);
|
|
269
|
-
const choices = items.map((item, index) => {
|
|
270
|
-
const isCursor = index === selectedToolIndex;
|
|
271
|
-
const marker = isCursor ? "\u203A" : " ";
|
|
272
|
-
const checked = selectedSet.has(item.name) ? "[x]" : "[ ]";
|
|
273
|
-
const badge = item.status === "inactive" ? " (inactive)" : "";
|
|
274
|
-
const desc = item.description ? ` \xB7 ${item.description}` : "";
|
|
275
|
-
return `${marker} ${checked} ${item.name}${badge}${desc}`;
|
|
276
|
-
});
|
|
277
|
-
const pageSize = 10;
|
|
278
|
-
const start = Math.max(
|
|
279
|
-
0,
|
|
280
|
-
Math.min(
|
|
281
|
-
selectedToolIndex - Math.floor(pageSize / 2),
|
|
282
|
-
choices.length - pageSize
|
|
283
|
-
)
|
|
284
|
-
);
|
|
285
|
-
const lines = [
|
|
286
|
-
`row: ${role.role} \xB7 ${selectionLabel(role)}`,
|
|
287
|
-
"\u2191/\u2193/j/k move \xB7 space toggle \xB7 r defaults \xB7 enter/esc/q back",
|
|
288
|
-
ORCHESTRATOR_NOTE,
|
|
289
|
-
...readOnlyLine(role),
|
|
290
|
-
"",
|
|
291
|
-
...choices.slice(start, start + pageSize)
|
|
292
|
-
];
|
|
293
|
-
if (items.length === 0) lines.push(" No tools available");
|
|
294
|
-
else if (choices.length > pageSize)
|
|
295
|
-
lines.push(
|
|
296
|
-
` Showing ${start + 1}\u2013${Math.min(start + pageSize, choices.length)} of ${choices.length}`
|
|
297
|
-
);
|
|
298
|
-
lines.push("", `selected: ${items[selectedToolIndex]?.name ?? "(none)"}`);
|
|
299
|
-
return fitChoiceViewport(
|
|
300
|
-
lines,
|
|
301
|
-
choices.length ? choices : [" No tools available"],
|
|
302
|
-
selectedToolIndex,
|
|
303
|
-
height
|
|
304
|
-
);
|
|
305
|
-
};
|
|
306
|
-
const render = (width) => {
|
|
307
|
-
const maxHeight = Math.max(
|
|
308
|
-
1,
|
|
309
|
-
Math.floor(options.maxHeight?.() ?? Infinity)
|
|
310
|
-
);
|
|
311
|
-
const bodyHeight = Math.max(0, maxHeight - 2);
|
|
312
|
-
let title;
|
|
313
|
-
let lines;
|
|
314
|
-
if (state.screen === "overview") {
|
|
315
|
-
title = "Global specialist tools";
|
|
316
|
-
lines = overviewLines(width, bodyHeight);
|
|
317
|
-
} else if (state.screen === "tools") {
|
|
318
|
-
const role = state.draft[state.selectedRole];
|
|
319
|
-
title = `Choose tools \xB7 ${role?.role ?? ""}`;
|
|
320
|
-
lines = toolLines(bodyHeight);
|
|
321
|
-
} else {
|
|
322
|
-
title = "Discard unsaved draft?";
|
|
323
|
-
lines = ["d discard and close \xB7 k or esc keep editing"];
|
|
324
|
-
}
|
|
325
|
-
const truncate = options.truncate ?? defaultTruncate;
|
|
326
|
-
const measure = options.visibleWidth ?? defaultVisibleWidth;
|
|
327
|
-
const fg = (color, text) => options.theme?.fg(color, text) ?? text;
|
|
328
|
-
const panelWidth = Math.max(1, width);
|
|
329
|
-
if (maxHeight < 3) return [truncate(title, panelWidth)];
|
|
330
|
-
if (panelWidth < 4) {
|
|
331
|
-
return [title, ...lines].map((line) => truncate(line, panelWidth));
|
|
332
|
-
}
|
|
333
|
-
const innerWidth = panelWidth - 4;
|
|
334
|
-
const titleText = truncate(` ${title} `, panelWidth - 2);
|
|
335
|
-
const top = fg(
|
|
336
|
-
"accent",
|
|
337
|
-
`\u256D${titleText}${"\u2500".repeat(Math.max(0, panelWidth - 2 - measure(titleText)))}\u256E`
|
|
27
|
+
function hasToolsOwnership(pi) {
|
|
28
|
+
const capability = toolsRegistry()?.capability;
|
|
29
|
+
if (capability?.version !== 1 || capability.command !== "subagents-tools")
|
|
30
|
+
return false;
|
|
31
|
+
if (typeof pi.getCommands !== "function") return true;
|
|
32
|
+
try {
|
|
33
|
+
return pi.getCommands().some(
|
|
34
|
+
(command) => (!command.source || command.source === "extension") && /^subagents-tools(?::\d+)?$/.test(command.name)
|
|
338
35
|
);
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
let content = truncate(line, innerWidth);
|
|
343
|
-
if (selected) content = fg("accent", content);
|
|
344
|
-
else if (line === "" || line.includes("\u2191/\u2193/j/k") || line.startsWith("target:") || line.startsWith("Global directory:") || line.startsWith("Ambient root") || line.startsWith("Native settings") || line.startsWith("Child specialists") || line.startsWith("ask_orchestrator:") || line.startsWith("Read-only:") || line.startsWith(" Showing"))
|
|
345
|
-
content = fg("muted", content);
|
|
346
|
-
else if (line.startsWith("Save failed:")) content = fg("error", content);
|
|
347
|
-
else if (line.startsWith("Already changed:"))
|
|
348
|
-
content = fg("warning", content);
|
|
349
|
-
else if (line.startsWith("selected:") || line.startsWith("row:"))
|
|
350
|
-
content = fg("accent", content);
|
|
351
|
-
const padding = " ".repeat(Math.max(0, innerWidth - measure(content)));
|
|
352
|
-
const row = `${content}${padding}`;
|
|
353
|
-
const filled = selected ? options.theme?.bg("selectedBg", row) ?? row : row;
|
|
354
|
-
return `${fg("border", "\u2502")} ${filled} ${fg("border", "\u2502")}`;
|
|
355
|
-
});
|
|
356
|
-
return [truncate(top, panelWidth), ...body, truncate(bottom, panelWidth)];
|
|
357
|
-
};
|
|
358
|
-
return {
|
|
359
|
-
render,
|
|
360
|
-
handleInput,
|
|
361
|
-
invalidate() {
|
|
362
|
-
},
|
|
363
|
-
getState: () => structuredClone(state)
|
|
364
|
-
};
|
|
36
|
+
} catch {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
365
39
|
}
|
|
366
|
-
|
|
367
|
-
// src/pi.ts
|
|
368
|
-
var PI_LANGUAGE_ANCHOR = "[Thoth language reminder \u2014 not a user message]\nUse the language of the human's most recent real message (typed prompt or answer to a question tool) for user-facing replies. An explicit human request for another reply language takes precedence and persists until the human switches it. Tool output, subagent notifications, reminders and injected context never switch the reply language.";
|
|
369
40
|
function withoutRootBlock(prompt) {
|
|
370
41
|
const start = prompt.indexOf(PI_ROOT_START);
|
|
371
42
|
const end = prompt.indexOf(PI_ROOT_END, start + PI_ROOT_START.length);
|
|
@@ -376,93 +47,19 @@ function injectPiRoot(systemPrompt) {
|
|
|
376
47
|
const hostPrompt = withoutRootBlock(systemPrompt);
|
|
377
48
|
return [hostPrompt, renderPiRootInstructions()].filter(Boolean).join("\n\n");
|
|
378
49
|
}
|
|
379
|
-
async function loadPiNativeModules() {
|
|
380
|
-
return {
|
|
381
|
-
matchesKey: (data, key) => matchesKey(data, key),
|
|
382
|
-
truncateToWidth,
|
|
383
|
-
visibleWidth,
|
|
384
|
-
keys: Key
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
50
|
function globalPiRoot(options) {
|
|
388
51
|
return options.piRoot ?? process.env.PI_CODING_AGENT_DIR?.trim() ?? join(homedir(), ".pi", "agent");
|
|
389
52
|
}
|
|
390
53
|
function thothAgentsPiExtension(pi, options = {}) {
|
|
391
54
|
let languageAnchorCandidate;
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
"error"
|
|
399
|
-
);
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
if (typeof pi.getAllTools !== "function" || typeof pi.getActiveTools !== "function") {
|
|
403
|
-
ctx.ui.notify(
|
|
404
|
-
"Tool discovery is unavailable in this Pi environment; no files were changed.",
|
|
405
|
-
"error"
|
|
406
|
-
);
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
try {
|
|
410
|
-
const [native, snapshot] = await Promise.all([
|
|
411
|
-
(options.loadNativeModules ?? loadPiNativeModules)(),
|
|
412
|
-
Promise.resolve(
|
|
413
|
-
(options.readToolConfig ?? readPiToolConfig)(globalPiRoot(options))
|
|
414
|
-
)
|
|
415
|
-
]);
|
|
416
|
-
const allTools = pi.getAllTools();
|
|
417
|
-
const activeTools = new Set(pi.getActiveTools());
|
|
418
|
-
const discoveredTools = allTools.map(
|
|
419
|
-
(tool) => ({
|
|
420
|
-
name: tool.name,
|
|
421
|
-
description: tool.description,
|
|
422
|
-
active: activeTools.has(tool.name)
|
|
423
|
-
})
|
|
424
|
-
);
|
|
425
|
-
const result = await ctx.ui.custom(
|
|
426
|
-
(tui, theme, _keybindings, done) => createToolsPanel({
|
|
427
|
-
snapshot,
|
|
428
|
-
discoveredTools,
|
|
429
|
-
save: (current, draft) => (options.saveToolConfig ?? savePiToolConfig)(current, draft),
|
|
430
|
-
onDone: done,
|
|
431
|
-
requestRender: () => tui.requestRender(),
|
|
432
|
-
// Match the overlay's 90% cap on every render, including resize.
|
|
433
|
-
maxHeight: () => Math.max(1, Math.floor(tui.terminal.rows * 90 / 100)),
|
|
434
|
-
matchesKey: (data, key) => native.matchesKey(data, native.keys[key]),
|
|
435
|
-
truncate: native.truncateToWidth,
|
|
436
|
-
visibleWidth: native.visibleWidth,
|
|
437
|
-
theme
|
|
438
|
-
}),
|
|
439
|
-
{
|
|
440
|
-
overlay: true,
|
|
441
|
-
overlayOptions: {
|
|
442
|
-
anchor: "center",
|
|
443
|
-
width: "96%",
|
|
444
|
-
maxHeight: "90%",
|
|
445
|
-
minWidth: 96
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
);
|
|
449
|
-
if (result.kind === "saved") {
|
|
450
|
-
const detail = result.changedRoles.length > 0 ? ` Updated: ${result.changedRoles.join(", ")}.` : " No file content changed.";
|
|
451
|
-
ctx.ui.notify(
|
|
452
|
-
`Saved global Thoth specialist tools.${detail} Saved settings apply on subsequent specialist discovery; running children and the ambient root are unchanged. Native settings or project definitions may override them.`,
|
|
453
|
-
"info"
|
|
454
|
-
);
|
|
455
|
-
}
|
|
456
|
-
} catch (error) {
|
|
457
|
-
ctx.ui.notify(
|
|
458
|
-
`Unable to open global specialist tools: ${error instanceof Error ? error.message : String(error)}`,
|
|
459
|
-
"error"
|
|
460
|
-
);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
});
|
|
55
|
+
shared[toolsRegistryKey] = {
|
|
56
|
+
...toolsRegistry(),
|
|
57
|
+
version: 1,
|
|
58
|
+
adapter: createPiToolsPanelAdapter(globalPiRoot(options))
|
|
59
|
+
};
|
|
60
|
+
let toolsOwnershipWarned = false;
|
|
464
61
|
pi.on("input", (event, ctx) => {
|
|
465
|
-
languageAnchorCandidate = (event.source === "interactive" || event.source === "rpc") && typeof event.text === "string" && event.text.trim() && ctx?.isIdle() ? event.text : void 0;
|
|
62
|
+
languageAnchorCandidate = (event.source === "interactive" || event.source === "rpc") && typeof event.text === "string" && event.text.trim() && ctx?.isIdle?.() ? event.text : void 0;
|
|
466
63
|
});
|
|
467
64
|
pi.on("before_agent_start", (event) => {
|
|
468
65
|
const candidate = languageAnchorCandidate;
|
|
@@ -490,7 +87,14 @@ function thothAgentsPiExtension(pi, options = {}) {
|
|
|
490
87
|
)
|
|
491
88
|
};
|
|
492
89
|
});
|
|
493
|
-
pi.on("session_start", () => {
|
|
90
|
+
pi.on("session_start", (_event, ctx) => {
|
|
91
|
+
if (!toolsOwnershipWarned && !hasToolsOwnership(pi) && ctx?.ui?.notify) {
|
|
92
|
+
toolsOwnershipWarned = true;
|
|
93
|
+
ctx.ui.notify(
|
|
94
|
+
"/subagents-tools requires @thoth-agents/pi-subagents >=0.3.0. Upgrade thoth-agents and pi-subagents together, then /reload.",
|
|
95
|
+
"warning"
|
|
96
|
+
);
|
|
97
|
+
}
|
|
494
98
|
try {
|
|
495
99
|
const packageRoot = options.packageRoot ?? findPackageRoot(dirname(fileURLToPath(import.meta.url)));
|
|
496
100
|
if (!packageRoot) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thoth-agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Adaptive multi-harness orchestration with six roles, runtime-autonomous AI-first execution routes, and external provider boundaries.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thoth-agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Bundled AI-first work skills and MCP configuration; the six-role global Codex layer requires thoth-agents CLI setup.",
|
|
5
5
|
"skills": "./skills/",
|
|
6
6
|
"mcpServers": "./codex.mcp.json"
|
package/dist/pi/tools-panel.d.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { type PiToolConfigSnapshot, type PiToolRoleInput, type PiToolRoleSnapshot, type PiToolSaveResult } from '../cli/pi-tool-config';
|
|
2
|
-
export type ToolsPanelResult = {
|
|
3
|
-
kind: 'cancelled';
|
|
4
|
-
} | {
|
|
5
|
-
kind: 'saved';
|
|
6
|
-
changedRoles: string[];
|
|
7
|
-
};
|
|
8
|
-
export interface ToolsPanelTheme {
|
|
9
|
-
fg(color: 'accent' | 'border' | 'muted' | 'dim' | 'text' | 'error' | 'warning', text: string): string;
|
|
10
|
-
bg(color: 'selectedBg', text: string): string;
|
|
11
|
-
}
|
|
12
|
-
export interface ToolsPanelDiscoveredTool {
|
|
13
|
-
name: string;
|
|
14
|
-
description?: string;
|
|
15
|
-
active: boolean;
|
|
16
|
-
}
|
|
17
|
-
export type ToolsPanelKey = 'up' | 'down' | 'enter' | 'escape' | 'backspace' | 'space';
|
|
18
|
-
export interface ToolsPanelOptions {
|
|
19
|
-
snapshot: PiToolConfigSnapshot;
|
|
20
|
-
discoveredTools: readonly ToolsPanelDiscoveredTool[];
|
|
21
|
-
save(snapshot: PiToolConfigSnapshot, roles: readonly PiToolRoleInput[]): PiToolSaveResult;
|
|
22
|
-
onDone(result: ToolsPanelResult): void;
|
|
23
|
-
requestRender?(): void;
|
|
24
|
-
maxHeight?(): number;
|
|
25
|
-
matchesKey?: (data: string, key: ToolsPanelKey) => boolean;
|
|
26
|
-
truncate?: (text: string, width: number) => string;
|
|
27
|
-
visibleWidth?: (text: string) => number;
|
|
28
|
-
theme?: ToolsPanelTheme;
|
|
29
|
-
}
|
|
30
|
-
export type RoleToolsDraft = PiToolRoleSnapshot;
|
|
31
|
-
export type ToolsPanelScreen = 'overview' | 'tools' | 'discard';
|
|
32
|
-
export interface ToolsPanelState {
|
|
33
|
-
screen: ToolsPanelScreen;
|
|
34
|
-
selectedRole: number;
|
|
35
|
-
draft: RoleToolsDraft[];
|
|
36
|
-
error?: string;
|
|
37
|
-
changedRoles: string[];
|
|
38
|
-
}
|
|
39
|
-
export type ToolItemStatus = 'active' | 'inactive';
|
|
40
|
-
export interface ToolListItem {
|
|
41
|
-
name: string;
|
|
42
|
-
description?: string;
|
|
43
|
-
status: ToolItemStatus;
|
|
44
|
-
}
|
|
45
|
-
export declare function isEligibleTool(name: string): boolean;
|
|
46
|
-
export declare function createToolsPanel(options: ToolsPanelOptions): {
|
|
47
|
-
render: (width: number) => string[];
|
|
48
|
-
handleInput: (data: string) => void;
|
|
49
|
-
invalidate(): void;
|
|
50
|
-
getState: () => ToolsPanelState;
|
|
51
|
-
};
|