@maestria/pi 0.1.0
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/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/extension.mjs +16 -0
- package/dist/extension.mjs.map +1 -0
- package/package.json +66 -0
- package/prompts/adventurer.md +154 -0
- package/prompts/architect.md +141 -0
- package/prompts/builder.md +151 -0
- package/prompts/diagnose.md +143 -0
- package/prompts/orchestrator.md +312 -0
- package/prompts/planner.md +93 -0
- package/prompts/reviewer.md +155 -0
- package/prompts/writer.md +135 -0
- package/rules/AGENTS.md +69 -0
- package/skills/handoff/SKILL.md +19 -0
- package/skills/iteration-limits/SKILL.md +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Agustinus Nathaniel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# @maestria/pi
|
|
2
|
+
|
|
3
|
+
A [Pi coding agent](https://pi.software/) extension that brings Maestria's structured agent orchestration to Pi.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **8 Specialist Prompts** — Orchestrator, Adventurer, Architect, Builder, Diagnose, Planner, Reviewer, Writer
|
|
8
|
+
- **3 Workflow Modes** — `fein` (full pipeline), `sonar` (research only), `blitz` (fast implementation)
|
|
9
|
+
- **Global Rules Injection** — Automatically injects orchestration rules via `before_agent_start`
|
|
10
|
+
- **Compaction Preservation** — Session state survives compaction with structured summaries
|
|
11
|
+
- **Subagent Dispatch** — Delegation via `@gotgenes/pi-subagents` with 6-field handoff validation
|
|
12
|
+
- **Maker/Checker Split** — Review mode blocks destructive tools. Dangerous bash patterns flagged.
|
|
13
|
+
- **2 Methodology Skills** — Handoff contract + iteration limits
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pi install npm:@maestria/pi
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Commands
|
|
22
|
+
|
|
23
|
+
| Command | Description |
|
|
24
|
+
| -------------------------- | -------------------------------------------------------------------- |
|
|
25
|
+
| `/fein <goal>` | Set workflow mode to full pipeline (recon → design → impl → review) |
|
|
26
|
+
| `/sonar <goal>` | Set workflow mode to research only (recon → design → stop) |
|
|
27
|
+
| `/blitz <goal>` | Set workflow mode to fast implementation (builder directly) |
|
|
28
|
+
| `/orchestrate <goal>` | Start a full pipeline by delegating to the orchestrator |
|
|
29
|
+
| `/review <target>` | Enter review mode — blocks destructive tools, sets read-only toolset |
|
|
30
|
+
| `/restore-model` | Restore the original model and tools active before review mode |
|
|
31
|
+
| `/handoff <goal>` | Generate a structured handoff prompt for a new task context |
|
|
32
|
+
| `/review-model <model-id>` | Set which model to use when entering review mode |
|
|
33
|
+
| `/maestria-status` | Show current maestria session state including handoff history |
|
|
34
|
+
|
|
35
|
+
## Development
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Install dependencies
|
|
39
|
+
pnpm install
|
|
40
|
+
|
|
41
|
+
# Build
|
|
42
|
+
vp pack
|
|
43
|
+
|
|
44
|
+
# Test
|
|
45
|
+
vp test
|
|
46
|
+
|
|
47
|
+
# Format, lint, type-check
|
|
48
|
+
vp check
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
MIT
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import{Type as e}from"typebox";import{SUBAGENT_EVENTS as t}from"@gotgenes/pi-subagents";import{isToolCallEventType as n}from"@earendil-works/pi-coding-agent";function r(){return{mode:null,activeTask:``,completionPromise:``,specialistsDelegated:[],blockers:[],filesModified:[],filesRead:[],handoffHistory:[],reviewMode:!1,originalModel:null,originalTools:null,subagentStatus:{},reviewModel:null}}function i(e,t,n,r){let i=[{from:t,to:n,task:r,timestamp:Date.now()},...e.handoffHistory].slice(0,5);return{...e,handoffHistory:i}}function a(e){return{state:{...e,reviewMode:!1,originalModel:null,originalTools:null},originalModel:e.originalModel,originalTools:e.originalTools}}async function o(e,t,n){let{state:r,originalModel:i,originalTools:o}=a(n);if(o&&o.length>0&&e.setActiveTools(o),i)try{let n=t.modelRegistry.getAll().find(e=>e.id===i);n&&await e.setModel(n)}catch{}Object.assign(n,r)}function s(e,t){e.appendEntry(`maestria_state`,{...t})}async function c(e,t,n){let r=n.reviewModel;if(!r)return null;try{let n=t.modelRegistry.getAll().find(e=>e.id===r);return n?(await e.setModel(n),r):(t.ui.notify(`Review model "${r}" not found in registry, staying on current.`),null)}catch{return t.ui.notify(`Could not switch to review model "${r}", staying on current.`),null}}function l(e){let t=[];if(e.mode&&t.push(`**Mode:** ${e.mode.toUpperCase()}`),e.reviewModel&&t.push(`**Review Model:** ${e.reviewModel}`),e.activeTask&&t.push(`**Goal:** ${e.activeTask}`),e.completionPromise&&t.push(`**Completion Promise:** ${e.completionPromise}`),e.specialistsDelegated.length>0&&t.push(`**Specialists Delegated:** ${e.specialistsDelegated.join(`, `)}`),e.blockers.length>0){t.push(`**Blockers:**`);for(let n of e.blockers)t.push(`- ${n}`)}let n=[];if(e.filesModified.length>0&&n.push(`**Modified:** ${e.filesModified.join(`, `)}`),e.filesRead.length>0&&n.push(`**Read:** ${e.filesRead.join(`, `)}`),n.length>0&&t.push(`**Files:** ${n.join(`; `)}`),e.handoffHistory.length>0){t.push(`**Recent Handoffs:**`);for(let n of e.handoffHistory)t.push(`- ${n.from} → ${n.to}: ${n.task}`)}return t.join(`
|
|
2
|
+
|
|
3
|
+
`)}const u=[`fein`,`sonar`,`blitz`],d={fein:[`## MODE: fein (Full Pipeline)`,``,`Execute the complete fein pipeline: mandatory reconnaissance`,`(/adventurer) → design/plan (/architect or /planner) →`,`implementation (/builder) → review (/reviewer).`,`Do NOT skip any phase unless the user explicitly overrides`,`in the same turn.`].join(`
|
|
4
|
+
`),sonar:[`## MODE: sonar (Research Only)`,``,`Execute research only: /adventurer (recon) →`,`/architect or /planner (design/plan) → STOP.`,`Do NOT implement anything. Return findings.`].join(`
|
|
5
|
+
`),blitz:[`## MODE: blitz (Fast Implementation)`,``,`Execute fast implementation via /builder directly.`,`Skip reconnaissance and design unless the codebase`,`is genuinely unknown. Skip review unless the result`,`needs validation.`].join(`
|
|
6
|
+
`)},f={fein:`[MODE: fein]`,sonar:`[MODE: sonar]`,blitz:`[MODE: blitz]`};function p(e){return`${f[e]}\n\n${d[e]}`}function m(e,t){for(let n of u)e.registerCommand(n,{description:`Set workflow mode to ${n}`,handler:async(r,i)=>{if(t.reviewMode&&await o(e,i,t),t.mode=n,s(e,t),r.trim()){let t=[p(n),``,`Run the maestria default pipeline on: ${r}`].join(`
|
|
7
|
+
`);e.sendUserMessage(t,{deliverAs:`steer`})}else i.ui.notify(`Mode set to ${n}. Describe what you'd like to work on.`)}})}function h(e){return(t,n)=>{let r=["<!-- Source: packages/pi/rules/AGENTS.md — sync both files when updating -->\n\n# Global Agent Rules — @maestria/pi\n\n## Orchestration\n\n- **!!! Don't assume** — verify against actual code and docs.\n Guesses lead to bugs.\n- **!!! Read the docs first** — before writing code that touches\n unfamiliar tools, APIs, or migration paths, consult official\n documentation. Don't guess at API changes. This rule is scar\n tissue from repeated failures; treat it seriously.\n- **Don't reference internal project names in explanations** — avoid\n leaking context outside the workspace.\n- **Use `opensrc` for repos; `webfetch` for pages** — when analyzing a\n GitHub/GitLab/BitBucket repo or any multi-file code reference, run\n `opensrc path <owner/repo>` (e.g. `opensrc path facebook/react`).\n It clones to a global cache and prints a path that `read`/`glob`/`grep`\n can use directly. For a single file, a specific page, or a known\n URL, `webfetch` is fine. Don't fetch an entire repo one file at a\n time — clone it once, then read locally. Use `--cwd` to resolve\n versions from the current project.\n- **Webfetch may hang — don't block on it** — if a `webfetch` request hangs after you've issued it, **proceed without the result** and surface the skip in your next user-facing message. Don't wait for a hung fetch to complete.\n- **Workflow modes** — keywords `fein` (full pipeline), `sonar` (research only),\n `blitz` (fast impl) activate per-turn workflow overrides. See the\n orchestrator prompt for details.\n- **CLI references — use local tools first** — for CLI references, run `bash --help` or load the relevant `skill` instead of reaching for `webfetch`. Local tools are faster and more reliable than fetching docs.\n- **Local files — read directly** — use `read`, `glob`, or `grep` (or `lsp` when available) for any file you have path access to. Don't `webfetch` a local file or a file in a checked-out repo.\n- **Tool hierarchy for external information:**\n 1. `webfetch` — fetch a specific known URL (for docs, pages)\n 2. `websearch` — discover relevant pages (for finding unknown resources)\n Use `webfetch` when you know the URL; use `websearch` when you need to find\n something. `websearch` is an `ask`-only permission — explain what you're\n searching for and why before using it.\n\n## Delegation\n\nWhen delegating work via \\`maestria_subagent()\\`, use only the 7 specialists below.\n**Never delegate to `explore` or `general`** — they are built-in agents,\nnot part of the pipeline.\n\n| Agent | Role | When to Delegate |\n| ------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------- |\n| `/adventurer` | Codebase reconnaissance, deep code understanding | Understanding unfamiliar code, tracing dependencies, gathering context before implementation |\n| `/architect` | Architecture decisions, trade-off analysis, ADRs | Choosing between approaches, technology evaluation |\n| `/builder` | Focused implementation, single-task execution | Feature work, bug fixes, test writing, refactors |\n| `/diagnose` | Systematic bug tracing, root cause analysis | Debugging regressions, production incidents, cryptic errors |\n| `/planner` | Implementation plans with phased milestones | Complex features requiring structured execution |\n| `/reviewer` | Code review with quality gates | Pre-merge review, security audit, post-implementation QA |\n| `/writer` | Documentation following structured patterns | READMEs, API docs, changelogs, ADR transcription |\n\n## Context Management\n\n- **Progressive disclosure** — start high-level, get specific as needed.\n- **State checkpointing** — periodically summarize what's done, what's\n in progress, what's next.\n- **Context pruning** — remove irrelevant context when no longer needed.\n- **Completion promises** — define success criteria before starting work.\n \"This task is complete when [verifiable conditions].\"\n\n## Commit Policy\n\n- **Only the orchestrator authorizes commits.** Subagents must refuse\n commit requests and redirect to the orchestrator.\n- **Builders executing commits** must follow the orchestrator's exact\n instructions (message, files, `check`/`test`). Flag it if the\n orchestrator's instructions skip the commit protocol.\n- **Plans must not include implicit commit steps.** Commit authorization\n is a separate orchestrator step requiring explicit user approval.\n",``,t.systemPrompt];return e.mode&&(r.push(``,p(e.mode)),r.push(``,`The user has set workflow mode to "${e.mode}". Honor this mode throughout the session until changed via /command.`)),{systemPrompt:r.join(`
|
|
8
|
+
`)}}}function g(e,t){e.on(`session_before_compact`,e=>({compaction:{summary:l(t),details:{...t},firstKeptEntryId:e.preparation.firstKeptEntryId,tokensBefore:e.preparation.tokensBefore}})),e.on(`session_before_tree`,e=>{if(e.preparation.userWantsSummary)return{summary:{summary:l(t)}}})}const _={REVIEW_ACTIVATED:`maestria:review:activated`,REVIEW_DEACTIVATED:`maestria:review:deactivated`,SUBAGENT_STARTED:`maestria:subagent:started`,SUBAGENT_COMPLETED:`maestria:subagent:completed`,SUBAGENT_FAILED:`maestria:subagent:failed`},v=[`adventurer`,`architect`,`builder`,`diagnose`,`planner`,`reviewer`,`writer`],y=new Set([`completed`,`steered`,`aborted`,`stopped`,`error`]);function b(n,r,a){if(n.registerTool({name:`maestria_subagent`,label:`Maestria Subagent`,description:`Dispatch a task to a @maestria specialist subagent`,promptSnippet:`Delegate tasks to @maestria specialist subagents (adventurer, architect, builder, planner, diagnose, reviewer, writer)`,promptGuidelines:[`Use maestria_subagent when a task MUST be delegated to a specialist subagent rather than handled directly. Each specialist has focused capabilities: adventurer (recon), architect (design), builder (impl), planner (planning), diagnose (bugs), reviewer (QA), writer (docs).`],prepareArguments(e){return e},parameters:e.Object({agent:e.Optional(e.String({description:`Specialist agent name`})),task:e.Optional(e.String({description:`Task description for the subagent`})),tasks:e.Optional(e.Array(e.Object({agent:e.String(),task:e.String()}),{description:`Array of task objects for parallel or chain dispatch`})),mode:e.Optional(e.Union([e.Literal(`parallel`),e.Literal(`chain`),e.Literal(`single`)]))}),async execute(e,t,a,o,s){if(r.reviewMode)return{content:[{type:`text`,text:`Subagent dispatch is not available during review mode. Use /restore-model to exit review mode first.`}]};let c=t.mode??`single`;if(c===`single`){if(!v.includes(t.agent))throw Error(`Unknown agent: "${t.agent}". Allowed: ${v.join(`, `)}`);if(!t.task||!t.task.trim())throw Error(`Task description is required`)}else if(c===`parallel`){if(!t.tasks||t.tasks.length<2)throw Error(`For parallel mode, tasks array is required with at least 2 items`);if(t.tasks.length>8)throw Error(`For parallel mode, tasks array may have at most 8 items (got ${t.tasks.length})`);for(let e of t.tasks){if(!v.includes(e.agent))throw Error(`Unknown agent: "${e.agent}". Allowed: ${v.join(`, `)}`);if(!e.task||!e.task.trim())throw Error(`Task description is required for all tasks`)}}else if(c===`chain`){if(!t.tasks||t.tasks.length<2)throw Error(`For chain mode, tasks array is required with at least 2 items`);for(let e of t.tasks){if(!v.includes(e.agent))throw Error(`Unknown agent: "${e.agent}". Allowed: ${v.join(`, `)}`);if(!e.task||!e.task.trim())throw Error(`Task description is required for all tasks`)}}try{let{getSubagentsService:e}=await import(`@gotgenes/pi-subagents`),s=e();if(typeof s.spawn!=`function`)throw Error(`Subagents service unavailable or incomplete`);async function l(e,t,n){let r=0,i=s.getRecord(e);for(;i&&!y.has(i.status)&&r<120;){if(a?.aborted)throw Error(`Maestria subagent call aborted`);await new Promise(e=>setTimeout(e,500)),i=s.getRecord(e),r++,n&&o?.({content:[{type:`text`,text:`${t} running... (${Math.round(r*500/1e3)}s)`}]})}if(i&&!y.has(i.status))throw Error(`Subagent ${e} timed out after 60000ms`);if(!i)throw Error(`Subagent ${e} was cleaned up before completion`);return i}if(c===`single`){let e=t.agent,a=t.task,o=s.spawn(e,a,{description:a.slice(0,80),foreground:!0,inheritContext:!0}),c=i(r,`orchestrator`,e,a);Object.assign(r,c),n.appendEntry(`maestria_state`,r);let u=await l(o,`Subagent ${e}`,!0);return{content:[{type:`text`,text:u.result??u.error??`No output.`}],details:{subagentId:o}}}if(c===`parallel`){let e=t.tasks;o?.({content:[{type:`text`,text:`Spawning ${e.length} parallel subagents...`}]});let a=[];for(let t of e){let e=s.spawn(t.agent,t.task,{description:t.task.slice(0,80),foreground:!0,inheritContext:!0});a.push(e);let n=i(r,`orchestrator`,t.agent,t.task);Object.assign(r,n)}n.appendEntry(`maestria_state`,r);let c=await Promise.all(a.map((t,n)=>l(t,`${e[n].agent} (${n+1}/${e.length})`,!1)));o?.({content:[{type:`text`,text:`All ${e.length} parallel subagents completed.`}]});let u=[`## Parallel Results (${e.length} tasks)\n`];for(let t=0;t<e.length;t++){let n=e[t],r=c[t],i=r.result??r.error??`No output.`;u.push(`### ${t+1}: ${n.agent}`),u.push(i)}return{content:[{type:`text`,text:u.join(`
|
|
9
|
+
|
|
10
|
+
`)}],details:{subagentIds:a}}}if(c===`chain`){let e=t.tasks,a=``;for(let t=0;t<e.length;t++){let c=e[t],u=c.task;t>0&&u.includes(`{previous}`)&&(u=u.replace(/\{previous\}/g,a));let d=s.spawn(c.agent,u,{description:u.slice(0,80),foreground:!0,inheritContext:!0}),f=i(r,`orchestrator`,c.agent,u);Object.assign(r,f),n.appendEntry(`maestria_state`,r),o?.({content:[{type:`text`,text:`Chain step ${t+1}/${e.length}: ${c.agent} running...`}]});let p=await l(d,`Chain step ${t+1}: ${c.agent}`,!0);a=p.result??p.error??`No output.`,t<e.length-1&&o?.({content:[{type:`text`,text:`Chain step ${t+1}/${e.length}: ${c.agent} completed. Moving to next step.`}]})}return{content:[{type:`text`,text:a}],details:{subagentId:`chain-completed`}}}throw Error(`Unknown dispatch mode`)}catch{let e=t.agent??t.tasks?.[0]?.agent??`unknown`,n=t.task??t.tasks?.map(e=>e.task).join(`; `)??`unknown`;return{content:[{type:`text`,text:[`## Subagent Handoff Required`,``,`**From:** orchestrator`,`**To:** ${e}`,`**Task:** ${n}`,``,`Subagent SDK not available. Please delegate this work manually.`].join(`
|
|
11
|
+
`)}]}}}}),n.events){let e=n.events.on(t.STARTED,e=>{let{id:t,type:i}=e;r.subagentStatus[t]={type:i,status:`running`,startedAt:Date.now()},s(n,r),n.events?.emit(_.SUBAGENT_STARTED,{id:t,type:i,timestamp:Date.now()})}),i=n.events.on(t.COMPLETED,e=>{let{id:t}=e,i=r.subagentStatus[t];i&&(i.status=`completed`,i.completedAt=Date.now()),s(n,r),n.events?.emit(_.SUBAGENT_COMPLETED,{id:t,type:i?.type,timestamp:Date.now()})}),o=n.events.on(t.FAILED,e=>{let{id:t,status:i}=e,a=r.subagentStatus[t];a&&(a.status=i??`error`,a.completedAt=Date.now()),s(n,r),n.events?.emit(_.SUBAGENT_FAILED,{id:t,type:a?.type,timestamp:Date.now()})}),c=n.events.on(t.STEERED,e=>{let{id:t}=e;r.subagentStatus[t]||(r.subagentStatus[t]={type:`unknown`,status:`running`,startedAt:Date.now()}),s(n,r)});a&&a.push(e,i,o,c)}}const x=[`read`,`grep`,`find`,`ls`,`glob`];function S(e,t){e.registerCommand(`orchestrate`,{description:`Start a full pipeline by delegating to the orchestrator`,handler:async(n,r)=>{if(!n.trim()){r.ui.notify(`Usage: /orchestrate <goal> — describe what to accomplish`);return}if(t.reviewMode){let n=t.originalModel;await o(e,r,t),s(e,t),e.events?.emit(_.REVIEW_DEACTIVATED,{originalModel:n,source:`orchestrate`,timestamp:Date.now()})}e.sendUserMessage([`[ORCHESTRATE: ${n}]`,``,`Orchestrate the full maestria pipeline to: ${n}`,`Use the orchestrator prompt template for subagent delegation.`].join(`
|
|
12
|
+
`),{deliverAs:`steer`})}}),e.registerCommand(`maestria-status`,{description:`Show current maestria session state including handoff history`,handler:async(e,n)=>{let r=l(t);if(!r){n.ui.notify(`No active maestria state to report.`);return}n.ui.setEditorText(r)}}),e.registerCommand(`review`,{description:`Enter review mode. Blocks destructive tools, sets read-only toolset.`,handler:async(n,r)=>{if(!n.trim()){r.ui.notify(`Usage: /review <target> — describe what to review`);return}let i=r.model?.id??null,a=e.getActiveTools(),o={...t,reviewMode:!0,originalModel:i,originalTools:a};if(Object.assign(t,o),s(e,t),t.reviewModel){let n=await c(e,r,t);n&&(r.ui.notify(`Review mode: switched to ${n}`),e.events?.emit(_.REVIEW_ACTIVATED,{originalModel:t.originalModel,reviewModel:n,timestamp:Date.now()}))}e.setActiveTools(x),e.sendUserMessage([`[REVIEW: ${n}]`,``,`Review: ${n}. Use the reviewer prompt template.`,`Read only, no edits, report findings.`].join(`
|
|
13
|
+
`),{deliverAs:`steer`})}}),e.registerCommand(`restore-model`,{description:`Restore the original model and tools that were active before review mode was entered.`,handler:async(n,r)=>{if(!t.reviewMode){r.ui.notify(`Not in review mode. Nothing to restore.`);return}let i=t.originalModel;await o(e,r,t),s(e,t),r.ui.notify(`Restored original model and tools.`),e.events?.emit(_.REVIEW_DEACTIVATED,{originalModel:i,timestamp:Date.now()})}}),e.registerCommand(`handoff`,{description:`Generate a structured handoff prompt for a new task context`,handler:async(n,r)=>{if(!n.trim()){r.ui.notify(`Usage: /handoff <goal> — describe the task context for handoff`);return}let i=n.trim(),a=[`**Goal:** `+i,``,`**Context:**`,`- Mode: `+(t.mode??`none`),`- Active task: `+(t.activeTask||`none`),`- Specialists delegated: `+((t.specialistsDelegated?.length??0)>0?t.specialistsDelegated.join(`, `):`none`),`- Recent handoffs: `+(t.handoffHistory?.length??0)+` entries`,`- Files modified: `+((t.filesModified?.length??0)>0?t.filesModified.join(`, `):`none`),``,`**Requirements:**`,`(fill in specific requirements)`,``,`**Known problems:**`,(t.blockers?.length??0)>0?t.blockers.map(e=>`- `+e).join(`
|
|
14
|
+
`):`(no known problems documented)`,``,`**Success criteria:**`,`(fill in how to verify completion)`,``,`**Next step:**`,`(fill in what happens after this task)`,``,`---`,`Complete the fields above before sending.`].join(`
|
|
15
|
+
`);t.handoffHistory=[{from:`current`,to:`next`,task:i,timestamp:Date.now()},...t.handoffHistory??[]].slice(0,5),s(e,t),e.sendUserMessage(a,{deliverAs:`steer`})}}),e.registerCommand(`review-model`,{description:`Set which model to use when entering review mode`,handler:async(n,r)=>{if(!n.trim()){r.ui.notify(`Usage: /review-model <model-id>`);return}let i=n.trim(),a=r.modelRegistry.getAll();if(!a.find(e=>e.id===i)){r.ui.notify(`Unknown model: "${i}". Available: ${a.map(e=>e.id).join(`, `)}`);return}t.reviewModel=i,s(e,t),r.ui.notify(`Review model set to: ${i}`)}})}const C=[/rm\s+-rf\s+\//,/dd\s+if=/,/>\s*\/dev\/sd/,/chmod\s+-R\s+777\s+\//,/mkfs\.\w+/,/:(){ :\|:& };:/,/>\s*\/etc\/(passwd|shadow|sudoers)/,/\beval\b/,/wget\s+-O\s*-\s*\|\s*(bash|sh)/,/curl\s+.*\|\s*(bash|sh)/,/crontab\s+-r/];function w(e,t){e.on(`tool_call`,async(e,r)=>{if(!(!e||!e.toolName)){if(t.reviewMode&&(n(`edit`,e)||n(`write`,e)||n(`bash`,e)))return{block:!0,reason:`Review mode is active. Report findings, do not edit.`};if(n(`bash`,e)){let t=e.input.command;if(t){for(let e of C)if(e.test(t))return r.hasUI&&await r.ui.confirm(`Dangerous Pattern Detected`,`This command matches a dangerous pattern:\n${t}\nProceed?`)?void 0:{block:!0,reason:`Command matches dangerous pattern: ${e}`}}}}})}function T(e){let t=r(),n=[];m(e,t);let i=h(t);e.on(`before_agent_start`,(e,t)=>i(e,t)),e.on(`session_start`,(e,n)=>{if(!n.sessionManager?.getEntries)return;let r=n.sessionManager.getEntries();for(let e=r.length-1;e>=0;e--){let n=r[e];if(n.type===`custom`&&n.customType===`maestria_state`){let e=n.data;e&&typeof e==`object`&&Object.assign(t,e);break}}}),g(e,t),b(e,t,n),S(e,t),e.on(`session_shutdown`,()=>{for(let e of n)e();n.length=0}),w(e,t)}export{T as default};
|
|
16
|
+
//# sourceMappingURL=extension.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.mjs","names":[],"sources":["../src/state.ts","../src/modes.ts","../src/rules-content.ts","../src/rules.ts","../src/compaction.ts","../src/subagent.ts","../src/commands.ts","../src/tools.ts","../src/extension.ts"],"sourcesContent":["import type { ExtensionAPI, ExtensionCommandContext } from '@earendil-works/pi-coding-agent';\nimport type { ModeKeyword } from '@/modes.js';\n\nconst HANDOFF_HISTORY_CAP = 5;\nconst FILE_HISTORY_CAP = 10;\n\nexport interface HandoffEntry {\n from: string;\n to: string;\n task: string;\n timestamp: number;\n}\n\nexport interface SubagentStatusInfo {\n type: string;\n status: string;\n startedAt: number;\n completedAt?: number;\n}\n\nexport interface MaestriaState {\n mode: ModeKeyword | null;\n activeTask: string;\n completionPromise: string;\n specialistsDelegated: string[];\n blockers: string[];\n filesModified: string[];\n filesRead: string[];\n handoffHistory: HandoffEntry[];\n reviewMode: boolean;\n originalModel: string | null;\n originalTools: string[] | null;\n subagentStatus: Record<string, SubagentStatusInfo>;\n /** Model ID to use when entering review mode. Null = no preference. */\n reviewModel: string | null;\n}\n\nexport function createInitialState(): MaestriaState {\n return {\n mode: null,\n activeTask: '',\n completionPromise: '',\n specialistsDelegated: [],\n blockers: [],\n filesModified: [],\n filesRead: [],\n handoffHistory: [],\n reviewMode: false,\n originalModel: null,\n originalTools: null,\n subagentStatus: {},\n reviewModel: null,\n };\n}\n\nexport function recordHandoff(\n state: MaestriaState,\n from: string,\n to: string,\n task: string,\n): MaestriaState {\n const entry: HandoffEntry = { from, to, task, timestamp: Date.now() };\n const history = [entry, ...state.handoffHistory].slice(0, HANDOFF_HISTORY_CAP);\n return { ...state, handoffHistory: history };\n}\n\nfunction prependDeduped(files: string[], path: string, cap: number): string[] {\n const filtered = files.filter((f) => f !== path);\n return [path, ...filtered].slice(0, cap);\n}\n\nexport function recordFileModified(state: MaestriaState, path: string): MaestriaState {\n return { ...state, filesModified: prependDeduped(state.filesModified, path, FILE_HISTORY_CAP) };\n}\n\nexport function recordFileRead(state: MaestriaState, path: string): MaestriaState {\n return { ...state, filesRead: prependDeduped(state.filesRead, path, FILE_HISTORY_CAP) };\n}\n\nexport function recordSubagentStatus(\n state: MaestriaState,\n id: string,\n info: SubagentStatusInfo,\n): MaestriaState {\n return { ...state, subagentStatus: { ...state.subagentStatus, [id]: info } };\n}\n\nexport function setReviewMode(state: MaestriaState, active: boolean): MaestriaState {\n return {\n ...state,\n reviewMode: active,\n };\n}\n\n/**\n * Exit review mode and return the original model/tools for restoration.\n * Returns a new state (immutable) with review mode cleared and the\n * saved originals so the caller can pass them to pi.setModel/setActiveTools.\n */\nexport function exitReviewMode(state: MaestriaState): {\n state: MaestriaState;\n originalModel: string | null;\n originalTools: string[] | null;\n} {\n return {\n state: {\n ...state,\n reviewMode: false,\n originalModel: null,\n originalTools: null,\n },\n originalModel: state.originalModel,\n originalTools: state.originalTools,\n };\n}\n\n/**\n * Restore the original model and tools saved when review mode was entered.\n * Clears review mode from state and resets pi to the pre-review configuration.\n */\nexport async function restoreOriginalState(\n pi: ExtensionAPI,\n ctx: ExtensionCommandContext,\n state: MaestriaState,\n): Promise<void> {\n const { state: clearedState, originalModel, originalTools } = exitReviewMode(state);\n\n // Restore original tools first (makes full toolset available again)\n if (originalTools && originalTools.length > 0) {\n pi.setActiveTools(originalTools);\n }\n\n // Restore original model — best-effort, failures are non-fatal\n if (originalModel) {\n try {\n const models = ctx.modelRegistry.getAll();\n const model = models.find((m: { id: string }) => m.id === originalModel);\n if (model) {\n await pi.setModel(model);\n }\n } catch {\n // Best-effort: model restoration is non-critical\n }\n }\n\n // Clear review mode state\n Object.assign(state, clearedState);\n}\n\n/**\n * Persist the current state to the session by appending a custom entry.\n * Creates a shallow copy to ensure appendEntry sees the latest snapshot.\n */\nexport function persistState(pi: ExtensionAPI, state: MaestriaState): void {\n pi.appendEntry('maestria_state', { ...state });\n}\n\n/**\n * If a review model is configured, switch to it.\n * Returns the model ID switched to, or null if no switch occurred.\n */\nexport async function cycleToReviewModel(\n pi: ExtensionAPI,\n ctx: ExtensionCommandContext,\n state: MaestriaState,\n): Promise<string | null> {\n const reviewModel = state.reviewModel;\n if (!reviewModel) {\n return null;\n }\n try {\n const models = ctx.modelRegistry.getAll();\n const model = models.find((m) => m.id === reviewModel);\n if (model) {\n await pi.setModel(model);\n return reviewModel;\n } else {\n ctx.ui.notify(`Review model \"${reviewModel}\" not found in registry, staying on current.`);\n return null;\n }\n } catch {\n ctx.ui.notify(`Could not switch to review model \"${reviewModel}\", staying on current.`);\n return null;\n }\n}\n\nexport function renderMaestriaSummary(state: MaestriaState): string {\n const parts: string[] = [];\n\n if (state.mode) {\n parts.push(`**Mode:** ${state.mode.toUpperCase()}`);\n }\n\n if (state.reviewModel) {\n parts.push(`**Review Model:** ${state.reviewModel}`);\n }\n\n if (state.activeTask) {\n parts.push(`**Goal:** ${state.activeTask}`);\n }\n\n if (state.completionPromise) {\n parts.push(`**Completion Promise:** ${state.completionPromise}`);\n }\n\n if (state.specialistsDelegated.length > 0) {\n parts.push(`**Specialists Delegated:** ${state.specialistsDelegated.join(', ')}`);\n }\n\n if (state.blockers.length > 0) {\n parts.push('**Blockers:**');\n for (const blocker of state.blockers) {\n parts.push(`- ${blocker}`);\n }\n }\n\n const fileSubs: string[] = [];\n if (state.filesModified.length > 0) {\n fileSubs.push(`**Modified:** ${state.filesModified.join(', ')}`);\n }\n if (state.filesRead.length > 0) {\n fileSubs.push(`**Read:** ${state.filesRead.join(', ')}`);\n }\n if (fileSubs.length > 0) {\n parts.push(`**Files:** ${fileSubs.join('; ')}`);\n }\n\n if (state.handoffHistory.length > 0) {\n parts.push('**Recent Handoffs:**');\n for (const entry of state.handoffHistory) {\n parts.push(`- ${entry.from} → ${entry.to}: ${entry.task}`);\n }\n }\n\n return parts.join('\\n\\n');\n}\n\nexport { HANDOFF_HISTORY_CAP, FILE_HISTORY_CAP };\n","import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@/state.js';\nimport { persistState, restoreOriginalState } from '@/state.js';\n\nexport const MODE_KEYWORDS = ['fein', 'sonar', 'blitz'] as const;\nexport type ModeKeyword = (typeof MODE_KEYWORDS)[number];\n\nconst MODE_PROMPTS: Record<ModeKeyword, string> = {\n fein: [\n '## MODE: fein (Full Pipeline)',\n '',\n 'Execute the complete fein pipeline: mandatory reconnaissance',\n '(/adventurer) → design/plan (/architect or /planner) →',\n 'implementation (/builder) → review (/reviewer).',\n 'Do NOT skip any phase unless the user explicitly overrides',\n 'in the same turn.',\n ].join('\\n'),\n sonar: [\n '## MODE: sonar (Research Only)',\n '',\n 'Execute research only: /adventurer (recon) →',\n '/architect or /planner (design/plan) → STOP.',\n 'Do NOT implement anything. Return findings.',\n ].join('\\n'),\n blitz: [\n '## MODE: blitz (Fast Implementation)',\n '',\n 'Execute fast implementation via /builder directly.',\n 'Skip reconnaissance and design unless the codebase',\n 'is genuinely unknown. Skip review unless the result',\n 'needs validation.',\n ].join('\\n'),\n};\n\nconst MODE_MARKERS: Record<ModeKeyword, string> = {\n fein: '[MODE: fein]',\n sonar: '[MODE: sonar]',\n blitz: '[MODE: blitz]',\n};\n\nexport function getModePrompt(keyword: ModeKeyword): string {\n return `${MODE_MARKERS[keyword]}\\n\\n${MODE_PROMPTS[keyword]}`;\n}\n\nexport function installModeCommands(pi: ExtensionAPI, state: MaestriaState): void {\n for (const keyword of MODE_KEYWORDS) {\n pi.registerCommand(keyword, {\n description: `Set workflow mode to ${keyword}`,\n handler: async (args, ctx) => {\n // Exit review mode if active (restore original model/tools)\n if (state.reviewMode) {\n await restoreOriginalState(pi, ctx, state);\n }\n\n state.mode = keyword;\n persistState(pi, state);\n\n if (args.trim()) {\n const modeMessage = [\n getModePrompt(keyword),\n '',\n `Run the maestria default pipeline on: ${args}`,\n ].join('\\n');\n pi.sendUserMessage(modeMessage, { deliverAs: 'steer' });\n } else {\n ctx.ui.notify(`Mode set to ${keyword}. Describe what you'd like to work on.`);\n }\n },\n });\n }\n}\n","// This file is auto-generated by scripts/build-rules.ts\n// Do not edit manually — edit rules/AGENTS.md instead.\n\nexport const RULES_CONTENT: string = `<!-- Source: packages/pi/rules/AGENTS.md — sync both files when updating -->\n\n# Global Agent Rules — @maestria/pi\n\n## Orchestration\n\n- **!!! Don't assume** — verify against actual code and docs.\n Guesses lead to bugs.\n- **!!! Read the docs first** — before writing code that touches\n unfamiliar tools, APIs, or migration paths, consult official\n documentation. Don't guess at API changes. This rule is scar\n tissue from repeated failures; treat it seriously.\n- **Don't reference internal project names in explanations** — avoid\n leaking context outside the workspace.\n- **Use \\`opensrc\\` for repos; \\`webfetch\\` for pages** — when analyzing a\n GitHub/GitLab/BitBucket repo or any multi-file code reference, run\n \\`opensrc path <owner/repo>\\` (e.g. \\`opensrc path facebook/react\\`).\n It clones to a global cache and prints a path that \\`read\\`/\\`glob\\`/\\`grep\\`\n can use directly. For a single file, a specific page, or a known\n URL, \\`webfetch\\` is fine. Don't fetch an entire repo one file at a\n time — clone it once, then read locally. Use \\`--cwd\\` to resolve\n versions from the current project.\n- **Webfetch may hang — don't block on it** — if a \\`webfetch\\` request hangs after you've issued it, **proceed without the result** and surface the skip in your next user-facing message. Don't wait for a hung fetch to complete.\n- **Workflow modes** — keywords \\`fein\\` (full pipeline), \\`sonar\\` (research only),\n \\`blitz\\` (fast impl) activate per-turn workflow overrides. See the\n orchestrator prompt for details.\n- **CLI references — use local tools first** — for CLI references, run \\`bash --help\\` or load the relevant \\`skill\\` instead of reaching for \\`webfetch\\`. Local tools are faster and more reliable than fetching docs.\n- **Local files — read directly** — use \\`read\\`, \\`glob\\`, or \\`grep\\` (or \\`lsp\\` when available) for any file you have path access to. Don't \\`webfetch\\` a local file or a file in a checked-out repo.\n- **Tool hierarchy for external information:**\n 1. \\`webfetch\\` — fetch a specific known URL (for docs, pages)\n 2. \\`websearch\\` — discover relevant pages (for finding unknown resources)\n Use \\`webfetch\\` when you know the URL; use \\`websearch\\` when you need to find\n something. \\`websearch\\` is an \\`ask\\`-only permission — explain what you're\n searching for and why before using it.\n\n## Delegation\n\nWhen delegating work via \\\\\\`maestria_subagent()\\\\\\`, use only the 7 specialists below.\n**Never delegate to \\`explore\\` or \\`general\\`** — they are built-in agents,\nnot part of the pipeline.\n\n| Agent | Role | When to Delegate |\n| ------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------- |\n| \\`/adventurer\\` | Codebase reconnaissance, deep code understanding | Understanding unfamiliar code, tracing dependencies, gathering context before implementation |\n| \\`/architect\\` | Architecture decisions, trade-off analysis, ADRs | Choosing between approaches, technology evaluation |\n| \\`/builder\\` | Focused implementation, single-task execution | Feature work, bug fixes, test writing, refactors |\n| \\`/diagnose\\` | Systematic bug tracing, root cause analysis | Debugging regressions, production incidents, cryptic errors |\n| \\`/planner\\` | Implementation plans with phased milestones | Complex features requiring structured execution |\n| \\`/reviewer\\` | Code review with quality gates | Pre-merge review, security audit, post-implementation QA |\n| \\`/writer\\` | Documentation following structured patterns | READMEs, API docs, changelogs, ADR transcription |\n\n## Context Management\n\n- **Progressive disclosure** — start high-level, get specific as needed.\n- **State checkpointing** — periodically summarize what's done, what's\n in progress, what's next.\n- **Context pruning** — remove irrelevant context when no longer needed.\n- **Completion promises** — define success criteria before starting work.\n \"This task is complete when [verifiable conditions].\"\n\n## Commit Policy\n\n- **Only the orchestrator authorizes commits.** Subagents must refuse\n commit requests and redirect to the orchestrator.\n- **Builders executing commits** must follow the orchestrator's exact\n instructions (message, files, \\`check\\`/\\`test\\`). Flag it if the\n orchestrator's instructions skip the commit protocol.\n- **Plans must not include implicit commit steps.** Commit authorization\n is a separate orchestrator step requiring explicit user approval.\n`;\n","import type {\n BeforeAgentStartEvent,\n BeforeAgentStartEventResult,\n ExtensionContext,\n} from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@/state.js';\nimport { getModePrompt } from '@/modes.js';\nimport { RULES_CONTENT } from '@/rules-content.js';\n\nexport function createBeforeAgentStartHandler(state: MaestriaState) {\n return (\n event: BeforeAgentStartEvent,\n _ctx: ExtensionContext,\n ): BeforeAgentStartEventResult | void => {\n const parts: string[] = [RULES_CONTENT, '', event.systemPrompt];\n\n if (state.mode) {\n parts.push('', getModePrompt(state.mode));\n parts.push(\n '',\n `The user has set workflow mode to \"${state.mode}\". ` +\n 'Honor this mode throughout the session until changed via /command.',\n );\n }\n\n return { systemPrompt: parts.join('\\n') };\n };\n}\n","import type {\n ExtensionAPI,\n SessionBeforeCompactEvent,\n SessionBeforeTreeEvent,\n} from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@/state.js';\nimport { renderMaestriaSummary } from '@/state.js';\n\nexport function installCompactionHandlers(pi: ExtensionAPI, state: MaestriaState): void {\n pi.on('session_before_compact', (event: SessionBeforeCompactEvent) => {\n return {\n compaction: {\n summary: renderMaestriaSummary(state),\n details: { ...state },\n firstKeptEntryId: event.preparation.firstKeptEntryId,\n tokensBefore: event.preparation.tokensBefore,\n },\n };\n });\n\n pi.on('session_before_tree', (event: SessionBeforeTreeEvent) => {\n if (event.preparation.userWantsSummary) {\n return {\n summary: {\n summary: renderMaestriaSummary(state),\n },\n };\n }\n return undefined;\n });\n}\n","import { Type } from 'typebox';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { SUBAGENT_EVENTS } from '@gotgenes/pi-subagents';\nimport type { MaestriaState } from '@/state.js';\nimport { persistState, recordHandoff } from '@/state.js';\n\n/**\n * Maestria cross-extension event names.\n * Other Pi extensions can subscribe via `pi.events?.on(...)`.\n * Convention: `maestria:<domain>:<action>`\n */\nexport const MAESTRIA_EVENTS = {\n REVIEW_ACTIVATED: 'maestria:review:activated',\n REVIEW_DEACTIVATED: 'maestria:review:deactivated',\n SUBAGENT_STARTED: 'maestria:subagent:started',\n SUBAGENT_COMPLETED: 'maestria:subagent:completed',\n SUBAGENT_FAILED: 'maestria:subagent:failed',\n} as const;\n\nconst ALLOWED_AGENTS = [\n 'adventurer',\n 'architect',\n 'builder',\n 'diagnose',\n 'planner',\n 'reviewer',\n 'writer',\n] as const;\ntype AllowedAgent = (typeof ALLOWED_AGENTS)[number];\n\n// The 6-field handoff contract\nconst HANDOFF_FIELDS = [\n 'Goal',\n 'Context',\n 'Requirements',\n 'Known problems',\n 'Success criteria',\n 'Next step',\n] as const;\n\n/** Terminal subagent statuses — agent will produce no more updates. */\nconst TERMINAL_STATUSES = new Set(['completed', 'steered', 'aborted', 'stopped', 'error']);\n\n/** Maximum time to wait for a subagent to complete, in milliseconds. */\nexport const POLL_TIMEOUT_MS = 60_000;\n\n/** Interval between subagent status checks, in milliseconds. */\nexport const POLL_INTERVAL_MS = 500;\n\n/** Maximum number of tasks allowed in parallel dispatch. */\nexport const MAX_PARALLEL_TASKS = 8;\n\nexport function validateHandoff(handoff: string): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n for (const field of HANDOFF_FIELDS) {\n const regex = new RegExp(`\\\\*\\\\*${field}:\\\\*\\\\*[\\\\s\\\\S]*?\\\\S`, 'i');\n if (!regex.test(handoff)) {\n errors.push(`Missing or empty field: \"${field}\"`);\n }\n }\n return { valid: errors.length === 0, errors };\n}\n\nexport function installSubagentTool(\n pi: ExtensionAPI,\n state: MaestriaState,\n cleanups?: Array<() => void>,\n): void {\n pi.registerTool({\n name: 'maestria_subagent',\n label: 'Maestria Subagent',\n description: 'Dispatch a task to a @maestria specialist subagent',\n promptSnippet:\n 'Delegate tasks to @maestria specialist subagents (adventurer, architect, builder, planner, diagnose, reviewer, writer)',\n promptGuidelines: [\n 'Use maestria_subagent when a task MUST be delegated to a specialist subagent rather than handled directly. Each specialist has focused capabilities: adventurer (recon), architect (design), builder (impl), planner (planning), diagnose (bugs), reviewer (QA), writer (docs).',\n ],\n prepareArguments(args: unknown) {\n return args;\n },\n parameters: Type.Object({\n agent: Type.Optional(Type.String({ description: 'Specialist agent name' })),\n task: Type.Optional(Type.String({ description: 'Task description for the subagent' })),\n tasks: Type.Optional(\n Type.Array(\n Type.Object({\n agent: Type.String(),\n task: Type.String(),\n }),\n { description: 'Array of task objects for parallel or chain dispatch' },\n ),\n ),\n mode: Type.Optional(\n Type.Union([Type.Literal('parallel'), Type.Literal('chain'), Type.Literal('single')]),\n ),\n }),\n async execute(\n _toolCallId: string,\n params: {\n agent?: string;\n task?: string;\n tasks?: Array<{ agent: string; task: string }>;\n mode?: 'parallel' | 'chain' | 'single';\n },\n signal: AbortSignal | undefined,\n onUpdate: ((result: { content: Array<{ type: string; text: string }> }) => void) | undefined,\n _ctx: ExtensionContext,\n ) {\n // Block subagent dispatch when in review mode\n if (state.reviewMode) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Subagent dispatch is not available during review mode. Use /restore-model to exit review mode first.',\n },\n ],\n };\n }\n\n // Determine dispatch mode (default to 'single' for backward compat)\n const mode = params.mode ?? 'single';\n\n // Validate parameters based on mode\n if (mode === 'single') {\n // Backward-compatible validation — must match original error messages exactly\n if (!ALLOWED_AGENTS.includes(params.agent as AllowedAgent)) {\n throw new Error(\n `Unknown agent: \"${params.agent}\". Allowed: ${ALLOWED_AGENTS.join(', ')}`,\n );\n }\n if (!params.task || !params.task.trim()) {\n throw new Error('Task description is required');\n }\n } else if (mode === 'parallel') {\n if (!params.tasks || params.tasks.length < 2) {\n throw new Error(`For parallel mode, tasks array is required with at least 2 items`);\n }\n if (params.tasks.length > MAX_PARALLEL_TASKS) {\n throw new Error(\n `For parallel mode, tasks array may have at most ${MAX_PARALLEL_TASKS} items (got ${params.tasks.length})`,\n );\n }\n for (const t of params.tasks) {\n if (!ALLOWED_AGENTS.includes(t.agent as AllowedAgent)) {\n throw new Error(`Unknown agent: \"${t.agent}\". Allowed: ${ALLOWED_AGENTS.join(', ')}`);\n }\n if (!t.task || !t.task.trim()) {\n throw new Error('Task description is required for all tasks');\n }\n }\n } else if (mode === 'chain') {\n if (!params.tasks || params.tasks.length < 2) {\n throw new Error('For chain mode, tasks array is required with at least 2 items');\n }\n for (const t of params.tasks) {\n if (!ALLOWED_AGENTS.includes(t.agent as AllowedAgent)) {\n throw new Error(`Unknown agent: \"${t.agent}\". Allowed: ${ALLOWED_AGENTS.join(', ')}`);\n }\n if (!t.task || !t.task.trim()) {\n throw new Error('Task description is required for all tasks');\n }\n }\n }\n\n // Attempt to dispatch via @gotgenes/pi-subagents; fallback gracefully\n try {\n const { getSubagentsService } = await import('@gotgenes/pi-subagents');\n const service = getSubagentsService()!;\n if (typeof service.spawn !== 'function') {\n throw new Error('Subagents service unavailable or incomplete');\n }\n\n // Helper: poll a single subagent until terminal or timeout\n async function pollSubagent(\n id: string,\n label: string,\n sendUpdates: boolean,\n ): Promise<{ status: string; result?: string; error?: string }> {\n const maxPolls = POLL_TIMEOUT_MS / POLL_INTERVAL_MS;\n let polls = 0;\n let record = service.getRecord(id);\n while (record && !TERMINAL_STATUSES.has(record.status) && polls < maxPolls) {\n if (signal?.aborted) throw new Error('Maestria subagent call aborted');\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n record = service.getRecord(id);\n polls++;\n if (sendUpdates) {\n onUpdate?.({\n content: [\n {\n type: 'text' as const,\n text: `${label} running... (${Math.round((polls * POLL_INTERVAL_MS) / 1000)}s)`,\n },\n ],\n });\n }\n }\n if (record && !TERMINAL_STATUSES.has(record.status)) {\n throw new Error(`Subagent ${id} timed out after ${POLL_TIMEOUT_MS}ms`);\n }\n if (!record) {\n throw new Error(`Subagent ${id} was cleaned up before completion`);\n }\n return record;\n }\n\n // --- SINGLE MODE ---\n if (mode === 'single') {\n const agent = params.agent!;\n const task = params.task!;\n\n // Spawn in foreground — returns subagent ID synchronously\n const id = service.spawn(agent, task, {\n description: task.slice(0, 80),\n foreground: true,\n inheritContext: true,\n });\n\n // Record handoff in state and persist (only after spawn succeeds)\n const updatedState = recordHandoff(state, 'orchestrator', agent, task);\n Object.assign(state, updatedState);\n pi.appendEntry('maestria_state', state);\n\n // Poll for completion\n const record = await pollSubagent(id, `Subagent ${agent}`, true);\n\n const resultText = record.result ?? record.error ?? 'No output.';\n\n return {\n content: [{ type: 'text' as const, text: resultText }],\n details: { subagentId: id },\n };\n }\n\n // --- PARALLEL MODE ---\n if (mode === 'parallel') {\n const taskList = params.tasks!;\n\n onUpdate?.({\n content: [\n { type: 'text' as const, text: `Spawning ${taskList.length} parallel subagents...` },\n ],\n });\n\n // Spawn all tasks\n const spawnedIds: string[] = [];\n for (const t of taskList) {\n const id = service.spawn(t.agent, t.task, {\n description: t.task.slice(0, 80),\n foreground: true,\n inheritContext: true,\n });\n spawnedIds.push(id);\n\n // Record each handoff\n const updatedState = recordHandoff(state, 'orchestrator', t.agent, t.task);\n Object.assign(state, updatedState);\n }\n pi.appendEntry('maestria_state', state);\n\n // Poll all concurrently\n const records = await Promise.all(\n spawnedIds.map((id, i) =>\n pollSubagent(id, `${taskList[i].agent} (${i + 1}/${taskList.length})`, false),\n ),\n );\n\n onUpdate?.({\n content: [\n {\n type: 'text' as const,\n text: `All ${taskList.length} parallel subagents completed.`,\n },\n ],\n });\n\n // Aggregate results\n const parts = [`## Parallel Results (${taskList.length} tasks)\\n`];\n for (let i = 0; i < taskList.length; i++) {\n const t = taskList[i];\n const rec = records[i];\n const resultText = rec.result ?? rec.error ?? 'No output.';\n parts.push(`### ${i + 1}: ${t.agent}`);\n parts.push(resultText);\n }\n\n return {\n content: [{ type: 'text' as const, text: parts.join('\\n\\n') }],\n details: { subagentIds: spawnedIds },\n };\n }\n\n // --- CHAIN MODE ---\n if (mode === 'chain') {\n const taskList = params.tasks!;\n let previousResult = '';\n\n for (let i = 0; i < taskList.length; i++) {\n const t = taskList[i];\n let taskText = t.task;\n\n // Substitute {previous} placeholder with previous result\n if (i > 0 && taskText.includes('{previous}')) {\n taskText = taskText.replace(/\\{previous\\}/g, previousResult);\n }\n\n const id = service.spawn(t.agent, taskText, {\n description: taskText.slice(0, 80),\n foreground: true,\n inheritContext: true,\n });\n\n // Record handoff\n const updatedState = recordHandoff(state, 'orchestrator', t.agent, taskText);\n Object.assign(state, updatedState);\n pi.appendEntry('maestria_state', state);\n\n onUpdate?.({\n content: [\n {\n type: 'text' as const,\n text: `Chain step ${i + 1}/${taskList.length}: ${t.agent} running...`,\n },\n ],\n });\n\n // Poll for completion\n const record = await pollSubagent(id, `Chain step ${i + 1}: ${t.agent}`, true);\n\n previousResult = record.result ?? record.error ?? 'No output.';\n\n if (i < taskList.length - 1) {\n onUpdate?.({\n content: [\n {\n type: 'text' as const,\n text: `Chain step ${i + 1}/${taskList.length}: ${t.agent} completed. Moving to next step.`,\n },\n ],\n });\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: previousResult }],\n details: { subagentId: 'chain-completed' },\n };\n }\n\n // Should not reach here — all modes are handled above\n throw new Error('Unknown dispatch mode');\n } catch {\n // Return handoff payload as structured text when SDK unavailable\n const agentName = params.agent ?? params.tasks?.[0]?.agent ?? 'unknown';\n const taskDesc = params.task ?? params.tasks?.map((t) => t.task).join('; ') ?? 'unknown';\n const handoffInfo = [\n `## Subagent Handoff Required`,\n ``,\n `**From:** orchestrator`,\n `**To:** ${agentName}`,\n `**Task:** ${taskDesc}`,\n ``,\n `Subagent SDK not available. Please delegate this work manually.`,\n ].join('\\n');\n\n return {\n content: [{ type: 'text' as const, text: handoffInfo }],\n };\n }\n },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any); // TypeBox inferred types don't match ToolDefinition exactly\n\n // Subscribe to subagent lifecycle events for accurate state tracking.\n // These subscriptions are set up once at extension init, not on every tool call.\n // pi.events is the shared EventBus — distinct from pi.on() lifecycle hooks.\n if (pi.events) {\n const unsubStarted = pi.events.on(SUBAGENT_EVENTS.STARTED, (data: unknown) => {\n const { id, type } = data as { id: string; type: string };\n state.subagentStatus[id] = { type, status: 'running', startedAt: Date.now() };\n persistState(pi, state);\n pi.events?.emit(MAESTRIA_EVENTS.SUBAGENT_STARTED, {\n id,\n type,\n timestamp: Date.now(),\n });\n });\n\n const unsubCompleted = pi.events.on(SUBAGENT_EVENTS.COMPLETED, (data: unknown) => {\n const { id } = data as { id: string };\n const existing = state.subagentStatus[id];\n if (existing) {\n existing.status = 'completed';\n existing.completedAt = Date.now();\n }\n persistState(pi, state);\n pi.events?.emit(MAESTRIA_EVENTS.SUBAGENT_COMPLETED, {\n id,\n type: existing?.type,\n timestamp: Date.now(),\n });\n });\n\n const unsubFailed = pi.events.on(SUBAGENT_EVENTS.FAILED, (data: unknown) => {\n const { id, status } = data as { id: string; status: string };\n const existing = state.subagentStatus[id];\n if (existing) {\n existing.status = status ?? 'error';\n existing.completedAt = Date.now();\n }\n persistState(pi, state);\n pi.events?.emit(MAESTRIA_EVENTS.SUBAGENT_FAILED, {\n id,\n type: existing?.type,\n timestamp: Date.now(),\n });\n });\n\n const unsubSteered = pi.events.on(SUBAGENT_EVENTS.STEERED, (data: unknown) => {\n // Steering is informational — no status transition, but ensure\n // the agent is tracked as running if it wasn't already observed.\n const { id } = data as { id: string };\n if (!state.subagentStatus[id]) {\n state.subagentStatus[id] = { type: 'unknown', status: 'running', startedAt: Date.now() };\n }\n persistState(pi, state);\n });\n\n if (cleanups) {\n cleanups.push(unsubStarted, unsubCompleted, unsubFailed, unsubSteered);\n }\n }\n}\n","import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@/state.js';\nimport {\n cycleToReviewModel,\n persistState,\n renderMaestriaSummary,\n restoreOriginalState,\n} from '@/state.js';\nimport { MAESTRIA_EVENTS } from '@/subagent.js';\n\n/**\n * Read-only tools that let a reviewer inspect code without making changes.\n *\n * - `read`, `grep`, `find`, `ls`, `glob` — all non-destructive.\n * - Excluded: `bash`, `edit`, `write` — these can modify the filesystem.\n *\n * `glob` is included for file pattern matching even though it's not a\n * built-in Pi tool — extensions may register it, and including it is a no-op\n * if absent.\n */\nconst READ_ONLY_TOOLS = ['read', 'grep', 'find', 'ls', 'glob'];\n\nexport function installCommands(pi: ExtensionAPI, state: MaestriaState): void {\n pi.registerCommand('orchestrate', {\n description: 'Start a full pipeline by delegating to the orchestrator',\n handler: async (args: string, ctx) => {\n if (!args.trim()) {\n ctx.ui.notify('Usage: /orchestrate <goal> — describe what to accomplish');\n return;\n }\n\n // Exit review mode if active (restore original model/tools)\n if (state.reviewMode) {\n const prevOriginalModel = state.originalModel;\n await restoreOriginalState(pi, ctx, state);\n persistState(pi, state);\n pi.events?.emit(MAESTRIA_EVENTS.REVIEW_DEACTIVATED, {\n originalModel: prevOriginalModel,\n source: 'orchestrate',\n timestamp: Date.now(),\n });\n }\n\n pi.sendUserMessage(\n [\n `[ORCHESTRATE: ${args}]`,\n '',\n `Orchestrate the full maestria pipeline to: ${args}`,\n 'Use the orchestrator prompt template for subagent delegation.',\n ].join('\\n'),\n { deliverAs: 'steer' },\n );\n },\n });\n\n pi.registerCommand('maestria-status', {\n description: 'Show current maestria session state including handoff history',\n handler: async (_args: string, ctx) => {\n const summary = renderMaestriaSummary(state);\n if (!summary) {\n ctx.ui.notify('No active maestria state to report.');\n return;\n }\n ctx.ui.setEditorText(summary);\n },\n });\n\n pi.registerCommand('review', {\n description: 'Enter review mode. Blocks destructive tools, sets read-only toolset.',\n handler: async (args: string, ctx) => {\n if (!args.trim()) {\n ctx.ui.notify('Usage: /review <target> — describe what to review');\n return;\n }\n\n // 1. Save current model and tools for later restoration\n const currentModelId = ctx.model?.id ?? null;\n const currentTools = pi.getActiveTools();\n\n // 2. Update state: mark review mode, store originals\n const updatedState: MaestriaState = {\n ...state,\n reviewMode: true,\n originalModel: currentModelId,\n originalTools: currentTools,\n };\n Object.assign(state, updatedState);\n persistState(pi, state);\n\n // 3. Switch to review model if configured\n if (state.reviewModel) {\n const switched = await cycleToReviewModel(pi, ctx, state);\n if (switched) {\n ctx.ui.notify(`Review mode: switched to ${switched}`);\n pi.events?.emit(MAESTRIA_EVENTS.REVIEW_ACTIVATED, {\n originalModel: state.originalModel,\n reviewModel: switched,\n timestamp: Date.now(),\n });\n }\n }\n\n // 4. Restrict to read-only tools\n pi.setActiveTools(READ_ONLY_TOOLS);\n\n pi.sendUserMessage(\n [\n `[REVIEW: ${args}]`,\n '',\n `Review: ${args}. Use the reviewer prompt template.`,\n 'Read only, no edits, report findings.',\n ].join('\\n'),\n { deliverAs: 'steer' },\n );\n },\n });\n\n pi.registerCommand('restore-model', {\n description:\n 'Restore the original model and tools that were active before review mode was entered.',\n handler: async (_args: string, ctx) => {\n if (!state.reviewMode) {\n ctx.ui.notify('Not in review mode. Nothing to restore.');\n return;\n }\n const prevOriginalModel = state.originalModel;\n await restoreOriginalState(pi, ctx, state);\n persistState(pi, state);\n ctx.ui.notify('Restored original model and tools.');\n pi.events?.emit(MAESTRIA_EVENTS.REVIEW_DEACTIVATED, {\n originalModel: prevOriginalModel,\n timestamp: Date.now(),\n });\n },\n });\n\n pi.registerCommand('handoff', {\n description: 'Generate a structured handoff prompt for a new task context',\n handler: async (args: string, ctx) => {\n if (!args.trim()) {\n ctx.ui.notify('Usage: /handoff <goal> — describe the task context for handoff');\n return;\n }\n\n // Build a structured handoff document with 6 fields\n const goal = args.trim();\n const handoffPrompt = [\n '**Goal:** ' + goal,\n '',\n '**Context:**',\n '- Mode: ' + (state.mode ?? 'none'),\n '- Active task: ' + (state.activeTask || 'none'),\n '- Specialists delegated: ' +\n ((state.specialistsDelegated?.length ?? 0) > 0\n ? state.specialistsDelegated.join(', ')\n : 'none'),\n '- Recent handoffs: ' + (state.handoffHistory?.length ?? 0) + ' entries',\n '- Files modified: ' +\n ((state.filesModified?.length ?? 0) > 0 ? state.filesModified.join(', ') : 'none'),\n '',\n '**Requirements:**',\n '(fill in specific requirements)',\n '',\n '**Known problems:**',\n (state.blockers?.length ?? 0) > 0\n ? state.blockers.map((b: string) => '- ' + b).join('\\n')\n : '(no known problems documented)',\n '',\n '**Success criteria:**',\n '(fill in how to verify completion)',\n '',\n '**Next step:**',\n '(fill in what happens after this task)',\n '',\n '---',\n 'Complete the fields above before sending.',\n ].join('\\n');\n\n // Record in state\n state.handoffHistory = [\n { from: 'current', to: 'next', task: goal, timestamp: Date.now() },\n ...(state.handoffHistory ?? []),\n ].slice(0, 5);\n\n // Persist state\n persistState(pi, state);\n\n // Send as user message with steer delivery\n pi.sendUserMessage(handoffPrompt, { deliverAs: 'steer' });\n },\n });\n\n pi.registerCommand('review-model', {\n description: 'Set which model to use when entering review mode',\n handler: async (args: string, ctx) => {\n if (!args.trim()) {\n ctx.ui.notify('Usage: /review-model <model-id>');\n return;\n }\n const modelId = args.trim();\n const models = ctx.modelRegistry.getAll();\n const model = models.find((m) => m.id === modelId);\n if (!model) {\n ctx.ui.notify(\n `Unknown model: \"${modelId}\". Available: ${models.map((m) => m.id).join(', ')}`,\n );\n return;\n }\n state.reviewModel = modelId;\n persistState(pi, state);\n ctx.ui.notify(`Review model set to: ${modelId}`);\n },\n });\n}\n","import {\n isToolCallEventType,\n type ExtensionAPI,\n type ToolCallEvent,\n type ExtensionContext,\n} from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@/state.js';\n\nconst DANGEROUS_PATTERNS = [\n /rm\\s+-rf\\s+\\//,\n /dd\\s+if=/,\n />\\s*\\/dev\\/sd/,\n /chmod\\s+-R\\s+777\\s+\\//,\n /mkfs\\.\\w+/,\n /:(){ :\\|:& };:/, // fork bomb\n />\\s*\\/etc\\/(passwd|shadow|sudoers)/,\n /\\beval\\b/,\n /wget\\s+-O\\s*-\\s*\\|\\s*(bash|sh)/,\n /curl\\s+.*\\|\\s*(bash|sh)/,\n /crontab\\s+-r/,\n];\n\nexport function installToolInterceptors(pi: ExtensionAPI, state: MaestriaState): void {\n pi.on('tool_call', async (event: ToolCallEvent, ctx: ExtensionContext) => {\n if (!event || !event.toolName) return;\n\n // Block destructive tools in review mode\n if (state.reviewMode) {\n if (\n isToolCallEventType('edit', event) ||\n isToolCallEventType('write', event) ||\n isToolCallEventType('bash', event)\n ) {\n return {\n block: true,\n reason: 'Review mode is active. Report findings, do not edit.',\n };\n }\n }\n\n // Block dangerous bash patterns regardless of mode\n if (isToolCallEventType('bash', event)) {\n const command = event.input.command;\n if (command) {\n for (const pattern of DANGEROUS_PATTERNS) {\n if (pattern.test(command)) {\n if (ctx.hasUI) {\n const confirmed = await ctx.ui.confirm(\n 'Dangerous Pattern Detected',\n `This command matches a dangerous pattern:\\n${command}\\nProceed?`,\n );\n if (confirmed) return undefined;\n }\n return {\n block: true,\n reason: `Command matches dangerous pattern: ${pattern}`,\n };\n }\n }\n }\n }\n\n return undefined; // allow\n });\n}\n","import type { ExtensionAPI, SessionStartEvent } from '@earendil-works/pi-coding-agent';\nimport { createInitialState } from '@/state.js';\nimport { installModeCommands } from '@/modes.js';\nimport { createBeforeAgentStartHandler } from '@/rules.js';\nimport { installCompactionHandlers } from '@/compaction.js';\nimport { installSubagentTool } from '@/subagent.js';\nimport { installCommands } from '@/commands.js';\nimport { installToolInterceptors } from '@/tools.js';\n\nexport default function (pi: ExtensionAPI): void {\n const state = createInitialState();\n const cleanups: Array<() => void> = [];\n\n // Install mode commands: /fein, /sonar, /blitz\n installModeCommands(pi, state);\n\n // Inject mode prompts when a mode is active\n const handleBeforeAgentStart = createBeforeAgentStartHandler(state);\n\n pi.on('before_agent_start', (event, ctx) => {\n return handleBeforeAgentStart(event, ctx);\n });\n\n // Restore persisted state on session start (reload/resume/fork)\n pi.on('session_start', (_event: SessionStartEvent, ctx) => {\n if (!ctx.sessionManager?.getEntries) return;\n const entries = ctx.sessionManager.getEntries();\n // Walk from newest to oldest, find the last persisted maestria_state entry\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n if (entry.type === 'custom' && entry.customType === 'maestria_state') {\n const data = entry.data;\n if (data && typeof data === 'object') {\n Object.assign(state, data);\n }\n break;\n }\n }\n });\n\n // Install compaction preservation handlers\n installCompactionHandlers(pi, state);\n\n // Install orchestration hooks: subagent tool and commands\n installSubagentTool(pi, state, cleanups);\n installCommands(pi, state);\n\n // Cleanup subscriptions on shutdown\n pi.on('session_shutdown', () => {\n for (const cleanup of cleanups) cleanup();\n cleanups.length = 0;\n });\n\n // Install tool call interceptors for review mode and dangerous patterns\n installToolInterceptors(pi, state);\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@maestria/pi",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Maestria extension for the Pi coding agent",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agent-orchestration",
|
|
7
|
+
"coding-agent",
|
|
8
|
+
"maestria",
|
|
9
|
+
"pi-package"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/agustinusnathaniel/maestria.git",
|
|
15
|
+
"directory": "packages/pi"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"prompts",
|
|
20
|
+
"rules",
|
|
21
|
+
"skills",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "vp pack",
|
|
31
|
+
"prebuild": "node --experimental-strip-types scripts/build-rules.ts",
|
|
32
|
+
"test": "vp test",
|
|
33
|
+
"lint": "vp check",
|
|
34
|
+
"validate-prompts": "node --experimental-strip-types scripts/validate-prompts.ts",
|
|
35
|
+
"validate-skills": "node --experimental-strip-types scripts/validate-skills.ts",
|
|
36
|
+
"sync-prompts": "bash scripts/sync-prompts.sh",
|
|
37
|
+
"validate": "npm run validate-prompts && npm run validate-skills && npm run sync-prompts"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@gotgenes/pi-subagents": "^17.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "catalog:",
|
|
44
|
+
"typescript": "catalog:",
|
|
45
|
+
"vitest": "catalog:"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"@earendil-works/pi-ai": "*",
|
|
49
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
50
|
+
"typebox": "*"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=22.12.0"
|
|
54
|
+
},
|
|
55
|
+
"pi": {
|
|
56
|
+
"extensions": [
|
|
57
|
+
"./dist/extension.mjs"
|
|
58
|
+
],
|
|
59
|
+
"prompts": [
|
|
60
|
+
"./prompts"
|
|
61
|
+
],
|
|
62
|
+
"skills": [
|
|
63
|
+
"./skills"
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
<!-- Source: packages/opencode/agents/adventurer.md — keep in sync when updating -->
|
|
2
|
+
|
|
3
|
+
You are a codebase reconnaissance agent.
|
|
4
|
+
|
|
5
|
+
## Mission
|
|
6
|
+
|
|
7
|
+
Map unknown territory so downstream specialists (builder, architect,
|
|
8
|
+
diagnose) can work with full context. You don't implement, design, or
|
|
9
|
+
debug — you **understand and report**.
|
|
10
|
+
|
|
11
|
+
The pipeline starts with you:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Explorer → Architect → Builder → Tester → Reviewer → [Output]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Scan first, plan second, implement third. Your reconnaissance is the
|
|
18
|
+
first step in every pipeline.
|
|
19
|
+
|
|
20
|
+
## Process
|
|
21
|
+
|
|
22
|
+
1. **Scope** — Understand what the delegate needs to know
|
|
23
|
+
2. **Explore** — Trace code paths, find key files, map relationships
|
|
24
|
+
3. **Document** — Produce a structured reconnaissance report
|
|
25
|
+
4. **Handoff** — Pass the report cleanly to the next agent
|
|
26
|
+
|
|
27
|
+
## Exploration Techniques
|
|
28
|
+
|
|
29
|
+
- **Entry point analysis** — Start from the user-facing API or entry
|
|
30
|
+
point
|
|
31
|
+
- **Call chain tracing** — Follow function calls from invocation to
|
|
32
|
+
implementation
|
|
33
|
+
- **Module mapping** — Document relationships between files and modules
|
|
34
|
+
- **Pattern discovery** — Identify conventions, idioms, repeated
|
|
35
|
+
patterns
|
|
36
|
+
- **Boundary identification** — Find where data crosses module/API
|
|
37
|
+
boundaries
|
|
38
|
+
- **Dependency tracing** — Map import chains and external dependencies
|
|
39
|
+
|
|
40
|
+
### Complexity Tiers
|
|
41
|
+
|
|
42
|
+
Adjust depth based on codebase size:
|
|
43
|
+
|
|
44
|
+
| Tier | Files | Strategy |
|
|
45
|
+
| ------ | -------- | ----------------------------------------------------- |
|
|
46
|
+
| Small | <50 | Full exploration, read most files |
|
|
47
|
+
| Medium | 50–300 | Targeted exploration, focus on high-value areas |
|
|
48
|
+
| Large | 300–1000 | Focused reads only, use grep-first approach |
|
|
49
|
+
| Huge | >1000 | Sampling strategy, skip generated/test/migration dirs |
|
|
50
|
+
|
|
51
|
+
## Iteration Limits
|
|
52
|
+
|
|
53
|
+
- **Max 3 exploration approaches** before declaring "unable to find" and reporting what was tried.
|
|
54
|
+
- **Never loop silently** — if a search strategy doesn't work after 3 attempts, surface the loop with the discovery log.
|
|
55
|
+
- **Escalation format:** "Tried X, Y, Z. Blocked by [cause]. Need [input] to proceed."
|
|
56
|
+
|
|
57
|
+
## Output Format
|
|
58
|
+
|
|
59
|
+
Structure findings so the next agent can start work immediately:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
# Reconnaissance Report: [Area]
|
|
63
|
+
|
|
64
|
+
## Key Files
|
|
65
|
+
- `path/to/file.ts` — Purpose, key exports, role in the system
|
|
66
|
+
|
|
67
|
+
## Call Chains
|
|
68
|
+
[Entry] → [Middleware] → [Implementation] → [Data Access]
|
|
69
|
+
|
|
70
|
+
## Data Flow
|
|
71
|
+
[Input] → [Transformation] → [Storage] → [Output]
|
|
72
|
+
|
|
73
|
+
## Discovery Log
|
|
74
|
+
- **Convention:** Pattern observed
|
|
75
|
+
- **Surprise:** Unexpected behavior or deviation from conventions
|
|
76
|
+
- **Risk:** Potential issue or fragile area identified
|
|
77
|
+
|
|
78
|
+
## Context for Next Agent
|
|
79
|
+
Specific guidance for the downstream specialist.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Rules
|
|
83
|
+
|
|
84
|
+
- **!!! Never edit files** — you are read-only reconnaissance
|
|
85
|
+
- **!!! Never implement solutions** — that's `/builder`'s job
|
|
86
|
+
- **!!! Never make design decisions** — that's `/architect`'s job
|
|
87
|
+
- **Use `opensrc` for investigating external dependencies** — when
|
|
88
|
+
you need to understand how a library works internally, use the
|
|
89
|
+
`opensrc` skill to clone and read its source instead of making
|
|
90
|
+
API calls or web requests
|
|
91
|
+
- **External repos: `opensrc` for big repos, `webfetch` for single pages** —
|
|
92
|
+
For GitHub/GitLab/BitBucket URLs, scoped queries (single file, single
|
|
93
|
+
page) → `webfetch` is fine. Whole repos or "how is X implemented in
|
|
94
|
+
library Y" → `opensrc path <owner/repo>` (clones to global cache,
|
|
95
|
+
gives you a path for `read`/`glob`/`grep`). Don't webfetch a
|
|
96
|
+
multi-file repo one file at a time — clone once, read locally.
|
|
97
|
+
- **One role per session** — don't mix exploration with building
|
|
98
|
+
- If you can't find something after reasonable effort, report what you
|
|
99
|
+
tried
|
|
100
|
+
- Prefer `lsp` tool for code intelligence over grep when possible
|
|
101
|
+
- Document negative findings too ("no middleware layer found")
|
|
102
|
+
- Include specific file paths and line numbers in findings
|
|
103
|
+
- For large codebases, use grep-first strategy to avoid token waste
|
|
104
|
+
- **!!! Maker/checker split** — your work is reviewed by `/reviewer` before it lands. The model that wrote the recon is too nice grading its own homework. Produce the report, do not QA it.
|
|
105
|
+
- **!!! Validate before handoff** — never present a report that hasn't been cross-checked against the source. Read your own report for completeness before reporting back.
|
|
106
|
+
- **!!! If anything is unclear or ambiguous, flag it in your report** — wrong assumptions waste more time than asking questions. State what is unclear and what you assumed instead.
|
|
107
|
+
- **Parallelization:** adventurer tasks on different modules/areas can run in parallel. Two adventurers mapping the same module produce overlapping reports. Read-only is safe; duplication is wasteful.
|
|
108
|
+
|
|
109
|
+
## Handoff
|
|
110
|
+
|
|
111
|
+
When done, your report should let the next agent start working
|
|
112
|
+
immediately without needing to re-explore the same code. The handoff
|
|
113
|
+
includes:
|
|
114
|
+
|
|
115
|
+
- What was found (with file paths and line numbers)
|
|
116
|
+
- What was NOT found (negative findings save downstream time)
|
|
117
|
+
- What the downstream specialist should focus on first
|
|
118
|
+
|
|
119
|
+
**If the scoping is unclear or the request is ambiguous, flag it in
|
|
120
|
+
your report.** Don't waste effort exploring the wrong area.
|
|
121
|
+
|
|
122
|
+
## Related Agents
|
|
123
|
+
|
|
124
|
+
- `/builder` — Primary consumer of reconnaissance output; starts
|
|
125
|
+
implementing based on your report
|
|
126
|
+
- `/architect` — Needs structural understanding before making decisions
|
|
127
|
+
- `/diagnose` — Needs call chain and dependency context for root cause
|
|
128
|
+
analysis
|
|
129
|
+
- `/reviewer` — May request targeted exploration for validation
|
|
130
|
+
|
|
131
|
+
## Skill Prescription
|
|
132
|
+
|
|
133
|
+
### Always load
|
|
134
|
+
|
|
135
|
+
_(none — adventurer is read-only; skills load only on trigger)_
|
|
136
|
+
|
|
137
|
+
### Load on trigger
|
|
138
|
+
|
|
139
|
+
- `agent-browser` (`vercel-labs/agent-browser`) — load when exploring a running web app, visual references/links provided, or Electron apps need inspection (skip if backend-only)
|
|
140
|
+
- `c4-architecture` (`softaworks/agent-toolkit`) — load when output requires a context/container diagram
|
|
141
|
+
- `domain-modeling` (`mattpocock/skills`) — load when mapping domain concepts, terminology, and ubiquitous language during reconnaissance
|
|
142
|
+
- `mermaid-diagrams` (`softaworks/agent-toolkit`) — load when a sequence/flow/ER diagram is requested
|
|
143
|
+
- `resolving-merge-conflicts` (`mattpocock/skills`) — load when investigating merge conflict history or understanding why a conflict occurred
|
|
144
|
+
- `opensrc` (`vercel-labs/opensrc`) — load when external library internals affect the answer
|
|
145
|
+
- `session-handoff` (`softaworks/agent-toolkit`) — load when creating a recon report or handoff document for another agent
|
|
146
|
+
|
|
147
|
+
### Defer to specialist
|
|
148
|
+
|
|
149
|
+
- `improve-codebase-architecture` (`mattpocock/skills`) → /architect / /planner's domain, not recon
|
|
150
|
+
|
|
151
|
+
### Skip if
|
|
152
|
+
|
|
153
|
+
- The task is a 1-file lookup; no skill load needed
|
|
154
|
+
- The user has not asked for any diagramming output
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<!-- Source: packages/opencode/agents/architect.md — keep in sync when updating -->
|
|
2
|
+
|
|
3
|
+
You make architecture decisions systematically.
|
|
4
|
+
|
|
5
|
+
## Phase 1: Understand the Problem
|
|
6
|
+
|
|
7
|
+
Clarify before options:
|
|
8
|
+
|
|
9
|
+
- What is the business goal?
|
|
10
|
+
- What are constraints (time, team, budget)?
|
|
11
|
+
- MVP or production? Timeline?
|
|
12
|
+
- Reversible or irreversible decision?
|
|
13
|
+
- What expertise does the team have?
|
|
14
|
+
- What are the guard rails? (what to do / what not to do)
|
|
15
|
+
|
|
16
|
+
## Phase 2: Present Options
|
|
17
|
+
|
|
18
|
+
Show 2-4 viable options with comparison:
|
|
19
|
+
|
|
20
|
+
| Criterion | Option A | Option B |
|
|
21
|
+
| ---------- | -------- | -------- |
|
|
22
|
+
| MVP Speed | Fast | Medium |
|
|
23
|
+
| Long-term | Debt | Clean |
|
|
24
|
+
| Complexity | Low | High |
|
|
25
|
+
|
|
26
|
+
## Phase 3: Clarify (max 5 questions)
|
|
27
|
+
|
|
28
|
+
Ask targeted questions to refine the recommendation. After 5 questions, make
|
|
29
|
+
a preliminary recommendation with your assumptions stated.
|
|
30
|
+
|
|
31
|
+
## Phase 4: Recommend
|
|
32
|
+
|
|
33
|
+
State recommendation with clear rationale and acknowledged trade-offs.
|
|
34
|
+
|
|
35
|
+
## Phase 5: Document as ADR
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
# ADR-XXX: [Title]
|
|
39
|
+
|
|
40
|
+
## Status
|
|
41
|
+
[Proposed | Accepted | Deprecated]
|
|
42
|
+
|
|
43
|
+
## Context
|
|
44
|
+
What motivates this decision?
|
|
45
|
+
|
|
46
|
+
## Decision
|
|
47
|
+
What change is being proposed?
|
|
48
|
+
|
|
49
|
+
## Consequences
|
|
50
|
+
What becomes easier or harder?
|
|
51
|
+
|
|
52
|
+
## Alternatives Considered
|
|
53
|
+
Options evaluated and why rejected
|
|
54
|
+
|
|
55
|
+
## Date
|
|
56
|
+
YYYY-MM-DD
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Shortcut Rules
|
|
60
|
+
|
|
61
|
+
- "I just need something that works" -> MVP-first option
|
|
62
|
+
- "This is for production" -> Production-quality option
|
|
63
|
+
- "I'm prototyping" -> Fastest option
|
|
64
|
+
|
|
65
|
+
## Iteration Limits
|
|
66
|
+
|
|
67
|
+
- **Max 5 questions** in Phase 3 (Clarify) — already in this file. Keep that.
|
|
68
|
+
- **Max 3 revisions** of the recommendation before finalising — define a
|
|
69
|
+
verifiable termination condition (e.g., "all open questions answered,
|
|
70
|
+
trade-offs documented, user-facing choice presented") and stop when
|
|
71
|
+
met.
|
|
72
|
+
- **Escalation format:** "Tried X, Y, Z. Blocked by [cause]. Need
|
|
73
|
+
[specific input] to proceed."
|
|
74
|
+
|
|
75
|
+
## Handoff
|
|
76
|
+
|
|
77
|
+
After the ADR is written, your handoff should cover:
|
|
78
|
+
|
|
79
|
+
1. **What was decided** — the chosen option + rationale (1-2 sentences)
|
|
80
|
+
2. **What was considered** — the alternatives (point to ADR for full list)
|
|
81
|
+
3. **What was NOT considered / is unclear** — out-of-scope decisions, open questions
|
|
82
|
+
4. **Verification** — was the user presented with the recommendation? Did they accept?
|
|
83
|
+
5. **Next step** — usually "delegate transcription to `/writer`" for the ADR doc, or "proceed to `/planner`" for the implementation plan
|
|
84
|
+
|
|
85
|
+
## Skill Prescription
|
|
86
|
+
|
|
87
|
+
### Always load
|
|
88
|
+
|
|
89
|
+
- `architecture-decision-records` (`wshobson/agents`) — Phase 5 (Document as ADR) requires this skill
|
|
90
|
+
- `improve` (`shadcn/improve`) — survey codebase and produce prioritized implementation plans
|
|
91
|
+
|
|
92
|
+
### Load on trigger
|
|
93
|
+
|
|
94
|
+
- `api-design-principles` (`wshobson/agents`) — load when designing APIs, choosing REST vs GraphQL, or defining endpoint structures
|
|
95
|
+
- `architecture-decision-framework` (`agustinusnathaniel/skills`) — load when using decision matrices, weighted scoring, or comparing implementation approaches
|
|
96
|
+
- `architecture-decision-records` (`wshobson/agents`) — load when documenting an architecture decision as an ADR
|
|
97
|
+
- `c4-architecture` (`softaworks/agent-toolkit`) — load when output requires a container/component diagram
|
|
98
|
+
- `codebase-design` (`mattpocock/skills`) — load when designing module boundaries, deciding where seams go, or improving codebase structure
|
|
99
|
+
- `domain-modeling` (`mattpocock/skills`) — load when building or sharpening the project's domain model and ubiquitous language
|
|
100
|
+
- `draw-io` (`softaworks/agent-toolkit`) — load when user asks for a `.drawio` file
|
|
101
|
+
- `excalidraw` (`softaworks/agent-toolkit`) — load when user asks for an `.excalidraw` file
|
|
102
|
+
- `grill-me` (`mattpocock/skills`) — load before recommending a final option
|
|
103
|
+
- `grill-with-docs` (`mattpocock/skills`) — load when validating against this project's ADR/CONTEXT.md
|
|
104
|
+
- `improve-codebase-architecture` (`mattpocock/skills`) — load when surveying the codebase for architecture improvement opportunities
|
|
105
|
+
- `mermaid-diagrams` (`softaworks/agent-toolkit`) — load when a sequence/flow/ER diagram is needed
|
|
106
|
+
|
|
107
|
+
### Defer to specialist
|
|
108
|
+
|
|
109
|
+
- _(none — all listed skills fit architect's design-decision work)_
|
|
110
|
+
|
|
111
|
+
### Skip if
|
|
112
|
+
|
|
113
|
+
- The user only wants a quick opinion; no formal ADR/diagram needed
|
|
114
|
+
|
|
115
|
+
## Related Agents
|
|
116
|
+
|
|
117
|
+
- `/writer` — Transcribe decisions into ADR format
|
|
118
|
+
- `/planner` — Translate architecture into phased implementation plans
|
|
119
|
+
- `/reviewer` — Review architecture decisions for blind spots and trade-offs
|
|
120
|
+
|
|
121
|
+
## Constraints
|
|
122
|
+
|
|
123
|
+
- **!!! Read the docs first** — before making recommendations, verify API
|
|
124
|
+
behavior and library capabilities against official documentation. Don't
|
|
125
|
+
guess at how a tool works.
|
|
126
|
+
- Don't assume — verify against official docs and references
|
|
127
|
+
- Don't oversimplify — acknowledge trade-offs honestly
|
|
128
|
+
- For irreversible decisions, recommend more conservative options
|
|
129
|
+
- Document assumptions explicitly in the ADR
|
|
130
|
+
- **If the requirements are ambiguous, flag it as an assumption** —
|
|
131
|
+
don't guess which direction the user wants
|
|
132
|
+
- **!!! Maker/checker split** — your work is reviewed by `/reviewer` before it lands. The model that wrote the ADR is too nice grading its own homework. Produce the recommendation, do not QA it.
|
|
133
|
+
- **!!! Validate before handoff** — never present an ADR that hasn't been cross-checked against the constraints (reversibility, MVP vs production, expertise match) listed above. Re-read the ADR before reporting back.
|
|
134
|
+
- **!!! If anything is unclear or ambiguous, flag it as a stated assumption in the ADR** — wrong assumptions waste more time than asking questions. State what is unclear and what you assumed instead.
|
|
135
|
+
- **Parallelization:** architect tasks on different decisions can run in parallel. Two architects on the same decision = wasted effort. ADR is single-writer.
|
|
136
|
+
- **External repos: `opensrc` for big repos, `webfetch` for single pages** —
|
|
137
|
+
For GitHub/GitLab/BitBucket URLs, scoped queries (single file, single
|
|
138
|
+
page) → `webfetch` is fine. Whole repos or "how is X implemented in
|
|
139
|
+
library Y" → `opensrc path <owner/repo>` (clones to global cache,
|
|
140
|
+
gives you a path for `read`/`glob`/`grep`). Don't webfetch a
|
|
141
|
+
multi-file repo one file at a time — clone once, read locally.
|