feat: restructure as pnpm monorepo workspace

- Move limit-turns.ts into packages/pi-turn-limit/src/index.ts
- Add root package.json (private workspace manifest)
- Add pnpm-workspace.yaml
- Add shared tsconfig.json
- Add pi package manifest to extension (pi.extensions, pi-package keyword)
- Update AGENTS.md with monorepo workflow
This commit is contained in:
Willem van den Ende 2026-04-20 23:04:34 +01:00
parent 1c6fc220ea
commit 57d238a1ea
7 changed files with 139 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules/
.pnpm-store/
pnpm-lock.yaml

32
AGENTS.md Normal file
View File

@ -0,0 +1,32 @@
This is a monorepo for pi extensions using pnpm workspaces.
## Structure
```
packages/
pi-turn-limit/ # First extension: limits the number of turns
```
## Development
```bash
pnpm install # Install all workspace dependencies
```
Each extension lives in `packages/<name>/` with its own `package.json`.
The root `package.json` is the workspace manifest.
## Adding a new extension
1. Create `packages/<name>/` directory
2. Add `package.json` with `"pi-package"` keyword and `pi.extensions` manifest
3. Put source in `packages/<name>/src/index.ts`
4. Run `pnpm install`
## Extension packaging
Extensions are loaded by pi as TypeScript files via jiti (no compilation needed).
The `pi` key in `package.json` tells pi which files to load.
See `~/.local/share/mise/installs/node/24.0.1/lib/node_modules/@mariozechner/pi-coding-agent/docs/extensions.md`
and `packages.md` for full details.

11
package.json Normal file
View File

@ -0,0 +1,11 @@
{
"name": "pi-extensions",
"version": "0.1.0",
"private": true,
"description": "Pi coding agent extensions monorepo",
"type": "module",
"license": "MIT",
"engines": {
"node": ">=24"
}
}

View File

@ -0,0 +1,17 @@
{
"name": "pi-turn-limit",
"version": "0.1.0",
"description": "Pi coding agent extension to limit number of turns taken by a model",
"type": "module",
"exports": {
".": "./src/index.ts"
},
"keywords": ["pi-package"],
"pi": {
"extensions": ["src/index.ts"]
},
"peerDependencies": {
"@mariozechner/pi-coding-agent": "*"
},
"license": "MIT"
}

View File

@ -0,0 +1,56 @@
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
// ============================================================================
// Configuration
// ============================================================================
const DEFAULT_MAX_TURNS = 25;
function getMaxTurns(): number {
const env = process.env.PI_MAX_TURNS;
if (!env) return DEFAULT_MAX_TURNS;
const parsed = parseInt(env, 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_MAX_TURNS;
}
// ============================================================================
// Pure detection logic (testable)
// ============================================================================
export function checkTurnLimit(
turnIndex: number,
maxTurns: number,
): { exceeded: boolean; turnIndex: number; maxTurns: number } {
return {
exceeded: turnIndex > maxTurns,
turnIndex,
maxTurns,
};
}
// ============================================================================
// Extension handler
// ============================================================================
export default function (pi: ExtensionAPI) {
let turnCount = 0;
const maxTurns = getMaxTurns();
pi.on("agent_start", async (_event, ctx) => {
// Reset counter for each new user prompt
turnCount = 0;
});
pi.on("turn_start", async (event, ctx) => {
turnCount++;
const { exceeded } = checkTurnLimit(event.turnIndex, maxTurns);
if (exceeded && ctx.hasUI) {
ctx.ui.notify(
`Turn limit exceeded: ${maxTurns} turns reached. Agent aborted.`,
"error",
);
ctx.abort();
}
});
}

2
pnpm-workspace.yaml Normal file
View File

@ -0,0 +1,2 @@
packages:
- "packages/*"

18
tsconfig.json Normal file
View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["packages/**/*"],
"exclude": ["node_modules"]
}