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:
parent
1c6fc220ea
commit
57d238a1ea
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
.pnpm-store/
|
||||
pnpm-lock.yaml
|
||||
32
AGENTS.md
Normal file
32
AGENTS.md
Normal 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
11
package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
17
packages/pi-turn-limit/package.json
Normal file
17
packages/pi-turn-limit/package.json
Normal 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"
|
||||
}
|
||||
56
packages/pi-turn-limit/src/index.ts
Normal file
56
packages/pi-turn-limit/src/index.ts
Normal 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
2
pnpm-workspace.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
packages:
|
||||
- "packages/*"
|
||||
18
tsconfig.json
Normal file
18
tsconfig.json
Normal 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"]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user