pi-recurse 0.1.3 → 0.1.5
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 +6 -0
- package/index.ts +3 -3
- package/package.json +4 -3
- package/skills/recurse/SKILL.md +90 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.5] - 2026-08-09
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Grey the recurse result summary, child ids, and expanded tree lines (muted) to match pi core's output styling.
|
|
13
|
+
|
|
8
14
|
## [0.1.0] - 2025-03-24
|
|
9
15
|
|
|
10
16
|
### Added
|
package/index.ts
CHANGED
|
@@ -478,7 +478,7 @@ export default function piRecurseExtension(pi: ExtensionAPI) {
|
|
|
478
478
|
const icon = stats.failed === 0 ? theme.fg('success', '✓') : theme.fg('warning', '⚠');
|
|
479
479
|
const hasChildren = data.results.some((r) => r.children);
|
|
480
480
|
|
|
481
|
-
let text = `${icon} ${stats.succeeded}/${stats.total} at depth ${depth}${mode ? ` · ${mode}` : ''}`;
|
|
481
|
+
let text = `${icon} ${theme.fg('muted', `${stats.succeeded}/${stats.total} at depth ${depth}${mode ? ` · ${mode}` : ''}`)}`;
|
|
482
482
|
|
|
483
483
|
if (stats.totalCost && stats.totalCost > 0) {
|
|
484
484
|
text += theme.fg('dim', ` · $${stats.totalCost.toFixed(4)}`);
|
|
@@ -493,13 +493,13 @@ export default function piRecurseExtension(pi: ExtensionAPI) {
|
|
|
493
493
|
// Render tree view
|
|
494
494
|
const tree = buildRecurseTree(data, mode);
|
|
495
495
|
const treeLines = renderRecurseTree(tree, 100);
|
|
496
|
-
text += '\n' + treeLines.join('\n');
|
|
496
|
+
text += '\n' + treeLines.map((line) => theme.fg('muted', line)).join('\n');
|
|
497
497
|
} else {
|
|
498
498
|
// Simple flat view
|
|
499
499
|
text += '\n';
|
|
500
500
|
for (const r of data.results) {
|
|
501
501
|
const status = r.success ? theme.fg('success', '✓') : theme.fg('error', '✗');
|
|
502
|
-
text += ` ${status} ${r.id}`;
|
|
502
|
+
text += ` ${status} ${theme.fg('muted', r.id)}`;
|
|
503
503
|
if (r.children) {
|
|
504
504
|
text += theme.fg('accent', ` → ${r.children.stats.total} children`);
|
|
505
505
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-recurse",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Recursive agent extension for Pi — spawn subagents programmatically with depth guardrails",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Tom X Nguyen",
|
|
@@ -29,12 +29,14 @@
|
|
|
29
29
|
"*.d.ts",
|
|
30
30
|
"lib/**",
|
|
31
31
|
"tests/**",
|
|
32
|
+
"skills/**",
|
|
32
33
|
"README.md",
|
|
33
34
|
"CHANGELOG.md"
|
|
34
35
|
],
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@commitlint/cli": "21.0.1",
|
|
37
38
|
"@commitlint/config-conventional": "21.0.1",
|
|
39
|
+
"@earendil-works/pi-tui": "0.84.1",
|
|
38
40
|
"@types/node": "25.9.1",
|
|
39
41
|
"@vitest/coverage-v8": "4.1.7",
|
|
40
42
|
"knip": "6.14.1",
|
|
@@ -43,8 +45,7 @@
|
|
|
43
45
|
"simple-git-hooks": "2.13.1",
|
|
44
46
|
"standard-version": "9.5.0",
|
|
45
47
|
"typescript": "6.0.3",
|
|
46
|
-
"vitest": "4.1.7"
|
|
47
|
-
"@earendil-works/pi-tui": "0.84.0"
|
|
48
|
+
"vitest": "4.1.7"
|
|
48
49
|
},
|
|
49
50
|
"peerDependencies": {
|
|
50
51
|
"@earendil-works/pi-coding-agent": ">=0.74.0",
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: recurse
|
|
3
|
+
description: Use the `recurse` tool for programmatic subagent delegation. Supports single, parallel, and chain modes for large file processing, batch analysis, and sequential pipelines.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Recurse Skill
|
|
7
|
+
|
|
8
|
+
Use the `recurse` tool for programmatic subagent delegation.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
- **Large files** that exceed context window → chunk and recurse
|
|
13
|
+
- **Batch analysis** of multiple files → parallel mode
|
|
14
|
+
- **Sequential pipelines** where step N depends on step N-1 → chain mode
|
|
15
|
+
- **Divide-and-conquer** refactoring → parallel tasks per file
|
|
16
|
+
|
|
17
|
+
## Mode Selection Guide
|
|
18
|
+
|
|
19
|
+
| Situation | Mode | Why |
|
|
20
|
+
|-----------|------|-----|
|
|
21
|
+
| One specific task | `single` | Simplest, direct |
|
|
22
|
+
| 10+ independent file reviews | `parallel` | Concurrent, fastest |
|
|
23
|
+
| Summary → Analysis → Plan | `chain` | Each step needs prior output |
|
|
24
|
+
| Cross-file refactoring | `parallel` with per-file tasks | Divide work, combine results |
|
|
25
|
+
|
|
26
|
+
## Example Patterns
|
|
27
|
+
|
|
28
|
+
### Parallel File Analysis
|
|
29
|
+
```typescript
|
|
30
|
+
const files = await findFiles("src/**/*.ts");
|
|
31
|
+
const results = await recurse({
|
|
32
|
+
mode: "parallel",
|
|
33
|
+
tasks: files.map(f => ({
|
|
34
|
+
id: f,
|
|
35
|
+
prompt: `Review ${f}: identify bugs, suggest improvements. Be concise.`
|
|
36
|
+
})),
|
|
37
|
+
concurrency: 4
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Aggregate
|
|
41
|
+
const issues = results.results.filter(r => r.output.includes("BUG"));
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Chain: Summarize → Analyze → Plan
|
|
45
|
+
```typescript
|
|
46
|
+
const plan = await recurse({
|
|
47
|
+
mode: "chain",
|
|
48
|
+
chain: [
|
|
49
|
+
{ id: "readme", prompt: "Summarize README.md in 3 bullet points" },
|
|
50
|
+
{ id: "risks", prompt: "Given this summary: {previous}, what are 3 implementation risks?" },
|
|
51
|
+
{ id: "mitigations", prompt: "Given these risks: {previous}, suggest mitigations for each" }
|
|
52
|
+
]
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Chunked Large File Processing
|
|
57
|
+
```typescript
|
|
58
|
+
const totalLines = await getLineCount("huge.log");
|
|
59
|
+
const chunkSize = 500;
|
|
60
|
+
const tasks = [];
|
|
61
|
+
|
|
62
|
+
for (let start = 1; start <= totalLines; start += chunkSize) {
|
|
63
|
+
const end = Math.min(start + chunkSize - 1, totalLines);
|
|
64
|
+
tasks.push({
|
|
65
|
+
id: `lines-${start}-${end}`,
|
|
66
|
+
prompt: `Extract ERROR entries from lines ${start}-${end}`,
|
|
67
|
+
context: await readLines("huge.log", start, end)
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const errors = await recurse({ mode: "parallel", tasks });
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Guardrails
|
|
75
|
+
|
|
76
|
+
The extension automatically enforces limits:
|
|
77
|
+
- Max depth (default: 3)
|
|
78
|
+
- Max total calls (default: 100)
|
|
79
|
+
- Timeout (default: 600s)
|
|
80
|
+
- Budget (optional, via RLM_BUDGET)
|
|
81
|
+
|
|
82
|
+
At depth ≥ 3, the recurse tool is disabled. Work directly instead.
|
|
83
|
+
|
|
84
|
+
## Cost Awareness
|
|
85
|
+
|
|
86
|
+
Check result.stats after recurse calls:
|
|
87
|
+
```typescript
|
|
88
|
+
const result = await recurse({ mode: "parallel", tasks });
|
|
89
|
+
console.log(`Cost: $${result.stats.totalCost?.toFixed(4) || 'unknown'}`);
|
|
90
|
+
```
|