@maestria/pi 0.6.5 → 0.6.7

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/agents/builder.md CHANGED
@@ -32,30 +32,9 @@ If the task is not atomic - if it spans multiple unrelated concerns - document t
32
32
  3. **Verify** - Run tests or type checks to confirm correctness
33
33
  4. **Report** - State what changed and why
34
34
 
35
- ## Implementation Patterns
35
+ ## Implementation Judgment
36
36
 
37
- ### Implementation Staircase
38
-
39
- For complex features, build incrementally:
40
-
41
- 1. Hardcoded version that demonstrates the concept
42
- 2. Add state management with mock data
43
- 3. Connect to real data/API
44
- 4. Add error handling and loading states
45
- 5. Optimize and polish
46
-
47
- Each step is verifiable before moving to the next.
48
-
49
- ### Constraint Escalation
50
-
51
- Start with tight constraints, relax as needed:
52
-
53
- - Round 0: "Check if the problem is already solved - is there a well-maintained open-source library or existing dependency that handles this?"
54
- - Round 1: "Solve this with existing dependencies only"
55
- - Round 2: "Now you can use standard library features"
56
- - Round 3: "Add external dependencies if necessary"
57
-
58
- This reveals what actually requires heavy tools vs. what's simple.
37
+ Start with the smallest change that satisfies acceptance. Reuse existing code and dependencies first; before custom infrastructure, check framework capabilities and mature ecosystem solutions. Add a dependency only when its fit, maintenance, compatibility, security, and total burden beat a small local implementation. Add layers only when the product requires them.
59
38
 
60
39
  ## Skill Prescription
61
40
 
@@ -115,6 +115,8 @@ When the orchestrator dispatches a general review plus risk-matched specialist l
115
115
  - **!!! Flag collateral deletions** in the diff.
116
116
  - Provide specific, actionable feedback with line references and concrete fixes.
117
117
  - Classify issues as critical / major / minor / suggestion.
118
+ - Review against the acceptance bar, not idealized code. Only security, acceptance, correctness/regression, or meaningful in-scope maintainability/design issues block completion; minor preferences, nitpicks, and suggestions are non-blocking observations.
119
+ - When acceptance evidence is complete and no material blocker remains, approve and stop. Do not create another review pass merely to find additional polish.
118
120
  - If you cannot reproduce an issue, say so.
119
121
  - If no issues are found, say so and state what you verified.
120
122
  - If scope is unclear: document assumption from diff context and proceed.
@@ -1,14 +1,14 @@
1
- import{homedir as e}from"node:os";import{dirname as t,join as n,resolve as r}from"node:path";import{fileURLToPath as i}from"node:url";import{existsSync as a,mkdirSync as o,readFileSync as s,writeFileSync as c}from"node:fs";import{Type as l}from"typebox";import{SUBAGENT_EVENTS as u}from"@gotgenes/pi-subagents";import{isToolCallEventType as d}from"@earendil-works/pi-coding-agent";function f(){return{mode:null,activeTask:``,completionPromise:``,specialistsDelegated:[],blockers:[],filesModified:[],filesRead:[],handoffHistory:[],reviewMode:!1,originalModel:null,originalTools:null,subagentStatus:{},reviewModel:null,nativeGoal:null}}function p(e,t,n){return[t,...e.filter(e=>e!==t)].slice(0,n)}function m(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 h(e,t){return{...e,filesModified:p(e.filesModified,t,10)}}function ee(e,t){return{...e,filesRead:p(e.filesRead,t,10)}}function g(e,t){return e.specialistsDelegated.includes(t)?e:{...e,specialistsDelegated:[...e.specialistsDelegated,t]}}function _(e){return{state:{...e,reviewMode:!1,originalModel:null,originalTools:null},originalModel:e.originalModel,originalTools:e.originalTools}}function v(e,t){e.appendEntry(`maestria_state`,{...t})}function y(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.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{homedir as e}from"node:os";import{dirname as t,join as n,resolve as r}from"node:path";import{fileURLToPath as i}from"node:url";import{existsSync as a,mkdirSync as o,readFileSync as s,writeFileSync as c}from"node:fs";import{Type as l}from"typebox";import{Data as u,Effect as d}from"effect";import{SUBAGENT_EVENTS as f}from"@gotgenes/pi-subagents";import{isToolCallEventType as p}from"@earendil-works/pi-coding-agent";function m(){return{mode:null,activeTask:``,completionPromise:``,specialistsDelegated:[],blockers:[],filesModified:[],filesRead:[],handoffHistory:[],reviewMode:!1,originalModel:null,originalTools:null,subagentStatus:{},reviewModel:null,nativeGoal:null}}function h(e,t,n){return[t,...e.filter(e=>e!==t)].slice(0,n)}function g(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 ee(e,t){return{...e,filesModified:h(e.filesModified,t,10)}}function _(e,t){return{...e,filesRead:h(e.filesRead,t,10)}}function te(e,t){return e.specialistsDelegated.includes(t)?e:{...e,specialistsDelegated:[...e.specialistsDelegated,t]}}function ne(e){return{state:{...e,reviewMode:!1,originalModel:null,originalTools:null},originalModel:e.originalModel,originalTools:e.originalTools}}function v(e,t){e.appendEntry(`maestria_state`,{...t})}function y(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.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
- `)}async function b(e,t,n){let{state:r,originalModel:i,originalTools:a}=_(n);if(a&&a.length>0&&e.setActiveTools(a),i)try{let n=t.modelRegistry.getAll().find(e=>e.id===i);n&&await e.setModel(n)}catch{}Object.assign(n,r)}async function x(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}}const S={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`},C=[`adventurer`,`architect`,`builder`,`diagnose`,`planner`,`reviewer`,`writer`];function w(e){if(!C.includes(e))throw Error(`Unknown agent: "${e}". Allowed: ${C.join(`, `)}`)}function T(e,t){if(!e||!e.trim())throw Error(t)}function E(e,t){if(!a(e))return console.warn(`[maestria] Agents source directory not found:`,e),0;try{o(t,{recursive:!0})}catch{return console.warn(`[maestria] Could not create agents directory:`,t),0}let r=0;for(let i of C){let o=n(e,`${i}.md`),l=n(t,`${i}.md`);if(!a(o)){console.warn(`[maestria] Agent source not found: ${i}.md`);continue}if(!a(l))try{c(l,s(o,`utf-8`),`utf-8`),r++}catch(e){console.warn(`[maestria] Failed to deploy agent ${i}:`,e)}}return r>0&&console.log(`[maestria] Deployed ${r} specialist agents to ${t}`),r}const D=n(t(i(import.meta.url)),`..`,`agents`);function te(t){E(D,n(e(),`.pi`,`agent`,`agents`))}const O=[`fein`,`sonar`,`blitz`],k={fein:`[MODE: fein]`,sonar:`[MODE: sonar]`,blitz:`[MODE: blitz]`},A={};function j(e,t){let n=s(r(t,`${e}.md`),`utf-8`),i=n.indexOf(`## MODE:`);return i===-1?n.replace(/\s+$/,``)+`
3
+ `)}async function b(e,t,n){let{state:r,originalModel:i,originalTools:a}=ne(n);if(a&&a.length>0&&e.setActiveTools(a),i)try{let n=t.modelRegistry.getAll().find(e=>e.id===i);n&&await e.setModel(n)}catch{}Object.assign(n,r)}async function x(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}}const S={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`},C=[`adventurer`,`architect`,`builder`,`diagnose`,`planner`,`reviewer`,`writer`];function w(e){if(!C.includes(e))throw Error(`Unknown agent: "${e}". Allowed: ${C.join(`, `)}`)}function T(e,t){if(!e||!e.trim())throw Error(t)}function E(e,t){if(!a(e))return console.warn(`[maestria] Agents source directory not found:`,e),0;try{o(t,{recursive:!0})}catch{return console.warn(`[maestria] Could not create agents directory:`,t),0}let r=0;for(let i of C){let o=n(e,`${i}.md`),l=n(t,`${i}.md`);if(!a(o)){console.warn(`[maestria] Agent source not found: ${i}.md`);continue}if(!a(l))try{c(l,s(o,`utf-8`),`utf-8`),r++}catch(e){console.warn(`[maestria] Failed to deploy agent ${i}:`,e)}}return r>0&&console.log(`[maestria] Deployed ${r} specialist agents to ${t}`),r}const re=n(t(i(import.meta.url)),`..`,`agents`);function ie(t){E(re,n(e(),`.pi`,`agent`,`agents`))}const D=[`fein`,`sonar`,`blitz`],ae={fein:`[MODE: fein]`,sonar:`[MODE: sonar]`,blitz:`[MODE: blitz]`},O={};function oe(e,t){let n=s(r(t,`${e}.md`),`utf-8`),i=n.indexOf(`## MODE:`);return i===-1?n.replace(/\s+$/,``)+`
4
4
  `:n.slice(i).replace(/\s+$/,``)+`
5
- `}function M(e,t){if(!(e in A))try{A[e]=j(e,t)}catch(t){console.warn(`[maestria] Failed to load mode prompt "${e}":`,t),A[e]=``}return`${k[e]}\n\n${A[e]}`}const N=/```[\s\S]*?```|`[^`]*`/g;function P(e){let t=[],n;for(;(n=N.exec(e))!==null;)t.push([n.index,n.index+n[0].length]);return t}function F(e,t){return t.some(([t,n])=>e>=t&&e<n)}const I={fein:3,sonar:2,blitz:1};function L(e,t){if(!e)return null;let n=P(e),r=null;for(let t of O){let i=RegExp(`\\b${t}\\b`,`gi`),a;for(;(a=i.exec(e))!==null;)F(a.index,n)||(r===null||I[t]>I[r.keyword])&&(r={keyword:t,index:a.index})}if(r===null)return null;let i=(e.slice(0,r.index)+e.slice(r.index+r.keyword.length).replace(/^:\s*/,``)).replace(/ {2,}/g,` `).trim();return{keyword:r.keyword,strippedText:i,prompt:M(r.keyword,t)}}function R(e,t){return t?`${e}\n\n${t}`:e}function z(e,t,n,r){e(async(e,i)=>{let a=L(e.text??``,n);return a?(t.reviewMode&&await r.restoreOriginalState(i),t.mode=a.keyword,r.persistState(),r.transform(R(a.prompt,a.strippedText))):r.noMatch})}function B(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 O)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.`)}})}const V=r(t(i(import.meta.url)),`../agents/commands`);function H(e,t){z(t=>e.on(`input`,t),t,V,{restoreOriginalState:n=>b(e,n,t),persistState:()=>v(e,t),noMatch:{action:`continue`},transform:e=>({action:`transform`,text:e})})}function U(e,t){B((t,n)=>e.registerCommand(t,n),t,{restoreOriginalState:n=>b(e,n,t),persistState:()=>v(e,t)})}const W=r(t(i(import.meta.url)),`../agents/commands`);function ne(e){return(t,n)=>{if(e.mode)return{systemPrompt:[t.systemPrompt,``,M(e.mode,W),``,`The user has set workflow mode to "${e.mode}". Honor this mode throughout the session until changed via /command.`].join(`
6
- `)}}}function G(e,t){e.on(`session_before_compact`,e=>{let n=e.preparation;return{compaction:{summary:y(t),details:{...t},firstKeptEntryId:n?.firstKeptEntryId,tokensBefore:n?.tokensBefore}}}),e.on(`session_before_tree`,e=>{if(e.preparation?.userWantsSummary)return{summary:{summary:y(t)}}})}function K(e,t){G({on:(t,n)=>{e.on(t,n)}},t)}const q=C,J=new Set([`completed`,`steered`,`aborted`,`stopped`,`error`]);async function Y(e,t,n,r,i,a){let o=0,s=r.getRecord(e);for(;s&&!J.has(s.status)&&o<360;){if(i?.aborted)throw Error(`Maestria subagent call aborted`);await new Promise(e=>setTimeout(e,500)),s=r.getRecord(e),o++,n&&a?.({content:[{type:`text`,text:`${t} running... (${Math.round(o*500/1e3)}s)`}]})}if(s&&!J.has(s.status))throw Error(`Subagent ${e} timed out after 180000ms`);if(!s)throw Error(`Subagent ${e} was cleaned up before completion`);return s}function X(e,t,n,r){let i=g(m(t,`orchestrator`,n,r),n);Object.assign(t,i),v(e,t)}function Z(e,t,n){if(e.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:l.Object({agent:l.String({description:`Specialist agent name (required): adventurer, architect, builder, diagnose, planner, reviewer, writer`}),task:l.String({description:`Task description for the subagent (required)`}),tasks:l.Optional(l.Array(l.Object({agent:l.String(),task:l.String()}),{description:`Array of task objects for parallel or chain dispatch`})),mode:l.Optional(l.Union([l.Literal(`parallel`),l.Literal(`chain`),l.Literal(`single`)]))}),async execute(n,r,i,a,o){if(t.reviewMode)return{content:[{type:`text`,text:`Subagent dispatch is not available during review mode. Use /restore-model to exit review mode first.`}]};let s=r.mode??`single`;if(s===`single`){if(!r.agent||!q.includes(r.agent))return{content:[{type:`text`,text:`Invalid maestria_subagent call: 'agent' is required and must be one of ${q.join(`, `)}. Re-dispatch with a valid agent name; the orchestrator may continue read-only exploration while the brief is corrected.`}]};T(r.task,`Task description is required`)}else if(s===`parallel`){if(!r.tasks||r.tasks.length<2)throw Error(`For parallel mode, tasks array is required with at least 2 items`);if(r.tasks.length>8)throw Error(`For parallel mode, tasks array may have at most 8 items (got ${r.tasks.length})`);for(let e of r.tasks)w(e.agent),T(e.task,`Task description is required for all tasks`)}else if(s===`chain`){if(!r.tasks||r.tasks.length<2)throw Error(`For chain mode, tasks array is required with at least 2 items`);for(let e of r.tasks)w(e.agent),T(e.task,`Task description is required for all tasks`)}let{getSubagentsService:c}=await import(`@gotgenes/pi-subagents`),l=c();if(!l||typeof l.spawn!=`function`)return{content:[{type:`text`,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(`
7
- `)}]};try{if(s===`single`){let n=r.agent,o=r.task,s=l.spawn(n,o,{description:o.slice(0,80),foreground:!0,inheritContext:!0});X(e,t,n,o);let c=await Y(s,`Subagent ${n}`,!0,l,i,a);return{content:[{type:`text`,text:c.result??c.error??`No output.`}],details:{subagentId:s}}}if(s===`parallel`){let n=r.tasks;a?.({content:[{type:`text`,text:`Spawning ${n.length} parallel subagents...`}]});let o=[];for(let r of n){let n=l.spawn(r.agent,r.task,{description:r.task.slice(0,80),foreground:!0,inheritContext:!0});o.push(n),X(e,t,r.agent,r.task)}let s=await Promise.all(o.map((e,t)=>Y(e,`${n[t].agent} (${t+1}/${n.length})`,!1,l,i,a)));a?.({content:[{type:`text`,text:`All ${n.length} parallel subagents completed.`}]});let c=[`## Parallel Results (${n.length} tasks)\n`];for(let e=0;e<n.length;e++){let t=n[e],r=s[e],i=r.result??r.error??`No output.`;c.push(`### ${e+1}: ${t.agent}`),c.push(i)}return{content:[{type:`text`,text:c.join(`
5
+ `}function k(e,t){if(!(e in O))try{O[e]=oe(e,t)}catch(t){console.warn(`[maestria] Failed to load mode prompt "${e}":`,t),O[e]=``}return`${ae[e]}\n\n${O[e]}`}const se=/```[\s\S]*?```|`[^`]*`/g;function A(e){let t=[],n;for(;(n=se.exec(e))!==null;)t.push([n.index,n.index+n[0].length]);return t}function j(e,t){return t.some(([t,n])=>e>=t&&e<n)}const M={fein:3,sonar:2,blitz:1};function N(e,t){if(!e)return null;let n=A(e),r=null;for(let t of D){let i=RegExp(`\\b${t}\\b`,`gi`),a;for(;(a=i.exec(e))!==null;)j(a.index,n)||(r===null||M[t]>M[r.keyword])&&(r={keyword:t,index:a.index})}if(r===null)return null;let i=(e.slice(0,r.index)+e.slice(r.index+r.keyword.length).replace(/^:\s*/,``)).replace(/ {2,}/g,` `).trim();return{keyword:r.keyword,strippedText:i,prompt:k(r.keyword,t)}}function P(e,t){return t?`${e}\n\n${t}`:e}function F(e,t,n,r){e(async(e,i)=>{let a=N(e.text??``,n);return a?(t.reviewMode&&await r.restoreOriginalState(i),t.mode=a.keyword,r.persistState(),r.transform(P(a.prompt,a.strippedText))):r.noMatch})}function I(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 D)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.`)}})}const L=r(t(i(import.meta.url)),`../agents/commands`);function R(e,t){F(t=>e.on(`input`,t),t,L,{restoreOriginalState:n=>b(e,n,t),persistState:()=>v(e,t),noMatch:{action:`continue`},transform:e=>({action:`transform`,text:e})})}function z(e,t){I((t,n)=>e.registerCommand(t,n),t,{restoreOriginalState:n=>b(e,n,t),persistState:()=>v(e,t)})}const B=r(t(i(import.meta.url)),`../agents/commands`);function V(e){return(t,n)=>{if(e.mode)return{systemPrompt:[t.systemPrompt,``,k(e.mode,B),``,`The user has set workflow mode to "${e.mode}". Honor this mode throughout the session until changed via /command.`].join(`
6
+ `)}}}function H(e,t){e.on(`session_before_compact`,e=>{let n=e.preparation;return{compaction:{summary:y(t),details:{...t},firstKeptEntryId:n?.firstKeptEntryId,tokensBefore:n?.tokensBefore}}}),e.on(`session_before_tree`,e=>{if(e.preparation?.userWantsSummary)return{summary:{summary:y(t)}}})}function U(e,t){H({on:(t,n)=>{e.on(t,n)}},t)}const W=new Set([`completed`,`steered`,`aborted`,`stopped`,`error`]);var G=class extends u.TaggedError(`SubagentPollError`){};function K(e){let t=e.intervalMs??500,n=e.timeoutMs??18e4,r=Math.ceil(n/t);return d.gen(function*(){let i=0,a=yield*d.sync(()=>e.service.getRecord(e.id));for(;a&&!W.has(a.status)&&i<r;)yield*d.sleep(t),a=yield*d.sync(()=>e.service.getRecord(e.id)),i++,e.sendUpdates&&(yield*d.sync(()=>{e.onUpdate?.({content:[{type:`text`,text:`${e.label} running... (${Math.round(i*t/1e3)}s)`}]})}));return a&&!W.has(a.status)?yield*d.fail(new G({id:e.id,reason:`timeout`,message:`Subagent ${e.id} timed out after ${n}ms`})):a||(yield*d.fail(new G({id:e.id,reason:`missing`,message:`Subagent ${e.id} was cleaned up before completion`})))})}function ce(e,t){return d.callback(n=>{let r=()=>{n(d.fail(new G({id:e,reason:`aborted`,message:`Maestria subagent call aborted`})))};if(t.aborted){r();return}return t.addEventListener(`abort`,r,{once:!0}),d.sync(()=>t.removeEventListener(`abort`,r))})}function q(e){let t=K(e);return e.signal?d.raceFirst(t,ce(e.id,e.signal)):t}const J=C,Y=18e4;function X(e,t){if(typeof e.abort==`function`)for(let n of t)try{e.abort(n)}catch{}}function Z(e){return d.tapError(q(e),()=>d.sync(()=>X(e.service,[e.id])))}function Q(e,t,n,r){let i=te(g(t,`orchestrator`,n,r),n);Object.assign(t,i),v(e,t)}function le(e,t,n){if(e.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:l.Object({agent:l.String({description:`Specialist agent name (required): adventurer, architect, builder, diagnose, planner, reviewer, writer`}),task:l.String({description:`Task description for the subagent (required)`}),tasks:l.Optional(l.Array(l.Object({agent:l.String(),task:l.String()}),{description:`Array of task objects for parallel or chain dispatch`})),mode:l.Optional(l.Union([l.Literal(`parallel`),l.Literal(`chain`),l.Literal(`single`)]))}),async execute(n,r,i,a,o){if(t.reviewMode)return{content:[{type:`text`,text:`Subagent dispatch is not available during review mode. Use /restore-model to exit review mode first.`}]};let s=r.mode??`single`;if(s===`single`){if(!r.agent||!J.includes(r.agent))return{content:[{type:`text`,text:`Invalid maestria_subagent call: 'agent' is required and must be one of ${J.join(`, `)}. Re-dispatch with a valid agent name; the orchestrator may continue read-only exploration while the brief is corrected.`}]};T(r.task,`Task description is required`)}else if(s===`parallel`){if(!r.tasks||r.tasks.length<2)throw Error(`For parallel mode, tasks array is required with at least 2 items`);if(r.tasks.length>8)throw Error(`For parallel mode, tasks array may have at most 8 items (got ${r.tasks.length})`);for(let e of r.tasks)w(e.agent),T(e.task,`Task description is required for all tasks`)}else if(s===`chain`){if(!r.tasks||r.tasks.length<2)throw Error(`For chain mode, tasks array is required with at least 2 items`);for(let e of r.tasks)w(e.agent),T(e.task,`Task description is required for all tasks`)}let{getSubagentsService:c}=await import(`@gotgenes/pi-subagents`),l=c();if(!l||typeof l.spawn!=`function`)return{content:[{type:`text`,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(`
7
+ `)}]};try{if(s===`single`){let n=r.agent,o=r.task,s=l.spawn(n,o,{description:o.slice(0,80),foreground:!0,inheritContext:!0});Q(e,t,n,o);let c=await d.runPromise(Z({id:s,label:`Subagent ${n}`,sendUpdates:!0,service:l,signal:i,onUpdate:a,intervalMs:500,timeoutMs:Y}));return{content:[{type:`text`,text:c.result??c.error??`No output.`}],details:{subagentId:s}}}if(s===`parallel`){let n=r.tasks;a?.({content:[{type:`text`,text:`Spawning ${n.length} parallel subagents...`}]});let o=[];for(let r of n){let n=l.spawn(r.agent,r.task,{description:r.task.slice(0,80),foreground:!0,inheritContext:!0});o.push(n),Q(e,t,r.agent,r.task)}let s=await d.runPromise(d.all(o.map((e,t)=>d.match(q({id:e,label:`${n[t].agent} (${t+1}/${n.length})`,sendUpdates:!1,service:l,signal:i,onUpdate:a,intervalMs:500,timeoutMs:Y}),{onSuccess:e=>({record:e}),onFailure:e=>(X(l,o),{error:e})})),{concurrency:`unbounded`}));a?.({content:[{type:`text`,text:`All ${n.length} parallel subagents settled.`}]});let c=[`## Parallel Results (${n.length} tasks)\n`];for(let e=0;e<n.length;e++){let t=n[e],r=s[e],i=`### ${e+1}: ${t.agent}`;if(`error`in r){let e=r.error instanceof Error?r.error.message:String(r.error);c.push(i),c.push(`⚠️ ${e}`)}else{let e=r.record.result??r.record.error??`No output.`;c.push(i),c.push(e)}}return{content:[{type:`text`,text:c.join(`
8
8
 
9
- `)}],details:{subagentIds:o}}}if(s===`chain`){let n=r.tasks,o=``;for(let r=0;r<n.length;r++){let s=n[r],c=s.task;r>0&&c.includes(`{previous}`)&&(c=c.replace(/\{previous\}/g,o));let u=l.spawn(s.agent,c,{description:c.slice(0,80),foreground:!0,inheritContext:!0});X(e,t,s.agent,c),a?.({content:[{type:`text`,text:`Chain step ${r+1}/${n.length}: ${s.agent} running...`}]});let d=await Y(u,`Chain step ${r+1}: ${s.agent}`,!0,l,i,a);o=d.result??d.error??`No output.`,r<n.length-1&&a?.({content:[{type:`text`,text:`Chain step ${r+1}/${n.length}: ${s.agent} completed. Moving to next step.`}]})}return{content:[{type:`text`,text:o}],details:{subagentId:`chain-completed`}}}throw Error(`Unknown dispatch mode`)}catch(e){console.warn(`[maestria] Subagent dispatch failed:`,e);let t=r.agent??r.tasks?.[0]?.agent??`unknown`,n=r.task??r.tasks?.map(e=>e.task).join(`; `)??`unknown`;return{content:[{type:`text`,text:[`## Subagent Handoff Required`,``,`**From:** orchestrator`,`**To:** ${t}`,`**Task:** ${n}`,``,`Subagent dispatch failed. Please delegate this work manually.`].join(`
10
- `)}]}}}}),e.events){let r=e.events.on(u.STARTED,n=>{let{id:r,type:i}=n;t.subagentStatus[r]={type:i,status:`running`,startedAt:Date.now()},v(e,t),e.events?.emit(S.SUBAGENT_STARTED,{id:r,type:i,timestamp:Date.now()})}),i=e.events.on(u.COMPLETED,n=>{let{id:r}=n,i=t.subagentStatus[r];i&&(i.status=`completed`,i.completedAt=Date.now()),v(e,t),e.events?.emit(S.SUBAGENT_COMPLETED,{id:r,type:i?.type,timestamp:Date.now()})}),a=e.events.on(u.FAILED,n=>{let{id:r,status:i}=n,a=t.subagentStatus[r];a&&(a.status=i??`error`,a.completedAt=Date.now()),v(e,t),e.events?.emit(S.SUBAGENT_FAILED,{id:r,type:a?.type,timestamp:Date.now()})}),o=e.events.on(u.STEERED,n=>{let{id:r}=n;t.subagentStatus[r]||(t.subagentStatus[r]={type:`unknown`,status:`running`,startedAt:Date.now()}),v(e,t)});n&&n.push(r,i,a,o)}}const re=[`read`,`grep`,`find`,`ls`,`glob`];function ie(e,t){e.registerCommand(`maestria-status`,{description:`Show current maestria session state including handoff history`,handler:async(e,n)=>{let r=y(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),v(e,t),t.reviewModel){let n=await x(e,r,t);n&&(r.ui.notify(`Review mode: switched to ${n}`),e.events?.emit(S.REVIEW_ACTIVATED,{originalModel:t.originalModel,reviewModel:n,timestamp:Date.now()}))}e.setActiveTools(re),e.sendUserMessage([`[REVIEW: ${n}]`,``,`Review: ${n}. Use the reviewer prompt template.`,`Read only, no edits, report findings.`].join(`
9
+ `)}],details:{subagentIds:o}}}if(s===`chain`){let n=r.tasks,o=``;for(let r=0;r<n.length;r++){let s=n[r],c=s.task;r>0&&c.includes(`{previous}`)&&(c=c.replace(/\{previous\}/g,o));let u=l.spawn(s.agent,c,{description:c.slice(0,80),foreground:!0,inheritContext:!0});Q(e,t,s.agent,c),a?.({content:[{type:`text`,text:`Chain step ${r+1}/${n.length}: ${s.agent} running...`}]});try{let e=await d.runPromise(Z({id:u,label:`Chain step ${r+1}: ${s.agent}`,sendUpdates:!0,service:l,signal:i,onUpdate:a,intervalMs:500,timeoutMs:Y}));o=e.result??e.error??`No output.`}catch(e){o=`[error] ${e instanceof Error?e.message:String(e)}`;break}r<n.length-1&&a?.({content:[{type:`text`,text:`Chain step ${r+1}/${n.length}: ${s.agent} completed. Moving to next step.`}]})}return{content:[{type:`text`,text:o}],details:{subagentId:`chain-completed`}}}throw Error(`Unknown dispatch mode`)}catch(e){console.warn(`[maestria] Subagent dispatch failed:`,e);let t=r.agent??r.tasks?.[0]?.agent??`unknown`,n=r.task??r.tasks?.map(e=>e.task).join(`; `)??`unknown`;return{content:[{type:`text`,text:[`## Subagent Handoff Required`,``,`**From:** orchestrator`,`**To:** ${t}`,`**Task:** ${n}`,``,`Subagent dispatch failed. Please delegate this work manually.`].join(`
10
+ `)}]}}}}),e.events){let r=e.events.on(f.STARTED,n=>{let{id:r,type:i}=n;t.subagentStatus[r]={type:i,status:`running`,startedAt:Date.now()},v(e,t),e.events?.emit(S.SUBAGENT_STARTED,{id:r,type:i,timestamp:Date.now()})}),i=e.events.on(f.COMPLETED,n=>{let{id:r}=n,i=t.subagentStatus[r];i&&(i.status=`completed`,i.completedAt=Date.now()),v(e,t),e.events?.emit(S.SUBAGENT_COMPLETED,{id:r,type:i?.type,timestamp:Date.now()})}),a=e.events.on(f.FAILED,n=>{let{id:r,status:i}=n,a=t.subagentStatus[r];a&&(a.status=i??`error`,a.completedAt=Date.now()),v(e,t),e.events?.emit(S.SUBAGENT_FAILED,{id:r,type:a?.type,timestamp:Date.now()})}),o=e.events.on(f.STEERED,n=>{let{id:r}=n;t.subagentStatus[r]||(t.subagentStatus[r]={type:`unknown`,status:`running`,startedAt:Date.now()}),v(e,t)});n&&n.push(r,i,a,o)}}const ue=[`read`,`grep`,`find`,`ls`,`glob`];function de(e,t){e.registerCommand(`maestria-status`,{description:`Show current maestria session state including handoff history`,handler:async(e,n)=>{let r=y(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),v(e,t),t.reviewModel){let n=await x(e,r,t);n&&(r.ui.notify(`Review mode: switched to ${n}`),e.events?.emit(S.REVIEW_ACTIVATED,{originalModel:t.originalModel,reviewModel:n,timestamp:Date.now()}))}e.setActiveTools(ue),e.sendUserMessage([`[REVIEW: ${n}]`,``,`Review: ${n}. Use the reviewer prompt template.`,`Read only, no edits, report findings.`].join(`
11
11
  `),{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 b(e,r,t),v(e,t),r.ui.notify(`Restored original model and tools.`),e.events?.emit(S.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(`
12
12
  `):`(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(`
13
- `);t.handoffHistory=[{from:`current`,to:`next`,task:i,timestamp:Date.now()},...t.handoffHistory??[]].slice(0,5),v(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,v(e,t),r.ui.notify(`Review model set to: ${i}`)}})}const ae=[/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/],oe=/^(ls|cat|head|tail|git status|git diff|git log|git branch|find|grep|rg|pnpm test|npm test|pwd|which)\b/;function se(e){let t=e.trim();if(t.includes(`$(`)||t.includes("`"))return!1;let n=t.replace(/\d?>&[12]/g,``);return!n.includes(`>`)&&n.split(/[\n;&|]+/).every(e=>oe.test(e.trim()))}function Q(e,t){e.on(`tool_call`,async(n,r)=>{if(!n||!n.toolName)return;if(t.mode!==null&&e.getActiveTools().includes(`subagent`)&&(d(`edit`,n)||d(`write`,n)||d(`patch`,n)||n.toolName===`bash`)){if(n.toolName===`bash`){let e=n.input;if(se(typeof e?.command==`string`?e.command:``))return}return{block:!0,reason:`Tool '${n.toolName}' is blocked for the orchestrator. Use 'maestria_subagent' to delegate mutations to specialists.`}}if(t.reviewMode&&(d(`edit`,n)||d(`write`,n)||d(`bash`,n)))return{block:!0,reason:`Review mode is active. Report findings, do not edit.`};if(d(`bash`,n)){if(!n.input||typeof n.input!=`object`)return;let e=n.input.command;if(e){for(let t of ae)if(t.test(e))return r.hasUI&&await r.ui.confirm(`Dangerous Pattern Detected`,`This command matches a dangerous pattern:\n${e}\nProceed?`)?void 0:{block:!0,reason:`Command matches dangerous pattern: ${t}`}}}let i=!1;if(d(`read`,n)){let e=n.input?.path;typeof e==`string`&&e&&(Object.assign(t,ee(t,e)),i=!0)}else if(d(`edit`,n)||d(`write`,n)){let e=n.input?.path;typeof e==`string`&&e&&(Object.assign(t,h(t,e)),i=!0)}i&&v(e,t)})}function ce(e){return typeof e==`object`&&!!e}function le(e){let t=e?.sessionManager;if(typeof t?.getBranch!=`function`)return null;let n=t.getBranch();return Array.isArray(n)?n:null}function $(e,t){let n=f(),r=le(t);if(!r){let t=e;for(let e of Object.keys(t))delete t[e];Object.assign(e,n);return}for(let e=r.length-1;e>=0;e--){let t=r[e];if(t.type===`custom`&&t.customType===`maestria_state`){ce(t.data)&&Object.assign(n,t.data);break}}let i=e;for(let e of Object.keys(i))delete i[e];Object.assign(e,n)}function ue(e){let t=f(),n=[];U(e,t),H(e,t);let r=ne(t);e.on(`before_agent_start`,(e,t)=>r(e,t)),e.on(`session_start`,(e,n)=>{te(n),$(t,n)}),e.on(`session_tree`,(e,n)=>{$(t,n)}),K(e,t),Z(e,t,n),ie(e,t),e.on(`session_shutdown`,()=>{for(let e of n)e();n.length=0}),Q(e,t)}export{ue as default};
13
+ `);t.handoffHistory=[{from:`current`,to:`next`,task:i,timestamp:Date.now()},...t.handoffHistory??[]].slice(0,5),v(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,v(e,t),r.ui.notify(`Review model set to: ${i}`)}})}const fe=[/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/],pe=/^(ls|cat|head|tail|git status|git diff|git log|git branch|find|grep|rg|pnpm test|npm test|pwd|which)\b/;function me(e){let t=e.trim();if(t.includes(`$(`)||t.includes("`"))return!1;let n=t.replace(/\d?>&[12]/g,``);return!n.includes(`>`)&&n.split(/[\n;&|]+/).every(e=>pe.test(e.trim()))}function he(e,t){e.on(`tool_call`,async(n,r)=>{if(!n||!n.toolName)return;if(t.mode!==null&&e.getActiveTools().includes(`subagent`)&&(p(`edit`,n)||p(`write`,n)||p(`patch`,n)||n.toolName===`bash`)){if(n.toolName===`bash`){let e=n.input;if(me(typeof e?.command==`string`?e.command:``))return}return{block:!0,reason:`Tool '${n.toolName}' is blocked for the orchestrator. Use 'maestria_subagent' to delegate mutations to specialists.`}}if(t.reviewMode&&(p(`edit`,n)||p(`write`,n)||p(`bash`,n)))return{block:!0,reason:`Review mode is active. Report findings, do not edit.`};if(p(`bash`,n)){if(!n.input||typeof n.input!=`object`)return;let e=n.input.command;if(e){for(let t of fe)if(t.test(e))return r.hasUI&&await r.ui.confirm(`Dangerous Pattern Detected`,`This command matches a dangerous pattern:\n${e}\nProceed?`)?void 0:{block:!0,reason:`Command matches dangerous pattern: ${t}`}}}let i=!1;if(p(`read`,n)){let e=n.input?.path;typeof e==`string`&&e&&(Object.assign(t,_(t,e)),i=!0)}else if(p(`edit`,n)||p(`write`,n)){let e=n.input?.path;typeof e==`string`&&e&&(Object.assign(t,ee(t,e)),i=!0)}i&&v(e,t)})}function ge(e){return typeof e==`object`&&!!e}function _e(e){let t=e?.sessionManager;if(typeof t?.getBranch!=`function`)return null;let n=t.getBranch();return Array.isArray(n)?n:null}function $(e,t){let n=m(),r=_e(t);if(!r){let t=e;for(let e of Object.keys(t))delete t[e];Object.assign(e,n);return}for(let e=r.length-1;e>=0;e--){let t=r[e];if(t.type===`custom`&&t.customType===`maestria_state`){ge(t.data)&&Object.assign(n,t.data);break}}let i=e;for(let e of Object.keys(i))delete i[e];Object.assign(e,n)}function ve(e){let t=m(),n=[];z(e,t),R(e,t);let r=V(t);e.on(`before_agent_start`,(e,t)=>r(e,t)),e.on(`session_start`,(e,n)=>{ie(n),$(t,n)}),e.on(`session_tree`,(e,n)=>{$(t,n)}),U(e,t),le(e,t,n),de(e,t),e.on(`session_shutdown`,()=>{for(let e of n)e();n.length=0}),he(e,t)}export{ve as default};
14
14
  //# sourceMappingURL=extension.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"extension.mjs","names":["deploySpecialistAgents","installModeAutoDetect","installModeCommands","COMMANDS_DIR","installCompactionHandlers"],"sources":["../../shared/pi/src/state-core.ts","../src/state/review.ts","../../shared/pi/src/subagent-utils.ts","../../shared/pi/src/agent-deployment.ts","../src/agents.ts","../../shared/pi/src/modes-core.ts","../src/modes.ts","../src/rules.ts","../../shared/pi/src/compaction-core.ts","../src/compaction.ts","../src/subagent.ts","../src/commands.ts","../../shared/pi/src/tools-core.ts","../src/tools.ts","../src/extension.ts"],"sourcesContent":["/**\n * Shared state management for Maestria platform packages.\n *\n * Pure TypeScript — no platform-specific dependencies.\n * Provides shared state-management types, transforms, persistence, and rendering\n * consumed directly by @maestria/omp and @maestria/pi.\n *\n * @module\n */\n\n// ── Types ──\n\nexport type ModeKeyword = 'fein' | 'sonar' | 'blitz';\n\nexport const HANDOFF_HISTORY_CAP = 5;\nexport const 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\n/**\n * Mirror of the host platform's native goal (e.g. OMP goal mode).\n *\n * Platform-agnostic by design: only the objective text and status are\n * carried so shared state stays free of platform-specific types.\n */\nexport interface NativeGoalMirror {\n objective: string;\n status: string;\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 reviewModel: string | null;\n nativeGoal: NativeGoalMirror | null;\n}\n\n// ── Transforms ──\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 nativeGoal: null,\n };\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 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\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 recordSpecialistDelegated(state: MaestriaState, name: string): MaestriaState {\n if (state.specialistsDelegated.includes(name)) return state;\n return { ...state, specialistsDelegated: [...state.specialistsDelegated, name] };\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 { ...state, reviewMode: active };\n}\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// ── Persistence ──\n\nexport function persistState(\n pi: { appendEntry: (type: string, data: unknown) => void },\n state: MaestriaState,\n): void {\n pi.appendEntry('maestria_state', { ...state });\n}\n\n// ── Render ──\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.nativeGoal) {\n parts.push(`**Native Goal:** ${state.nativeGoal.objective} (${state.nativeGoal.status})`);\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","import type {\n ExtensionAPI,\n ExtensionCommandContext,\n ExtensionContext,\n} from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@maestria/shared-pi/state-core';\nimport { exitReviewMode } from '@maestria/shared-pi/state-core';\n\nexport async function restoreOriginalState(\n pi: ExtensionAPI,\n ctx: ExtensionContext,\n state: MaestriaState,\n): Promise<void> {\n const { state: clearedState, originalModel, originalTools } = exitReviewMode(state);\n\n if (originalTools && originalTools.length > 0) {\n pi.setActiveTools(originalTools);\n }\n\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 Object.assign(state, clearedState);\n}\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","/**\n * Shared subagent validation utilities for Maestria platform packages.\n *\n * Pure TypeScript — no platform-specific dependencies.\n * Imported by both @maestria/omp and @maestria/pi to eliminate duplication.\n *\n * @module\n */\n\n/** Maestria cross-extension event names. */\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\n/** The set of specialist agent types maestria supports. */\nexport const ALLOWED_AGENTS = [\n 'adventurer',\n 'architect',\n 'builder',\n 'diagnose',\n 'planner',\n 'reviewer',\n 'writer',\n] as const;\n\n/** A valid specialist agent name. */\nexport type AllowedAgent = (typeof ALLOWED_AGENTS)[number];\n\n/** The 7-field handoff contract used in delegation. */\nexport const HANDOFF_FIELDS = [\n 'Goal',\n 'Context',\n 'Requirements',\n 'Known problems',\n 'Assumptions documented',\n 'Success criteria',\n 'Next step',\n] as const;\n\n/** Result of validating a handoff document against the contract fields. */\nexport interface HandoffValidation {\n valid: boolean;\n errors: string[];\n}\n\n/**\n * Asserts that `agent` is a known maestria specialist.\n * @throws {Error} if the agent name is not in ALLOWED_AGENTS.\n */\nexport function assertValidAgent(agent: string): asserts agent is AllowedAgent {\n if (!ALLOWED_AGENTS.includes(agent as AllowedAgent)) {\n throw new Error(`Unknown agent: \"${agent}\". Allowed: ${ALLOWED_AGENTS.join(', ')}`);\n }\n}\n\n/**\n * Asserts that `task` is a non-empty, non-whitespace string.\n * @throws {Error} with the given label if task is falsy or all-whitespace.\n */\nexport function assertNonEmptyTask(\n task: string | undefined,\n label: string,\n): asserts task is string {\n if (!task || !task.trim()) {\n throw new Error(label);\n }\n}\n\n/**\n * Validates that a handoff document contains all required fields\n * with non-empty content. Each field is expected in markdown bold format:\n * `**Field:** content`.\n */\nexport function validateHandoff(handoff: string): HandoffValidation {\n const errors: string[] = [];\n for (const field of HANDOFF_FIELDS) {\n // Match field header and capture content up to the next field or end of string.\n // This avoids false positives when an empty field is followed by another field's `**` header.\n const pattern = `\\\\*\\\\*${field}:\\\\*\\\\*([\\\\s\\\\S]*?)(?=\\\n\\\\*\\\\*|$)`;\n const match = handoff.match(new RegExp(pattern, 'i'));\n if (!match || !match[1] || !match[1].trim()) {\n errors.push(`Missing or empty field: \"${field}\"`);\n }\n }\n return { valid: errors.length === 0, errors };\n}\n","/**\n * Shared agent deployment logic for Maestria platform packages.\n *\n * Both @maestria/omp and @maestria/pi deploy specialist agent .md files\n * to their respective platform agent directories. This module eliminates\n * the duplication between the two packages.\n *\n * @module\n */\n\nimport { readFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { ALLOWED_AGENTS } from './subagent-utils.js';\n\n/**\n * Deploy bundled specialist agent .md files to the given destination directory.\n *\n * Only creates files that don't already exist — never overwrites user-customized agents.\n *\n * @param agentsSrc - Path to the source directory containing specialist .md files\n * @param agentsDest - Absolute path to the platform's agents destination directory\n * (e.g. `join(homedir(), '.omp', 'agent', 'agents')`)\n * @returns The number of agents newly deployed\n */\nexport function deploySpecialistAgents(agentsSrc: string, agentsDest: string): number {\n if (!existsSync(agentsSrc)) {\n console.warn('[maestria] Agents source directory not found:', agentsSrc);\n return 0;\n }\n\n try {\n mkdirSync(agentsDest, { recursive: true });\n } catch {\n console.warn('[maestria] Could not create agents directory:', agentsDest);\n return 0;\n }\n\n let deployed = 0;\n for (const name of ALLOWED_AGENTS) {\n const srcFile = join(agentsSrc, `${name}.md`);\n const destFile = join(agentsDest, `${name}.md`);\n\n if (!existsSync(srcFile)) {\n console.warn(`[maestria] Agent source not found: ${name}.md`);\n continue;\n }\n\n if (existsSync(destFile)) continue;\n\n try {\n const content = readFileSync(srcFile, 'utf-8');\n writeFileSync(destFile, content, 'utf-8');\n deployed++;\n } catch (err) {\n console.warn(`[maestria] Failed to deploy agent ${name}:`, err);\n }\n }\n\n if (deployed > 0) {\n console.log(`[maestria] Deployed ${deployed} specialist agents to ${agentsDest}`);\n }\n\n return deployed;\n}\n","import { homedir } from 'node:os';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { deploySpecialistAgents as deployAgents } from '@maestria/shared-pi/agent-deployment';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nconst AGENTS_SRC = join(__dirname, '..', 'agents');\n\n/**\n * Deploy bundled specialist agent .md files to the pi-subagents agents directory.\n *\n * pi-subagents discovers agent types from ~/.pi/agent/agents/*.md on every\n * registry.reload() call (which fires automatically on each tool invocation).\n * This function ensures the files are in place before the first subagent dispatch.\n *\n * Only creates files that don't already exist — never overwrites user-customized agents.\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function deploySpecialistAgents(_ctx?: unknown): void {\n deployAgents(AGENTS_SRC, join(homedir(), '.pi', 'agent', 'agents'));\n}\n","/**\n * Shared mode constants and utilities for Maestria platform packages.\n *\n * Pure TypeScript — no platform-specific dependencies.\n * Imported by both @maestria/omp and @maestria/pi to eliminate duplication\n * in mode prompt loading, keyword detection, and text transformation.\n *\n * @module\n */\n\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport type { MaestriaState } from './state-core.js';\n\n// ── Constants ──\n\nexport const MODE_KEYWORDS = ['fein', 'sonar', 'blitz'] as const;\nexport const MODE_CLEAR_COMMAND = 'mode-clear';\nexport type ModeKeyword = (typeof MODE_KEYWORDS)[number];\n\nexport const MODE_MARKERS: Record<ModeKeyword, string> = {\n fein: '[MODE: fein]',\n sonar: '[MODE: sonar]',\n blitz: '[MODE: blitz]',\n};\n\n// ── Prompt loading ──\n\n/** Lazily cached mode prompts — shared across platforms. */\nconst _promptCache: Partial<Record<ModeKeyword, string>> = {};\n\n/**\n * Load and cache a mode prompt from a commands directory.\n * The commandsDir should point to a directory containing `fein.md`,\n * `sonar.md`, and `blitz.md` files.\n */\nexport function loadModePrompt(name: string, commandsDir: string): string {\n const content = readFileSync(resolve(commandsDir, `${name}.md`), 'utf-8');\n const modeIdx = content.indexOf('## MODE:');\n if (modeIdx !== -1) {\n return content.slice(modeIdx).replace(/\\s+$/, '') + '\\n';\n }\n return content.replace(/\\s+$/, '') + '\\n';\n}\n\n/**\n * Get the full mode prompt (marker + body) for a keyword, loading from\n * the given commands directory on first access.\n */\nexport function getModePrompt(keyword: ModeKeyword, commandsDir: string): string {\n if (!(keyword in _promptCache)) {\n try {\n _promptCache[keyword] = loadModePrompt(keyword, commandsDir);\n } catch (e) {\n console.warn(`[maestria] Failed to load mode prompt \"${keyword}\":`, e);\n _promptCache[keyword] = '';\n }\n }\n return `${MODE_MARKERS[keyword]}\\n\\n${_promptCache[keyword]}`;\n}\n\n// ── Keyword detection ──\n\n/** Result of detecting a mode keyword in text. */\nexport interface ModeDetectResult {\n /** The detected keyword. */\n keyword: ModeKeyword;\n /** The text with the keyword stripped and trimmed. */\n strippedText: string;\n /** The full mode prompt (marker + body). */\n prompt: string;\n}\n\n/** Regex matching fenced code blocks (```) and inline backtick spans (`). */\nconst CODE_BLOCK_RE = /```[\\s\\S]*?```|`[^`]*`/g;\n\n/**\n * Find ranges of fenced code blocks and inline code spans in text.\n * Returns [start, end) positions. Keywords inside these ranges are\n * ignored during detection (per ADR-OC-003).\n */\nfunction findAllCodeBlockRanges(text: string): Array<[number, number]> {\n const ranges: Array<[number, number]> = [];\n let match: RegExpExecArray | null;\n while ((match = CODE_BLOCK_RE.exec(text)) !== null) {\n ranges.push([match.index, match.index + match[0].length]);\n }\n return ranges;\n}\n\nfunction isInRanges(index: number, ranges: Array<[number, number]>): boolean {\n return ranges.some(([start, end]) => index >= start && index < end);\n}\n\n/**\n * Priority mapping for mode keyword restrictiveness.\n * Higher number = more restrictive = wins when multiple keywords are present.\n * fein (3): full pipeline with mandatory gates\n * sonar (2): research only, no code\n * blitz (1): fast implementation, skip optional ceremony; required review remains\n */\nconst MODE_PRIORITY: Record<ModeKeyword, number> = {\n fein: 3,\n sonar: 2,\n blitz: 1,\n};\n\n/**\n * Detect a mode keyword (fein/sonar/blitz) in text as a whole word,\n * case-insensitive. Detection rules (per ADR-OC-003):\n * - Word-boundary regex matching (\\bfein\\b, \\bsonar\\b, \\bblitz\\b)\n * - Most restrictive match wins (fein > sonar > blitz)\n * - Case-insensitive\n * - Matches inside fenced code blocks (```) and inline backticks (`) are ignored\n */\nexport function detectModeInText(text: string, commandsDir: string): ModeDetectResult | null {\n if (!text) return null;\n\n const codeRanges = findAllCodeBlockRanges(text);\n let best: { keyword: ModeKeyword; index: number } | null = null;\n\n for (const keyword of MODE_KEYWORDS) {\n const regex = new RegExp(`\\\\b${keyword}\\\\b`, 'gi');\n let match: RegExpExecArray | null;\n while ((match = regex.exec(text)) !== null) {\n if (isInRanges(match.index, codeRanges)) continue;\n // Most-restrictive wins: prefer higher-priority mode over position\n if (best === null || MODE_PRIORITY[keyword] > MODE_PRIORITY[best.keyword]) {\n best = { keyword, index: match.index };\n }\n }\n }\n\n if (best === null) return null;\n\n // Strip the matched keyword, cleaning up a trailing colon and collapsing\n // double spaces (mirrors opencode's stripKeyword behavior).\n const before = text.slice(0, best.index);\n const after = text.slice(best.index + best.keyword.length).replace(/^:\\s*/, '');\n const strippedText = (before + after).replace(/ {2,}/g, ' ').trim();\n\n return {\n keyword: best.keyword,\n strippedText,\n prompt: getModePrompt(best.keyword, commandsDir),\n };\n}\n\n/**\n * Build the final text to send to the LLM: prompt + stripped text.\n * If strippedText is empty, returns just the prompt.\n */\nexport function buildModeText(prompt: string, strippedText: string): string {\n return strippedText ? `${prompt}\\n\\n${strippedText}` : prompt;\n}\n\n// ── Platform handler factories ──\n\n/**\n * Install an input event handler that detects mode keywords (fein/sonar/blitz)\n * in user input, strips them, and injects the mode prompt.\n *\n * @param onInput - Platform's `pi.on('input', handler)` method\n * @param state - Shared maestria state\n * @param commandsDir - Path to directory containing fein.md/sonar.md/blitz.md\n * @param opts - Platform-specific callbacks and result builders\n */\nexport function installModeAutoDetect(\n onInput: (handler: (event: unknown, ctx: unknown) => unknown) => void,\n state: MaestriaState,\n commandsDir: string,\n opts: {\n /** Exit review mode — calls platform's restoreOriginalState */\n restoreOriginalState: (ctx: unknown) => Promise<void>;\n /** Persist state after mode change */\n persistState: () => void;\n /** Return value when no keyword is detected (e.g. Pi: { action: 'continue' }) */\n noMatch: unknown;\n /** Build return value from transformed text (e.g. Pi: { action: 'transform', text }) */\n transform: (text: string) => unknown;\n },\n): void {\n onInput(async (event: unknown, ctx: unknown) => {\n const text = ((event as Record<string, unknown>).text as string) ?? '';\n const result = detectModeInText(text, commandsDir);\n if (!result) return opts.noMatch;\n\n if (state.reviewMode) {\n await opts.restoreOriginalState(ctx);\n }\n\n state.mode = result.keyword;\n opts.persistState();\n\n return opts.transform(buildModeText(result.prompt, result.strippedText));\n });\n}\n\n/**\n * Install slash commands for fein/sonar/blitz that set the workflow mode\n * and show a notification. Task description injection is handled by the\n * auto-detect handler instead.\n *\n * @param registerCommand - Platform's `pi.registerCommand(name, opts)` method\n * @param state - Shared maestria state\n * @param opts - Platform-specific callbacks\n */\nexport function installModeCommands(\n registerCommand: (\n name: string,\n options: { description: string; handler: (...args: unknown[]) => unknown },\n ) => void,\n state: MaestriaState,\n opts: {\n /** Exit review mode before switching modes */\n restoreOriginalState: (ctx: unknown) => Promise<void>;\n /** Persist state after mode change */\n persistState: () => void;\n },\n): void {\n registerCommand(MODE_CLEAR_COMMAND, {\n description: 'Clear workflow mode and return to neutral routing',\n handler: async (_args: unknown, ctx: unknown) => {\n if (state.reviewMode) {\n await opts.restoreOriginalState(ctx);\n }\n state.mode = null;\n opts.persistState();\n ((ctx as Record<string, unknown>).ui as { notify: (msg: string) => void }).notify(\n 'Workflow mode cleared. Neutral routing is active.',\n );\n },\n });\n\n for (const keyword of MODE_KEYWORDS) {\n registerCommand(keyword, {\n description: `Set workflow mode to ${keyword}`,\n handler: async (_args: unknown, ctx: unknown) => {\n if (state.reviewMode) {\n await opts.restoreOriginalState(ctx);\n }\n\n state.mode = keyword;\n opts.persistState();\n\n ((ctx as Record<string, unknown>).ui as { notify: (msg: string) => void }).notify(\n `Mode set to ${keyword}. Describe what you'd like to work on.`,\n );\n },\n });\n }\n}\n","import { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@/state.js';\nimport { persistState, restoreOriginalState } from '@/state.js';\nimport {\n installModeAutoDetect as installAutoDetect,\n installModeCommands as installCommands,\n} from '@maestria/shared-pi/modes-core';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst COMMANDS_DIR = resolve(__dirname, '../agents/commands');\n\nexport function installModeAutoDetect(pi: ExtensionAPI, state: MaestriaState): void {\n installAutoDetect((handler) => pi.on('input', handler as never), state, COMMANDS_DIR, {\n restoreOriginalState: (ctx) => restoreOriginalState(pi, ctx as ExtensionContext, state),\n persistState: () => persistState(pi, state),\n noMatch: { action: 'continue' as const },\n transform: (text) => ({ action: 'transform' as const, text }),\n });\n}\n\nexport function installModeCommands(pi: ExtensionAPI, state: MaestriaState): void {\n installCommands((name, opts) => pi.registerCommand(name, opts as never), state, {\n restoreOriginalState: (ctx) => restoreOriginalState(pi, ctx as ExtensionContext, state),\n persistState: () => persistState(pi, state),\n });\n}\n","import { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type {\n BeforeAgentStartEvent,\n BeforeAgentStartEventResult,\n ExtensionContext,\n} from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@/state.js';\nimport { getModePrompt } from '@maestria/shared-pi/modes-core';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst COMMANDS_DIR = resolve(__dirname, '../agents/commands');\n\n/**\n * Creates a before_agent_start handler that injects workflow mode prompts.\n *\n * This is the only dynamic prompt injection needed from the extension.\n * Static behavioral content (orchestrator prompt + global rules) is\n * auto-injected by Pi's skill system via SKILL.md files registered in\n * the pi.skills manifest field - the standard Pi extension pattern.\n *\n * When no mode is active, the handler returns void (no modification),\n * letting Pi's built-in prompt assembly (skills + context files + tools)\n * stand as-is.\n */\nexport function createModePromptHandler(state: MaestriaState) {\n return (\n event: BeforeAgentStartEvent,\n _ctx: ExtensionContext,\n ): BeforeAgentStartEventResult | void => {\n if (!state.mode) return;\n\n const parts: string[] = [\n event.systemPrompt,\n '',\n getModePrompt(state.mode, COMMANDS_DIR),\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 return { systemPrompt: parts.join('\\n') };\n };\n}\n","/**\n * Shared compaction handlers for Maestria platform packages.\n *\n * Pure TypeScript — no platform-specific dependencies.\n * Imported by both @maestria/omp and @maestria/pi to eliminate duplication.\n *\n * @module\n */\n\nimport { renderMaestriaSummary } from './state-core.js';\nimport type { MaestriaState } from './state-core.js';\n\n/**\n * Install handlers for session compaction and tree events to persist\n * and restore maestria state across session compaction boundaries.\n *\n * Uses duck-typed `pi` parameter — both Pi and OMP ExtensionAPI types\n * satisfy the `{ on(event: string, handler): void }` shape needed here.\n */\nexport function installCompactionHandlers(\n pi: {\n on: (event: string, handler: (...args: unknown[]) => unknown) => void;\n },\n state: MaestriaState,\n): void {\n pi.on('session_before_compact', (event: unknown) => {\n const prep = (event as Record<string, unknown>).preparation as\n | Record<string, unknown>\n | undefined;\n return {\n compaction: {\n summary: renderMaestriaSummary(state),\n details: { ...state },\n firstKeptEntryId: prep?.firstKeptEntryId,\n tokensBefore: prep?.tokensBefore,\n },\n };\n });\n\n pi.on('session_before_tree', (event: unknown) => {\n const prep = (event as Record<string, unknown>).preparation as\n | Record<string, unknown>\n | undefined;\n if (prep?.userWantsSummary) {\n return {\n summary: {\n summary: renderMaestriaSummary(state),\n },\n };\n }\n return undefined;\n });\n}\n","/**\n * Pi platform compaction handlers.\n *\n * Thin wrapper around the shared implementation in\n * @maestria/shared-pi/compaction-core.\n *\n * @module\n */\n\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@/state.js';\nimport { installCompactionHandlers as installHandlers } from '@maestria/shared-pi/compaction-core';\n\n/**\n * Install session compaction and tree event handlers for Pi.\n * Delegates to the shared implementation which is duck-type compatible\n * with Pi's ExtensionAPI.\n */\nexport function installCompactionHandlers(pi: ExtensionAPI, state: MaestriaState): void {\n // Bridge: ExtensionAPI.on has overloaded event types incompatible with\n // the duck-typed { on: (event: string, handler) => void } in the shared\n // module. The as-never cast is safe at runtime — both SDKs share the same\n // event shapes.\n installHandlers(\n {\n on: (event, handler) => {\n pi.on(event as never, handler as never);\n },\n },\n state,\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, recordSpecialistDelegated } from '@/state.js';\nimport {\n ALLOWED_AGENTS,\n assertValidAgent,\n assertNonEmptyTask,\n MAESTRIA_EVENTS,\n} from '@maestria/shared-pi/subagent-utils';\n\nconst ALLOWED_AGENT_NAMES: ReadonlyArray<string> = ALLOWED_AGENTS;\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 = 180_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\n// ── Polling helper ───────────────────────────────────────────────\n\ntype SubagentRecord = { status: string; result?: string; error?: string };\n\nasync function pollSubagent(\n id: string,\n label: string,\n sendUpdates: boolean,\n service: { getRecord(id: string): SubagentRecord | undefined },\n signal: AbortSignal | undefined,\n onUpdate: ((result: { content: Array<{ type: string; text: string }> }) => void) | undefined,\n): Promise<SubagentRecord> {\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// ── Handoff recording helper ────────────────────────────────────\n\nfunction recordAndPersist(\n pi: ExtensionAPI,\n state: MaestriaState,\n agentName: string,\n taskText: string,\n): void {\n const updatedState = recordSpecialistDelegated(\n recordHandoff(state, 'orchestrator', agentName, taskText),\n agentName,\n );\n Object.assign(state, updatedState);\n persistState(pi, state);\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.String({\n description:\n 'Specialist agent name (required): adventurer, architect, builder, diagnose, planner, reviewer, writer',\n }),\n task: Type.String({ description: 'Task description for the subagent (required)' }),\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 if (!params.agent || !ALLOWED_AGENT_NAMES.includes(params.agent)) {\n return {\n content: [\n {\n type: 'text' as const,\n text:\n `Invalid maestria_subagent call: 'agent' is required and must be one of ` +\n `${ALLOWED_AGENT_NAMES.join(', ')}. ` +\n `Re-dispatch with a valid agent name; the orchestrator may continue read-only exploration while the brief is corrected.`,\n },\n ],\n };\n }\n assertNonEmptyTask(params.task, 'Task description is required');\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 assertValidAgent(t.agent);\n assertNonEmptyTask(t.task, 'Task description is required for all tasks');\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 assertValidAgent(t.agent);\n assertNonEmptyTask(t.task, 'Task description is required for all tasks');\n }\n }\n\n // Attempt to dispatch via @gotgenes/pi-subagents; handle missing service\n const { getSubagentsService } = await import('@gotgenes/pi-subagents');\n const service = getSubagentsService();\n if (!service || typeof service.spawn !== 'function') {\n return {\n content: [\n {\n type: 'text' as const,\n text: [\n '## Subagent Dispatch Unavailable',\n '',\n 'The `@gotgenes/pi-subagents` extension is required for subagent dispatch but has not been loaded.',\n '',\n 'Install it as a Pi extension:',\n '',\n '```',\n 'pi install npm:@gotgenes/pi-subagents',\n '```',\n '',\n 'Then restart your Pi session.',\n ].join('\\n'),\n },\n ],\n };\n }\n\n try {\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 recordAndPersist(pi, state, agent, task);\n\n // Poll for completion\n const record = await pollSubagent(\n id,\n `Subagent ${agent}`,\n true,\n service,\n signal,\n onUpdate,\n );\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 recordAndPersist(pi, state, t.agent, t.task);\n }\n\n // Poll all concurrently\n const records = await Promise.all(\n spawnedIds.map((id, i) =>\n pollSubagent(\n id,\n `${taskList[i].agent} (${i + 1}/${taskList.length})`,\n false,\n service,\n signal,\n onUpdate,\n ),\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 recordAndPersist(pi, state, t.agent, taskText);\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(\n id,\n `Chain step ${i + 1}: ${t.agent}`,\n true,\n service,\n signal,\n onUpdate,\n );\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 (err) {\n console.warn('[maestria] Subagent dispatch failed:', err);\n // Return handoff payload as structured text when dispatch fails\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 dispatch failed. 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 '@maestria/shared-pi/subagent-utils';\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('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 7 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 '**Assumptions documented:**',\n '(document assumptions made, tagged [inferred] where uncertain)',\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","/**\n * Shared tool interceptor utilities for Maestria platform packages.\n *\n * Pure TypeScript — no platform-specific dependencies.\n * Imported by both @maestria/omp and @maestria/pi to eliminate duplication.\n *\n * @module\n */\n\n/**\n * Dangerous bash command patterns that should always be blocked,\n * regardless of mode or specialist role.\n */\nexport const 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 /:(){ :\\|:& };:/,\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\n/**\n * Read-only bash command prefixes allowed for the orchestrator's recon and\n * verification. Anything not matching — or chaining into a mutation — is\n * blocked; mutations belong to specialists.\n */\nconst READ_ONLY_BASH_PREFIX =\n /^(ls|cat|head|tail|git status|git diff|git log|git branch|find|grep|rg|pnpm test|npm test|pwd|which)\\b/;\n\n/**\n * True when a bash command performs no mutation.\n *\n * A naive prefix check is bypassable — `git status && git checkout .` or\n * `ls; rm -rf dist` both pass a prefix-only match — so every segment of a\n * chained command (`;`, `&&`, `||`, `|`, or newline) must itself be\n * read-only, and command substitution (`$(...)`, backticks) and output\n * redirection (`>` / `>>`) are rejected because they can hide a mutation\n * behind a read-only prefix. `2>&1`-style fd redirects are allowed (they\n * don't write).\n */\nexport function isReadOnlyBashCommand(rawCommand: string): boolean {\n const command = rawCommand.trim();\n if (command.includes('$(') || command.includes('`')) return false;\n // Strip `2>&1`-style fd redirects first so the `&` inside them is not\n // mistaken for a command separator and the `>` is not counted as output\n // redirection.\n const withoutFdRedirects = command.replace(/\\d?>&[12]/g, '');\n if (withoutFdRedirects.includes('>')) return false;\n return withoutFdRedirects\n .split(/[\\n;&|]+/)\n .every((segment) => READ_ONLY_BASH_PREFIX.test(segment.trim()));\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';\nimport { DANGEROUS_PATTERNS, isReadOnlyBashCommand } from '@maestria/shared-pi/tools-core';\nimport { persistState, recordFileModified, recordFileRead } from '@/state.js';\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 // ── Orchestrator routing enforcement ──\n // When a maestria workflow mode is active, restrict the root session\n // (orchestrator) to read-only recon + delegation. The orchestrator may\n // read, search, and inspect to route and verify, but mutations (edit,\n // write, mutation-capable bash) belong to specialists. Subagent sessions\n // are detected by the absence of the pi-subagents 'subagent' tool\n // (stripped by applyRecursionGuard in child sessions).\n if (state.mode !== null && pi.getActiveTools().includes('subagent')) {\n const isMutation =\n isToolCallEventType('edit', event) ||\n isToolCallEventType('write', event) ||\n isToolCallEventType('patch', event) ||\n event.toolName === 'bash';\n if (isMutation) {\n // Read-only bash (ls, git status, git diff, tests) is allowed so the\n // orchestrator can verify state; mutation-capable commands still\n // belong to specialists.\n if (event.toolName === 'bash') {\n const input = event.input as { command?: unknown } | undefined;\n const command = typeof input?.command === 'string' ? input.command : '';\n if (isReadOnlyBashCommand(command)) {\n return undefined;\n }\n }\n return {\n block: true,\n reason:\n `Tool '${event.toolName}' is blocked for the orchestrator. ` +\n `Use 'maestria_subagent' to delegate mutations to specialists.`,\n };\n }\n }\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 if (!event.input || typeof event.input !== 'object') return undefined;\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 // Record file access for session state (ADR-PI-002: tool_call maintains file tracking).\n // Only record when the call is allowed to proceed.\n let tracked = false;\n if (isToolCallEventType('read', event)) {\n const path = event.input?.path;\n if (typeof path === 'string' && path) {\n Object.assign(state, recordFileRead(state, path));\n tracked = true;\n }\n } else if (isToolCallEventType('edit', event) || isToolCallEventType('write', event)) {\n const path = event.input?.path;\n if (typeof path === 'string' && path) {\n Object.assign(state, recordFileModified(state, path));\n tracked = true;\n }\n }\n if (tracked) {\n persistState(pi, state);\n }\n\n return undefined; // allow\n });\n}\n","import type {\n ExtensionAPI,\n ExtensionContext,\n SessionStartEvent,\n SessionTreeEvent,\n} from '@earendil-works/pi-coding-agent';\nimport { createInitialState } from '@/state.js';\nimport type { MaestriaState } from '@/state.js';\nimport { deploySpecialistAgents } from '@/agents.js';\nimport { installModeCommands, installModeAutoDetect } from '@/modes.js';\nimport { createModePromptHandler } from '@/rules.js';\nimport { installCompactionHandlers } from '@/compaction.js';\nimport { installSubagentTool } from '@/subagent.js';\nimport { installCommands } from '@/commands.js';\nimport { installToolInterceptors } from '@/tools.js';\n\ninterface PersistedStateEntry {\n type: string;\n customType?: string;\n data?: unknown;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction currentSessionEntries(ctx: ExtensionContext): PersistedStateEntry[] | null {\n // getBranch() is the public current-session view and avoids restoring state\n // from a sibling branch in the same session tree. Never fall back to\n // getEntries(), which spans the entire session tree.\n const sessionManager = ctx?.sessionManager;\n if (typeof sessionManager?.getBranch !== 'function') return null;\n\n const branch = sessionManager.getBranch();\n return Array.isArray(branch) ? (branch as PersistedStateEntry[]) : null;\n}\n\nfunction restoreStateFromSession(state: MaestriaState, ctx: ExtensionContext): void {\n const next = createInitialState();\n const entries = currentSessionEntries(ctx);\n\n if (!entries) {\n const mutableState = state as unknown as Record<string, unknown>;\n for (const key of Object.keys(mutableState)) delete mutableState[key];\n Object.assign(state, next);\n return;\n }\n\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 if (isRecord(entry.data)) Object.assign(next, entry.data);\n break;\n }\n }\n\n const mutableState = state as unknown as Record<string, unknown>;\n for (const key of Object.keys(mutableState)) delete mutableState[key];\n Object.assign(state, next);\n}\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 installModeAutoDetect(pi, state);\n\n // Inject mode prompt when a workflow mode is active\n const handleModePrompt = createModePromptHandler(state);\n\n pi.on('before_agent_start', (event, ctx) => {\n return handleModePrompt(event, ctx);\n });\n\n // Deploy specialist agent files for pi-subagents discovery\n pi.on('session_start', (_event: SessionStartEvent, ctx) => {\n deploySpecialistAgents(ctx);\n\n // Restore persisted state on session start (reload/resume/fork)\n restoreStateFromSession(state, ctx);\n });\n\n // Rehydrate state when navigating the session tree to a different branch\n pi.on('session_tree', (_event: SessionTreeEvent, ctx) => {\n restoreStateFromSession(state, ctx);\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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"}
1
+ {"version":3,"file":"extension.mjs","names":["deploySpecialistAgents","installModeAutoDetect","installModeCommands","COMMANDS_DIR","installCompactionHandlers"],"sources":["../../shared/pi/src/state-core.ts","../src/state/review.ts","../../shared/pi/src/subagent-utils.ts","../../shared/pi/src/agent-deployment.ts","../src/agents.ts","../../shared/pi/src/modes-core.ts","../src/modes.ts","../src/rules.ts","../../shared/pi/src/compaction-core.ts","../src/compaction.ts","../src/subagent-polling.ts","../src/subagent.ts","../src/commands.ts","../../shared/pi/src/tools-core.ts","../src/tools.ts","../src/extension.ts"],"sourcesContent":["/**\n * Shared state management for Maestria platform packages.\n *\n * Pure TypeScript — no platform-specific dependencies.\n * Provides shared state-management types, transforms, persistence, and rendering\n * consumed directly by @maestria/omp and @maestria/pi.\n *\n * @module\n */\n\n// ── Types ──\n\nexport type ModeKeyword = 'fein' | 'sonar' | 'blitz';\n\nexport const HANDOFF_HISTORY_CAP = 5;\nexport const 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\n/**\n * Mirror of the host platform's native goal (e.g. OMP goal mode).\n *\n * Platform-agnostic by design: only the objective text and status are\n * carried so shared state stays free of platform-specific types.\n */\nexport interface NativeGoalMirror {\n objective: string;\n status: string;\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 reviewModel: string | null;\n nativeGoal: NativeGoalMirror | null;\n}\n\n// ── Transforms ──\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 nativeGoal: null,\n };\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 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\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 recordSpecialistDelegated(state: MaestriaState, name: string): MaestriaState {\n if (state.specialistsDelegated.includes(name)) return state;\n return { ...state, specialistsDelegated: [...state.specialistsDelegated, name] };\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 { ...state, reviewMode: active };\n}\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// ── Persistence ──\n\nexport function persistState(\n pi: { appendEntry: (type: string, data: unknown) => void },\n state: MaestriaState,\n): void {\n pi.appendEntry('maestria_state', { ...state });\n}\n\n// ── Render ──\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.nativeGoal) {\n parts.push(`**Native Goal:** ${state.nativeGoal.objective} (${state.nativeGoal.status})`);\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","import type {\n ExtensionAPI,\n ExtensionCommandContext,\n ExtensionContext,\n} from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@maestria/shared-pi/state-core';\nimport { exitReviewMode } from '@maestria/shared-pi/state-core';\n\nexport async function restoreOriginalState(\n pi: ExtensionAPI,\n ctx: ExtensionContext,\n state: MaestriaState,\n): Promise<void> {\n const { state: clearedState, originalModel, originalTools } = exitReviewMode(state);\n\n if (originalTools && originalTools.length > 0) {\n pi.setActiveTools(originalTools);\n }\n\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 Object.assign(state, clearedState);\n}\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","/**\n * Shared subagent validation utilities for Maestria platform packages.\n *\n * Pure TypeScript — no platform-specific dependencies.\n * Imported by both @maestria/omp and @maestria/pi to eliminate duplication.\n *\n * @module\n */\n\n/** Maestria cross-extension event names. */\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\n/** The set of specialist agent types maestria supports. */\nexport const ALLOWED_AGENTS = [\n 'adventurer',\n 'architect',\n 'builder',\n 'diagnose',\n 'planner',\n 'reviewer',\n 'writer',\n] as const;\n\n/** A valid specialist agent name. */\nexport type AllowedAgent = (typeof ALLOWED_AGENTS)[number];\n\n/** The 7-field handoff contract used in delegation. */\nexport const HANDOFF_FIELDS = [\n 'Goal',\n 'Context',\n 'Requirements',\n 'Known problems',\n 'Assumptions documented',\n 'Success criteria',\n 'Next step',\n] as const;\n\n/** Result of validating a handoff document against the contract fields. */\nexport interface HandoffValidation {\n valid: boolean;\n errors: string[];\n}\n\n/**\n * Asserts that `agent` is a known maestria specialist.\n * @throws {Error} if the agent name is not in ALLOWED_AGENTS.\n */\nexport function assertValidAgent(agent: string): asserts agent is AllowedAgent {\n if (!ALLOWED_AGENTS.includes(agent as AllowedAgent)) {\n throw new Error(`Unknown agent: \"${agent}\". Allowed: ${ALLOWED_AGENTS.join(', ')}`);\n }\n}\n\n/**\n * Asserts that `task` is a non-empty, non-whitespace string.\n * @throws {Error} with the given label if task is falsy or all-whitespace.\n */\nexport function assertNonEmptyTask(\n task: string | undefined,\n label: string,\n): asserts task is string {\n if (!task || !task.trim()) {\n throw new Error(label);\n }\n}\n\n/**\n * Validates that a handoff document contains all required fields\n * with non-empty content. Each field is expected in markdown bold format:\n * `**Field:** content`.\n */\nexport function validateHandoff(handoff: string): HandoffValidation {\n const errors: string[] = [];\n for (const field of HANDOFF_FIELDS) {\n // Match field header and capture content up to the next field or end of string.\n // This avoids false positives when an empty field is followed by another field's `**` header.\n const pattern = `\\\\*\\\\*${field}:\\\\*\\\\*([\\\\s\\\\S]*?)(?=\\\n\\\\*\\\\*|$)`;\n const match = handoff.match(new RegExp(pattern, 'i'));\n if (!match || !match[1] || !match[1].trim()) {\n errors.push(`Missing or empty field: \"${field}\"`);\n }\n }\n return { valid: errors.length === 0, errors };\n}\n","/**\n * Shared agent deployment logic for Maestria platform packages.\n *\n * Both @maestria/omp and @maestria/pi deploy specialist agent .md files\n * to their respective platform agent directories. This module eliminates\n * the duplication between the two packages.\n *\n * @module\n */\n\nimport { readFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { ALLOWED_AGENTS } from './subagent-utils.js';\n\n/**\n * Deploy bundled specialist agent .md files to the given destination directory.\n *\n * Only creates files that don't already exist — never overwrites user-customized agents.\n *\n * @param agentsSrc - Path to the source directory containing specialist .md files\n * @param agentsDest - Absolute path to the platform's agents destination directory\n * (e.g. `join(homedir(), '.omp', 'agent', 'agents')`)\n * @returns The number of agents newly deployed\n */\nexport function deploySpecialistAgents(agentsSrc: string, agentsDest: string): number {\n if (!existsSync(agentsSrc)) {\n console.warn('[maestria] Agents source directory not found:', agentsSrc);\n return 0;\n }\n\n try {\n mkdirSync(agentsDest, { recursive: true });\n } catch {\n console.warn('[maestria] Could not create agents directory:', agentsDest);\n return 0;\n }\n\n let deployed = 0;\n for (const name of ALLOWED_AGENTS) {\n const srcFile = join(agentsSrc, `${name}.md`);\n const destFile = join(agentsDest, `${name}.md`);\n\n if (!existsSync(srcFile)) {\n console.warn(`[maestria] Agent source not found: ${name}.md`);\n continue;\n }\n\n if (existsSync(destFile)) continue;\n\n try {\n const content = readFileSync(srcFile, 'utf-8');\n writeFileSync(destFile, content, 'utf-8');\n deployed++;\n } catch (err) {\n console.warn(`[maestria] Failed to deploy agent ${name}:`, err);\n }\n }\n\n if (deployed > 0) {\n console.log(`[maestria] Deployed ${deployed} specialist agents to ${agentsDest}`);\n }\n\n return deployed;\n}\n","import { homedir } from 'node:os';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { deploySpecialistAgents as deployAgents } from '@maestria/shared-pi/agent-deployment';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nconst AGENTS_SRC = join(__dirname, '..', 'agents');\n\n/**\n * Deploy bundled specialist agent .md files to the pi-subagents agents directory.\n *\n * pi-subagents discovers agent types from ~/.pi/agent/agents/*.md on every\n * registry.reload() call (which fires automatically on each tool invocation).\n * This function ensures the files are in place before the first subagent dispatch.\n *\n * Only creates files that don't already exist — never overwrites user-customized agents.\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function deploySpecialistAgents(_ctx?: unknown): void {\n deployAgents(AGENTS_SRC, join(homedir(), '.pi', 'agent', 'agents'));\n}\n","/**\n * Shared mode constants and utilities for Maestria platform packages.\n *\n * Pure TypeScript — no platform-specific dependencies.\n * Imported by both @maestria/omp and @maestria/pi to eliminate duplication\n * in mode prompt loading, keyword detection, and text transformation.\n *\n * @module\n */\n\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport type { MaestriaState } from './state-core.js';\n\n// ── Constants ──\n\nexport const MODE_KEYWORDS = ['fein', 'sonar', 'blitz'] as const;\nexport const MODE_CLEAR_COMMAND = 'mode-clear';\nexport type ModeKeyword = (typeof MODE_KEYWORDS)[number];\n\nexport const MODE_MARKERS: Record<ModeKeyword, string> = {\n fein: '[MODE: fein]',\n sonar: '[MODE: sonar]',\n blitz: '[MODE: blitz]',\n};\n\n// ── Prompt loading ──\n\n/** Lazily cached mode prompts — shared across platforms. */\nconst _promptCache: Partial<Record<ModeKeyword, string>> = {};\n\n/**\n * Load and cache a mode prompt from a commands directory.\n * The commandsDir should point to a directory containing `fein.md`,\n * `sonar.md`, and `blitz.md` files.\n */\nexport function loadModePrompt(name: string, commandsDir: string): string {\n const content = readFileSync(resolve(commandsDir, `${name}.md`), 'utf-8');\n const modeIdx = content.indexOf('## MODE:');\n if (modeIdx !== -1) {\n return content.slice(modeIdx).replace(/\\s+$/, '') + '\\n';\n }\n return content.replace(/\\s+$/, '') + '\\n';\n}\n\n/**\n * Get the full mode prompt (marker + body) for a keyword, loading from\n * the given commands directory on first access.\n */\nexport function getModePrompt(keyword: ModeKeyword, commandsDir: string): string {\n if (!(keyword in _promptCache)) {\n try {\n _promptCache[keyword] = loadModePrompt(keyword, commandsDir);\n } catch (e) {\n console.warn(`[maestria] Failed to load mode prompt \"${keyword}\":`, e);\n _promptCache[keyword] = '';\n }\n }\n return `${MODE_MARKERS[keyword]}\\n\\n${_promptCache[keyword]}`;\n}\n\n// ── Keyword detection ──\n\n/** Result of detecting a mode keyword in text. */\nexport interface ModeDetectResult {\n /** The detected keyword. */\n keyword: ModeKeyword;\n /** The text with the keyword stripped and trimmed. */\n strippedText: string;\n /** The full mode prompt (marker + body). */\n prompt: string;\n}\n\n/** Regex matching fenced code blocks (```) and inline backtick spans (`). */\nconst CODE_BLOCK_RE = /```[\\s\\S]*?```|`[^`]*`/g;\n\n/**\n * Find ranges of fenced code blocks and inline code spans in text.\n * Returns [start, end) positions. Keywords inside these ranges are\n * ignored during detection (per ADR-OC-003).\n */\nfunction findAllCodeBlockRanges(text: string): Array<[number, number]> {\n const ranges: Array<[number, number]> = [];\n let match: RegExpExecArray | null;\n while ((match = CODE_BLOCK_RE.exec(text)) !== null) {\n ranges.push([match.index, match.index + match[0].length]);\n }\n return ranges;\n}\n\nfunction isInRanges(index: number, ranges: Array<[number, number]>): boolean {\n return ranges.some(([start, end]) => index >= start && index < end);\n}\n\n/**\n * Priority mapping for mode keyword restrictiveness.\n * Higher number = more restrictive = wins when multiple keywords are present.\n * fein (3): full pipeline with mandatory gates\n * sonar (2): research only, no code\n * blitz (1): fast implementation, skip optional ceremony; required review remains\n */\nconst MODE_PRIORITY: Record<ModeKeyword, number> = {\n fein: 3,\n sonar: 2,\n blitz: 1,\n};\n\n/**\n * Detect a mode keyword (fein/sonar/blitz) in text as a whole word,\n * case-insensitive. Detection rules (per ADR-OC-003):\n * - Word-boundary regex matching (\\bfein\\b, \\bsonar\\b, \\bblitz\\b)\n * - Most restrictive match wins (fein > sonar > blitz)\n * - Case-insensitive\n * - Matches inside fenced code blocks (```) and inline backticks (`) are ignored\n */\nexport function detectModeInText(text: string, commandsDir: string): ModeDetectResult | null {\n if (!text) return null;\n\n const codeRanges = findAllCodeBlockRanges(text);\n let best: { keyword: ModeKeyword; index: number } | null = null;\n\n for (const keyword of MODE_KEYWORDS) {\n const regex = new RegExp(`\\\\b${keyword}\\\\b`, 'gi');\n let match: RegExpExecArray | null;\n while ((match = regex.exec(text)) !== null) {\n if (isInRanges(match.index, codeRanges)) continue;\n // Most-restrictive wins: prefer higher-priority mode over position\n if (best === null || MODE_PRIORITY[keyword] > MODE_PRIORITY[best.keyword]) {\n best = { keyword, index: match.index };\n }\n }\n }\n\n if (best === null) return null;\n\n // Strip the matched keyword, cleaning up a trailing colon and collapsing\n // double spaces (mirrors opencode's stripKeyword behavior).\n const before = text.slice(0, best.index);\n const after = text.slice(best.index + best.keyword.length).replace(/^:\\s*/, '');\n const strippedText = (before + after).replace(/ {2,}/g, ' ').trim();\n\n return {\n keyword: best.keyword,\n strippedText,\n prompt: getModePrompt(best.keyword, commandsDir),\n };\n}\n\n/**\n * Build the final text to send to the LLM: prompt + stripped text.\n * If strippedText is empty, returns just the prompt.\n */\nexport function buildModeText(prompt: string, strippedText: string): string {\n return strippedText ? `${prompt}\\n\\n${strippedText}` : prompt;\n}\n\n// ── Platform handler factories ──\n\n/**\n * Install an input event handler that detects mode keywords (fein/sonar/blitz)\n * in user input, strips them, and injects the mode prompt.\n *\n * @param onInput - Platform's `pi.on('input', handler)` method\n * @param state - Shared maestria state\n * @param commandsDir - Path to directory containing fein.md/sonar.md/blitz.md\n * @param opts - Platform-specific callbacks and result builders\n */\nexport function installModeAutoDetect(\n onInput: (handler: (event: unknown, ctx: unknown) => unknown) => void,\n state: MaestriaState,\n commandsDir: string,\n opts: {\n /** Exit review mode — calls platform's restoreOriginalState */\n restoreOriginalState: (ctx: unknown) => Promise<void>;\n /** Persist state after mode change */\n persistState: () => void;\n /** Return value when no keyword is detected (e.g. Pi: { action: 'continue' }) */\n noMatch: unknown;\n /** Build return value from transformed text (e.g. Pi: { action: 'transform', text }) */\n transform: (text: string) => unknown;\n },\n): void {\n onInput(async (event: unknown, ctx: unknown) => {\n const text = ((event as Record<string, unknown>).text as string) ?? '';\n const result = detectModeInText(text, commandsDir);\n if (!result) return opts.noMatch;\n\n if (state.reviewMode) {\n await opts.restoreOriginalState(ctx);\n }\n\n state.mode = result.keyword;\n opts.persistState();\n\n return opts.transform(buildModeText(result.prompt, result.strippedText));\n });\n}\n\n/**\n * Install slash commands for fein/sonar/blitz that set the workflow mode\n * and show a notification. Task description injection is handled by the\n * auto-detect handler instead.\n *\n * @param registerCommand - Platform's `pi.registerCommand(name, opts)` method\n * @param state - Shared maestria state\n * @param opts - Platform-specific callbacks\n */\nexport function installModeCommands(\n registerCommand: (\n name: string,\n options: { description: string; handler: (...args: unknown[]) => unknown },\n ) => void,\n state: MaestriaState,\n opts: {\n /** Exit review mode before switching modes */\n restoreOriginalState: (ctx: unknown) => Promise<void>;\n /** Persist state after mode change */\n persistState: () => void;\n },\n): void {\n registerCommand(MODE_CLEAR_COMMAND, {\n description: 'Clear workflow mode and return to neutral routing',\n handler: async (_args: unknown, ctx: unknown) => {\n if (state.reviewMode) {\n await opts.restoreOriginalState(ctx);\n }\n state.mode = null;\n opts.persistState();\n ((ctx as Record<string, unknown>).ui as { notify: (msg: string) => void }).notify(\n 'Workflow mode cleared. Neutral routing is active.',\n );\n },\n });\n\n for (const keyword of MODE_KEYWORDS) {\n registerCommand(keyword, {\n description: `Set workflow mode to ${keyword}`,\n handler: async (_args: unknown, ctx: unknown) => {\n if (state.reviewMode) {\n await opts.restoreOriginalState(ctx);\n }\n\n state.mode = keyword;\n opts.persistState();\n\n ((ctx as Record<string, unknown>).ui as { notify: (msg: string) => void }).notify(\n `Mode set to ${keyword}. Describe what you'd like to work on.`,\n );\n },\n });\n }\n}\n","import { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@/state.js';\nimport { persistState, restoreOriginalState } from '@/state.js';\nimport {\n installModeAutoDetect as installAutoDetect,\n installModeCommands as installCommands,\n} from '@maestria/shared-pi/modes-core';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst COMMANDS_DIR = resolve(__dirname, '../agents/commands');\n\nexport function installModeAutoDetect(pi: ExtensionAPI, state: MaestriaState): void {\n installAutoDetect((handler) => pi.on('input', handler as never), state, COMMANDS_DIR, {\n restoreOriginalState: (ctx) => restoreOriginalState(pi, ctx as ExtensionContext, state),\n persistState: () => persistState(pi, state),\n noMatch: { action: 'continue' as const },\n transform: (text) => ({ action: 'transform' as const, text }),\n });\n}\n\nexport function installModeCommands(pi: ExtensionAPI, state: MaestriaState): void {\n installCommands((name, opts) => pi.registerCommand(name, opts as never), state, {\n restoreOriginalState: (ctx) => restoreOriginalState(pi, ctx as ExtensionContext, state),\n persistState: () => persistState(pi, state),\n });\n}\n","import { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type {\n BeforeAgentStartEvent,\n BeforeAgentStartEventResult,\n ExtensionContext,\n} from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@/state.js';\nimport { getModePrompt } from '@maestria/shared-pi/modes-core';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst COMMANDS_DIR = resolve(__dirname, '../agents/commands');\n\n/**\n * Creates a before_agent_start handler that injects workflow mode prompts.\n *\n * This is the only dynamic prompt injection needed from the extension.\n * Static behavioral content (orchestrator prompt + global rules) is\n * auto-injected by Pi's skill system via SKILL.md files registered in\n * the pi.skills manifest field - the standard Pi extension pattern.\n *\n * When no mode is active, the handler returns void (no modification),\n * letting Pi's built-in prompt assembly (skills + context files + tools)\n * stand as-is.\n */\nexport function createModePromptHandler(state: MaestriaState) {\n return (\n event: BeforeAgentStartEvent,\n _ctx: ExtensionContext,\n ): BeforeAgentStartEventResult | void => {\n if (!state.mode) return;\n\n const parts: string[] = [\n event.systemPrompt,\n '',\n getModePrompt(state.mode, COMMANDS_DIR),\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 return { systemPrompt: parts.join('\\n') };\n };\n}\n","/**\n * Shared compaction handlers for Maestria platform packages.\n *\n * Pure TypeScript — no platform-specific dependencies.\n * Imported by both @maestria/omp and @maestria/pi to eliminate duplication.\n *\n * @module\n */\n\nimport { renderMaestriaSummary } from './state-core.js';\nimport type { MaestriaState } from './state-core.js';\n\n/**\n * Install handlers for session compaction and tree events to persist\n * and restore maestria state across session compaction boundaries.\n *\n * Uses duck-typed `pi` parameter — both Pi and OMP ExtensionAPI types\n * satisfy the `{ on(event: string, handler): void }` shape needed here.\n */\nexport function installCompactionHandlers(\n pi: {\n on: (event: string, handler: (...args: unknown[]) => unknown) => void;\n },\n state: MaestriaState,\n): void {\n pi.on('session_before_compact', (event: unknown) => {\n const prep = (event as Record<string, unknown>).preparation as\n | Record<string, unknown>\n | undefined;\n return {\n compaction: {\n summary: renderMaestriaSummary(state),\n details: { ...state },\n firstKeptEntryId: prep?.firstKeptEntryId,\n tokensBefore: prep?.tokensBefore,\n },\n };\n });\n\n pi.on('session_before_tree', (event: unknown) => {\n const prep = (event as Record<string, unknown>).preparation as\n | Record<string, unknown>\n | undefined;\n if (prep?.userWantsSummary) {\n return {\n summary: {\n summary: renderMaestriaSummary(state),\n },\n };\n }\n return undefined;\n });\n}\n","/**\n * Pi platform compaction handlers.\n *\n * Thin wrapper around the shared implementation in\n * @maestria/shared-pi/compaction-core.\n *\n * @module\n */\n\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@/state.js';\nimport { installCompactionHandlers as installHandlers } from '@maestria/shared-pi/compaction-core';\n\n/**\n * Install session compaction and tree event handlers for Pi.\n * Delegates to the shared implementation which is duck-type compatible\n * with Pi's ExtensionAPI.\n */\nexport function installCompactionHandlers(pi: ExtensionAPI, state: MaestriaState): void {\n // Bridge: ExtensionAPI.on has overloaded event types incompatible with\n // the duck-typed { on: (event: string, handler) => void } in the shared\n // module. The as-never cast is safe at runtime — both SDKs share the same\n // event shapes.\n installHandlers(\n {\n on: (event, handler) => {\n pi.on(event as never, handler as never);\n },\n },\n state,\n );\n}\n","import { Data, Effect } from 'effect';\n\n/** Terminal subagent statuses - agent will produce no more updates. */\nconst TERMINAL_STATUSES = new Set(['completed', 'steered', 'aborted', 'stopped', 'error']);\n\nexport type SubagentRecord = { status: string; result?: string; error?: string };\n\nexport type SubagentPollFailureReason = 'aborted' | 'timeout' | 'missing';\n\n/**\n * Typed failures from the polling boundary.\n *\n * Keeping these failures in the Effect error channel lets parallel polling\n * interrupt sibling fibers while preserving enough context for the existing\n * tool-level handoff fallback.\n */\nexport class SubagentPollError extends Data.TaggedError('SubagentPollError')<{\n readonly id: string;\n readonly reason: SubagentPollFailureReason;\n readonly message: string;\n}> {}\n\nexport interface SubagentPollingService {\n getRecord(id: string): SubagentRecord | undefined;\n abort?: (id: string) => boolean;\n}\n\nexport interface PollSubagentOptions {\n readonly id: string;\n readonly label: string;\n readonly sendUpdates: boolean;\n readonly service: SubagentPollingService;\n readonly signal?: AbortSignal;\n readonly onUpdate?: (result: { content: Array<{ type: string; text: string }> }) => void;\n readonly intervalMs?: number;\n readonly timeoutMs?: number;\n}\n\nfunction pollLoop(options: PollSubagentOptions): Effect.Effect<SubagentRecord, SubagentPollError> {\n const intervalMs = options.intervalMs ?? 500;\n const timeoutMs = options.timeoutMs ?? 180_000;\n const maxPolls = Math.ceil(timeoutMs / intervalMs);\n\n return Effect.gen(function* () {\n let polls = 0;\n let record = yield* Effect.sync(() => options.service.getRecord(options.id));\n\n while (record && !TERMINAL_STATUSES.has(record.status) && polls < maxPolls) {\n yield* Effect.sleep(intervalMs);\n record = yield* Effect.sync(() => options.service.getRecord(options.id));\n polls++;\n\n if (options.sendUpdates) {\n yield* Effect.sync(() => {\n options.onUpdate?.({\n content: [\n {\n type: 'text' as const,\n text: `${options.label} running... (${Math.round((polls * intervalMs) / 1000)}s)`,\n },\n ],\n });\n });\n }\n }\n\n if (record && !TERMINAL_STATUSES.has(record.status)) {\n return yield* Effect.fail(\n new SubagentPollError({\n id: options.id,\n reason: 'timeout',\n message: `Subagent ${options.id} timed out after ${timeoutMs}ms`,\n }),\n );\n }\n\n if (!record) {\n return yield* Effect.fail(\n new SubagentPollError({\n id: options.id,\n reason: 'missing',\n message: `Subagent ${options.id} was cleaned up before completion`,\n }),\n );\n }\n\n return record;\n });\n}\n\nfunction abortEffect(id: string, signal: AbortSignal): Effect.Effect<never, SubagentPollError> {\n return Effect.callback<never, SubagentPollError>((resume) => {\n const onAbort = () => {\n resume(\n Effect.fail(\n new SubagentPollError({\n id,\n reason: 'aborted',\n message: 'Maestria subagent call aborted',\n }),\n ),\n );\n };\n\n if (signal.aborted) {\n onAbort();\n return;\n }\n\n signal.addEventListener('abort', onAbort, { once: true });\n return Effect.sync(() => signal.removeEventListener('abort', onAbort));\n });\n}\n\n/**\n * Poll one subagent as a cancellable Effect.\n *\n * The host Pi API is Promise-based, so callers cross the boundary with\n * `Effect.runPromise`. Keeping the polling workflow as an Effect gives the\n * parallel dispatcher structured cancellation: a failed or aborted poll\n * interrupts its sibling poll fibers instead of leaving timers running.\n */\nexport function pollSubagentEffect(\n options: PollSubagentOptions,\n): Effect.Effect<SubagentRecord, SubagentPollError> {\n const poll = pollLoop(options);\n return options.signal ? Effect.raceFirst(poll, abortEffect(options.id, options.signal)) : poll;\n}\n","import { Type } from 'typebox';\nimport { Effect } from 'effect';\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, recordSpecialistDelegated } from '@/state.js';\nimport {\n ALLOWED_AGENTS,\n assertValidAgent,\n assertNonEmptyTask,\n MAESTRIA_EVENTS,\n} from '@maestria/shared-pi/subagent-utils';\nimport { pollSubagentEffect, type SubagentPollingService } from '@/subagent-polling.js';\n\nconst ALLOWED_AGENT_NAMES: ReadonlyArray<string> = ALLOWED_AGENTS;\n\n/** Maximum time to wait for a subagent to complete, in milliseconds. */\nexport const POLL_TIMEOUT_MS = 180_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\nfunction abortSubagents(service: SubagentPollingService, ids: readonly string[]): void {\n if (typeof service.abort !== 'function') return;\n\n for (const id of ids) {\n try {\n service.abort(id);\n } catch {\n // Best-effort cleanup: the original polling failure remains authoritative.\n }\n }\n}\n\nfunction pollSubagentOrAbortEffect(options: Parameters<typeof pollSubagentEffect>[0]) {\n return Effect.tapError(pollSubagentEffect(options), () =>\n Effect.sync(() => abortSubagents(options.service, [options.id])),\n );\n}\n\n// ── Handoff recording helper ────────────────────────────────────\n\nfunction recordAndPersist(\n pi: ExtensionAPI,\n state: MaestriaState,\n agentName: string,\n taskText: string,\n): void {\n const updatedState = recordSpecialistDelegated(\n recordHandoff(state, 'orchestrator', agentName, taskText),\n agentName,\n );\n Object.assign(state, updatedState);\n persistState(pi, state);\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.String({\n description:\n 'Specialist agent name (required): adventurer, architect, builder, diagnose, planner, reviewer, writer',\n }),\n task: Type.String({ description: 'Task description for the subagent (required)' }),\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 if (!params.agent || !ALLOWED_AGENT_NAMES.includes(params.agent)) {\n return {\n content: [\n {\n type: 'text' as const,\n text:\n `Invalid maestria_subagent call: 'agent' is required and must be one of ` +\n `${ALLOWED_AGENT_NAMES.join(', ')}. ` +\n `Re-dispatch with a valid agent name; the orchestrator may continue read-only exploration while the brief is corrected.`,\n },\n ],\n };\n }\n assertNonEmptyTask(params.task, 'Task description is required');\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 assertValidAgent(t.agent);\n assertNonEmptyTask(t.task, 'Task description is required for all tasks');\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 assertValidAgent(t.agent);\n assertNonEmptyTask(t.task, 'Task description is required for all tasks');\n }\n }\n\n // Attempt to dispatch via @gotgenes/pi-subagents; handle missing service\n const { getSubagentsService } = await import('@gotgenes/pi-subagents');\n const service = getSubagentsService();\n if (!service || typeof service.spawn !== 'function') {\n return {\n content: [\n {\n type: 'text' as const,\n text: [\n '## Subagent Dispatch Unavailable',\n '',\n 'The `@gotgenes/pi-subagents` extension is required for subagent dispatch but has not been loaded.',\n '',\n 'Install it as a Pi extension:',\n '',\n '```',\n 'pi install npm:@gotgenes/pi-subagents',\n '```',\n '',\n 'Then restart your Pi session.',\n ].join('\\n'),\n },\n ],\n };\n }\n\n try {\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 recordAndPersist(pi, state, agent, task);\n\n // Poll for completion (abort on poll failure so nothing is orphaned)\n const record = await Effect.runPromise(\n pollSubagentOrAbortEffect({\n id,\n label: `Subagent ${agent}`,\n sendUpdates: true,\n service,\n signal,\n onUpdate,\n intervalMs: POLL_INTERVAL_MS,\n timeoutMs: POLL_TIMEOUT_MS,\n }),\n );\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 recordAndPersist(pi, state, t.agent, t.task);\n }\n\n // Poll all concurrently, preserving completed results when one poll fails.\n // A failed poll aborts every sibling so no subagent is orphaned.\n const outcomes = await Effect.runPromise(\n Effect.all(\n spawnedIds.map((id, i) =>\n Effect.match(\n pollSubagentEffect({\n id,\n label: `${taskList[i].agent} (${i + 1}/${taskList.length})`,\n sendUpdates: false,\n service,\n signal,\n onUpdate,\n intervalMs: POLL_INTERVAL_MS,\n timeoutMs: POLL_TIMEOUT_MS,\n }),\n {\n onSuccess: (record) => ({ record }),\n onFailure: (error) => {\n abortSubagents(service, spawnedIds);\n return { error };\n },\n },\n ),\n ),\n { concurrency: 'unbounded' },\n ),\n );\n\n onUpdate?.({\n content: [\n {\n type: 'text' as const,\n text: `All ${taskList.length} parallel subagents settled.`,\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 outcome = outcomes[i];\n const header = `### ${i + 1}: ${t.agent}`;\n if ('error' in outcome) {\n const message =\n outcome.error instanceof Error ? outcome.error.message : String(outcome.error);\n parts.push(header);\n parts.push(`⚠️ ${message}`);\n } else {\n const resultText = outcome.record.result ?? outcome.record.error ?? 'No output.';\n parts.push(header);\n parts.push(resultText);\n }\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 recordAndPersist(pi, state, t.agent, taskText);\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 (abort on poll failure so nothing is orphaned)\n try {\n const record = await Effect.runPromise(\n pollSubagentOrAbortEffect({\n id,\n label: `Chain step ${i + 1}: ${t.agent}`,\n sendUpdates: true,\n service,\n signal,\n onUpdate,\n intervalMs: POLL_INTERVAL_MS,\n timeoutMs: POLL_TIMEOUT_MS,\n }),\n );\n previousResult = record.result ?? record.error ?? 'No output.';\n } catch (error) {\n previousResult = `[error] ${error instanceof Error ? error.message : String(error)}`;\n break;\n }\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 (err) {\n console.warn('[maestria] Subagent dispatch failed:', err);\n // Return handoff payload as structured text when dispatch fails\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 dispatch failed. 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 '@maestria/shared-pi/subagent-utils';\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('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 7 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 '**Assumptions documented:**',\n '(document assumptions made, tagged [inferred] where uncertain)',\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","/**\n * Shared tool interceptor utilities for Maestria platform packages.\n *\n * Pure TypeScript — no platform-specific dependencies.\n * Imported by both @maestria/omp and @maestria/pi to eliminate duplication.\n *\n * @module\n */\n\n/**\n * Dangerous bash command patterns that should always be blocked,\n * regardless of mode or specialist role.\n */\nexport const 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 /:(){ :\\|:& };:/,\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\n/**\n * Read-only bash command prefixes allowed for the orchestrator's recon and\n * verification. Anything not matching — or chaining into a mutation — is\n * blocked; mutations belong to specialists.\n */\nconst READ_ONLY_BASH_PREFIX =\n /^(ls|cat|head|tail|git status|git diff|git log|git branch|find|grep|rg|pnpm test|npm test|pwd|which)\\b/;\n\n/**\n * True when a bash command performs no mutation.\n *\n * A naive prefix check is bypassable — `git status && git checkout .` or\n * `ls; rm -rf dist` both pass a prefix-only match — so every segment of a\n * chained command (`;`, `&&`, `||`, `|`, or newline) must itself be\n * read-only, and command substitution (`$(...)`, backticks) and output\n * redirection (`>` / `>>`) are rejected because they can hide a mutation\n * behind a read-only prefix. `2>&1`-style fd redirects are allowed (they\n * don't write).\n */\nexport function isReadOnlyBashCommand(rawCommand: string): boolean {\n const command = rawCommand.trim();\n if (command.includes('$(') || command.includes('`')) return false;\n // Strip `2>&1`-style fd redirects first so the `&` inside them is not\n // mistaken for a command separator and the `>` is not counted as output\n // redirection.\n const withoutFdRedirects = command.replace(/\\d?>&[12]/g, '');\n if (withoutFdRedirects.includes('>')) return false;\n return withoutFdRedirects\n .split(/[\\n;&|]+/)\n .every((segment) => READ_ONLY_BASH_PREFIX.test(segment.trim()));\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';\nimport { DANGEROUS_PATTERNS, isReadOnlyBashCommand } from '@maestria/shared-pi/tools-core';\nimport { persistState, recordFileModified, recordFileRead } from '@/state.js';\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 // ── Orchestrator routing enforcement ──\n // When a maestria workflow mode is active, restrict the root session\n // (orchestrator) to read-only recon + delegation. The orchestrator may\n // read, search, and inspect to route and verify, but mutations (edit,\n // write, mutation-capable bash) belong to specialists. Subagent sessions\n // are detected by the absence of the pi-subagents 'subagent' tool\n // (stripped by applyRecursionGuard in child sessions).\n if (state.mode !== null && pi.getActiveTools().includes('subagent')) {\n const isMutation =\n isToolCallEventType('edit', event) ||\n isToolCallEventType('write', event) ||\n isToolCallEventType('patch', event) ||\n event.toolName === 'bash';\n if (isMutation) {\n // Read-only bash (ls, git status, git diff, tests) is allowed so the\n // orchestrator can verify state; mutation-capable commands still\n // belong to specialists.\n if (event.toolName === 'bash') {\n const input = event.input as { command?: unknown } | undefined;\n const command = typeof input?.command === 'string' ? input.command : '';\n if (isReadOnlyBashCommand(command)) {\n return undefined;\n }\n }\n return {\n block: true,\n reason:\n `Tool '${event.toolName}' is blocked for the orchestrator. ` +\n `Use 'maestria_subagent' to delegate mutations to specialists.`,\n };\n }\n }\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 if (!event.input || typeof event.input !== 'object') return undefined;\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 // Record file access for session state (ADR-PI-002: tool_call maintains file tracking).\n // Only record when the call is allowed to proceed.\n let tracked = false;\n if (isToolCallEventType('read', event)) {\n const path = event.input?.path;\n if (typeof path === 'string' && path) {\n Object.assign(state, recordFileRead(state, path));\n tracked = true;\n }\n } else if (isToolCallEventType('edit', event) || isToolCallEventType('write', event)) {\n const path = event.input?.path;\n if (typeof path === 'string' && path) {\n Object.assign(state, recordFileModified(state, path));\n tracked = true;\n }\n }\n if (tracked) {\n persistState(pi, state);\n }\n\n return undefined; // allow\n });\n}\n","import type {\n ExtensionAPI,\n ExtensionContext,\n SessionStartEvent,\n SessionTreeEvent,\n} from '@earendil-works/pi-coding-agent';\nimport { createInitialState } from '@/state.js';\nimport type { MaestriaState } from '@/state.js';\nimport { deploySpecialistAgents } from '@/agents.js';\nimport { installModeCommands, installModeAutoDetect } from '@/modes.js';\nimport { createModePromptHandler } from '@/rules.js';\nimport { installCompactionHandlers } from '@/compaction.js';\nimport { installSubagentTool } from '@/subagent.js';\nimport { installCommands } from '@/commands.js';\nimport { installToolInterceptors } from '@/tools.js';\n\ninterface PersistedStateEntry {\n type: string;\n customType?: string;\n data?: unknown;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction currentSessionEntries(ctx: ExtensionContext): PersistedStateEntry[] | null {\n // getBranch() is the public current-session view and avoids restoring state\n // from a sibling branch in the same session tree. Never fall back to\n // getEntries(), which spans the entire session tree.\n const sessionManager = ctx?.sessionManager;\n if (typeof sessionManager?.getBranch !== 'function') return null;\n\n const branch = sessionManager.getBranch();\n return Array.isArray(branch) ? (branch as PersistedStateEntry[]) : null;\n}\n\nfunction restoreStateFromSession(state: MaestriaState, ctx: ExtensionContext): void {\n const next = createInitialState();\n const entries = currentSessionEntries(ctx);\n\n if (!entries) {\n const mutableState = state as unknown as Record<string, unknown>;\n for (const key of Object.keys(mutableState)) delete mutableState[key];\n Object.assign(state, next);\n return;\n }\n\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 if (isRecord(entry.data)) Object.assign(next, entry.data);\n break;\n }\n }\n\n const mutableState = state as unknown as Record<string, unknown>;\n for (const key of Object.keys(mutableState)) delete mutableState[key];\n Object.assign(state, next);\n}\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 installModeAutoDetect(pi, state);\n\n // Inject mode prompt when a workflow mode is active\n const handleModePrompt = createModePromptHandler(state);\n\n pi.on('before_agent_start', (event, ctx) => {\n return handleModePrompt(event, ctx);\n });\n\n // Deploy specialist agent files for pi-subagents discovery\n pi.on('session_start', (_event: SessionStartEvent, ctx) => {\n deploySpecialistAgents(ctx);\n\n // Restore persisted state on session start (reload/resume/fork)\n restoreStateFromSession(state, ctx);\n });\n\n // Rehydrate state when navigating the session tree to a different branch\n pi.on('session_tree', (_event: SessionTreeEvent, ctx) => {\n restoreStateFromSession(state, ctx);\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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maestria/pi",
3
- "version": "0.6.5",
3
+ "version": "0.6.7",
4
4
  "description": "Maestria extension for the Pi coding agent",
5
5
  "keywords": [
6
6
  "agent-orchestration",
@@ -26,7 +26,9 @@
26
26
  "access": "public",
27
27
  "provenance": true
28
28
  },
29
- "dependencies": {},
29
+ "dependencies": {
30
+ "effect": "4.0.0-rc.108"
31
+ },
30
32
  "devDependencies": {
31
33
  "@types/node": "^26",
32
34
  "typescript": "^6.0.3",
@@ -34,7 +36,7 @@
34
36
  "@maestria/shared-pi": "0.4.0"
35
37
  },
36
38
  "peerDependencies": {
37
- "@earendil-works/pi-coding-agent": "*",
39
+ "@earendil-works/pi-coding-agent": "^0.84.1",
38
40
  "@gotgenes/pi-subagents": "^18.0.0",
39
41
  "typebox": "*"
40
42
  },
@@ -53,7 +55,6 @@
53
55
  "build": "vp pack",
54
56
  "test": "vp test",
55
57
  "lint": "vp check",
56
- "sync": "pnpm exec tsx ../core/scripts/sync.ts --config sync.config.ts",
57
58
  "validate-skills": "node --experimental-strip-types scripts/validate-skills.ts",
58
59
  "validate": "npm run validate-skills"
59
60
  }
@@ -19,7 +19,8 @@ This is the cross-platform behavior contract. It defines outcomes, evidence, saf
19
19
  `!!!` marks a non-negotiable default-path rule. Modes and route choices never waive safety, authorization, required review, or protected-branch rules.
20
20
 
21
21
  - **!!! Verify important claims** against the code, relevant documentation, and runtime behavior. Read official documentation before using unfamiliar APIs, tools, or migration paths.
22
- - **!!! Optimize for the user outcome and observable evidence.** Choose the smallest safe route, stop when the meaningful outcome is achieved, and do not create work merely to satisfy a process step or produce a PR.
22
+ - **!!! Match effort to stakes.** Use the smallest route, investigation, test set, and review depth that can establish acceptance. Escalate only when uncertainty, impact, or complexity warrants it.
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; use a small local implementation when it is simpler and lower risk. Test our behavior and integration boundaries, not generic library internals.
23
24
  - Do not avoid useful analysis or investigation by anthropomorphizing machine effort; choose approaches by technical trade-offs and evidence.
24
25
  - Audit and ship affected documentation and required changesets with code when project policy requires them.
25
26
  - **!!! Exhaust available evidence before asking.** Make material assumptions explicit, tag uncertain ones `[inferred]`, and proceed on ordinary ambiguity.
@@ -62,16 +63,18 @@ Supported specialists are `adventurer`, `architect`, `builder`, `diagnose`, `pla
62
63
  ## Bounded Repair and Fail-Loud Behavior
63
64
 
64
65
  - Ordinary in-scope repair may continue without routine user approval while it is making observable progress and remains within scope.
65
- - Set a practical repair bound, normally three rounds. Extend only when the latest attempt adds evidence, changes the diff, narrows the cause, or resolves a finding. Never silently reset the bound.
66
+ - Review is a convergence gate, not an invitation to polish indefinitely. Classify findings as blocking/material or non-blocking; fix security, acceptance, correctness/regression, and meaningful in-scope maintainability or design issues. Minor preferences and suggestions are follow-ups.
67
+ - Default to one independent review and one repair/re-review pass. Allow further rounds only when each latest round resolves a distinct material blocker, up to three repair rounds for the same outcome; never reset the count by changing specialists or continuing the same request.
66
68
  - Repeated causes, repeated findings, restored diffs, or no new evidence are non-progress. Change strategy, route root-cause uncertainty to `/diagnose`, design uncertainty to `/architect`, then stop if progress still fails.
67
69
  - Do not loop silently. Report: `Tried X, Y, Z. Blocked by [cause]. Need [input] to proceed.` Preserve the last diff and finding provenance.
68
70
 
69
71
  ## Authorization, Lifecycle, and Branches
70
72
 
71
73
  - Stop and obtain applicable authorization before security-boundary changes, authentication or permissions work, data migration or possible loss, production-impacting changes, or irreversible operations. Ordinary ambiguity is not an authorization checkpoint.
72
- - Before completion, stop background processes started for the task unless they are intentionally part of the requested result. Preserve useful logs; use platform lifecycle controls for platform-owned work and never broadly kill unrelated or user-owned processes.
73
- - Validated, independently reviewed work may be committed by the authorized executor on a recognized feature branch after inspecting and staging only the intended diff.
74
- - Never commit or push protected branches. Commit, push, PR, merge, and release are separate gates. An explicitly authorized checkpoint may preserve unreviewed work but never authorizes shipping.
74
+ - For normal repository work, branch, commit, push, and PR are part of delivery after acceptance evidence and required review. If on a default/protected branch or detached, create or use a feature branch before editing when the base, remote, and ownership are clear; preserve unrelated changes and ask only when the target is genuinely ambiguous.
75
+ - Inspect status and the intended diff, stage only intended files, and use logical conventional commits. Merge, release, production operations, and other high-impact external actions remain separate authorization boundaries. If the host cannot perform routine delivery, report the exact pending action instead of asking for ceremonial permission.
76
+ - Track task-owned long-lived processes. Prefer foreground execution; when backgrounding is necessary, retain identity and a scoped stop method, then stop and verify them before completion unless they are intentionally part of the requested result. Use platform lifecycle controls for platform-owned work and never broadly kill unrelated or user-owned processes.
77
+ - Never commit or push protected branches. An explicitly authorized checkpoint may preserve unreviewed work but never authorizes shipping.
75
78
 
76
79
  ## Canonical Source Invariant
77
80
 
@@ -23,13 +23,13 @@ Apply explicit mode precedence and safety exceptions first, then choose the smal
23
23
 
24
24
  | Route | Use when | Result |
25
25
  | --- | --- | --- |
26
- | `full` | `fein`, multiple dependent perspectives, cross-package or cross-cutting work, high risk, or uncertainty that needs design and implementation | Reconnaissance or design, implementation, and independent review as justified |
26
+ | `full` | `fein`, multiple dependent perspectives, high risk, or meaningful uncertainty that needs design and implementation | Reconnaissance or design, implementation, and independent review as justified |
27
27
  | `focused` | One specialist can own a concrete outcome, investigation, or implementation | One specialist, with independent review for meaningful builder work |
28
28
  | `direct` | The current session can safely complete known, low-risk work and the host permits it | The current session completes and verifies the work |
29
29
 
30
30
  Security, authentication, permissions, data migration or loss, production impact, irreversible changes, and unresolved safety ambiguity override `direct` and `blitz`. Use at least `focused`, or `full` when the issue is cross-cutting or high-risk. Ask only where project rules require a checkpoint.
31
31
 
32
- **!!! Check the branch** before git mutation. On an unrecognized branch, ask first. Worktrees are isolated. Never commit or push a protected branch.
32
+ **!!! Check the branch** before git mutation. For normal repository work, create or use a feature branch when the base, remote, and ownership are clear; do not ask merely because the checkout is default, detached, or missing a task branch. Worktrees are isolated. Never commit or push a protected branch.
33
33
 
34
34
  For focused builder work, review behavior, public interfaces or configuration, multiple production files, data, auth, or security changes. Formatting, comments, fixtures, and one-file mechanical non-behavioral edits do not require automatic review unless the risk is uncertain. This is a review decision, not permission to make an unreviewed commit.
35
35
 
@@ -78,7 +78,7 @@ Triage findings in this order:
78
78
  3. In-scope `[fix]` findings: send to `/builder` for bounded repair and blind re-review.
79
79
  4. Out-of-scope or platform findings: record as follow-ups. `[dismiss]` means document the rationale. `[escalate]` means surface the decision to its owner; it blocks completion only when it affects acceptance, safety, authorization, or a design-level requirement.
80
80
 
81
- Approve when no blocking finding remains and acceptance evidence is complete. Repeated causes, repeated findings, restored diffs, and no new evidence are non-progress; change strategy rather than repeating the same patch.
81
+ Approve when acceptance evidence is complete and no blocking/material finding remains. Minor preferences and suggestions do not block delivery. Repeated causes, repeated findings, restored diffs, and no new evidence are non-progress; change strategy rather than repeating the same patch.
82
82
 
83
83
  ## Workflow and Delegation
84
84
 
@@ -100,7 +100,9 @@ Modes are case-insensitive and per-turn unless the platform documents another li
100
100
 
101
101
  ## Commit and Session Flow
102
102
 
103
- After implementation and required review, the authorized executor may commit validated work on a recognized feature branch. Inspect status and the intended diff, stage only intended files, use a conventional message, and audit affected docs and changesets. Push, PR, merge, and release are separate gates. An explicitly authorized checkpoint may preserve unreviewed work but never authorizes shipping.
103
+ For normal engineering work, own the delivery path: `inspect -> plan -> implement -> validate -> review -> repair material blockers -> commit -> push -> PR`. Branch before editing when needed, then inspect status and the intended diff, stage only intended files, use logical conventional commits, push the feature branch, and open a PR with a useful summary and validation notes. Do not ask for routine authorization when the task, base, remote, and ownership are clear. Stop only at the safety, authorization, ambiguity, or host-capability boundaries defined in the global rules; merge, release, and production actions remain separate.
104
+
105
+ An explicitly authorized checkpoint may preserve unreviewed work but never authorizes shipping. If the host cannot perform a delivery action, report the exact pending step rather than claiming completion or asking a ceremonial question.
104
106
 
105
107
  1. Select the route and load relevant project rules.
106
108
  2. Complete the work directly or delegate with a concise outcome brief.