Browse Source

Add documentation for agent personas and update package metadata

- Create AGENTS.md to describe the pi-mainagent extension and its architecture.
- Update README.md to clarify the purpose of the package.
- Bump version in package.json to 1.0.1.
- Add module index and persona frontmatter specifications in specs directory.
softword 1 week ago
parent
commit
b4b751769d
5 changed files with 100 additions and 1 deletions
  1. 54 0
      AGENTS.md
  2. 2 0
      README.md
  3. 2 1
      package.json
  4. 9 0
      specs/module-index.md
  5. 33 0
      specs/persona-frontmatter.md

+ 54 - 0
AGENTS.md

@@ -0,0 +1,54 @@
+# pi-mainagent
+
+pi extension providing agent personas: `/mainagent` command to switch between Markdown-defined personas (model, thinking level, tool filters), with persisted selection across sessions.
+
+## Tech Stack
+
+- TypeScript (pi extension, no build step — loaded as source by pi)
+- Runtime APIs: `@earendil-works/pi-coding-agent` (`ExtensionAPI`, `ExtensionContext`), `pi-tui` dialogs, `pi-ai` model catalog
+- No dependencies bundled: core pi packages are `peerDependencies`
+
+## Architecture
+
+- Single extension file: `extensions/pi-mainagent.ts` (entry point: `export default function piMainagentExtension`)
+- State: `~/.pi/agent/agents/*.md` (persona definitions) + `~/.pi/agent/agent-state.json` (active persona name)
+- Flow: `registerCommand("mainagent")` → picker dialog → `applySelection()` mutates session (model, thinking, tool policy, appended prompt) and updates the `main-agent-status` widget
+- Events used: `session_start` (re-apply persisted selection), `before_agent_start` (persona prompt), `session_shutdown`
+
+## Commands
+
+- No build/test/lint configured. Verify changes by loading the extension: `pi -e D:/source/pi-mainagent`
+
+## Code Style
+
+- ES modules, 2-space indent, double quotes
+- Constants in SCREAMING_SNAKE_CASE at top of file; helpers as plain functions
+- UI strings via pi TUI components (`showSelectDialog`, theme-aware)
+
+## Coding Rules
+
+- Do NOT replace the minimal frontmatter parser with a YAML library — see @specs/persona-frontmatter.md for the exact supported grammar
+- Persona prompts are APPENDED to pi's system prompt; never strip base prompt or skills
+- `/mainagent off` must restore previous session settings exactly (saved in memory, not re-derived)
+- Prompt files > 64 KB (`MAX_PROMPT_FILE_BYTES`) are rejected
+- Publish hygiene: only `extensions/` ships to npm (`"files"` in package.json); docs and drafts stay out
+
+## Constraints / What to avoid
+
+- No new runtime dependencies (keep `peerDependencies` only)
+- Do not persist tool/model changes globally — they are session-scoped; only the persona *name* is persisted
+- Never write outside `~/.pi/agent/` (agents dir, state file, prompt files referenced by definitions)
+
+## Workflow
+
+1. Edit `extensions/pi-mainagent.ts`
+2. Verify: `pi -e D:/source/pi-mainagent` → run `/mainagent` in the TUI
+3. Commit; to release: bump version in `package.json`, `npm publish`
+
+Self-maintenance rule:
+> When you modify code, update the relevant file in `specs/`
+
+## References
+
+- @specs/module-index.md — modules and criticality tiers
+- @specs/persona-frontmatter.md — persona file grammar and editing rules

+ 2 - 0
README.md

@@ -1,5 +1,7 @@
 # pi-mainagent
 
+[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/D5G722BFYK)
+
 A [pi](https://pi.dev) package that brings **agent personas** to your coding agent — the same idea as `claude --agent <name>` in Claude Code, or starting an [opencode](https://opencode.ai) session with a pre-selected agent.
 
 > **Note:** pi does **not** ship this feature natively. Out of the box there is no way to define reusable agent personas or start pi with a pre-selected agent — this package fills that gap.

+ 2 - 1
package.json

@@ -1,7 +1,8 @@
 {
   "name": "pi-mainagent",
-  "version": "1.0.0",
+  "version": "1.0.1",
   "description": "Agent personas for pi, like claude --agent <name> in Claude Code: switch personas with /mainagent and start pi directly with your persisted agent — model, thinking level and tool filters included",
+  "files": ["extensions"],
   "keywords": ["pi-package"],
   "license": "MIT",
   "repository": {

+ 9 - 0
specs/module-index.md

@@ -0,0 +1,9 @@
+Read this when planning changes — module map and criticality tiers.
+
+| Module | Path | Tier | Summary | Spec link |
+|---|---|---|---|---|
+| Extension entry & wiring | `extensions/pi-mainagent.ts` (`piMainagentExtension`, `registerMainAgentFeatures`) | 1 | Registers command, events, widget; single-file extension | — |
+| Persona loading & state | `extensions/pi-mainagent.ts` (`loadAgents`, `readState`, `writeState`, `agentsDir`) | 1 | Reads `~/.pi/agent/agents/*.md`, persists active persona name to `agent-state.json` | @specs/persona-frontmatter.md |
+| Frontmatter parse & edit | `extensions/pi-mainagent.ts` (frontmatter helpers, `applyFrontmatterUpdates`) | 1 | Minimal YAML-subset parser + in-place field editing preserving user formatting | @specs/persona-frontmatter.md |
+| Selection application | `extensions/pi-mainagent.ts` (`applySelection`, tool pattern expansion) | 2 | Applies model/thinking/tool filters to the session; patterns like `ssh-manager_*` expanded against registered tools | — |
+| TUI dialogs | `extensions/pi-mainagent.ts` (`showAgentPicker`, `showAgentActions`, `showModelEffortPicker`) | 2 | SelectList-based menus; max 10 visible items, theme-aware | — |

+ 33 - 0
specs/persona-frontmatter.md

@@ -0,0 +1,33 @@
+Read this when touching persona definition parsing or frontmatter editing.
+
+# Persona file frontmatter
+
+## Purpose
+
+Persona definitions are Markdown files in `~/.pi/agent/agents/` with an optional `---` frontmatter block. The extension parses them with a hand-written YAML *subset* parser (no YAML dependency) and edits fields in place so user formatting/comments survive.
+
+## Data model
+
+`AgentDef` (parsed): `name`, `description`, `model` (`provider/id`), `thinking` (ThinkingLevel), `tools: string[]`, `excludeTools: string[]`, `prompt` (path to external Markdown file, max 64 KB), body text.
+
+| Constant | Value / location | Meaning |
+|---|---|---|
+| `AGENTS_SUBDIR` | `agents` | subdir under `~/.pi/agent/` |
+| `STATE_FILENAME` | `agent-state.json` | persisted active persona name |
+| `MAX_PROMPT_FILE_BYTES` | 64 * 1024 | hard cap for external prompt files |
+| `UNSUPPORTED_YAML_SCALAR_PREFIX` | `/^[!&*[{\|>-]/` | scalar prefixes that make the file "unsupported" (read-only in menus) |
+| `DEFAULT_EFFORT` | `medium` | thinking level when unspecified |
+
+## Business rules
+
+- Only a YAML subset is supported: flat `key: value` scalars and block lists (`- item` with 2-space indent, see `DESCRIPTION_BLOCK_INDENT` / `isBlockValueLine`). Anything containing an unsupported scalar prefix (`!`, `&`, `*`, `[`, `{`, `|`, `>`, `-` inline) marks the file as unsupported → shown but not editable.
+- Files may start with a UTF-8 BOM (`UTF8_BOM` constant) — must be stripped before parsing.
+- Frontmatter is optional; a file with body only is a valid persona (prompt only).
+- Field edits (`updateFrontmatterFields`) rewrite ONLY the target key's extent (`findKeyExtent`), leaving all other lines byte-identical — comments and blank lines inside frontmatter must survive.
+- Model IDs use exactly one `/` separator (`MODEL_ID_SEPARATOR`); thinking values validated against `THINKING_LEVELS`.
+- Tool entries may be glob-ish patterns (`ssh-manager_*`); they are expanded against tools registered in the running session (`expandToolPatterns`), not matched against a static list.
+- State file (`agent-state.json`) stores only the active persona **name**; if the named file no longer exists, the state is treated as absent (no error).
+
+## Cross-links
+
+- Module map: @specs/module-index.md