@maestria/pi 0.7.12 → 0.7.14

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/README.md CHANGED
@@ -19,11 +19,16 @@ Uninstall via `pnpx maestria@latest uninstall pi`. The `@gotgenes/pi-subagents`
19
19
 
20
20
  ## What It Provides
21
21
 
22
- - **4 methodology skills** - orchestrator dispatcher, global agent rules, handoff contract, and iteration limits, injected into every session.
23
- - **3 workflow modes** - `/fein` (full pipeline), `/sonar` (research only), `/blitz` (fast implementation).
22
+ - **Methodology skills** (4 as of 2026-09-22; see the [package directory](https://github.com/agustinusnathaniel/maestria/blob/main/packages/pi/skills) for the current list) - orchestrator dispatcher, global agent rules, handoff contract, and iteration limits, injected into every session.
23
+ - **Workflow modes** (3 as of 2026-09-22; see the [user-facing documentation](https://maestria.sznm.dev/pi-omp/) for the current list) - `/fein` (full pipeline), `/sonar` (research only), `/blitz` (fast implementation).
24
24
  - **Compaction preservation** - session state survives compaction with structured summaries.
25
25
  - **Subagent dispatch** - delegation to specialist subagents via the `@gotgenes/pi-subagents` peer package.
26
26
  - **Maker/checker split** - `/review` mode blocks destructive tools where Pi supports it.
27
+ - **Root project customization** - `.maestria/workflow.md` then `.maestria/rules.md` from the session directory, injected every turn as subordinate guidance (never waives safety, authorization, or host permissions).
28
+
29
+ ## Root Project Customization
30
+
31
+ Place optional `.maestria/workflow.md` (sequencing) and `.maestria/rules.md` (rules) at the root of the directory you open the session in. Scope is root-only: no ancestor scan, no nested inheritance, and the root is the host-selected session cwd read live each turn (never a process-global). Files are re-read in full on every `before_agent_start` turn, so additions, edits, and deletions apply on the next turn with no restart; nothing is persisted to session entries or compaction state, and post-compaction turns pick up the same fresh read. Absent or empty files leave the prompt unchanged. A present-but-unusable file (directory, special file, unreadable, unresolvable, or a symlink escaping the root) surfaces via a UI notification plus a STOP banner in the system prompt telling the model to report the error and wait, instead of running with silently absent config. Diagnostics name only the relative file and the failure kind. Whether subagent turns automatically receive the same injection is unverified, so delegation briefs still carry the active constraints. Limitation: the Pi host swallows `before_agent_start` handler exceptions, so a broken file cannot cancel the model call itself; the notification plus banner is the loudest supported signal.
27
32
 
28
33
  ## Support / Platform Notes
29
34
 
@@ -75,7 +75,7 @@ Your report should let the next agent start work immediately without re-explorin
75
75
  - What was NOT found (negative findings save downstream time)
76
76
  - What the downstream specialist should focus on first
77
77
 
78
- **If the scoping is unclear or the request is ambiguous, document your scope assumption in the report with rationale and proceed.** Don't ask for clarification - make the best call based on what's given.
78
+ **If scoping is unclear or the request is ambiguous, make the best call, document that scope assumption with rationale in the report, and proceed without asking for clarification.**
79
79
 
80
80
  ## Rules
81
81
 
package/agents/builder.md CHANGED
@@ -46,7 +46,7 @@ Prefer deny by default, keep secrets in the trusted runtime, and fail closed on
46
46
 
47
47
  When superseding code, mark the old path as do-not-extend, keep it until migration completes, then remove it in an isolated change.
48
48
 
49
- Never hand-edit generated outputs; change the single source and regenerate.
49
+ Never hand-edit generated outputs; edit the authoritative source and regenerate per the canonical source invariant.
50
50
 
51
51
  ## Skills
52
52
 
package/agents/planner.md CHANGED
@@ -47,4 +47,4 @@ Include planned phases, assumptions, verification and rollback evidence, and the
47
47
 
48
48
  ## Skills
49
49
 
50
- Use available skill descriptions for unresolved requirements, product discovery, issue/PRD creation, or prototyping when that work is part of the assignment. Skip skill loads for one-step plans.
50
+ Use available skill descriptions for unresolved requirements, product discovery, issue/PRD creation, or prototyping when that work is part of the assignment. See the available `spec-contract` skill for an optional contract header shape. Skip skill loads for one-step plans.
@@ -71,6 +71,7 @@ Use these categories to identify relevant risks. Cover the changed contract and
71
71
  - Is meaningful regression risk covered proportionate to stakes (per Global Rules testing judgment)?
72
72
  - Do tests cover edge cases and error paths where the contract demands it?
73
73
  - Are tests meaningful (observable behavior, not implementation details)?
74
+ - For visual changes, check rendered coverage against the changed surface; missing required evidence blocks acceptance.
74
75
 
75
76
  ### 8. Assumption Validation
76
77
 
@@ -85,6 +86,10 @@ Use these categories to identify relevant risks. Cover the changed contract and
85
86
  - Does the output read like a professional email to a trusted colleague?
86
87
  - Format findings as: `style: [issue] -> [fix/dismiss]`
87
88
 
89
+ ### 10. Spec-contract drift (optional pointer, skip when absent)
90
+
91
+ - When a spec-contract header or owning spec is linked, apply its drift, acceptance-coverage, ambiguity-tagging, delta-stating, and append-only rules; skip when absent.
92
+
88
93
  ## Questions to Ask Yourself
89
94
 
90
95
  1. Is this specific code change related to the overall intended goal?
@@ -135,7 +140,7 @@ Then produce:
135
140
 
136
141
  ## Skills
137
142
 
138
- Use available UI review guidance for interface changes, accessibility guidance for interaction or access risks, metadata guidance for page discovery/sharing, and motion guidance for animation issues. Load `skill-judge` when reviewing skill packages. Skip unrelated skill loads for backend or infrastructure diffs.
143
+ For interface changes, use UI review guidance; for interaction or access risks, accessibility guidance; for page discovery/sharing, metadata guidance; for animation issues, motion guidance. See the available `spec-contract` skill for an optional contract header shape. Load `skill-judge` for skill packages. Skip unrelated loads for backend or infrastructure diffs.
139
144
 
140
145
  ## References
141
146
 
@@ -1,11 +1,14 @@
1
- import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as r}from"node:fs";import i from"node:path";import{homedir as a}from"node:os";import{Data as o,Effect as s}from"effect";import{Type as c}from"typebox";import{SUBAGENT_EVENTS as l}from"@gotgenes/pi-subagents";import{defineTool as u}from"@earendil-works/pi-coding-agent";const d=()=>({activeTask:``,blockers:[],completionPromise:``,filesModified:[],filesRead:[],handoffHistory:[],mode:null,nativeGoal:null,originalModel:null,originalTools:null,reviewMode:!1,reviewModel:null,specialistsDelegated:[],subagentStatus:{}}),f=(e,t,n)=>[t,...e.filter(e=>e!==t)].slice(0,n),p=(e,t,n,r)=>{let i=[{from:t,task:r,timestamp:Date.now(),to:n},...e.handoffHistory].slice(0,5);return{...e,handoffHistory:i}},ee=(e,t)=>({...e,filesModified:f(e.filesModified,t,10)}),te=(e,t)=>({...e,filesRead:f(e.filesRead,t,10)}),ne=(e,t)=>e.specialistsDelegated.includes(t)?e:{...e,specialistsDelegated:[...e.specialistsDelegated,t]},re=e=>({originalModel:e.originalModel,originalTools:e.originalTools,state:{...e,originalModel:null,originalTools:null,reviewMode:!1}}),m=(e,t)=>{e.appendEntry(`maestria_state`,{...t})},h=e=>typeof e==`object`&&!!e,g=e=>{let t=e?.sessionManager;if(typeof t?.getBranch!=`function`)return null;let n=t.getBranch();return Array.isArray(n)?n:null},_=e=>{let t=d();if(!e)return t;for(let n=e.length-1;n>=0;--n){let r=e[n];if(r.type===`custom`&&r.customType===`maestria_state`){h(r.data)&&Object.assign(t,r.data);break}}return t},v=(e,t)=>{for(let t of Object.keys(e))Reflect.deleteProperty(e,t);Object.assign(e,t)},y=e=>{let t=[];if(e.mode&&t.push(`**Mode:** ${e.mode.toUpperCase()}`),e.reviewModel!==null&&e.reviewModel!==void 0&&e.reviewModel!==``&&t.push(`**Review Model:** ${e.reviewModel}`),e.activeTask&&t.push(`**Goal:** ${e.activeTask}`),e.nativeGoal&&t.push(`**Native Goal:** ${e.nativeGoal.objective} (${e.nativeGoal.status})`),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(`
1
+ import{existsSync as e,lstatSync as t,mkdirSync as n,readFileSync as r,realpathSync as i,writeFileSync as a}from"node:fs";import o from"node:path";import{homedir as s}from"node:os";import{Data as c,Effect as l}from"effect";import{Type as u}from"typebox";import{SUBAGENT_EVENTS as d}from"@gotgenes/pi-subagents";import{defineTool as f}from"@earendil-works/pi-coding-agent";const p=()=>({activeTask:``,blockers:[],completionPromise:``,filesModified:[],filesRead:[],handoffHistory:[],mode:null,nativeGoal:null,originalModel:null,originalTools:null,reviewMode:!1,reviewModel:null,specialistsDelegated:[],subagentStatus:{}}),m=(e,t,n)=>[t,...e.filter(e=>e!==t)].slice(0,n),h=(e,t,n,r)=>{let i=[{from:t,task:r,timestamp:Date.now(),to:n},...e.handoffHistory].slice(0,5);return{...e,handoffHistory:i}},ee=(e,t)=>({...e,filesModified:m(e.filesModified,t,10)}),te=(e,t)=>({...e,filesRead:m(e.filesRead,t,10)}),ne=(e,t)=>e.specialistsDelegated.includes(t)?e:{...e,specialistsDelegated:[...e.specialistsDelegated,t]},re=e=>({originalModel:e.originalModel,originalTools:e.originalTools,state:{...e,originalModel:null,originalTools:null,reviewMode:!1}}),g=(e,t)=>{e.appendEntry(`maestria_state`,{...t})},_=e=>typeof e==`object`&&!!e,v=e=>{let t=e?.sessionManager;if(typeof t?.getBranch!=`function`)return null;let n=t.getBranch();return Array.isArray(n)?n:null},y=e=>{let t=p();if(!e)return t;for(let n=e.length-1;n>=0;--n){let r=e[n];if(r.type===`custom`&&r.customType===`maestria_state`){_(r.data)&&Object.assign(t,r.data);break}}return t},b=(e,t)=>{for(let t of Object.keys(e))Reflect.deleteProperty(e,t);Object.assign(e,t)},x=e=>{let t=[];if(e.mode&&t.push(`**Mode:** ${e.mode.toUpperCase()}`),e.reviewModel!==null&&e.reviewModel!==void 0&&e.reviewModel!==``&&t.push(`**Review Model:** ${e.reviewModel}`),e.activeTask&&t.push(`**Goal:** ${e.activeTask}`),e.nativeGoal&&t.push(`**Native Goal:** ${e.nativeGoal.objective} (${e.nativeGoal.status})`),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
2
 
3
- `)},b=(e,t)=>{e.on(`session_before_compact`,e=>{let n=e.preparation;return{compaction:{details:{...t},firstKeptEntryId:n?.firstKeptEntryId??``,summary:y(t),tokensBefore:n?.tokensBefore??0}}}),e.on(`session_before_tree`,e=>e.preparation?.userWantsSummary===!0?{summary:{summary:y(t)}}:void 0)},x=(e,t)=>({setActiveTools:t=>e.setActiveTools(t),setModel:async n=>{t(n)&&await e.setModel(n)}}),S=async(e,t,n)=>{let{state:r,originalModel:i,originalTools:a}=re(n);if(a&&a.length>0&&await e.setActiveTools(a),i!=null&&i!==``)try{let n=t.modelRegistry.getAll().find(e=>e.id===i);n&&await e.setModel(n)}catch{}Object.assign(n,r)},C=async(e,t,n)=>{let{reviewModel:r}=n;if(r==null||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}},w={REVIEW_ACTIVATED:`maestria:review:activated`,REVIEW_DEACTIVATED:`maestria:review:deactivated`,SUBAGENT_COMPLETED:`maestria:subagent:completed`,SUBAGENT_FAILED:`maestria:subagent:failed`,SUBAGENT_STARTED:`maestria:subagent:started`},T=[`adventurer`,`architect`,`builder`,`diagnose`,`planner`,`reviewer`,`writer`],E=e=>{if(!T.some(t=>t===e))throw Error(`Unknown agent: "${e}". Allowed: ${T.join(`, `)}`)},D=(e,t)=>{if(e==null||e===``||!e.trim())throw Error(t)},ie=[`read`,`grep`,`find`,`ls`,`glob`],ae=(e,t)=>{e.registerCommand(`maestria-status`,{description:`Show current maestria session state including handoff history`,handler:(e,n)=>{let r=y(t);if(!r){n.ui.notify(`No active maestria state to report.`);return}n.ui.setEditorText(r)}})},oe=(e,t)=>{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();if(Object.assign(t,{...t,originalModel:i,originalTools:a,reviewMode:!0}),m(e,t),t.reviewModel!==null&&t.reviewModel!==void 0&&t.reviewModel!==``){let n=await C(e,r,t);n!=null&&n!==``&&(r.ui.notify(`Review mode: switched to ${n}`),e.events?.emit(w.REVIEW_ACTIVATED,{originalModel:t.originalModel,reviewModel:n,timestamp:Date.now()}))}e.setActiveTools(ie),e.sendUserMessage([`[REVIEW: ${n}]`,``,`Review: ${n}. Use the reviewer prompt template.`,`Read only, no edits, report findings.`].join(`
4
- `),{deliverAs:`steer`})}})},se=(e,t)=>{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 S(e,r,t),m(e,t),r.ui.notify(`Restored original model and tools.`),e.events?.emit(w.REVIEW_DEACTIVATED,{originalModel:i,timestamp:Date.now()})}})},ce=(e,t)=>{e.registerCommand(`handoff`,{description:`Generate a structured handoff prompt for a new task context`,handler:(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(`
3
+ `)},ie=(e,t)=>{e.on(`session_before_compact`,e=>{let n=e.preparation;return{compaction:{details:{...t},firstKeptEntryId:n?.firstKeptEntryId??``,summary:x(t),tokensBefore:n?.tokensBefore??0}}}),e.on(`session_before_tree`,e=>e.preparation?.userWantsSummary===!0?{summary:{summary:x(t)}}:void 0)},ae=(e,t)=>({setActiveTools:t=>e.setActiveTools(t),setModel:async n=>{t(n)&&await e.setModel(n)}}),S=async(e,t,n)=>{let{state:r,originalModel:i,originalTools:a}=re(n);if(a&&a.length>0&&await e.setActiveTools(a),i!=null&&i!==``)try{let n=t.modelRegistry.getAll().find(e=>e.id===i);n&&await e.setModel(n)}catch{}Object.assign(n,r)},C=async(e,t,n)=>{let{reviewModel:r}=n;if(r==null||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}},w={REVIEW_ACTIVATED:`maestria:review:activated`,REVIEW_DEACTIVATED:`maestria:review:deactivated`,SUBAGENT_COMPLETED:`maestria:subagent:completed`,SUBAGENT_FAILED:`maestria:subagent:failed`,SUBAGENT_STARTED:`maestria:subagent:started`},T=[`adventurer`,`architect`,`builder`,`diagnose`,`planner`,`reviewer`,`writer`],E=e=>{if(!T.some(t=>t===e))throw Error(`Unknown agent: "${e}". Allowed: ${T.join(`, `)}`)},D=(e,t)=>{if(e==null||e===``||!e.trim())throw Error(t)},oe=[`read`,`grep`,`find`,`ls`,`glob`],se=(e,t)=>{e.registerCommand(`maestria-status`,{description:`Show current maestria session state including handoff history`,handler:(e,n)=>{let r=x(t);if(!r){n.ui.notify(`No active maestria state to report.`);return}n.ui.setEditorText(r)}})},ce=(e,t)=>{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();if(Object.assign(t,{...t,originalModel:i,originalTools:a,reviewMode:!0}),g(e,t),t.reviewModel!==null&&t.reviewModel!==void 0&&t.reviewModel!==``){let n=await C(e,r,t);n!=null&&n!==``&&(r.ui.notify(`Review mode: switched to ${n}`),e.events?.emit(w.REVIEW_ACTIVATED,{originalModel:t.originalModel,reviewModel:n,timestamp:Date.now()}))}e.setActiveTools(oe),e.sendUserMessage([`[REVIEW: ${n}]`,``,`Review: ${n}. Use the reviewer prompt template.`,`Read only, no edits, report findings.`].join(`
4
+ `),{deliverAs:`steer`})}})},le=(e,t)=>{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 S(e,r,t),g(e,t),r.ui.notify(`Restored original model and tools.`),e.events?.emit(w.REVIEW_DEACTIVATED,{originalModel:i,timestamp:Date.now()})}})},ue=(e,t)=>{e.registerCommand(`handoff`,{description:`Generate a structured handoff prompt for a new task context`,handler:(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(`
5
5
  `):`(no known problems documented)`,``,`**Assumptions documented:**`,`(document assumptions made, tagged [inferred] where uncertain)`,``,`**Success criteria:**`,`(fill in how to verify completion)`,``,`**Next step:**`,`(fill in what happens after this task)`,``,`---`,`Complete the fields above before sending.`].join(`
6
- `);Object.assign(t,p(t,`current`,`next`,i)),m(e,t),e.sendUserMessage(a,{deliverAs:`steer`})}})},le=(e,t)=>{e.registerCommand(`review-model`,{description:`Set which model to use when entering review mode`,handler:(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,m(e,t),r.ui.notify(`Review model set to: ${i}`)}})},ue=(e,t)=>{ae(e,t),oe(e,t),se(e,t),ce(e,t),le(e,t)},de=(a,o)=>{if(!e(a))return console.warn(`[maestria] Agents source directory not found:`,a),0;try{t(o,{recursive:!0})}catch{return console.warn(`[maestria] Could not create agents directory:`,o),0}let s=0;for(let t of T){let c=i.join(a,`${t}.md`),l=i.join(o,`${t}.md`);if(!e(c)){console.warn(`[maestria] Agent source not found: ${t}.md`);continue}if(!e(l))try{let e=n(c,`utf-8`);r(l,e,`utf-8`),s+=1}catch(e){console.warn(`[maestria] Failed to deploy agent ${t}:`,e)}}return s>0&&console.log(`[maestria] Deployed ${s} specialist agents to ${o}`),s},fe=import.meta.dirname,pe=i.join(fe,`..`,`agents`),me=()=>{de(pe,i.join(a(),`.pi`,`agent`,`agents`))},O=e=>typeof e==`object`&&!!e,k=e=>O(e)?typeof e.api==`string`&&typeof e.baseUrl==`string`&&typeof e.contextWindow==`number`&&typeof e.cost==`object`&&e.cost!==null&&typeof e.id==`string`&&Array.isArray(e.input)&&typeof e.maxTokens==`number`&&typeof e.name==`string`&&typeof e.provider==`string`&&typeof e.reasoning==`boolean`:!1,A=e=>({appendEntry:(t,n)=>{e.appendEntry(t,n)},events:e.events,getActiveTools:()=>e.getActiveTools(),registerCommand:(t,n)=>{e.registerCommand(t,{description:n.description,handler:async(e,t)=>{await n.handler(e,t)}})},sendUserMessage:(t,n)=>{(n.deliverAs===`steer`||n.deliverAs===`followUp`)&&e.sendUserMessage(t,{deliverAs:n.deliverAs})},setActiveTools:t=>{e.setActiveTools(t)},setModel:async t=>{k(t)&&await e.setModel(t)}}),j=[`fein`,`sonar`,`blitz`],M={blitz:`[MODE: blitz]`,fein:`[MODE: fein]`,sonar:`[MODE: sonar]`},N={blitz:1,fein:3,sonar:2},P=/```[\s\S]*?```|`[^`]*`/gu,F=e=>{let t=[],n;for(P.lastIndex=0;(n=P.exec(e))!==null;)t.push([n.index,n.index+n[0].length]);return t},I=(e,t)=>t.some(([t,n])=>e>=t&&e<n),L=e=>{let t=e.indexOf(`## MODE:`);return t===-1?`${e.replace(/\s+$/u,``)}\n`:`${e.slice(t).replace(/\s+$/u,``)}\n`},R=e=>e.replaceAll(/[.*+?^${}()|[\]\\]/gu,`\\$&`),z=e=>RegExp(`\\b${R(e)}\\b`,`giu`),he=(e,t)=>{if(!e)return null;let n=F(e),r=t?new Set([...t].map(e=>e.toLowerCase())):void 0,i=null;for(let t of j){if(r!=null&&r.has(t))continue;let a=z(t),o;for(;(o=a.exec(e))!==null;)I(o.index,n)||(i===null||N[t]>N[i.mode])&&(i={index:o.index,keyword:o[0],mode:t})}return i},ge=(e,t)=>(e.slice(0,t.index)+e.slice(t.index+t.keyword.length).replace(/^:\s*/u,``)).replaceAll(/ {2,}/gu,` `).trim();function _e(e,t={}){let{cache:n=new Map,getCacheKey:r}=t,i=function(t){let i=r?r(t):t;if(n.has(i))return n.get(i);let a=e.call(this,t);return n.set(i,a),a};return i.cache=n,i}const ve=j,ye=M,be=(e,t)=>{let r=n(i.resolve(t,`${e}.md`),`utf-8`);return L(r)},xe=_e(e=>{let t=e.indexOf(`\0`),n=e.slice(0,t),r=e.slice(t+1);try{return be(r,n)}catch(e){return console.warn(`[maestria] Failed to load mode prompt "${r}":`,e),``}}),B=(e,t)=>{let n=`${i.resolve(t)}\0${e}`;return`${ye[e]}\n\n${xe(n)}`},Se=(e,t,n)=>{if(!e)return null;let r=he(e,n);if(r===null)return null;let i=ge(e,r);return{keyword:r.mode,prompt:B(r.mode,t),strippedText:i}},Ce=(e,t)=>t?`${e}\n\n${t}`:e,we=(e,t,n,r)=>{e(async(e,i)=>{let{text:a}=e,o=Se(a,n);return o?(t.reviewMode&&await r.restoreOriginalState(i),t.mode=o.keyword,r.persistState(),r.transform(Ce(o.prompt,o.strippedText))):r.noMatch})},Te=(e,t,n)=>{e(`mode-clear`,{description:`Clear workflow mode and return to neutral routing`,handler:async(e,r)=>{t.reviewMode&&await n.restoreOriginalState(r),t.mode=null,n.persistState(),r.ui.notify(`Workflow mode cleared. Neutral routing is active.`)}});for(let r of ve)e(r,{description:`Set workflow mode to ${r}`,handler:async(e,i)=>{t.reviewMode&&await n.restoreOriginalState(i),t.mode=r,n.persistState(),i.ui.notify(`Mode set to ${r}. Describe what you'd like to work on.`)}})},V=async(e,t,n)=>{await S(x(e,k),t,n)},Ee=import.meta.dirname,De=i.resolve(Ee,`../agents/commands`),Oe=(e,t)=>{we(t=>{e.on(`input`,t)},t,De,{noMatch:{action:`continue`},persistState:()=>{m(e,t)},restoreOriginalState:async n=>{await V(e,n,t)},transform:e=>({action:`transform`,text:e})})},ke=e=>typeof e!=`object`||!e?!1:`modelRegistry`in e&&`ui`in e,Ae=(e,t)=>{Te((t,n)=>{e.registerCommand(t,{description:n.description,handler:async(e,t)=>{await n.handler(e,t)}})},t,{persistState:()=>{m(e,t)},restoreOriginalState:async n=>{ke(n)&&await V(e,n,t)}})},je=import.meta.dirname,Me=i.resolve(je,`../agents/commands`),Ne=e=>(t,n)=>e.mode===null?{}:{systemPrompt:[t.systemPrompt,``,B(e.mode,Me),``,`The user has set workflow mode to "${e.mode}". Honor this mode throughout the session until changed via /command.`].join(`
7
- `)},H=new Set([`completed`,`steered`,`aborted`,`stopped`,`error`]);var U=class extends o.TaggedError(`SubagentPollError`){};const Pe=(e,t)=>s.sync(()=>{e.onUpdate?.({content:[{text:`${e.label} running... (${Math.round(t*(e.intervalMs??500)/1e3)}s)`,type:`text`}]})}),Fe=e=>{let t=e.intervalMs??500,n=e.timeoutMs??18e4,r=Math.ceil(n/t);return s.gen(function*(){let i=0,a=yield*s.sync(()=>e.service.getRecord(e.id));for(;a&&!H.has(a.status)&&i<r;)yield*s.sleep(t),a=yield*s.sync(()=>e.service.getRecord(e.id)),i+=1,e.sendUpdates&&(yield*Pe(e,i));return a&&!H.has(a.status)?yield*s.fail(new U({id:e.id,message:`Subagent ${e.id} timed out after ${n}ms`,reason:`timeout`})):a||(yield*s.fail(new U({id:e.id,message:`Subagent ${e.id} was cleaned up before completion`,reason:`missing`})))})},Ie=(e,t)=>s.callback(n=>{let r=()=>{n(s.fail(new U({id:e,message:`Maestria subagent call aborted`,reason:`aborted`})))};return t.aborted?r():t.addEventListener(`abort`,r,{once:!0}),s.sync(()=>{t.removeEventListener(`abort`,r)})}),W=e=>{let t=Fe(e);return e.signal?s.raceFirst(t,Ie(e.id,e.signal)):t},Le=e=>typeof e==`object`&&!!e,G=(e,t)=>{if(!Le(e))return;let n=e[t];return typeof n==`string`?n:void 0},Re=(e,t,n)=>e.on(l.STARTED,r=>{let i=G(r,`id`),a=G(r,`type`)??`unknown`;i!==void 0&&(n.subagentStatus[i]={startedAt:Date.now(),status:`running`,type:a},m(t,n),e.emit(w.SUBAGENT_STARTED,{id:i,timestamp:Date.now(),type:a}))}),ze=(e,t,n)=>e.on(l.COMPLETED,r=>{let i=G(r,`id`);if(i===void 0)return;let a=n.subagentStatus[i];a!=null&&(a.status=`completed`,a.completedAt=Date.now()),m(t,n),e.emit(w.SUBAGENT_COMPLETED,{id:i,timestamp:Date.now(),type:a?.type})}),Be=(e,t,n)=>e.on(l.FAILED,r=>{let i=G(r,`id`);if(i===void 0)return;let a=G(r,`status`)??`error`,o=n.subagentStatus[i];o!=null&&(o.status=a,o.completedAt=Date.now()),m(t,n),e.emit(w.SUBAGENT_FAILED,{id:i,timestamp:Date.now(),type:o?.type})}),Ve=(e,t,n)=>e.on(l.STEERED,e=>{let r=G(e,`id`);r!==void 0&&(n.subagentStatus[r]??={startedAt:Date.now(),status:`running`,type:`unknown`},m(t,n))}),He=(e,t,n)=>{let{events:r}=e;if(r==null)return;let i=[Re(r,e,t),ze(r,e,t),Be(r,e,t),Ve(r,e,t)];n?.push(...i)},K=T,q=18e4,J=(e,t)=>{for(let n of t)try{e.abort?.(n)}catch{}},Y=e=>s.tapError(W(e),()=>s.sync(()=>{J(e.service,[e.id])})),X=(e,t,n,r)=>{let i=ne(p(t,`orchestrator`,n,r),n);Object.assign(t,i),m(e,t)},Ue=e=>{let t=e.mode??`single`;if(t===`single`){if(e.agent===void 0||e.agent===``||!K.includes(e.agent))return`Invalid maestria_subagent call: 'agent' is required and must be one of ${K.join(`, `)}.`;D(e.task,`Task description is required`)}else if(t===`parallel`){if(!e.tasks||e.tasks.length<2)throw Error(`For parallel mode, tasks array is required with at least 2 items`);if(e.tasks.length>8)throw Error(`For parallel mode, tasks array may have at most 8 items (got ${e.tasks.length})`);for(let t of e.tasks)E(t.agent),D(t.task,`Task description is required for all tasks`)}else if(t===`chain`){if(!e.tasks||e.tasks.length<2)throw Error(`For chain mode, tasks array is required with at least 2 items`);for(let t of e.tasks)E(t.agent),D(t.task,`Task description is required for all tasks`)}return t},We=async(e,t,n,r,i,a,o)=>{let c=n.spawn(r,i,{description:i.slice(0,80),foreground:!0,inheritContext:!0});X(e,t,r,i);let l=await s.runPromise(Y({id:c,intervalMs:500,label:`Subagent ${r}`,onUpdate:o,sendUpdates:!0,service:n,signal:a,timeoutMs:q}));return{content:[{text:l.result??l.error??`No output.`,type:`text`}],details:{subagentId:c}}},Ge=(e,t,n,r)=>{let i=[];try{for(let a of r){let r=n.spawn(a.agent,a.task,{description:a.task.slice(0,80),foreground:!0,inheritContext:!0});i.push(r),X(e,t,a.agent,a.task)}}catch(e){throw J(n,i),e}return i},Ke=async(e,t,n,r,i)=>await s.runPromise(s.all(e.map((a,o)=>{let c=t[o];return s.match(W({id:a,intervalMs:500,label:`${c.agent} (${o+1}/${t.length})`,onUpdate:i,sendUpdates:!1,service:n,signal:r,timeoutMs:q}),{onFailure:t=>(J(n,e),{error:t}),onSuccess:e=>({record:e})})}),{concurrency:`unbounded`})),qe=(e,t,n)=>{let r=[`## Parallel Results (${e.length} tasks)\n`];for(let[t,i]of e.entries()){let e=n[t];r.push(`### ${t+1}: ${i.agent}`),`error`in e?r.push(`⚠️ ${e.error instanceof Error?e.error.message:String(e.error)}`):r.push(e.record.result??e.record.error??`No output.`)}return{content:[{text:r.join(`
6
+ `);Object.assign(t,h(t,`current`,`next`,i)),g(e,t),e.sendUserMessage(a,{deliverAs:`steer`})}})},de=(e,t)=>{e.registerCommand(`review-model`,{description:`Set which model to use when entering review mode`,handler:(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,g(e,t),r.ui.notify(`Review model set to: ${i}`)}})},fe=(e,t)=>{se(e,t),ce(e,t),le(e,t),ue(e,t),de(e,t)},pe=(t,i)=>{if(!e(t))return console.warn(`[maestria] Agents source directory not found:`,t),0;try{n(i,{recursive:!0})}catch{return console.warn(`[maestria] Could not create agents directory:`,i),0}let s=0;for(let n of T){let c=o.join(t,`${n}.md`),l=o.join(i,`${n}.md`);if(!e(c)){console.warn(`[maestria] Agent source not found: ${n}.md`);continue}if(!e(l))try{let e=r(c,`utf-8`);a(l,e,`utf-8`),s+=1}catch(e){console.warn(`[maestria] Failed to deploy agent ${n}:`,e)}}return s>0&&console.log(`[maestria] Deployed ${s} specialist agents to ${i}`),s},me=import.meta.dirname,he=o.join(me,`..`,`agents`),ge=()=>{pe(he,o.join(s(),`.pi`,`agent`,`agents`))},_e=e=>typeof e==`object`&&!!e,O=e=>_e(e)?typeof e.api==`string`&&typeof e.baseUrl==`string`&&typeof e.contextWindow==`number`&&typeof e.cost==`object`&&e.cost!==null&&typeof e.id==`string`&&Array.isArray(e.input)&&typeof e.maxTokens==`number`&&typeof e.name==`string`&&typeof e.provider==`string`&&typeof e.reasoning==`boolean`:!1,ve=e=>({appendEntry:(t,n)=>{e.appendEntry(t,n)},events:e.events,getActiveTools:()=>e.getActiveTools(),registerCommand:(t,n)=>{e.registerCommand(t,{description:n.description,handler:async(e,t)=>{await n.handler(e,t)}})},sendUserMessage:(t,n)=>{(n.deliverAs===`steer`||n.deliverAs===`followUp`)&&e.sendUserMessage(t,{deliverAs:n.deliverAs})},setActiveTools:t=>{e.setActiveTools(t)},setModel:async t=>{O(t)&&await e.setModel(t)}}),k=[`fein`,`sonar`,`blitz`],A={blitz:`[MODE: blitz]`,fein:`[MODE: fein]`,sonar:`[MODE: sonar]`},j={blitz:1,fein:3,sonar:2},M=/```[\s\S]*?```|`[^`]*`/gu,N=e=>{let t=[],n;for(M.lastIndex=0;(n=M.exec(e))!==null;)t.push([n.index,n.index+n[0].length]);return t},P=(e,t)=>t.some(([t,n])=>e>=t&&e<n),F=e=>{let t=e.indexOf(`## MODE:`);return t===-1?`${e.replace(/\s+$/u,``)}\n`:`${e.slice(t).replace(/\s+$/u,``)}\n`},ye=e=>e.replaceAll(/[.*+?^${}()|[\]\\]/gu,`\\$&`),be=e=>RegExp(`\\b${ye(e)}\\b`,`giu`),xe=(e,t)=>{if(!e)return null;let n=N(e),r=t?new Set([...t].map(e=>e.toLowerCase())):void 0,i=null;for(let t of k){if(r!=null&&r.has(t))continue;let a=be(t),o;for(;(o=a.exec(e))!==null;)P(o.index,n)||(i===null||j[t]>j[i.mode])&&(i={index:o.index,keyword:o[0],mode:t})}return i},Se=(e,t)=>(e.slice(0,t.index)+e.slice(t.index+t.keyword.length).replace(/^:\s*/u,``)).replaceAll(/ {2,}/gu,` `).trim();function Ce(e,t={}){let{cache:n=new Map,getCacheKey:r}=t,i=function(t){let i=r?r(t):t;if(n.has(i))return n.get(i);let a=e.call(this,t);return n.set(i,a),a};return i.cache=n,i}const we=k,Te=A,Ee=(e,t)=>{let n=r(o.resolve(t,`${e}.md`),`utf-8`);return F(n)},De=Ce(e=>{let t=e.indexOf(`\0`),n=e.slice(0,t),r=e.slice(t+1);try{return Ee(r,n)}catch(e){return console.warn(`[maestria] Failed to load mode prompt "${r}":`,e),``}}),I=(e,t)=>{let n=`${o.resolve(t)}\0${e}`;return`${Te[e]}\n\n${De(n)}`},Oe=(e,t,n)=>{if(!e)return null;let r=xe(e,n);if(r===null)return null;let i=Se(e,r);return{keyword:r.mode,prompt:I(r.mode,t),strippedText:i}},ke=(e,t)=>t?`${e}\n\n${t}`:e,Ae=(e,t,n,r)=>{e(async(e,i)=>{let{text:a}=e,o=Oe(a,n);return o?(t.reviewMode&&await r.restoreOriginalState(i),t.mode=o.keyword,r.persistState(),r.transform(ke(o.prompt,o.strippedText))):r.noMatch})},je=(e,t,n)=>{e(`mode-clear`,{description:`Clear workflow mode and return to neutral routing`,handler:async(e,r)=>{t.reviewMode&&await n.restoreOriginalState(r),t.mode=null,n.persistState(),r.ui.notify(`Workflow mode cleared. Neutral routing is active.`)}});for(let r of we)e(r,{description:`Set workflow mode to ${r}`,handler:async(e,i)=>{t.reviewMode&&await n.restoreOriginalState(i),t.mode=r,n.persistState(),i.ui.notify(`Mode set to ${r}. Describe what you'd like to work on.`)}})},L=async(e,t,n)=>{await S(ae(e,O),t,n)},Me=import.meta.dirname,Ne=o.resolve(Me,`../agents/commands`),Pe=(e,t)=>{Ae(t=>{e.on(`input`,t)},t,Ne,{noMatch:{action:`continue`},persistState:()=>{g(e,t)},restoreOriginalState:async n=>{await L(e,n,t)},transform:e=>({action:`transform`,text:e})})},Fe=e=>typeof e!=`object`||!e?!1:`modelRegistry`in e&&`ui`in e,Ie=(e,t)=>{je((t,n)=>{e.registerCommand(t,{description:n.description,handler:async(e,t)=>{await n.handler(e,t)}})},t,{persistState:()=>{g(e,t)},restoreOriginalState:async n=>{Fe(n)&&await L(e,n,t)}})},Le=[`.maestria/workflow.md`,`.maestria/rules.md`],Re=e=>typeof e==`string`&&e!==``,R=e=>typeof e==`object`&&!!e&&`code`in e&&e.code===`ENOENT`,ze=e=>e instanceof Error&&e.message.startsWith(`[maestria] Project config`),z=(e,t)=>{if(t===`directory`)throw Error(`[maestria] Project config "${e}" is a directory, expected a file`);if(t===`other`)throw Error(`[maestria] Project config "${e}" is not a regular file`)},B=(e,t,n)=>ze(n)?n:Error(`[maestria] Project config "${e}" ${t}`),Be={kindOf:e=>{try{let n=t(e);return n.isDirectory()?`directory`:n.isFile()||n.isSymbolicLink()?`file`:`other`}catch(e){if(R(e))return`missing`;throw e}},readFile:e=>r(e,`utf-8`),resolveLink:e=>i(e)},Ve=(e,t)=>{let n=o.relative(e,t);return n===`..`||n.startsWith(`..${o.sep}`)||o.isAbsolute(n)},He=(e,t,n)=>{let r=o.join(e,t),i;try{i=n.kindOf(r)}catch(e){throw B(t,`cannot be accessed`,e)}if(i===`missing`)return;z(t,i);let a;try{a=n.resolveLink(r)}catch(e){throw B(t,`cannot be resolved`,e)}if(Ve(e,a))throw Error(`[maestria] Project config "${t}" resolves outside the project root`);let s;try{s=n.kindOf(a)}catch(e){throw B(t,`cannot be accessed`,e)}if(s===`missing`)throw Error(`[maestria] Project config "${t}" cannot be accessed`);z(t,s);let c;try{c=n.readFile(r)}catch(e){throw B(t,`exists but cannot be read`,e)}if(c!==``)return{content:c,rel:t}},V=(e,t=Be)=>{if(!Re(e))return[];let n;try{n=i(e)}catch(t){if(!R(t))throw Error(`[maestria] Project config root cannot be accessed`);n=o.resolve(e)}let r=[];for(let e of Le){let i=He(n,e,t);i!==void 0&&r.push(i)}return r},Ue=e=>[`Project customization from ${e.rel} (subordinate guidance: it may replace configurable workflows but never waives safety, authorization, or host permissions):`,e.content].join(`
7
+ `),We=e=>[`Project customization failed to load. STOP: do not execute the user request on potentially overridden configuration.`,`Report this error to the user and wait for the project files to be fixed.`,e].join(`
8
+ `),Ge=e=>typeof e?.cwd==`string`&&e.cwd!==``?e.cwd:void 0,Ke=(e,t)=>{try{e?.ui?.notify?.(t)}catch{}},qe=(e,t)=>{let n=Ge(e);if(n===void 0)return{errorMessage:void 0,sections:[]};try{return{errorMessage:void 0,sections:t===void 0?V(n):V(n,t)}}catch(t){let n=t instanceof Error?t.message:String(t);return Ke(e,n),{errorMessage:n,sections:[]}}},Je=import.meta.dirname,Ye=o.resolve(Je,`../agents/commands`),Xe=(e,t)=>(n,r)=>{let i=qe(r,t);if(i.errorMessage!==void 0)return{systemPrompt:[n.systemPrompt,``,We(i.errorMessage)].join(`
9
+ `)};let{sections:a}=i;if(e.mode===null&&a.length===0)return{};let o=[n.systemPrompt,``];e.mode!==null&&o.push(I(e.mode,Ye),``,`The user has set workflow mode to "${e.mode}". Honor this mode throughout the session until changed via /command.`,``);for(let e of a)o.push(Ue(e),``);return{systemPrompt:o.join(`
10
+ `)}},H=new Set([`completed`,`steered`,`aborted`,`stopped`,`error`]);var U=class extends c.TaggedError(`SubagentPollError`){};const Ze=(e,t)=>l.sync(()=>{e.onUpdate?.({content:[{text:`${e.label} running... (${Math.round(t*(e.intervalMs??500)/1e3)}s)`,type:`text`}]})}),Qe=e=>{let t=e.intervalMs??500,n=e.timeoutMs??18e4,r=Math.ceil(n/t);return l.gen(function*(){let i=0,a=yield*l.sync(()=>e.service.getRecord(e.id));for(;a&&!H.has(a.status)&&i<r;)yield*l.sleep(t),a=yield*l.sync(()=>e.service.getRecord(e.id)),i+=1,e.sendUpdates&&(yield*Ze(e,i));return a&&!H.has(a.status)?yield*l.fail(new U({id:e.id,message:`Subagent ${e.id} timed out after ${n}ms`,reason:`timeout`})):a||(yield*l.fail(new U({id:e.id,message:`Subagent ${e.id} was cleaned up before completion`,reason:`missing`})))})},$e=(e,t)=>l.callback(n=>{let r=()=>{n(l.fail(new U({id:e,message:`Maestria subagent call aborted`,reason:`aborted`})))};return t.aborted?r():t.addEventListener(`abort`,r,{once:!0}),l.sync(()=>{t.removeEventListener(`abort`,r)})}),W=e=>{let t=Qe(e);return e.signal?l.raceFirst(t,$e(e.id,e.signal)):t},et=e=>typeof e==`object`&&!!e,G=(e,t)=>{if(!et(e))return;let n=e[t];return typeof n==`string`?n:void 0},tt=(e,t,n)=>e.on(d.STARTED,r=>{let i=G(r,`id`),a=G(r,`type`)??`unknown`;i!==void 0&&(n.subagentStatus[i]={startedAt:Date.now(),status:`running`,type:a},g(t,n),e.emit(w.SUBAGENT_STARTED,{id:i,timestamp:Date.now(),type:a}))}),nt=(e,t,n)=>e.on(d.COMPLETED,r=>{let i=G(r,`id`);if(i===void 0)return;let a=n.subagentStatus[i];a!=null&&(a.status=`completed`,a.completedAt=Date.now()),g(t,n),e.emit(w.SUBAGENT_COMPLETED,{id:i,timestamp:Date.now(),type:a?.type})}),rt=(e,t,n)=>e.on(d.FAILED,r=>{let i=G(r,`id`);if(i===void 0)return;let a=G(r,`status`)??`error`,o=n.subagentStatus[i];o!=null&&(o.status=a,o.completedAt=Date.now()),g(t,n),e.emit(w.SUBAGENT_FAILED,{id:i,timestamp:Date.now(),type:o?.type})}),it=(e,t,n)=>e.on(d.STEERED,e=>{let r=G(e,`id`);r!==void 0&&(n.subagentStatus[r]??={startedAt:Date.now(),status:`running`,type:`unknown`},g(t,n))}),at=(e,t,n)=>{let{events:r}=e;if(r==null)return;let i=[tt(r,e,t),nt(r,e,t),rt(r,e,t),it(r,e,t)];n?.push(...i)},K=T,q=18e4,J=(e,t)=>{for(let n of t)try{e.abort?.(n)}catch{}},Y=e=>l.tapError(W(e),()=>l.sync(()=>{J(e.service,[e.id])})),X=(e,t,n,r)=>{let i=ne(h(t,`orchestrator`,n,r),n);Object.assign(t,i),g(e,t)},ot=e=>{let t=e.mode??`single`;if(t===`single`){if(e.agent===void 0||e.agent===``||!K.includes(e.agent))return`Invalid maestria_subagent call: 'agent' is required and must be one of ${K.join(`, `)}.`;D(e.task,`Task description is required`)}else if(t===`parallel`){if(!e.tasks||e.tasks.length<2)throw Error(`For parallel mode, tasks array is required with at least 2 items`);if(e.tasks.length>8)throw Error(`For parallel mode, tasks array may have at most 8 items (got ${e.tasks.length})`);for(let t of e.tasks)E(t.agent),D(t.task,`Task description is required for all tasks`)}else if(t===`chain`){if(!e.tasks||e.tasks.length<2)throw Error(`For chain mode, tasks array is required with at least 2 items`);for(let t of e.tasks)E(t.agent),D(t.task,`Task description is required for all tasks`)}return t},st=async(e,t,n,r,i,a,o)=>{let s=n.spawn(r,i,{description:i.slice(0,80),foreground:!0,inheritContext:!0});X(e,t,r,i);let c=await l.runPromise(Y({id:s,intervalMs:500,label:`Subagent ${r}`,onUpdate:o,sendUpdates:!0,service:n,signal:a,timeoutMs:q}));return{content:[{text:c.result??c.error??`No output.`,type:`text`}],details:{subagentId:s}}},ct=(e,t,n,r)=>{let i=[];try{for(let a of r){let r=n.spawn(a.agent,a.task,{description:a.task.slice(0,80),foreground:!0,inheritContext:!0});i.push(r),X(e,t,a.agent,a.task)}}catch(e){throw J(n,i),e}return i},lt=async(e,t,n,r,i)=>await l.runPromise(l.all(e.map((a,o)=>{let s=t[o];return l.match(W({id:a,intervalMs:500,label:`${s.agent} (${o+1}/${t.length})`,onUpdate:i,sendUpdates:!1,service:n,signal:r,timeoutMs:q}),{onFailure:t=>(J(n,e),{error:t}),onSuccess:e=>({record:e})})}),{concurrency:`unbounded`})),ut=(e,t,n)=>{let r=[`## Parallel Results (${e.length} tasks)\n`];for(let[t,i]of e.entries()){let e=n[t];r.push(`### ${t+1}: ${i.agent}`),`error`in e?r.push(`⚠️ ${e.error instanceof Error?e.error.message:String(e.error)}`):r.push(e.record.result??e.record.error??`No output.`)}return{content:[{text:r.join(`
8
11
 
9
- `),type:`text`}],details:{subagentIds:t}}},Je=async(e,t,n,r,i,a)=>{a?.({content:[{text:`Spawning ${r.length} parallel subagents...`,type:`text`}]});let o=Ge(e,t,n,r),s=await Ke(o,r,n,i,a);return a?.({content:[{text:`All ${r.length} parallel subagents settled.`,type:`text`}]}),qe(r,o,s)},Ye=(e,t)=>e.replaceAll(`{previous}`,()=>t),Z=async(e,t,n,r,i,a,o,c)=>{if(o>=r.length)return c;let l=r[o],u=o>0?Ye(l.task,c):l.task,d=n.spawn(l.agent,u,{description:u.slice(0,80),foreground:!0,inheritContext:!0});X(e,t,l.agent,u),a?.({content:[{text:`Chain step ${o+1}/${r.length}: ${l.agent} running...`,type:`text`}]});let f;try{let e=await s.runPromise(Y({id:d,intervalMs:500,label:`Chain step ${o+1}: ${l.agent}`,onUpdate:a,sendUpdates:!0,service:n,signal:i,timeoutMs:q}));f=e.result??e.error??`No output.`}catch(e){return`[error] ${e instanceof Error?e.message:String(e)}`}return o<r.length-1&&a?.({content:[{text:`Chain step ${o+1}/${r.length}: ${l.agent} completed. Moving to next step.`,type:`text`}]}),await Z(e,t,n,r,i,a,o+1,f)},Xe=async(e,t,n,r,i,a)=>({content:[{text:await Z(e,t,n,r,i,a,0,``),type:`text`}],details:{subagentId:`chain-completed`}}),Ze=async(e,t,n,r,i,a)=>{let o=r.mode??`single`;if(o===`single`){if(typeof r.agent!=`string`||typeof r.task!=`string`)throw TypeError(`Single mode requires an agent and task`);return await We(e,t,n,r.agent,r.task,i,a)}if((o===`parallel`||o===`chain`)&&Array.isArray(r.tasks))return await(o===`parallel`?Je(e,t,n,r.tasks,i,a):Xe(e,t,n,r.tasks,i,a));throw Error(`Unknown dispatch mode`)},Qe=c.Object({agent:c.String({description:`Specialist agent name (required): adventurer, architect, builder, diagnose, planner, reviewer, writer`}),mode:c.Optional(c.Union([c.Literal(`parallel`),c.Literal(`chain`),c.Literal(`single`)])),task:c.String({description:`Task description for the subagent (required)`}),tasks:c.Optional(c.Array(c.Object({agent:c.String(),task:c.String()}),{description:`Array of task objects for parallel or chain dispatch`}))}),$e=()=>({content:[{text:[`## Subagent Dispatch Unavailable`,``,"The `@gotgenes/pi-subagents` extension is required for subagent dispatch but has not been loaded.",``,`Install it as a Pi extension:`,``,"```",`pi install npm:@gotgenes/pi-subagents`,"```",``,`Then restart your Pi session.`].join(`
10
- `),type:`text`}],details:{}}),et=(e,t)=>({content:[{text:[`## Subagent Handoff Required`,``,`**From:** orchestrator`,`**To:** ${e}`,`**Task:** ${t}`,``,`Subagent dispatch failed. Please delegate this work manually.`].join(`
11
- `),type:`text`}],details:{}}),tt=async(e,t,n,r,i)=>{let{getSubagentsService:a}=await import(`@gotgenes/pi-subagents`),o=a();if(!o||typeof o.spawn!=`function`)return $e();try{return await Ze(e,t,o,n,r,i)}catch(e){console.warn(`[maestria] Subagent dispatch failed:`,e);let t=n.agent??n.tasks?.[0]?.agent??`unknown`,r=n.task??n.tasks?.map(e=>e.task).join(`; `)??`unknown`;return et(t,r)}},nt=(e,t)=>({description:`Dispatch a task to a @gotgenes/pi-subagents specialist subagent`,async execute(n,r,i,a,o){if(t.reviewMode)return{content:[{text:`Subagent dispatch is not available during review mode. Use /restore-model to exit review mode first.`,type:`text`}],details:{}};let s=Ue(r);return s.startsWith(`Invalid`)?{content:[{text:`${s} Re-dispatch with a valid agent name; the orchestrator may continue read-only exploration while the brief is corrected.`,type:`text`}],details:{}}:await tt(e,t,r,i,a?e=>{a({content:e.content.map(({text:e})=>({text:e,type:`text`})),details:e.details??{}})}:void 0)},label:`Maestria Subagent`,name:`maestria_subagent`,parameters:Qe,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).`],promptSnippet:`Delegate tasks to @maestria specialist subagents (adventurer, architect, builder, planner, diagnose, reviewer, writer)`}),Q=(e,t,n)=>{e.registerTool(nt(e,t)),He(e,t,n)},rt=e=>({appendEntry:(t,n)=>{e.appendEntry(t,n)},events:e.events,registerTool:t=>{e.registerTool(u({...t,async execute(e,n,r,i,a){return await t.execute(e,n,r,i,a)}}))}}),it=[/rm\s+-rf\s+\//u,/dd\s+if=/u,/>\s*\/dev\/sd/u,/chmod\s+-R\s+777\s+\//u,/mkfs\.\w+/u,/:\(\)\{ :\|:& \};:/u,/>\s*\/etc\/(?<targetFile>passwd|shadow|sudoers)/u,/\beval\b/u,/wget\s+-O\s*-\s*\|\s*(?<wgetShell>bash|sh)/u,/curl\s+.*\|\s*(?<curlShell>bash|sh)/u,/crontab\s+-r/u],at=/^(?<command>ls|cat|head|tail|git status|git diff|git log|git branch|find|grep|rg|pnpm test|npm test|pwd|which)\b/u,ot=e=>{let t=e.trim();if(t.includes(`$(`)||t.includes("`"))return!1;let n=t.replaceAll(/\d?>&[12]/gu,``);return!n.includes(`>`)&&n.split(/[\n;&|]+/u).every(e=>at.test(e.trim()))},st=e=>{for(let t of it)if(t.test(e))return t;return null},ct=e=>e===`edit`||e===`write`||e===`bash`?`Review mode is active. Report findings, do not edit.`:null,$=e=>typeof e==`object`&&!!e,lt=(e,t)=>t??(e===`task`?`Use 'maestria_subagent' or 'task()' to delegate mutations to specialists.`:`Use 'maestria_subagent' to delegate mutations to specialists.`),ut=(e,t,n,r,i)=>{if(e.mode!==null&&n.getActiveTools().some(e=>e===r)&&(n.isMutationTool??(e=>{let t=e.toolName??``;return t===`edit`||t===`write`||t===`patch`||t===`bash`||n.extraMutations?.some(e=>e===t)===!0}))(t)&&t.toolName!==r){if(n.isBashTool(t)){let e=$(t.input)?t.input:void 0,n=typeof e?.command==`string`?e.command:``;if(ot(n))return}return{block:!0,reason:`Tool '${t.toolName}' is blocked for the orchestrator. ${i}`}}},dt=async(e,t,n)=>{if(!t.isBashTool(e))return;let r=$(e.input)?e.input:void 0;if(!r)return;let{command:i}=r;if(typeof i!=`string`||!i)return;let a=st(i);if(a&&!(n.hasUI===!0&&n.ui&&await n.ui.confirm(`Dangerous Pattern Detected`,`This command matches a dangerous pattern:\n${i}\nProceed?`)))return{block:!0,reason:`Command matches dangerous pattern: ${a}`}},ft=(e,t,n)=>{if(n.isReadTool(t)){let n=$(t.input)?t.input.path:void 0;if(typeof n==`string`&&n)return Object.assign(e,te(e,n)),!0}else if(n.isWriteTool(t)){let n=$(t.input)?t.input.path:void 0;if(typeof n==`string`&&n)return Object.assign(e,ee(e,n)),!0}return!1},pt=e=>{let{delegationTool:t}=e,n=lt(t,e.delegationHint),r=()=>{if(e.persist){e.persist();return}e.pi&&m(e.pi,e.getState())};return async(i,a)=>{if(i.toolName===void 0||i.toolName===``)return;let o=e.getState(),{toolName:s}=i,c=ut(o,i,e,t,n);if(c)return c;if(o.reviewMode&&(e.isWriteTool(i)||e.isBashTool(i)))return{block:!0,reason:ct(s)??`Review mode is active. Report findings, do not edit.`};let l=await dt(i,e,a);if(l)return l;ft(o,i,e)&&r()}},mt=(e,t)=>{let n=pt({delegationTool:`subagent`,getActiveTools:()=>e.getActiveTools(),getState:()=>t,isBashTool:e=>e.toolName===`bash`,isMutationTool:e=>e.toolName===`edit`||e.toolName===`write`||e.toolName===`patch`||e.toolName===`bash`,isReadTool:e=>e.toolName===`read`,isWriteTool:e=>e.toolName===`edit`||e.toolName===`write`,persist:()=>{m(e,t)}});e.on(`tool_call`,n)},ht=e=>{let t=d(),n=[];Ae(e,t),Oe(e,t);let r=Ne(t);e.on(`before_agent_start`,(e,t)=>r(e,t)),e.on(`session_start`,(e,n)=>{me(),v(t,_(g(n)))}),e.on(`session_tree`,(e,n)=>{v(t,_(g(n)))}),b(e,t),Q(rt(e),t,n),ue(A(e),t),e.on(`session_shutdown`,()=>{for(let e of n)e();n.length=0}),mt(e,t)};export{ht as default};
12
+ `),type:`text`}],details:{subagentIds:t}}},dt=async(e,t,n,r,i,a)=>{a?.({content:[{text:`Spawning ${r.length} parallel subagents...`,type:`text`}]});let o=ct(e,t,n,r),s=await lt(o,r,n,i,a);return a?.({content:[{text:`All ${r.length} parallel subagents settled.`,type:`text`}]}),ut(r,o,s)},ft=(e,t)=>e.replaceAll(`{previous}`,()=>t),Z=async(e,t,n,r,i,a,o,s)=>{if(o>=r.length)return s;let c=r[o],u=o>0?ft(c.task,s):c.task,d=n.spawn(c.agent,u,{description:u.slice(0,80),foreground:!0,inheritContext:!0});X(e,t,c.agent,u),a?.({content:[{text:`Chain step ${o+1}/${r.length}: ${c.agent} running...`,type:`text`}]});let f;try{let e=await l.runPromise(Y({id:d,intervalMs:500,label:`Chain step ${o+1}: ${c.agent}`,onUpdate:a,sendUpdates:!0,service:n,signal:i,timeoutMs:q}));f=e.result??e.error??`No output.`}catch(e){return`[error] ${e instanceof Error?e.message:String(e)}`}return o<r.length-1&&a?.({content:[{text:`Chain step ${o+1}/${r.length}: ${c.agent} completed. Moving to next step.`,type:`text`}]}),await Z(e,t,n,r,i,a,o+1,f)},pt=async(e,t,n,r,i,a)=>({content:[{text:await Z(e,t,n,r,i,a,0,``),type:`text`}],details:{subagentId:`chain-completed`}}),mt=async(e,t,n,r,i,a)=>{let o=r.mode??`single`;if(o===`single`){if(typeof r.agent!=`string`||typeof r.task!=`string`)throw TypeError(`Single mode requires an agent and task`);return await st(e,t,n,r.agent,r.task,i,a)}if((o===`parallel`||o===`chain`)&&Array.isArray(r.tasks))return await(o===`parallel`?dt(e,t,n,r.tasks,i,a):pt(e,t,n,r.tasks,i,a));throw Error(`Unknown dispatch mode`)},ht=u.Object({agent:u.String({description:`Specialist agent name (required): adventurer, architect, builder, diagnose, planner, reviewer, writer`}),mode:u.Optional(u.Union([u.Literal(`parallel`),u.Literal(`chain`),u.Literal(`single`)])),task:u.String({description:`Task description for the subagent (required)`}),tasks:u.Optional(u.Array(u.Object({agent:u.String(),task:u.String()}),{description:`Array of task objects for parallel or chain dispatch`}))}),gt=()=>({content:[{text:[`## Subagent Dispatch Unavailable`,``,"The `@gotgenes/pi-subagents` extension is required for subagent dispatch but has not been loaded.",``,`Install it as a Pi extension:`,``,"```",`pi install npm:@gotgenes/pi-subagents`,"```",``,`Then restart your Pi session.`].join(`
13
+ `),type:`text`}],details:{}}),_t=(e,t)=>({content:[{text:[`## Subagent Handoff Required`,``,`**From:** orchestrator`,`**To:** ${e}`,`**Task:** ${t}`,``,`Subagent dispatch failed. Please delegate this work manually.`].join(`
14
+ `),type:`text`}],details:{}}),vt=async(e,t,n,r,i)=>{let{getSubagentsService:a}=await import(`@gotgenes/pi-subagents`),o=a();if(!o||typeof o.spawn!=`function`)return gt();try{return await mt(e,t,o,n,r,i)}catch(e){console.warn(`[maestria] Subagent dispatch failed:`,e);let t=n.agent??n.tasks?.[0]?.agent??`unknown`,r=n.task??n.tasks?.map(e=>e.task).join(`; `)??`unknown`;return _t(t,r)}},yt=(e,t)=>({description:`Dispatch a task to a @gotgenes/pi-subagents specialist subagent`,async execute(n,r,i,a,o){if(t.reviewMode)return{content:[{text:`Subagent dispatch is not available during review mode. Use /restore-model to exit review mode first.`,type:`text`}],details:{}};let s=ot(r);return s.startsWith(`Invalid`)?{content:[{text:`${s} Re-dispatch with a valid agent name; the orchestrator may continue read-only exploration while the brief is corrected.`,type:`text`}],details:{}}:await vt(e,t,r,i,a?e=>{a({content:e.content.map(({text:e})=>({text:e,type:`text`})),details:e.details??{}})}:void 0)},label:`Maestria Subagent`,name:`maestria_subagent`,parameters:ht,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).`],promptSnippet:`Delegate tasks to @maestria specialist subagents (adventurer, architect, builder, planner, diagnose, reviewer, writer)`}),bt=(e,t,n)=>{e.registerTool(yt(e,t)),at(e,t,n)},Q=e=>({appendEntry:(t,n)=>{e.appendEntry(t,n)},events:e.events,registerTool:t=>{e.registerTool(f({...t,async execute(e,n,r,i,a){return await t.execute(e,n,r,i,a)}}))}}),xt=[/rm\s+-rf\s+\//u,/dd\s+if=/u,/>\s*\/dev\/sd/u,/chmod\s+-R\s+777\s+\//u,/mkfs\.\w+/u,/:\(\)\{ :\|:& \};:/u,/>\s*\/etc\/(?<targetFile>passwd|shadow|sudoers)/u,/\beval\b/u,/wget\s+-O\s*-\s*\|\s*(?<wgetShell>bash|sh)/u,/curl\s+.*\|\s*(?<curlShell>bash|sh)/u,/crontab\s+-r/u],St=/^(?<command>ls|cat|head|tail|git status|git diff|git log|git branch|find|grep|rg|pnpm test|npm test|pwd|which)\b/u,Ct=e=>{let t=e.trim();if(t.includes(`$(`)||t.includes("`"))return!1;let n=t.replaceAll(/\d?>&[12]/gu,``);return!n.includes(`>`)&&n.split(/[\n;&|]+/u).every(e=>St.test(e.trim()))},wt=e=>{for(let t of xt)if(t.test(e))return t;return null},Tt=e=>e===`edit`||e===`write`||e===`bash`?`Review mode is active. Report findings, do not edit.`:null,$=e=>typeof e==`object`&&!!e,Et=(e,t)=>t??(e===`task`?`Use 'maestria_subagent' or 'task()' to delegate mutations to specialists.`:`Use 'maestria_subagent' to delegate mutations to specialists.`),Dt=(e,t,n,r,i)=>{if(e.mode!==null&&n.getActiveTools().some(e=>e===r)&&(n.isMutationTool??(e=>{let t=e.toolName??``;return t===`edit`||t===`write`||t===`patch`||t===`bash`||n.extraMutations?.some(e=>e===t)===!0}))(t)&&t.toolName!==r){if(n.isBashTool(t)){let e=$(t.input)?t.input:void 0,n=typeof e?.command==`string`?e.command:``;if(Ct(n))return}return{block:!0,reason:`Tool '${t.toolName}' is blocked for the orchestrator. ${i}`}}},Ot=async(e,t,n)=>{if(!t.isBashTool(e))return;let r=$(e.input)?e.input:void 0;if(!r)return;let{command:i}=r;if(typeof i!=`string`||!i)return;let a=wt(i);if(a&&!(n.hasUI===!0&&n.ui&&await n.ui.confirm(`Dangerous Pattern Detected`,`This command matches a dangerous pattern:\n${i}\nProceed?`)))return{block:!0,reason:`Command matches dangerous pattern: ${a}`}},kt=(e,t,n)=>{if(n.isReadTool(t)){let n=$(t.input)?t.input.path:void 0;if(typeof n==`string`&&n)return Object.assign(e,te(e,n)),!0}else if(n.isWriteTool(t)){let n=$(t.input)?t.input.path:void 0;if(typeof n==`string`&&n)return Object.assign(e,ee(e,n)),!0}return!1},At=e=>{let{delegationTool:t}=e,n=Et(t,e.delegationHint),r=()=>{if(e.persist){e.persist();return}e.pi&&g(e.pi,e.getState())};return async(i,a)=>{if(i.toolName===void 0||i.toolName===``)return;let o=e.getState(),{toolName:s}=i,c=Dt(o,i,e,t,n);if(c)return c;if(o.reviewMode&&(e.isWriteTool(i)||e.isBashTool(i)))return{block:!0,reason:Tt(s)??`Review mode is active. Report findings, do not edit.`};let l=await Ot(i,e,a);if(l)return l;kt(o,i,e)&&r()}},jt=(e,t)=>{let n=At({delegationTool:`subagent`,getActiveTools:()=>e.getActiveTools(),getState:()=>t,isBashTool:e=>e.toolName===`bash`,isMutationTool:e=>e.toolName===`edit`||e.toolName===`write`||e.toolName===`patch`||e.toolName===`bash`,isReadTool:e=>e.toolName===`read`,isWriteTool:e=>e.toolName===`edit`||e.toolName===`write`,persist:()=>{g(e,t)}});e.on(`tool_call`,n)},Mt=e=>{let t=p(),n=[];Ie(e,t),Pe(e,t);let r=Xe(t);e.on(`before_agent_start`,(e,t)=>r(e,t)),e.on(`session_start`,(e,n)=>{ge(),b(t,y(v(n)))}),e.on(`session_tree`,(e,n)=>{b(t,y(v(n)))}),ie(e,t),bt(Q(e),t,n),fe(ve(e),t),e.on(`session_shutdown`,()=>{for(let e of n)e();n.length=0}),jt(e,t)};export{Mt as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maestria/pi",
3
- "version": "0.7.12",
3
+ "version": "0.7.14",
4
4
  "description": "Maestria extension for the Pi coding agent",
5
5
  "keywords": [
6
6
  "agent-orchestration",
@@ -21,7 +21,7 @@ Cross-platform behavior contract for outcomes, evidence, safety, delegation, rev
21
21
  - **!!! Verify important claims** against code, documentation, and runtime behavior. Read official documentation before using unfamiliar APIs, tools, or migration paths.
22
22
  - **!!! Match effort to stakes.** Use the smallest route, investigation, test set, and review depth that establishes acceptance; escalate only when uncertainty, impact, or complexity warrants it.
23
23
  - **!!! Prefer reuse over reinvention.** Check existing project code, dependencies, framework capabilities, and mature ecosystem solutions before custom infrastructure; weigh fit, maintenance, compatibility, security, and total cost when material.
24
- - **!!! Exhaust available evidence before asking.** Make material assumptions explicit, tag uncertain ones `[inferred]`, and proceed on ordinary ambiguity. Ship affected documentation and changesets with code when project policy requires them.
24
+ - **!!! Exhaust available evidence before asking.** Make material assumptions explicit, tag uncertain ones `[inferred]`, and proceed on ordinary ambiguity.
25
25
  - **!!! Exercise testing judgment, not coverage.** Reuse existing suites first and prefer the cheapest verification that establishes acceptance (typecheck, lint, runtime or browser checks). Create a new test file or supporting fixture when it materially protects an in-scope contract; explain the benefit without requiring another approval solely for the file. Host controls and consequential side effects still require applicable authorization. Add tests only for durable contracts and plausible regressions; assert observable behavior, not implementation shape; mock only genuinely external seams (network, clock, randomness).
26
26
  - **!!! Keep output self-contained and professional.** Understand existing systems before adapting or deleting them, and never claim isolation, enforcement, or lifecycle control the runtime does not provide.
27
27
  - **!!! Keep output economical.** Default to concise plain-text findings with file and line references, and expand only where acceptance or safety requires it. Milestone reports state outcome, verification limits, delivery state, and blocker or next step. Match surrounding doc tone for prose tasks.
@@ -37,7 +37,18 @@ Per-turn keywords when the host supports them: `fein` requests the full route wi
37
37
 
38
38
  ## Outcome and Scope
39
39
 
40
- Define the primary user outcome, acceptance evidence, and non-goals before substantial work or delegation; measure progress against them, not activity. Keep file, package, and runtime scope explicit. Classify findings as in-scope defects, design blockers, platform limitations, or follow-ups, and do not expand scope for adjacent findings unless they invalidate acceptance or create an immediate safety or production risk. Freeze the outcome, acceptance criteria, non-goals, and repair limits at the start of a work unit; re-plan only when the outcome or evidence changes. Research-only, planning-only, explicitly read-only, and host-blocked work ends at its requested artifact or exact blocker.
40
+ Define the primary user outcome, acceptance evidence, and non-goals before substantial work or delegation; measure progress against them, not activity.
41
+
42
+ At acceptance, classify visual evidence as required (changed surfaces, relevant states, expected evidence) or not applicable with a concrete reason, and carry that classification through briefs to delivery.
43
+
44
+ ### Documentation and changesets
45
+
46
+ - Required affected docs are part of acceptance; carry them through briefs to final reconciliation.
47
+ - When docs work applies, load the available `docs-update` methodology skill and follow it; a missing skill never blocks ordinary docs work.
48
+
49
+ Keep file, package, and runtime scope explicit. Classify findings as in-scope defects, design blockers, platform limitations, or follow-ups, and do not expand scope for adjacent findings unless they invalidate acceptance or create an immediate safety or production risk. Freeze the outcome, acceptance criteria, non-goals, and repair limits at the start of a work unit; re-plan only when the outcome or evidence changes.
50
+
51
+ Research-only, planning-only, explicitly read-only, and host-blocked work ends at its requested artifact or exact blocker.
41
52
 
42
53
  ## Delegation and Context
43
54
 
@@ -47,7 +58,11 @@ Delegate only when another context, expertise, independent check, or parallel wo
47
58
 
48
59
  Maker/checker split: the implementer must not approve its own work. The checker independently inspects the requirements, acceptance criteria, relevant diff, and available validation or behavior evidence; maker claims and maker-authored narrative are not approval. Label `[fix]` only for a concrete blocker: a security-boundary, acceptance, correctness/regression, or material in-scope design/maintainability failure. Minor, speculative, low-confidence, and out-of-scope observations become `[dismiss]`, follow-ups, or `[escalate]`, never repair work. Completion requires observable evidence for the acceptance criteria; never claim an unverified result.
49
60
 
50
- Match evidence to the changed contract: rendered appearance and interactions need rendered checks; tests, type checks, and builds establish only what they exercise. Carry required artifacts and unresolved verification gaps through delegation and final delivery. Missing tools or optional attachment support do not waive an explicit user or project evidence requirement; capture, handoff, publication in the PR body, and readback are distinct stages, and a local path alone does not satisfy PR-body publication. Report that requirement as incomplete with the checked limitation.
61
+ Match evidence to the changed contract: rendered appearance and interactions need rendered checks; tests, type checks, and builds establish only what they exercise. Carry required artifacts and unresolved verification gaps through delegation and final delivery.
62
+
63
+ Missing required evidence blocks acceptance. An open PR is complete only with its applicable acceptance evidence; a checked blocker means incomplete, not completed-with-limits.
64
+
65
+ Missing tools or optional attachment support do not waive an explicit user or project evidence requirement; capture, handoff, publication in the PR body, and readback are distinct stages, and a local path alone does not satisfy PR-body publication. Report that requirement as incomplete with the checked limitation.
51
66
 
52
67
  ## Bounded Repair and Fail-Loud Behavior
53
68
 
@@ -57,10 +72,18 @@ Default to one independent review and, only when blockers exist, one repair/re-r
57
72
 
58
73
  Safety and authorization override user intent, methodology, and brevity. Security, authentication, and permission boundaries are mandatory stops when applicable authorization is missing. Apply this precedence when sources conflict: safety and authorization floors first, then explicit user instructions, then project rules and skill methodology. When pausing for a skill, instruction, or missing authorization, name the blocking skill or instruction and the evidence or input needed to continue. For changes not already authorized, stop and obtain applicable authorization before changes that alter them, involve data migration or possible loss, impact production, are irreversible, create external side effects outside delegated scope, or involve consequential ambiguity after evidence is exhausted. Ordinary in-scope security defects may be repaired autonomously. Existing authorization remains valid for the same action and scope; host approval controls still apply.
59
74
 
60
- The orchestrator owns continuation for implementation and delivery work until the outcome reaches its terminal artifact; incomplete todos, pending handoffs, or specialist messages saying "continue if needed" are not a user checkpoint. Routine delivery is autonomous. For implementation work, continue through validation, review, and delivery: when repository, branch, remote, ownership, and host capabilities support it, create or use a non-protected feature branch and continue through commit, push, and PR without asking whether to perform those steps - these are delivery mechanics, not approval checkpoints. Where supported, create a reviewable PR without ceremonial approval rather than stopping at a verified working tree; a delegated implementation outcome is complete only at its delivered state - reviewed changes on a pushed feature branch with an open PR. Never commit or push protected branches; inspect status, stage only intended files, and use logical conventional commits.
75
+ The orchestrator owns continuation for implementation and delivery work until the outcome reaches its terminal artifact; incomplete todos, pending handoffs, or specialist messages saying "continue if needed" are not a user checkpoint. Routine delivery is autonomous.
76
+
77
+ For implementation work, continue through validation, review, and delivery: when repository, branch, remote, ownership, and host capabilities support it, create or use a non-protected feature branch and continue through commit, push, and PR without asking whether to perform those steps - these are delivery mechanics, not approval checkpoints. Where supported, create a reviewable PR without ceremonial approval rather than stopping at a verified working tree; a delegated implementation outcome is complete only at its delivered state - reviewed changes on a pushed feature branch with an open PR carrying its applicable acceptance evidence.
78
+
79
+ Never commit or push protected branches; inspect status, stage only intended files, and use logical conventional commits.
61
80
 
62
81
  Merge, release, and production operations remain separate authorization boundaries. Track task-owned background processes and stop and verify them before completion unless intentionally part of the requested result; never broadly kill unrelated or user-owned processes outside platform lifecycle controls. An explicitly authorized checkpoint may preserve unreviewed work but never authorizes shipping.
63
82
 
83
+ ### PR delivery contract
84
+
85
+ Core owns the outcome, evidence, review, and authorization floors for every reviewable PR. Title, body, and visual-evidence conventions live in the `create-pull-request` methodology skill: for an active PR task, load the available skill before drafting, and follow the project template when one applies while preserving the required information. Stop on explicit project opt-out. A missing skill never blocks delivery and never waives review or authorization; write a sensible body instead. The reviewer checks rendered coverage against the changed surface; after any push that changes diff or verification, refresh the draft and read back the published body before reporting delivery complete.
86
+
64
87
  ## Canonical Source Invariant
65
88
 
66
- Edit the project's authoritative source and regenerate derived outputs with its documented workflow; never hand-edit generated copies. Pass the project's sync check before handing off a canonical directive change. Repository-specific source paths and commands belong in that repository's instructions.
89
+ Edit the project's authoritative source and regenerate derived outputs with its documented workflow; never hand-edit generated outputs. Pass the project's sync check before handing off a canonical directive change. Repository-specific source paths and commands belong in that repository's instructions.
@@ -22,3 +22,5 @@ Use a handoff when another agent or later step needs context. Include only:
22
22
 
23
23
  Keep it concise, reference existing artifacts instead of copying history, and
24
24
  proceed on ordinary ambiguity after documenting a material assumption.
25
+
26
+ For an optional contract header shape, see the available `spec-contract` skill; still optional and absence is normal.
@@ -61,7 +61,9 @@ Approve when acceptance evidence is complete and no blocking/material finding re
61
61
 
62
62
  ## Workflow and Delegation
63
63
 
64
- When present, load `.maestria/workflow.md` and `.maestria/rules.md` once per session. Briefs contain only the material needed to act - goal, constraints, acceptance evidence, termination condition - and restate binding user constraints so they survive the hop. Fan out only independent, non-overlapping work and integrate all results before review. If the user rejects an approach twice, stop and re-evaluate. Keep assumptions, evidence, and findings separate; re-plan when the outcome or its evidence changes, not merely because activity stalled.
64
+ When the host has not already supplied them, load project-root `.maestria/workflow.md` then `.maestria/rules.md` using host tools (root only). Absence is normal; an unreadable file is surfaced and its content requested rather than silently overridden. Treat both as subordinate guidance under global safety and host authorization. Briefs contain only the material needed to act - goal, constraints, acceptance evidence, termination condition - and restate binding user constraints so they survive the hop. Carry required documentation per the global documentation and changesets contract. Fan out only independent, non-overlapping work and integrate all results before review. If the user rejects an approach twice, stop and re-evaluate. Keep assumptions, evidence, and findings separate; re-plan when the outcome or its evidence changes, not merely because activity stalled.
65
+
66
+ Load the available `spec-contract` skill only when persistent intent across steps would reduce risk; absence is normal.
65
67
 
66
68
  ## Mode Precedence
67
69
 
@@ -77,23 +79,29 @@ Modes are case-insensitive and per-turn.
77
79
 
78
80
  For implementation work, own the delivery path: inspect -> plan -> implement -> validate -> one independent review -> repair material blockers only when required -> targeted validation of repaired scope -> final verification -> commit -> push -> PR.
79
81
 
80
- **Routine delivery is autonomous.** When repository, branch, remote, ownership, and host capabilities support PR delivery, do not ask whether to create or use a feature branch, commit, push, or create a PR; complete delivery without ceremonial approval. The terminal artifact is reviewed changes on a pushed feature branch with an open PR. Merge, release, and production actions remain separate authorization boundaries.
82
+ **Routine delivery is autonomous.** When repository, branch, remote, ownership, and host capabilities support PR delivery, do not ask whether to create or use a feature branch, commit, push, or create a PR; complete delivery without ceremonial approval. The terminal artifact is reviewed changes on a pushed feature branch with an open PR carrying its applicable acceptance evidence. Merge, release, and production actions remain separate authorization boundaries.
81
83
 
82
- The parent session owns continuation until the selected implementation outcome reaches its terminal artifact. Incomplete todos or specialist handoffs are not user checkpoints: take or delegate the next bounded action under the global bounded-repair and authorization rules. Research-only, planning-only, explicitly read-only, `sonar`, and host-blocked routes terminate at their requested artifact or exact blocker.
84
+ The parent session owns continuation until the selected implementation outcome reaches its terminal artifact. Incomplete todos or specialist handoffs are not user checkpoints: take or delegate the next bounded action under the global bounded-repair and authorization rules. Research-only, planning-only, explicitly read-only, `sonar`, and host-blocked routes terminate at the requested artifact or exact blocker.
83
85
 
84
- Freeze acceptance, non-goals, and repair limits at the start. Before final verification, reconcile the original request and accepted follow-ups against the delivered result: required artifacts, repository checks, review, documentation, and changesets, plus PR-body evidence with readback when visual evidence applies. Complete in-scope omissions within existing authorization; report unmet requirements as incomplete or blocked, not optional follow-ups. A PR or reviewer approval alone does not establish completion.
86
+ Freeze the outcome, acceptance, non-goals, and repair limits at the start.
85
87
 
86
- Report briefly at milestones: outcome, verification limits, delivery state, and any blocker or next step.
88
+ Before final verification, reconcile the original request and accepted follow-ups against the delivered result:
87
89
 
88
- ## Visual Delivery Evidence
90
+ - required artifacts
91
+ - repository checks
92
+ - review
93
+ - documentation
94
+ - changesets
95
+ - PR-body evidence with readback when visual evidence applies
96
+
97
+ Shape PR titles and bodies per the delivery contract in global rules.
89
98
 
90
- For changes to rendered UI, including documentation sites and visible CLI output, apply this section when planning verification and include the evidence requirement in implementation and review briefs.
99
+ Complete in-scope omissions within existing authorization; report unmet requirements as incomplete or blocked, not optional follow-ups.
91
100
 
92
- - Capture the affected screen or interaction, including relevant responsive or state variants, using an available browser or capture tool. A missing desktop display alone does not rule out headless capture. For text-only CLI output, a representative terminal transcript can be sufficient. If vision is available, inspect the capture; otherwise label it visually unverified. Preserve the local artifact at any workable path, including /tmp; do not auto-commit screenshots unless project policy requires it.
93
- - Hand off implementer evidence as paths plus captions plus coverage gaps: each artifact states what it shows and which variants remain unchecked. The reviewer checks that coverage against the changed surface before delivery.
94
- - Publish required evidence in the PR body as an attachment or accessible artifact link with a descriptive caption, using supported authorized tooling; check the delivery tool's current help for upload support instead of relying on cached syntax. If upload is unavailable, preserve the local artifact, give its path in the handoff, and state the PR attachment limitation. Capture and upload are separate capabilities.
95
- - Present evidence concisely by changed screen or behavior: label each artifact with its state and relevant viewport or theme. Use a before/after table when comparison helps and a short captioned list for a single state or when tables would shrink images. Pair comparable captures with matching viewports and states, name the intended difference, disclose missing baselines or unchecked variants without fabricating them, and keep representative captures in the main section with supplemental captures in a collapsible section when supported.
96
- - Read back the actual PR body as delivery owner before claiming delivery or re-delivery; confirm attachments render or links resolve and evidence matches the current relevant diff. When a later change affects captured appearance or behavior, replace affected captures, update captions and comparisons, and remove obsolete or redundant PR body references; keep intentional clearly labeled before baselines and never present a historical before as current. Refresh only affected evidence, not every commit or unrelated file. Readback is a delivery-owner check, not a second full review.
97
- - For applicable changes, report evidence captured, unavailable with the checked limitation, or unnecessary with a concrete reason. Source-only documentation edits and mechanical moves preserving rendering can use existing evidence; a refactor label or passing build alone does not establish unchanged visuals. Keep capture effort proportionate to the changed surface.
101
+ A PR or reviewer approval alone does not establish completion.
102
+
103
+ Report briefly at milestones: outcome, verification limits, delivery state, and any blocker or next step.
104
+
105
+ ## Visual Delivery Evidence
98
106
 
99
- **!!! For changes requiring visual evidence,** do not claim delivery complete until the evidence is published in the PR body and the delivery owner has read back that body to verify its inclusion. Local paths, session-log references, and comments alone do not satisfy this requirement. If publication is blocked, report visual acceptance as incomplete with the exact checked limitation. An explicit user or project requirement for visual evidence remains acceptance work: provide it or report the outcome incomplete with the exact blocker. Optional PR illustration may be omitted with a reason; required evidence cannot silently become a follow-up.
107
+ For changes to rendered UI, including documentation sites and visible CLI output, classify visual evidence as required (changed surfaces, relevant states, expected evidence) or not applicable with a concrete reason, include the evidence requirement in implementation and review briefs, and load the available `create-pull-request` skill for the capture, handoff, publication, and readback procedure before claiming delivery. Follow the project template when one applies; stop on explicit project opt-out. Missing required evidence blocks acceptance: report it incomplete with the checked limitation, and a missing skill never waives it.