@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.
@@ -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
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- var desc = Object.getOwnPropertyDescriptor(m, k);
17
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
- desc = { enumerable: true, get: function() { return m[k]; } };
19
- }
20
- Object.defineProperty(o, k2, desc);
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: Token-efficient code reader that returns minimal results.",
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
- "You are a token-efficient analyst. Execute read/search/list tasks and return",
102
- "ONLY the essential result. Maximum 10 lines. Use bullet summaries.",
103
- "Never dump entire files. Focus only on what was asked.",
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
- (0, config_1.saveConfig)(config, { silent: true });
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((0, ui_1.msg)("deps_installing") +
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", (0, ui_1.msg)("status_active"));
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", (0, ui_1.msg)("status_error"));
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 (0, reader_manager_1.checkDependencies)(promptFn, createProgressCallback(ctx));
143
+ const ready = await checkDependencies(promptFn, createProgressCallback(ctx));
183
144
  if (!ready) {
184
145
  config.enabled = false;
185
- (0, config_1.saveConfig)(config, { silent: true });
186
- ctx.ui.setStatus("read-delegator", (0, ui_1.msg)("status_error"));
187
- ctx.ui.notify((0, ui_1.msg)("deps_disabled"), "warning");
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
- (0, config_1.saveConfig)(config, { silent: true });
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 default_1(pi) {
200
- const config = (0, config_1.loadConfig)();
160
+ export default async function (pi) {
161
+ const config = loadConfig();
201
162
  // Detect language
202
- (0, ui_1.getLanguage)(config.language);
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 = (0, reader_manager_1.isSubagentsInstalled)();
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 ? (0, ui_1.msg)("status_active") : (0, ui_1.msg)("status_off"));
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
- const firstWord = command.trim().split(/\s+/)[0]?.toLowerCase() ?? "";
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. /read-delegator command
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: "Manage read delegation (on|off|status|model)",
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 raw = args?.trim() ?? "";
269
- const spaceIdx = raw.indexOf(" ");
270
- const sub = spaceIdx >= 0
271
- ? raw.slice(0, spaceIdx).toLowerCase()
272
- : raw.toLowerCase() || "status";
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
- /** Progress callback for dependency installation. */
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. */