@rohaquinlop/pi-subagents 0.2.0 → 0.3.0
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 +21 -0
- package/index.ts +70 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -159,6 +159,27 @@ Built-in tools (`read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`) work auto
|
|
|
159
159
|
|
|
160
160
|
The `subagent` tool itself is listed in `CUSTOM_TOOL_EXTENSIONS` pointing back to this extension's own `index.ts` — that's how an agent like `worker` can recursively spawn other agents. Recursion is bounded only by each agent's `subagent_agents` allowlist (e.g. worker can spawn scout/researcher, neither of which declares the `subagent` tool, so the chain stops at depth 2).
|
|
161
161
|
|
|
162
|
+
### 4. Model-specific extensions (no manual mapping)
|
|
163
|
+
|
|
164
|
+
Extensions that apply to specific models (not tools) can declare `appliesToModels`
|
|
165
|
+
in their `package.json` and auto-load without any `CUSTOM_TOOL_EXTENSIONS` mapping:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"pi": {
|
|
170
|
+
"extensions": ["./extensions/index.ts"],
|
|
171
|
+
"appliesToModels": ["deepseek-*"]
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
When a subagent's configured model matches one of the patterns, the extension is
|
|
177
|
+
loaded via `--extension` in the child process. Glob patterns (`*` wildcard) match
|
|
178
|
+
the **model ID** (the portion after the last `/`, e.g. `deepseek-v4-flash` in
|
|
179
|
+
`nan/deepseek-v4-flash`). Plain strings without wildcards match the **provider
|
|
180
|
+
name** (the portion before the `/`, e.g. `nan` in `nan/deepseek-v4-flash`).
|
|
181
|
+
Matching is case-insensitive.
|
|
182
|
+
|
|
162
183
|
## Structure
|
|
163
184
|
|
|
164
185
|
```
|
package/index.ts
CHANGED
|
@@ -156,6 +156,43 @@ function buildCustomToolExtensions(): Record<string, string> {
|
|
|
156
156
|
|
|
157
157
|
const CUSTOM_TOOL_EXTENSIONS: Record<string, string> = buildCustomToolExtensions();
|
|
158
158
|
|
|
159
|
+
interface ModelExtension {
|
|
160
|
+
patterns: string[];
|
|
161
|
+
path: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function buildModelExtensions(): ModelExtension[] {
|
|
165
|
+
const result: ModelExtension[] = [];
|
|
166
|
+
try {
|
|
167
|
+
const entries = fs.readdirSync(EXT_BASE, { withFileTypes: true });
|
|
168
|
+
for (const entry of entries) {
|
|
169
|
+
if (!entry.isDirectory()) continue;
|
|
170
|
+
const pkgPath = path.join(EXT_BASE, entry.name, "package.json");
|
|
171
|
+
if (!fs.existsSync(pkgPath)) continue;
|
|
172
|
+
try {
|
|
173
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
174
|
+
const patterns: unknown = pkg?.pi?.appliesToModels;
|
|
175
|
+
const extEntries: unknown = pkg?.pi?.extensions;
|
|
176
|
+
if (!Array.isArray(patterns) || patterns.length === 0) continue;
|
|
177
|
+
if (!patterns.every((p: unknown): p is string => typeof p === "string")) continue;
|
|
178
|
+
if (!Array.isArray(extEntries) || extEntries.length === 0) continue;
|
|
179
|
+
if (!extEntries.every((e: unknown): e is string => typeof e === "string")) continue;
|
|
180
|
+
const extPath = path.join(EXT_BASE, entry.name, extEntries[0]);
|
|
181
|
+
if (fs.existsSync(extPath)) {
|
|
182
|
+
result.push({ patterns, path: extPath });
|
|
183
|
+
}
|
|
184
|
+
} catch {
|
|
185
|
+
// skip corrupted package.json
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
} catch {
|
|
189
|
+
// skip if EXT_BASE doesn't exist
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const MODEL_EXTENSIONS: ModelExtension[] = buildModelExtensions();
|
|
195
|
+
|
|
159
196
|
// ── Agent Discovery & Registration ────────────────────────────────────
|
|
160
197
|
|
|
161
198
|
let agents: AgentConfig[] = [];
|
|
@@ -300,6 +337,29 @@ function truncLine(text: string, maxWidth: number): string {
|
|
|
300
337
|
|
|
301
338
|
// ── Subagent Execution ────────────────────────────────────────────────
|
|
302
339
|
|
|
340
|
+
/**
|
|
341
|
+
* Match a model string against a glob-style pattern (supports only `*` as wildcard).
|
|
342
|
+
* Pattern "deepseek-*" matches "nan/deepseek-v4-flash" and "deepseek-v4-pro".
|
|
343
|
+
* Plain strings without wildcards match the provider name (portion before `/`).
|
|
344
|
+
*/
|
|
345
|
+
function matchModelPattern(model: string, pattern: string): boolean {
|
|
346
|
+
const slashIdx = model.indexOf("/");
|
|
347
|
+
const hasProvider = slashIdx !== -1;
|
|
348
|
+
const modelId = hasProvider ? model.slice(slashIdx + 1) : model;
|
|
349
|
+
const provider = hasProvider ? model.slice(0, slashIdx) : "";
|
|
350
|
+
|
|
351
|
+
// Pattern without wildcards: match against provider name
|
|
352
|
+
if (!pattern.includes("*") && hasProvider) {
|
|
353
|
+
return provider.toLowerCase() === pattern.toLowerCase();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Glob pattern: match against model ID
|
|
357
|
+
// Escape regex-special characters, then convert escaped * back to .*
|
|
358
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*");
|
|
359
|
+
const regex = new RegExp("^" + escaped + "$", "i");
|
|
360
|
+
return regex.test(modelId);
|
|
361
|
+
}
|
|
362
|
+
|
|
303
363
|
async function buildPiArgs(
|
|
304
364
|
agent: AgentConfig,
|
|
305
365
|
task: string,
|
|
@@ -348,6 +408,16 @@ async function buildPiArgs(
|
|
|
348
408
|
args.push("--no-tools");
|
|
349
409
|
}
|
|
350
410
|
|
|
411
|
+
// Auto-load model-specific extensions (e.g. deepseek-cache)
|
|
412
|
+
for (const me of MODEL_EXTENSIONS) {
|
|
413
|
+
for (const pattern of me.patterns) {
|
|
414
|
+
if (matchModelPattern(agent.model, pattern)) {
|
|
415
|
+
extensionPaths.add(me.path);
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
351
421
|
for (const extPath of extensionPaths) {
|
|
352
422
|
args.push("--extension", extPath);
|
|
353
423
|
}
|
package/package.json
CHANGED