@stfade/pi-read-delegator 1.0.4 → 1.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/index.d.ts +2 -60
- package/index.js +212 -232
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -1,61 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Lifecycle:
|
|
5
|
-
* init(agent) → load config, check deps, ensure template, enable/disable
|
|
6
|
-
* enable(agent) → block tools, add system prompt, attach bash filter
|
|
7
|
-
* disable(agent) → restore tools, remove prompt, detach bash filter
|
|
8
|
-
*
|
|
9
|
-
* Commands:
|
|
10
|
-
* /read-delegator on → enable the delegator
|
|
11
|
-
* /read-delegator off → disable the delegator
|
|
12
|
-
* /read-delegator status → show current status
|
|
13
|
-
*/
|
|
14
|
-
import { type AgentWithSubagent } from "./reader-manager";
|
|
15
|
-
/**
|
|
16
|
-
* The Pi agent interface as consumed by pi-read-delegator.
|
|
17
|
-
* Extends the building-block types from sub-modules.
|
|
18
|
-
*/
|
|
19
|
-
export interface PiAgent extends AgentWithSubagent {
|
|
20
|
-
/** Return current tool definitions. */
|
|
21
|
-
getTools(): Array<{
|
|
22
|
-
name: string;
|
|
23
|
-
}>;
|
|
24
|
-
/** Remove a tool by name. */
|
|
25
|
-
removeTool(name: string): void;
|
|
26
|
-
/** Add/re-add a tool definition. */
|
|
27
|
-
addTool(definition: {
|
|
28
|
-
name: string;
|
|
29
|
-
[key: string]: unknown;
|
|
30
|
-
}): void;
|
|
31
|
-
/** Append a persistent system message to the conversation. */
|
|
32
|
-
addSystemMessage(text: string): void;
|
|
33
|
-
/** Remove a previously-added system message by its exact text. */
|
|
34
|
-
removeSystemMessage(text: string): void;
|
|
35
|
-
/** Register a hook that fires BEFORE a tool with the given name is called. */
|
|
36
|
-
onBeforeToolCall(toolName: string, callback: (params: unknown) => Promise<unknown> | unknown): void;
|
|
37
|
-
/** Register a Pi command (like /read-delegator on). */
|
|
38
|
-
registerCommand(name: string, handler: (args: string[]) => Promise<string> | string): void;
|
|
39
|
-
/** Execute a raw shell command directly on the system. */
|
|
40
|
-
executeShellCommand(command: string): Promise<{
|
|
41
|
-
stdout: string;
|
|
42
|
-
stderr: string;
|
|
43
|
-
}>;
|
|
44
|
-
/** Prompt the user for input. */
|
|
45
|
-
promptUser(message: string): Promise<string>;
|
|
46
|
-
/** Display a message to the user. */
|
|
47
|
-
displayMessage(message: string): void;
|
|
48
|
-
/** Set status bar text. */
|
|
49
|
-
setStatusBarText(text: string): void;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Initialize the extension.
|
|
53
|
-
*
|
|
54
|
-
* This is the function Pi calls when loading the extension.
|
|
55
|
-
* It returns a lifecycle object with enable() and disable().
|
|
56
|
-
*/
|
|
57
|
-
export declare function init(agent: PiAgent): {
|
|
58
|
-
enable: () => void;
|
|
59
|
-
disable: () => void;
|
|
60
|
-
};
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
export default function (pi: ExtensionAPI): Promise<void>;
|
|
61
3
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -1,260 +1,240 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* enable(agent) → block tools, add system prompt, attach bash filter
|
|
8
|
-
* disable(agent) → restore tools, remove prompt, detach bash filter
|
|
9
|
-
*
|
|
10
|
-
* Commands:
|
|
11
|
-
* /read-delegator on → enable the delegator
|
|
12
|
-
* /read-delegator off → disable the delegator
|
|
13
|
-
* /read-delegator status → show current status
|
|
14
|
-
*/
|
|
15
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.init = init;
|
|
17
|
-
const config_1 = require("./config");
|
|
18
|
-
const tool_blocker_1 = require("./tool-blocker");
|
|
19
|
-
const bash_filter_1 = require("./bash-filter");
|
|
20
|
-
const reader_manager_1 = require("./reader-manager");
|
|
21
|
-
const ui_1 = require("./ui");
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
// Module state
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
let enabled = false;
|
|
26
|
-
let config = null;
|
|
27
|
-
let currentSystemMessage = null;
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
// Lifecycle: init
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
/**
|
|
32
|
-
* Initialize the extension.
|
|
33
|
-
*
|
|
34
|
-
* This is the function Pi calls when loading the extension.
|
|
35
|
-
* It returns a lifecycle object with enable() and disable().
|
|
36
|
-
*/
|
|
37
|
-
function init(agent) {
|
|
38
|
-
// 1. Load configuration
|
|
39
|
-
config = (0, config_1.loadConfig)();
|
|
40
|
-
// 2. Detect language
|
|
41
|
-
(0, ui_1.getLanguage)(config.language);
|
|
42
|
-
// 3. Initialize status bar
|
|
43
|
-
(0, ui_1.initStatusBar)(agent);
|
|
44
|
-
// 4. Register commands
|
|
45
|
-
registerCommands(agent);
|
|
46
|
-
// 5. Run async init tasks (dependency check, template) in background.
|
|
47
|
-
// We do NOT block init — if deps are missing the user will be prompted.
|
|
48
|
-
initAsync(agent);
|
|
49
|
-
// Build lifecycle interface
|
|
50
|
-
const enable = () => doEnable(agent);
|
|
51
|
-
const disable = () => doDisable(agent);
|
|
52
|
-
// If config says enabled, auto-enable (synchronous part first)
|
|
53
|
-
if (config?.enabled) {
|
|
54
|
-
doEnable(agent);
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
55
7
|
}
|
|
56
|
-
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.default = default_1;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const os = __importStar(require("node:os"));
|
|
39
|
+
const path = __importStar(require("node:path"));
|
|
40
|
+
const DEFAULT_CONFIG = {
|
|
41
|
+
enabled: true,
|
|
42
|
+
reader_subagent_name: "reader",
|
|
43
|
+
blocked_tools: ["read", "grep", "find", "ls"],
|
|
44
|
+
orchestrator_prompt: [
|
|
45
|
+
"You are an orchestrator. You do NOT have direct file-reading tools.",
|
|
46
|
+
"For any file reading, searching, or directory listing, use the",
|
|
47
|
+
"'subagent' tool with agent='reader'.",
|
|
48
|
+
'Example: subagent(agent: "reader", task: "Find all TS files that import \'lodash\'")',
|
|
49
|
+
"Never try to use read, grep, find, or ls yourself. Always delegate.",
|
|
50
|
+
].join("\n"),
|
|
51
|
+
language: "auto",
|
|
52
|
+
};
|
|
53
|
+
const READ_BASH_COMMANDS = new Set([
|
|
54
|
+
"cat",
|
|
55
|
+
"grep",
|
|
56
|
+
"find",
|
|
57
|
+
"ls",
|
|
58
|
+
"head",
|
|
59
|
+
"tail",
|
|
60
|
+
"less",
|
|
61
|
+
"wc",
|
|
62
|
+
"nl",
|
|
63
|
+
"more",
|
|
64
|
+
"bat",
|
|
65
|
+
"rg",
|
|
66
|
+
"fd",
|
|
67
|
+
"awk",
|
|
68
|
+
"du",
|
|
69
|
+
"df",
|
|
70
|
+
"stat",
|
|
71
|
+
"file",
|
|
72
|
+
"which",
|
|
73
|
+
"where",
|
|
74
|
+
"type",
|
|
75
|
+
"dir",
|
|
76
|
+
]);
|
|
77
|
+
function configPath() {
|
|
78
|
+
return path.join(os.homedir(), ".pi", "agent", "read-delegator.json");
|
|
57
79
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
80
|
+
function readerPath() {
|
|
81
|
+
return path.join(os.homedir(), ".pi", "agent", "agents", "reader.md");
|
|
82
|
+
}
|
|
83
|
+
function loadConfig() {
|
|
84
|
+
const cp = configPath();
|
|
62
85
|
try {
|
|
63
|
-
|
|
64
|
-
|
|
86
|
+
if (fs.existsSync(cp)) {
|
|
87
|
+
const raw = fs.readFileSync(cp, "utf8");
|
|
88
|
+
const parsed = JSON.parse(raw);
|
|
89
|
+
return { ...DEFAULT_CONFIG, ...parsed };
|
|
90
|
+
}
|
|
65
91
|
}
|
|
66
|
-
catch
|
|
67
|
-
|
|
68
|
-
(0, ui_1.logError)("reader_failed", String(err));
|
|
69
|
-
// Disable the extension if dependencies can't be satisfied
|
|
70
|
-
doDisable(agent);
|
|
71
|
-
return;
|
|
92
|
+
catch {
|
|
93
|
+
// corrupt file --- fall back to defaults
|
|
72
94
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
(
|
|
95
|
+
try {
|
|
96
|
+
const dir = path.dirname(cp);
|
|
97
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
98
|
+
fs.writeFileSync(cp, JSON.stringify(DEFAULT_CONFIG, null, 2), "utf8");
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// read-only home directory --- ignore
|
|
77
102
|
}
|
|
103
|
+
return { ...DEFAULT_CONFIG };
|
|
78
104
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return;
|
|
105
|
+
function saveConfig(config) {
|
|
106
|
+
const cp = configPath();
|
|
107
|
+
try {
|
|
108
|
+
const dir = path.dirname(cp);
|
|
109
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
110
|
+
fs.writeFileSync(cp, JSON.stringify(config, null, 2), "utf8");
|
|
86
111
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return;
|
|
112
|
+
catch {
|
|
113
|
+
// read-only home directory --- ignore
|
|
90
114
|
}
|
|
91
|
-
// Block read tools
|
|
92
|
-
(0, tool_blocker_1.blockTools)(agent, config.blocked_tools);
|
|
93
|
-
// Add system message
|
|
94
|
-
currentSystemMessage = config.orchestrator_prompt;
|
|
95
|
-
agent.addSystemMessage(config.orchestrator_prompt);
|
|
96
|
-
// Attach bash filter hook
|
|
97
|
-
attachBashFilter(agent);
|
|
98
|
-
// Update status
|
|
99
|
-
enabled = true;
|
|
100
|
-
(0, ui_1.updateStatusBar)("active");
|
|
101
|
-
(0, ui_1.log)("enabled");
|
|
102
|
-
agent.displayMessage((0, ui_1.msg)("enabled"));
|
|
103
115
|
}
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
116
|
+
async function ensureReaderTemplate() {
|
|
117
|
+
const rp = readerPath();
|
|
118
|
+
if (fs.existsSync(rp))
|
|
107
119
|
return;
|
|
120
|
+
const content = [
|
|
121
|
+
"---",
|
|
122
|
+
"name: reader",
|
|
123
|
+
"description: Token-efficient code reader that returns minimal results.",
|
|
124
|
+
"tools: read, grep, find, ls",
|
|
125
|
+
"model: lmstudio/nvidia/nemotron-3-nano-4b",
|
|
126
|
+
"---",
|
|
127
|
+
"",
|
|
128
|
+
"You are a token-efficient analyst. Execute read/search/list tasks and return",
|
|
129
|
+
"ONLY the essential result. Maximum 10 lines. Use bullet summaries.",
|
|
130
|
+
"Never dump entire files. Focus only on what was asked.",
|
|
131
|
+
].join("\n");
|
|
132
|
+
try {
|
|
133
|
+
const dir = path.dirname(rp);
|
|
134
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
135
|
+
await fs.promises.writeFile(rp, content, "utf8");
|
|
108
136
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// Remove system message
|
|
112
|
-
if (currentSystemMessage) {
|
|
113
|
-
try {
|
|
114
|
-
agent.removeSystemMessage(currentSystemMessage);
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
// Best effort — the message text may have been mutated
|
|
118
|
-
}
|
|
119
|
-
currentSystemMessage = null;
|
|
137
|
+
catch {
|
|
138
|
+
// read-only home directory --- template creation is best-effort
|
|
120
139
|
}
|
|
121
|
-
// Detach bash filter (we can't undo onBeforeToolCall, but we set a flag)
|
|
122
|
-
enabled = false;
|
|
123
|
-
(0, ui_1.updateStatusBar)("idle");
|
|
124
|
-
(0, ui_1.log)("disabled");
|
|
125
|
-
agent.displayMessage((0, ui_1.msg)("disabled"));
|
|
126
140
|
}
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
|
-
// Bash filter hook
|
|
129
|
-
// ---------------------------------------------------------------------------
|
|
130
141
|
/**
|
|
131
|
-
*
|
|
142
|
+
* Determine which tools should stay active after blocking read tools.
|
|
132
143
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* - Write commands → executed directly
|
|
136
|
-
* - Ambiguous → user is prompted
|
|
144
|
+
* We MUST keep the 'subagent' tool (registered by pi-subagents) active;
|
|
145
|
+
* otherwise the orchestrator cannot call the reader at all.
|
|
137
146
|
*/
|
|
138
|
-
function
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return undefined; // undefined = proceed normally
|
|
147
|
-
const p = params;
|
|
148
|
-
const command = typeof p.command === "string" ? p.command : "";
|
|
149
|
-
if (!command)
|
|
150
|
-
return undefined; // Let the tool handle the error
|
|
151
|
-
// Classify the command
|
|
152
|
-
if ((0, bash_filter_1.isWriteCommand)(command)) {
|
|
153
|
-
// Let the raw bash/shell tool execute this directly
|
|
154
|
-
return undefined; // undefined → Pi runs the original tool
|
|
155
|
-
}
|
|
156
|
-
if ((0, bash_filter_1.isReadCommand)(command)) {
|
|
157
|
-
// Forward to Reader subagent
|
|
158
|
-
(0, ui_1.log)("reader_calling", command);
|
|
159
|
-
try {
|
|
160
|
-
const result = await (0, reader_manager_1.callReader)(agent, config, (0, bash_filter_1.wrapForReader)(command));
|
|
161
|
-
(0, ui_1.log)("reader_done");
|
|
162
|
-
// Return the result directly — Pi will use this as the tool output
|
|
163
|
-
// instead of running the original bash command.
|
|
164
|
-
return { result, subagent_used: true };
|
|
165
|
-
}
|
|
166
|
-
catch (err) {
|
|
167
|
-
(0, ui_1.logError)("reader_failed", String(err));
|
|
168
|
-
// Offer the [R/A/C] dialog
|
|
169
|
-
try {
|
|
170
|
-
const handled = await (0, reader_manager_1.handleReaderError)(agent, config, config.blocked_tools, err, (0, bash_filter_1.wrapForReader)(command), agent.promptUser);
|
|
171
|
-
// If "Allow once" was selected, return a special marker
|
|
172
|
-
if (handled.startsWith("[ALLOW_ONCE]")) {
|
|
173
|
-
return { result: handled, allow_once: true };
|
|
174
|
-
}
|
|
175
|
-
// Retry succeeded — return the result
|
|
176
|
-
return { result: handled, subagent_used: true };
|
|
177
|
-
}
|
|
178
|
-
catch (finalErr) {
|
|
179
|
-
(0, ui_1.updateStatusBar)("error");
|
|
180
|
-
return {
|
|
181
|
-
error: true,
|
|
182
|
-
message: finalErr instanceof Error
|
|
183
|
-
? finalErr.message
|
|
184
|
-
: "Reader failed",
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
// Ambiguous command → ask user
|
|
190
|
-
const answer = await agent.promptUser(`The command "${command}" may read files. Run via Reader? [Y/n]`);
|
|
191
|
-
if (answer.trim().toLowerCase() === "n" ||
|
|
192
|
-
answer.trim().toLowerCase() === "no") {
|
|
193
|
-
// Let the original tool run
|
|
194
|
-
return undefined;
|
|
195
|
-
}
|
|
196
|
-
// Forward to Reader
|
|
197
|
-
(0, ui_1.log)("reader_calling", command);
|
|
198
|
-
try {
|
|
199
|
-
const result = await (0, reader_manager_1.callReader)(agent, config, (0, bash_filter_1.wrapForReader)(command));
|
|
200
|
-
(0, ui_1.log)("reader_done");
|
|
201
|
-
return { result, subagent_used: true };
|
|
202
|
-
}
|
|
203
|
-
catch (err) {
|
|
204
|
-
(0, ui_1.logError)("reader_failed", String(err));
|
|
205
|
-
return {
|
|
206
|
-
error: true,
|
|
207
|
-
message: err instanceof Error ? err.message : "Reader failed",
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
catch {
|
|
213
|
-
// onBeforeToolCall not supported for this tool — no-op
|
|
214
|
-
}
|
|
215
|
-
}
|
|
147
|
+
function computeActiveTools(pi, blocked) {
|
|
148
|
+
const all = pi.getAllTools();
|
|
149
|
+
const blockedSet = new Set(blocked);
|
|
150
|
+
// Always keep "subagent" --- it is the bridge to the reader.
|
|
151
|
+
const forceKeep = new Set(["subagent"]);
|
|
152
|
+
return all
|
|
153
|
+
.map((t) => t.name)
|
|
154
|
+
.filter((name) => forceKeep.has(name) || !blockedSet.has(name));
|
|
216
155
|
}
|
|
217
156
|
// ---------------------------------------------------------------------------
|
|
218
|
-
//
|
|
157
|
+
// Extension entry
|
|
219
158
|
// ---------------------------------------------------------------------------
|
|
220
|
-
function
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
159
|
+
async function default_1(pi) {
|
|
160
|
+
const config = loadConfig();
|
|
161
|
+
// --- ALL actions go inside events. Factory body is registration-only. ---
|
|
162
|
+
// Inject orchestrator system prompt
|
|
163
|
+
pi.on("before_agent_start", async (event, _ctx) => {
|
|
164
|
+
if (!config.enabled)
|
|
165
|
+
return;
|
|
166
|
+
return {
|
|
167
|
+
systemPrompt: event.systemPrompt + "\n\n" + config.orchestrator_prompt,
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
// Block tools and set status bar AFTER runtime is ready
|
|
171
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
172
|
+
if (config.enabled) {
|
|
173
|
+
pi.setActiveTools(computeActiveTools(pi, config.blocked_tools));
|
|
174
|
+
}
|
|
175
|
+
ctx.ui.setStatus("read-delegator", config.enabled ? "● reader: " + config.reader_subagent_name : undefined);
|
|
176
|
+
});
|
|
177
|
+
// Intercept bash read commands
|
|
178
|
+
pi.on("tool_call", async (event, _ctx) => {
|
|
179
|
+
if (!config.enabled)
|
|
180
|
+
return;
|
|
181
|
+
if (event.toolName === "bash" || event.toolName === "shell") {
|
|
182
|
+
const command = String(event.input?.command ?? "");
|
|
183
|
+
const firstWord = command.trim().split(/\s+/)[0]?.toLowerCase() ?? "";
|
|
184
|
+
if (READ_BASH_COMMANDS.has(firstWord)) {
|
|
185
|
+
return {
|
|
186
|
+
block: true,
|
|
187
|
+
reason: [
|
|
188
|
+
'Use subagent(agent: "' +
|
|
189
|
+
config.reader_subagent_name +
|
|
190
|
+
'", task: "Execute and summarize: ' +
|
|
191
|
+
command +
|
|
192
|
+
'")',
|
|
193
|
+
"instead of running file-reading commands directly.",
|
|
194
|
+
].join(" "),
|
|
195
|
+
};
|
|
233
196
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
// Register /read-delegator command
|
|
200
|
+
pi.registerCommand("read-delegator", {
|
|
201
|
+
description: "Manage read delegation (on|off|status)",
|
|
202
|
+
handler: async (args, ctx) => {
|
|
203
|
+
const sub = args?.trim().toLowerCase() ?? "status";
|
|
204
|
+
switch (sub) {
|
|
205
|
+
case "on":
|
|
206
|
+
case "enable": {
|
|
207
|
+
config.enabled = true;
|
|
208
|
+
saveConfig(config);
|
|
209
|
+
pi.setActiveTools(computeActiveTools(pi, config.blocked_tools));
|
|
210
|
+
ctx.ui.notify("🟢 Read delegation enabled", "info");
|
|
211
|
+
ctx.ui.setStatus("read-delegator", "● reader: " + config.reader_subagent_name);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
case "off":
|
|
215
|
+
case "disable": {
|
|
237
216
|
config.enabled = false;
|
|
238
|
-
|
|
217
|
+
saveConfig(config);
|
|
218
|
+
pi.setActiveTools(pi.getAllTools().map((t) => t.name));
|
|
219
|
+
ctx.ui.notify("🔴 Read delegation disabled", "info");
|
|
220
|
+
ctx.ui.setStatus("read-delegator", undefined);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
case "status":
|
|
224
|
+
default: {
|
|
225
|
+
const lines = [
|
|
226
|
+
"Read delegation: " +
|
|
227
|
+
(config.enabled ? "🟢 enabled" : "🔴 disabled"),
|
|
228
|
+
"Blocked tools: " + config.blocked_tools.join(", "),
|
|
229
|
+
"Reader subagent: " + config.reader_subagent_name,
|
|
230
|
+
];
|
|
231
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
232
|
+
return;
|
|
239
233
|
}
|
|
240
|
-
doDisable(agent);
|
|
241
|
-
return (0, ui_1.msg)("disabled");
|
|
242
|
-
}
|
|
243
|
-
case "status": {
|
|
244
|
-
const status = (0, ui_1.getStatus)();
|
|
245
|
-
const blocked = (0, tool_blocker_1.getBlockedTools)();
|
|
246
|
-
return (`pi-read-delegator is ${status}\n` +
|
|
247
|
-
`Enabled: ${enabled ? "yes" : "no"}\n` +
|
|
248
|
-
`Blocked tools: ${blocked.join(", ") || "(none)"}\n` +
|
|
249
|
-
`Reader subagent: ${config?.reader_subagent_name ?? "reader"}\n` +
|
|
250
|
-
`Language: ${config?.language ?? "auto"}`);
|
|
251
234
|
}
|
|
252
|
-
|
|
253
|
-
return ("Usage:\n" +
|
|
254
|
-
" /read-delegator on — enable read delegation\n" +
|
|
255
|
-
" /read-delegator off — disable read delegation\n" +
|
|
256
|
-
" /read-delegator status — show current status");
|
|
257
|
-
}
|
|
235
|
+
},
|
|
258
236
|
});
|
|
237
|
+
// Ensure reader.md template (independent I/O — no Pi API call)
|
|
238
|
+
await ensureReaderTemplate();
|
|
259
239
|
}
|
|
260
240
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED