Skip to content

Commit 7f55427

Browse files
committed
Add support for pi coding agent
For better functionality, requires pi-subagents package to be installed too for the pi setup
1 parent 94993f7 commit 7f55427

10 files changed

Lines changed: 484 additions & 17 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ bin/gstack-global-discover*
2121
.cursor/
2222
.openclaw/
2323
.hermes/
24+
.pi/
2425
.gbrain/
2526
.gbrain-source
2627
.context/

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ These are conversational skills. Your OpenClaw agent runs them directly via chat
101101

102102
### Other AI Agents
103103

104-
gstack works on 10 AI coding agents, not just Claude. Setup auto-detects which
104+
gstack works on 11 AI coding agents, not just Claude. Setup auto-detects which
105105
agents you have installed:
106106

107107
```bash
@@ -120,6 +120,7 @@ Or target a specific agent with `./setup --host <name>`:
120120
| Slate | `--host slate` | `~/.slate/skills/gstack-*/` |
121121
| Kiro | `--host kiro` | `~/.kiro/skills/gstack-*/` |
122122
| Hermes | `--host hermes` | `~/.hermes/skills/gstack-*/` |
123+
| Pi | `--host pi` | `~/.pi/agent/skills/gstack-*/` |
123124
| GBrain (mod) | `--host gbrain` | `~/.gbrain/skills/gstack-*/` |
124125

125126
**Want to add support for another agent?** See [docs/ADDING_A_HOST.md](docs/ADDING_A_HOST.md).

docs/ADDING_A_HOST.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Adding a New Host to gstack
22

33
gstack uses a declarative host config system. Each supported AI coding agent
4-
(Claude, Codex, Factory, Kiro, OpenCode, Slate, Cursor, OpenClaw) is defined
4+
(Claude, Codex, Factory, Kiro, OpenCode, Slate, Cursor, OpenClaw, Pi) is defined
55
as a typed TypeScript config object. Adding a new host means creating one file
66
and re-exporting it. Zero code changes to the generator, setup, or tooling.
77

@@ -17,6 +17,7 @@ hosts/
1717
├── slate.ts # Slate (Random Labs)
1818
├── cursor.ts # Cursor
1919
├── openclaw.ts # OpenClaw (hybrid: config + adapter)
20+
├── pi.ts # Pi CLI
2021
└── index.ts # Registry: imports all, derives Host type
2122
```
2223

docs/PI.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Pi integration
2+
3+
[gstack](../README.md) supports [Pi](https://pi.dev) as a native host. Pi keeps
4+
its core CLI intentionally small and does not include sub-agents, so the Pi
5+
integration uses the [`pi-subagents`](https://pi.dev/packages/pi-subagents)
6+
package for delegation.
7+
8+
## Install
9+
10+
Run the normal setup command with the Pi host selected:
11+
12+
```bash
13+
./setup --host pi
14+
```
15+
16+
If Pi is installed and you use automatic host detection, this also works:
17+
18+
```bash
19+
./setup --host auto
20+
```
21+
22+
Setup installs the package at user scope when it is missing:
23+
24+
```bash
25+
pi install npm:pi-subagents
26+
```
27+
28+
The package provides the `subagent` and `subagent_wait` tools, the builtin
29+
`reviewer`, `worker`, `scout`, `researcher`, `delegate`, and `oracle` agents,
30+
and the `workflowScript` API for sequential and parallel workflows. Review the
31+
package source before installing it because Pi packages run with full system
32+
access.
33+
34+
## What setup installs
35+
36+
- Generated Pi skills under `~/.pi/agent/skills/gstack-*`.
37+
- A small runtime root at `~/.pi/agent/skills/gstack` containing gstack's
38+
binaries and runtime assets.
39+
- The `pi-subagents` package in Pi's global package settings.
40+
41+
Pi skill files are generated from the same `.tmpl` sources as the other hosts,
42+
but their frontmatter and paths are rewritten for Pi. Delegation sections stay
43+
enabled and refer to `pi-subagents` workflows instead of Claude Code's Agent
44+
tool.
45+
46+
## Updating
47+
48+
Run setup again after pulling a gstack update:
49+
50+
```bash
51+
cd /path/to/gstack
52+
./setup --host pi
53+
```
54+
55+
Update the Pi package separately when desired:
56+
57+
```bash
58+
pi update npm:pi-subagents
59+
```
60+
61+
Use `pi list` to confirm that the package is installed. If it is missing, run
62+
`pi install npm:pi-subagents` and restart Pi (or use `/reload`).
63+
64+
## Troubleshooting
65+
66+
### `subagent` is unavailable
67+
68+
Install the package at user scope and restart Pi:
69+
70+
```bash
71+
pi install npm:pi-subagents
72+
pi list
73+
```
74+
75+
If the package appears in `pi list` but the tool is still unavailable, run
76+
`/subagents-doctor` inside Pi and then `/reload`.
77+
78+
### Skills are stale
79+
80+
Regenerate the Pi output from the gstack checkout and rerun setup:
81+
82+
```bash
83+
bun run gen:skill-docs --host pi
84+
./setup --host pi
85+
```
86+
87+
Pi skills are generated into `.pi/skills/` in the checkout and copied or linked
88+
to the user skill directory by setup. The generated `.pi/` directory is
89+
runtime output and is intentionally gitignored.

hosts/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ import cursor from './cursor';
1616
import openclaw from './openclaw';
1717
import hermes from './hermes';
1818
import gbrain from './gbrain';
19+
import pi, { PI_SUBAGENTS_PACKAGE, PI_SUBAGENT_ROLES } from './pi';
1920

2021
/** All registered host configs. Add new hosts here. */
21-
export const ALL_HOST_CONFIGS: HostConfig[] = [claude, codex, factory, kiro, opencode, slate, cursor, openclaw, hermes, gbrain];
22+
export const ALL_HOST_CONFIGS: HostConfig[] = [claude, codex, factory, kiro, opencode, slate, cursor, openclaw, hermes, gbrain, pi];
2223

2324
/** Map from host name to config. */
2425
export const HOST_CONFIG_MAP: Record<string, HostConfig> = Object.fromEntries(
@@ -65,4 +66,4 @@ export function getExternalHosts(): HostConfig[] {
6566
}
6667

6768
// Re-export individual configs for direct import
68-
export { claude, codex, factory, kiro, opencode, slate, cursor, openclaw, hermes, gbrain };
69+
export { claude, codex, factory, kiro, opencode, slate, cursor, openclaw, hermes, gbrain, pi, PI_SUBAGENTS_PACKAGE, PI_SUBAGENT_ROLES };

hosts/pi.ts

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import type { HostConfig } from '../scripts/host-config';
2+
3+
/** Pi package installed by setup to provide the `subagent` tool and builtin agents. */
4+
export const PI_SUBAGENTS_PACKAGE = 'npm:pi-subagents';
5+
6+
/** Role mapping used by generated Pi delegation instructions. */
7+
export const PI_SUBAGENT_ROLES = {
8+
review: 'reviewer',
9+
implementation: 'worker',
10+
reconnaissance: 'scout',
11+
secondOpinion: 'oracle',
12+
general: 'delegate',
13+
} as const;
14+
15+
function piSingleWorkflow(agent: string, key = 'main'): string {
16+
return `Call the \`subagent\` tool with this input: {"workflowScript":"return runs.run('${key}', { agent: '${agent}', context: 'fresh', task: '<task>' })","async":false}.`;
17+
}
18+
19+
function piParallelWorkflow(agent: string): string {
20+
return `Call the \`subagent\` tool with this input: {"workflowScript":"const [first, second] = await runs.all([{ key: 'first', agent: '${agent}', task: '<first task>' }, { key: 'second', agent: '${agent}', task: '<second task>' }]); return [first.output, second.output]","async":false}. Add one runs.all item for each selected task and return the collected outputs.`;
21+
}
22+
23+
const pi: HostConfig = {
24+
name: 'pi',
25+
displayName: 'Pi',
26+
cliCommand: 'pi',
27+
cliAliases: [],
28+
29+
globalRoot: '.pi/agent/skills/gstack',
30+
localSkillRoot: '.pi/skills/gstack',
31+
hostSubdir: '.pi',
32+
usesEnvVars: true,
33+
34+
frontmatter: {
35+
mode: 'allowlist',
36+
keepFields: ['name', 'description'],
37+
descriptionLimit: 1024,
38+
},
39+
40+
generation: {
41+
generateMetadata: false,
42+
skipSkills: ['codex'],
43+
},
44+
45+
pathRewrites: [
46+
{ from: '~/.claude/skills/gstack', to: '~/.pi/agent/skills/gstack' },
47+
{ from: '~/.claude/skills', to: '~/.pi/agent/skills' },
48+
{ from: '$HOME/.claude/skills/gstack', to: '$HOME/.pi/agent/skills/gstack' },
49+
{ from: '$HOME/.claude/skills', to: '$HOME/.pi/agent/skills' },
50+
{ from: '.claude/skills/gstack', to: '.pi/skills/gstack' },
51+
{ from: '.claude/skills', to: '.pi/skills' },
52+
// Pi uses AGENTS.md for project instructions.
53+
{ from: 'CLAUDE.md', to: 'AGENTS.md' },
54+
// Identity rewrites keep generated instructions host-neutral.
55+
{ from: 'Claude Code', to: 'Pi' },
56+
{ from: 'claude code', to: 'Pi' },
57+
{ from: 'this Pi window', to: 'this Pi session' },
58+
],
59+
60+
toolRewrites: {
61+
'AskUserQuestion': 'ask the user in chat',
62+
'WebSearch': 'web search (if available)',
63+
'use the Bash tool': 'use the bash tool',
64+
'use the Write tool': 'use the write tool',
65+
'use the Read tool': 'use the read tool',
66+
'use the Edit tool': 'use the edit tool',
67+
'use the Grep tool': 'use the grep tool',
68+
'use the Glob tool': 'use the find tool',
69+
'the Bash tool': 'the bash tool',
70+
'the Read tool': 'the read tool',
71+
'the Write tool': 'the write tool',
72+
'the Edit tool': 'the edit tool',
73+
'the Grep tool': 'the grep tool',
74+
'the Glob tool': 'the find tool',
75+
76+
// pi-subagents exposes a workflowScript API rather than Claude's Agent
77+
// tool. Handle the complete high-value instructions before the generic
78+
// replacements below so the generated prose retains its intent.
79+
'**Launch N Agent subagents in a single message** (parallel execution). Use the Agent\ntool with `subagent_type: "general-purpose"` for each variant. Each agent is independent\nand handles its own generation, quality check, verification, and retry.': `**Launch N child agents in one workflowScript** (parallel execution). ${piParallelWorkflow(PI_SUBAGENT_ROLES.general)} Each child is independent and handles its own generation, quality check, verification, and retry.`,
80+
'**Dispatch this step as a subagent** using the Agent tool with `subagent_type: "general-purpose"`.': `**Dispatch this step with the subagent tool.** ${piSingleWorkflow(PI_SUBAGENT_ROLES.review)}`,
81+
'**Dispatch the fetch + classification as a subagent** using the Agent tool with `subagent_type: "general-purpose"`.': `**Dispatch the fetch + classification with the subagent tool.** ${piSingleWorkflow(PI_SUBAGENT_ROLES.reconnaissance)}`,
82+
'**Dispatch /document-release as a subagent** using the Agent tool with `subagent_type: "general-purpose"`.': `**Dispatch /document-release with the subagent tool.** ${piSingleWorkflow(PI_SUBAGENT_ROLES.implementation)}`,
83+
'For each selected specialist, launch an independent subagent via the Agent tool.\n**Launch ALL selected specialists in a single message** (multiple Agent tool calls)\nso they run in parallel. Each subagent has fresh context — no prior review bias.': `For each selected specialist, add a fresh reviewer child to one workflowScript. ${piParallelWorkflow(PI_SUBAGENT_ROLES.review)} Each child has fresh context — no prior review bias.`,
84+
'If activated, dispatch one more subagent via the Agent tool (foreground, not background).': `If activated, run one more reviewer child in a foreground workflow. ${piSingleWorkflow(PI_SUBAGENT_ROLES.review, 'red-team')}`,
85+
'For each candidate finding, launch an independent verification sub-task using the Agent tool. The verifier has fresh context and cannot see the initial scan\'s reasoning — only the finding itself and the FP filtering rules.': `For each candidate finding, add an independent reviewer child to a workflowScript. ${piParallelWorkflow(PI_SUBAGENT_ROLES.review)} Each verifier has fresh context and cannot see the initial scan's reasoning — only the finding itself and the FP filtering rules.`,
86+
'Launch all verifiers in parallel.': `Use a workflowScript with runs.all([...]) to launch all verifiers in parallel. ${piParallelWorkflow(PI_SUBAGENT_ROLES.review)}`,
87+
'Use the Agent tool to dispatch an independent reviewer. The reviewer has fresh context': `Use the subagent tool with a workflowScript. ${piSingleWorkflow(PI_SUBAGENT_ROLES.review)} The reviewer child has fresh context`,
88+
'**If CODEX_NOT_AVAILABLE (or Codex errored):**\n\nDispatch via the Agent tool. The subagent has fresh context — genuine independence.': `**If CODEX_NOT_AVAILABLE (or Codex errored):**\n\nUse the \`subagent\` tool with a workflowScript. ${piSingleWorkflow(PI_SUBAGENT_ROLES.secondOpinion)} The oracle child has fresh context — genuine independence.`,
89+
'Dispatch via the Agent tool. The subagent has fresh context': `Use the \`subagent\` tool with a workflowScript. ${piSingleWorkflow(PI_SUBAGENT_ROLES.review)} The reviewer child has fresh context`,
90+
'Dispatch via the Agent tool with the same prompt.': `Use the \`subagent\` tool with a workflowScript. ${piSingleWorkflow(PI_SUBAGENT_ROLES.review)} Pass it the same prompt and bound it at a 5-minute timeout.`,
91+
'Dispatch via the Agent tool.': `Use the \`subagent\` tool with a workflowScript. ${piSingleWorkflow(PI_SUBAGENT_ROLES.review)} `,
92+
93+
// The autoplan and design templates describe the Agent tool in several
94+
// shorter forms. Keep those instructions, but point them at Pi's API.
95+
'via Agent tool': `via the \`subagent\` tool. ${piSingleWorkflow(PI_SUBAGENT_ROLES.review)}`,
96+
'foreground Agent tool': 'foreground `subagent` workflow (`async: false`)',
97+
'Agent tool,': '`subagent` tool with a `workflowScript`,',
98+
'Agent tool)': '`subagent` tool with a `workflowScript`)',
99+
'Claude subagent': 'Pi reviewer subagent',
100+
'Claude adversarial subagent': 'Pi adversarial reviewer subagent',
101+
'Claude design subagent': 'Pi design reviewer subagent',
102+
'Claude CEO subagent': 'Pi CEO reviewer subagent',
103+
'Claude eng subagent': 'Pi eng reviewer subagent',
104+
'Claude DX subagent': 'Pi DX reviewer subagent',
105+
'Claude-only': 'Pi-only',
106+
'Claude ': 'Pi ',
107+
'CLAUDE SUBAGENT': 'PI REVIEWER SUBAGENT',
108+
'Subagent prompt': 'Child-agent prompt',
109+
'subagent prompt': 'child-agent prompt',
110+
'subagent_type: "general-purpose"': 'agent: "delegate"',
111+
'do NOT use run_in_background': 'set `async: false` for this foreground run',
112+
'run_in_background': '`async: true`',
113+
'Claude Code\'s Agent tool': 'Pi\'s `subagent` tool',
114+
'agents do NOT inherit': 'child agents do NOT inherit',
115+
'Agent subagents': 'subagent children',
116+
'Agent tool': '`subagent` tool',
117+
},
118+
119+
// Unlike Pi's bare CLI, pi-subagents provides the child sessions needed by
120+
// these workflows. Keep the delegation and outside-voice resolvers enabled;
121+
// setup installs the package before Pi skills are generated.
122+
suppressedResolvers: [],
123+
124+
runtimeRoot: {
125+
globalSymlinks: [
126+
'bin', 'browse/dist', 'browse/bin', 'design/dist', 'make-pdf/dist',
127+
'extension', 'lib/diagram-render', 'gstack-upgrade', 'ETHOS.md',
128+
],
129+
globalFiles: {
130+
'review': ['checklist.md', 'TODOS-format.md'],
131+
},
132+
},
133+
134+
install: {
135+
prefixable: false,
136+
linkingStrategy: 'symlink-generated',
137+
},
138+
139+
learningsMode: 'basic',
140+
};
141+
142+
export default pi;

scripts/gen-skill-docs.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,13 @@ function processExternalHost(
780780
// Transform frontmatter (host-aware)
781781
let result = transformFrontmatter(content, host);
782782

783+
// Pi requires the frontmatter name to match the parent directory.
784+
// Templates use short names (e.g., "ship") but Pi emits into
785+
// gstack-prefixed directories (e.g., gstack-ship/).
786+
if (host === 'pi') {
787+
result = result.replace(/^(name:\s*).+$/m, `$1${name}`);
788+
}
789+
783790
// Insert safety advisory at the top of the body (after frontmatter)
784791
if (safetyProse) {
785792
const bodyStart = result.indexOf('\n---') + 4;

0 commit comments

Comments
 (0)