pi-subagents 0.9.2 → 0.11.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/CHANGELOG.md +33 -0
- package/README.md +56 -6
- package/async-execution.ts +64 -30
- package/chain-clarify.ts +16 -4
- package/chain-execution.ts +31 -1
- package/execution.ts +16 -1
- package/index.ts +234 -25
- package/package.json +11 -2
- package/parallel-utils.ts +93 -0
- package/render.ts +78 -8
- package/schemas.ts +1 -1
- package/settings.ts +16 -14
- package/skills.ts +25 -1
- package/subagent-runner.ts +360 -176
- package/utils.ts +23 -7
package/settings.ts
CHANGED
|
@@ -213,7 +213,7 @@ export function resolveStepBehavior(
|
|
|
213
213
|
* Resolve a file path: absolute paths pass through, relative paths get chainDir prepended.
|
|
214
214
|
*/
|
|
215
215
|
function resolveChainPath(filePath: string, chainDir: string): string {
|
|
216
|
-
return path.isAbsolute(filePath) ? filePath :
|
|
216
|
+
return path.isAbsolute(filePath) ? filePath : path.join(chainDir, filePath);
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
/**
|
|
@@ -243,7 +243,7 @@ export function buildChainInstructions(
|
|
|
243
243
|
|
|
244
244
|
// Progress instructions in suffix (less critical)
|
|
245
245
|
if (behavior.progress) {
|
|
246
|
-
const progressPath =
|
|
246
|
+
const progressPath = path.join(chainDir, "progress.md");
|
|
247
247
|
if (isFirstProgressAgent) {
|
|
248
248
|
suffixParts.push(`Create and maintain progress at: ${progressPath}`);
|
|
249
249
|
} else {
|
|
@@ -288,7 +288,7 @@ export function resolveParallelBehaviors(
|
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
// Build subdirectory path for this parallel task
|
|
291
|
-
const subdir = `parallel-${stepIndex}
|
|
291
|
+
const subdir = path.join(`parallel-${stepIndex}`, `${taskIndex}-${task.agent}`);
|
|
292
292
|
|
|
293
293
|
// Output: task override > agent default (namespaced) > false
|
|
294
294
|
// Absolute paths pass through unchanged; relative paths get namespaced under subdir
|
|
@@ -299,11 +299,11 @@ export function resolveParallelBehaviors(
|
|
|
299
299
|
} else if (path.isAbsolute(task.output)) {
|
|
300
300
|
output = task.output; // Absolute path: use as-is
|
|
301
301
|
} else {
|
|
302
|
-
output =
|
|
302
|
+
output = path.join(subdir, task.output); // Relative: namespace under subdir
|
|
303
303
|
}
|
|
304
304
|
} else if (config.output) {
|
|
305
305
|
// Agent defaults are always relative, so namespace them
|
|
306
|
-
output =
|
|
306
|
+
output = path.join(subdir, config.output);
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
// Reads: task override > agent default > false
|
|
@@ -372,15 +372,17 @@ export function aggregateParallelOutputs(results: ParallelTaskResult[]): string
|
|
|
372
372
|
.map((r, i) => {
|
|
373
373
|
const header = `=== Parallel Task ${i + 1} (${r.agent}) ===`;
|
|
374
374
|
const hasTextOutput = Boolean(r.output?.trim());
|
|
375
|
-
const status = r.exitCode
|
|
376
|
-
?
|
|
377
|
-
: r.
|
|
378
|
-
? `⚠️
|
|
379
|
-
:
|
|
380
|
-
? `⚠️
|
|
381
|
-
: !hasTextOutput &&
|
|
382
|
-
?
|
|
383
|
-
:
|
|
375
|
+
const status = r.exitCode === -1
|
|
376
|
+
? "⏭️ SKIPPED"
|
|
377
|
+
: r.exitCode !== 0
|
|
378
|
+
? `⚠️ FAILED (exit code ${r.exitCode})${r.error ? `: ${r.error}` : ""}`
|
|
379
|
+
: r.error
|
|
380
|
+
? `⚠️ WARNING: ${r.error}`
|
|
381
|
+
: !hasTextOutput && r.outputTargetPath && r.outputTargetExists === false
|
|
382
|
+
? `⚠️ EMPTY OUTPUT (expected output file missing: ${r.outputTargetPath})`
|
|
383
|
+
: !hasTextOutput && !r.outputTargetPath
|
|
384
|
+
? "⚠️ EMPTY OUTPUT (no textual response returned)"
|
|
385
|
+
: "";
|
|
384
386
|
const body = status
|
|
385
387
|
? (hasTextOutput ? `${status}\n${r.output}` : status)
|
|
386
388
|
: r.output;
|
package/skills.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Skill resolution and caching for subagent extension
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { execSync } from "node:child_process";
|
|
5
6
|
import * as fs from "node:fs";
|
|
6
7
|
import * as os from "node:os";
|
|
7
8
|
import * as path from "node:path";
|
|
@@ -89,11 +90,31 @@ function getPackageSkillPaths(packageRoot: string): string[] {
|
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
let cachedGlobalNpmRoot: string | null = null;
|
|
94
|
+
|
|
95
|
+
function getGlobalNpmRoot(): string | null {
|
|
96
|
+
if (cachedGlobalNpmRoot !== null) return cachedGlobalNpmRoot;
|
|
97
|
+
try {
|
|
98
|
+
cachedGlobalNpmRoot = execSync("npm root -g", { encoding: "utf-8", timeout: 5000 }).trim();
|
|
99
|
+
return cachedGlobalNpmRoot;
|
|
100
|
+
} catch {
|
|
101
|
+
cachedGlobalNpmRoot = ""; // Empty string means "tried but failed"
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
92
106
|
function collectPackageSkillPaths(cwd: string): string[] {
|
|
93
107
|
const dirs = [
|
|
94
108
|
path.join(cwd, CONFIG_DIR, "npm", "node_modules"),
|
|
95
109
|
path.join(AGENT_DIR, "npm", "node_modules"),
|
|
96
110
|
];
|
|
111
|
+
|
|
112
|
+
// Add global npm root if available (where pi installs global packages)
|
|
113
|
+
const globalRoot = getGlobalNpmRoot();
|
|
114
|
+
if (globalRoot) {
|
|
115
|
+
dirs.push(globalRoot);
|
|
116
|
+
}
|
|
117
|
+
|
|
97
118
|
const results: string[] = [];
|
|
98
119
|
|
|
99
120
|
for (const dir of dirs) {
|
|
@@ -178,6 +199,8 @@ function inferSkillSource(rawSource: unknown, filePath: string, cwd: string): Sk
|
|
|
178
199
|
const projectRoot = path.resolve(cwd, CONFIG_DIR);
|
|
179
200
|
const isProjectScoped = isWithinPath(filePath, projectRoot);
|
|
180
201
|
const isUserScoped = isWithinPath(filePath, AGENT_DIR);
|
|
202
|
+
const globalRoot = getGlobalNpmRoot();
|
|
203
|
+
const isGlobalPackage = globalRoot ? isWithinPath(filePath, globalRoot) : false;
|
|
181
204
|
|
|
182
205
|
if (source === "project") return "project";
|
|
183
206
|
if (source === "user") return "user";
|
|
@@ -188,7 +211,7 @@ function inferSkillSource(rawSource: unknown, filePath: string, cwd: string): Sk
|
|
|
188
211
|
}
|
|
189
212
|
if (source === "package") {
|
|
190
213
|
if (isProjectScoped) return "project-package";
|
|
191
|
-
if (isUserScoped) return "user-package";
|
|
214
|
+
if (isUserScoped || isGlobalPackage) return "user-package";
|
|
192
215
|
return "unknown";
|
|
193
216
|
}
|
|
194
217
|
if (source === "extension") return "extension";
|
|
@@ -196,6 +219,7 @@ function inferSkillSource(rawSource: unknown, filePath: string, cwd: string): Sk
|
|
|
196
219
|
|
|
197
220
|
if (isProjectScoped) return "project";
|
|
198
221
|
if (isUserScoped) return "user";
|
|
222
|
+
if (isGlobalPackage) return "user-package";
|
|
199
223
|
return "unknown";
|
|
200
224
|
}
|
|
201
225
|
|