thoth-agents 0.4.2 → 0.5.1
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/LICENSE +30 -0
- package/README.md +86 -58
- package/dist/agents/prompt-sections.d.ts +5 -3
- package/dist/agents/prompt-utils.d.ts +1 -1
- package/dist/agents/worker.d.ts +2 -0
- package/dist/{chunk-STCURXAQ.js → chunk-MRFCA3U2.js} +33 -67
- package/dist/{chunk-EFGSGPYM.js → chunk-OW6I7ZUU.js} +391 -416
- package/dist/chunk-V7HSGRMB.js +1016 -0
- package/dist/{chunk-WCLUZZHI.js → chunk-VUNYH263.js} +1580 -351
- package/dist/cli/claude-code-paths.d.ts +2 -2
- package/dist/cli/codex-install.d.ts +5 -2
- package/dist/cli/codex-paths.d.ts +2 -2
- package/dist/cli/index.js +47 -11
- package/dist/cli/operations/pi.d.ts +1 -0
- package/dist/cli/pi-effort.d.ts +1 -1
- package/dist/cli/pi-external-package.d.ts +24 -0
- package/dist/cli/pi-git-source.d.ts +1 -0
- package/dist/cli/pi-install.d.ts +92 -15
- package/dist/cli/pi-managed-write.d.ts +3 -0
- package/dist/cli/pi-model-config.d.ts +17 -0
- package/dist/cli/pi-native-probe.d.ts +2 -0
- package/dist/cli/pi-paths.d.ts +2 -0
- package/dist/cli/pi-resources.d.ts +3 -1
- package/dist/cli/pi-tool-config.d.ts +32 -0
- package/dist/cli/tui/index.js +3 -3
- package/dist/cli/types.d.ts +2 -0
- package/dist/config/constants.d.ts +4 -8
- package/dist/config/schema.d.ts +7 -36
- package/dist/harness/adapters/claude-code.d.ts +2 -4
- package/dist/harness/core/agent-pack.d.ts +52 -42
- package/dist/harness/core/memory-governance.d.ts +2 -2
- package/dist/harness/core/sdd.d.ts +159 -319
- package/dist/harness/writers/pi-agent.d.ts +3 -1
- package/dist/index.js +12 -6
- package/dist/pi/tools-panel.d.ts +51 -0
- package/dist/pi.d.ts +44 -2
- package/dist/pi.js +471 -9
- package/dist/utils/subprocess.d.ts +1 -0
- package/package.json +23 -6
- package/pi/.thoth-agents-assets.json +5 -6
- package/pi/agents/thoth-designer.md +23 -8
- package/pi/agents/thoth-explorer.md +29 -9
- package/pi/agents/thoth-librarian.md +30 -8
- package/pi/agents/thoth-oracle.md +18 -10
- package/pi/agents/thoth-worker.md +96 -0
- package/plugin/.claude-plugin/.thoth-agents-plugin-assets.json +7 -12
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/.codex-plugin/plugin.json +2 -2
- package/plugin/agents/designer.md +13 -5
- package/plugin/agents/explorer.md +16 -5
- package/plugin/agents/librarian.md +15 -4
- package/plugin/agents/oracle.md +13 -7
- package/plugin/agents/orchestrator.md +82 -91
- package/plugin/agents/worker.md +76 -0
- package/plugin/skills/plan-reviewer/SKILL.md +39 -141
- package/plugin/skills/thoth-archive/SKILL.md +50 -47
- package/plugin/skills/thoth-archive/scripts/archive.mjs +287 -654
- package/plugin/skills/thoth-constitution/SKILL.md +23 -42
- package/plugin/skills/thoth-constitution/templates/constitution.md +54 -44
- package/plugin/skills/thoth-init/SKILL.md +19 -29
- package/plugin/skills/thoth-init/scripts/init.mjs +73 -66
- package/plugin/skills/thoth-sdd/SKILL.md +164 -117
- package/plugin/skills/thoth-sdd/references/phases/checklist.md +2 -22
- package/plugin/skills/thoth-sdd/references/phases/clarify.md +14 -12
- package/plugin/skills/thoth-sdd/references/phases/converge.md +2 -13
- package/plugin/skills/thoth-sdd/references/phases/explore.md +3 -8
- package/plugin/skills/thoth-sdd/references/phases/implement.md +58 -57
- package/plugin/skills/thoth-sdd/references/phases/plan.md +2 -18
- package/plugin/skills/thoth-sdd/references/phases/specify.md +2 -33
- package/plugin/skills/thoth-sdd/references/phases/tasks.md +4 -45
- package/plugin/skills/thoth-sdd/references/phases/verify.md +4 -22
- package/plugin/skills/thoth-sdd/scripts/validate.mjs +610 -1232
- package/plugin/skills/thoth-sdd/templates/change.md +94 -0
- package/skills/README.md +11 -13
- package/skills/plan-reviewer/SKILL.md +39 -141
- package/skills/thoth-archive/SKILL.md +50 -47
- package/skills/thoth-archive/scripts/archive.mjs +287 -654
- package/skills/thoth-constitution/SKILL.md +23 -42
- package/skills/thoth-constitution/templates/constitution.md +54 -44
- package/skills/thoth-init/SKILL.md +19 -29
- package/skills/thoth-init/scripts/init.mjs +73 -66
- package/skills/thoth-sdd/SKILL.md +164 -117
- package/skills/thoth-sdd/references/phases/checklist.md +2 -22
- package/skills/thoth-sdd/references/phases/clarify.md +14 -12
- package/skills/thoth-sdd/references/phases/converge.md +2 -13
- package/skills/thoth-sdd/references/phases/explore.md +3 -8
- package/skills/thoth-sdd/references/phases/implement.md +58 -57
- package/skills/thoth-sdd/references/phases/plan.md +2 -18
- package/skills/thoth-sdd/references/phases/specify.md +2 -33
- package/skills/thoth-sdd/references/phases/tasks.md +4 -45
- package/skills/thoth-sdd/references/phases/verify.md +4 -22
- package/skills/thoth-sdd/scripts/validate.mjs +610 -1232
- package/skills/thoth-sdd/templates/change.md +94 -0
- package/thoth-agents.schema.json +9 -77
- package/dist/agents/deep.d.ts +0 -2
- package/dist/agents/quick.d.ts +0 -2
- package/dist/chunk-2U5MBLLV.js +0 -312
- package/pi/agents/thoth-deep.md +0 -81
- package/pi/agents/thoth-quick.md +0 -81
- package/plugin/agents/deep.md +0 -68
- package/plugin/agents/quick.md +0 -68
- package/plugin/skills/plan-reviewer/templates/plan-review.md +0 -65
- package/plugin/skills/thoth-sdd/templates/archive-report.md +0 -25
- package/plugin/skills/thoth-sdd/templates/checklist.md +0 -27
- package/plugin/skills/thoth-sdd/templates/plan.md +0 -46
- package/plugin/skills/thoth-sdd/templates/spec.md +0 -62
- package/plugin/skills/thoth-sdd/templates/tasks.md +0 -58
- package/plugin/skills/thoth-sdd/templates/verify-report.md +0 -28
- package/skills/plan-reviewer/templates/plan-review.md +0 -65
- package/skills/thoth-sdd/templates/archive-report.md +0 -25
- package/skills/thoth-sdd/templates/checklist.md +0 -27
- package/skills/thoth-sdd/templates/plan.md +0 -46
- package/skills/thoth-sdd/templates/spec.md +0 -62
- package/skills/thoth-sdd/templates/tasks.md +0 -58
- package/skills/thoth-sdd/templates/verify-report.md +0 -28
package/dist/pi.js
CHANGED
|
@@ -1,17 +1,371 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PI_ROOT_END,
|
|
3
3
|
PI_ROOT_START,
|
|
4
|
+
readPiToolConfig,
|
|
4
5
|
renderPiRootInstructions,
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
savePiToolConfig,
|
|
7
|
+
syncPiSpecialists,
|
|
8
|
+
validatePiSpecialistTools
|
|
9
|
+
} from "./chunk-V7HSGRMB.js";
|
|
7
10
|
import {
|
|
8
11
|
findPackageRoot
|
|
9
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OW6I7ZUU.js";
|
|
10
13
|
|
|
11
14
|
// src/pi.ts
|
|
12
15
|
import { homedir } from "os";
|
|
13
16
|
import { dirname, join } from "path";
|
|
14
17
|
import { fileURLToPath } from "url";
|
|
18
|
+
import {
|
|
19
|
+
Key,
|
|
20
|
+
matchesKey,
|
|
21
|
+
truncateToWidth,
|
|
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;
|
|
37
|
+
try {
|
|
38
|
+
validatePiSpecialistTools([name]);
|
|
39
|
+
return true;
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function sameTools(left, right) {
|
|
45
|
+
if (left.length !== right.length) return false;
|
|
46
|
+
return left.every((tool, index) => tool === right[index]);
|
|
47
|
+
}
|
|
48
|
+
function cloneRoles(roles) {
|
|
49
|
+
return roles.map((role) => structuredClone(role));
|
|
50
|
+
}
|
|
51
|
+
function defaultTruncate(text, width) {
|
|
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`
|
|
338
|
+
);
|
|
339
|
+
const bottom = fg("border", `\u2570${"\u2500".repeat(panelWidth - 2)}\u256F`);
|
|
340
|
+
const body = lines.map((line) => {
|
|
341
|
+
const selected = line.startsWith("\u203A ");
|
|
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
|
+
};
|
|
365
|
+
}
|
|
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.";
|
|
15
369
|
function withoutRootBlock(prompt) {
|
|
16
370
|
const start = prompt.indexOf(PI_ROOT_START);
|
|
17
371
|
const end = prompt.indexOf(PI_ROOT_END, start + PI_ROOT_START.length);
|
|
@@ -22,19 +376,127 @@ function injectPiRoot(systemPrompt) {
|
|
|
22
376
|
const hostPrompt = withoutRootBlock(systemPrompt);
|
|
23
377
|
return [hostPrompt, renderPiRootInstructions()].filter(Boolean).join("\n\n");
|
|
24
378
|
}
|
|
379
|
+
async function loadPiNativeModules() {
|
|
380
|
+
return {
|
|
381
|
+
matchesKey: (data, key) => matchesKey(data, key),
|
|
382
|
+
truncateToWidth,
|
|
383
|
+
visibleWidth,
|
|
384
|
+
keys: Key
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function globalPiRoot(options) {
|
|
388
|
+
return options.piRoot ?? process.env.PI_CODING_AGENT_DIR?.trim() ?? join(homedir(), ".pi", "agent");
|
|
389
|
+
}
|
|
25
390
|
function thothAgentsPiExtension(pi, options = {}) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
30
|
-
|
|
391
|
+
let languageAnchorCandidate;
|
|
392
|
+
pi.registerCommand?.("subagents-tools", {
|
|
393
|
+
description: "Edit global Thoth specialist tools",
|
|
394
|
+
handler: async (_args, ctx) => {
|
|
395
|
+
if (ctx.mode !== "tui") {
|
|
396
|
+
ctx.ui.notify(
|
|
397
|
+
"/subagents-tools requires interactive TUI mode; no files were changed.",
|
|
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
|
+
});
|
|
464
|
+
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;
|
|
466
|
+
});
|
|
467
|
+
pi.on("before_agent_start", (event) => {
|
|
468
|
+
const candidate = languageAnchorCandidate;
|
|
469
|
+
languageAnchorCandidate = void 0;
|
|
470
|
+
const anchor = candidate !== void 0 && candidate === event.prompt ? {
|
|
471
|
+
message: {
|
|
472
|
+
customType: "thoth-language-anchor",
|
|
473
|
+
content: PI_LANGUAGE_ANCHOR,
|
|
474
|
+
display: false
|
|
475
|
+
}
|
|
476
|
+
} : void 0;
|
|
477
|
+
if (event.systemPromptOptions && typeof event.systemPromptOptions === "object") {
|
|
478
|
+
const promptOptions = event.systemPromptOptions;
|
|
479
|
+
const rootBlock = renderPiRootInstructions();
|
|
480
|
+
const append = promptOptions.appendSystemPrompt ?? "";
|
|
481
|
+
if (!append.includes(rootBlock)) {
|
|
482
|
+
promptOptions.appendSystemPrompt = [append, rootBlock].filter(Boolean).join("\n\n");
|
|
483
|
+
}
|
|
484
|
+
return anchor;
|
|
485
|
+
}
|
|
486
|
+
return {
|
|
487
|
+
...anchor,
|
|
488
|
+
systemPrompt: injectPiRoot(
|
|
489
|
+
typeof event.systemPrompt === "string" ? event.systemPrompt : ""
|
|
490
|
+
)
|
|
491
|
+
};
|
|
492
|
+
});
|
|
31
493
|
pi.on("session_start", () => {
|
|
32
494
|
try {
|
|
33
495
|
const packageRoot = options.packageRoot ?? findPackageRoot(dirname(fileURLToPath(import.meta.url)));
|
|
34
496
|
if (!packageRoot) return;
|
|
35
497
|
syncPiSpecialists({
|
|
36
498
|
packageRoot,
|
|
37
|
-
piRoot: options
|
|
499
|
+
piRoot: globalPiRoot(options)
|
|
38
500
|
});
|
|
39
501
|
} catch {
|
|
40
502
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thoth-agents",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Adaptive multi-harness orchestration with
|
|
3
|
+
"version": "0.5.1",
|
|
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",
|
|
7
7
|
"bin": {
|
|
@@ -51,15 +51,17 @@
|
|
|
51
51
|
]
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
|
-
"build": "pnpm run integration:sync && tsup && tsc --emitDeclarationOnly && pnpm run generate-schema",
|
|
54
|
+
"build": "pnpm run integration:sync && tsup && tsc --emitDeclarationOnly && pnpm run generate-schema && pnpm run build:pi-extensions",
|
|
55
55
|
"generate-schema": "tsx scripts/generate-schema.ts",
|
|
56
56
|
"integration:sync": "tsx src/harness/generate-integration-packages.ts",
|
|
57
57
|
"integration:verify": "vitest run src/harness/generate-integration-packages.test.ts src/harness/integration-lifecycle.test.ts",
|
|
58
58
|
"verify:pi-package": "node scripts/verify-pi-package.mjs",
|
|
59
59
|
"setup:codex:local": "pnpm run build && tsx scripts/setup-codex-local.ts",
|
|
60
|
+
"setup:pi:local": "pnpm run build && node scripts/setup-pi-local.mjs",
|
|
60
61
|
"release:notes": "tsx scripts/generate-release-notes.ts",
|
|
61
62
|
"typecheck": "tsc --noEmit",
|
|
62
63
|
"test": "vitest run",
|
|
64
|
+
"test:pi-extensions": "node --test --test-timeout=110000 scripts/pi-extension-bundles.test.mjs",
|
|
63
65
|
"lint": "biome lint .",
|
|
64
66
|
"format": "biome format . --write",
|
|
65
67
|
"check": "biome check --write .",
|
|
@@ -68,37 +70,52 @@
|
|
|
68
70
|
"prepublishOnly": "pnpm run build && pnpm run integration:verify",
|
|
69
71
|
"version": "pnpm run integration:sync && pnpm run integration:verify && git add -- plugin",
|
|
70
72
|
"release:marketplace": "node scripts/publish-marketplace.mjs",
|
|
73
|
+
"release:pi": "node scripts/release-pi.mjs",
|
|
71
74
|
"release:patch": "npm version patch --ignore-scripts=false && git push --follow-tags",
|
|
72
75
|
"release:minor": "npm version minor --ignore-scripts=false && git push --follow-tags",
|
|
73
|
-
"release:major": "npm version major --ignore-scripts=false && git push --follow-tags"
|
|
76
|
+
"release:major": "npm version major --ignore-scripts=false && git push --follow-tags",
|
|
77
|
+
"build:pi-extensions": "node scripts/build-pi-extensions.mjs"
|
|
74
78
|
},
|
|
75
79
|
"dependencies": {
|
|
76
80
|
"@ast-grep/cli": "^0.40.0",
|
|
77
81
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
78
82
|
"@opencode-ai/plugin": "1.18.2",
|
|
79
83
|
"@opencode-ai/sdk": "1.18.2",
|
|
84
|
+
"hosted-git-info": "9.0.3",
|
|
80
85
|
"ink": "^7.0.3",
|
|
81
86
|
"react": "^19.2.6",
|
|
87
|
+
"semver": "^7.8.5",
|
|
82
88
|
"unique-names-generator": "^4.7.1",
|
|
83
89
|
"vscode-jsonrpc": "^8.2.0",
|
|
84
90
|
"vscode-languageserver-protocol": "^3.17.5",
|
|
85
91
|
"which": "^6.0.0",
|
|
92
|
+
"yaml": "2.9.0",
|
|
86
93
|
"zod": "^4.1.8"
|
|
87
94
|
},
|
|
95
|
+
"peerDependencies": {
|
|
96
|
+
"@earendil-works/pi-ai": ">=0.99.0",
|
|
97
|
+
"@earendil-works/pi-tui": ">=0.99.0"
|
|
98
|
+
},
|
|
88
99
|
"devDependencies": {
|
|
89
100
|
"@biomejs/biome": "2.4.2",
|
|
101
|
+
"@earendil-works/pi-ai": "1.0.2",
|
|
102
|
+
"@earendil-works/pi-coding-agent": "1.0.2",
|
|
103
|
+
"@earendil-works/pi-tui": "1.0.2",
|
|
104
|
+
"@types/hosted-git-info": "3.0.5",
|
|
90
105
|
"@types/node": "^22.19.19",
|
|
91
106
|
"@types/react": "^19.2.15",
|
|
107
|
+
"@types/semver": "^7.8.0",
|
|
92
108
|
"@types/which": "^3.0.4",
|
|
109
|
+
"esbuild": "0.28.2",
|
|
93
110
|
"ink-testing-library": "^4.0.0",
|
|
94
111
|
"tsup": "^8.5.1",
|
|
95
|
-
"tsx": "4.22.
|
|
112
|
+
"tsx": "4.22.4",
|
|
96
113
|
"typescript": "^5.9.3",
|
|
97
114
|
"vitest": "4.1.6"
|
|
98
115
|
},
|
|
99
116
|
"overrides": {
|
|
100
117
|
"hono": "4.12.19",
|
|
101
118
|
"postcss": "8.5.14",
|
|
102
|
-
"tsx": "4.22.
|
|
119
|
+
"tsx": "4.22.4"
|
|
103
120
|
}
|
|
104
121
|
}
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"owner": "thoth-agents",
|
|
4
4
|
"files": {
|
|
5
|
-
"agents/thoth-explorer.md": "
|
|
6
|
-
"agents/thoth-librarian.md": "
|
|
7
|
-
"agents/thoth-oracle.md": "
|
|
8
|
-
"agents/thoth-designer.md": "
|
|
9
|
-
"agents/thoth-
|
|
10
|
-
"agents/thoth-deep.md": "fc07a523836113ba885341a33570ab46d2a7789649ad5ab40f0eb8a9c3ffd2de"
|
|
5
|
+
"agents/thoth-explorer.md": "d9b47792d85e479ed3e0179671223708f097e18c2173320d8a7eb1ab7886775a",
|
|
6
|
+
"agents/thoth-librarian.md": "82e5e3c1e3e8b589937382e8fb66cf3425914685d88edf968b2b52657f284511",
|
|
7
|
+
"agents/thoth-oracle.md": "808b2b6cfe9d48113704c64737d836cc14ff7fc2e1c5f87486f3e985aa8b92f5",
|
|
8
|
+
"agents/thoth-designer.md": "20cc2042c71ad2e2a783d11d75308fee5331a65eb3f066bc0d4046f117031382",
|
|
9
|
+
"agents/thoth-worker.md": "de9d9e6230ce16279c5bc929d9c1b65574c2fc589aec9a98fe5ea7cebd447323"
|
|
11
10
|
}
|
|
12
11
|
}
|