@maestria/prime-agent 0.3.5 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/INSTALL.md CHANGED
@@ -29,7 +29,7 @@ prime-agent package install npm:@maestria/prime-agent
29
29
 
30
30
  Prime installs it via npm and records it in global settings (`~/.prime/agent/settings.json`). Add `--local` to use project settings (`.prime/agent/settings.json`), which Prime installs automatically at startup.
31
31
 
32
- The published package includes the compiled `dist/extension.mjs` and its sourcemap. Prime discovers both resources through the package's `pi.skills` and `pi.extensions` entries.
32
+ The published package includes the compiled `dist/extension.mjs`. Prime discovers the extension and skills through the package's `pi.extensions` and `pi.skills` entries.
33
33
 
34
34
  #### Installing from source
35
35
 
@@ -1,4 +1,3 @@
1
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
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
- //# sourceMappingURL=extension.mjs.map
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maestria/prime-agent",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
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": [
@@ -36,9 +36,9 @@
36
36
  "provenance": true
37
37
  },
38
38
  "devDependencies": {
39
- "@types/node": "^26.2.0",
39
+ "@types/node": "^26.5.1",
40
40
  "typescript": "^7.0.2",
41
- "vite-plus": "0.3.0",
41
+ "vite-plus": "0.3.1",
42
42
  "vitest": "4.1.11"
43
43
  },
44
44
  "engines": {
@@ -1 +0,0 @@
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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"}