@maestria/prime-agent 0.3.2 → 0.3.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.
package/README.md CHANGED
@@ -29,7 +29,7 @@ For skills-only installs, point Prime at the package's `skills/` directory in se
29
29
  - **Verified subset only:** the extension covers mode commands and mode prompt injection. There is no recursive-subagent dispatch - "delegate to a specialist" loads the relevant skill and applies its methodology. JSON/RPC headless-mode integration is deferred.
30
30
  - **Advisory, not enforced:** skills, rules, and role prompts are guidance, not security enforcement. Prime has no skill-level tool-denial mechanism, so read-only roles state their role intent without claiming a runtime boundary.
31
31
  - **Not a sandbox:** Prime executes model-generated Python and project commands with your user permissions. Restrict use to trusted repositories, skills, and instructions.
32
- - **No filesystem writes:** nothing is written to `~/.pi` or `.prime/agent`.
32
+ - **Extension has no filesystem writes:** the compiled extension does not write to `~/.pi` or `.prime/agent`; Prime's package manager still manages its own registration files.
33
33
  - **No extra dependencies:** the extension uses only the Prime-bundled API; no pi package dependency is required.
34
34
 
35
35
  ## Documentation and Changelog
@@ -1,4 +1,4 @@
1
- import{dirname as e,join as t,resolve as n}from"node:path";import{fileURLToPath as r}from"node:url";import{readFileSync as i}from"node:fs";function a(){return{mode:null}}function o(e){let t=e;return t.type===`custom`&&t.customType===`maestria_mode`}function s(e){if(typeof e!=`object`||!e)return!1;let t=e.mode;return t===null||t===`fein`||t===`sonar`||t===`blitz`}function c(e){if(!Array.isArray(e))return null;for(let t=e.length-1;t>=0;t--){let n=e[t];if(o(n)&&s(n.data))return n.data}return null}function l(e,t){e.appendEntry(`maestria_mode`,{mode:t.mode})}function u(e,t){e.mode=c(t)?.mode??null}const d=[`fein`,`sonar`,`blitz`],f={fein:`[MODE: fein]`,sonar:`[MODE: sonar]`,blitz:`[MODE: blitz]`},p={fein:`Set workflow mode to fein (full pipeline)`,sonar:`Set workflow mode to sonar (research only)`,blitz:`Set workflow mode to blitz (fast path)`},m={};function h(e,n){if(e in m)return m[e];let r=``;try{let a=i(t(n,e,`SKILL.md`),`utf8`),o=a.indexOf(`## MODE:`);if(o===-1)console.warn(`[maestria] prime-agent: mode skill "${e}" has no "## MODE:" heading; mode prompt injection disabled for this mode.`);else{let t=a.slice(o);r=`${f[e]}\n\n${t.replace(/\s+$/,``)}\n`}}catch(t){console.warn(`[maestria] prime-agent: failed to load mode skill "${e}" from ${n}; mode prompt injection disabled for this mode.`,t)}return m[e]=r,r}function g(e,t){return n=>{if(!e.mode)return;let r=h(e.mode,t);if(r)return{systemPrompt:[n.systemPrompt,``,r,``,`The user has set workflow mode to "${e.mode}". Honor this mode throughout the session until it is changed or cleared.`].join(`
2
- `)}}}function _(e,t){for(let n of d)e.registerCommand(n,{description:p[n],handler:async(r,i)=>{t.mode=n,l(e,t),r.trim()?e.sendUserMessage(r.trim(),{deliverAs:`steer`}):i.ui.notify(`Mode set to ${n}. Describe what you'd like to work on.`)}});e.registerCommand(`mode-clear`,{description:`Clear workflow mode and return to neutral routing`,handler:async(n,r)=>{t.mode=null,l(e,t),r.ui.notify(`Workflow mode cleared. Neutral routing is active.`)}}),e.registerCommand(`maestria-status`,{description:`Show the current maestria workflow mode and extension subset`,handler:async(e,n)=>{let r=[`# Maestria status (prime-agent)`,``,`Workflow mode: ${t.mode??`none`}`,``,`Commands: /fein, /sonar, /blitz, /mode-clear`,``,`This extension covers mode selection and mode prompt injection only.`,`Recursive-subagent (rlm) dispatch and JSON/RPC headless mode are NOT provided by this package.`].join(`
3
- `);n.ui.setEditorText(r)}})}function v(){let t=e(r(import.meta.url));return n(t,`../skills`)}function y(e){let t=a(),n=v();_(e,t),e.on(`before_agent_start`,g(t,n)),e.on(`session_start`,(e,n)=>{u(t,n.sessionManager.getBranch())}),e.on(`session_tree`,(e,n)=>{u(t,n.sessionManager.getBranch())})}export{y as default};
1
+ import e from"node:path";import{readFileSync as t}from"node:fs";const n=()=>({mode:null}),r=e=>e.type===`custom`&&`customType`in e&&e.customType===`maestria_mode`,i=e=>{if(typeof e!=`object`||!e||!(`mode`in e))return!1;let{mode:t}=e;return t===null||t===`fein`||t===`sonar`||t===`blitz`},a=e=>{if(!Array.isArray(e))return null;for(let t=e.length-1;t>=0;--t){let n=e[t];if(r(n)&&i(n.data))return n.data}return null},o=(e,t)=>{e.appendEntry(`maestria_mode`,{mode:t.mode})},s=(e,t)=>{e.mode=a(t)?.mode??null},c=[`fein`,`sonar`,`blitz`],l={blitz:`[MODE: blitz]`,fein:`[MODE: fein]`,sonar:`[MODE: sonar]`},u={blitz:`Set workflow mode to blitz (fast path)`,fein:`Set workflow mode to fein (full pipeline)`,sonar:`Set workflow mode to sonar (research only)`},d={},f=(n,r)=>{let i=d[n];if(i!==void 0)return i;let a=``;try{let i=t(e.join(r,n,`SKILL.md`),`utf-8`),o=i.indexOf(`## MODE:`);if(o===-1)console.warn(`[maestria] prime-agent: mode skill "${n}" has no "## MODE:" heading; mode prompt injection disabled for this mode.`);else{let e=i.slice(o);a=`${l[n]}\n\n${e.replace(/\s+$/u,``)}\n`}}catch(e){console.warn(`[maestria] prime-agent: failed to load mode skill "${n}" from ${r}; mode prompt injection disabled for this mode.`,e)}return d[n]=a,a},p=(e,t)=>n=>{if(!e.mode)return;let r=f(e.mode,t);if(r)return{systemPrompt:[n.systemPrompt,``,r,``,`The user has set workflow mode to "${e.mode}". Honor this mode throughout the session until it is changed or cleared.`].join(`
2
+ `)}},m=(e,t)=>{for(let n of c)e.registerCommand(n,{description:u[n],handler:async(r,i)=>{t.mode=n,o(e,t),r.trim()?e.sendUserMessage(r.trim(),{deliverAs:`steer`}):i.ui.notify(`Mode set to ${n}. Describe what you'd like to work on.`),await Promise.resolve()}});e.registerCommand(`mode-clear`,{description:`Clear workflow mode and return to neutral routing`,handler:async(n,r)=>{t.mode=null,o(e,t),r.ui.notify(`Workflow mode cleared. Neutral routing is active.`),await Promise.resolve()}}),e.registerCommand(`maestria-status`,{description:`Show the current maestria workflow mode and extension subset`,handler:async(e,n)=>{let r=[`# Maestria status (prime-agent)`,``,`Workflow mode: ${t.mode??`none`}`,``,`Commands: /fein, /sonar, /blitz, /mode-clear`,``,`This extension covers mode selection and mode prompt injection only.`,`Recursive-subagent (rlm) dispatch and JSON/RPC headless mode are NOT provided by this package.`].join(`
3
+ `);n.ui.setEditorText(r),await Promise.resolve()}})},h=()=>{let t=import.meta.dirname;return e.resolve(t,`../skills`)},g=e=>{let t=n(),r=h();m(e,t),e.on(`before_agent_start`,p(t,r)),e.on(`session_start`,(e,n)=>{s(t,n.sessionManager.getBranch())}),e.on(`session_tree`,(e,n)=>{s(t,n.sessionManager.getBranch())})};export{g as default};
4
4
  //# sourceMappingURL=extension.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"extension.mjs","names":[],"sources":["../src/state.ts","../src/modes.ts","../src/extension.ts"],"sourcesContent":["// packages/prime-agent/src/state.ts\n// Minimal session-scoped state for the Prime extension: the active workflow\n// mode (fein/sonar/blitz) or none.\n//\n// State is persisted through the host session API (`pi.appendEntry`) as a\n// `custom` session entry with `customType: \"maestria_mode\"`. Custom entries\n// are session entries: they survive reloads, forks, and compaction, and they\n// are NOT part of LLM context. Restore reads only the current branch\n// (`sessionManager.getBranch()`), never a sibling branch of the session tree,\n// mirroring the @maestria/pi extension's state pattern. No files are written\n// (no `~/.pi`, no `.prime/agent` writes); everything rides on the host session.\n\nimport type { CustomEntry, ExtensionAPI, SessionEntry } from './pi-api.js';\n\n/** Session entry type used to persist the active mode. */\nexport const MODE_STATE_CUSTOM_TYPE = 'maestria_mode';\n\nexport interface MaestriaModeState {\n /** Active workflow mode, or null when neutral routing is active. */\n mode: 'fein' | 'sonar' | 'blitz' | null;\n}\n\nexport function createInitialState(): MaestriaModeState {\n return { mode: null };\n}\n\nfunction isCustomEntry(entry: SessionEntry): entry is CustomEntry & { data?: MaestriaModeState } {\n // SessionEntryBase.type is a plain string, so a discriminated-union narrowing\n // on `type` does not apply; cast to read the optional customType.\n const maybe = entry as SessionEntry & { customType?: string };\n return maybe.type === 'custom' && maybe.customType === MODE_STATE_CUSTOM_TYPE;\n}\n\nfunction isModeState(value: unknown): value is MaestriaModeState {\n if (typeof value !== 'object' || value === null) return false;\n const mode = (value as Record<string, unknown>).mode;\n return mode === null || mode === 'fein' || mode === 'sonar' || mode === 'blitz';\n}\n\n/**\n * Read the mode state from the current session branch: the most recent\n * `maestria_mode` custom entry wins. Returns null when no entry exists.\n */\nexport function readModeStateFromEntries(\n entries: SessionEntry[] | null | undefined,\n): MaestriaModeState | null {\n if (!Array.isArray(entries)) return null;\n // Entries are returned in tree order; the last matching entry is the most\n // recently appended one on the current branch.\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n if (isCustomEntry(entry) && isModeState(entry.data)) return entry.data;\n }\n return null;\n}\n\n/** Persist the current mode as a session custom entry (no LLM context). */\nexport function persistModeState(pi: ExtensionAPI, state: MaestriaModeState): void {\n pi.appendEntry(MODE_STATE_CUSTOM_TYPE, { mode: state.mode });\n}\n\n/**\n * Restore the mode state from the current session branch into `state`.\n * When the branch has no `maestria_mode` entry, mode resets to null\n * (fail-closed: never inherit a sibling branch's mode).\n */\nexport function restoreModeState(\n state: MaestriaModeState,\n entries: SessionEntry[] | null | undefined,\n): void {\n const persisted = readModeStateFromEntries(entries);\n state.mode = persisted?.mode ?? null;\n}\n","// packages/prime-agent/src/modes.ts\n// Prime-local implementation of the Maestria workflow modes (fein/sonar/blitz).\n//\n// Behavioral model: the @maestria/pi extension's mode implementation\n// (packages/pi/src/modes.ts + packages/shared/pi/src/modes-core.ts), adapted to\n// the Prime fork's public extension API and to this package's skills-first\n// projection. This module is deliberately self-contained (Prime-local thin\n// extension): it does not import @maestria/pi or @maestria/shared-pi, and it\n// uses only the public ExtensionAPI surface mirrored in ./pi-api.ts.\n//\n// Mode content is NOT duplicated here: it is loaded from the package's\n// generated skills (`skills/<mode>/SKILL.md`, the `## MODE:` section onward),\n// so the extension's injected prompt is exactly the sync-projected mode skill\n// (canonical content lives in packages/core/agent-directives/, ADR-CORE-005).\n\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type {\n BeforeAgentStartEvent,\n BeforeAgentStartEventResult,\n ExtensionAPI,\n ExtensionCommandContext,\n ExtensionContext,\n} from './pi-api.js';\nimport type { MaestriaModeState } from './state.js';\nimport { persistModeState } from './state.js';\n\nexport const MODE_KEYWORDS = ['fein', 'sonar', 'blitz'] as const;\nexport type ModeKeyword = (typeof MODE_KEYWORDS)[number];\n\n/** Marker line prepended to injected mode content (shared with other Maestria platforms). */\nexport const MODE_MARKERS: Record<ModeKeyword, string> = {\n fein: '[MODE: fein]',\n sonar: '[MODE: sonar]',\n blitz: '[MODE: blitz]',\n};\n\nconst MODE_COMMAND_DESCRIPTIONS: Record<ModeKeyword, string> = {\n fein: 'Set workflow mode to fein (full pipeline)',\n sonar: 'Set workflow mode to sonar (research only)',\n blitz: 'Set workflow mode to blitz (fast path)',\n};\n\n// ---------------------------------------------------------------------------\n// Mode prompt loading (from generated skills)\n// ---------------------------------------------------------------------------\n\nconst _promptCache: Partial<Record<ModeKeyword, string>> = {};\n\n/**\n * Load the mode prompt for a keyword from the package's generated skills\n * directory: `skills/<mode>/SKILL.md`, sliced from the `## MODE:` heading\n * onward, prefixed with the `[MODE: <mode>]` marker. Returns an empty string\n * (and warns) when the skill file is missing or has no mode section, so a\n * packaging mistake degrades to \"no injection\" rather than an extension crash.\n */\nexport function getModePrompt(keyword: ModeKeyword, skillsDir: string): string {\n if (keyword in _promptCache) return _promptCache[keyword]!;\n\n let prompt = '';\n try {\n const content = readFileSync(join(skillsDir, keyword, 'SKILL.md'), 'utf8');\n const modeIdx = content.indexOf('## MODE:');\n if (modeIdx === -1) {\n // A generated skill without the mode section must not leak the whole\n // SKILL.md into the system prompt: degrade to \"no injection\" instead.\n console.warn(\n `[maestria] prime-agent: mode skill \"${keyword}\" has no \"## MODE:\" heading; ` +\n `mode prompt injection disabled for this mode.`,\n );\n } else {\n const body = content.slice(modeIdx);\n prompt = `${MODE_MARKERS[keyword]}\\n\\n${body.replace(/\\s+$/, '')}\\n`;\n }\n } catch (error) {\n console.warn(\n `[maestria] prime-agent: failed to load mode skill \"${keyword}\" from ${skillsDir}; ` +\n `mode prompt injection disabled for this mode.`,\n error,\n );\n }\n _promptCache[keyword] = prompt;\n return prompt;\n}\n\n// ---------------------------------------------------------------------------\n// before_agent_start mode prompt injection\n// ---------------------------------------------------------------------------\n\n/**\n * Create the `before_agent_start` handler that appends the active mode prompt\n * to the chained system prompt. Returns void when no mode is active (no\n * modification), so Prime's normal prompt assembly stands as-is.\n */\nexport function createModePromptHandler(\n state: MaestriaModeState,\n skillsDir: string,\n): (event: BeforeAgentStartEvent, _ctx: ExtensionContext) => BeforeAgentStartEventResult | void {\n return (event: BeforeAgentStartEvent): BeforeAgentStartEventResult | void => {\n if (!state.mode) return;\n\n const modePrompt = getModePrompt(state.mode, skillsDir);\n if (!modePrompt) return;\n\n return {\n systemPrompt: [\n event.systemPrompt,\n '',\n modePrompt,\n '',\n `The user has set workflow mode to \"${state.mode}\". Honor this mode throughout the session until it is changed or cleared.`,\n ].join('\\n'),\n };\n };\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport const MODE_CLEAR_COMMAND = 'mode-clear';\nexport const STATUS_COMMAND = 'maestria-status';\n\n/**\n * Install the mode slash commands (`/fein`, `/sonar`, `/blitz`, `/mode-clear`)\n * and the status/help command (`/maestria-status`). Mode selection is persisted\n * as a session custom entry; the prompt is injected on the next agent turn by\n * the `before_agent_start` handler.\n */\nexport function installCommands(pi: ExtensionAPI, state: MaestriaModeState): void {\n for (const keyword of MODE_KEYWORDS) {\n pi.registerCommand(keyword, {\n description: MODE_COMMAND_DESCRIPTIONS[keyword],\n handler: async (args: string, ctx: ExtensionCommandContext) => {\n state.mode = keyword;\n persistModeState(pi, state);\n // Forward a goal argument (e.g. `/fein implement the pipeline`) so the\n // injected mode prompt's \"if the user provided a goal, run it now\"\n // instruction has the goal to act on.\n if (args.trim()) {\n pi.sendUserMessage(args.trim(), { deliverAs: 'steer' });\n } else {\n ctx.ui.notify(`Mode set to ${keyword}. Describe what you'd like to work on.`);\n }\n },\n });\n }\n\n pi.registerCommand(MODE_CLEAR_COMMAND, {\n description: 'Clear workflow mode and return to neutral routing',\n handler: async (_args: string, ctx: ExtensionCommandContext) => {\n state.mode = null;\n persistModeState(pi, state);\n ctx.ui.notify('Workflow mode cleared. Neutral routing is active.');\n },\n });\n\n pi.registerCommand(STATUS_COMMAND, {\n description: 'Show the current maestria workflow mode and extension subset',\n handler: async (_args: string, ctx: ExtensionCommandContext) => {\n const mode = state.mode ?? 'none';\n const summary = [\n '# Maestria status (prime-agent)',\n '',\n `Workflow mode: ${mode}`,\n '',\n 'Commands: /fein, /sonar, /blitz, /mode-clear',\n '',\n 'This extension covers mode selection and mode prompt injection only.',\n 'Recursive-subagent (rlm) dispatch and JSON/RPC headless mode are NOT provided by this package.',\n ].join('\\n');\n ctx.ui.setEditorText(summary);\n },\n });\n}\n","// packages/prime-agent/src/extension.ts\n// Prime Agent extension entry point (default-export factory).\n//\n// Compiled to `dist/extension.mjs` and declared in package.json under\n// `pi.extensions`; Prime loads it with its extension loader (pinned fork\n// 7787f07415d843b9a800f6a4720e0c739bd608e5, loader.ts: a jiti import of the\n// declared path calling the default export with the live ExtensionAPI).\n//\n// Verified subset (public Prime/Pi extension API only, see src/pi-api.ts):\n// - slash commands /fein /sonar /blitz /mode-clear and /maestria-status\n// - before_agent_start mode prompt injection (systemPrompt chaining)\n// - session-scoped mode state via custom session entries, restored on\n// session_start (reload/resume/fork) and session_tree (branch navigation)\n//\n// NOT provided (explicitly deferred, documented in README/INSTALL/ADR-CORE-014):\n// native recursive-subagent (`rlm`) dispatch - the pinned fork exposes no\n// public JS extension bridge for it (it is an IPython-side tool) - and\n// JSON/RPC headless mode integration. No tool interception is installed and no\n// sandbox/enforcement claim is made. This extension writes no files (no\n// `~/.pi`, no `.prime/agent` writes): state rides on host session entries.\n\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { ExtensionAPI } from './pi-api.js';\nimport { createInitialState, restoreModeState } from './state.js';\nimport { createModePromptHandler, installCommands } from './modes.js';\n\n/**\n * Resolve the package's generated `skills/` directory. When running from the\n * built `dist/extension.mjs`, this is `<packageRoot>/skills`; when running from\n * source (tests), it is the same package-relative location.\n */\nfunction resolveSkillsDir(): string {\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n return resolve(moduleDir, '../skills');\n}\n\nexport default function (pi: ExtensionAPI): void {\n const state = createInitialState();\n const skillsDir = resolveSkillsDir();\n\n // Mode commands + status command (session-scoped state, persisted via\n // pi.appendEntry custom entries).\n installCommands(pi, state);\n\n // Mode prompt injection on the next agent turn.\n pi.on('before_agent_start', createModePromptHandler(state, skillsDir));\n\n // Restore the active mode when a session starts, is reloaded, resumed, or\n // forked, and when navigating the session tree to a different branch.\n pi.on('session_start', (_event, ctx) => {\n restoreModeState(state, ctx.sessionManager.getBranch());\n });\n\n pi.on('session_tree', (_event, ctx) => {\n restoreModeState(state, ctx.sessionManager.getBranch());\n });\n}\n"],"mappings":"2IAsBA,SAAgB,GAAwC,CACtD,MAAO,CAAE,KAAM,IAAK,CACtB,CAEA,SAAS,EAAc,EAA0E,CAG/F,IAAM,EAAQ,EACd,OAAO,EAAM,OAAS,UAAY,EAAM,aAAA,eAC1C,CAEA,SAAS,EAAY,EAA4C,CAC/D,GAAI,OAAO,GAAU,WAAY,EAAgB,MAAO,GACxD,IAAM,EAAQ,EAAkC,KAChD,OAAO,IAAS,MAAQ,IAAS,QAAU,IAAS,SAAW,IAAS,OAC1E,CAMA,SAAgB,EACd,EAC0B,CAC1B,GAAI,CAAC,MAAM,QAAQ,CAAO,EAAG,OAAO,KAGpC,IAAK,IAAI,EAAI,EAAQ,OAAS,EAAG,GAAK,EAAG,IAAK,CAC5C,IAAM,EAAQ,EAAQ,GACtB,GAAI,EAAc,CAAK,GAAK,EAAY,EAAM,IAAI,EAAG,OAAO,EAAM,IACpE,CACA,OAAO,IACT,CAGA,SAAgB,EAAiB,EAAkB,EAAgC,CACjF,EAAG,YAAY,gBAAwB,CAAE,KAAM,EAAM,IAAK,CAAC,CAC7D,CAOA,SAAgB,EACd,EACA,EACM,CAEN,EAAM,KADY,EAAyB,CACtB,CAAC,EAAE,MAAQ,IAClC,CC7CA,MAAa,EAAgB,CAAC,OAAQ,QAAS,OAAO,EAIzC,EAA4C,CACvD,KAAM,eACN,MAAO,gBACP,MAAO,eACT,EAEM,EAAyD,CAC7D,KAAM,4CACN,MAAO,6CACP,MAAO,wCACT,EAMM,EAAqD,CAAC,EAS5D,SAAgB,EAAc,EAAsB,EAA2B,CAC7E,GAAI,KAAW,EAAc,OAAO,EAAa,GAEjD,IAAI,EAAS,GACb,GAAI,CACF,IAAM,EAAU,EAAa,EAAK,EAAW,EAAS,UAAU,EAAG,MAAM,EACnE,EAAU,EAAQ,QAAQ,UAAU,EAC1C,GAAI,IAAY,GAGd,QAAQ,KACN,uCAAuC,EAAQ,2EAEjD,MACK,CACL,IAAM,EAAO,EAAQ,MAAM,CAAO,EAClC,EAAS,GAAG,EAAa,GAAS,MAAM,EAAK,QAAQ,OAAQ,EAAE,EAAE,GACnE,CACF,OAAS,EAAO,CACd,QAAQ,KACN,sDAAsD,EAAQ,SAAS,EAAU,iDAEjF,CACF,CACF,CAEA,MADA,GAAa,GAAW,EACjB,CACT,CAWA,SAAgB,EACd,EACA,EAC8F,CAC9F,MAAQ,IAAqE,CAC3E,GAAI,CAAC,EAAM,KAAM,OAEjB,IAAM,EAAa,EAAc,EAAM,KAAM,CAAS,EACjD,KAEL,MAAO,CACL,aAAc,CACZ,EAAM,aACN,GACA,EACA,GACA,sCAAsC,EAAM,KAAK,0EACnD,CAAC,CAAC,KAAK;CAAI,CACb,CACF,CACF,CAeA,SAAgB,EAAgB,EAAkB,EAAgC,CAChF,IAAK,IAAM,KAAW,EACpB,EAAG,gBAAgB,EAAS,CAC1B,YAAa,EAA0B,GACvC,QAAS,MAAO,EAAc,IAAiC,CAC7D,EAAM,KAAO,EACb,EAAiB,EAAI,CAAK,EAItB,EAAK,KAAK,EACZ,EAAG,gBAAgB,EAAK,KAAK,EAAG,CAAE,UAAW,OAAQ,CAAC,EAEtD,EAAI,GAAG,OAAO,eAAe,EAAQ,uCAAuC,CAEhF,CACF,CAAC,EAGH,EAAG,gBAAgB,aAAoB,CACrC,YAAa,oDACb,QAAS,MAAO,EAAe,IAAiC,CAC9D,EAAM,KAAO,KACb,EAAiB,EAAI,CAAK,EAC1B,EAAI,GAAG,OAAO,mDAAmD,CACnE,CACF,CAAC,EAED,EAAG,gBAAgB,kBAAgB,CACjC,YAAa,+DACb,QAAS,MAAO,EAAe,IAAiC,CAE9D,IAAM,EAAU,CACd,kCACA,GACA,kBAJW,EAAM,MAAQ,SAKzB,GACA,+CACA,GACA,uEACA,gGACF,CAAC,CAAC,KAAK;CAAI,EACX,EAAI,GAAG,cAAc,CAAO,CAC9B,CACF,CAAC,CACH,CC9IA,SAAS,GAA2B,CAClC,IAAM,EAAY,EAAQ,EAAc,YAAY,GAAG,CAAC,EACxD,OAAO,EAAQ,EAAW,WAAW,CACvC,CAEA,SAAA,EAAyB,EAAwB,CAC/C,IAAM,EAAQ,EAAmB,EAC3B,EAAY,EAAiB,EAInC,EAAgB,EAAI,CAAK,EAGzB,EAAG,GAAG,qBAAsB,EAAwB,EAAO,CAAS,CAAC,EAIrE,EAAG,GAAG,iBAAkB,EAAQ,IAAQ,CACtC,EAAiB,EAAO,EAAI,eAAe,UAAU,CAAC,CACxD,CAAC,EAED,EAAG,GAAG,gBAAiB,EAAQ,IAAQ,CACrC,EAAiB,EAAO,EAAI,eAAe,UAAU,CAAC,CACxD,CAAC,CACH"}
1
+ {"version":3,"file":"extension.mjs","names":[],"sources":["../src/state.ts","../src/modes.ts","../src/extension.ts"],"sourcesContent":["// packages/prime-agent/src/state.ts\n// Minimal session-scoped state for the Prime extension: the active workflow\n// mode (fein/sonar/blitz) or none.\n//\n// State is persisted through the host session API (`pi.appendEntry`) as a\n// `custom` session entry with `customType: \"maestria_mode\"`. Custom entries\n// are session entries: they survive reloads, forks, and compaction, and they\n// are NOT part of LLM context. Restore reads only the current branch\n// (`sessionManager.getBranch()`), never a sibling branch of the session tree,\n// mirroring the @maestria/pi extension's state pattern. No files are written\n// (no `~/.pi`, no `.prime/agent` writes); everything rides on the host session.\n\nimport type { CustomEntry, ExtensionAPI, SessionEntry } from './pi-api.js';\n\n/** Session entry type used to persist the active mode. */\nexport const MODE_STATE_CUSTOM_TYPE = 'maestria_mode';\n\nexport interface MaestriaModeState {\n /** Active workflow mode, or null when neutral routing is active. */\n mode: 'fein' | 'sonar' | 'blitz' | null;\n}\n\nexport const createInitialState = (): MaestriaModeState => ({ mode: null });\n\nconst isCustomEntry = (entry: SessionEntry): entry is CustomEntry =>\n entry.type === 'custom' && 'customType' in entry && entry.customType === MODE_STATE_CUSTOM_TYPE;\n\nconst isModeState = (value: unknown): value is MaestriaModeState => {\n if (typeof value !== 'object' || value === null || !('mode' in value)) {\n return false;\n }\n const { mode } = value;\n return mode === null || mode === 'fein' || mode === 'sonar' || mode === 'blitz';\n};\n\n/**\n * Read the mode state from the current session branch: the most recent\n * `maestria_mode` custom entry wins. Returns null when no entry exists.\n */\nexport const readModeStateFromEntries = (\n entries: SessionEntry[] | null | undefined,\n): MaestriaModeState | null => {\n if (!Array.isArray(entries)) {\n return null;\n }\n // Entries are returned in tree order; the last matching entry is the most\n // recently appended one on the current branch.\n for (let i = entries.length - 1; i >= 0; i -= 1) {\n const entry = entries[i];\n if (isCustomEntry(entry) && isModeState(entry.data)) {\n return entry.data;\n }\n }\n return null;\n};\n\n/** Persist the current mode as a session custom entry (no LLM context). */\nexport const persistModeState = (pi: ExtensionAPI, state: MaestriaModeState): void => {\n pi.appendEntry(MODE_STATE_CUSTOM_TYPE, { mode: state.mode });\n};\n\n/**\n * Restore the mode state from the current session branch into `state`.\n * When the branch has no `maestria_mode` entry, mode resets to null\n * (fail-closed: never inherit a sibling branch's mode).\n */\nexport const restoreModeState = (\n state: MaestriaModeState,\n entries: SessionEntry[] | null | undefined,\n): void => {\n const persisted = readModeStateFromEntries(entries);\n state.mode = persisted?.mode ?? null;\n};\n","// packages/prime-agent/src/modes.ts\n// Prime-local implementation of the Maestria workflow modes (fein/sonar/blitz).\n//\n// Behavioral model: the @maestria/pi extension's mode implementation\n// (packages/pi/src/modes.ts + packages/shared/pi/src/modes-core.ts), adapted to\n// the Prime fork's public extension API and to this package's skills-first\n// projection. This module is deliberately self-contained (Prime-local thin\n// extension): it does not import @maestria/pi or @maestria/shared-pi, and it\n// uses only the public ExtensionAPI surface mirrored in ./pi-api.ts.\n//\n// Mode content is NOT duplicated here: it is loaded from the package's\n// generated skills (`skills/<mode>/SKILL.md`, the `## MODE:` section onward),\n// so the extension's injected prompt is exactly the sync-projected mode skill\n// (canonical content lives in packages/core/agent-directives/, ADR-CORE-005).\n\nimport { readFileSync } from 'node:fs';\nimport path from 'node:path';\n\nimport type {\n BeforeAgentStartEvent,\n BeforeAgentStartEventResult,\n ExtensionAPI,\n ExtensionCommandContext,\n ExtensionContext,\n} from './pi-api.js';\nimport type { MaestriaModeState } from './state.js';\nimport { persistModeState } from './state.js';\n\nexport const MODE_KEYWORDS = ['fein', 'sonar', 'blitz'] as const;\nexport type ModeKeyword = (typeof MODE_KEYWORDS)[number];\n\n/** Marker line prepended to injected mode content (shared with other Maestria platforms). */\nexport const MODE_MARKERS: Record<ModeKeyword, string> = {\n blitz: '[MODE: blitz]',\n fein: '[MODE: fein]',\n sonar: '[MODE: sonar]',\n};\n\nconst MODE_COMMAND_DESCRIPTIONS: Record<ModeKeyword, string> = {\n blitz: 'Set workflow mode to blitz (fast path)',\n fein: 'Set workflow mode to fein (full pipeline)',\n sonar: 'Set workflow mode to sonar (research only)',\n};\n\n// ---------------------------------------------------------------------------\n// Mode prompt loading (from generated skills)\n// ---------------------------------------------------------------------------\n\nconst _promptCache: Partial<Record<ModeKeyword, string>> = {};\n\n/**\n * Load the mode prompt for a keyword from the package's generated skills\n * directory: `skills/<mode>/SKILL.md`, sliced from the `## MODE:` heading\n * onward, prefixed with the `[MODE: <mode>]` marker. Returns an empty string\n * (and warns) when the skill file is missing or has no mode section, so a\n * packaging mistake degrades to \"no injection\" rather than an extension crash.\n */\nexport const getModePrompt = (keyword: ModeKeyword, skillsDir: string): string => {\n const cachedPrompt = _promptCache[keyword];\n if (cachedPrompt !== undefined) {\n return cachedPrompt;\n }\n\n let prompt = '';\n try {\n const content = readFileSync(path.join(skillsDir, keyword, 'SKILL.md'), 'utf-8');\n const modeIdx = content.indexOf('## MODE:');\n if (modeIdx === -1) {\n // A generated skill without the mode section must not leak the whole\n // SKILL.md into the system prompt: degrade to \"no injection\" instead.\n console.warn(\n `[maestria] prime-agent: mode skill \"${keyword}\" has no \"## MODE:\" heading; ` +\n `mode prompt injection disabled for this mode.`,\n );\n } else {\n const body = content.slice(modeIdx);\n prompt = `${MODE_MARKERS[keyword]}\\n\\n${body.replace(/\\s+$/u, '')}\\n`;\n }\n } catch (error) {\n console.warn(\n `[maestria] prime-agent: failed to load mode skill \"${keyword}\" from ${skillsDir}; ` +\n `mode prompt injection disabled for this mode.`,\n error,\n );\n }\n _promptCache[keyword] = prompt;\n return prompt;\n};\n\n// ---------------------------------------------------------------------------\n// before_agent_start mode prompt injection\n// ---------------------------------------------------------------------------\n\n/**\n * Create the `before_agent_start` handler that appends the active mode prompt\n * to the chained system prompt. Returns void when no mode is active (no\n * modification), so Prime's normal prompt assembly stands as-is.\n */\nexport const createModePromptHandler =\n (\n state: MaestriaModeState,\n skillsDir: string,\n ): ((\n event: BeforeAgentStartEvent,\n _ctx: ExtensionContext,\n ) => BeforeAgentStartEventResult | undefined) =>\n (event: BeforeAgentStartEvent): BeforeAgentStartEventResult | undefined => {\n if (!state.mode) {\n return undefined;\n }\n\n const modePrompt = getModePrompt(state.mode, skillsDir);\n if (!modePrompt) {\n return undefined;\n }\n\n return {\n systemPrompt: [\n event.systemPrompt,\n '',\n modePrompt,\n '',\n `The user has set workflow mode to \"${state.mode}\". Honor this mode throughout the session until it is changed or cleared.`,\n ].join('\\n'),\n };\n };\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport const MODE_CLEAR_COMMAND = 'mode-clear';\nexport const STATUS_COMMAND = 'maestria-status';\n\n/**\n * Install the mode slash commands (`/fein`, `/sonar`, `/blitz`, `/mode-clear`)\n * and the status/help command (`/maestria-status`). Mode selection is persisted\n * as a session custom entry; the prompt is injected on the next agent turn by\n * the `before_agent_start` handler.\n */\nexport const installCommands = (pi: ExtensionAPI, state: MaestriaModeState): void => {\n for (const keyword of MODE_KEYWORDS) {\n pi.registerCommand(keyword, {\n description: MODE_COMMAND_DESCRIPTIONS[keyword],\n handler: async (args: string, ctx: ExtensionCommandContext) => {\n state.mode = keyword;\n persistModeState(pi, state);\n // Forward a goal argument (e.g. `/fein implement the pipeline`) so the\n // injected mode prompt's \"if the user provided a goal, run it now\"\n // instruction has the goal to act on.\n if (args.trim()) {\n pi.sendUserMessage(args.trim(), { deliverAs: 'steer' });\n } else {\n ctx.ui.notify(`Mode set to ${keyword}. Describe what you'd like to work on.`);\n }\n await Promise.resolve();\n },\n });\n }\n\n pi.registerCommand(MODE_CLEAR_COMMAND, {\n description: 'Clear workflow mode and return to neutral routing',\n handler: async (_args: string, ctx: ExtensionCommandContext) => {\n state.mode = null;\n persistModeState(pi, state);\n ctx.ui.notify('Workflow mode cleared. Neutral routing is active.');\n await Promise.resolve();\n },\n });\n\n pi.registerCommand(STATUS_COMMAND, {\n description: 'Show the current maestria workflow mode and extension subset',\n handler: async (_args: string, ctx: ExtensionCommandContext) => {\n const mode = state.mode ?? 'none';\n const summary = [\n '# Maestria status (prime-agent)',\n '',\n `Workflow mode: ${mode}`,\n '',\n 'Commands: /fein, /sonar, /blitz, /mode-clear',\n '',\n 'This extension covers mode selection and mode prompt injection only.',\n 'Recursive-subagent (rlm) dispatch and JSON/RPC headless mode are NOT provided by this package.',\n ].join('\\n');\n ctx.ui.setEditorText(summary);\n await Promise.resolve();\n },\n });\n};\n","// packages/prime-agent/src/extension.ts\n// Prime Agent extension entry point (default-export factory).\n//\n// Compiled to `dist/extension.mjs` and declared in package.json under\n// `pi.extensions`; Prime loads it with its extension loader (pinned fork\n// 7787f07415d843b9a800f6a4720e0c739bd608e5, loader.ts: a jiti import of the\n// declared path calling the default export with the live ExtensionAPI).\n//\n// Verified subset (public Prime/Pi extension API only, see src/pi-api.ts):\n// - slash commands /fein /sonar /blitz /mode-clear and /maestria-status\n// - before_agent_start mode prompt injection (systemPrompt chaining)\n// - session-scoped mode state via custom session entries, restored on\n// session_start (reload/resume/fork) and session_tree (branch navigation)\n//\n// NOT provided (explicitly deferred, documented in README/INSTALL/ADR-CORE-014):\n// native recursive-subagent (`rlm`) dispatch - the pinned fork exposes no\n// public JS extension bridge for it (it is an IPython-side tool) - and\n// JSON/RPC headless mode integration. No tool interception is installed and no\n// sandbox/enforcement claim is made. This extension writes no files (no\n// `~/.pi`, no `.prime/agent` writes): state rides on host session entries.\n\nimport path from 'node:path';\n\nimport { createModePromptHandler, installCommands } from './modes.js';\nimport type {\n ExtensionAPI,\n ExtensionContext,\n SessionStartEvent,\n SessionTreeEvent,\n} from './pi-api.js';\nimport { createInitialState, restoreModeState } from './state.js';\n\n/**\n * Resolve the package's generated `skills/` directory. When running from the\n * built `dist/extension.mjs`, this is `<packageRoot>/skills`; when running from\n * source (tests), it is the same package-relative location.\n */\nconst resolveSkillsDir = (): string => {\n const moduleDir = import.meta.dirname;\n return path.resolve(moduleDir, '../skills');\n};\n\nconst extension = (pi: ExtensionAPI): void => {\n const state = createInitialState();\n const skillsDir = resolveSkillsDir();\n\n // Mode commands + status command (session-scoped state, persisted via\n // pi.appendEntry custom entries).\n installCommands(pi, state);\n\n // Mode prompt injection on the next agent turn.\n pi.on('before_agent_start', createModePromptHandler(state, skillsDir));\n\n // Restore the active mode when a session starts, is reloaded, resumed, or\n // forked, and when navigating the session tree to a different branch.\n pi.on('session_start', (_event: SessionStartEvent, ctx: ExtensionContext) => {\n restoreModeState(state, ctx.sessionManager.getBranch());\n });\n\n pi.on('session_tree', (_event: SessionTreeEvent, ctx: ExtensionContext) => {\n restoreModeState(state, ctx.sessionManager.getBranch());\n });\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/prime-agent",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "private": false,
5
5
  "description": "Maestria methodology for Prime Agent - specialist roles, orchestrator, global rules, and workflow modes as Agent Skills, plus a small Prime/Pi extension for mode commands and mode prompt injection",
6
6
  "keywords": [
@@ -38,7 +38,7 @@
38
38
  "devDependencies": {
39
39
  "@types/node": "^26.2.0",
40
40
  "typescript": "^7.0.2",
41
- "vite-plus": "0.2.9",
41
+ "vite-plus": "0.3.0",
42
42
  "vitest": "4.1.11"
43
43
  },
44
44
  "engines": {
@@ -1,5 +1,4 @@
1
1
  ---
2
- name: adventurer
3
2
  description: |-
4
3
  Codebase reconnaissance skill. Maps unknown territory -
5
4
  traces call chains, maps module relationships, generates structured recon
@@ -7,6 +6,7 @@ description: |-
7
6
  only, never implementation or design.
8
7
  Use for: understanding unfamiliar code, tracing dependencies, gathering context
9
8
  before implementation, investigating module structures.
9
+ name: adventurer
10
10
  ---
11
11
 
12
12
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -1,10 +1,10 @@
1
1
  ---
2
- name: architect
3
2
  description: |-
4
3
  Architecture decisions using decision matrices and ADRs.
5
4
  Evaluates options with weighted criteria, clarifies business context first.
6
5
  Use for: technology choices, implementation approaches, trade-off analysis,
7
6
  threat modeling, or ADR decisions.
7
+ name: architect
8
8
  ---
9
9
 
10
10
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: blitz
3
2
  description: Fast implementation mode - skip optional ceremony for familiar, low-risk work; never waive safety or required review. Load when the user invokes blitz or asks for a fast route.
3
+ name: blitz
4
4
  ---
5
5
 
6
6
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -1,9 +1,9 @@
1
1
  ---
2
- name: builder
3
2
  description: |-
4
3
  Focused implementation skill for atomic tasks. Executes
5
4
  one verifiable unit of work with minimal context and a clean diff.
6
5
  Use for: targeted fixes, feature implementation, refactors, adding tests.
6
+ name: builder
7
7
  ---
8
8
 
9
9
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -34,6 +34,18 @@ If the task is not atomic - if it spans multiple unrelated concerns - document t
34
34
 
35
35
  Start with the smallest change that satisfies acceptance. Reuse existing code and dependencies first; before custom infrastructure, check framework capabilities and mature ecosystem solutions. Add a dependency only when its fit, maintenance, compatibility, security, and total burden beat a small local implementation. Add layers only when the product requires them.
36
36
 
37
+ At trust boundaries, validate and normalize inputs once into a stable internal shape; client or convenience checks never replace authoritative security enforcement. Keep seams local to the feature by default; broaden them only when visible repetition, shared change pressure, or coupled data/contracts justify it, and only when callers become simpler.
38
+
39
+ When changing a shared interface, trace every caller and supported usage mode; preserve or deliberately migrate them, then verify through the highest practical consumer. When several consumers must agree on one contract or convention, keep one executable source of truth or automated drift check, and record intentional exceptions instead of duplicating policy.
40
+
41
+ Keep mechanical chores separate from behavior changes, and prefer many small reviewable increments over one large change.
42
+
43
+ Prefer deny by default, keep secrets in the trusted runtime, and fail closed on missing or invalid configuration.
44
+
45
+ When superseding code, mark the old path as do-not-extend, keep it until migration completes, then remove it in an isolated change.
46
+
47
+ Never hand-edit generated outputs; change the single source and regenerate.
48
+
37
49
  ## Skills
38
50
 
39
51
  Load on trigger: `agent-browser` (UI verification), `tdd` (explicit TDD requests), `pnpm` (package/lockfile changes), `mcp-builder` (MCP servers), `webapp-testing` (browser-level testing), `frontend-design` (UI build tasks), `commit-work` (staging and commit messages). Skip skill loads for mechanical one-line fixes.
@@ -1,9 +1,9 @@
1
1
  ---
2
- name: diagnose
3
2
  description: |-
4
3
  Systematic 6-step regression tracing: from error message
5
4
  to root cause to prevention.
6
5
  Use for: cryptic errors, regressions, production bugs, unclear root causes.
6
+ name: diagnose
7
7
  ---
8
8
 
9
9
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -66,9 +66,9 @@ Fix the root cause with minimal changes:
66
66
 
67
67
  Prevent similar bugs:
68
68
 
69
- - Add/update regression tests
69
+ - Consider regression tests where a durable contract or plausible recurrence justifies them (per Global Rules testing judgment)
70
70
  - Consider linting rules to catch the pattern
71
- - Document the lesson in a knowledge artifact for future reference
71
+ - **!!! Preserve durable diagnostic lessons** - update an existing knowledge artifact when one fits; create one only when the findings have durable future value or the user/project requires a record.
72
72
 
73
73
  ## Step 6: Verify Fix
74
74
 
@@ -81,7 +81,6 @@ Confirm it works:
81
81
 
82
82
  ## Rules
83
83
 
84
- - **!!! Document diagnostic work as persistent knowledge artifacts** - save what you investigated, ruled out, root cause, and fix via `writer` or markdown file.
85
84
  - **!!! Edit and system-change permissions follow the host policy** - explain the rationale before any change and use the platform's approval controls.
86
85
  - **!!! Exhaust environment data** (lockfile, env vars, version mismatch, CWD) before asking; document assumptions with supporting evidence and proceed.
87
86
  - **Parallelization:** different bugs in parallel; same bug = consolidate.
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: fein
3
2
  description: Full pipeline mode - reconnaissance or design, implementation, and independent review. Load when the user invokes fein or asks for the complete maestria pipeline.
3
+ name: fein
4
4
  ---
5
5
 
6
6
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -1,5 +1,4 @@
1
1
  ---
2
- name: global-rules
3
2
  description: |-
4
3
  Universal agent rules contract: universal floors,
5
4
  orchestration, delegation, context management, handoff, blind review, bounded
@@ -7,6 +6,7 @@ description: |-
7
6
  safety.
8
7
  Load once per session and apply to routing, delegation, review, and commit
9
8
  decisions.
9
+ name: global-rules
10
10
  ---
11
11
 
12
12
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -24,6 +24,7 @@ Cross-platform behavior contract for outcomes, evidence, safety, delegation, rev
24
24
  - **!!! 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.
25
25
  - **!!! 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.
26
26
  - **!!! 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.
27
+ - **!!! 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).
27
28
  - **!!! 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.
28
29
  - **!!! 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.
29
30
 
@@ -55,7 +56,7 @@ Default to one independent review and, only when blockers exist, one repair/re-r
55
56
 
56
57
  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.
57
58
 
58
- 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.
59
+ 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.
59
60
 
60
61
  ## Canonical Source Invariant
61
62
 
@@ -1,9 +1,9 @@
1
1
  ---
2
- name: handoff
3
2
  description: |-
4
3
  The handoff contract for inter-specialist delegation.
5
4
  Load when receiving a task from another specialist, or when handing off work to
6
5
  the next stage in the pipeline.
6
+ name: handoff
7
7
  ---
8
8
 
9
9
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -1,10 +1,10 @@
1
1
  ---
2
- name: iteration-limits
3
2
  description: |-
4
3
  The iteration-limit pattern with verifiable termination
5
4
  and escalation format.
6
5
  Load when defining termination conditions for a loop, or when a loop is at risk
7
6
  of running too long.
7
+ name: iteration-limits
8
8
  ---
9
9
 
10
10
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -1,5 +1,4 @@
1
1
  ---
2
- name: orchestrator
3
2
  description: |-
4
3
  Maestria methodology dispatcher for Prime Agent. Routes
5
4
  work (direct/focused/full), selects and loads the specialist skills
@@ -8,6 +7,7 @@ description: |-
8
7
  (fein/sonar/blitz).
9
8
  Use for multi-step or multi-file work, planning, review, debugging,
10
9
  architecture decisions, or documentation.
10
+ name: orchestrator
11
11
  ---
12
12
 
13
13
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -79,7 +79,7 @@ Modes are case-insensitive and per-turn.
79
79
 
80
80
  For implementation work, own the delivery path: inspect -> plan -> implement -> validate -> one independent review -> repair material blockers only when required -> targeted validation of repaired scope -> final verification -> commit -> push -> PR.
81
81
 
82
- **Routine delivery is autonomous.** When repository, branch, remote, ownership, and host capabilities support PR delivery, do not ask whether to create or use a feature branch, commit, push, or create a PR; complete 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.
82
+ **Routine delivery is autonomous.** When repository, branch, remote, ownership, and host capabilities support PR delivery, do not ask whether to create or use a feature branch, commit, push, or create a PR; complete 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.
83
83
 
84
84
  The parent session owns continuation until the selected implementation outcome reaches its terminal artifact. Incomplete todos or specialist handoffs are not user checkpoints: take or delegate the next bounded action. 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.
85
85
 
@@ -1,11 +1,11 @@
1
1
  ---
2
- name: planner
3
2
  description: |-
4
3
  Create detailed implementation plans with phased
5
4
  dependencies, timelines, verifiable success criteria, and rollback points.
6
5
  Breaks complex features into verifiable milestones.
7
6
  Use for: complex features requiring multi-phase execution, when the plan needs
8
7
  review before building.
8
+ name: planner
9
9
  ---
10
10
 
11
11
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -27,6 +27,8 @@ You create implementation plans.
27
27
  4. **Verification** - Criteria to confirm phase completion
28
28
  5. **Rollback Points** - Safe stopping points between phases
29
29
 
30
+ Deliver each increment as a runnable slice including its wiring, not as a single layer.
31
+
30
32
  ## Rules
31
33
 
32
34
  Planning briefs state the outcome, phases, dependencies, acceptance evidence, assumptions, rollback points, and next step.
@@ -36,7 +38,9 @@ Planning briefs state the outcome, phases, dependencies, acceptance evidence, as
36
38
  - **!!! Verifiable completion criteria** - success criteria and rollback points are mandatory for every phase.
37
39
  - **!!! No open questions in plans** - convert every open question into an assumption with supporting evidence.
38
40
 
39
- **Guard rails:** follow existing conventions; don't change architecture unasked, don't add dependencies without approval, don't refactor while adding features, don't skip verification.
41
+ **Guard rails:** follow existing conventions; don't change architecture unasked; don't add dependencies without approval; don't bundle unrelated cleanup. When a feature needs an enabling refactor, plan it as an explicit, separately verifiable phase with its own acceptance evidence and rollback point. Don't skip verification.
42
+
43
+ For migrations spanning many call sites or modules, name the current and target states, prove the target on a representative slice, and migrate in separately verifiable batches. Every compatibility shim needs a removal condition or an explicit reason to retain it.
40
44
 
41
45
  ## Handoff
42
46
 
@@ -1,11 +1,11 @@
1
1
  ---
2
- name: reviewer
3
2
  description: |-
4
3
  Code review with quality gates. Reviews for correctness,
5
4
  edge cases, security, performance, maintainability, and adherence to
6
5
  conventions; provides specific, actionable feedback and preserves blind review.
7
6
  Use for: post-implementation review, pre-commit review, architecture document
8
7
  review.
8
+ name: reviewer
9
9
  ---
10
10
 
11
11
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -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,6 +1,6 @@
1
1
  ---
2
- name: sonar
3
2
  description: Research-only mode - read-only specialist work, then STOP before implementation. Load when the user invokes sonar or asks for research-only work.
3
+ name: sonar
4
4
  ---
5
5
 
6
6
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -1,10 +1,10 @@
1
1
  ---
2
- name: writer
3
2
  description: |-
4
3
  Documentation writing following structured patterns.
5
4
  Creates clear, comprehensive docs for code, APIs, and systems.
6
5
  Use for: README files, API docs, architecture docs, changelogs, decision
7
6
  records.
7
+ name: writer
8
8
  ---
9
9
 
10
10
  <!-- Auto-generated from @maestria/core. Do not edit directly.
@@ -30,6 +30,7 @@ You write documentation.
30
30
  - Follow the project's existing doc style
31
31
  - One concept per section
32
32
  - Document guard rails and constraints explicitly
33
+ - Verify factual claims against current code/config; for operator-critical instructions, link to the authoritative source and include a runnable check with the expected success or failure signal
33
34
  - Don't invent isolation, lifecycle, or enforcement guarantees the adapter does not provide.
34
35
 
35
36
  ## Format
@@ -63,8 +64,8 @@ You write documentation.
63
64
 
64
65
  ## Check
65
66
 
66
- - **Termination condition:** links checked, examples runnable, tone matches docs, proofread once.
67
- - **!!! Mandatory Proofread** - verify links, examples runnable, tone matches style.
67
+ - **Termination condition:** factual claims match current code/config; links work; examples and operator checks run with the expected signals; tone matches surrounding docs.
68
+ - **!!! Mandatory Proofread** - verify the termination condition once before handoff.
68
69
  - **!!! Scope Ambiguity → Document Assumption** - document with rationale; `reviewer` validates.
69
70
 
70
71
  - **Parallelization:** writer tasks on different docs can run in parallel. Same doc is single-writer.