@maestria/pi 0.7.3 → 0.7.4

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.
@@ -69,7 +69,7 @@ Fix the root cause with minimal changes:
69
69
 
70
70
  Prevent similar bugs:
71
71
 
72
- - Add/update regression tests
72
+ - Consider regression tests where a durable contract or plausible recurrence justifies them (per Global Rules testing judgment)
73
73
  - Consider linting rules to catch the pattern
74
74
  - Document the lesson in a knowledge artifact for future reference
75
75
 
@@ -68,9 +68,9 @@ The initial general reviewer must give a verdict for every category. A specializ
68
68
 
69
69
  ### 7. Test Coverage
70
70
 
71
- - Are tests present for new functionality?
72
- - Do tests cover edge cases and error paths?
73
- - Are tests meaningful (not just checking implementation details)?
71
+ - Is meaningful regression risk covered proportionate to stakes (per Global Rules testing judgment)?
72
+ - Do tests cover edge cases and error paths where the contract demands it?
73
+ - Are tests meaningful (observable behavior, not implementation details)?
74
74
 
75
75
  ### 8. Assumption Validation
76
76
 
@@ -1,14 +1,12 @@
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 te(e,t){return{...e,filesRead:h(e.filesRead,t,10)}}function _(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(`
1
+ import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as r}from"node:fs";import i from"node:path";import{homedir as a}from"node:os";import{Data as o,Effect as s}from"effect";import{Type as c}from"typebox";import{SUBAGENT_EVENTS as l}from"@gotgenes/pi-subagents";import{defineTool as u}from"@earendil-works/pi-coding-agent";const d={REVIEW_ACTIVATED:`maestria:review:activated`,REVIEW_DEACTIVATED:`maestria:review:deactivated`,SUBAGENT_COMPLETED:`maestria:subagent:completed`,SUBAGENT_FAILED:`maestria:subagent:failed`,SUBAGENT_STARTED:`maestria:subagent:started`},f=[`adventurer`,`architect`,`builder`,`diagnose`,`planner`,`reviewer`,`writer`],p=e=>{if(!f.some(t=>t===e))throw Error(`Unknown agent: "${e}". Allowed: ${f.join(`, `)}`)},m=(e,t)=>{if(e==null||e===``||!e.trim())throw Error(t)},h=(a,o)=>{if(!e(a))return console.warn(`[maestria] Agents source directory not found:`,a),0;try{t(o,{recursive:!0})}catch{return console.warn(`[maestria] Could not create agents directory:`,o),0}let s=0;for(let t of f){let c=i.join(a,`${t}.md`),l=i.join(o,`${t}.md`);if(!e(c)){console.warn(`[maestria] Agent source not found: ${t}.md`);continue}if(!e(l))try{let e=n(c,`utf-8`);r(l,e,`utf-8`),s+=1}catch(e){console.warn(`[maestria] Failed to deploy agent ${t}:`,e)}}return s>0&&console.log(`[maestria] Deployed ${s} specialist agents to ${o}`),s},ee=import.meta.dirname,te=i.join(ee,`..`,`agents`),ne=e=>{h(te,i.join(a(),`.pi`,`agent`,`agents`))},g=()=>({activeTask:``,blockers:[],completionPromise:``,filesModified:[],filesRead:[],handoffHistory:[],mode:null,nativeGoal:null,originalModel:null,originalTools:null,reviewMode:!1,reviewModel:null,specialistsDelegated:[],subagentStatus:{}}),_=(e,t,n)=>[t,...e.filter(e=>e!==t)].slice(0,n),v=(e,t,n,r)=>{let i=[{from:t,task:r,timestamp:Date.now(),to:n},...e.handoffHistory].slice(0,5);return{...e,handoffHistory:i}},re=(e,t)=>({...e,filesModified:_(e.filesModified,t,10)}),ie=(e,t)=>({...e,filesRead:_(e.filesRead,t,10)}),ae=(e,t)=>e.specialistsDelegated.includes(t)?e:{...e,specialistsDelegated:[...e.specialistsDelegated,t]},oe=e=>({originalModel:e.originalModel,originalTools:e.originalTools,state:{...e,originalModel:null,originalTools:null,reviewMode:!1}}),y=(e,t)=>{e.appendEntry(`maestria_state`,{...t})},b=e=>{let t=[];if(e.mode&&t.push(`**Mode:** ${e.mode.toUpperCase()}`),e.reviewModel!==null&&e.reviewModel!==void 0&&e.reviewModel!==``&&t.push(`**Review Model:** ${e.reviewModel}`),e.activeTask&&t.push(`**Goal:** ${e.activeTask}`),e.nativeGoal&&t.push(`**Native Goal:** ${e.nativeGoal.objective} (${e.nativeGoal.status})`),e.completionPromise&&t.push(`**Completion Promise:** ${e.completionPromise}`),e.specialistsDelegated.length>0&&t.push(`**Specialists Delegated:** ${e.specialistsDelegated.join(`, `)}`),e.blockers.length>0){t.push(`**Blockers:**`);for(let n of e.blockers)t.push(`- ${n}`)}let n=[];if(e.filesModified.length>0&&n.push(`**Modified:** ${e.filesModified.join(`, `)}`),e.filesRead.length>0&&n.push(`**Read:** ${e.filesRead.join(`, `)}`),n.length>0&&t.push(`**Files:** ${n.join(`; `)}`),e.handoffHistory.length>0){t.push(`**Recent Handoffs:**`);for(let n of e.handoffHistory)t.push(`- ${n.from} → ${n.to}: ${n.task}`)}return t.join(`
2
2
 
3
- `)}async function b(e,t,n){let{state:r,originalModel:i,originalTools:a}=ne(n);if(a&&a.length>0&&await 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}}async function S(e,t,n){await b(e,t,n)}const C={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`},w=[`adventurer`,`architect`,`builder`,`diagnose`,`planner`,`reviewer`,`writer`];function T(e){if(!w.includes(e))throw Error(`Unknown agent: "${e}". Allowed: ${w.join(`, `)}`)}function E(e,t){if(!e||!e.trim())throw Error(t)}function re(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 w){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{let e=s(o,`utf-8`);c(l,e,`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 ie=i(import.meta.url),ae=t(ie),oe=n(ae,`..`,`agents`);function se(t){re(oe,n(e(),`.pi`,`agent`,`agents`))}const D=[`fein`,`sonar`,`blitz`],ce={fein:`[MODE: fein]`,sonar:`[MODE: sonar]`,blitz:`[MODE: blitz]`},O={fein:3,sonar:2,blitz:1},k=/```[\s\S]*?```|`[^`]*`/g;function le(e){let t=[],n;for(k.lastIndex=0;(n=k.exec(e))!==null;)t.push([n.index,n.index+n[0].length]);return t}function ue(e,t){return t.some(([t,n])=>e>=t&&e<n)}function A(e){let t=e.indexOf(`## MODE:`);return t===-1?e.replace(/\s+$/,``)+`
4
- `:e.slice(t).replace(/\s+$/,``)+`
5
- `}function j(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function M(e){return RegExp(`\\b${j(e)}\\b`,`gi`)}function N(e,t){if(!e)return null;let n=le(e),r=t?new Set(Array.from(t).map(e=>e.toLowerCase())):void 0,i=null;for(let t of D){if(r?.has(t))continue;let a=M(t),o;for(;(o=a.exec(e))!==null;)ue(o.index,n)||(i===null||O[t]>O[i.mode])&&(i={mode:t,keyword:o[0],index:o.index})}return i}function P(e,t){return(e.slice(0,t.index)+e.slice(t.index+t.keyword.length).replace(/^:\s*/,``)).replace(/ {2,}/g,` `).trim()}const F=D,I=ce,L={};function R(e,t){return A(s(r(t,`${e}.md`),`utf-8`))}function z(e,t){if(!(e in L))try{L[e]=R(e,t)}catch(t){console.warn(`[maestria] Failed to load mode prompt "${e}":`,t),L[e]=``}return`${I[e]}\n\n${L[e]}`}function B(e,t,n){if(!e)return null;let r=N(e,n);if(r===null)return null;let i=P(e,r);return{keyword:r.mode,strippedText:i,prompt:z(r.mode,t)}}function V(e,t){return t?`${e}\n\n${t}`:e}function H(e,t,n,r){e(async(e,i)=>{let a=B(e.text??``,n);return a?(t.reviewMode&&await r.restoreOriginalState(i),t.mode=a.keyword,r.persistState(),r.transform(V(a.prompt,a.strippedText))):r.noMatch})}function U(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 F)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 de=t(i(import.meta.url)),fe=r(de,`../agents/commands`);function pe(e,t){H(t=>e.on(`input`,t),t,fe,{restoreOriginalState:n=>S(e,n,t),persistState:()=>v(e,t),noMatch:{action:`continue`},transform:e=>({action:`transform`,text:e})})}function me(e,t){U((t,n)=>e.registerCommand(t,n),t,{restoreOriginalState:n=>S(e,n,t),persistState:()=>v(e,t)})}const he=t(i(import.meta.url)),ge=r(he,`../agents/commands`);function _e(e){return(t,n)=>{if(e.mode)return{systemPrompt:[t.systemPrompt,``,z(e.mode,ge),``,`The user has set workflow mode to "${e.mode}". Honor this mode throughout the session until changed via /command.`].join(`
6
- `)}}}function ve(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 ye(e,t){ve({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 be(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 xe(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 K(e){let t=be(e);return e.signal?d.raceFirst(t,xe(e.id,e.signal)):t}const q=w,J=18e4;function Y(e,t){if(typeof e.abort==`function`)for(let n of t)try{e.abort(n)}catch{}}function X(e){return d.tapError(K(e),()=>d.sync(()=>Y(e.service,[e.id])))}function Z(e,t,n,r){let i=_(g(t,`orchestrator`,n,r),n);Object.assign(t,i),v(e,t)}function Se(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.`}]};E(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)T(e.agent),E(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)T(e.agent),E(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});Z(e,t,n,o);let c=await d.runPromise(X({id:s,label:`Subagent ${n}`,sendUpdates:!0,service:l,signal:i,onUpdate:a,intervalMs:500,timeoutMs:J}));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=[];try{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),Z(e,t,r.agent,r.task)}}catch(e){throw Y(l,o),e}let s=await d.runPromise(d.all(o.map((e,t)=>d.match(K({id:e,label:`${n[t].agent} (${t+1}/${n.length})`,sendUpdates:!1,service:l,signal:i,onUpdate:a,intervalMs:500,timeoutMs:J}),{onSuccess:e=>({record:e}),onFailure:e=>(Y(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
-
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});Z(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(X({id:u,label:`Chain step ${r+1}: ${s.agent}`,sendUpdates:!0,service:l,signal:i,onUpdate:a,intervalMs:500,timeoutMs:J}));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(C.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(C.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(C.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 Ce=[`read`,`grep`,`find`,`ls`,`glob`];function we(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(C.REVIEW_ACTIVATED,{originalModel:t.originalModel,reviewModel:n,timestamp:Date.now()}))}e.setActiveTools(Ce),e.sendUserMessage([`[REVIEW: ${n}]`,``,`Review: ${n}. Use the reviewer prompt template.`,`Read only, no edits, report findings.`].join(`
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(C.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(`
3
+ `)},x=async(e,t,n)=>{let{state:r,originalModel:i,originalTools:a}=oe(n);if(a&&a.length>0&&await e.setActiveTools(a),i!=null&&i!==``)try{let n=t.modelRegistry.getAll().find(e=>e.id===i);n&&await e.setModel(n)}catch{}Object.assign(n,r)},se=async(e,t,n)=>{let{reviewModel:r}=n;if(r==null||r===``)return null;try{let n=t.modelRegistry.getAll().find(e=>e.id===r);return n?(await e.setModel(n),r):(t.ui.notify(`Review model "${r}" not found in registry, staying on current.`),null)}catch{return t.ui.notify(`Could not switch to review model "${r}", staying on current.`),null}},ce=[`read`,`grep`,`find`,`ls`,`glob`],le=(e,t)=>{e.registerCommand(`maestria-status`,{description:`Show current maestria session state including handoff history`,handler:(e,n)=>{let r=b(t);if(!r){n.ui.notify(`No active maestria state to report.`);return}n.ui.setEditorText(r)}})},ue=(e,t)=>{e.registerCommand(`review`,{description:`Enter review mode. Blocks destructive tools, sets read-only toolset.`,handler:async(n,r)=>{if(!n.trim()){r.ui.notify(`Usage: /review <target> - describe what to review`);return}let i=r.model?.id??null,a=e.getActiveTools();if(Object.assign(t,{...t,originalModel:i,originalTools:a,reviewMode:!0}),y(e,t),t.reviewModel!==null&&t.reviewModel!==void 0&&t.reviewModel!==``){let n=await se(e,r,t);n!=null&&n!==``&&(r.ui.notify(`Review mode: switched to ${n}`),e.events?.emit(d.REVIEW_ACTIVATED,{originalModel:t.originalModel,reviewModel:n,timestamp:Date.now()}))}e.setActiveTools(ce),e.sendUserMessage([`[REVIEW: ${n}]`,``,`Review: ${n}. Use the reviewer prompt template.`,`Read only, no edits, report findings.`].join(`
4
+ `),{deliverAs:`steer`})}})},de=(e,t)=>{e.registerCommand(`restore-model`,{description:`Restore the original model and tools that were active before review mode was entered.`,handler:async(n,r)=>{if(!t.reviewMode){r.ui.notify(`Not in review mode. Nothing to restore.`);return}let i=t.originalModel;await x(e,r,t),y(e,t),r.ui.notify(`Restored original model and tools.`),e.events?.emit(d.REVIEW_DEACTIVATED,{originalModel:i,timestamp:Date.now()})}})},fe=(e,t)=>{e.registerCommand(`handoff`,{description:`Generate a structured handoff prompt for a new task context`,handler:(n,r)=>{if(!n.trim()){r.ui.notify(`Usage: /handoff <goal> - describe the task context for handoff`);return}let i=n.trim(),a=[`**Goal:** ${i}`,``,`**Context:**`,`- Mode: ${t.mode??`none`}`,`- Active task: ${t.activeTask||`none`}`,`- Specialists delegated: ${(t.specialistsDelegated?.length??0)>0?t.specialistsDelegated.join(`, `):`none`}`,`- Recent handoffs: ${t.handoffHistory?.length??0} entries`,`- Files modified: ${(t.filesModified?.length??0)>0?t.filesModified.join(`, `):`none`}`,``,`**Requirements:**`,`(fill in specific requirements)`,``,`**Known problems:**`,(t.blockers?.length??0)>0?t.blockers.map(e=>`- ${e}`).join(`
12
5
  `):`(no known problems documented)`,``,`**Assumptions documented:**`,`(document assumptions made, tagged [inferred] where uncertain)`,``,`**Success criteria:**`,`(fill in how to verify completion)`,``,`**Next step:**`,`(fill in what happens after this task)`,``,`---`,`Complete the fields above before sending.`].join(`
13
- `);Object.assign(t,g(t,`current`,`next`,i)),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}`)}})}function Q(e,t){we(e,t)}const Te=[/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/],Ee=/^(ls|cat|head|tail|git status|git diff|git log|git branch|find|grep|rg|pnpm test|npm test|pwd|which)\b/;function De(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=>Ee.test(e.trim()))}function Oe(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(De(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 Te)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,te(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 ke(e){return typeof e==`object`&&!!e}function Ae(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=Ae(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`){ke(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 je(e){let t=m(),n=[];me(e,t),pe(e,t);let r=_e(t);e.on(`before_agent_start`,(e,t)=>r(e,t)),e.on(`session_start`,(e,n)=>{se(n),$(t,n)}),e.on(`session_tree`,(e,n)=>{$(t,n)}),ye(e,t),Se(e,t,n),Q(e,t),e.on(`session_shutdown`,()=>{for(let e of n)e();n.length=0}),Oe(e,t)}export{je as default};
6
+ `);Object.assign(t,v(t,`current`,`next`,i)),y(e,t),e.sendUserMessage(a,{deliverAs:`steer`})}})},pe=(e,t)=>{e.registerCommand(`review-model`,{description:`Set which model to use when entering review mode`,handler:(n,r)=>{if(!n.trim()){r.ui.notify(`Usage: /review-model <model-id>`);return}let i=n.trim(),a=r.modelRegistry.getAll();if(!a.find(e=>e.id===i)){r.ui.notify(`Unknown model: "${i}". Available: ${a.map(e=>e.id).join(`, `)}`);return}t.reviewModel=i,y(e,t),r.ui.notify(`Review model set to: ${i}`)}})},me=(e,t)=>{le(e,t),ue(e,t),de(e,t),fe(e,t),pe(e,t)},he=e=>typeof e==`object`&&!!e,ge=e=>he(e)?typeof e.api==`string`&&typeof e.baseUrl==`string`&&typeof e.contextWindow==`number`&&typeof e.cost==`object`&&e.cost!==null&&typeof e.id==`string`&&Array.isArray(e.input)&&typeof e.maxTokens==`number`&&typeof e.name==`string`&&typeof e.provider==`string`&&typeof e.reasoning==`boolean`:!1,_e=e=>({appendEntry:(t,n)=>{e.appendEntry(t,n)},events:e.events,getActiveTools:()=>e.getActiveTools(),registerCommand:(t,n)=>{e.registerCommand(t,{description:n.description,handler:async(e,t)=>{await n.handler(e,t)}})},sendUserMessage:(t,n)=>{(n.deliverAs===`steer`||n.deliverAs===`followUp`)&&e.sendUserMessage(t,{deliverAs:n.deliverAs})},setActiveTools:t=>{e.setActiveTools(t)},setModel:async t=>{ge(t)&&await e.setModel(t)}}),ve=(e,t)=>{me(e,t)},ye=(e,t)=>{e.on(`session_before_compact`,e=>{let n=e.preparation;return{compaction:{details:{...t},firstKeptEntryId:n?.firstKeptEntryId??``,summary:b(t),tokensBefore:n?.tokensBefore??0}}}),e.on(`session_before_tree`,e=>e.preparation?.userWantsSummary===!0?{summary:{summary:b(t)}}:void 0)},S=e=>({on:(...t)=>{let[n,r]=t;n===`session_before_compact`&&e.on(`session_before_compact`,e=>r(e)),n===`session_before_tree`&&e.on(`session_before_tree`,e=>r(e))}}),C=(e,t)=>{ye(e,t)},w=[`fein`,`sonar`,`blitz`],T={blitz:`[MODE: blitz]`,fein:`[MODE: fein]`,sonar:`[MODE: sonar]`},E={blitz:1,fein:3,sonar:2},D=/```[\s\S]*?```|`[^`]*`/gu,O=e=>{let t=[],n;for(D.lastIndex=0;(n=D.exec(e))!==null;)t.push([n.index,n.index+n[0].length]);return t},k=(e,t)=>t.some(([t,n])=>e>=t&&e<n),A=e=>{let t=e.indexOf(`## MODE:`);return t===-1?`${e.replace(/\s+$/u,``)}\n`:`${e.slice(t).replace(/\s+$/u,``)}\n`},j=e=>e.replaceAll(/[.*+?^${}()|[\]\\]/gu,`\\$&`),M=e=>RegExp(`\\b${j(e)}\\b`,`giu`),N=(e,t)=>{if(!e)return null;let n=O(e),r=t?new Set([...t].map(e=>e.toLowerCase())):void 0,i=null;for(let t of w){if(r!=null&&r.has(t))continue;let a=M(t),o;for(;(o=a.exec(e))!==null;)k(o.index,n)||(i===null||E[t]>E[i.mode])&&(i={index:o.index,keyword:o[0],mode:t})}return i},P=(e,t)=>(e.slice(0,t.index)+e.slice(t.index+t.keyword.length).replace(/^:\s*/u,``)).replaceAll(/ {2,}/gu,` `).trim(),be=w,xe=T,F=new Map,Se=(e,t)=>{let r=n(i.resolve(t,`${e}.md`),`utf-8`);return A(r)},I=(e,t)=>{let n=`${i.resolve(t)}\0${e}`;if(!F.has(n))try{F.set(n,Se(e,t))}catch(t){console.warn(`[maestria] Failed to load mode prompt "${e}":`,t),F.set(n,``)}return`${xe[e]}\n\n${F.get(n)??``}`},Ce=(e,t,n)=>{if(!e)return null;let r=N(e,n);if(r===null)return null;let i=P(e,r);return{keyword:r.mode,prompt:I(r.mode,t),strippedText:i}},we=(e,t)=>t?`${e}\n\n${t}`:e,Te=(e,t,n,r)=>{e(async(e,i)=>{let{text:a}=e,o=Ce(a,n);return o?(t.reviewMode&&await r.restoreOriginalState(i),t.mode=o.keyword,r.persistState(),r.transform(we(o.prompt,o.strippedText))):r.noMatch})},Ee=(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 be)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.`)}})},De=e=>typeof e==`object`&&!!e,Oe=e=>De(e)?typeof e.api==`string`&&typeof e.baseUrl==`string`&&typeof e.contextWindow==`number`&&typeof e.cost==`object`&&e.cost!==null&&typeof e.id==`string`&&Array.isArray(e.input)&&typeof e.maxTokens==`number`&&typeof e.name==`string`&&typeof e.provider==`string`&&typeof e.reasoning==`boolean`:!1,L=async(e,t,n)=>{await x({setActiveTools:t=>{e.setActiveTools(t)},setModel:async t=>{Oe(t)&&await e.setModel(t)}},t,n)},ke=import.meta.dirname,Ae=i.resolve(ke,`../agents/commands`),je=(e,t)=>{Te(t=>{e.on(`input`,t)},t,Ae,{noMatch:{action:`continue`},persistState:()=>{y(e,t)},restoreOriginalState:async n=>{await L(e,n,t)},transform:e=>({action:`transform`,text:e})})},Me=e=>typeof e!=`object`||!e?!1:`modelRegistry`in e&&`ui`in e,Ne=e=>({appendEntry:(t,n)=>{e.appendEntry(t,n)},registerCommand:(t,n)=>{e.registerCommand(t,{description:n.description,handler:async(e,t)=>{await n.handler(e,t)}})},setActiveTools:t=>{e.setActiveTools(t)},setModel:async t=>await e.setModel(t)}),Pe=(e,t)=>{Ee((t,n)=>{e.registerCommand(t,{description:n.description,handler:async(e,t)=>{await n.handler(e,t)}})},t,{persistState:()=>{y(e,t)},restoreOriginalState:async n=>{Me(n)&&await L(e,n,t)}})},Fe=import.meta.dirname,Ie=i.resolve(Fe,`../agents/commands`),Le=e=>(t,n)=>e.mode===null?{}:{systemPrompt:[t.systemPrompt,``,I(e.mode,Ie),``,`The user has set workflow mode to "${e.mode}". Honor this mode throughout the session until changed via /command.`].join(`
7
+ `)},R=new Set([`completed`,`steered`,`aborted`,`stopped`,`error`]);var z=class extends o.TaggedError(`SubagentPollError`){};const Re=(e,t)=>s.sync(()=>{e.onUpdate?.({content:[{text:`${e.label} running... (${Math.round(t*(e.intervalMs??500)/1e3)}s)`,type:`text`}]})}),ze=e=>{let t=e.intervalMs??500,n=e.timeoutMs??18e4,r=Math.ceil(n/t);return s.gen(function*(){let i=0,a=yield*s.sync(()=>e.service.getRecord(e.id));for(;a&&!R.has(a.status)&&i<r;)yield*s.sleep(t),a=yield*s.sync(()=>e.service.getRecord(e.id)),i+=1,e.sendUpdates&&(yield*Re(e,i));return a&&!R.has(a.status)?yield*s.fail(new z({id:e.id,message:`Subagent ${e.id} timed out after ${n}ms`,reason:`timeout`})):a||(yield*s.fail(new z({id:e.id,message:`Subagent ${e.id} was cleaned up before completion`,reason:`missing`})))})},Be=(e,t)=>s.callback(n=>{let r=()=>{n(s.fail(new z({id:e,message:`Maestria subagent call aborted`,reason:`aborted`})))};return t.aborted?r():t.addEventListener(`abort`,r,{once:!0}),s.sync(()=>{t.removeEventListener(`abort`,r)})}),B=e=>{let t=ze(e);return e.signal?s.raceFirst(t,Be(e.id,e.signal)):t},Ve=e=>typeof e==`object`&&!!e,V=(e,t)=>{if(!Ve(e))return;let n=e[t];return typeof n==`string`?n:void 0},He=(e,t,n)=>e.on(l.STARTED,r=>{let i=V(r,`id`),a=V(r,`type`)??`unknown`;i!==void 0&&(n.subagentStatus[i]={startedAt:Date.now(),status:`running`,type:a},y(t,n),e.emit(d.SUBAGENT_STARTED,{id:i,timestamp:Date.now(),type:a}))}),Ue=(e,t,n)=>e.on(l.COMPLETED,r=>{let i=V(r,`id`);if(i===void 0)return;let a=n.subagentStatus[i];a!=null&&(a.status=`completed`,a.completedAt=Date.now()),y(t,n),e.emit(d.SUBAGENT_COMPLETED,{id:i,timestamp:Date.now(),type:a?.type})}),We=(e,t,n)=>e.on(l.FAILED,r=>{let i=V(r,`id`);if(i===void 0)return;let a=V(r,`status`)??`error`,o=n.subagentStatus[i];o!=null&&(o.status=a,o.completedAt=Date.now()),y(t,n),e.emit(d.SUBAGENT_FAILED,{id:i,timestamp:Date.now(),type:o?.type})}),Ge=(e,t,n)=>e.on(l.STEERED,e=>{let r=V(e,`id`);r!==void 0&&(n.subagentStatus[r]??={startedAt:Date.now(),status:`running`,type:`unknown`},y(t,n))}),Ke=(e,t,n)=>{let{events:r}=e;if(r==null)return;let i=[He(r,e,t),Ue(r,e,t),We(r,e,t),Ge(r,e,t)];n?.push(...i)},H=f,U=18e4,W=m,G=p,K=(e,t)=>{if(typeof e.abort==`function`)for(let n of t)try{e.abort(n)}catch{}},q=e=>s.tapError(B(e),()=>s.sync(()=>{K(e.service,[e.id])})),J=(e,t,n,r)=>{let i=ae(v(t,`orchestrator`,n,r),n);Object.assign(t,i),y(e,t)},qe=e=>{let t=e.mode??`single`;if(t===`single`){if(e.agent===void 0||e.agent===null||e.agent===``||!H.includes(e.agent))return`Invalid maestria_subagent call: 'agent' is required and must be one of ${H.join(`, `)}.`;W(e.task,`Task description is required`)}else if(t===`parallel`){if(!e.tasks||e.tasks.length<2)throw Error(`For parallel mode, tasks array is required with at least 2 items`);if(e.tasks.length>8)throw Error(`For parallel mode, tasks array may have at most 8 items (got ${e.tasks.length})`);for(let t of e.tasks)G(t.agent),W(t.task,`Task description is required for all tasks`)}else if(t===`chain`){if(!e.tasks||e.tasks.length<2)throw Error(`For chain mode, tasks array is required with at least 2 items`);for(let t of e.tasks)G(t.agent),W(t.task,`Task description is required for all tasks`)}return t},Je=async(e,t,n,r,i,a,o)=>{let c=n.spawn(r,i,{description:i.slice(0,80),foreground:!0,inheritContext:!0});J(e,t,r,i);let l=await s.runPromise(q({id:c,intervalMs:500,label:`Subagent ${r}`,onUpdate:o,sendUpdates:!0,service:n,signal:a,timeoutMs:U}));return{content:[{text:l.result??l.error??`No output.`,type:`text`}],details:{subagentId:c}}},Ye=(e,t,n,r)=>{let i=[];try{for(let a of r){let r=n.spawn(a.agent,a.task,{description:a.task.slice(0,80),foreground:!0,inheritContext:!0});i.push(r),J(e,t,a.agent,a.task)}}catch(e){throw K(n,i),e}return i},Xe=async(e,t,n,r,i)=>await s.runPromise(s.all(e.map((a,o)=>{let c=t[o];return s.match(B({id:a,intervalMs:500,label:`${c.agent} (${o+1}/${t.length})`,onUpdate:i,sendUpdates:!1,service:n,signal:r,timeoutMs:U}),{onFailure:t=>(K(n,e),{error:t}),onSuccess:e=>({record:e})})}),{concurrency:`unbounded`})),Ze=(e,t,n)=>{let r=[`## Parallel Results (${e.length} tasks)\n`];for(let[t,i]of e.entries()){let e=n[t];r.push(`### ${t+1}: ${i.agent}`),`error`in e?r.push(`⚠️ ${e.error instanceof Error?e.error.message:String(e.error)}`):r.push(e.record.result??e.record.error??`No output.`)}return{content:[{text:r.join(`
8
+
9
+ `),type:`text`}],details:{subagentIds:t}}},Qe=async(e,t,n,r,i,a)=>{a?.({content:[{text:`Spawning ${r.length} parallel subagents...`,type:`text`}]});let o=Ye(e,t,n,r),s=await Xe(o,r,n,i,a);return a?.({content:[{text:`All ${r.length} parallel subagents settled.`,type:`text`}]}),Ze(r,o,s)},$e=(e,t)=>e.replaceAll(`{previous}`,()=>t),Y=async(e,t,n,r,i,a,o,c)=>{if(o>=r.length)return c;let l=r[o],u=o>0?$e(l.task,c):l.task,d=n.spawn(l.agent,u,{description:u.slice(0,80),foreground:!0,inheritContext:!0});J(e,t,l.agent,u),a?.({content:[{text:`Chain step ${o+1}/${r.length}: ${l.agent} running...`,type:`text`}]});let f;try{let e=await s.runPromise(q({id:d,intervalMs:500,label:`Chain step ${o+1}: ${l.agent}`,onUpdate:a,sendUpdates:!0,service:n,signal:i,timeoutMs:U}));f=e.result??e.error??`No output.`}catch(e){return`[error] ${e instanceof Error?e.message:String(e)}`}return o<r.length-1&&a?.({content:[{text:`Chain step ${o+1}/${r.length}: ${l.agent} completed. Moving to next step.`,type:`text`}]}),await Y(e,t,n,r,i,a,o+1,f)},et=async(e,t,n,r,i,a)=>({content:[{text:await Y(e,t,n,r,i,a,0,``),type:`text`}],details:{subagentId:`chain-completed`}}),tt=async(e,t,n,r,i,a)=>{let o=r.mode??`single`;if(o===`single`){if(typeof r.agent!=`string`||typeof r.task!=`string`)throw TypeError(`Single mode requires an agent and task`);return await Je(e,t,n,r.agent,r.task,i,a)}if((o===`parallel`||o===`chain`)&&Array.isArray(r.tasks))return await(o===`parallel`?Qe(e,t,n,r.tasks,i,a):et(e,t,n,r.tasks,i,a));throw Error(`Unknown dispatch mode`)},nt=c.Object({agent:c.String({description:`Specialist agent name (required): adventurer, architect, builder, diagnose, planner, reviewer, writer`}),mode:c.Optional(c.Union([c.Literal(`parallel`),c.Literal(`chain`),c.Literal(`single`)])),task:c.String({description:`Task description for the subagent (required)`}),tasks:c.Optional(c.Array(c.Object({agent:c.String(),task:c.String()}),{description:`Array of task objects for parallel or chain dispatch`}))}),rt=()=>({content:[{text:[`## Subagent Dispatch Unavailable`,``,"The `@gotgenes/pi-subagents` extension is required for subagent dispatch but has not been loaded.",``,`Install it as a Pi extension:`,``,"```",`pi install npm:@gotgenes/pi-subagents`,"```",``,`Then restart your Pi session.`].join(`
10
+ `),type:`text`}],details:{}}),it=(e,t)=>({content:[{text:[`## Subagent Handoff Required`,``,`**From:** orchestrator`,`**To:** ${e}`,`**Task:** ${t}`,``,`Subagent dispatch failed. Please delegate this work manually.`].join(`
11
+ `),type:`text`}],details:{}}),at=async(e,t,n,r,i)=>{let{getSubagentsService:a}=await import(`@gotgenes/pi-subagents`),o=a();if(!o||typeof o.spawn!=`function`)return rt();try{return await tt(e,t,o,n,r,i)}catch(e){console.warn(`[maestria] Subagent dispatch failed:`,e);let t=n.agent??n.tasks?.[0]?.agent??`unknown`,r=n.task??n.tasks?.map(e=>e.task).join(`; `)??`unknown`;return it(t,r)}},ot=(e,t)=>({description:`Dispatch a task to a @gotgenes/pi-subagents specialist subagent`,async execute(n,r,i,a,o){if(t.reviewMode)return{content:[{text:`Subagent dispatch is not available during review mode. Use /restore-model to exit review mode first.`,type:`text`}],details:{}};let s=qe(r);return s.startsWith(`Invalid`)?{content:[{text:`${s} Re-dispatch with a valid agent name; the orchestrator may continue read-only exploration while the brief is corrected.`,type:`text`}],details:{}}:await at(e,t,r,i,a?e=>{a({content:e.content.map(({text:e})=>({text:e,type:`text`})),details:e.details??{}})}:void 0)},label:`Maestria Subagent`,name:`maestria_subagent`,parameters:nt,promptGuidelines:[`Use maestria_subagent when a task MUST be delegated to a specialist subagent rather than handled directly. Each specialist has focused capabilities: adventurer (recon), architect (design), builder (impl), planner (planning), diagnose (bugs), reviewer (QA), writer (docs).`],promptSnippet:`Delegate tasks to @maestria specialist subagents (adventurer, architect, builder, planner, diagnose, reviewer, writer)`}),st=(e,t,n)=>{e.registerTool(ot(e,t)),Ke(e,t,n)},ct=e=>({appendEntry:(t,n)=>{e.appendEntry(t,n)},events:e.events,registerTool:t=>{e.registerTool(u({...t,async execute(e,n,r,i,a){return await t.execute(e,n,r,i,a)}}))}}),lt=[/rm\s+-rf\s+\//u,/dd\s+if=/u,/>\s*\/dev\/sd/u,/chmod\s+-R\s+777\s+\//u,/mkfs\.\w+/u,/:\(\)\{ :\|:& \};:/u,/>\s*\/etc\/(?<targetFile>passwd|shadow|sudoers)/u,/\beval\b/u,/wget\s+-O\s*-\s*\|\s*(?<wgetShell>bash|sh)/u,/curl\s+.*\|\s*(?<curlShell>bash|sh)/u,/crontab\s+-r/u],ut=/^(?<command>ls|cat|head|tail|git status|git diff|git log|git branch|find|grep|rg|pnpm test|npm test|pwd|which)\b/u,dt=e=>{let t=e.trim();if(t.includes(`$(`)||t.includes("`"))return!1;let n=t.replaceAll(/\d?>&[12]/gu,``);return!n.includes(`>`)&&n.split(/[\n;&|]+/u).every(e=>ut.test(e.trim()))},ft=e=>{for(let t of lt)if(t.test(e))return t;return null},pt=e=>e===`edit`||e===`write`||e===`bash`?`Review mode is active. Report findings, do not edit.`:null,X=e=>typeof e==`object`&&!!e,mt=(e,t)=>t??(e===`task`?`Use 'maestria_subagent' or 'task()' to delegate mutations to specialists.`:`Use 'maestria_subagent' to delegate mutations to specialists.`),Z=(e,t,n,r,i)=>{if(!(e.mode===null||!n.getActiveTools().some(e=>e===r))&&!(!(n.isMutationTool??(e=>{let t=e.toolName??``;return t===`edit`||t===`write`||t===`patch`||t===`bash`||n.extraMutations?.some(e=>e===t)===!0}))(t)||t.toolName===r)){if(n.isBashTool(t)){let e=X(t.input)?t.input:void 0,n=typeof e?.command==`string`?e.command:``;if(dt(n))return}return{block:!0,reason:`Tool '${t.toolName}' is blocked for the orchestrator. ${i}`}}},ht=async(e,t,n)=>{if(!t.isBashTool(e))return;let r=X(e.input)?e.input:void 0;if(!r)return;let{command:i}=r;if(typeof i!=`string`||!i)return;let a=ft(i);if(a&&!(n.hasUI===!0&&n.ui&&await n.ui.confirm(`Dangerous Pattern Detected`,`This command matches a dangerous pattern:\n${i}\nProceed?`)))return{block:!0,reason:`Command matches dangerous pattern: ${a}`}},gt=(e,t,n)=>{if(n.isReadTool(t)){let n=X(t.input)?t.input.path:void 0;if(typeof n==`string`&&n)return Object.assign(e,ie(e,n)),!0}else if(n.isWriteTool(t)){let n=X(t.input)?t.input.path:void 0;if(typeof n==`string`&&n)return Object.assign(e,re(e,n)),!0}return!1},_t=e=>{let{delegationTool:t}=e,n=mt(t,e.delegationHint),r=()=>{if(e.persist){e.persist();return}e.pi&&y(e.pi,e.getState())};return async(i,a)=>{if(i.toolName===void 0||i.toolName===``)return;let o=e.getState(),{toolName:s}=i,c=Z(o,i,e,t,n);if(c)return c;if(o.reviewMode&&(e.isWriteTool(i)||e.isBashTool(i)))return{block:!0,reason:pt(s)??`Review mode is active. Report findings, do not edit.`};let l=await ht(i,e,a);if(l)return l;gt(o,i,e)&&r()}},vt=e=>({appendEntry:(t,n)=>{e.appendEntry(t,n)},getActiveTools:()=>e.getActiveTools(),on:(t,n)=>{e.on(`tool_call`,async(e,t)=>await n(e,t))}}),yt=(e,t)=>{let n=_t({delegationTool:`subagent`,getActiveTools:()=>e.getActiveTools(),getState:()=>t,isBashTool:e=>e.toolName===`bash`,isMutationTool:e=>e.toolName===`edit`||e.toolName===`write`||e.toolName===`patch`||e.toolName===`bash`,isReadTool:e=>e.toolName===`read`,isWriteTool:e=>e.toolName===`edit`||e.toolName===`write`,persist:()=>{y(e,t)}});e.on(`tool_call`,n)},bt=e=>typeof e==`object`&&!!e,xt=e=>{let t=e?.sessionManager;if(typeof t?.getBranch!=`function`)return null;let n=t.getBranch();return Array.isArray(n)?n:null},Q=e=>{for(let t of Object.keys(e))Reflect.deleteProperty(e,t)},$=(e,t)=>{let n=g(),r=xt(t);if(!r){Q(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`){bt(t.data)&&Object.assign(n,t.data);break}}Q(e),Object.assign(e,n)},St=e=>{let t=g(),n=[];Pe(Ne(e),t),je(e,t);let r=Le(t);e.on(`before_agent_start`,(e,t)=>r(e,t)),e.on(`session_start`,(e,n)=>{ne(n),$(t,n)}),e.on(`session_tree`,(e,n)=>{$(t,n)}),C(S(e),t),st(ct(e),t,n),ve(_e(e),t),e.on(`session_shutdown`,()=>{for(let e of n)e();n.length=0}),yt(vt(e),t)};export{St as default};
14
12
  //# sourceMappingURL=extension.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"extension.mjs","names":["restoreOriginalState","restoreCore","deploySpecialistAgents","__dirname","MODE_KEYWORDS","MODE_MARKERS","SHARED_KEYWORDS","SHARED_MARKERS","sharedDetectMode","sharedStripKeyword","installModeAutoDetect","installModeCommands","__dirname","COMMANDS_DIR","installCompactionHandlers","installCommands","restoreOriginalState"],"sources":["../../shared/pi/src/state-core.ts","../../shared/pi/src/review-core.ts","../src/state/review.ts","../../shared/pi/src/subagent-utils.ts","../../shared/pi/src/agent-deployment.ts","../src/agents.ts","../../shared/mode/src/index.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","../../shared/pi/src/commands-core.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","/**\n * Shared review-mode orchestration for Maestria platform packages.\n *\n * Pure TypeScript - no platform-specific dependencies.\n * Consumed by both @maestria/omp and @maestria/pi to eliminate duplication\n * of review-mode model/tool restoration and switching.\n *\n * @module\n */\n\nimport type { MaestriaState } from './state-core.js';\nimport { exitReviewMode } from './state-core.js';\n\n// ── Duck-typed platform interfaces ──\n\ninterface ReviewPi {\n setActiveTools(tools: string[]): void | Promise<void>;\n setModel(model: unknown): void | Promise<void>;\n}\n\ninterface ReviewCtx {\n modelRegistry: { getAll(): Array<{ id: string }> };\n ui: { notify(msg: string): void };\n}\n\nexport async function restoreOriginalState(\n pi: ReviewPi,\n ctx: ReviewCtx,\n state: MaestriaState,\n): Promise<void> {\n const { state: clearedState, originalModel, originalTools } = exitReviewMode(state);\n\n if (originalTools && originalTools.length > 0) {\n await 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: ReviewPi,\n ctx: ReviewCtx,\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","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 {\n cycleToReviewModel as cycleCore,\n restoreOriginalState as restoreCore,\n} from '@maestria/shared-pi/review-core';\n\nexport async function restoreOriginalState(\n pi: ExtensionAPI,\n ctx: ExtensionContext,\n state: MaestriaState,\n): Promise<void> {\n await restoreCore(pi as never, ctx as never, state);\n}\n\nexport async function cycleToReviewModel(\n pi: ExtensionAPI,\n ctx: ExtensionCommandContext,\n state: MaestriaState,\n): Promise<string | null> {\n return cycleCore(pi as never, ctx as never, state);\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 * Private runtime-neutral shared mode mechanics.\n *\n * Pure TypeScript - no host SDKs, no filesystem APIs.\n * Consolidates the duplicated pure logic from:\n * - `packages/opencode/src/modes/index.ts`\n * - `packages/shared/pi/src/modes-core.ts`\n *\n * Consumers (OpenCode, shared-pi) delegate pure detection,\n * stripping, and mode-section extraction to this module while\n * keeping host-specific concerns (lazy file loading, session\n * state, command registration) locally.\n *\n * @module @maestria/shared-mode\n */\n\n// ── Constants ──\n\nexport const MODE_KEYWORDS = ['fein', 'sonar', 'blitz'] as const;\nexport type ModeKeyword = (typeof MODE_KEYWORDS)[number];\n\n/**\n * Backward-compatible alias used by OpenCode (`VALID_KEYWORDS`).\n */\nexport const VALID_KEYWORDS: readonly ModeKeyword[] = MODE_KEYWORDS;\n\nexport const MODE_MARKERS: Record<ModeKeyword, string> = {\n fein: '[MODE: fein]',\n sonar: '[MODE: sonar]',\n blitz: '[MODE: blitz]',\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 */\nexport const MODE_PRIORITY: Record<ModeKeyword, number> = {\n fein: 3,\n sonar: 2,\n blitz: 1,\n};\n\n// ── Types ──\n\n/**\n * Result of pure mode detection (no prompt/marker).\n * Host wrappers add prompt/marker and stripped text as needed.\n */\nexport interface ModeDetectPure {\n mode: ModeKeyword;\n keyword: string;\n index: number;\n}\n\n// ── Pure helpers ──\n\n/**\n * Regex matching fenced code blocks (```) and inline backtick spans (`).\n * Unclosed fenced blocks (``` without closing ```) are intentionally not\n * excluded - the regex requires matching fences. This is the accepted\n * false-positive behavior documented in ADR-OC-003.\n */\nexport const CODE_BLOCK_RE: RegExp = /```[\\s\\S]*?```|`[^`]*`/g;\n\nexport function findCodeBlockRanges(text: string): Array<[number, number]> {\n const ranges: Array<[number, number]> = [];\n let match: RegExpExecArray | null;\n // Reset lastIndex in case callers reuse the exported regex\n CODE_BLOCK_RE.lastIndex = 0;\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\nexport function isInRanges(index: number, ranges: Array<[number, number]>): boolean {\n return ranges.some(([start, end]) => index >= start && index < end);\n}\n\n/**\n * Extract the `## MODE:` section from a command file's content.\n * Mirrors the logic in both original modules: find `## MODE:`, slice\n * from there, trim trailing whitespace, append a single newline; if\n * no marker is found, normalize trailing whitespace the same way.\n */\nexport function extractModeSection(content: string): string {\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\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction buildKeywordRegex(keyword: string): RegExp {\n return new RegExp(`\\\\b${escapeRegExp(keyword)}\\\\b`, 'gi');\n}\n\nfunction isModeKeyword(value: string): value is ModeKeyword {\n return (MODE_KEYWORDS as readonly string[]).includes(value);\n}\n\nexport function getModeMarker(mode: string): string {\n if (isModeKeyword(mode)) return MODE_MARKERS[mode];\n return '';\n}\n\n// ── Detection ──\n\n/**\n * Detect a workflow mode keyword in the given text.\n *\n * Rules (per ADR-OC-003):\n * - Word-boundary regex matching (`\\bfein\\b`, etc.), case-insensitive\n * - Most restrictive match wins (fein > sonar > blitz) regardless of position\n * - Disabled keywords (case-insensitive) are ignored\n * - Matches inside fenced code blocks (```) and inline backticks (`) are ignored\n * - Unclosed fences are not excluded (accepted false-positive)\n *\n * Returns the pure detection result (mode, matched keyword text, index) or null.\n */\nexport function detectMode(text: string, disabled?: Set<string>): ModeDetectPure | null {\n if (!text) return null;\n const codeRanges = findCodeBlockRanges(text);\n const normalizedDisabled = disabled\n ? new Set(Array.from(disabled).map((k) => k.toLowerCase()))\n : undefined;\n let best: ModeDetectPure | null = null;\n\n for (const keyword of MODE_KEYWORDS) {\n if (normalizedDisabled?.has(keyword)) continue;\n const regex = buildKeywordRegex(keyword);\n let match: RegExpExecArray | null;\n while ((match = regex.exec(text)) !== null) {\n if (isInRanges(match.index, codeRanges)) continue;\n if (best === null || MODE_PRIORITY[keyword] > MODE_PRIORITY[best.mode]) {\n best = { mode: keyword, keyword: match[0], index: match.index };\n }\n }\n }\n\n return best;\n}\n\n/**\n * Remove the matched keyword from the text, cleaning up a trailing colon\n * and collapsing double spaces.\n *\n * Mirrors both originals: strip a leading colon and surrounding whitespace\n * after the keyword, then collapse double spaces and trim.\n */\nexport function stripKeyword(text: string, result: { index: number; keyword: string }): string {\n const before = text.slice(0, result.index);\n const after = text.slice(result.index + result.keyword.length);\n const cleaned = after.replace(/^:\\s*/, '');\n return (before + cleaned).replace(/ {2,}/g, ' ').trim();\n}\n","/**\n * Shared mode constants and utilities for Maestria Pi-family packages.\n *\n * Delegates pure mechanics to `@maestria/shared-mode` while preserving\n * host-specific concerns: lazy prompt loading from `commandsDir`,\n * session-state side effects, and platform handler factories.\n *\n * @module\n */\n\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport {\n MODE_KEYWORDS as SHARED_KEYWORDS,\n MODE_MARKERS as SHARED_MARKERS,\n extractModeSection,\n detectMode as sharedDetectMode,\n stripKeyword as sharedStripKeyword,\n} from '@maestria/shared-mode';\nimport type { ModeKeyword as SharedModeKeyword } from '@maestria/shared-mode';\nimport type { MaestriaState } from './state-core.js';\n\n// ── Constants (re-exported from shared-mode to preserve public API) ──\n\nexport const MODE_KEYWORDS = SHARED_KEYWORDS;\nexport const MODE_CLEAR_COMMAND = 'mode-clear';\nexport type ModeKeyword = SharedModeKeyword;\n\nexport const MODE_MARKERS: Record<ModeKeyword, string> = SHARED_MARKERS;\n\n// ── Prompt loading ──\n\n/** Lazily cached mode prompts - shared across Pi-family. */\nconst _promptCache: Partial<Record<ModeKeyword, string>> = {};\n\n/**\n * Load and cache a mode prompt from a commands directory.\n * Delegates `## MODE:` extraction to the neutral shared-mode helper.\n */\nexport function loadModePrompt(name: string, commandsDir: string): string {\n const content = readFileSync(resolve(commandsDir, `${name}.md`), 'utf-8');\n return extractModeSection(content);\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/**\n * Detect a mode keyword (fein/sonar/blitz) in text as a whole word,\n * case-insensitive. Delegates pure detection (including code-block\n * exclusion, word-boundary, priority, and case-insensitivity) to\n * `@maestria/shared-mode`. The accepted unclosed-fence behavior\n * (ADR-OC-003) is preserved via the shared helper.\n *\n * Lazy prompt loading remains host-specific (reads `commandsDir`).\n * Optional `disabled` set mirrors OpenCode's disabled-keyword support.\n */\nexport function detectModeInText(\n text: string,\n commandsDir: string,\n disabled?: Set<string>,\n): ModeDetectResult | null {\n if (!text) return null;\n\n const pure = sharedDetectMode(text, disabled);\n if (pure === null) return null;\n\n const strippedText = sharedStripKeyword(text, pure);\n\n return {\n keyword: pure.mode,\n strippedText,\n prompt: getModePrompt(pure.mode, 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 */\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 */\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. If a later spawn throws, abort the subagents already\n // spawned so they are not orphaned; the outer catch returns the handoff\n // fallback.\n const spawnedIds: string[] = [];\n try {\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 } catch (err) {\n abortSubagents(service, spawnedIds);\n throw err;\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 // A replacer function (not a string) is required: string replacements interpret\n // $&, $', $`, and $1..$99 in the replacement text, so a previous result\n // containing those sequences would silently corrupt the next task brief.\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","/**\n * Shared slash-command installation for Maestria platform packages.\n *\n * Pure TypeScript - no platform-specific dependencies.\n * Consumed by both @maestria/omp and @maestria/pi to eliminate duplication\n * of the maestria-status/review/restore-model/handoff/review-model commands.\n *\n * @module\n */\n\nimport type { MaestriaState } from './state-core.js';\nimport { persistState, recordHandoff, renderMaestriaSummary } from './state-core.js';\nimport { cycleToReviewModel, restoreOriginalState } from './review-core.js';\nimport { MAESTRIA_EVENTS } from './subagent-utils.js';\n\n// ── Duck-typed platform interfaces ──\n\ninterface CommandsCtx {\n ui: {\n notify(msg: string): void;\n setEditorText(text: string): void;\n };\n model?: { id: string };\n modelRegistry: { getAll(): Array<{ id: string }> };\n}\n\ninterface CommandsPi {\n registerCommand(\n name: string,\n opts: {\n description: string;\n handler: (args: string, ctx: CommandsCtx) => Promise<void> | void;\n },\n ): void;\n getActiveTools(): string[];\n setActiveTools(tools: string[]): void | Promise<void>;\n setModel(model: unknown): void | Promise<void>;\n sendUserMessage(text: string, opts: { deliverAs: string }): void;\n appendEntry(type: string, data: unknown): void;\n events?: { emit(event: string, data: unknown): void };\n}\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: CommandsPi, 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 void 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 Object.assign(state, recordHandoff(state, 'current', 'next', goal));\n\n // Persist state\n persistState(pi, state);\n\n // Send as user message with steer delivery\n pi.sendUserMessage(handoffPrompt, { deliverAs: 'steer' });\n },\n });\n\n pi.registerCommand('review-model', {\n description: 'Set which model to use when entering review mode',\n handler: async (args: string, ctx) => {\n if (!args.trim()) {\n ctx.ui.notify('Usage: /review-model <model-id>');\n return;\n }\n const modelId = args.trim();\n const models = ctx.modelRegistry.getAll();\n const model = models.find((m) => m.id === modelId);\n if (!model) {\n ctx.ui.notify(\n `Unknown model: \"${modelId}\". Available: ${models.map((m) => m.id).join(', ')}`,\n );\n return;\n }\n state.reviewModel = modelId;\n persistState(pi, state);\n ctx.ui.notify(`Review model set to: ${modelId}`);\n },\n });\n}\n","import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { MaestriaState } from '@/state.js';\nimport { installCommands as installCommandsCore } from '@maestria/shared-pi/commands-core';\n\nexport function installCommands(pi: ExtensionAPI, state: MaestriaState): void {\n installCommandsCore(pi as never, state);\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","__dirname","restoreOriginalState","restoreOriginalState","installCommands","isRecord","isPiModel","installCompactionHandlers","MODE_KEYWORDS","MODE_MARKERS","SHARED_KEYWORDS","SHARED_MARKERS","sharedDetectMode","sharedStripKeyword","installModeAutoDetect","installModeCommands","isRecord","restoreCore","__dirname","COMMANDS_DIR","isRecord","assertTaskContract","isRecord"],"sources":["../../shared/pi/src/subagent-utils.ts","../../shared/pi/src/agent-deployment.ts","../src/agents.ts","../../shared/pi/src/state-core.ts","../../shared/pi/src/review-core.ts","../../shared/pi/src/commands-core.ts","../src/commands.ts","../../shared/pi/src/compaction-core.ts","../src/compaction.ts","../../shared/mode/src/index.ts","../../shared/pi/src/modes-core.ts","../src/state/review.ts","../src/modes.ts","../src/rules.ts","../src/subagent-polling.ts","../src/subagent-events.ts","../src/subagent.ts","../src/subagent-api.ts","../../shared/pi/src/tools-core.ts","../src/tools.ts","../src/extension.ts"],"sourcesContent":["/**\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_COMPLETED: 'maestria:subagent:completed',\n SUBAGENT_FAILED: 'maestria:subagent:failed',\n SUBAGENT_STARTED: 'maestria:subagent:started',\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 const assertValidAgent = (agent: string): asserts agent is AllowedAgent => {\n if (!ALLOWED_AGENTS.some((allowedAgent) => allowedAgent === agent)) {\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 const assertNonEmptyTask = (\n task: string | undefined,\n label: string,\n): asserts task is string => {\n if (task === undefined || task === null || 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 const 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}:\\\\*\\\\*(?<content>[\\\\s\\\\S]*?)(?=\\\\n\\\\*\\\\*|$)`;\n const match = new RegExp(pattern, 'iu').exec(handoff);\n const content = match?.groups?.content?.trim();\n if (content === undefined || content === '') {\n errors.push(`Missing or empty field: \"${field}\"`);\n }\n }\n return { errors, valid: errors.length === 0 };\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 { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\n\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 const 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 = path.join(agentsSrc, `${name}.md`);\n const destFile = path.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)) {\n continue;\n }\n\n try {\n const content = readFileSync(srcFile, 'utf-8');\n writeFileSync(destFile, content, 'utf-8');\n deployed += 1;\n } catch (error) {\n console.warn(`[maestria] Failed to deploy agent ${name}:`, error);\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 { deploySpecialistAgents as deployAgents } from '@maestria/shared-pi/agent-deployment';\nimport { homedir } from 'node:os';\nimport path from 'node:path';\n\nconst __dirname = import.meta.dirname;\n\nconst AGENTS_SRC = path.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 */\nexport const deploySpecialistAgents = (_ctx?: unknown): void => {\n deployAgents(AGENTS_SRC, path.join(homedir(), '.pi', 'agent', 'agents'));\n};\n","/**\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 const createInitialState = (): MaestriaState => ({\n activeTask: '',\n blockers: [],\n completionPromise: '',\n filesModified: [],\n filesRead: [],\n handoffHistory: [],\n mode: null,\n nativeGoal: null,\n originalModel: null,\n originalTools: null,\n reviewMode: false,\n reviewModel: null,\n specialistsDelegated: [],\n subagentStatus: {},\n});\n\nconst 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 const recordHandoff = (\n state: MaestriaState,\n from: string,\n to: string,\n task: string,\n): MaestriaState => {\n const entry: HandoffEntry = { from, task, timestamp: Date.now(), to };\n const history = [entry, ...state.handoffHistory].slice(0, HANDOFF_HISTORY_CAP);\n return { ...state, handoffHistory: history };\n};\n\nexport const recordFileModified = (state: MaestriaState, path: string): MaestriaState => ({\n ...state,\n filesModified: prependDeduped(state.filesModified, path, FILE_HISTORY_CAP),\n});\n\nexport const recordFileRead = (state: MaestriaState, path: string): MaestriaState => ({\n ...state,\n filesRead: prependDeduped(state.filesRead, path, FILE_HISTORY_CAP),\n});\n\nexport const recordSpecialistDelegated = (state: MaestriaState, name: string): MaestriaState => {\n if (state.specialistsDelegated.includes(name)) {\n return state;\n }\n return { ...state, specialistsDelegated: [...state.specialistsDelegated, name] };\n};\n\nexport const recordSubagentStatus = (\n state: MaestriaState,\n id: string,\n info: SubagentStatusInfo,\n): MaestriaState => ({ ...state, subagentStatus: { ...state.subagentStatus, [id]: info } });\n\nexport const setReviewMode = (state: MaestriaState, active: boolean): MaestriaState => ({\n ...state,\n reviewMode: active,\n});\n\nexport const exitReviewMode = (\n state: MaestriaState,\n): {\n state: MaestriaState;\n originalModel: string | null;\n originalTools: string[] | null;\n} => ({\n originalModel: state.originalModel,\n originalTools: state.originalTools,\n state: {\n ...state,\n originalModel: null,\n originalTools: null,\n reviewMode: false,\n },\n});\n\n// ── Persistence ──\n\nexport const 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 const 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 !== null && state.reviewModel !== undefined && 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","/**\n * Shared review-mode orchestration for Maestria platform packages.\n *\n * Pure TypeScript - no platform-specific dependencies.\n * Consumed by both @maestria/omp and @maestria/pi to eliminate duplication\n * of review-mode model/tool restoration and switching.\n *\n * @module\n */\n\nimport type { MaestriaState } from './state-core.js';\nimport { exitReviewMode } from './state-core.js';\n\n// ── Duck-typed platform interfaces ──\n\ninterface ReviewPi {\n setActiveTools: (tools: string[]) => void | Promise<void>;\n setModel: (model: unknown) => void | Promise<void>;\n}\n\ninterface ReviewCtx {\n modelRegistry: { getAll: () => { id: string }[] };\n ui: { notify: (msg: string) => void };\n}\n\nexport const restoreOriginalState = async (\n pi: ReviewPi,\n ctx: ReviewCtx,\n state: MaestriaState,\n): Promise<void> => {\n const { state: clearedState, originalModel, originalTools } = exitReviewMode(state);\n\n if (originalTools && originalTools.length > 0) {\n await pi.setActiveTools(originalTools);\n }\n\n if (originalModel !== undefined && originalModel !== null && 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 const cycleToReviewModel = async (\n pi: ReviewPi,\n ctx: ReviewCtx,\n state: MaestriaState,\n): Promise<string | null> => {\n const { reviewModel } = state;\n if (reviewModel === undefined || reviewModel === null || 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 }\n ctx.ui.notify(`Review model \"${reviewModel}\" not found in registry, staying on current.`);\n return null;\n } catch {\n ctx.ui.notify(`Could not switch to review model \"${reviewModel}\", staying on current.`);\n return null;\n }\n};\n","/**\n * Shared slash-command installation for Maestria platform packages.\n *\n * Pure TypeScript - no platform-specific dependencies.\n * Consumed by both @maestria/omp and @maestria/pi to eliminate duplication\n * of the maestria-status/review/restore-model/handoff/review-model commands.\n *\n * @module\n */\n\nimport { cycleToReviewModel, restoreOriginalState } from './review-core.js';\nimport type { MaestriaState } from './state-core.js';\nimport { persistState, recordHandoff, renderMaestriaSummary } from './state-core.js';\nimport { MAESTRIA_EVENTS } from './subagent-utils.js';\n\n// ── Duck-typed platform interfaces ──\n\nexport interface CommandsCtx {\n ui: {\n notify: (msg: string) => void;\n setEditorText: (text: string) => void;\n };\n model?: { id: string };\n modelRegistry: { getAll: () => { id: string }[] };\n}\n\nexport interface CommandsPi {\n registerCommand: (\n name: string,\n opts: {\n description: string;\n handler: (args: string, ctx: CommandsCtx) => Promise<void> | void;\n },\n ) => void;\n getActiveTools: () => string[];\n setActiveTools: (tools: string[]) => void | Promise<void>;\n setModel: (model: unknown) => void | Promise<void>;\n sendUserMessage: (text: string, opts: { deliverAs: string }) => void;\n appendEntry: (type: string, data: unknown) => void;\n events?: { emit: (event: string, data: unknown) => void };\n}\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\nconst registerMaestriaStatus = (pi: CommandsPi, state: MaestriaState): void => {\n pi.registerCommand('maestria-status', {\n description: 'Show current maestria session state including handoff history',\n handler: (_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\nconst registerReviewCommand = (pi: CommandsPi, state: MaestriaState): void => {\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 const currentModelId = ctx.model?.id ?? null;\n const currentTools = pi.getActiveTools();\n Object.assign(state, {\n ...state,\n originalModel: currentModelId,\n originalTools: currentTools,\n reviewMode: true,\n } as MaestriaState);\n persistState(pi, state);\n if (\n state.reviewModel !== null &&\n state.reviewModel !== undefined &&\n state.reviewModel !== ''\n ) {\n const switched = await cycleToReviewModel(pi, ctx, state);\n if (switched !== null && switched !== undefined && 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 void pi.setActiveTools(READ_ONLY_TOOLS);\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\nconst registerRestoreModel = (pi: CommandsPi, state: MaestriaState): void => {\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\nconst registerHandoffCommand = (pi: CommandsPi, state: MaestriaState): void => {\n pi.registerCommand('handoff', {\n description: 'Generate a structured handoff prompt for a new task context',\n handler: (args: string, ctx) => {\n if (!args.trim()) {\n ctx.ui.notify('Usage: /handoff <goal> - describe the task context for handoff');\n return;\n }\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 }`,\n `- Recent handoffs: ${state.handoffHistory?.length ?? 0} entries`,\n `- Files modified: ${\n (state.filesModified?.length ?? 0) > 0 ? state.filesModified.join(', ') : 'none'\n }`,\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 Object.assign(state, recordHandoff(state, 'current', 'next', goal));\n persistState(pi, state);\n pi.sendUserMessage(handoffPrompt, { deliverAs: 'steer' });\n },\n });\n};\n\nconst registerReviewModel = (pi: CommandsPi, state: MaestriaState): void => {\n pi.registerCommand('review-model', {\n description: 'Set which model to use when entering review mode',\n handler: (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\nexport const installCommands = (pi: CommandsPi, state: MaestriaState): void => {\n registerMaestriaStatus(pi, state);\n registerReviewCommand(pi, state);\n registerRestoreModel(pi, state);\n registerHandoffCommand(pi, state);\n registerReviewModel(pi, state);\n};\n","import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { installCommands as installCommandsCore } from '@maestria/shared-pi/commands-core';\nimport type { CommandsPi } from '@maestria/shared-pi/commands-core';\n\nimport type { MaestriaState } from '@/state.js';\n\ntype PiModel = Parameters<ExtensionAPI['setModel']>[0];\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst isPiModel = (value: unknown): value is PiModel => {\n if (!isRecord(value)) {\n return false;\n }\n return (\n typeof value.api === 'string' &&\n typeof value.baseUrl === 'string' &&\n typeof value.contextWindow === 'number' &&\n typeof value.cost === 'object' &&\n value.cost !== null &&\n typeof value.id === 'string' &&\n Array.isArray(value.input) &&\n typeof value.maxTokens === 'number' &&\n typeof value.name === 'string' &&\n typeof value.provider === 'string' &&\n typeof value.reasoning === 'boolean'\n );\n};\n\nexport const createCommandsApi = (pi: ExtensionAPI): CommandsPi => ({\n appendEntry: (type, data) => {\n pi.appendEntry(type, data);\n },\n events: pi.events,\n getActiveTools: () => pi.getActiveTools(),\n registerCommand: (name, options) => {\n pi.registerCommand(name, {\n description: options.description,\n handler: async (args, ctx) => {\n await options.handler(args, ctx);\n },\n });\n },\n sendUserMessage: (text, options) => {\n if (options.deliverAs === 'steer' || options.deliverAs === 'followUp') {\n pi.sendUserMessage(text, { deliverAs: options.deliverAs });\n }\n },\n setActiveTools: (tools) => {\n pi.setActiveTools(tools);\n },\n setModel: async (model) => {\n if (isPiModel(model)) {\n await pi.setModel(model);\n }\n },\n});\n\nexport const installCommands = (pi: CommandsPi, state: MaestriaState): void => {\n installCommandsCore(pi, state);\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\nexport interface CompactionEvent {\n preparation?: {\n firstKeptEntryId?: string;\n tokensBefore?: number;\n };\n}\n\nexport interface TreeEvent {\n preparation?: {\n userWantsSummary?: boolean;\n };\n}\n\nexport interface CompactionResult {\n compaction?: {\n details?: unknown;\n firstKeptEntryId: string;\n summary: string;\n tokensBefore: number;\n };\n}\n\nexport interface TreeResult {\n summary?: {\n summary: string;\n };\n}\n\nexport type CompactionOn = (\n ...args:\n | ['session_before_compact', (event: CompactionEvent) => CompactionResult]\n | ['session_before_tree', (event: TreeEvent) => TreeResult | undefined]\n) => void;\n\nexport interface CompactionPi {\n on: CompactionOn;\n}\n\n/**\n * Install handlers for session compaction and tree events to persist\n * and restore maestria state across session compaction boundaries.\n */\nexport const installCompactionHandlers = (pi: CompactionPi, state: MaestriaState): void => {\n pi.on('session_before_compact', (event: CompactionEvent) => {\n const prep = event.preparation;\n return {\n compaction: {\n details: { ...state },\n firstKeptEntryId: prep?.firstKeptEntryId ?? '',\n summary: renderMaestriaSummary(state),\n tokensBefore: prep?.tokensBefore ?? 0,\n },\n };\n });\n\n pi.on('session_before_tree', (event: TreeEvent) => {\n const prep = event.preparation;\n return prep?.userWantsSummary === true\n ? {\n summary: {\n summary: renderMaestriaSummary(state),\n },\n }\n : undefined;\n });\n};\n","/**\n * Pi platform compaction handlers.\n *\n * Thin wrapper around the shared compaction-core implementation.\n *\n * @module\n */\n\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { installCompactionHandlers as installHandlers } from '@maestria/shared-pi/compaction-core';\nimport type {\n CompactionEvent,\n CompactionPi,\n CompactionResult,\n TreeEvent,\n TreeResult,\n} from '@maestria/shared-pi/compaction-core';\n\nimport type { MaestriaState } from '@/state.js';\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 const createCompactionApi = (pi: ExtensionAPI): CompactionPi => {\n const on = (\n ...args:\n | ['session_before_compact', (event: CompactionEvent) => CompactionResult]\n | ['session_before_tree', (event: TreeEvent) => TreeResult | undefined]\n ): void => {\n const [event, handler] = args;\n if (event === 'session_before_compact') {\n pi.on('session_before_compact', (eventData) => handler(eventData));\n }\n if (event === 'session_before_tree') {\n pi.on('session_before_tree', (eventData) => handler(eventData));\n }\n };\n return { on };\n};\n\nexport const installCompactionHandlers = (pi: CompactionPi, state: MaestriaState): void => {\n installHandlers(pi, state);\n};\n","/**\n * Private runtime-neutral shared mode mechanics.\n *\n * Pure TypeScript - no host SDKs, no filesystem APIs.\n * Consolidates the duplicated pure logic from:\n * - `packages/opencode/src/modes/index.ts`\n * - `packages/shared/pi/src/modes-core.ts`\n *\n * Consumers (OpenCode, shared-pi) delegate pure detection,\n * stripping, and mode-section extraction to this module while\n * keeping host-specific concerns (lazy file loading, session\n * state, command registration) locally.\n *\n * @module @maestria/shared-mode\n */\n\n// ── Constants ──\n\nexport const MODE_KEYWORDS = ['fein', 'sonar', 'blitz'] as const;\nexport type ModeKeyword = (typeof MODE_KEYWORDS)[number];\n\n/**\n * Backward-compatible alias used by OpenCode (`VALID_KEYWORDS`).\n */\nexport const VALID_KEYWORDS: readonly ModeKeyword[] = MODE_KEYWORDS;\n\nexport const MODE_MARKERS: Record<ModeKeyword, string> = {\n blitz: '[MODE: blitz]',\n fein: '[MODE: fein]',\n sonar: '[MODE: sonar]',\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 */\nexport const MODE_PRIORITY: Record<ModeKeyword, number> = {\n blitz: 1,\n fein: 3,\n sonar: 2,\n};\n\n// ── Types ──\n\n/**\n * Result of pure mode detection (no prompt/marker).\n * Host wrappers add prompt/marker and stripped text as needed.\n */\nexport interface ModeDetectPure {\n mode: ModeKeyword;\n keyword: string;\n index: number;\n}\n\n// ── Pure helpers ──\n\n/**\n * Regex matching fenced code blocks (```) and inline backtick spans (`).\n * Unclosed fenced blocks (``` without closing ```) are intentionally not\n * excluded - the regex requires matching fences. This is the accepted\n * false-positive behavior documented in ADR-OC-003.\n */\nexport const CODE_BLOCK_RE = /```[\\s\\S]*?```|`[^`]*`/gu;\n\nexport const findCodeBlockRanges = (text: string): [number, number][] => {\n const ranges: [number, number][] = [];\n let match: RegExpExecArray | null;\n // Reset lastIndex in case callers reuse the exported regex\n CODE_BLOCK_RE.lastIndex = 0;\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\nexport const isInRanges = (index: number, ranges: [number, number][]): boolean =>\n ranges.some(([start, end]) => index >= start && index < end);\n\n/**\n * Extract the `## MODE:` section from a command file's content.\n * Mirrors the logic in both original modules: find `## MODE:`, slice\n * from there, trim trailing whitespace, append a single newline; if\n * no marker is found, normalize trailing whitespace the same way.\n */\nexport const extractModeSection = (content: string): string => {\n const modeIdx = content.indexOf('## MODE:');\n if (modeIdx !== -1) {\n return `${content.slice(modeIdx).replace(/\\s+$/u, '')}\\n`;\n }\n return `${content.replace(/\\s+$/u, '')}\\n`;\n};\n\nconst escapeRegExp = (value: string): string => value.replaceAll(/[.*+?^${}()|[\\]\\\\]/gu, '\\\\$&');\n\nconst buildKeywordRegex = (keyword: string): RegExp =>\n new RegExp(`\\\\b${escapeRegExp(keyword)}\\\\b`, 'giu');\n\nconst isModeKeyword = (value: string): value is ModeKeyword =>\n (MODE_KEYWORDS as readonly string[]).includes(value);\n\nexport const getModeMarker = (mode: string): string => {\n if (isModeKeyword(mode)) {\n return MODE_MARKERS[mode];\n }\n return '';\n};\n\n// ── Detection ──\n\n/**\n * Detect a workflow mode keyword in the given text.\n *\n * Rules (per ADR-OC-003):\n * - Word-boundary regex matching (`\\bfein\\b`, etc.), case-insensitive\n * - Most restrictive match wins (fein > sonar > blitz) regardless of position\n * - Disabled keywords (case-insensitive) are ignored\n * - Matches inside fenced code blocks (```) and inline backticks (`) are ignored\n * - Unclosed fences are not excluded (accepted false-positive)\n *\n * Returns the pure detection result (mode, matched keyword text, index) or null.\n */\nexport const detectMode = (text: string, disabled?: Set<string>): ModeDetectPure | null => {\n if (!text) {\n return null;\n }\n const codeRanges = findCodeBlockRanges(text);\n const normalizedDisabled = disabled\n ? new Set([...disabled].map((k) => k.toLowerCase()))\n : undefined;\n let best: ModeDetectPure | null = null;\n\n for (const keyword of MODE_KEYWORDS) {\n if (\n normalizedDisabled !== undefined &&\n normalizedDisabled !== null &&\n normalizedDisabled.has(keyword)\n ) {\n continue;\n }\n const regex = buildKeywordRegex(keyword);\n let match: RegExpExecArray | null;\n while ((match = regex.exec(text)) !== null) {\n if (isInRanges(match.index, codeRanges)) {\n continue;\n }\n if (best === null || MODE_PRIORITY[keyword] > MODE_PRIORITY[best.mode]) {\n best = { index: match.index, keyword: match[0], mode: keyword };\n }\n }\n }\n\n return best;\n};\n\n/**\n * Remove the matched keyword from the text, cleaning up a trailing colon\n * and collapsing double spaces.\n *\n * Mirrors both originals: strip a leading colon and surrounding whitespace\n * after the keyword, then collapse double spaces and trim.\n */\nexport const stripKeyword = (text: string, result: { index: number; keyword: string }): string => {\n const before = text.slice(0, result.index);\n const after = text.slice(result.index + result.keyword.length);\n const cleaned = after.replace(/^:\\s*/u, '');\n return (before + cleaned).replaceAll(/ {2,}/gu, ' ').trim();\n};\n","/**\n * Shared mode constants and utilities for Maestria Pi-family packages.\n *\n * Delegates pure mechanics to `@maestria/shared-mode` while preserving\n * host-specific concerns: lazy prompt loading from `commandsDir`,\n * session-state side effects, and platform handler factories.\n *\n * @module\n */\n\nimport {\n extractModeSection,\n MODE_KEYWORDS as SHARED_KEYWORDS,\n MODE_MARKERS as SHARED_MARKERS,\n detectMode as sharedDetectMode,\n stripKeyword as sharedStripKeyword,\n} from '@maestria/shared-mode';\nimport type { ModeKeyword as SharedModeKeyword } from '@maestria/shared-mode';\nimport { readFileSync } from 'node:fs';\nimport path from 'node:path';\n\nimport type { MaestriaState } from './state-core.js';\n\n// ── Constants (re-exported from shared-mode to preserve public API) ──\n\nexport const MODE_KEYWORDS = SHARED_KEYWORDS;\nexport const MODE_CLEAR_COMMAND = 'mode-clear';\nexport type ModeKeyword = SharedModeKeyword;\n\nexport const MODE_MARKERS: Record<ModeKeyword, string> = SHARED_MARKERS;\n\n// ── Prompt loading ──\n\n/** Lazily cached mode prompts, scoped by commands directory and keyword. */\nconst _promptCache = new Map<string, string>();\n\n/**\n * Load and cache a mode prompt from a commands directory.\n * Delegates `## MODE:` extraction to the neutral shared-mode helper.\n */\nexport const loadModePrompt = (name: string, commandsDir: string): string => {\n const content = readFileSync(path.resolve(commandsDir, `${name}.md`), 'utf-8');\n return extractModeSection(content);\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 const getModePrompt = (keyword: ModeKeyword, commandsDir: string): string => {\n const cacheKey = `${path.resolve(commandsDir)}\\0${keyword}`;\n if (!_promptCache.has(cacheKey)) {\n try {\n _promptCache.set(cacheKey, loadModePrompt(keyword, commandsDir));\n } catch (error) {\n console.warn(`[maestria] Failed to load mode prompt \"${keyword}\":`, error);\n _promptCache.set(cacheKey, '');\n }\n }\n return `${MODE_MARKERS[keyword]}\\n\\n${_promptCache.get(cacheKey) ?? ''}`;\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/**\n * Detect a mode keyword (fein/sonar/blitz) in text as a whole word,\n * case-insensitive. Delegates pure detection (including code-block\n * exclusion, word-boundary, priority, and case-insensitivity) to\n * `@maestria/shared-mode`. The accepted unclosed-fence behavior\n * (ADR-OC-003) is preserved via the shared helper.\n *\n * Lazy prompt loading remains host-specific (reads `commandsDir`).\n * Optional `disabled` set mirrors OpenCode's disabled-keyword support.\n */\nexport const detectModeInText = (\n text: string,\n commandsDir: string,\n disabled?: Set<string>,\n): ModeDetectResult | null => {\n if (!text) {\n return null;\n }\n\n const pure = sharedDetectMode(text, disabled);\n if (pure === null) {\n return null;\n }\n\n const strippedText = sharedStripKeyword(text, pure);\n\n return {\n keyword: pure.mode,\n prompt: getModePrompt(pure.mode, commandsDir),\n strippedText,\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 const buildModeText = (prompt: string, strippedText: string): string =>\n strippedText ? `${prompt}\\n\\n${strippedText}` : prompt;\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 */\nexport const installModeAutoDetect = <Context, Result>(\n onInput: (handler: (event: { text: string }, ctx: Context) => Promise<Result>) => void,\n state: MaestriaState,\n commandsDir: string,\n opts: {\n /** Exit review mode - calls platform's restoreOriginalState */\n restoreOriginalState: (ctx: Context) => 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: Result;\n /** Build return value from transformed text (e.g. Pi: { action: 'transform', text }) */\n transform: (text: string) => Result;\n },\n): void => {\n onInput(async (event, ctx) => {\n const { text } = event;\n const result = detectModeInText(text, commandsDir);\n if (!result) {\n return opts.noMatch;\n }\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 */\ninterface ModeCommandContext {\n ui: { notify: (msg: string) => void };\n}\n\nexport const installModeCommands = <Context extends ModeCommandContext>(\n registerCommand: (\n name: string,\n options: {\n description: string;\n handler: (args: string, ctx: Context) => Promise<void> | void;\n },\n ) => void,\n state: MaestriaState,\n opts: {\n /** Exit review mode before switching modes */\n restoreOriginalState: (ctx: Context) => 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: string, ctx: Context) => {\n if (state.reviewMode) {\n await opts.restoreOriginalState(ctx);\n }\n state.mode = null;\n opts.persistState();\n ctx.ui.notify('Workflow mode cleared. Neutral routing is active.');\n },\n });\n\n for (const keyword of MODE_KEYWORDS) {\n registerCommand(keyword, {\n description: `Set workflow mode to ${keyword}`,\n handler: async (_args: string, ctx: Context) => {\n if (state.reviewMode) {\n await opts.restoreOriginalState(ctx);\n }\n\n state.mode = keyword;\n opts.persistState();\n\n ctx.ui.notify(`Mode set to ${keyword}. Describe what you'd like to work on.`);\n },\n });\n }\n};\n","import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport {\n cycleToReviewModel as cycleCore,\n restoreOriginalState as restoreCore,\n} from '@maestria/shared-pi/review-core';\nimport type { MaestriaState } from '@maestria/shared-pi/state-core';\n\ntype PiModel = Parameters<ExtensionAPI['setModel']>[0];\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst isPiModel = (value: unknown): value is PiModel => {\n if (!isRecord(value)) {\n return false;\n }\n return (\n typeof value.api === 'string' &&\n typeof value.baseUrl === 'string' &&\n typeof value.contextWindow === 'number' &&\n typeof value.cost === 'object' &&\n value.cost !== null &&\n typeof value.id === 'string' &&\n Array.isArray(value.input) &&\n typeof value.maxTokens === 'number' &&\n typeof value.name === 'string' &&\n typeof value.provider === 'string' &&\n typeof value.reasoning === 'boolean'\n );\n};\n\nexport interface ReviewModelContext {\n modelRegistry: { getAll: () => { id: string }[] };\n ui: { notify: (message: string) => void };\n}\n\nexport interface ReviewModelApi {\n setActiveTools: ExtensionAPI['setActiveTools'];\n setModel: ExtensionAPI['setModel'];\n}\n\nexport type ReviewApi = ReviewModelApi;\n\nexport const restoreOriginalState = async (\n pi: ReviewApi,\n ctx: ReviewModelContext,\n state: MaestriaState,\n): Promise<void> => {\n await restoreCore(\n {\n setActiveTools: (tools) => {\n pi.setActiveTools(tools);\n },\n setModel: async (model) => {\n if (isPiModel(model)) {\n await pi.setModel(model);\n }\n },\n },\n ctx,\n state,\n );\n};\n\nexport const cycleToReviewModel = async (\n pi: ReviewApi,\n ctx: ReviewModelContext,\n state: MaestriaState,\n): Promise<string | null> =>\n await cycleCore(\n {\n setActiveTools: (tools) => {\n pi.setActiveTools(tools);\n },\n setModel: async (model) => {\n if (isPiModel(model)) {\n await pi.setModel(model);\n }\n },\n },\n ctx,\n state,\n );\n","import type {\n ExtensionAPI,\n ExtensionContext,\n InputEventResult,\n} from '@earendil-works/pi-coding-agent';\nimport {\n installModeAutoDetect as installAutoDetect,\n installModeCommands as installCommands,\n} from '@maestria/shared-pi/modes-core';\nimport path from 'node:path';\n\nimport type { MaestriaState } from '@/state.js';\nimport { persistState, restoreOriginalState } from '@/state.js';\n\nconst __dirname = import.meta.dirname;\nconst COMMANDS_DIR = path.resolve(__dirname, '../agents/commands');\n\nexport const installModeAutoDetect = (pi: ExtensionAPI, state: MaestriaState): void => {\n installAutoDetect<ExtensionContext, InputEventResult>(\n (handler) => {\n pi.on('input', handler);\n },\n state,\n COMMANDS_DIR,\n {\n noMatch: { action: 'continue' as const },\n persistState: () => {\n persistState(pi, state);\n },\n restoreOriginalState: async (ctx) => {\n await restoreOriginalState(pi, ctx, state);\n },\n transform: (text) => ({ action: 'transform' as const, text }),\n },\n );\n};\n\nexport interface ModeCommandContext {\n ui: { notify: (message: string) => void };\n}\n\nexport interface ModeCommandsApi {\n appendEntry: (type: string, data?: unknown) => void;\n registerCommand: (\n name: string,\n options: {\n description: string;\n handler: (args: string, ctx: ModeCommandContext) => Promise<void> | void;\n },\n ) => void;\n setActiveTools: ExtensionAPI['setActiveTools'];\n setModel: ExtensionAPI['setModel'];\n}\n\nconst isExtensionContext = (value: unknown): value is ExtensionContext => {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n return 'modelRegistry' in value && 'ui' in value;\n};\n\nexport const createModeCommandsApi = (pi: ExtensionAPI): ModeCommandsApi => ({\n appendEntry: (type, data) => {\n pi.appendEntry(type, data);\n },\n registerCommand: (name, options) => {\n pi.registerCommand(name, {\n description: options.description,\n handler: async (args, ctx) => {\n await options.handler(args, ctx);\n },\n });\n },\n setActiveTools: (tools) => {\n pi.setActiveTools(tools);\n },\n setModel: async (model) => await pi.setModel(model),\n});\n\nexport const installModeCommands = (pi: ModeCommandsApi, state: MaestriaState): void => {\n installCommands<ModeCommandContext>(\n (name, opts) => {\n pi.registerCommand(name, {\n description: opts.description,\n handler: async (args, ctx) => {\n await opts.handler(args, ctx);\n },\n });\n },\n state,\n {\n persistState: () => {\n persistState(pi, state);\n },\n restoreOriginalState: async (ctx) => {\n if (isExtensionContext(ctx)) {\n await restoreOriginalState(pi, ctx, state);\n }\n },\n },\n );\n};\n","import type {\n BeforeAgentStartEvent,\n BeforeAgentStartEventResult,\n} from '@earendil-works/pi-coding-agent';\nimport { getModePrompt } from '@maestria/shared-pi/modes-core';\nimport path from 'node:path';\n\nimport type { MaestriaState } from '@/state.js';\n\nconst __dirname = import.meta.dirname;\nconst COMMANDS_DIR = path.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 an empty result (no modification),\n * letting Pi's built-in prompt assembly (skills + context files + tools)\n * stand as-is.\n */\nexport const createModePromptHandler =\n (state: MaestriaState) =>\n (event: BeforeAgentStartEvent, _ctx: object): BeforeAgentStartEventResult => {\n if (state.mode === null) {\n return {};\n }\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","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 interface SubagentRecord {\n status: string;\n result?: string;\n error?: string;\n}\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: { type: string; text: string }[] }) => void;\n readonly intervalMs?: number;\n readonly timeoutMs?: number;\n}\n\nconst createPollUpdateEffect = (options: PollSubagentOptions, polls: number) =>\n Effect.sync(() => {\n options.onUpdate?.({\n content: [\n {\n text: `${options.label} running... (${Math.round((polls * (options.intervalMs ?? 500)) / 1000)}s)`,\n type: 'text' as const,\n },\n ],\n });\n });\n\nconst pollLoop = (\n options: PollSubagentOptions,\n): 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* pollLoopEffect() {\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 += 1;\n\n if (options.sendUpdates) {\n yield* createPollUpdateEffect(options, polls);\n }\n }\n\n if (record && !TERMINAL_STATUSES.has(record.status)) {\n return yield* Effect.fail(\n new SubagentPollError({\n id: options.id,\n message: `Subagent ${options.id} timed out after ${timeoutMs}ms`,\n reason: 'timeout',\n }),\n );\n }\n\n if (!record) {\n return yield* Effect.fail(\n new SubagentPollError({\n id: options.id,\n message: `Subagent ${options.id} was cleaned up before completion`,\n reason: 'missing',\n }),\n );\n }\n\n return record;\n });\n};\n\nconst abortEffect = (id: string, signal: AbortSignal): Effect.Effect<never, SubagentPollError> =>\n Effect.callback<never, SubagentPollError>((resume) => {\n const onAbort = () => {\n resume(\n Effect.fail(\n new SubagentPollError({\n id,\n message: 'Maestria subagent call aborted',\n reason: 'aborted',\n }),\n ),\n );\n };\n\n if (signal.aborted) {\n onAbort();\n } else {\n signal.addEventListener('abort', onAbort, { once: true });\n }\n\n return Effect.sync(() => {\n 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 const 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 { SUBAGENT_EVENTS } from '@gotgenes/pi-subagents';\nimport { MAESTRIA_EVENTS } from '@maestria/shared-pi/subagent-utils';\n\nimport type { MaestriaState } from '@/state.js';\nimport { persistState } from '@/state.js';\n\nexport interface SubagentEventHost {\n appendEntry: (type: string, data: unknown) => void;\n events?: {\n emit: (event: string, data: unknown) => void;\n on: (event: string, handler: (data: unknown) => void) => () => void;\n } | null;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst getStringField = (value: unknown, key: string): string | undefined => {\n if (!isRecord(value)) {\n return undefined;\n }\n const field = value[key];\n return typeof field === 'string' ? field : undefined;\n};\n\ntype PiEvents = NonNullable<SubagentEventHost['events']>;\n\nconst subscribeStarted = (\n events: PiEvents,\n pi: SubagentEventHost,\n state: MaestriaState,\n): (() => void) =>\n events.on(SUBAGENT_EVENTS.STARTED, (data: unknown) => {\n const id = getStringField(data, 'id');\n const type = getStringField(data, 'type') ?? 'unknown';\n if (id === undefined) {\n return;\n }\n state.subagentStatus[id] = { startedAt: Date.now(), status: 'running', type };\n persistState(pi, state);\n events.emit(MAESTRIA_EVENTS.SUBAGENT_STARTED, { id, timestamp: Date.now(), type });\n });\n\nconst subscribeCompleted = (\n events: PiEvents,\n pi: SubagentEventHost,\n state: MaestriaState,\n): (() => void) =>\n events.on(SUBAGENT_EVENTS.COMPLETED, (data: unknown) => {\n const id = getStringField(data, 'id');\n if (id === undefined) {\n return;\n }\n const existing = state.subagentStatus[id];\n if (existing !== null && existing !== undefined) {\n existing.status = 'completed';\n existing.completedAt = Date.now();\n }\n persistState(pi, state);\n events.emit(MAESTRIA_EVENTS.SUBAGENT_COMPLETED, {\n id,\n timestamp: Date.now(),\n type: existing?.type,\n });\n });\n\nconst subscribeFailed = (\n events: PiEvents,\n pi: SubagentEventHost,\n state: MaestriaState,\n): (() => void) =>\n events.on(SUBAGENT_EVENTS.FAILED, (data: unknown) => {\n const id = getStringField(data, 'id');\n if (id === undefined) {\n return;\n }\n const status = getStringField(data, 'status') ?? 'error';\n const existing = state.subagentStatus[id];\n if (existing !== null && existing !== undefined) {\n existing.status = status;\n existing.completedAt = Date.now();\n }\n persistState(pi, state);\n events.emit(MAESTRIA_EVENTS.SUBAGENT_FAILED, {\n id,\n timestamp: Date.now(),\n type: existing?.type,\n });\n });\n\nconst subscribeSteered = (\n events: PiEvents,\n pi: SubagentEventHost,\n state: MaestriaState,\n): (() => void) =>\n events.on(SUBAGENT_EVENTS.STEERED, (data: unknown) => {\n const id = getStringField(data, 'id');\n if (id === undefined) {\n return;\n }\n state.subagentStatus[id] ??= { startedAt: Date.now(), status: 'running', type: 'unknown' };\n persistState(pi, state);\n });\n\nexport const subscribeSubagentEvents = (\n pi: SubagentEventHost,\n state: MaestriaState,\n cleanups?: (() => void)[],\n): void => {\n const { events } = pi;\n if (events === null || events === undefined) {\n return;\n }\n const subscriptions = [\n subscribeStarted(events, pi, state),\n subscribeCompleted(events, pi, state),\n subscribeFailed(events, pi, state),\n subscribeSteered(events, pi, state),\n ];\n cleanups?.push(...subscriptions);\n};\n","import type { AgentToolUpdateCallback } from '@earendil-works/pi-coding-agent';\nimport {\n ALLOWED_AGENTS,\n assertNonEmptyTask as assertTaskContract,\n assertValidAgent,\n} from '@maestria/shared-pi/subagent-utils';\nimport { Effect } from 'effect';\nimport { Type } from 'typebox';\nimport type { Static } from 'typebox';\n\nimport type { MaestriaState } from '@/state.js';\nimport { persistState, recordHandoff, recordSpecialistDelegated } from '@/state.js';\nimport { pollSubagentEffect } from '@/subagent-polling.js';\nimport type { SubagentPollingService, SubagentRecord } from '@/subagent-polling.js';\nimport { subscribeSubagentEvents } from '@/subagent-events.js';\nimport type { SubagentEventHost } from '@/subagent-events.js';\n\nconst ALLOWED_AGENT_NAMES: readonly string[] = ALLOWED_AGENTS;\n\nexport const POLL_TIMEOUT_MS = 180_000;\nexport const POLL_INTERVAL_MS = 500;\nexport const MAX_PARALLEL_TASKS = 8;\n\ntype SubagentSpawnService = SubagentPollingService & {\n spawn: (\n agent: string,\n task: string,\n opts: { description: string; foreground: boolean; inheritContext: boolean },\n ) => string;\n};\n\ninterface SubagentTask {\n agent: string;\n task: string;\n}\nexport interface SubagentParams {\n agent?: string;\n task?: string;\n tasks?: SubagentTask[];\n mode?: string;\n}\ninterface ToolUpdate {\n content: { type: string; text: string }[];\n details?: Record<string, unknown>;\n}\ntype ToolUpdateHandler = ((result: ToolUpdate) => void) | undefined;\nexport type PiToolUpdateHandler = AgentToolUpdateCallback<Record<string, unknown>> | undefined;\nexport interface ToolResult {\n content: { text: string; type: 'text' }[];\n details: Record<string, unknown>;\n}\n\nexport interface SubagentToolDefinition {\n description: string;\n execute: (\n toolCallId: string,\n params: SubagentParams,\n signal: AbortSignal | undefined,\n onUpdate: PiToolUpdateHandler,\n ctx: unknown,\n ) => Promise<ToolResult>;\n label: string;\n name: string;\n parameters: typeof SUBAGENT_PARAMETERS;\n promptGuidelines?: string[];\n promptSnippet?: string;\n}\n\nexport interface SubagentToolApi extends SubagentEventHost {\n registerTool: (tool: SubagentToolDefinition) => void;\n}\n\nconst assertTask: (task: string | undefined, label: string) => asserts task is string =\n assertTaskContract;\nconst assertAgent: (agent: string) => void = assertValidAgent;\n\nconst abortSubagents = (service: SubagentPollingService, ids: readonly string[]): void => {\n if (typeof service.abort !== 'function') {\n return;\n }\n for (const id of ids) {\n try {\n service.abort(id);\n } catch {\n // Best-effort cleanup\n }\n }\n};\n\nconst pollSubagentOrAbortEffect = (options: Parameters<typeof pollSubagentEffect>[0]) =>\n Effect.tapError(pollSubagentEffect(options), () =>\n Effect.sync(() => {\n abortSubagents(options.service, [options.id]);\n }),\n );\n\nconst recordAndPersist = (\n pi: SubagentEventHost,\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\nconst validatePiParams = (params: SubagentParams): string => {\n const mode = params.mode ?? 'single';\n if (mode === 'single') {\n if (\n params.agent === undefined ||\n params.agent === null ||\n params.agent === '' ||\n !ALLOWED_AGENT_NAMES.includes(params.agent)\n ) {\n return `Invalid maestria_subagent call: 'agent' is required and must be one of ${ALLOWED_AGENT_NAMES.join(', ')}.`;\n }\n assertTask(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 assertAgent(t.agent);\n assertTask(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 assertAgent(t.agent);\n assertTask(t.task, 'Task description is required for all tasks');\n }\n }\n return mode;\n};\n\nconst handleSingleMode = async (\n pi: SubagentEventHost,\n state: MaestriaState,\n service: SubagentSpawnService,\n agent: string,\n task: string,\n signal: AbortSignal | undefined,\n onUpdate: ToolUpdateHandler,\n): Promise<ToolResult> => {\n const id = service.spawn(agent, task, {\n description: task.slice(0, 80),\n foreground: true,\n inheritContext: true,\n });\n recordAndPersist(pi, state, agent, task);\n const record = await Effect.runPromise(\n pollSubagentOrAbortEffect({\n id,\n intervalMs: POLL_INTERVAL_MS,\n label: `Subagent ${agent}`,\n onUpdate,\n sendUpdates: true,\n service,\n signal,\n timeoutMs: POLL_TIMEOUT_MS,\n }),\n );\n return {\n content: [{ text: record.result ?? record.error ?? 'No output.', type: 'text' as const }],\n details: { subagentId: id },\n };\n};\n\ntype ParallelOutcome = { error: unknown } | { record: SubagentRecord };\n\nconst spawnParallelSubagents = (\n pi: SubagentEventHost,\n state: MaestriaState,\n service: SubagentSpawnService,\n taskList: SubagentTask[],\n): string[] => {\n const spawnedIds: string[] = [];\n try {\n for (const task of taskList) {\n const id = service.spawn(task.agent, task.task, {\n description: task.task.slice(0, 80),\n foreground: true,\n inheritContext: true,\n });\n spawnedIds.push(id);\n recordAndPersist(pi, state, task.agent, task.task);\n }\n } catch (error) {\n abortSubagents(service, spawnedIds);\n throw error;\n }\n return spawnedIds;\n};\n\nconst pollParallelSubagents = async (\n spawnedIds: string[],\n taskList: SubagentTask[],\n service: SubagentSpawnService,\n signal: AbortSignal | undefined,\n onUpdate: ToolUpdateHandler,\n): Promise<ParallelOutcome[]> =>\n await Effect.runPromise(\n Effect.all(\n spawnedIds.map((id, index) => {\n const task = taskList[index];\n return Effect.match(\n pollSubagentEffect({\n id,\n intervalMs: POLL_INTERVAL_MS,\n label: `${task.agent} (${index + 1}/${taskList.length})`,\n onUpdate,\n sendUpdates: false,\n service,\n signal,\n timeoutMs: POLL_TIMEOUT_MS,\n }),\n {\n onFailure: (error) => {\n abortSubagents(service, spawnedIds);\n return { error };\n },\n onSuccess: (record) => ({ record }),\n },\n );\n }),\n { concurrency: 'unbounded' },\n ),\n );\n\nconst renderParallelResults = (\n taskList: SubagentTask[],\n spawnedIds: string[],\n outcomes: ParallelOutcome[],\n): ToolResult => {\n const parts = [`## Parallel Results (${taskList.length} tasks)\\n`];\n for (const [index, task] of taskList.entries()) {\n const outcome = outcomes[index];\n parts.push(`### ${index + 1}: ${task.agent}`);\n if ('error' in outcome) {\n parts.push(\n `⚠️ ${outcome.error instanceof Error ? outcome.error.message : String(outcome.error)}`,\n );\n } else {\n parts.push(outcome.record.result ?? outcome.record.error ?? 'No output.');\n }\n }\n return {\n content: [{ text: parts.join('\\n\\n'), type: 'text' as const }],\n details: { subagentIds: spawnedIds },\n };\n};\n\nconst handleParallelMode = async (\n pi: SubagentEventHost,\n state: MaestriaState,\n service: SubagentSpawnService,\n taskList: SubagentTask[],\n signal: AbortSignal | undefined,\n onUpdate: ToolUpdateHandler,\n): Promise<ToolResult> => {\n onUpdate?.({\n content: [{ text: `Spawning ${taskList.length} parallel subagents...`, type: 'text' }],\n });\n const spawnedIds = spawnParallelSubagents(pi, state, service, taskList);\n const outcomes = await pollParallelSubagents(spawnedIds, taskList, service, signal, onUpdate);\n onUpdate?.({\n content: [{ text: `All ${taskList.length} parallel subagents settled.`, type: 'text' }],\n });\n return renderParallelResults(taskList, spawnedIds, outcomes);\n};\n\nconst substitutePreviousResult = (taskText: string, previousResult: string): string =>\n taskText.replaceAll('{previous}', () => previousResult);\n\nconst runChainSteps = async (\n pi: SubagentEventHost,\n state: MaestriaState,\n service: SubagentSpawnService,\n taskList: SubagentTask[],\n signal: AbortSignal | undefined,\n onUpdate: ToolUpdateHandler,\n index: number,\n previousResult: string,\n): Promise<string> => {\n if (index >= taskList.length) {\n return previousResult;\n }\n\n const task = taskList[index];\n const taskText = index > 0 ? substitutePreviousResult(task.task, previousResult) : task.task;\n const id = service.spawn(task.agent, taskText, {\n description: taskText.slice(0, 80),\n foreground: true,\n inheritContext: true,\n });\n recordAndPersist(pi, state, task.agent, taskText);\n onUpdate?.({\n content: [\n {\n text: `Chain step ${index + 1}/${taskList.length}: ${task.agent} running...`,\n type: 'text',\n },\n ],\n });\n\n let nextResult: string;\n try {\n const record = await Effect.runPromise(\n pollSubagentOrAbortEffect({\n id,\n intervalMs: POLL_INTERVAL_MS,\n label: `Chain step ${index + 1}: ${task.agent}`,\n onUpdate,\n sendUpdates: true,\n service,\n signal,\n timeoutMs: POLL_TIMEOUT_MS,\n }),\n );\n nextResult = record.result ?? record.error ?? 'No output.';\n } catch (error) {\n return `[error] ${error instanceof Error ? error.message : String(error)}`;\n }\n\n if (index < taskList.length - 1) {\n onUpdate?.({\n content: [\n {\n text: `Chain step ${index + 1}/${taskList.length}: ${task.agent} completed. Moving to next step.`,\n type: 'text',\n },\n ],\n });\n }\n\n return await runChainSteps(pi, state, service, taskList, signal, onUpdate, index + 1, nextResult);\n};\n\nconst handleChainMode = async (\n pi: SubagentEventHost,\n state: MaestriaState,\n service: SubagentSpawnService,\n taskList: SubagentTask[],\n signal: AbortSignal | undefined,\n onUpdate: ToolUpdateHandler,\n): Promise<ToolResult> => ({\n content: [\n {\n text: await runChainSteps(pi, state, service, taskList, signal, onUpdate, 0, ''),\n type: 'text',\n },\n ],\n details: { subagentId: 'chain-completed' },\n});\n\nconst dispatchByMode = async (\n pi: SubagentEventHost,\n state: MaestriaState,\n service: SubagentSpawnService,\n params: SubagentParams,\n signal: AbortSignal | undefined,\n onUpdate: ToolUpdateHandler,\n): Promise<ToolResult> => {\n const mode = params.mode ?? 'single';\n if (mode === 'single') {\n if (typeof params.agent !== 'string' || typeof params.task !== 'string') {\n throw new TypeError('Single mode requires an agent and task');\n }\n return await handleSingleMode(pi, state, service, params.agent, params.task, signal, onUpdate);\n }\n if ((mode === 'parallel' || mode === 'chain') && Array.isArray(params.tasks)) {\n return await (mode === 'parallel'\n ? handleParallelMode(pi, state, service, params.tasks, signal, onUpdate)\n : handleChainMode(pi, state, service, params.tasks, signal, onUpdate));\n }\n throw new Error('Unknown dispatch mode');\n};\n\nexport const SUBAGENT_PARAMETERS = Type.Object({\n agent: Type.String({\n description:\n 'Specialist agent name (required): adventurer, architect, builder, diagnose, planner, reviewer, writer',\n }),\n mode: Type.Optional(\n Type.Union([Type.Literal('parallel'), Type.Literal('chain'), Type.Literal('single')]),\n ),\n task: Type.String({ description: 'Task description for the subagent (required)' }),\n tasks: Type.Optional(\n Type.Array(Type.Object({ agent: Type.String(), task: Type.String() }), {\n description: 'Array of task objects for parallel or chain dispatch',\n }),\n ),\n});\n\nexport type SubagentToolParams = Static<typeof SUBAGENT_PARAMETERS>;\n\nconst unavailableResult = (): ToolResult => ({\n content: [\n {\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 type: 'text',\n },\n ],\n details: {},\n});\n\nconst handoffResult = (agentName: string, taskDesc: string): ToolResult => ({\n content: [\n {\n text: [\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 type: 'text',\n },\n ],\n details: {},\n});\n\nconst executeSubagent = async (\n pi: SubagentEventHost,\n state: MaestriaState,\n params: SubagentParams,\n signal: AbortSignal | undefined,\n onUpdate: ToolUpdateHandler,\n): Promise<ToolResult> => {\n const { getSubagentsService } = await import('@gotgenes/pi-subagents');\n const service = getSubagentsService();\n if (!service || typeof service.spawn !== 'function') {\n return unavailableResult();\n }\n try {\n return await dispatchByMode(pi, state, service, params, signal, onUpdate);\n } catch (error) {\n console.warn('[maestria] Subagent dispatch failed:', error);\n const agentName = params.agent ?? params.tasks?.[0]?.agent ?? 'unknown';\n const taskDesc = params.task ?? params.tasks?.map((task) => task.task).join('; ') ?? 'unknown';\n return handoffResult(agentName, taskDesc);\n }\n};\n\nconst createSubagentTool = (\n pi: SubagentEventHost,\n state: MaestriaState,\n): SubagentToolDefinition => ({\n description: 'Dispatch a task to a @gotgenes/pi-subagents specialist subagent',\n async execute(\n _toolCallId: string,\n params: SubagentParams,\n signal: AbortSignal | undefined,\n onUpdate: PiToolUpdateHandler,\n _ctx: unknown,\n ): Promise<ToolResult> {\n if (state.reviewMode) {\n return {\n content: [\n {\n text: 'Subagent dispatch is not available during review mode. Use /restore-model to exit review mode first.',\n type: 'text',\n },\n ],\n details: {},\n };\n }\n const mode = validatePiParams(params);\n if (mode.startsWith('Invalid')) {\n return {\n content: [\n {\n text: `${mode} Re-dispatch with a valid agent name; the orchestrator may continue read-only exploration while the brief is corrected.`,\n type: 'text',\n },\n ],\n details: {},\n };\n }\n const updateHandler: ToolUpdateHandler = onUpdate\n ? (result) => {\n onUpdate({\n content: result.content.map(({ text }) => ({ text, type: 'text' as const })),\n details: result.details ?? {},\n });\n }\n : undefined;\n return await executeSubagent(pi, state, params, signal, updateHandler);\n },\n label: 'Maestria Subagent',\n name: 'maestria_subagent',\n parameters: SUBAGENT_PARAMETERS,\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 promptSnippet:\n 'Delegate tasks to @maestria specialist subagents (adventurer, architect, builder, planner, diagnose, reviewer, writer)',\n});\n\nexport const installSubagentTool = (\n pi: SubagentToolApi,\n state: MaestriaState,\n cleanups?: (() => void)[],\n): void => {\n pi.registerTool(createSubagentTool(pi, state));\n subscribeSubagentEvents(pi, state, cleanups);\n};\n","import { defineTool } from '@earendil-works/pi-coding-agent';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport type {\n PiToolUpdateHandler,\n SubagentToolApi,\n SubagentToolDefinition,\n SubagentToolParams,\n ToolResult,\n} from '@/subagent.js';\n\nexport const createSubagentToolApi = (pi: ExtensionAPI): SubagentToolApi => ({\n appendEntry: (type, data) => {\n pi.appendEntry(type, data);\n },\n events: pi.events,\n registerTool: (tool: SubagentToolDefinition) => {\n pi.registerTool(\n defineTool({\n ...tool,\n async execute(\n toolCallId: string,\n params: SubagentToolParams,\n signal: AbortSignal | undefined,\n onUpdate: PiToolUpdateHandler,\n ctx: ExtensionContext,\n ): Promise<ToolResult> {\n return await tool.execute(toolCallId, params, signal, onUpdate, ctx);\n },\n }),\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\nimport {\n persistState as persistStateCore,\n recordFileModified,\n recordFileRead,\n} from './state-core.js';\nimport type { MaestriaState } from './state-core.js';\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+\\//u,\n /dd\\s+if=/u,\n />\\s*\\/dev\\/sd/u,\n /chmod\\s+-R\\s+777\\s+\\//u,\n /mkfs\\.\\w+/u,\n /:\\(\\)\\{ :\\|:& \\};:/u,\n />\\s*\\/etc\\/(?<targetFile>passwd|shadow|sudoers)/u,\n /\\beval\\b/u,\n /wget\\s+-O\\s*-\\s*\\|\\s*(?<wgetShell>bash|sh)/u,\n /curl\\s+.*\\|\\s*(?<curlShell>bash|sh)/u,\n /crontab\\s+-r/u,\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 /^(?<command>ls|cat|head|tail|git status|git diff|git log|git branch|find|grep|rg|pnpm test|npm test|pwd|which)\\b/u;\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 const isReadOnlyBashCommand = (rawCommand: string): boolean => {\n const command = rawCommand.trim();\n if (command.includes('$(') || command.includes('`')) {\n return false;\n }\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.replaceAll(/\\d?>&[12]/gu, '');\n if (withoutFdRedirects.includes('>')) {\n return false;\n }\n return withoutFdRedirects\n .split(/[\\n;&|]+/u)\n .every((segment) => READ_ONLY_BASH_PREFIX.test(segment.trim()));\n};\n\n// ── Pure helpers ──\n\nexport const findDangerousPattern = (command: string): RegExp | null => {\n for (const pattern of DANGEROUS_PATTERNS) {\n if (pattern.test(command)) {\n return pattern;\n }\n }\n return null;\n};\n\n/** Alias for findDangerousPattern - returns matching pattern or null. */\nexport const isDangerousCommand = findDangerousPattern;\n\nexport const shouldBlockForOrchestrator = (\n state: { mode: unknown },\n activeTools: string[],\n delegationTool: string,\n): boolean => state.mode !== null && activeTools.includes(delegationTool);\n\nexport const getBlockedReviewReason = (toolName: string): string | null => {\n if (toolName === 'edit' || toolName === 'write' || toolName === 'bash') {\n return 'Review mode is active. Report findings, do not edit.';\n }\n return null;\n};\n\nexport const isOrchestratorBlocked = (\n state: { mode: unknown },\n activeTools: string[],\n delegationTool: string,\n): boolean => shouldBlockForOrchestrator(state, activeTools, delegationTool);\n\nexport const findDangerousPatternForCommand = (command: string): RegExp | null =>\n findDangerousPattern(command);\n\n// ── Factory ──\n\nexport interface ToolCallEventLike {\n toolName?: string;\n input?: unknown;\n}\n\nexport interface ToolCallContext {\n hasUI?: boolean;\n ui?: { confirm: (title: string, msg: string) => Promise<boolean> };\n}\n\nexport interface ToolCallResult {\n block: boolean;\n reason: string;\n}\n\nexport type ToolCallHandler = (\n event: ToolCallEventLike,\n ctx: ToolCallContext,\n) => Promise<ToolCallResult | undefined>;\n\nexport interface ToolCallHandlerOptions {\n getState: () => MaestriaState;\n getActiveTools: () => string[];\n delegationTool: string;\n delegationHint?: string;\n extraMutations?: string[];\n isMutationTool?: (event: ToolCallEventLike) => boolean;\n isReadTool: (event: ToolCallEventLike) => boolean;\n isWriteTool: (event: ToolCallEventLike) => boolean;\n isBashTool: (event: ToolCallEventLike) => boolean;\n persist?: () => void;\n pi?: { appendEntry: (type: string, data: unknown) => void };\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst resolveDelegationHint = (delegationTool: string, hint?: string): string =>\n hint ??\n (delegationTool === 'task'\n ? \"Use 'maestria_subagent' or 'task()' to delegate mutations to specialists.\"\n : \"Use 'maestria_subagent' to delegate mutations to specialists.\");\n\nconst checkOrchestratorBlock = (\n state: MaestriaState,\n event: ToolCallEventLike,\n options: ToolCallHandlerOptions,\n delegationTool: string,\n hint: string,\n): { block: boolean; reason: string } | undefined => {\n if (state.mode === null || !options.getActiveTools().some((t) => t === delegationTool)) {\n return undefined;\n }\n const isMutationTool =\n options.isMutationTool ??\n ((e: ToolCallEventLike) => {\n const name = e.toolName ?? '';\n const base = name === 'edit' || name === 'write' || name === 'patch' || name === 'bash';\n if (base) {\n return true;\n }\n return options.extraMutations?.some((m) => m === name) === true;\n });\n if (!isMutationTool(event) || event.toolName === delegationTool) {\n return undefined;\n }\n if (options.isBashTool(event)) {\n const input = isRecord(event.input) ? event.input : 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: `Tool '${event.toolName}' is blocked for the orchestrator. ${hint}`,\n };\n};\n\nconst checkDangerousPattern = async (\n event: ToolCallEventLike,\n options: ToolCallHandlerOptions,\n ctx: ToolCallContext,\n): Promise<{ block: boolean; reason: string } | undefined> => {\n if (!options.isBashTool(event)) {\n return undefined;\n }\n const input = isRecord(event.input) ? event.input : undefined;\n if (!input) {\n return undefined;\n }\n const { command } = input;\n if (typeof command !== 'string' || !command) {\n return undefined;\n }\n const matched = findDangerousPattern(command);\n if (!matched) {\n return undefined;\n }\n if (ctx.hasUI === true && ctx.ui) {\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) {\n return undefined;\n }\n }\n return { block: true, reason: `Command matches dangerous pattern: ${matched}` };\n};\n\nconst trackFileAccess = (\n state: MaestriaState,\n event: ToolCallEventLike,\n options: ToolCallHandlerOptions,\n): boolean => {\n if (options.isReadTool(event)) {\n const p = isRecord(event.input) ? event.input.path : undefined;\n if (typeof p === 'string' && p) {\n Object.assign(state, recordFileRead(state, p));\n return true;\n }\n } else if (options.isWriteTool(event)) {\n const p = isRecord(event.input) ? event.input.path : undefined;\n if (typeof p === 'string' && p) {\n Object.assign(state, recordFileModified(state, p));\n return true;\n }\n }\n return false;\n};\n\nexport const createToolCallHandler = (options: ToolCallHandlerOptions): ToolCallHandler => {\n const { delegationTool } = options;\n const hint = resolveDelegationHint(delegationTool, options.delegationHint);\n const doPersist = (): void => {\n if (options.persist) {\n options.persist();\n return;\n }\n if (options.pi) {\n persistStateCore(options.pi, options.getState());\n }\n };\n return async (\n event: ToolCallEventLike,\n ctx: ToolCallContext,\n ): Promise<{ block: boolean; reason: string } | undefined> => {\n if (event.toolName === undefined || event.toolName === '') {\n return undefined;\n }\n const state = options.getState();\n const { toolName } = event;\n const orchestrator = checkOrchestratorBlock(state, event, options, delegationTool, hint);\n if (orchestrator) {\n return orchestrator;\n }\n if (state.reviewMode && (options.isWriteTool(event) || options.isBashTool(event))) {\n const reason =\n getBlockedReviewReason(toolName) ?? 'Review mode is active. Report findings, do not edit.';\n return { block: true, reason };\n }\n const dangerous = await checkDangerousPattern(event, options, ctx);\n if (dangerous) {\n return dangerous;\n }\n if (trackFileAccess(state, event, options)) {\n doPersist();\n }\n return undefined;\n };\n};\n","import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { createToolCallHandler } from '@maestria/shared-pi/tools-core';\nimport type { ToolCallHandler } from '@maestria/shared-pi/tools-core';\n\nimport type { MaestriaState } from '@/state.js';\nimport { persistState } from '@/state.js';\n\nexport interface ToolApi {\n appendEntry: (type: string, data: unknown) => void;\n getActiveTools: () => string[];\n on: (event: 'tool_call', handler: ToolCallHandler) => void;\n}\n\nexport const createToolApi = (pi: ExtensionAPI): ToolApi => ({\n appendEntry: (type, data) => {\n pi.appendEntry(type, data);\n },\n getActiveTools: () => pi.getActiveTools(),\n on: (_event, handler) => {\n pi.on('tool_call', async (event, ctx) => await handler(event, ctx));\n },\n});\n\nexport const installToolInterceptors = (pi: ToolApi, state: MaestriaState): void => {\n const handler = createToolCallHandler({\n delegationTool: 'subagent',\n getActiveTools: () => pi.getActiveTools(),\n getState: () => state,\n isBashTool: (e) => e.toolName === 'bash',\n isMutationTool: (e) =>\n e.toolName === 'edit' ||\n e.toolName === 'write' ||\n e.toolName === 'patch' ||\n e.toolName === 'bash',\n isReadTool: (e) => e.toolName === 'read',\n isWriteTool: (e) => e.toolName === 'edit' || e.toolName === 'write',\n persist: () => {\n persistState(pi, state);\n },\n });\n pi.on('tool_call', handler);\n};\n","import type {\n ExtensionAPI,\n ExtensionContext,\n SessionStartEvent,\n SessionTreeEvent,\n} from '@earendil-works/pi-coding-agent';\n\nimport { deploySpecialistAgents } from '@/agents.js';\nimport { createCommandsApi, installCommands } from '@/commands.js';\nimport { createCompactionApi, installCompactionHandlers } from '@/compaction.js';\nimport { createModeCommandsApi, installModeAutoDetect, installModeCommands } from '@/modes.js';\nimport { createModePromptHandler } from '@/rules.js';\nimport { createInitialState } from '@/state.js';\nimport type { MaestriaState } from '@/state.js';\nimport { installSubagentTool } from '@/subagent.js';\nimport { createSubagentToolApi } from '@/subagent-api.js';\nimport { createToolApi, installToolInterceptors } from '@/tools.js';\n\ninterface PersistedStateEntry {\n type: string;\n customType?: string;\n data?: unknown;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst 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') {\n return null;\n }\n\n const branch = sessionManager.getBranch();\n return Array.isArray(branch) ? branch : null;\n};\n\nconst clearState = (state: MaestriaState): void => {\n for (const key of Object.keys(state)) {\n Reflect.deleteProperty(state, key);\n }\n};\n\nconst restoreStateFromSession = (state: MaestriaState, ctx: ExtensionContext): void => {\n const next = createInitialState();\n const entries = currentSessionEntries(ctx);\n\n if (!entries) {\n clearState(state);\n Object.assign(state, next);\n return;\n }\n\n for (let i = entries.length - 1; i >= 0; i -= 1) {\n const entry = entries[i];\n if (entry.type === 'custom' && entry.customType === 'maestria_state') {\n if (isRecord(entry.data)) {\n Object.assign(next, entry.data);\n }\n break;\n }\n }\n\n clearState(state);\n Object.assign(state, next);\n};\n\nconst extension = (pi: ExtensionAPI): void => {\n const state = createInitialState();\n const cleanups: (() => void)[] = [];\n\n // Install mode commands: /fein, /sonar, /blitz\n installModeCommands(createModeCommandsApi(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) => handleModePrompt(event, ctx));\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(createCompactionApi(pi), state);\n\n // Install orchestration hooks: subagent tool and commands\n installSubagentTool(createSubagentToolApi(pi), state, cleanups);\n installCommands(createCommandsApi(pi), state);\n\n // Cleanup subscriptions on shutdown\n pi.on('session_shutdown', () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n cleanups.length = 0;\n });\n\n // Install tool call interceptors for review mode and dangerous patterns\n installToolInterceptors(createToolApi(pi), state);\n};\n\nexport default extension;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maestria/pi",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "Maestria extension for the Pi coding agent",
5
5
  "keywords": [
6
6
  "agent-orchestration",
@@ -33,7 +33,7 @@
33
33
  "@types/node": "^26.2.0",
34
34
  "typescript": "^7.0.2",
35
35
  "vitest": "4.1.11",
36
- "@maestria/core": "0.8.2",
36
+ "@maestria/core": "0.8.3",
37
37
  "@maestria/shared-pi": "0.4.0"
38
38
  },
39
39
  "peerDependencies": {
@@ -22,6 +22,7 @@ Cross-platform behavior contract for outcomes, evidence, safety, delegation, rev
22
22
  - **!!! Match effort to stakes.** Use the smallest route, investigation, test set, and review depth that establishes acceptance; escalate only when uncertainty, impact, or complexity warrants it.
23
23
  - **!!! Prefer reuse over reinvention.** Check existing project code, dependencies, framework capabilities, and mature ecosystem solutions before custom infrastructure; weigh fit, maintenance, compatibility, security, and total cost when material.
24
24
  - **!!! Exhaust available evidence before asking.** Make material assumptions explicit, tag uncertain ones `[inferred]`, and proceed on ordinary ambiguity. Ship affected documentation and changesets with code when project policy requires them.
25
+ - **!!! Exercise testing judgment, not coverage.** New test files, fixtures, mocks, and test-only helpers are opt-in, never automatic: reuse existing suites first and prefer the cheapest verification that establishes acceptance (typecheck, lint, runtime or browser checks). Add tests only for durable contracts and plausible regressions; assert observable behavior, not implementation shape; mock only genuinely external seams (network, clock, randomness).
25
26
  - **!!! Keep output self-contained and professional.** Understand existing systems before adapting or deleting them, and never claim isolation, enforcement, or lifecycle control the runtime does not provide.
26
27
  - **!!! Human-facing output.** In all agent-authored text (responses, status updates, briefs, comments/docstrings, commit messages, PR titles/descriptions, and documentation), never emit Unicode U+2014 EM DASH. Prefer commas, colons, parentheses, or ASCII hyphen-minus (`-`). Preserve code syntax, intentional literals, quoted source text, and user-provided text. Scan authored output before handoff or delivery.
27
28
 
@@ -53,7 +54,7 @@ Default to one independent review and, only when blockers exist, one repair/re-r
53
54
 
54
55
  Safety and authorization override user intent, methodology, and brevity. Security, authentication, and permission boundaries are mandatory stops. Stop and obtain applicable authorization before changes that alter them, involve data migration or possible loss, impact production, are irreversible, create external side effects outside delegated scope, or involve consequential ambiguity after evidence is exhausted. Ordinary in-scope security defects may be repaired autonomously.
55
56
 
56
- The orchestrator owns continuation for implementation and delivery work until the outcome reaches its terminal artifact; incomplete todos, pending handoffs, or specialist messages saying "continue if needed" are not a user checkpoint. Routine delivery is autonomous. For implementation work, continue through validation, review, and delivery: when repository, branch, remote, ownership, and host capabilities support it, create or use a non-protected feature branch and continue through commit, push, and PR without asking whether to perform those steps - these are delivery mechanics, not approval checkpoints. Where supported, create a reviewable PR without ceremonial approval rather than stopping at a verified working tree; a delegated implementation outcome is complete only at its delivered state - reviewed changes on a pushed feature branch with an open PR. Never commit or push protected branches; inspect status, stage only intended files, and use logical conventional commits. Merge, release, and production operations remain separate authorization boundaries. Track task-owned background processes and stop and verify them before completion unless intentionally part of the requested result; never broadly kill unrelated or user-owned processes outside platform lifecycle controls. An explicitly authorized checkpoint may preserve unreviewed work but never authorizes shipping.
57
+ The orchestrator owns continuation for implementation and delivery work until the outcome reaches its terminal artifact; incomplete todos, pending handoffs, or specialist messages saying "continue if needed" are not a user checkpoint. Routine delivery is autonomous. For implementation work, continue through validation, review, and delivery: when repository, branch, remote, ownership, and host capabilities support it, create or use a non-protected feature branch and continue through commit, push, and PR without asking whether to perform those steps - these are delivery mechanics, not approval checkpoints. Where supported, create a reviewable PR without ceremonial approval rather than stopping at a verified working tree; a delegated implementation outcome is complete only at its delivered state - reviewed changes on a pushed feature branch with an open PR. Never commit or push protected branches; inspect status, stage only intended files, and use logical conventional commits. Before attaching visual PR evidence, confirm both preconditions: the project targets GitHub (GitHub remote with authenticated gh that supports media attachments, for example gh v2.99.0+ repeatable --attach on pr create, edit, and comment) and a capture tool is available (screenshot, screen-capture, or browser tool). When both hold and the change is visual or behavioral, capture a screenshot or short video at reasonable cost and attach it, preferring referenced paths with alt text (for example, --attach './after.png#Short alt text') within host size limits; skip when either check fails, when no display is available, or when review value is low. Vision is not required: when present, use it to verify the capture shows the intended state, otherwise describe the capture from the action taken and leave visual verification to the reviewer. Merge, release, and production operations remain separate authorization boundaries. Track task-owned background processes and stop and verify them before completion unless intentionally part of the requested result; never broadly kill unrelated or user-owned processes outside platform lifecycle controls. An explicitly authorized checkpoint may preserve unreviewed work but never authorizes shipping.
57
58
 
58
59
  ## Canonical Source Invariant
59
60
 
@@ -77,7 +77,7 @@ Modes are case-insensitive and per-turn.
77
77
 
78
78
  For implementation work, own the delivery path: inspect -> plan -> implement -> validate -> one independent review -> repair material blockers only when required -> targeted validation of repaired scope -> final verification -> commit -> push -> PR.
79
79
 
80
- **Routine delivery is autonomous.** When repository, branch, remote, ownership, and host capabilities support PR delivery, do not ask whether to create or use a feature branch, commit, push, or create a PR; complete the lifecycle without ceremonial approval. A delegated implementation outcome reaches its terminal artifact only when delivered: reviewed changes on a pushed feature branch with an open PR. Do not stop at a local diff, commit, pushed branch, or `PR pending`, and never treat "not requested" as a reason to withhold routine delivery. Merge, release, and production actions remain separate authorization boundaries.
80
+ **Routine delivery is autonomous.** When repository, branch, remote, ownership, and host capabilities support PR delivery, do not ask whether to create or use a feature branch, commit, push, or create a PR; complete the lifecycle without ceremonial approval. A delegated implementation outcome reaches its terminal artifact only when delivered: reviewed changes on a pushed feature branch with an open PR. Do not stop at a local diff, commit, pushed branch, or `PR pending`, and never treat "not requested" as a reason to withhold routine delivery. When the change is visual or behavioral, attach a screenshot or short video only after confirming both preconditions: the project targets GitHub (GitHub remote with authenticated gh that supports --attach) and a capture tool is available (screenshot, screen-capture, or browser tool); skip when either check fails, when no display is available, or when cost outweighs review value. Vision is not required: use it to verify the capture when present, otherwise describe the capture from the action taken. Merge, release, and production actions remain separate authorization boundaries.
81
81
 
82
82
  The parent session owns continuation until the selected implementation outcome reaches its terminal artifact. Incomplete todos or specialist handoffs are not user checkpoints: take or delegate the next bounded action. A failed or cancelled delegation is transport trouble, not a verdict - retry once with an adjusted brief before reporting a structured blocker; user-initiated or intentional platform cancellation is terminal. Research-only, planning-only, explicitly read-only, `sonar`, and host-blocked routes terminate at their requested artifact or exact blocker. Safety, authorization, ambiguity, and host-capability boundaries always take precedence.
83
83