@stfade/pi-read-delegator 1.0.12 → 1.0.13
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 +178 -69
- package/{bash-filter.d.ts → dist/bash-filter.d.ts} +16 -6
- package/dist/bash-filter.js +420 -0
- package/{config.d.ts → dist/config.d.ts} +0 -1
- package/{config.js → dist/config.js} +34 -59
- package/{index.js → dist/index.js} +111 -149
- package/{reader-manager.d.ts → dist/reader-manager.d.ts} +26 -1
- package/{reader-manager.js → dist/reader-manager.js} +127 -72
- package/{tool-blocker.js → dist/tool-blocker.js} +13 -21
- package/{ui.js → dist/ui.js} +15 -62
- package/package.json +20 -17
- package/bash-filter.js +0 -242
- package/templates/reader.md +0 -8
- /package/{index.d.ts → dist/index.d.ts} +0 -0
- /package/{tool-blocker.d.ts → dist/tool-blocker.d.ts} +0 -0
- /package/{ui.d.ts → dist/ui.d.ts} +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* index.ts — pi-read-delegator entry point
|
|
4
3
|
*
|
|
@@ -11,74 +10,13 @@
|
|
|
11
10
|
* - before_agent_start: inject orchestrator system prompt
|
|
12
11
|
* - tool_call: intercept bash read commands → redirect to reader
|
|
13
12
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}) : (function(o, m, k, k2) {
|
|
22
|
-
if (k2 === undefined) k2 = k;
|
|
23
|
-
o[k2] = m[k];
|
|
24
|
-
}));
|
|
25
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
-
}) : function(o, v) {
|
|
28
|
-
o["default"] = v;
|
|
29
|
-
});
|
|
30
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
-
var ownKeys = function(o) {
|
|
32
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
-
var ar = [];
|
|
34
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
-
return ar;
|
|
36
|
-
};
|
|
37
|
-
return ownKeys(o);
|
|
38
|
-
};
|
|
39
|
-
return function (mod) {
|
|
40
|
-
if (mod && mod.__esModule) return mod;
|
|
41
|
-
var result = {};
|
|
42
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
-
__setModuleDefault(result, mod);
|
|
44
|
-
return result;
|
|
45
|
-
};
|
|
46
|
-
})();
|
|
47
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
-
exports.default = default_1;
|
|
49
|
-
const fs = __importStar(require("node:fs"));
|
|
50
|
-
const os = __importStar(require("node:os"));
|
|
51
|
-
const path = __importStar(require("node:path"));
|
|
52
|
-
const config_1 = require("./config");
|
|
53
|
-
const reader_manager_1 = require("./reader-manager");
|
|
54
|
-
const ui_1 = require("./ui");
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
// Bash read commands — these are intercepted and redirected to reader
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
|
-
const READ_BASH_COMMANDS = new Set([
|
|
59
|
-
"cat",
|
|
60
|
-
"grep",
|
|
61
|
-
"find",
|
|
62
|
-
"ls",
|
|
63
|
-
"head",
|
|
64
|
-
"tail",
|
|
65
|
-
"less",
|
|
66
|
-
"wc",
|
|
67
|
-
"nl",
|
|
68
|
-
"more",
|
|
69
|
-
"bat",
|
|
70
|
-
"rg",
|
|
71
|
-
"fd",
|
|
72
|
-
"awk",
|
|
73
|
-
"du",
|
|
74
|
-
"df",
|
|
75
|
-
"stat",
|
|
76
|
-
"file",
|
|
77
|
-
"which",
|
|
78
|
-
"where",
|
|
79
|
-
"type",
|
|
80
|
-
"dir",
|
|
81
|
-
]);
|
|
13
|
+
import * as fs from "node:fs";
|
|
14
|
+
import * as os from "node:os";
|
|
15
|
+
import * as path from "node:path";
|
|
16
|
+
import { loadConfig, saveConfig } from "./config";
|
|
17
|
+
import { checkDependencies, isSubagentsInstalled, sessionCache, } from "./reader-manager";
|
|
18
|
+
import { getLanguage, msg } from "./ui";
|
|
19
|
+
import { isReadCommand } from "./bash-filter";
|
|
82
20
|
// ---------------------------------------------------------------------------
|
|
83
21
|
// Reader template path
|
|
84
22
|
// ---------------------------------------------------------------------------
|
|
@@ -93,14 +31,37 @@ function syncReaderTemplate(model) {
|
|
|
93
31
|
const content = [
|
|
94
32
|
"---",
|
|
95
33
|
"name: reader",
|
|
96
|
-
"description:
|
|
34
|
+
"description: Compact code-reader — executes tasks, returns results with line numbers",
|
|
97
35
|
"tools: read, grep, find, ls",
|
|
98
36
|
`model: ${model}`,
|
|
99
37
|
"---",
|
|
100
38
|
"",
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
39
|
+
"Execute the task. Return only the result, nothing else.",
|
|
40
|
+
"Always include line numbers for grep and read results.",
|
|
41
|
+
"No explanations, summaries, or conversational text.",
|
|
42
|
+
"",
|
|
43
|
+
"### Structure masks (auto-apply to code output)",
|
|
44
|
+
"- Skip import statements unless task explicitly mentions them.",
|
|
45
|
+
"- Collapse long type annotations: `Record<string, string>[]` → `Record<...>[]`.",
|
|
46
|
+
"- Truncate paths: `C:/Users/samet/Documents/Projects/pi-read-delegator/src/` → `src/`.",
|
|
47
|
+
"",
|
|
48
|
+
"### Stats-first for grep / find",
|
|
49
|
+
"- grep: show total match count on line 1, then matches. Large result sets (>20): only count.",
|
|
50
|
+
"- find: show file count first, then list. >50 files: only count.",
|
|
51
|
+
"- ls: show file count first, then list.",
|
|
52
|
+
"",
|
|
53
|
+
"### Smart filtering",
|
|
54
|
+
"- Skip node_modules, .git, dist, .next, coverage, __pycache__ unless task specifies a path inside.",
|
|
55
|
+
"- Skip binary files (images, .exe, .dll, .zip, .db) — return '(binary)'.",
|
|
56
|
+
"- Deduplicate: if same file appears in multiple grep matches, show it once with all line numbers.",
|
|
57
|
+
"",
|
|
58
|
+
"### Output format (no markdown headers)",
|
|
59
|
+
"grep: src/file.ts:42 matched line",
|
|
60
|
+
"read: 42: line content",
|
|
61
|
+
"find: file list, one per line",
|
|
62
|
+
"ls: name size",
|
|
63
|
+
"No matches: (no matches)",
|
|
64
|
+
"Error: Error: <message>",
|
|
104
65
|
].join("\n");
|
|
105
66
|
fs.writeFileSync(rp, content, "utf8");
|
|
106
67
|
}
|
|
@@ -131,7 +92,7 @@ async function pickReaderModel(config, ctx) {
|
|
|
131
92
|
const selected = await ctx.ui.select("Choose reader model (ESC to keep current)", options);
|
|
132
93
|
if (selected && selected !== config.reader_model) {
|
|
133
94
|
config.reader_model = selected;
|
|
134
|
-
|
|
95
|
+
saveConfig(config, { silent: true });
|
|
135
96
|
syncReaderTemplate(selected);
|
|
136
97
|
ctx.ui.notify("✅ Reader model set to: " + selected, "info");
|
|
137
98
|
return selected;
|
|
@@ -162,15 +123,15 @@ function createProgressCallback(ctx) {
|
|
|
162
123
|
return (status) => {
|
|
163
124
|
if (status === "installing") {
|
|
164
125
|
ctx.ui.setStatus("read-delegator", "⏳ Installing pi-subagents…");
|
|
165
|
-
ctx.ui.notify(
|
|
126
|
+
ctx.ui.notify(msg("deps_installing") +
|
|
166
127
|
" This may take up to 60 seconds. Please wait…", "info");
|
|
167
128
|
}
|
|
168
129
|
else if (status === "done") {
|
|
169
|
-
ctx.ui.setStatus("read-delegator",
|
|
130
|
+
ctx.ui.setStatus("read-delegator", msg("status_active"));
|
|
170
131
|
ctx.ui.notify("✅ pi-subagents installed successfully.", "info");
|
|
171
132
|
}
|
|
172
133
|
else if (status === "failed") {
|
|
173
|
-
ctx.ui.setStatus("read-delegator",
|
|
134
|
+
ctx.ui.setStatus("read-delegator", msg("status_error"));
|
|
174
135
|
}
|
|
175
136
|
};
|
|
176
137
|
}
|
|
@@ -179,30 +140,30 @@ async function performDependencyCheck(ctx, config) {
|
|
|
179
140
|
const ok = await ctx.ui.confirm("pi-subagents required", message);
|
|
180
141
|
return ok ? "y" : "n";
|
|
181
142
|
};
|
|
182
|
-
const ready = await
|
|
143
|
+
const ready = await checkDependencies(promptFn, createProgressCallback(ctx));
|
|
183
144
|
if (!ready) {
|
|
184
145
|
config.enabled = false;
|
|
185
|
-
|
|
186
|
-
ctx.ui.setStatus("read-delegator",
|
|
187
|
-
ctx.ui.notify(
|
|
146
|
+
saveConfig(config, { silent: true });
|
|
147
|
+
ctx.ui.setStatus("read-delegator", msg("status_error"));
|
|
148
|
+
ctx.ui.notify(msg("deps_disabled"), "warning");
|
|
188
149
|
return false;
|
|
189
150
|
}
|
|
190
151
|
if (!config.enabled) {
|
|
191
152
|
config.enabled = true;
|
|
192
|
-
|
|
153
|
+
saveConfig(config, { silent: true });
|
|
193
154
|
}
|
|
194
155
|
return true;
|
|
195
156
|
}
|
|
196
157
|
// ---------------------------------------------------------------------------
|
|
197
158
|
// Extension factory — registration only; actions go inside events
|
|
198
159
|
// ---------------------------------------------------------------------------
|
|
199
|
-
async function
|
|
200
|
-
const config =
|
|
160
|
+
export default async function (pi) {
|
|
161
|
+
const config = loadConfig();
|
|
201
162
|
// Detect language
|
|
202
|
-
|
|
163
|
+
getLanguage(config.language);
|
|
203
164
|
// Quick sync dependency check — interactive prompt is deferred to
|
|
204
165
|
// session_start where we have access to ctx.ui.confirm().
|
|
205
|
-
let depsReady =
|
|
166
|
+
let depsReady = isSubagentsInstalled();
|
|
206
167
|
let depsChecked = depsReady; // if already ready, no need to check again
|
|
207
168
|
// -----------------------------------------------------------------------
|
|
208
169
|
// 1. session_start: dependency check → tool blocking → status bar
|
|
@@ -221,7 +182,7 @@ async function default_1(pi) {
|
|
|
221
182
|
if (config.enabled) {
|
|
222
183
|
pi.setActiveTools(computeActiveTools(pi, config.blocked_tools));
|
|
223
184
|
}
|
|
224
|
-
ctx.ui.setStatus("read-delegator", config.enabled ?
|
|
185
|
+
ctx.ui.setStatus("read-delegator", config.enabled ? msg("status_active") : msg("status_off"));
|
|
225
186
|
});
|
|
226
187
|
// -----------------------------------------------------------------------
|
|
227
188
|
// 2. before_agent_start: inject orchestrator system prompt
|
|
@@ -243,8 +204,7 @@ async function default_1(pi) {
|
|
|
243
204
|
const command = String(event.input?.command ?? "");
|
|
244
205
|
if (!command)
|
|
245
206
|
return;
|
|
246
|
-
|
|
247
|
-
if (READ_BASH_COMMANDS.has(firstWord)) {
|
|
207
|
+
if (isReadCommand(command)) {
|
|
248
208
|
return {
|
|
249
209
|
block: true,
|
|
250
210
|
reason: [
|
|
@@ -260,71 +220,73 @@ async function default_1(pi) {
|
|
|
260
220
|
}
|
|
261
221
|
});
|
|
262
222
|
// -----------------------------------------------------------------------
|
|
263
|
-
// 4.
|
|
223
|
+
// 4. Commands
|
|
264
224
|
// -----------------------------------------------------------------------
|
|
225
|
+
// Shared status helper
|
|
226
|
+
const showStatus = (ctx) => {
|
|
227
|
+
const s = sessionCache.stats();
|
|
228
|
+
const lines = [
|
|
229
|
+
"Read delegation: " + (config.enabled ? "🟢 enabled" : "🔴 disabled"),
|
|
230
|
+
"Reader subagent: " + config.reader_subagent_name,
|
|
231
|
+
"Reader model: " + config.reader_model,
|
|
232
|
+
"Dependencies: " + (depsReady ? "✅ ready" : "❌ missing"),
|
|
233
|
+
"Blocked tools: " + config.blocked_tools.join(", "),
|
|
234
|
+
"Cache: " + s.files + " files (" + s.sizeKB + " KB)",
|
|
235
|
+
];
|
|
236
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
237
|
+
};
|
|
238
|
+
// Shortcut: enable/disable toggle with subcommand syntax (kept for back compat)
|
|
265
239
|
pi.registerCommand("read-delegator", {
|
|
266
|
-
description: "
|
|
240
|
+
description: "Show read-delegator status",
|
|
241
|
+
handler: async (_args, ctx) => {
|
|
242
|
+
showStatus(ctx);
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
pi.registerCommand("read-delegator-status", {
|
|
246
|
+
description: "Show read-delegator status",
|
|
247
|
+
handler: async (_args, ctx) => {
|
|
248
|
+
showStatus(ctx);
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
pi.registerCommand("read-delegator-on", {
|
|
252
|
+
description: "Enable read delegation",
|
|
253
|
+
handler: async (_args, ctx) => {
|
|
254
|
+
if (!depsReady) {
|
|
255
|
+
ctx.ui.notify("pi-subagents not installed. Install it first to enable read delegation.", "warning");
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
config.enabled = true;
|
|
259
|
+
saveConfig(config, { silent: true });
|
|
260
|
+
pi.setActiveTools(computeActiveTools(pi, config.blocked_tools));
|
|
261
|
+
ctx.ui.notify(msg("enabled"), "info");
|
|
262
|
+
ctx.ui.setStatus("read-delegator", msg("status_active"));
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
pi.registerCommand("read-delegator-off", {
|
|
266
|
+
description: "Disable read delegation",
|
|
267
|
+
handler: async (_args, ctx) => {
|
|
268
|
+
config.enabled = false;
|
|
269
|
+
saveConfig(config, { silent: true });
|
|
270
|
+
pi.setActiveTools(pi.getAllTools().map((t) => t.name));
|
|
271
|
+
ctx.ui.notify(msg("disabled"), "info");
|
|
272
|
+
ctx.ui.setStatus("read-delegator", msg("status_off"));
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
pi.registerCommand("read-delegator-model", {
|
|
276
|
+
description: "Set or view the reader model",
|
|
267
277
|
handler: async (args, ctx) => {
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
switch (sub) {
|
|
274
|
-
case "on":
|
|
275
|
-
case "enable": {
|
|
276
|
-
if (!depsReady) {
|
|
277
|
-
ctx.ui.notify("pi-subagents not installed. Install it first to enable read delegation.", "warning");
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
config.enabled = true;
|
|
281
|
-
(0, config_1.saveConfig)(config, { silent: true });
|
|
282
|
-
pi.setActiveTools(computeActiveTools(pi, config.blocked_tools));
|
|
283
|
-
ctx.ui.notify((0, ui_1.msg)("enabled"), "info");
|
|
284
|
-
ctx.ui.setStatus("read-delegator", (0, ui_1.msg)("status_active"));
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
case "off":
|
|
288
|
-
case "disable": {
|
|
289
|
-
config.enabled = false;
|
|
290
|
-
(0, config_1.saveConfig)(config, { silent: true });
|
|
291
|
-
pi.setActiveTools(pi.getAllTools().map((t) => t.name));
|
|
292
|
-
ctx.ui.notify((0, ui_1.msg)("disabled"), "info");
|
|
293
|
-
ctx.ui.setStatus("read-delegator", (0, ui_1.msg)("status_off"));
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
case "model": {
|
|
297
|
-
const modelArg = spaceIdx >= 0 ? raw.slice(spaceIdx + 1).trim() : "";
|
|
298
|
-
if (!modelArg) {
|
|
299
|
-
// Interactive picker via available models
|
|
300
|
-
const picked = await pickReaderModel(config, ctx);
|
|
301
|
-
if (picked === undefined) {
|
|
302
|
-
ctx.ui.notify("Current reader model: " +
|
|
303
|
-
config.reader_model +
|
|
304
|
-
"\n\nChange: /read-delegator model <provider/model>", "info");
|
|
305
|
-
}
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
config.reader_model = modelArg;
|
|
309
|
-
(0, config_1.saveConfig)(config, { silent: true });
|
|
310
|
-
syncReaderTemplate(modelArg);
|
|
311
|
-
ctx.ui.notify("✅ Reader model set to: " + modelArg, "info");
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
case "status":
|
|
315
|
-
default: {
|
|
316
|
-
const lines = [
|
|
317
|
-
"Read delegation: " +
|
|
318
|
-
(config.enabled ? "🟢 enabled" : "🔴 disabled"),
|
|
319
|
-
"Reader subagent: " + config.reader_subagent_name,
|
|
320
|
-
"Reader model: " + config.reader_model,
|
|
321
|
-
"Dependencies: " + (depsReady ? "✅ ready" : "❌ missing"),
|
|
322
|
-
"Blocked tools: " + config.blocked_tools.join(", "),
|
|
323
|
-
];
|
|
324
|
-
ctx.ui.notify(lines.join("\n"), "info");
|
|
325
|
-
return;
|
|
278
|
+
const modelArg = args?.trim() ?? "";
|
|
279
|
+
if (!modelArg) {
|
|
280
|
+
// Interactive picker via available models
|
|
281
|
+
const picked = await pickReaderModel(config, ctx);
|
|
282
|
+
if (picked === undefined) {
|
|
326
283
|
}
|
|
284
|
+
return;
|
|
327
285
|
}
|
|
286
|
+
config.reader_model = modelArg;
|
|
287
|
+
saveConfig(config, { silent: true });
|
|
288
|
+
syncReaderTemplate(modelArg);
|
|
289
|
+
ctx.ui.notify("✅ Reader model set to: " + modelArg, "info");
|
|
328
290
|
},
|
|
329
291
|
});
|
|
330
292
|
// 5. Background: sync reader.md template from config
|
|
@@ -9,7 +9,32 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { ReadDelegatorConfig } from "./config";
|
|
11
11
|
import type { ExtensionAgent } from "./tool-blocker";
|
|
12
|
-
|
|
12
|
+
export declare class SessionFileCache {
|
|
13
|
+
private files;
|
|
14
|
+
private readRanges;
|
|
15
|
+
/** Cache a file's content with a hash for change detection. */
|
|
16
|
+
set(path: string, content: string): void;
|
|
17
|
+
get(path: string): string | undefined;
|
|
18
|
+
has(path: string): boolean;
|
|
19
|
+
getHash(path: string): string | undefined;
|
|
20
|
+
/** Record that a specific line range of a path has been read. */
|
|
21
|
+
markReadRange(path: string, from: number, to: number): void;
|
|
22
|
+
getReadRanges(path: string): Array<[number, number]>;
|
|
23
|
+
/** Compute gaps (unread line ranges) given total line count. */
|
|
24
|
+
getUnreadRanges(path: string, totalLines: number): Array<[number, number]>;
|
|
25
|
+
/** Return cache statistics for the status command. */
|
|
26
|
+
stats(): {
|
|
27
|
+
files: number;
|
|
28
|
+
sizeKB: number;
|
|
29
|
+
ranges: number;
|
|
30
|
+
};
|
|
31
|
+
clear(): void;
|
|
32
|
+
}
|
|
33
|
+
/** Singleton cache instance shared across the extension session. */
|
|
34
|
+
export declare const sessionCache: SessionFileCache;
|
|
35
|
+
/**
|
|
36
|
+
* Progress callback for dependency installation.
|
|
37
|
+
*/
|
|
13
38
|
export type InstallProgress = "installing" | "done" | "failed";
|
|
14
39
|
export interface AgentWithSubagent extends ExtensionAgent {
|
|
15
40
|
/** Call a subagent by name with a task string. Returns the subagent's response. */
|