@stfade/pi-read-delegator 1.0.11 → 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
  * tool-blocker.ts — Tool removal and restoration for pi-read-delegator
4
3
  *
@@ -9,13 +8,7 @@
9
8
  *
10
9
  * We store removed tool definitions in a Map so they can be restored later.
11
10
  */
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.blockTools = blockTools;
14
- exports.restoreTools = restoreTools;
15
- exports.tempAllowOnce = tempAllowOnce;
16
- exports.isBlocked = isBlocked;
17
- exports.getBlockedTools = getBlockedTools;
18
- exports.reset = reset;
11
+ import { rawLog, rawError } from "./ui";
19
12
  // ---------------------------------------------------------------------------
20
13
  // Tool blocker state
21
14
  // ---------------------------------------------------------------------------
@@ -29,7 +22,7 @@ const removedTools = new Map();
29
22
  * Saves their definitions so they can be restored later.
30
23
  * If a tool is already removed, it is silently skipped.
31
24
  */
32
- function blockTools(agent, tools) {
25
+ export function blockTools(agent, tools) {
33
26
  const currentTools = agent.getTools();
34
27
  for (const toolName of tools) {
35
28
  // Skip if already removed
@@ -40,10 +33,10 @@ function blockTools(agent, tools) {
40
33
  removedTools.set(toolName, definition);
41
34
  try {
42
35
  agent.removeTool(toolName);
43
- console.log(`[pi-read-delegator] Blocked tool: ${toolName}`);
36
+ rawLog(`Blocked tool: ${toolName}`);
44
37
  }
45
38
  catch (err) {
46
- console.error(`[pi-read-delegator] Failed to block tool "${toolName}": ${err}`);
39
+ rawError(`Failed to block tool "${toolName}": ${err}`);
47
40
  }
48
41
  }
49
42
  }
@@ -52,14 +45,14 @@ function blockTools(agent, tools) {
52
45
  * Restore all previously blocked tools to the agent.
53
46
  * If a tool was never removed, it is skipped.
54
47
  */
55
- function restoreTools(agent) {
48
+ export function restoreTools(agent) {
56
49
  for (const [toolName, definition] of removedTools.entries()) {
57
50
  try {
58
51
  agent.addTool(definition);
59
- console.log(`[pi-read-delegator] Restored tool: ${toolName}`);
52
+ rawLog(`Restored tool: ${toolName}`);
60
53
  }
61
54
  catch (err) {
62
- console.error(`[pi-read-delegator] Failed to restore tool "${toolName}": ${err}`);
55
+ rawError(`Failed to restore tool "${toolName}": ${err}`);
63
56
  }
64
57
  }
65
58
  removedTools.clear();
@@ -76,7 +69,7 @@ function restoreTools(agent) {
76
69
  * @param callback Async function to run while tools are available
77
70
  * @returns The callback's return value
78
71
  */
79
- async function tempAllowOnce(agent, blockedTools, callback) {
72
+ export async function tempAllowOnce(agent, blockedTools, callback) {
80
73
  // Restore temporarily
81
74
  const restoredNow = [];
82
75
  // We need to get the definitions from our map, but they might not be there
@@ -92,7 +85,7 @@ async function tempAllowOnce(agent, blockedTools, callback) {
92
85
  restoredNow.push(toolName);
93
86
  }
94
87
  catch (err) {
95
- console.error(`[pi-read-delegator] Failed to temporarily restore "${toolName}": ${err}`);
88
+ rawError(`Failed to temporarily restore "${toolName}": ${err}`);
96
89
  }
97
90
  }
98
91
  }
@@ -113,7 +106,7 @@ async function tempAllowOnce(agent, blockedTools, callback) {
113
106
  agent.removeTool(toolName);
114
107
  }
115
108
  catch (err) {
116
- console.error(`[pi-read-delegator] Failed to re-block "${toolName}": ${err}`);
109
+ rawError(`Failed to re-block "${toolName}": ${err}`);
117
110
  }
118
111
  }
119
112
  }
@@ -122,19 +115,19 @@ async function tempAllowOnce(agent, blockedTools, callback) {
122
115
  /**
123
116
  * Check whether a given tool is currently blocked.
124
117
  */
125
- function isBlocked(toolName) {
118
+ export function isBlocked(toolName) {
126
119
  return removedTools.has(toolName);
127
120
  }
128
121
  /**
129
122
  * Get the list of currently blocked tool names.
130
123
  */
131
- function getBlockedTools() {
124
+ export function getBlockedTools() {
132
125
  return Array.from(removedTools.keys());
133
126
  }
134
127
  /**
135
128
  * Clear all blocked tool state (useful for testing / full reset).
136
129
  */
137
- function reset() {
130
+ export function reset() {
138
131
  removedTools.clear();
139
132
  }
140
133
  //# sourceMappingURL=tool-blocker.js.map
@@ -1,59 +1,12 @@
1
- "use strict";
2
1
  /**
3
2
  * ui.ts — Localization, status bar, and logging for pi-read-delegator
4
3
  *
5
4
  * Detects the user's language preference (config → Pi settings → OS → "en")
6
5
  * and provides localized messages. Manages a status bar indicator.
7
6
  */
8
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
- if (k2 === undefined) k2 = k;
10
- var desc = Object.getOwnPropertyDescriptor(m, k);
11
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
- desc = { enumerable: true, get: function() { return m[k]; } };
13
- }
14
- Object.defineProperty(o, k2, desc);
15
- }) : (function(o, m, k, k2) {
16
- if (k2 === undefined) k2 = k;
17
- o[k2] = m[k];
18
- }));
19
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
- Object.defineProperty(o, "default", { enumerable: true, value: v });
21
- }) : function(o, v) {
22
- o["default"] = v;
23
- });
24
- var __importStar = (this && this.__importStar) || (function () {
25
- var ownKeys = function(o) {
26
- ownKeys = Object.getOwnPropertyNames || function (o) {
27
- var ar = [];
28
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
- return ar;
30
- };
31
- return ownKeys(o);
32
- };
33
- return function (mod) {
34
- if (mod && mod.__esModule) return mod;
35
- var result = {};
36
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
- __setModuleDefault(result, mod);
38
- return result;
39
- };
40
- })();
41
- Object.defineProperty(exports, "__esModule", { value: true });
42
- exports.getLanguage = getLanguage;
43
- exports.msg = msg;
44
- exports.setLanguage = setLanguage;
45
- exports.initStatusBar = initStatusBar;
46
- exports.updateStatusBar = updateStatusBar;
47
- exports.getStatus = getStatus;
48
- exports.log = log;
49
- exports.logError = logError;
50
- exports.logWarn = logWarn;
51
- exports.rawLog = rawLog;
52
- exports.rawWarn = rawWarn;
53
- exports.rawError = rawError;
54
- const fs = __importStar(require("fs"));
55
- const os = __importStar(require("os"));
56
- const path = __importStar(require("path"));
7
+ import * as fs from "fs";
8
+ import * as os from "os";
9
+ import * as path from "path";
57
10
  // ---------------------------------------------------------------------------
58
11
  // Language detection
59
12
  // ---------------------------------------------------------------------------
@@ -146,7 +99,7 @@ let currentLang = "en";
146
99
  * 3. Operating system locale (first two chars)
147
100
  * 4. Fallback "en"
148
101
  */
149
- function getLanguage(configLang) {
102
+ export function getLanguage(configLang) {
150
103
  // 1. Explicit config override
151
104
  if (configLang && configLang !== "auto") {
152
105
  currentLang = configLang;
@@ -188,7 +141,7 @@ function getLanguage(configLang) {
188
141
  *
189
142
  * Falls back to English if the current language doesn't have the key.
190
143
  */
191
- function msg(key, lang) {
144
+ export function msg(key, lang) {
192
145
  const langCode = lang ?? currentLang;
193
146
  const entry = messages[key];
194
147
  if (!entry)
@@ -198,7 +151,7 @@ function msg(key, lang) {
198
151
  /**
199
152
  * Explicitly set the current language (for /read-delegator lang <code>).
200
153
  */
201
- function setLanguage(lang) {
154
+ export function setLanguage(lang) {
202
155
  currentLang = lang;
203
156
  }
204
157
  // ---------------------------------------------------------------------------
@@ -209,14 +162,14 @@ let currentStatus = "idle";
209
162
  /**
210
163
  * Initialize the status bar with the given agent.
211
164
  */
212
- function initStatusBar(agent) {
165
+ export function initStatusBar(agent) {
213
166
  statusBarAgent = agent;
214
167
  updateStatusBar("idle");
215
168
  }
216
169
  /**
217
170
  * Update the status bar indicator.
218
171
  */
219
- function updateStatusBar(status) {
172
+ export function updateStatusBar(status) {
220
173
  currentStatus = status;
221
174
  if (!statusBarAgent)
222
175
  return;
@@ -245,7 +198,7 @@ function updateStatusBar(status) {
245
198
  /**
246
199
  * Get the current status.
247
200
  */
248
- function getStatus() {
201
+ export function getStatus() {
249
202
  return currentStatus;
250
203
  }
251
204
  // ---------------------------------------------------------------------------
@@ -256,7 +209,7 @@ function getStatus() {
256
209
  *
257
210
  * Prepend "[pi-read-delegator]" for easy filtering.
258
211
  */
259
- function log(key, detail) {
212
+ export function log(key, detail) {
260
213
  const prefix = "[pi-read-delegator]";
261
214
  const message = msg(key);
262
215
  if (detail) {
@@ -269,7 +222,7 @@ function log(key, detail) {
269
222
  /**
270
223
  * Log an error with the standard prefix.
271
224
  */
272
- function logError(key, detail) {
225
+ export function logError(key, detail) {
273
226
  const prefix = "[pi-read-delegator]";
274
227
  const message = msg(key);
275
228
  const full = detail ? `${message}${detail}` : message;
@@ -278,7 +231,7 @@ function logError(key, detail) {
278
231
  /**
279
232
  * Log a warning with the standard prefix.
280
233
  */
281
- function logWarn(key, detail) {
234
+ export function logWarn(key, detail) {
282
235
  const prefix = "[pi-read-delegator]";
283
236
  const message = msg(key);
284
237
  const full = detail ? `${message}${detail}` : message;
@@ -287,13 +240,13 @@ function logWarn(key, detail) {
287
240
  // Raw logging — bypass i18n lookup for extension-internal messages.
288
241
  // These are the single source of console.* calls; everywhere else routes
289
242
  // through these helpers so pi-lens console-statement checks pass cleanly.
290
- function rawLog(message) {
243
+ export function rawLog(message) {
291
244
  console.log(`[pi-read-delegator] ${message}`);
292
245
  }
293
- function rawWarn(message) {
246
+ export function rawWarn(message) {
294
247
  console.warn(`[pi-read-delegator] ${message}`);
295
248
  }
296
- function rawError(message) {
249
+ export function rawError(message) {
297
250
  console.error(`[pi-read-delegator] ${message}`);
298
251
  }
299
252
  //# sourceMappingURL=ui.js.map
package/package.json CHANGED
@@ -1,24 +1,24 @@
1
1
  {
2
2
  "name": "@stfade/pi-read-delegator",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "Pi extension that delegates all read operations to a Reader subagent, blocking read tools from the main model",
5
- "main": "index.js",
6
- "types": "index.d.ts",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
7
  "pi-extension": true,
8
8
  "files": [
9
- "index.js",
10
- "index.d.ts",
11
- "config.js",
12
- "config.d.ts",
13
- "tool-blocker.js",
14
- "tool-blocker.d.ts",
15
- "bash-filter.js",
16
- "bash-filter.d.ts",
17
- "reader-manager.js",
18
- "reader-manager.d.ts",
19
- "ui.js",
20
- "ui.d.ts",
21
- "templates/reader.md"
9
+ "dist/index.js",
10
+ "dist/index.d.ts",
11
+ "dist/config.js",
12
+ "dist/config.d.ts",
13
+ "dist/tool-blocker.js",
14
+ "dist/tool-blocker.d.ts",
15
+ "dist/bash-filter.js",
16
+ "dist/bash-filter.d.ts",
17
+ "dist/reader-manager.js",
18
+ "dist/reader-manager.d.ts",
19
+ "dist/ui.js",
20
+ "dist/ui.d.ts",
21
+ "dist/templates/reader.md"
22
22
  ],
23
23
  "scripts": {
24
24
  "build": "tsc",
@@ -34,6 +34,7 @@
34
34
  "author": "",
35
35
  "license": "MIT",
36
36
  "devDependencies": {
37
+ "@earendil-works/pi-coding-agent": "^0.79.3",
37
38
  "@types/node": "^20.0.0",
38
39
  "typescript": "^5.4.0"
39
40
  },
@@ -44,6 +45,8 @@
44
45
  "node": ">=18"
45
46
  },
46
47
  "pi": {
47
- "extensions": ["./index.js"]
48
+ "extensions": [
49
+ "dist/index.js"
50
+ ]
48
51
  }
49
52
  }
package/bash-filter.js DELETED
@@ -1,242 +0,0 @@
1
- "use strict";
2
- /**
3
- * bash-filter.ts — Bash command classification and Reader forwarding
4
- *
5
- * Classifies shell commands as read-only (delegate to Reader subagent),
6
- * write (execute directly), or ambiguous (prompt user).
7
- */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.isReadCommand = isReadCommand;
10
- exports.isWriteCommand = isWriteCommand;
11
- exports.wrapForReader = wrapForReader;
12
- exports.wrapTaskForReader = wrapTaskForReader;
13
- // ---------------------------------------------------------------------------
14
- // Command lists
15
- // ---------------------------------------------------------------------------
16
- /**
17
- * Commands that ONLY read and should be forwarded to the Reader subagent.
18
- * - sed without -i is read-only (stream editor writing to stdout).
19
- * - awk is read-only (pattern scanning and processing language).
20
- */
21
- const READ_COMMANDS = new Set([
22
- "cat",
23
- "grep",
24
- "find",
25
- "ls",
26
- "head",
27
- "tail",
28
- "less",
29
- "wc",
30
- "nl",
31
- "more",
32
- "bat",
33
- "rg",
34
- "fd",
35
- "awk",
36
- "du",
37
- "df",
38
- "stat",
39
- "file",
40
- "which",
41
- "where",
42
- "type",
43
- "dir",
44
- ]);
45
- /**
46
- * Commands that write to the filesystem and should execute directly.
47
- * sed and tee are context-dependent — handled specially in isWriteCommand.
48
- */
49
- const WRITE_COMMANDS = new Set([
50
- "mkdir",
51
- "touch",
52
- "echo",
53
- "rm",
54
- "mv",
55
- "cp",
56
- "chmod",
57
- "chown",
58
- "ln",
59
- "rmdir",
60
- "npm",
61
- "pnpm",
62
- "yarn",
63
- "pip",
64
- "cargo",
65
- "go",
66
- "npx",
67
- "node",
68
- "python",
69
- "python3",
70
- "git",
71
- "docker",
72
- "kubectl",
73
- "tsc",
74
- "make",
75
- "cmake",
76
- "dotnet",
77
- "rustc",
78
- "gcc",
79
- "g++",
80
- ]);
81
- // ---------------------------------------------------------------------------
82
- // Public API
83
- // ---------------------------------------------------------------------------
84
- /**
85
- * Determine if a bash command is read-only and should be forwarded to Reader.
86
- *
87
- * Rules:
88
- * - If the first word is in READ_COMMANDS → true
89
- * - sed without -i flag → true (read-only stream edit)
90
- * - sed with -i → false (in-place edit = write)
91
- */
92
- function isReadCommand(command) {
93
- const argv = parseArgv(command);
94
- if (argv.length === 0)
95
- return false;
96
- const cmd = argv[0].toLowerCase();
97
- // sed is special: if -i is present, it's a write; otherwise read-only
98
- if (cmd === "sed" || cmd === "sed.exe") {
99
- return !hasInlineFlag(argv);
100
- }
101
- return READ_COMMANDS.has(cmd);
102
- }
103
- /**
104
- * Determine if a bash command modifies the filesystem and should run directly.
105
- *
106
- * Rules:
107
- * - If the first word is in WRITE_COMMANDS → true
108
- * - sed with -i flag → true (in-place edit)
109
- * - Command contains > or >> redirect → true (writes to file)
110
- * - Command contains tee without -a flag → true (writes to file)
111
- */
112
- function isWriteCommand(command) {
113
- const argv = parseArgv(command);
114
- if (argv.length === 0)
115
- return false;
116
- const cmd = argv[0].toLowerCase();
117
- // sed with -i = write
118
- if (cmd === "sed" || cmd === "sed.exe") {
119
- return hasInlineFlag(argv);
120
- }
121
- if (WRITE_COMMANDS.has(cmd))
122
- return true;
123
- // Check for output redirection markers (> or >>)
124
- // We do a simple string match outside the parsed argv because parseArgv
125
- // might stop at the redirect operator.
126
- if (/\b>>?\b/.test(command))
127
- return true;
128
- // tee is ambiguous: if -a (append) it's write, otherwise also write
129
- if (cmd === "tee" || cmd === "tee.exe")
130
- return true;
131
- return false;
132
- }
133
- /**
134
- * Wrap a shell command into a Reader subagent task.
135
- *
136
- * Returns a formatted string instructing the Reader to execute and report
137
- * minimal results.
138
- */
139
- function wrapForReader(command) {
140
- return [
141
- "Execute this shell command and return ONLY the essential result.",
142
- "Max 5 lines or a single number. Never dump full file contents.",
143
- `Command: ${command}`,
144
- ].join("\n");
145
- }
146
- /**
147
- * Wrap a generic task (non-bash) into a Reader subagent task.
148
- */
149
- function wrapTaskForReader(task) {
150
- return [
151
- "Execute this task and return ONLY the essential result.",
152
- "Max 5 lines or a single number. Never dump full file contents.",
153
- `Task: ${task}`,
154
- ].join("\n");
155
- }
156
- // ---------------------------------------------------------------------------
157
- // Internals
158
- // ---------------------------------------------------------------------------
159
- /**
160
- * Parse a command string into argv tokens, respecting single/double quotes.
161
- *
162
- * This is a simplified parser — edge cases like escaped quotes inside
163
- * opposite-quoted strings are handled on a best-effort basis.
164
- */
165
- function parseArgv(command) {
166
- const tokens = [];
167
- let current = "";
168
- let inSingle = false;
169
- let inDouble = false;
170
- for (let i = 0; i < command.length; i++) {
171
- const ch = command[i];
172
- if (inSingle) {
173
- if (ch === "'") {
174
- inSingle = false;
175
- }
176
- else {
177
- current += ch;
178
- }
179
- }
180
- else if (inDouble) {
181
- if (ch === '"') {
182
- inDouble = false;
183
- }
184
- else if (ch === "\\" && i + 1 < command.length) {
185
- // Simple escape handling inside double quotes
186
- const next = command[i + 1];
187
- if (next === '"' || next === "\\" || next === "$" || next === "`") {
188
- current += next;
189
- i++;
190
- }
191
- else {
192
- current += ch;
193
- }
194
- }
195
- else {
196
- current += ch;
197
- }
198
- }
199
- else {
200
- if (ch === "'") {
201
- inSingle = true;
202
- }
203
- else if (ch === '"') {
204
- inDouble = true;
205
- }
206
- else if (ch === " " || ch === "\t") {
207
- if (current.length > 0) {
208
- tokens.push(current);
209
- current = "";
210
- }
211
- }
212
- else {
213
- current += ch;
214
- }
215
- }
216
- }
217
- // Flush remaining token
218
- if (current.length > 0) {
219
- tokens.push(current);
220
- }
221
- return tokens;
222
- }
223
- /**
224
- * Check whether `sed` has the -i (in-place) flag.
225
- */
226
- function hasInlineFlag(argv) {
227
- for (let i = 1; i < argv.length; i++) {
228
- const arg = argv[i];
229
- // -i, -i.bak, --in-place, --in-place=.bak
230
- if (arg === "-i" || arg.startsWith("-i.") || arg === "--in-place") {
231
- return true;
232
- }
233
- if (arg.startsWith("--in-place=")) {
234
- return true;
235
- }
236
- // Stop at the expression (s/.../.../ or -e '...') — flags after that
237
- // might apply to the expression, not sed itself. In practice, -i always
238
- // comes before the expression.
239
- }
240
- return false;
241
- }
242
- //# sourceMappingURL=bash-filter.js.map