@stfade/pi-read-delegator 1.0.10 → 1.0.11

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.js CHANGED
@@ -124,6 +124,44 @@ function computeActiveTools(pi, blocked) {
124
124
  .filter((name) => forceKeep.has(name) || !blockedSet.has(name));
125
125
  }
126
126
  // ---------------------------------------------------------------------------
127
+ // Dependency check helpers — extracted to keep session_start handler lean
128
+ // ---------------------------------------------------------------------------
129
+ function createProgressCallback(ctx) {
130
+ return (status) => {
131
+ if (status === "installing") {
132
+ ctx.ui.setStatus("read-delegator", "⏳ Installing pi-subagents…");
133
+ ctx.ui.notify((0, ui_1.msg)("deps_installing") +
134
+ " This may take up to 60 seconds. Please wait…", "info");
135
+ }
136
+ else if (status === "done") {
137
+ ctx.ui.setStatus("read-delegator", (0, ui_1.msg)("status_active"));
138
+ ctx.ui.notify("✅ pi-subagents installed successfully.", "info");
139
+ }
140
+ else if (status === "failed") {
141
+ ctx.ui.setStatus("read-delegator", (0, ui_1.msg)("status_error"));
142
+ }
143
+ };
144
+ }
145
+ async function performDependencyCheck(ctx, config) {
146
+ const promptFn = async (message) => {
147
+ const ok = await ctx.ui.confirm("pi-subagents required", message);
148
+ return ok ? "y" : "n";
149
+ };
150
+ const ready = await (0, reader_manager_1.checkDependencies)(promptFn, createProgressCallback(ctx));
151
+ if (!ready) {
152
+ config.enabled = false;
153
+ (0, config_1.saveConfig)(config, { silent: true });
154
+ ctx.ui.setStatus("read-delegator", (0, ui_1.msg)("status_error"));
155
+ ctx.ui.notify((0, ui_1.msg)("deps_disabled"), "warning");
156
+ return false;
157
+ }
158
+ if (!config.enabled) {
159
+ config.enabled = true;
160
+ (0, config_1.saveConfig)(config, { silent: true });
161
+ }
162
+ return true;
163
+ }
164
+ // ---------------------------------------------------------------------------
127
165
  // Extension factory — registration only; actions go inside events
128
166
  // ---------------------------------------------------------------------------
129
167
  async function default_1(pi) {
@@ -140,27 +178,10 @@ async function default_1(pi) {
140
178
  pi.on("session_start", async (_event, ctx) => {
141
179
  // --- Dependency check ---
142
180
  if (!depsChecked) {
143
- // Build a prompt function backed by ctx.ui.confirm()
144
- const promptFn = async (message) => {
145
- const ok = await ctx.ui.confirm("pi-subagents required", message);
146
- return ok ? "y" : "n";
147
- };
148
- depsReady = await (0, reader_manager_1.checkDependencies)(promptFn);
181
+ depsReady = await performDependencyCheck(ctx, config);
149
182
  depsChecked = true;
150
- if (!depsReady) {
151
- // Dependency missing and user declined / install failed
152
- config.enabled = false;
153
- (0, config_1.saveConfig)(config, { silent: true });
154
- ctx.ui.setStatus("read-delegator", (0, ui_1.msg)("status_error"));
155
- ctx.ui.notify((0, ui_1.msg)("deps_disabled"), "warning");
183
+ if (!depsReady)
156
184
  return;
157
- }
158
- // If deps are now ready, re-enable config in case user previously
159
- // declined but now installed manually before session start.
160
- if (!config.enabled) {
161
- config.enabled = true;
162
- (0, config_1.saveConfig)(config, { silent: true });
163
- }
164
185
  }
165
186
  // --- Tool blocking ---
166
187
  if (config.enabled) {
@@ -171,7 +192,7 @@ async function default_1(pi) {
171
192
  // -----------------------------------------------------------------------
172
193
  // 2. before_agent_start: inject orchestrator system prompt
173
194
  // -----------------------------------------------------------------------
174
- pi.on("before_agent_start", async (event, _ctx) => {
195
+ pi.on("before_agent_start", (event, _ctx) => {
175
196
  if (!config.enabled)
176
197
  return;
177
198
  return {
@@ -181,7 +202,7 @@ async function default_1(pi) {
181
202
  // -----------------------------------------------------------------------
182
203
  // 3. tool_call: intercept bash read commands
183
204
  // -----------------------------------------------------------------------
184
- pi.on("tool_call", async (event, _ctx) => {
205
+ pi.on("tool_call", (event, _ctx) => {
185
206
  if (!config.enabled)
186
207
  return;
187
208
  if (event.toolName === "bash" || event.toolName === "shell") {
@@ -209,7 +230,7 @@ async function default_1(pi) {
209
230
  // -----------------------------------------------------------------------
210
231
  pi.registerCommand("read-delegator", {
211
232
  description: "Manage read delegation (on|off|status)",
212
- handler: async (args, ctx) => {
233
+ handler: (args, ctx) => {
213
234
  const sub = args?.trim().toLowerCase() ?? "status";
214
235
  switch (sub) {
215
236
  case "on":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stfade/pi-read-delegator",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "Pi extension that delegates all read operations to a Reader subagent, blocking read tools from the main model",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -9,6 +9,8 @@
9
9
  */
10
10
  import type { ReadDelegatorConfig } from "./config";
11
11
  import type { ExtensionAgent } from "./tool-blocker";
12
+ /** Progress callback for dependency installation. */
13
+ export type InstallProgress = "installing" | "done" | "failed";
12
14
  export interface AgentWithSubagent extends ExtensionAgent {
13
15
  /** Call a subagent by name with a task string. Returns the subagent's response. */
14
16
  callSubagent(params: {
@@ -21,7 +23,7 @@ export declare class ReaderError extends Error {
21
23
  readonly originalError?: unknown | undefined;
22
24
  constructor(message: string, originalError?: unknown | undefined);
23
25
  }
24
- export declare function checkDependencies(prompt: (message: string) => Promise<string>): Promise<boolean>;
26
+ export declare function checkDependencies(prompt: (message: string) => Promise<string>, onProgress?: (status: InstallProgress) => void): Promise<boolean>;
25
27
  /**
26
28
  * Quick synchronous check: does pi-subagents exist on disk?
27
29
  */
package/reader-manager.js CHANGED
@@ -52,6 +52,7 @@ const fs = __importStar(require("fs"));
52
52
  const path = __importStar(require("path"));
53
53
  const os = __importStar(require("os"));
54
54
  const child_process_1 = require("child_process");
55
+ const ui_1 = require("./ui");
55
56
  /** Error thrown when the Reader subagent fails. */
56
57
  class ReaderError extends Error {
57
58
  originalError;
@@ -90,39 +91,43 @@ function piSubagentsDir() {
90
91
  function piSubagentsPackageJson() {
91
92
  return path.join(piSubagentsDir(), "package.json");
92
93
  }
93
- async function checkDependencies(prompt) {
94
+ async function checkDependencies(prompt, onProgress) {
94
95
  // Already installed — nothing to do
95
96
  if (fs.existsSync(piSubagentsPackageJson())) {
96
97
  return true;
97
98
  }
98
- console.warn("[pi-read-delegator] ⚠️ pi-subagents is not installed.");
99
+ (0, ui_1.rawWarn)("⚠️ pi-subagents is not installed.");
99
100
  const answer = await prompt("pi-subagents is not installed. Install it now? [Y/n]");
100
101
  const normalized = answer.trim().toLowerCase();
101
102
  if (normalized !== "" && normalized !== "y" && normalized !== "yes") {
102
- console.error("[pi-read-delegator] ❌ Cannot proceed without pi-subagents. Extension disabled.");
103
+ (0, ui_1.rawError)("❌ Cannot proceed without pi-subagents. Extension disabled.");
103
104
  return false;
104
105
  }
105
106
  // Attempt installation
106
107
  const piNpmDir = path.join(os.homedir(), ".pi", "agent", "npm");
107
- console.log("[pi-read-delegator] 📦 Installing pi-subagents to " + piNpmDir + "…");
108
+ (0, ui_1.rawLog)("📦 Installing pi-subagents to " + piNpmDir + "…");
109
+ onProgress?.("installing");
108
110
  try {
109
111
  (0, child_process_1.execSync)(`npm install --prefix "${piNpmDir}" pi-subagents`, {
110
112
  stdio: "pipe",
111
113
  timeout: 60_000,
112
114
  encoding: "utf-8",
113
115
  });
114
- console.log("[pi-read-delegator] ✅ pi-subagents installed via npm.");
116
+ (0, ui_1.rawLog)("✅ pi-subagents installed via npm.");
117
+ onProgress?.("done");
115
118
  }
116
119
  catch (firstErr) {
117
- console.error("[pi-read-delegator] ⚠️ npm install failed:", firstErr instanceof Error ? firstErr.message : String(firstErr));
118
- console.error("[pi-read-delegator] Cannot proceed without pi-subagents. Extension disabled.");
120
+ onProgress?.("failed");
121
+ (0, ui_1.rawError)("⚠️ npm install failed: " +
122
+ (firstErr instanceof Error ? firstErr.message : String(firstErr)));
123
+ (0, ui_1.rawError)("❌ Cannot proceed without pi-subagents. Extension disabled.");
119
124
  return false;
120
125
  }
121
126
  // Verify installation took effect
122
127
  if (fs.existsSync(piSubagentsPackageJson())) {
123
128
  return true;
124
129
  }
125
- console.error("[pi-read-delegator] ❌ pi-subagents installed but cannot be found at " +
130
+ (0, ui_1.rawError)("❌ pi-subagents installed but cannot be found at " +
126
131
  piSubagentsDir() +
127
132
  ". Restart Pi and try again.");
128
133
  return false;
@@ -149,19 +154,19 @@ function ensureReaderTemplate() {
149
154
  // Path to the bundled template (sibling to the compiled JS)
150
155
  const bundledPath = path.join(__dirname, "templates", "reader.md");
151
156
  if (!fs.existsSync(bundledPath)) {
152
- console.warn("[pi-read-delegator] ⚠️ Bundled reader template not found at:", bundledPath);
153
- console.warn("[pi-read-delegator] Please create ~/.pi/agent/agents/reader.md manually.");
157
+ (0, ui_1.rawWarn)("⚠️ Bundled reader template not found at: " + bundledPath);
158
+ (0, ui_1.rawWarn)("Please create ~/.pi/agent/agents/reader.md manually.");
154
159
  return false;
155
160
  }
156
161
  try {
157
162
  const content = fs.readFileSync(bundledPath, "utf-8");
158
163
  ensureDir(path.dirname(READER_TEMPLATE_PATH));
159
164
  fs.writeFileSync(READER_TEMPLATE_PATH, content, "utf-8");
160
- console.log(`[pi-read-delegator] Created reader subagent template: ${READER_TEMPLATE_PATH}`);
165
+ (0, ui_1.rawLog)(`✅ Created reader subagent template: ${READER_TEMPLATE_PATH}`);
161
166
  return true;
162
167
  }
163
168
  catch (err) {
164
- console.error("[pi-read-delegator] ⚠️ Failed to create reader template:", err);
169
+ (0, ui_1.rawError)("⚠️ Failed to create reader template: " + String(err));
165
170
  return false;
166
171
  }
167
172
  }
@@ -210,19 +215,19 @@ async function callReader(agent, config, task, timeoutMs = 30_000) {
210
215
  */
211
216
  async function handleReaderError(agent, config, blockedTools, error, task, prompt) {
212
217
  const errMsg = error instanceof Error ? error.message : String(error);
213
- console.error(`[pi-read-delegator] Reader failed: ${errMsg}`);
218
+ (0, ui_1.rawError)(`❌ Reader failed: ${errMsg}`);
214
219
  const answer = await prompt(`\n❌ Reader subagent failed: ${errMsg}\n` +
215
220
  `[R]etry [A]llow once (let main model do it) [C]ancel\n`);
216
221
  const choice = answer.trim().toLowerCase();
217
222
  if (choice === "r" || choice === "retry") {
218
223
  // Retry the same task
219
- console.log("[pi-read-delegator] 🔄 Retrying Reader…");
224
+ (0, ui_1.rawLog)("🔄 Retrying Reader…");
220
225
  return callReader(agent, config, task);
221
226
  }
222
227
  if (choice === "a" || choice === "allow" || choice === "allow once") {
223
228
  // Temporarily unblock tools, let main model execute the task,
224
229
  // then re-block.
225
- console.log("[pi-read-delegator] 🔓 Allowing main model to read once…");
230
+ (0, ui_1.rawLog)("🔓 Allowing main model to read once…");
226
231
  // NOTE: For "Allow once", we need the main model to perform the task.
227
232
  // However, we are inside a tool call — the main model can't run code
228
233
  // inline. We return a specially formatted string that instructs the
package/ui.d.ts CHANGED
@@ -56,4 +56,7 @@ export declare function logError(key: string, detail?: string): void;
56
56
  * Log a warning with the standard prefix.
57
57
  */
58
58
  export declare function logWarn(key: string, detail?: string): void;
59
+ export declare function rawLog(message: string): void;
60
+ export declare function rawWarn(message: string): void;
61
+ export declare function rawError(message: string): void;
59
62
  //# sourceMappingURL=ui.d.ts.map
package/ui.js CHANGED
@@ -48,6 +48,9 @@ exports.getStatus = getStatus;
48
48
  exports.log = log;
49
49
  exports.logError = logError;
50
50
  exports.logWarn = logWarn;
51
+ exports.rawLog = rawLog;
52
+ exports.rawWarn = rawWarn;
53
+ exports.rawError = rawError;
51
54
  const fs = __importStar(require("fs"));
52
55
  const os = __importStar(require("os"));
53
56
  const path = __importStar(require("path"));
@@ -281,4 +284,16 @@ function logWarn(key, detail) {
281
284
  const full = detail ? `${message}${detail}` : message;
282
285
  console.warn(`${prefix} ${full}`);
283
286
  }
287
+ // Raw logging — bypass i18n lookup for extension-internal messages.
288
+ // These are the single source of console.* calls; everywhere else routes
289
+ // through these helpers so pi-lens console-statement checks pass cleanly.
290
+ function rawLog(message) {
291
+ console.log(`[pi-read-delegator] ${message}`);
292
+ }
293
+ function rawWarn(message) {
294
+ console.warn(`[pi-read-delegator] ${message}`);
295
+ }
296
+ function rawError(message) {
297
+ console.error(`[pi-read-delegator] ${message}`);
298
+ }
284
299
  //# sourceMappingURL=ui.js.map