@agimon-ai/po-skill 0.0.0-alpha.27 → 0.0.0-alpha.29
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 +20 -19
- package/dist/extensions/mcp.cjs.map +1 -1
- package/dist/extensions/mcp.mjs.map +1 -1
- package/dist/extensions/server.cjs +55 -48
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.d.mts.map +1 -1
- package/dist/extensions/server.mjs +55 -48
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/skills-Ccvg1Hcz.mjs.map +1 -1
- package/dist/skills-ComtgYb1.cjs.map +1 -1
- package/package.json +8 -8
- package/src/prompts/po-author-skill/SKILL.md +3 -3
- package/src/prompts/po-use-skill/SKILL.md +2 -0
package/README.md
CHANGED
|
@@ -2,25 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
The session skill catalog for [Po](https://www.npmjs.com/package/@agimon-ai/po).
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
5
|
+
Po discovers plugin skills first, shared `.claude/skills` second, and repository `.po/skills`
|
|
6
|
+
third. The server awaits discovery and registers each loaded skill once, merging its admitting
|
|
7
|
+
domains into an any-of gate. Repository skills are unconditional. Commands and MCP discovery
|
|
8
|
+
follow the same domain gates; MCP eligibility is tracked separately.
|
|
9
|
+
|
|
10
|
+
Pi globally deduplicates skill names. A plugin skill can therefore shadow a same-name shared
|
|
11
|
+
fallback even when its domain is off. Selection-aware fallback is not currently supported.
|
|
12
|
+
|
|
13
|
+
The session agent uses `load_skill` to read instructions by exact name. With at most 20 active
|
|
14
|
+
skills, its tool description lists their names and descriptions and `search_skills` is hidden.
|
|
15
|
+
Above 20, `search_skills` is available with all active names in its description, and returns
|
|
16
|
+
matching names and descriptions without bodies or paths. The host updates this surface as
|
|
17
|
+
skill activation changes.
|
|
18
|
+
|
|
19
|
+
## Contributing skills
|
|
20
|
+
|
|
21
|
+
Configure plugin skill directories and subsets in `domains.yaml`, use shared `.claude/skills`,
|
|
22
|
+
or add repository skills under `.po/skills`. Discovered skills are registered by po-skill, not
|
|
23
|
+
po-domain. Package-owned resources use named `resource/*.server.ts` routes with host conditions
|
|
24
|
+
for major modes, minor modes, or domains. The host owns activation and attribution.
|
|
24
25
|
|
|
25
26
|
## Help guidance
|
|
26
27
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.cjs","names":["defineRoutedContribution","defineMcpSkill","readPackageResource","defineMcpSkill","readPackageResource","Type","defineMcpTool","defineMcpTool","defineMcpPlugin","skillDiscovered","skillPoAuthorSkill","skillPoUseSkill","toolLoadSkill","toolSearchSkills"],"sources":["../../src/services/mcpSkills/index.ts","../../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/po-author-skill.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/po-use-skill.mcp.ts","../../src/schemas/mcpSkillTools.ts","../../src/services/mcpSkillTools/index.ts","../../src/extensions/workspaces/sessions/(backend)/tool/load_skill.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/tool/search_skills.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import type { PoMcpPluginContext, PoMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { BACKGROUND_CONTEXT, getOrThrow, withAbortSignal, type FileSystem } from '@agimon-ai/po-core/platform';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'po/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: PoMcpPluginContext): readonly PoMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], fs: FileSystem, signal: AbortSignal) {\n return (context: Context): void => {\n context.provide(MCP_SKILLS_SERVICE, {\n list(remote) {\n signal.throwIfAborted();\n remote.signal.throwIfAborted();\n const domains = remote.selection.read().domains;\n const skills = new Map<string, PoMcpSkill>();\n for (const group of groups) {\n if (group.domain !== undefined && !domains.includes(group.domain)) continue;\n for (const skill of group.skills) {\n // Match command precedence, including shared copies across domains.\n if (skills.has(skill.name)) continue;\n skills.set(skill.name, {\n name: skill.name,\n description: skill.description,\n async read() {\n const readSignal = AbortSignal.any([signal, remote.signal]);\n readSignal.throwIfAborted();\n const text = getOrThrow(\n await fs.readTextFile(skill.filePath, withAbortSignal(readSignal, BACKGROUND_CONTEXT)),\n );\n readSignal.throwIfAborted();\n return text;\n },\n });\n }\n }\n return [...skills.values()];\n },\n } satisfies McpSkillProvider);\n };\n}\n\nexport function bindMcpSkills(context: PoMcpPluginContext): readonly PoMcpSkill[] {\n const provider = context.services.get<McpSkillProvider>(MCP_SKILLS_SERVICE);\n if (!provider) throw new Error('Discovered MCP skills are unavailable.');\n return provider.list(context);\n}\n","import { defineRoutedContribution } from '@agimon-ai/po-core/extensionFile';\n\nimport { bindMcpSkills } from '../../../../../services/mcpSkills';\n\nexport default defineRoutedContribution(bindMcpSkills, { cardinality: 'many' });\n","import { defineMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineMcpSkill({\n name: 'po-author-skill',\n description:\n 'Author and distribute Po agent skills. Use when creating a SKILL.md, adding supporting references or scripts, contributing a runtime skill directory through Cordis, or publishing activation-gated Help prompts from a Po package.',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-author-skill/SKILL.md'),\n});\n","import { defineMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineMcpSkill({\n name: 'po-use-skill',\n description:\n \"Use Po's skill catalog and deferred discovery. Use when browsing available skills, invoking /skill:name, understanding Help and extension-owned skill groups, or diagnosing why a skill is absent or shadowed.\",\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-skill/SKILL.md'),\n});\n","import { Type } from 'typebox';\n\nexport const searchSkillsParameters = Type.Object({ query: Type.Optional(Type.String()) });\nexport const loadSkillParameters = Type.Object({ name: Type.String() });\n","import type { PoHeadlessHostService, PoHeadlessTool } from '@agimon-ai/po-core/headless';\n\nimport { loadSkillParameters, searchSkillsParameters } from '../../schemas/mcpSkillTools';\n\nfunction text(text: string) {\n return { content: [{ type: 'text' as const, text }] };\n}\n\nfunction skills(context: Parameters<PoHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): PoHeadlessTool<typeof searchSkillsParameters> {\n return {\n // web-plugin-tool-renderers: ignore search_skills (remote MCP only)\n name: 'search_skills',\n label: 'Search skills',\n description: 'Discover repository and active-domain skills available to this remote connection.',\n parameters: searchSkillsParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { query }, _signal, _onUpdate, context) {\n const needle = query?.trim().toLocaleLowerCase();\n const available = await skills(context).list();\n const matches =\n needle === undefined || needle === ''\n ? available\n : available.filter(({ name, description }) => `${name}\\n${description}`.toLocaleLowerCase().includes(needle));\n return { ...text(JSON.stringify(matches)), structuredContent: { skills: matches } };\n },\n };\n}\n\n/**\n * The session agent's way into a skill. Its skills are listed in the prompt, which Po\n * controls, so it needs no search; a remote agent's prompt is not ours, hence the pair above.\n */\nexport function createSessionLoadSkillTool(\n host: Pick<PoHeadlessHostService, 'readSkill'>,\n): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (server session only, shown as a generic call)\n // PO_LOAD_SKILL_TOOL, spelled out so the renderer rule can read it.\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full instructions of a skill from the available skills list by exact name.',\n parameters: loadSkillParameters,\n executionMode: 'parallel',\n async execute(_toolCallId, { name }) {\n const body = host.readSkill(name);\n if (body === undefined) return { ...text(`No skill named ${name} is available in this session.`), isError: true };\n return text(body);\n },\n };\n}\n\nexport function createLoadSkillTool(): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (remote MCP only)\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full Markdown instructions for an available remote skill by exact name.',\n parameters: loadSkillParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { name }, _signal, _onUpdate, context) {\n return text(await skills(context).read(name));\n },\n };\n}\n","import { defineMcpTool } from '@agimon-ai/po-core/mcpFacet';\n\nimport { createLoadSkillTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createLoadSkillTool());\n","import { defineMcpTool } from '@agimon-ai/po-core/mcpFacet';\n\nimport { createSearchSkillsTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createSearchSkillsTool());\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineMcpPlugin, type PoMcpSessionPlugin } from '@agimon-ai/po-core/mcpFacet';\n\nimport skillDiscovered from '../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp';\nimport skillPoAuthorSkill from '../src/extensions/workspaces/sessions/(backend)/skill/po-author-skill.mcp';\nimport skillPoUseSkill from '../src/extensions/workspaces/sessions/(backend)/skill/po-use-skill.mcp';\nimport toolLoadSkill from '../src/extensions/workspaces/sessions/(backend)/tool/load_skill.mcp';\nimport toolSearchSkills from '../src/extensions/workspaces/sessions/(backend)/tool/search_skills.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\ntype ManyFactory<T, C> = (context: C) => T | readonly T[];\nconst many = <T, C>(value: T | readonly T[] | ManyFactory<T, C>, context: C): readonly T[] => {\n const resolved = typeof value === 'function' ? (value as ManyFactory<T, C>)(context) : value;\n return Array.isArray(resolved) ? resolved : [resolved as T];\n};\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nconst withWidget = <T>(key: string, value: T): T => value === undefined ? value : ({\n ...value, _meta: { ...(value as { _meta?: object })._meta, [\"po/widget\"]: key },\n});\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/po-skill',\n session: (context) => ({\n get skills(): PoMcpSessionPlugin['skills'] { return [...many(skillDiscovered, context), at(skillPoAuthorSkill, context), at(skillPoUseSkill, context)]; },\n get tools(): PoMcpSessionPlugin['tools'] { return [withWidget(\"@agimon-ai/po-skill/load_skill\", via({ name: 'load_skill' }, at(toolLoadSkill, context))), withWidget(\"@agimon-ai/po-skill/search_skills\", via({ name: 'search_skills' }, at(toolSearchSkills, context)))]; },\n }) satisfies PoMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mcp.cjs","names":["defineRoutedContribution","defineMcpSkill","readPackageResource","defineMcpSkill","readPackageResource","Type","defineMcpTool","defineMcpTool","defineMcpPlugin","skillDiscovered","skillPoAuthorSkill","skillPoUseSkill","toolLoadSkill","toolSearchSkills"],"sources":["../../src/services/mcpSkills/index.ts","../../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/po-author-skill.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/po-use-skill.mcp.ts","../../src/schemas/mcpSkillTools.ts","../../src/services/mcpSkillTools/index.ts","../../src/extensions/workspaces/sessions/(backend)/tool/load_skill.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/tool/search_skills.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import type { PoMcpPluginContext, PoMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { BACKGROUND_CONTEXT, getOrThrow, withAbortSignal, type FileSystem } from '@agimon-ai/po-core/platform';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'po/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: PoMcpPluginContext): readonly PoMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], fs: FileSystem, signal: AbortSignal) {\n return (context: Context): void => {\n context.provide(MCP_SKILLS_SERVICE, {\n list(remote) {\n signal.throwIfAborted();\n remote.signal.throwIfAborted();\n const domains = remote.selection.read().domains;\n const skills = new Map<string, PoMcpSkill>();\n for (const group of groups) {\n if (group.domains !== undefined && !group.domains.some((domain) => domains.includes(domain))) continue;\n for (const skill of group.skills) {\n // Retain name precedence defensively for remote providers.\n if (skills.has(skill.name)) continue;\n skills.set(skill.name, {\n name: skill.name,\n description: skill.description,\n async read() {\n const readSignal = AbortSignal.any([signal, remote.signal]);\n readSignal.throwIfAborted();\n const text = getOrThrow(\n await fs.readTextFile(skill.filePath, withAbortSignal(readSignal, BACKGROUND_CONTEXT)),\n );\n readSignal.throwIfAborted();\n return text;\n },\n });\n }\n }\n return [...skills.values()];\n },\n } satisfies McpSkillProvider);\n };\n}\n\nexport function bindMcpSkills(context: PoMcpPluginContext): readonly PoMcpSkill[] {\n const provider = context.services.get<McpSkillProvider>(MCP_SKILLS_SERVICE);\n if (!provider) throw new Error('Discovered MCP skills are unavailable.');\n return provider.list(context);\n}\n","import { defineRoutedContribution } from '@agimon-ai/po-core/extensionFile';\n\nimport { bindMcpSkills } from '../../../../../services/mcpSkills';\n\nexport default defineRoutedContribution(bindMcpSkills, { cardinality: 'many' });\n","import { defineMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineMcpSkill({\n name: 'po-author-skill',\n description:\n 'Author and distribute Po agent skills. Use when creating a SKILL.md, adding supporting references or scripts, contributing a runtime skill directory through Cordis, or publishing activation-gated Help prompts from a Po package.',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-author-skill/SKILL.md'),\n});\n","import { defineMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineMcpSkill({\n name: 'po-use-skill',\n description:\n \"Use Po's skill catalog and deferred discovery. Use when browsing available skills, invoking /skill:name, understanding Help and extension-owned skill groups, or diagnosing why a skill is absent or shadowed.\",\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-skill/SKILL.md'),\n});\n","import { Type } from 'typebox';\n\nexport const searchSkillsParameters = Type.Object({ query: Type.Optional(Type.String()) });\nexport const loadSkillParameters = Type.Object({ name: Type.String() });\n","import type { PoHeadlessHostService, PoHeadlessTool } from '@agimon-ai/po-core/headless';\n\nimport { loadSkillParameters, searchSkillsParameters } from '../../schemas/mcpSkillTools';\n\nfunction text(text: string) {\n return { content: [{ type: 'text' as const, text }] };\n}\n\nfunction skills(context: Parameters<PoHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): PoHeadlessTool<typeof searchSkillsParameters> {\n return {\n // web-plugin-tool-renderers: ignore search_skills (remote MCP only)\n name: 'search_skills',\n label: 'Search skills',\n description: 'Discover repository and active-domain skills available to this remote connection.',\n parameters: searchSkillsParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { query }, _signal, _onUpdate, context) {\n const needle = query?.trim().toLocaleLowerCase();\n const available = await skills(context).list();\n const matches =\n needle === undefined || needle === ''\n ? available\n : available.filter(({ name, description }) => `${name}\\n${description}`.toLocaleLowerCase().includes(needle));\n return { ...text(JSON.stringify(matches)), structuredContent: { skills: matches } };\n },\n };\n}\n\nexport function createSessionSearchSkillsTool(\n host: Pick<PoHeadlessHostService, 'listSkills'>,\n): PoHeadlessTool<typeof searchSkillsParameters> {\n return {\n // web-plugin-tool-renderers: ignore search_skills (server session only, shown as a generic call)\n name: 'search_skills',\n label: 'Search skills',\n description: 'Search available session skills by name or description.',\n parameters: searchSkillsParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n executionMode: 'parallel',\n async execute(_toolCallId, { query }) {\n const needle = query?.trim().toLocaleLowerCase();\n const available = host.listSkills().map(({ name, description }) => ({ name, description }));\n const matches =\n needle === undefined || needle === ''\n ? available\n : available.filter(({ name, description }) => `${name}\\n${description}`.toLocaleLowerCase().includes(needle));\n return { ...text(JSON.stringify(matches)), structuredContent: { skills: matches } };\n },\n };\n}\n\nexport function createSessionLoadSkillTool(\n host: Pick<PoHeadlessHostService, 'readSkill'>,\n): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (server session only, shown as a generic call)\n // PO_LOAD_SKILL_TOOL, spelled out so the renderer rule can read it.\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full instructions of an available session skill by exact name.',\n parameters: loadSkillParameters,\n executionMode: 'parallel',\n async execute(_toolCallId, { name }) {\n const body = host.readSkill(name);\n if (body === undefined) return { ...text(`No skill named ${name} is available in this session.`), isError: true };\n return text(body);\n },\n };\n}\n\nexport function createLoadSkillTool(): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (remote MCP only)\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full Markdown instructions for an available remote skill by exact name.',\n parameters: loadSkillParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { name }, _signal, _onUpdate, context) {\n return text(await skills(context).read(name));\n },\n };\n}\n","import { defineMcpTool } from '@agimon-ai/po-core/mcpFacet';\n\nimport { createLoadSkillTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createLoadSkillTool());\n","import { defineMcpTool } from '@agimon-ai/po-core/mcpFacet';\n\nimport { createSearchSkillsTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createSearchSkillsTool());\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineMcpPlugin, type PoMcpSessionPlugin } from '@agimon-ai/po-core/mcpFacet';\n\nimport skillDiscovered from '../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp';\nimport skillPoAuthorSkill from '../src/extensions/workspaces/sessions/(backend)/skill/po-author-skill.mcp';\nimport skillPoUseSkill from '../src/extensions/workspaces/sessions/(backend)/skill/po-use-skill.mcp';\nimport toolLoadSkill from '../src/extensions/workspaces/sessions/(backend)/tool/load_skill.mcp';\nimport toolSearchSkills from '../src/extensions/workspaces/sessions/(backend)/tool/search_skills.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\ntype ManyFactory<T, C> = (context: C) => T | readonly T[];\nconst many = <T, C>(value: T | readonly T[] | ManyFactory<T, C>, context: C): readonly T[] => {\n const resolved = typeof value === 'function' ? (value as ManyFactory<T, C>)(context) : value;\n return Array.isArray(resolved) ? resolved : [resolved as T];\n};\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nconst withWidget = <T>(key: string, value: T): T => value === undefined ? value : ({\n ...value, _meta: { ...(value as { _meta?: object })._meta, [\"po/widget\"]: key },\n});\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/po-skill',\n session: (context) => ({\n get skills(): PoMcpSessionPlugin['skills'] { return [...many(skillDiscovered, context), at(skillPoAuthorSkill, context), at(skillPoUseSkill, context)]; },\n get tools(): PoMcpSessionPlugin['tools'] { return [withWidget(\"@agimon-ai/po-skill/load_skill\", via({ name: 'load_skill' }, at(toolLoadSkill, context))), withWidget(\"@agimon-ai/po-skill/search_skills\", via({ name: 'search_skills' }, at(toolSearchSkills, context)))]; },\n }) satisfies PoMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.mjs","names":["skillDiscovered","skillPoAuthorSkill","skillPoUseSkill","toolLoadSkill","toolSearchSkills"],"sources":["../../src/services/mcpSkills/index.ts","../../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/po-author-skill.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/po-use-skill.mcp.ts","../../src/schemas/mcpSkillTools.ts","../../src/services/mcpSkillTools/index.ts","../../src/extensions/workspaces/sessions/(backend)/tool/load_skill.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/tool/search_skills.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import type { PoMcpPluginContext, PoMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { BACKGROUND_CONTEXT, getOrThrow, withAbortSignal, type FileSystem } from '@agimon-ai/po-core/platform';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'po/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: PoMcpPluginContext): readonly PoMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], fs: FileSystem, signal: AbortSignal) {\n return (context: Context): void => {\n context.provide(MCP_SKILLS_SERVICE, {\n list(remote) {\n signal.throwIfAborted();\n remote.signal.throwIfAborted();\n const domains = remote.selection.read().domains;\n const skills = new Map<string, PoMcpSkill>();\n for (const group of groups) {\n if (group.domain !== undefined && !domains.includes(group.domain)) continue;\n for (const skill of group.skills) {\n // Match command precedence, including shared copies across domains.\n if (skills.has(skill.name)) continue;\n skills.set(skill.name, {\n name: skill.name,\n description: skill.description,\n async read() {\n const readSignal = AbortSignal.any([signal, remote.signal]);\n readSignal.throwIfAborted();\n const text = getOrThrow(\n await fs.readTextFile(skill.filePath, withAbortSignal(readSignal, BACKGROUND_CONTEXT)),\n );\n readSignal.throwIfAborted();\n return text;\n },\n });\n }\n }\n return [...skills.values()];\n },\n } satisfies McpSkillProvider);\n };\n}\n\nexport function bindMcpSkills(context: PoMcpPluginContext): readonly PoMcpSkill[] {\n const provider = context.services.get<McpSkillProvider>(MCP_SKILLS_SERVICE);\n if (!provider) throw new Error('Discovered MCP skills are unavailable.');\n return provider.list(context);\n}\n","import { defineRoutedContribution } from '@agimon-ai/po-core/extensionFile';\n\nimport { bindMcpSkills } from '../../../../../services/mcpSkills';\n\nexport default defineRoutedContribution(bindMcpSkills, { cardinality: 'many' });\n","import { defineMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineMcpSkill({\n name: 'po-author-skill',\n description:\n 'Author and distribute Po agent skills. Use when creating a SKILL.md, adding supporting references or scripts, contributing a runtime skill directory through Cordis, or publishing activation-gated Help prompts from a Po package.',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-author-skill/SKILL.md'),\n});\n","import { defineMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineMcpSkill({\n name: 'po-use-skill',\n description:\n \"Use Po's skill catalog and deferred discovery. Use when browsing available skills, invoking /skill:name, understanding Help and extension-owned skill groups, or diagnosing why a skill is absent or shadowed.\",\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-skill/SKILL.md'),\n});\n","import { Type } from 'typebox';\n\nexport const searchSkillsParameters = Type.Object({ query: Type.Optional(Type.String()) });\nexport const loadSkillParameters = Type.Object({ name: Type.String() });\n","import type { PoHeadlessHostService, PoHeadlessTool } from '@agimon-ai/po-core/headless';\n\nimport { loadSkillParameters, searchSkillsParameters } from '../../schemas/mcpSkillTools';\n\nfunction text(text: string) {\n return { content: [{ type: 'text' as const, text }] };\n}\n\nfunction skills(context: Parameters<PoHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): PoHeadlessTool<typeof searchSkillsParameters> {\n return {\n // web-plugin-tool-renderers: ignore search_skills (remote MCP only)\n name: 'search_skills',\n label: 'Search skills',\n description: 'Discover repository and active-domain skills available to this remote connection.',\n parameters: searchSkillsParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { query }, _signal, _onUpdate, context) {\n const needle = query?.trim().toLocaleLowerCase();\n const available = await skills(context).list();\n const matches =\n needle === undefined || needle === ''\n ? available\n : available.filter(({ name, description }) => `${name}\\n${description}`.toLocaleLowerCase().includes(needle));\n return { ...text(JSON.stringify(matches)), structuredContent: { skills: matches } };\n },\n };\n}\n\n/**\n * The session agent's way into a skill. Its skills are listed in the prompt, which Po\n * controls, so it needs no search; a remote agent's prompt is not ours, hence the pair above.\n */\nexport function createSessionLoadSkillTool(\n host: Pick<PoHeadlessHostService, 'readSkill'>,\n): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (server session only, shown as a generic call)\n // PO_LOAD_SKILL_TOOL, spelled out so the renderer rule can read it.\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full instructions of a skill from the available skills list by exact name.',\n parameters: loadSkillParameters,\n executionMode: 'parallel',\n async execute(_toolCallId, { name }) {\n const body = host.readSkill(name);\n if (body === undefined) return { ...text(`No skill named ${name} is available in this session.`), isError: true };\n return text(body);\n },\n };\n}\n\nexport function createLoadSkillTool(): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (remote MCP only)\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full Markdown instructions for an available remote skill by exact name.',\n parameters: loadSkillParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { name }, _signal, _onUpdate, context) {\n return text(await skills(context).read(name));\n },\n };\n}\n","import { defineMcpTool } from '@agimon-ai/po-core/mcpFacet';\n\nimport { createLoadSkillTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createLoadSkillTool());\n","import { defineMcpTool } from '@agimon-ai/po-core/mcpFacet';\n\nimport { createSearchSkillsTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createSearchSkillsTool());\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineMcpPlugin, type PoMcpSessionPlugin } from '@agimon-ai/po-core/mcpFacet';\n\nimport skillDiscovered from '../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp';\nimport skillPoAuthorSkill from '../src/extensions/workspaces/sessions/(backend)/skill/po-author-skill.mcp';\nimport skillPoUseSkill from '../src/extensions/workspaces/sessions/(backend)/skill/po-use-skill.mcp';\nimport toolLoadSkill from '../src/extensions/workspaces/sessions/(backend)/tool/load_skill.mcp';\nimport toolSearchSkills from '../src/extensions/workspaces/sessions/(backend)/tool/search_skills.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\ntype ManyFactory<T, C> = (context: C) => T | readonly T[];\nconst many = <T, C>(value: T | readonly T[] | ManyFactory<T, C>, context: C): readonly T[] => {\n const resolved = typeof value === 'function' ? (value as ManyFactory<T, C>)(context) : value;\n return Array.isArray(resolved) ? resolved : [resolved as T];\n};\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nconst withWidget = <T>(key: string, value: T): T => value === undefined ? value : ({\n ...value, _meta: { ...(value as { _meta?: object })._meta, [\"po/widget\"]: key },\n});\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/po-skill',\n session: (context) => ({\n get skills(): PoMcpSessionPlugin['skills'] { return [...many(skillDiscovered, context), at(skillPoAuthorSkill, context), at(skillPoUseSkill, context)]; },\n get tools(): PoMcpSessionPlugin['tools'] { return [withWidget(\"@agimon-ai/po-skill/load_skill\", via({ name: 'load_skill' }, at(toolLoadSkill, context))), withWidget(\"@agimon-ai/po-skill/search_skills\", via({ name: 'search_skills' }, at(toolSearchSkills, context)))]; },\n }) satisfies PoMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mcp.mjs","names":["skillDiscovered","skillPoAuthorSkill","skillPoUseSkill","toolLoadSkill","toolSearchSkills"],"sources":["../../src/services/mcpSkills/index.ts","../../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/po-author-skill.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/po-use-skill.mcp.ts","../../src/schemas/mcpSkillTools.ts","../../src/services/mcpSkillTools/index.ts","../../src/extensions/workspaces/sessions/(backend)/tool/load_skill.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/tool/search_skills.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import type { PoMcpPluginContext, PoMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { BACKGROUND_CONTEXT, getOrThrow, withAbortSignal, type FileSystem } from '@agimon-ai/po-core/platform';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'po/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: PoMcpPluginContext): readonly PoMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], fs: FileSystem, signal: AbortSignal) {\n return (context: Context): void => {\n context.provide(MCP_SKILLS_SERVICE, {\n list(remote) {\n signal.throwIfAborted();\n remote.signal.throwIfAborted();\n const domains = remote.selection.read().domains;\n const skills = new Map<string, PoMcpSkill>();\n for (const group of groups) {\n if (group.domains !== undefined && !group.domains.some((domain) => domains.includes(domain))) continue;\n for (const skill of group.skills) {\n // Retain name precedence defensively for remote providers.\n if (skills.has(skill.name)) continue;\n skills.set(skill.name, {\n name: skill.name,\n description: skill.description,\n async read() {\n const readSignal = AbortSignal.any([signal, remote.signal]);\n readSignal.throwIfAborted();\n const text = getOrThrow(\n await fs.readTextFile(skill.filePath, withAbortSignal(readSignal, BACKGROUND_CONTEXT)),\n );\n readSignal.throwIfAborted();\n return text;\n },\n });\n }\n }\n return [...skills.values()];\n },\n } satisfies McpSkillProvider);\n };\n}\n\nexport function bindMcpSkills(context: PoMcpPluginContext): readonly PoMcpSkill[] {\n const provider = context.services.get<McpSkillProvider>(MCP_SKILLS_SERVICE);\n if (!provider) throw new Error('Discovered MCP skills are unavailable.');\n return provider.list(context);\n}\n","import { defineRoutedContribution } from '@agimon-ai/po-core/extensionFile';\n\nimport { bindMcpSkills } from '../../../../../services/mcpSkills';\n\nexport default defineRoutedContribution(bindMcpSkills, { cardinality: 'many' });\n","import { defineMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineMcpSkill({\n name: 'po-author-skill',\n description:\n 'Author and distribute Po agent skills. Use when creating a SKILL.md, adding supporting references or scripts, contributing a runtime skill directory through Cordis, or publishing activation-gated Help prompts from a Po package.',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-author-skill/SKILL.md'),\n});\n","import { defineMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineMcpSkill({\n name: 'po-use-skill',\n description:\n \"Use Po's skill catalog and deferred discovery. Use when browsing available skills, invoking /skill:name, understanding Help and extension-owned skill groups, or diagnosing why a skill is absent or shadowed.\",\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-skill/SKILL.md'),\n});\n","import { Type } from 'typebox';\n\nexport const searchSkillsParameters = Type.Object({ query: Type.Optional(Type.String()) });\nexport const loadSkillParameters = Type.Object({ name: Type.String() });\n","import type { PoHeadlessHostService, PoHeadlessTool } from '@agimon-ai/po-core/headless';\n\nimport { loadSkillParameters, searchSkillsParameters } from '../../schemas/mcpSkillTools';\n\nfunction text(text: string) {\n return { content: [{ type: 'text' as const, text }] };\n}\n\nfunction skills(context: Parameters<PoHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): PoHeadlessTool<typeof searchSkillsParameters> {\n return {\n // web-plugin-tool-renderers: ignore search_skills (remote MCP only)\n name: 'search_skills',\n label: 'Search skills',\n description: 'Discover repository and active-domain skills available to this remote connection.',\n parameters: searchSkillsParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { query }, _signal, _onUpdate, context) {\n const needle = query?.trim().toLocaleLowerCase();\n const available = await skills(context).list();\n const matches =\n needle === undefined || needle === ''\n ? available\n : available.filter(({ name, description }) => `${name}\\n${description}`.toLocaleLowerCase().includes(needle));\n return { ...text(JSON.stringify(matches)), structuredContent: { skills: matches } };\n },\n };\n}\n\nexport function createSessionSearchSkillsTool(\n host: Pick<PoHeadlessHostService, 'listSkills'>,\n): PoHeadlessTool<typeof searchSkillsParameters> {\n return {\n // web-plugin-tool-renderers: ignore search_skills (server session only, shown as a generic call)\n name: 'search_skills',\n label: 'Search skills',\n description: 'Search available session skills by name or description.',\n parameters: searchSkillsParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n executionMode: 'parallel',\n async execute(_toolCallId, { query }) {\n const needle = query?.trim().toLocaleLowerCase();\n const available = host.listSkills().map(({ name, description }) => ({ name, description }));\n const matches =\n needle === undefined || needle === ''\n ? available\n : available.filter(({ name, description }) => `${name}\\n${description}`.toLocaleLowerCase().includes(needle));\n return { ...text(JSON.stringify(matches)), structuredContent: { skills: matches } };\n },\n };\n}\n\nexport function createSessionLoadSkillTool(\n host: Pick<PoHeadlessHostService, 'readSkill'>,\n): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (server session only, shown as a generic call)\n // PO_LOAD_SKILL_TOOL, spelled out so the renderer rule can read it.\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full instructions of an available session skill by exact name.',\n parameters: loadSkillParameters,\n executionMode: 'parallel',\n async execute(_toolCallId, { name }) {\n const body = host.readSkill(name);\n if (body === undefined) return { ...text(`No skill named ${name} is available in this session.`), isError: true };\n return text(body);\n },\n };\n}\n\nexport function createLoadSkillTool(): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (remote MCP only)\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full Markdown instructions for an available remote skill by exact name.',\n parameters: loadSkillParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { name }, _signal, _onUpdate, context) {\n return text(await skills(context).read(name));\n },\n };\n}\n","import { defineMcpTool } from '@agimon-ai/po-core/mcpFacet';\n\nimport { createLoadSkillTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createLoadSkillTool());\n","import { defineMcpTool } from '@agimon-ai/po-core/mcpFacet';\n\nimport { createSearchSkillsTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createSearchSkillsTool());\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineMcpPlugin, type PoMcpSessionPlugin } from '@agimon-ai/po-core/mcpFacet';\n\nimport skillDiscovered from '../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp';\nimport skillPoAuthorSkill from '../src/extensions/workspaces/sessions/(backend)/skill/po-author-skill.mcp';\nimport skillPoUseSkill from '../src/extensions/workspaces/sessions/(backend)/skill/po-use-skill.mcp';\nimport toolLoadSkill from '../src/extensions/workspaces/sessions/(backend)/tool/load_skill.mcp';\nimport toolSearchSkills from '../src/extensions/workspaces/sessions/(backend)/tool/search_skills.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\ntype ManyFactory<T, C> = (context: C) => T | readonly T[];\nconst many = <T, C>(value: T | readonly T[] | ManyFactory<T, C>, context: C): readonly T[] => {\n const resolved = typeof value === 'function' ? (value as ManyFactory<T, C>)(context) : value;\n return Array.isArray(resolved) ? resolved : [resolved as T];\n};\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nconst withWidget = <T>(key: string, value: T): T => value === undefined ? value : ({\n ...value, _meta: { ...(value as { _meta?: object })._meta, [\"po/widget\"]: key },\n});\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/po-skill',\n session: (context) => ({\n get skills(): PoMcpSessionPlugin['skills'] { return [...many(skillDiscovered, context), at(skillPoAuthorSkill, context), at(skillPoUseSkill, context)]; },\n get tools(): PoMcpSessionPlugin['tools'] { return [withWidget(\"@agimon-ai/po-skill/load_skill\", via({ name: 'load_skill' }, at(toolLoadSkill, context))), withWidget(\"@agimon-ai/po-skill/search_skills\", via({ name: 'search_skills' }, at(toolSearchSkills, context)))]; },\n }) satisfies PoMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
|
|
@@ -38,7 +38,7 @@ function mountMcpSkills(groups, fs, signal) {
|
|
|
38
38
|
const domains = remote.selection.read().domains;
|
|
39
39
|
const skills = /* @__PURE__ */ new Map();
|
|
40
40
|
for (const group of groups) {
|
|
41
|
-
if (group.
|
|
41
|
+
if (group.domains !== void 0 && !group.domains.some((domain) => domains.includes(domain))) continue;
|
|
42
42
|
for (const skill of group.skills) {
|
|
43
43
|
if (skills.has(skill.name)) continue;
|
|
44
44
|
skills.set(skill.name, {
|
|
@@ -58,7 +58,9 @@ function mountMcpSkills(groups, fs, signal) {
|
|
|
58
58
|
} });
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/schemas/mcpSkillTools.ts
|
|
63
|
+
const searchSkillsParameters = typebox.Type.Object({ query: typebox.Type.Optional(typebox.Type.String()) });
|
|
62
64
|
const loadSkillParameters = typebox.Type.Object({ name: typebox.Type.String() });
|
|
63
65
|
//#endregion
|
|
64
66
|
//#region src/services/mcpSkillTools/index.ts
|
|
@@ -68,15 +70,37 @@ function text(text) {
|
|
|
68
70
|
text
|
|
69
71
|
}] };
|
|
70
72
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
function createSessionSearchSkillsTool(host) {
|
|
74
|
+
return {
|
|
75
|
+
name: "search_skills",
|
|
76
|
+
label: "Search skills",
|
|
77
|
+
description: "Search available session skills by name or description.",
|
|
78
|
+
parameters: searchSkillsParameters,
|
|
79
|
+
annotations: {
|
|
80
|
+
readOnlyHint: true,
|
|
81
|
+
destructiveHint: false,
|
|
82
|
+
openWorldHint: false
|
|
83
|
+
},
|
|
84
|
+
executionMode: "parallel",
|
|
85
|
+
async execute(_toolCallId, { query }) {
|
|
86
|
+
const needle = query?.trim().toLocaleLowerCase();
|
|
87
|
+
const available = host.listSkills().map(({ name, description }) => ({
|
|
88
|
+
name,
|
|
89
|
+
description
|
|
90
|
+
}));
|
|
91
|
+
const matches = needle === void 0 || needle === "" ? available : available.filter(({ name, description }) => `${name}\n${description}`.toLocaleLowerCase().includes(needle));
|
|
92
|
+
return {
|
|
93
|
+
...text(JSON.stringify(matches)),
|
|
94
|
+
structuredContent: { skills: matches }
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
75
99
|
function createSessionLoadSkillTool(host) {
|
|
76
100
|
return {
|
|
77
101
|
name: "load_skill",
|
|
78
102
|
label: "Load skill",
|
|
79
|
-
description: "Load the full instructions of
|
|
103
|
+
description: "Load the full instructions of an available session skill by exact name.",
|
|
80
104
|
parameters: loadSkillParameters,
|
|
81
105
|
executionMode: "parallel",
|
|
82
106
|
async execute(_toolCallId, { name }) {
|
|
@@ -296,7 +320,7 @@ function within(filePath, roots) {
|
|
|
296
320
|
* launch selection would freeze the command list at whatever was selected when
|
|
297
321
|
* the session host was built. Enumerating every domain up front and tagging
|
|
298
322
|
* each group with its `when` lets the existing kernel refresh do the work, the
|
|
299
|
-
* same way
|
|
323
|
+
* same way other host contributions follow selection changes.
|
|
300
324
|
*/
|
|
301
325
|
async function discoverServerSkills(execution, fs, signal) {
|
|
302
326
|
signal.throwIfAborted();
|
|
@@ -345,14 +369,12 @@ async function discoverServerSkills(execution, fs, signal) {
|
|
|
345
369
|
for (const domain of domains) if (await (0, _agimon_ai_po_config_domains.resolveSharedSkillsAsync)(fs, execution.repoRoot, [domain], context, home)) sources.push({
|
|
346
370
|
domain,
|
|
347
371
|
paths: sharedPaths,
|
|
348
|
-
mcpPaths: sharedPaths
|
|
349
|
-
listed: true
|
|
372
|
+
mcpPaths: sharedPaths
|
|
350
373
|
});
|
|
351
374
|
}
|
|
352
375
|
sources.push({
|
|
353
376
|
paths: [repoSkills],
|
|
354
|
-
mcpPaths: [repoSkills]
|
|
355
|
-
listed: true
|
|
377
|
+
mcpPaths: [repoSkills]
|
|
356
378
|
});
|
|
357
379
|
signal.throwIfAborted();
|
|
358
380
|
const unique = [.../* @__PURE__ */ new Set([...sources.flatMap((source) => source.paths), ...state.skillDirectories ?? []])];
|
|
@@ -361,18 +383,17 @@ async function discoverServerSkills(execution, fs, signal) {
|
|
|
361
383
|
skillPaths: unique
|
|
362
384
|
}).ready();
|
|
363
385
|
signal.throwIfAborted();
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
})).filter((group) => group.skills.length > 0);
|
|
386
|
+
const merge = (key) => inventory.skills.flatMap((skill) => {
|
|
387
|
+
const admitting = sources.filter((source) => within(skill.filePath, source[key]));
|
|
388
|
+
if (admitting.length === 0) return [];
|
|
389
|
+
const domains = [...new Set(admitting.flatMap((source) => source.domain === void 0 ? [] : [source.domain]))];
|
|
390
|
+
return [{
|
|
391
|
+
...admitting.some((source) => source.domain === void 0) ? {} : { domains },
|
|
392
|
+
skills: [skill]
|
|
393
|
+
}];
|
|
394
|
+
});
|
|
395
|
+
const groups = merge("paths");
|
|
396
|
+
const mcpGroups = merge("mcpPaths");
|
|
376
397
|
const activeSkills = inventory.skills.filter((skill) => within(skill.filePath, [...state.skillDirectories ?? [], repoSkills]));
|
|
377
398
|
return {
|
|
378
399
|
inventory: {
|
|
@@ -381,7 +402,6 @@ async function discoverServerSkills(execution, fs, signal) {
|
|
|
381
402
|
},
|
|
382
403
|
groups,
|
|
383
404
|
mcpGroups,
|
|
384
|
-
listed,
|
|
385
405
|
catalog: (0, _earendil_works_pi_coding_agent.formatSkillsForPrompt)(activeSkills) || "(no discovered skills)"
|
|
386
406
|
};
|
|
387
407
|
}
|
|
@@ -395,9 +415,9 @@ async function discoverServerSkills(execution, fs, signal) {
|
|
|
395
415
|
* active, and looking it up in the union keeps one lookup table instead of one
|
|
396
416
|
* per domain.
|
|
397
417
|
*/
|
|
398
|
-
function skillCommand(skill,
|
|
418
|
+
function skillCommand(skill, domains, inventory) {
|
|
399
419
|
return {
|
|
400
|
-
...
|
|
420
|
+
...domains === void 0 ? {} : { when: { domains } },
|
|
401
421
|
name: `${require_skills.SKILL_COMMAND_PREFIX}${skill.name}`,
|
|
402
422
|
description: skill.description,
|
|
403
423
|
execute: async (args, execution) => {
|
|
@@ -431,7 +451,7 @@ function createSkillCommands(inventory, catalog, groups) {
|
|
|
431
451
|
level: "info"
|
|
432
452
|
});
|
|
433
453
|
}
|
|
434
|
-
}, ...groups.flatMap((group) => group.skills.map((skill) => skillCommand(skill, group.
|
|
454
|
+
}, ...groups.flatMap((group) => group.skills.map((skill) => skillCommand(skill, group.domains, inventory)))];
|
|
435
455
|
}
|
|
436
456
|
//#endregion
|
|
437
457
|
//#region src/extensions/workspaces/sessions/(backend)/_lib/skillServer.ts
|
|
@@ -445,23 +465,17 @@ async function createSkillServer(agent, fs, signal, resources) {
|
|
|
445
465
|
...(0, _agimon_ai_po_config_harnessState.readHarnessState)(execution.environment).skillDirectories ?? [],
|
|
446
466
|
Math.floor(Date.now() / SKILL_INVENTORY_FRESH_MS)
|
|
447
467
|
];
|
|
448
|
-
const { inventory, catalog, groups, mcpGroups
|
|
468
|
+
const { inventory, catalog, groups, mcpGroups } = await resources.acquire(key, () => discoverServerSkills(execution, fs, new AbortController().signal));
|
|
449
469
|
return {
|
|
450
470
|
commands: createSkillCommands(inventory, catalog, groups),
|
|
451
|
-
tools: [createSessionLoadSkillTool(agent)],
|
|
471
|
+
tools: [createSessionLoadSkillTool(agent), createSessionSearchSkillsTool(agent)],
|
|
452
472
|
services: [mountMcpSkills(mcpGroups, fs, signal)],
|
|
453
|
-
resources: [...
|
|
473
|
+
resources: [...groups.flatMap((group) => group.skills.map((skill) => ({
|
|
454
474
|
name: skill.name,
|
|
455
475
|
description: skill.description,
|
|
456
476
|
path: skill.filePath,
|
|
457
477
|
kind: "skill",
|
|
458
|
-
...group.
|
|
459
|
-
domain: group.domain,
|
|
460
|
-
attribution: {
|
|
461
|
-
kind: "domain",
|
|
462
|
-
mode: group.domain
|
|
463
|
-
}
|
|
464
|
-
} },
|
|
478
|
+
...group.domains === void 0 ? {} : { when: { domains: group.domains } },
|
|
465
479
|
read: async () => (0, _agimon_ai_po_core_platform.getOrThrow)(await fs.readTextFile(skill.filePath, _agimon_ai_po_core_platform.BACKGROUND_CONTEXT))
|
|
466
480
|
}))), ...[{
|
|
467
481
|
name: "po-author-skill",
|
|
@@ -504,21 +518,14 @@ var skills_server_default$1 = (0, _agimon_ai_po_core_extensionFile.defineRoutedC
|
|
|
504
518
|
var skills_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.resources, { cardinality: "many" });
|
|
505
519
|
//#endregion
|
|
506
520
|
//#region src/extensions/workspaces/sessions/(backend)/tool/load_skill.server.ts
|
|
507
|
-
/** The session agent's skill
|
|
508
|
-
var load_skill_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.tools
|
|
521
|
+
/** The session agent's skill tools; the Pi CLI keeps Pi's own skill handling. */
|
|
522
|
+
var load_skill_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.tools ?? [], { cardinality: "many" });
|
|
509
523
|
//#endregion
|
|
510
524
|
//#region generated/server.ts
|
|
511
|
-
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
512
525
|
const many = (value, context) => {
|
|
513
526
|
const resolved = typeof value === "function" ? value(context) : value;
|
|
514
527
|
return Array.isArray(resolved) ? resolved : [resolved];
|
|
515
528
|
};
|
|
516
|
-
/* v8 ignore next -- generated normalization branch */
|
|
517
|
-
const defined = (values) => values.filter((value) => value !== void 0);
|
|
518
|
-
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
519
|
-
...identity,
|
|
520
|
-
...value
|
|
521
|
-
};
|
|
522
529
|
const serverPlugin = (0, _agimon_ai_po_core_serverPlugin.defineServerPlugin)({
|
|
523
530
|
name: "@agimon-ai/po-skill",
|
|
524
531
|
providerOverrides: (0, _agimon_ai_po_core_serverPlugin.composeServerProviderOverrides)(root_server_default),
|
|
@@ -538,7 +545,7 @@ const serverPlugin = (0, _agimon_ai_po_core_serverPlugin.defineServerPlugin)({
|
|
|
538
545
|
return [...many(skills_server_default, contextSession)];
|
|
539
546
|
},
|
|
540
547
|
get tools() {
|
|
541
|
-
return
|
|
548
|
+
return [...many(load_skill_server_default, contextSession)];
|
|
542
549
|
},
|
|
543
550
|
services: [...scopeSession.services ?? []],
|
|
544
551
|
get activities() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.cjs","names":["getOrThrow","withAbortSignal","BACKGROUND_CONTEXT","Type","path","fileURLToPath","fs","pathToFileURL","getAgentDir","loadSkills","Worker","stripFrontmatter","path","withAbortSignal","BACKGROUND_CONTEXT","readHarnessState","nodeHost","listDomainNamesAsync","materializePluginEntries","resolvePluginEntriesAsync","collectResources","discoverSkillsAsync","resolveSharedSkillsAsync","formatSkillsForPrompt","SKILL_COMMAND_PREFIX","SKILL_INVOCATION_PREFIX","SKILLS_COMMAND","nodeHost","readHarnessState","getOrThrow","BACKGROUND_CONTEXT","packageResourcePath","readPackageResource","defineRoot","requirePoFs","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineServerPlugin","composeServerProviderOverrides","rootSession","composeRootInjects","composeRootHooks","commandSkills","resourceSkills","toolLoadSkill"],"sources":["../../src/constants/skillInventory.ts","../../src/services/mcpSkills/index.ts","../../src/schemas/mcpSkillTools.ts","../../src/services/mcpSkillTools/index.ts","../../src/services/deferredSkills/index.ts","../../src/services/serverInventory/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillCommands.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillServer.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/skills.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/skills.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/load_skill.server.ts","../../generated/server.ts"],"sourcesContent":["/**\n * How long sessions with the same inputs share one skill inventory instead of each reading the disk.\n * ponytail: a skill added on disk reaches new sessions within this window, not at once; watch the\n * skill roots if that lag matters.\n */\nexport const SKILL_INVENTORY_FRESH_MS = 60_000;\n","import type { PoMcpPluginContext, PoMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { BACKGROUND_CONTEXT, getOrThrow, withAbortSignal, type FileSystem } from '@agimon-ai/po-core/platform';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'po/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: PoMcpPluginContext): readonly PoMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], fs: FileSystem, signal: AbortSignal) {\n return (context: Context): void => {\n context.provide(MCP_SKILLS_SERVICE, {\n list(remote) {\n signal.throwIfAborted();\n remote.signal.throwIfAborted();\n const domains = remote.selection.read().domains;\n const skills = new Map<string, PoMcpSkill>();\n for (const group of groups) {\n if (group.domain !== undefined && !domains.includes(group.domain)) continue;\n for (const skill of group.skills) {\n // Match command precedence, including shared copies across domains.\n if (skills.has(skill.name)) continue;\n skills.set(skill.name, {\n name: skill.name,\n description: skill.description,\n async read() {\n const readSignal = AbortSignal.any([signal, remote.signal]);\n readSignal.throwIfAborted();\n const text = getOrThrow(\n await fs.readTextFile(skill.filePath, withAbortSignal(readSignal, BACKGROUND_CONTEXT)),\n );\n readSignal.throwIfAborted();\n return text;\n },\n });\n }\n }\n return [...skills.values()];\n },\n } satisfies McpSkillProvider);\n };\n}\n\nexport function bindMcpSkills(context: PoMcpPluginContext): readonly PoMcpSkill[] {\n const provider = context.services.get<McpSkillProvider>(MCP_SKILLS_SERVICE);\n if (!provider) throw new Error('Discovered MCP skills are unavailable.');\n return provider.list(context);\n}\n","import { Type } from 'typebox';\n\nexport const searchSkillsParameters = Type.Object({ query: Type.Optional(Type.String()) });\nexport const loadSkillParameters = Type.Object({ name: Type.String() });\n","import type { PoHeadlessHostService, PoHeadlessTool } from '@agimon-ai/po-core/headless';\n\nimport { loadSkillParameters, searchSkillsParameters } from '../../schemas/mcpSkillTools';\n\nfunction text(text: string) {\n return { content: [{ type: 'text' as const, text }] };\n}\n\nfunction skills(context: Parameters<PoHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): PoHeadlessTool<typeof searchSkillsParameters> {\n return {\n // web-plugin-tool-renderers: ignore search_skills (remote MCP only)\n name: 'search_skills',\n label: 'Search skills',\n description: 'Discover repository and active-domain skills available to this remote connection.',\n parameters: searchSkillsParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { query }, _signal, _onUpdate, context) {\n const needle = query?.trim().toLocaleLowerCase();\n const available = await skills(context).list();\n const matches =\n needle === undefined || needle === ''\n ? available\n : available.filter(({ name, description }) => `${name}\\n${description}`.toLocaleLowerCase().includes(needle));\n return { ...text(JSON.stringify(matches)), structuredContent: { skills: matches } };\n },\n };\n}\n\n/**\n * The session agent's way into a skill. Its skills are listed in the prompt, which Po\n * controls, so it needs no search; a remote agent's prompt is not ours, hence the pair above.\n */\nexport function createSessionLoadSkillTool(\n host: Pick<PoHeadlessHostService, 'readSkill'>,\n): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (server session only, shown as a generic call)\n // PO_LOAD_SKILL_TOOL, spelled out so the renderer rule can read it.\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full instructions of a skill from the available skills list by exact name.',\n parameters: loadSkillParameters,\n executionMode: 'parallel',\n async execute(_toolCallId, { name }) {\n const body = host.readSkill(name);\n if (body === undefined) return { ...text(`No skill named ${name} is available in this session.`), isError: true };\n return text(body);\n },\n };\n}\n\nexport function createLoadSkillTool(): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (remote MCP only)\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full Markdown instructions for an available remote skill by exact name.',\n parameters: loadSkillParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { name }, _signal, _onUpdate, context) {\n return text(await skills(context).read(name));\n },\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { Worker } from 'node:worker_threads';\n\nimport {\n type BuildSystemPromptOptions,\n formatSkillsForPrompt,\n getAgentDir,\n loadSkills,\n type Skill,\n stripFrontmatter,\n} from '@earendil-works/pi-coding-agent';\n\nimport type { DeferredSkillLoaderOptions, DeferredSkillSnapshot } from '../../types/skills';\n\nexport type { DeferredSkillLoaderOptions, DeferredSkillSnapshot } from '../../types/skills';\n\nconst PI_PACKAGE = '@earendil-works/pi-coding-agent';\nconst PACKAGE_MANIFEST = 'package.json';\n// A fresh worker takes ~240 ms to import Pi's main entry but ~20 ms for this module alone, and\n// every session spawns one. ponytail: not in Pi's exports map, so a Pi release that moves it\n// falls back to the main entry (slow, still correct); deferredSkills.test.ts pins the path.\nconst PI_SKILLS_MODULE = path.join('dist', 'core', 'skills.js');\nconst SKILL_WORKER_SOURCE = `\nconst { parentPort, workerData } = require('node:worker_threads');\nvoid (async () => {\n try {\n const { loadSkills } = await import(workerData.piModuleUrl);\n parentPort.postMessage({ ok: true, result: loadSkills(workerData.loadOptions) });\n } catch (error) {\n parentPort.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) });\n }\n})();\n`;\n\nfunction installedPiModuleUrl(): string {\n const anchors = [process.argv[1], fileURLToPath(import.meta.url), path.join(process.cwd(), PACKAGE_MANIFEST)];\n for (const anchor of anchors) {\n if (!anchor) continue;\n let directory = path.dirname(path.resolve(anchor));\n while (true) {\n const packageRoot = path.join(directory, 'node_modules', PI_PACKAGE);\n const manifestPath = path.join(packageRoot, PACKAGE_MANIFEST);\n try {\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as { main?: unknown };\n if (typeof manifest.main === 'string') {\n const entry = path.resolve(packageRoot, manifest.main);\n const skills = path.join(packageRoot, PI_SKILLS_MODULE);\n if (fs.existsSync(skills)) return pathToFileURL(skills).href;\n if (fs.existsSync(entry)) return pathToFileURL(entry).href;\n }\n } catch {\n // This anchor does not expose Pi at this level; continue up its module chain.\n }\n const parent = path.dirname(directory);\n if (parent === directory) break;\n directory = parent;\n }\n }\n throw new Error(`Cannot resolve ${PI_PACKAGE} from the Po or host module trees`);\n}\n\ninterface SkillWorkerResult {\n ok: boolean;\n result?: { skills: Skill[]; diagnostics: Array<{ path?: string; message: string }> };\n error?: string;\n}\n\n/**\n * One session's Po-selected skill inventory.\n *\n * Construction is deliberately cheap. `start()` schedules the synchronous Pi\n * walker after session_start returns, while `ready()` is the single promise the\n * first input awaits before Pi expands `/skill:name`.\n */\nexport class DeferredSkillLoader {\n readonly #options: DeferredSkillLoaderOptions;\n #promise: Promise<DeferredSkillSnapshot> | undefined;\n\n constructor(options: DeferredSkillLoaderOptions) {\n this.#options = options;\n }\n\n start(): Promise<DeferredSkillSnapshot> {\n if (this.#promise) return this.#promise;\n\n if (!this.#options.schedule && !this.#options.load) {\n this.#promise = this.#loadInWorker();\n return this.#promise;\n }\n\n const schedule = this.#options.schedule ?? ((load: () => void) => setImmediate(load));\n this.#promise = new Promise((resolve) => {\n const fail = (error: unknown): void => {\n resolve(this.#failure(error));\n };\n try {\n schedule(() => {\n try {\n resolve(this.#load());\n } catch (error) {\n fail(error);\n }\n });\n } catch (error) {\n fail(error);\n }\n });\n return this.#promise;\n }\n\n ready(): Promise<DeferredSkillSnapshot> {\n return this.start();\n }\n\n #loadOptions(): { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean } {\n return {\n cwd: this.#options.cwd,\n agentDir: this.#options.agentDir ?? getAgentDir(),\n skillPaths: [...this.#options.skillPaths],\n includeDefaults: false,\n };\n }\n\n #snapshot(result: {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n }): DeferredSkillSnapshot {\n return {\n skills: result.skills,\n diagnostics: result.diagnostics.map(\n (diagnostic) => `${diagnostic.path ?? this.#options.cwd}: ${diagnostic.message}`,\n ),\n };\n }\n\n #failure(error: unknown): DeferredSkillSnapshot {\n return {\n skills: [],\n diagnostics: [`${this.#options.cwd}: ${error instanceof Error ? error.message : String(error)}`],\n };\n }\n\n #load(): DeferredSkillSnapshot {\n return this.#snapshot((this.#options.load ?? loadSkills)(this.#loadOptions()));\n }\n\n #loadInWorker(): Promise<DeferredSkillSnapshot> {\n return new Promise((resolve) => {\n let settled = false;\n const settle = (snapshot: DeferredSkillSnapshot): void => {\n if (settled) return;\n settled = true;\n resolve(snapshot);\n };\n let worker: Worker;\n try {\n worker = new Worker(SKILL_WORKER_SOURCE, {\n eval: true,\n workerData: {\n piModuleUrl: this.#options.piModuleUrl ?? installedPiModuleUrl(),\n loadOptions: this.#loadOptions(),\n },\n });\n } catch (error) {\n settle(this.#failure(error));\n return;\n }\n worker.unref();\n worker.once('message', (message: SkillWorkerResult) => {\n if (!message.ok || !message.result) {\n settle(this.#failure(message.error ?? 'Skill worker failed'));\n return;\n }\n settle(this.#snapshot(message.result));\n });\n worker.once('error', (error) => settle(this.#failure(error)));\n worker.once('exit', (code) => {\n if (code !== 0) settle(this.#failure(`Skill worker exited with code ${code}`));\n });\n });\n }\n}\n\n/** Appends only the inventory Pi would append for these deferred skills. */\nexport function buildPromptWithDeferredSkills(\n systemPrompt: string,\n options: BuildSystemPromptOptions,\n skills: Skill[],\n): string {\n if (options.selectedTools && !options.selectedTools.includes('read')) return systemPrompt;\n return `${systemPrompt}${formatSkillsForPrompt(skills)}`;\n}\n\n/** Expands a deferred `/skill:name` before Pi consults its synchronous inventory. */\nexport function expandDeferredSkillCommand(text: string, skills: readonly Skill[]): string {\n if (!text.startsWith('/skill:')) return text;\n const spaceIndex = text.indexOf(' ');\n const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);\n const skill = skills.find((candidate) => candidate.name === skillName);\n if (!skill) return text;\n\n try {\n const args = spaceIndex === -1 ? '' : text.slice(spaceIndex + 1).trim();\n const body = stripFrontmatter(fs.readFileSync(skill.filePath, 'utf8')).trim();\n const skillBlock = `<skill name=\"${skill.name}\" location=\"${skill.filePath}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n return args ? `${skillBlock}\\n\\n${args}` : skillBlock;\n } catch {\n // Pi uses the same literal-text fallback if a skill disappears between\n // discovery and submission; the agent can still respond to the command.\n return text;\n }\n}\n","import path from 'node:path';\n\nimport {\n listDomainNamesAsync,\n resolvePluginEntriesAsync,\n resolveSharedSkillsAsync,\n type PluginEntry,\n} from '@agimon-ai/po-config/domains';\nimport { readHarnessState } from '@agimon-ai/po-config/harnessState';\nimport type { PoHeadlessExecutionContext } from '@agimon-ai/po-core/headless';\nimport { BACKGROUND_CONTEXT, nodeHost, withAbortSignal, type FileSystem } from '@agimon-ai/po-core/platform';\nimport { materializePluginEntries } from '@agimon-ai/po-domain/plugins';\nimport { collectResources, discoverSkillsAsync } from '@agimon-ai/po-domain/resources';\nimport { formatSkillsForPrompt } from '@earendil-works/pi-coding-agent';\n\nimport type { ServerSkillInventory } from '../../types/skills';\nimport { DeferredSkillLoader } from '../deferredSkills';\n\nexport type { ServerSkillGroup, ServerSkillInventory } from '../../types/skills';\n\ninterface SkillSource {\n readonly domain?: string;\n readonly paths: readonly string[];\n readonly mcpPaths: readonly string[];\n /** Advertised by this package rather than by po-domain. */\n readonly listed?: boolean;\n}\n\nconst LOCAL_ONLY_MCP_PACKAGES = new Set([\n '@agimon-ai/po-author',\n '@agimon-ai/po-git',\n '@agimon-ai/po-goal',\n '@agimon-ai/po-help',\n '@agimon-ai/po-loop',\n '@agimon-ai/po-user-feedback',\n '@agimon-ai/po-voice',\n '@agimon-ai/po-workflow',\n]);\n\ntype IoContext = Parameters<FileSystem['readTextFile']>[1];\n\nasync function isMcpSkillPlugin(fs: FileSystem, entry: PluginEntry, context: IoContext): Promise<boolean> {\n if (entry.source?.type === 'npm') return !LOCAL_ONLY_MCP_PACKAGES.has(entry.source.package);\n // A package manifest is optional for catalog plugins, but a present identity\n // that cannot be read or parsed must never make the plugin remotely eligible.\n const source = await fs.readTextFile(path.join(entry.directory, 'package.json'), context);\n if (!source.ok) return source.error.code === 'not_found';\n try {\n const manifest = JSON.parse(source.value) as unknown;\n if (typeof manifest !== 'object' || manifest === null || Array.isArray(manifest)) return false;\n const name = (manifest as { name?: unknown }).name;\n return typeof name === 'string' && name.length > 0 && name.trim() === name && !LOCAL_ONLY_MCP_PACKAGES.has(name);\n } catch {\n return false;\n }\n}\n\n/**\n * Whether a discovered skill came from one of these paths.\n *\n * A source holds exact SKILL.md files for plugin and shared skills, but a bare\n * directory for `.po/skills`, so membership is containment rather than\n * equality.\n */\nfunction within(filePath: string, roots: readonly string[]): boolean {\n const resolved = path.resolve(filePath);\n return roots.some((root) => {\n const base = path.resolve(root);\n return resolved === base || resolved.startsWith(`${base}${path.sep}`);\n });\n}\n/**\n * Every skill the session could reach, grouped by what activates it.\n *\n * A cockpit `/domains` switch reconciles contributions rather than rebuilding\n * the server plugin: the session declaration is mounted once. So enumerating only the\n * launch selection would freeze the command list at whatever was selected when\n * the session host was built. Enumerating every domain up front and tagging\n * each group with its `when` lets the existing kernel refresh do the work, the\n * same way domainServerResources registers per-domain skill resources.\n */\nexport async function discoverServerSkills(\n execution: PoHeadlessExecutionContext,\n fs: FileSystem,\n signal: AbortSignal,\n): Promise<ServerSkillInventory> {\n signal.throwIfAborted();\n const context = withAbortSignal(signal, BACKGROUND_CONTEXT);\n const state = readHarnessState(execution.environment);\n const home = execution.environment.HOME ?? nodeHost.homeDirectory();\n const repoSkills = path.join(execution.repoRoot, '.po', 'skills');\n const sources: SkillSource[] = [];\n const diagnostics: string[] = [];\n // A malformed domain is a configuration error in one domain, so it must not\n // remove every other domain's skills. Enumerating the whole manifest is new\n // exposure here: reading the launch selection could not fail this way.\n const domains: string[] = [];\n try {\n domains.push(...(await listDomainNamesAsync(fs, execution.repoRoot, context, home)));\n } catch (error) {\n diagnostics.push(`Domain skills are unavailable: ${error instanceof Error ? error.message : String(error)}`);\n }\n\n // Domain-scoped plugin skills come first. The host keeps the first command\n // registered for a name, so this ordering reproduces the collector's rule\n // that a selected plugin outranks a shared fallback of the same name.\n for (const domain of domains) {\n signal.throwIfAborted();\n try {\n const plugins = await materializePluginEntries(\n await resolvePluginEntriesAsync(fs, execution.repoRoot, [domain], [], context, home),\n );\n const mcpPluginRoots = (\n await Promise.all(\n plugins.map(async (plugin) =>\n (await isMcpSkillPlugin(fs, plugin, context)) ? path.join(plugin.directory, 'skills') : undefined,\n ),\n )\n ).filter((root): root is string => root !== undefined);\n const collected = await collectResources(execution.repoRoot, plugins, {\n agents: false,\n mcp: false,\n sharedSkills: false,\n });\n try {\n if (collected.skillDirectories.length > 0) {\n const paths = [...collected.skillDirectories];\n sources.push({\n domain,\n paths,\n // Exact collected paths retain this domain's per-plugin skill subset.\n // A plugin root would also admit skills loaded for another domain.\n mcpPaths: paths.filter((skillPath) => within(skillPath, mcpPluginRoots)),\n });\n }\n } finally {\n await collected.cleanup();\n }\n } catch (error) {\n diagnostics.push(`Domain '${domain}': ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n // Shared .claude/skills are not owned by any one domain, and a domain can opt\n // out of them. `when` cannot express \"unless every selected domain opts out\",\n // but registering a copy per domain that keeps them says the same thing: the\n // group is active if any such domain is selected, which is exactly what\n // resolveSharedSkills computes.\n signal.throwIfAborted();\n const shared = await discoverSkillsAsync(execution.repoRoot, path.join(execution.repoRoot, '.claude', 'skills'));\n if (shared.length > 0) {\n const sharedPaths = shared.map((skill) => skill.path);\n for (const domain of domains) {\n if (await resolveSharedSkillsAsync(fs, execution.repoRoot, [domain], context, home)) {\n sources.push({ domain, paths: sharedPaths, mcpPaths: sharedPaths, listed: true });\n }\n }\n }\n\n // Repository-local skills belong to the repository, not to a selection.\n sources.push({ paths: [repoSkills], mcpPaths: [repoSkills], listed: true });\n\n signal.throwIfAborted();\n // One walk for every group. Pi drops a path it has already loaded, so the\n // loader gets the unique set and each group selects its own skills back out.\n const unique = [...new Set([...sources.flatMap((source) => source.paths), ...(state.skillDirectories ?? [])])];\n const inventory = await new DeferredSkillLoader({ cwd: execution.cwd, skillPaths: unique }).ready();\n signal.throwIfAborted();\n\n const groups = sources\n .map((source) => ({\n ...(source.domain === undefined ? {} : { domain: source.domain }),\n skills: inventory.skills.filter((skill) => within(skill.filePath, source.paths)),\n }))\n .filter((group) => group.skills.length > 0);\n const listed = sources\n .filter((source) => source.listed === true)\n .map((source) => ({\n ...(source.domain === undefined ? {} : { domain: source.domain }),\n skills: inventory.skills.filter((skill) => within(skill.filePath, source.paths)),\n }))\n .filter((group) => group.skills.length > 0);\n const mcpGroups = sources\n .map((source) => ({\n ...(source.domain === undefined ? {} : { domain: source.domain }),\n skills: inventory.skills.filter((skill) => within(skill.filePath, source.mcpPaths)),\n }))\n .filter((group) => group.skills.length > 0);\n\n // The catalog still describes the active selection rather than everything on\n // disk, because it is prompt text rather than a menu.\n const activeSkills = inventory.skills.filter((skill) =>\n within(skill.filePath, [...(state.skillDirectories ?? []), repoSkills]),\n );\n return {\n inventory: { skills: inventory.skills, diagnostics: [...inventory.diagnostics, ...diagnostics] },\n groups,\n mcpGroups,\n listed,\n catalog: formatSkillsForPrompt(activeSkills) || '(no discovered skills)',\n };\n}\n","import type { PoHeadlessCommand } from '@agimon-ai/po-core/headless';\nimport type { Skill } from '@earendil-works/pi-coding-agent';\n\nimport { expandDeferredSkillCommand, type DeferredSkillSnapshot } from '../../../../../services/deferredSkills';\nimport type { ServerSkillGroup } from '../../../../../services/serverInventory';\nimport { SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX, SKILLS_COMMAND } from '../../../../../types/skills';\n\n/**\n * One command per skill, gated by whatever activates that skill.\n *\n * Expansion always reads the full inventory, not the group: a command is only\n * dispatchable while its condition holds, so by the time one runs its skill is\n * active, and looking it up in the union keeps one lookup table instead of one\n * per domain.\n */\nfunction skillCommand(skill: Skill, domain: string | undefined, inventory: DeferredSkillSnapshot): PoHeadlessCommand {\n return {\n ...(domain === undefined ? {} : { when: { domain } }),\n name: `${SKILL_COMMAND_PREFIX}${skill.name}`,\n description: skill.description,\n execute: async (args, execution) => {\n const text = `${SKILL_INVOCATION_PREFIX}${skill.name}${args ? ` ${args}` : ''}`;\n const expanded = expandDeferredSkillCommand(text, inventory.skills);\n if (expanded === text) throw new Error(`Skill '${skill.name}' is no longer readable.`);\n await admit(execution.session, expanded);\n },\n };\n}\n\n/**\n * The dispatching prompt acks when this command returns, so awaiting the whole\n * turn (session.prompt) kept the composer draft until the run ended.\n */\nfunction admit(session: Parameters<PoHeadlessCommand['execute']>[1]['session'], text: string): Promise<void> {\n return session.admitPrompt ? session.admitPrompt(text, 'prompt', 'operator') : session.prompt(text);\n}\nexport function createSkillCommands(\n inventory: DeferredSkillSnapshot,\n catalog: string,\n groups: readonly ServerSkillGroup[],\n): readonly PoHeadlessCommand[] {\n return [\n {\n name: SKILLS_COMMAND,\n description: 'List discovered skills or invoke one by name.',\n async execute(args, execution) {\n const requested = args.trim();\n if (requested) {\n await admit(\n execution.session,\n expandDeferredSkillCommand(`${SKILL_INVOCATION_PREFIX}${requested}`, inventory.skills),\n );\n return;\n }\n await execution.client.notify({\n title: 'Po skills',\n body: catalog,\n level: 'info',\n });\n },\n },\n ...groups.flatMap((group) => group.skills.map((skill) => skillCommand(skill, group.domain, inventory))),\n ];\n}\n","import { readHarnessState } from '@agimon-ai/po-config/harnessState';\nimport type { PoHeadlessHostService } from '@agimon-ai/po-core/headless';\nimport { BACKGROUND_CONTEXT, getOrThrow, nodeHost, type FileSystem } from '@agimon-ai/po-core/platform';\nimport {\n type PoServerResources,\n type PoServerSessionPlugin,\n packageResourcePath,\n readPackageResource,\n} from '@agimon-ai/po-core/serverPlugin';\n\nimport { SKILL_INVENTORY_FRESH_MS } from '../../../../../constants/skillInventory';\nimport { mountMcpSkills } from '../../../../../services/mcpSkills';\nimport { createSessionLoadSkillTool } from '../../../../../services/mcpSkillTools';\nimport { discoverServerSkills } from '../../../../../services/serverInventory';\nimport { createSkillCommands } from './skillCommands';\n\nexport async function createSkillServer(\n agent: PoHeadlessHostService,\n fs: FileSystem,\n signal: AbortSignal,\n resources: PoServerResources,\n): Promise<PoServerSessionPlugin> {\n const execution = agent.context;\n // Sessions with the same inputs share one discovery. The key holds every input discoverServerSkills\n // reads besides the disk, so equal keys give equal inventories, and the window bounds how old that\n // read of the disk can be.\n const key = [\n 'inventory',\n execution.repoRoot,\n execution.cwd,\n execution.environment.HOME ?? nodeHost.homeDirectory(),\n ...(readHarnessState(execution.environment).skillDirectories ?? []),\n Math.floor(Date.now() / SKILL_INVENTORY_FRESH_MS),\n ];\n const { inventory, catalog, groups, mcpGroups, listed } = await resources.acquire(key, () =>\n // Every session sharing the discovery waits on it, so none of their signals may abort it.\n discoverServerSkills(execution, fs, new AbortController().signal),\n );\n return {\n commands: createSkillCommands(inventory, catalog, groups),\n tools: [createSessionLoadSkillTool(agent)],\n services: [mountMcpSkills(mcpGroups, fs, signal)],\n resources: [\n // Each skill joins the prompt's one <available_skills> list, gated like the rest.\n ...listed.flatMap((group) =>\n group.skills.map((skill) => ({\n name: skill.name,\n description: skill.description,\n path: skill.filePath,\n kind: 'skill' as const,\n ...(group.domain === undefined\n ? {}\n : { when: { domain: group.domain, attribution: { kind: 'domain' as const, mode: group.domain } } }),\n read: async () => getOrThrow(await fs.readTextFile(skill.filePath, BACKGROUND_CONTEXT)),\n })),\n ),\n ...[\n {\n name: 'po-author-skill',\n description:\n 'Author and distribute Po agent skills. Use when creating a SKILL.md, contributing a runtime skill directory, or publishing activation-gated Help prompts and diagnostic tools.',\n },\n {\n name: 'po-use-skill',\n description:\n \"Use Po's skill catalog and deferred discovery. Diagnose missing or shadowed skills and invoke the narrowest available guidance.\",\n },\n ].map((skill) => ({\n ...skill,\n when: { state: { 'minor-mode': 'help' }, attribution: { kind: 'minor' as const, mode: 'help' } },\n kind: 'skill' as const,\n path: packageResourcePath(import.meta.url, `src/prompts/${skill.name}/SKILL.md`),\n read: () => readPackageResource(import.meta.url, `src/prompts/${skill.name}/SKILL.md`),\n })),\n ],\n };\n}\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport { requirePoFs } from '@agimon-ai/po-core/platform';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nimport { createSkillServer } from './_lib/skillServer';\n\nexport default defineRoot(async ({ agent, context: root, signal, resources }: PoServerPluginContext) => {\n const runtime: PoServerSessionPlugin = agent\n ? await createSkillServer(agent, requirePoFs(root), signal, resources)\n : { commands: [], resources: [] };\n return { value: runtime, services: runtime.services };\n});\n","import { defineRoutedContribution } from '@agimon-ai/po-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, PoServerSessionPlugin>) => context.root.commands!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution } from '@agimon-ai/po-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, PoServerSessionPlugin>) => context.root.resources!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\n/** The session agent's skill loader; the Pi CLI keeps Pi's own skill handling. */\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, PoServerSessionPlugin>) => context.root.tools?.[0],\n { cardinality: 'optional' },\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport commandSkills from '../src/extensions/workspaces/sessions/(backend)/command/skills.server';\nimport resourceSkills from '../src/extensions/workspaces/sessions/(backend)/resource/skills.server';\nimport toolLoadSkill from '../src/extensions/workspaces/sessions/(backend)/tool/load_skill.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\ntype ManyFactory<T, C> = (context: C) => T | readonly T[];\nconst many = <T, C>(value: T | readonly T[] | ManyFactory<T, C>, context: C): readonly T[] => {\n const resolved = typeof value === 'function' ? (value as ManyFactory<T, C>)(context) : value;\n return Array.isArray(resolved) ? resolved : [resolved as T];\n};\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-skill',\n providerOverrides: composeServerProviderOverrides(rootSession),\n injects: composeRootInjects({ session: [rootSession] }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): PoServerSessionPlugin['commands'] { return [...many(commandSkills, contextSession)]; },\n get resources(): PoServerSessionPlugin['resources'] { return [...many(resourceSkills, contextSession)]; },\n get tools(): PoServerSessionPlugin['tools'] { return defined([via({ name: 'load_skill' }, at(toolLoadSkill, contextSession))]); },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.cjs","names":["getOrThrow","withAbortSignal","BACKGROUND_CONTEXT","Type","path","fileURLToPath","fs","pathToFileURL","getAgentDir","loadSkills","Worker","stripFrontmatter","path","withAbortSignal","BACKGROUND_CONTEXT","readHarnessState","nodeHost","listDomainNamesAsync","materializePluginEntries","resolvePluginEntriesAsync","collectResources","discoverSkillsAsync","resolveSharedSkillsAsync","formatSkillsForPrompt","SKILL_COMMAND_PREFIX","SKILL_INVOCATION_PREFIX","SKILLS_COMMAND","nodeHost","readHarnessState","getOrThrow","BACKGROUND_CONTEXT","packageResourcePath","readPackageResource","defineRoot","requirePoFs","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineServerPlugin","composeServerProviderOverrides","rootSession","composeRootInjects","composeRootHooks","commandSkills","resourceSkills","toolLoadSkill"],"sources":["../../src/constants/skillInventory.ts","../../src/services/mcpSkills/index.ts","../../src/schemas/mcpSkillTools.ts","../../src/services/mcpSkillTools/index.ts","../../src/services/deferredSkills/index.ts","../../src/services/serverInventory/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillCommands.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillServer.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/skills.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/skills.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/load_skill.server.ts","../../generated/server.ts"],"sourcesContent":["/**\n * How long sessions with the same inputs share one skill inventory instead of each reading the disk.\n * ponytail: a skill added on disk reaches new sessions within this window, not at once; watch the\n * skill roots if that lag matters.\n */\nexport const SKILL_INVENTORY_FRESH_MS = 60_000;\n","import type { PoMcpPluginContext, PoMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { BACKGROUND_CONTEXT, getOrThrow, withAbortSignal, type FileSystem } from '@agimon-ai/po-core/platform';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'po/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: PoMcpPluginContext): readonly PoMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], fs: FileSystem, signal: AbortSignal) {\n return (context: Context): void => {\n context.provide(MCP_SKILLS_SERVICE, {\n list(remote) {\n signal.throwIfAborted();\n remote.signal.throwIfAborted();\n const domains = remote.selection.read().domains;\n const skills = new Map<string, PoMcpSkill>();\n for (const group of groups) {\n if (group.domains !== undefined && !group.domains.some((domain) => domains.includes(domain))) continue;\n for (const skill of group.skills) {\n // Retain name precedence defensively for remote providers.\n if (skills.has(skill.name)) continue;\n skills.set(skill.name, {\n name: skill.name,\n description: skill.description,\n async read() {\n const readSignal = AbortSignal.any([signal, remote.signal]);\n readSignal.throwIfAborted();\n const text = getOrThrow(\n await fs.readTextFile(skill.filePath, withAbortSignal(readSignal, BACKGROUND_CONTEXT)),\n );\n readSignal.throwIfAborted();\n return text;\n },\n });\n }\n }\n return [...skills.values()];\n },\n } satisfies McpSkillProvider);\n };\n}\n\nexport function bindMcpSkills(context: PoMcpPluginContext): readonly PoMcpSkill[] {\n const provider = context.services.get<McpSkillProvider>(MCP_SKILLS_SERVICE);\n if (!provider) throw new Error('Discovered MCP skills are unavailable.');\n return provider.list(context);\n}\n","import { Type } from 'typebox';\n\nexport const searchSkillsParameters = Type.Object({ query: Type.Optional(Type.String()) });\nexport const loadSkillParameters = Type.Object({ name: Type.String() });\n","import type { PoHeadlessHostService, PoHeadlessTool } from '@agimon-ai/po-core/headless';\n\nimport { loadSkillParameters, searchSkillsParameters } from '../../schemas/mcpSkillTools';\n\nfunction text(text: string) {\n return { content: [{ type: 'text' as const, text }] };\n}\n\nfunction skills(context: Parameters<PoHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): PoHeadlessTool<typeof searchSkillsParameters> {\n return {\n // web-plugin-tool-renderers: ignore search_skills (remote MCP only)\n name: 'search_skills',\n label: 'Search skills',\n description: 'Discover repository and active-domain skills available to this remote connection.',\n parameters: searchSkillsParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { query }, _signal, _onUpdate, context) {\n const needle = query?.trim().toLocaleLowerCase();\n const available = await skills(context).list();\n const matches =\n needle === undefined || needle === ''\n ? available\n : available.filter(({ name, description }) => `${name}\\n${description}`.toLocaleLowerCase().includes(needle));\n return { ...text(JSON.stringify(matches)), structuredContent: { skills: matches } };\n },\n };\n}\n\nexport function createSessionSearchSkillsTool(\n host: Pick<PoHeadlessHostService, 'listSkills'>,\n): PoHeadlessTool<typeof searchSkillsParameters> {\n return {\n // web-plugin-tool-renderers: ignore search_skills (server session only, shown as a generic call)\n name: 'search_skills',\n label: 'Search skills',\n description: 'Search available session skills by name or description.',\n parameters: searchSkillsParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n executionMode: 'parallel',\n async execute(_toolCallId, { query }) {\n const needle = query?.trim().toLocaleLowerCase();\n const available = host.listSkills().map(({ name, description }) => ({ name, description }));\n const matches =\n needle === undefined || needle === ''\n ? available\n : available.filter(({ name, description }) => `${name}\\n${description}`.toLocaleLowerCase().includes(needle));\n return { ...text(JSON.stringify(matches)), structuredContent: { skills: matches } };\n },\n };\n}\n\nexport function createSessionLoadSkillTool(\n host: Pick<PoHeadlessHostService, 'readSkill'>,\n): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (server session only, shown as a generic call)\n // PO_LOAD_SKILL_TOOL, spelled out so the renderer rule can read it.\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full instructions of an available session skill by exact name.',\n parameters: loadSkillParameters,\n executionMode: 'parallel',\n async execute(_toolCallId, { name }) {\n const body = host.readSkill(name);\n if (body === undefined) return { ...text(`No skill named ${name} is available in this session.`), isError: true };\n return text(body);\n },\n };\n}\n\nexport function createLoadSkillTool(): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (remote MCP only)\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full Markdown instructions for an available remote skill by exact name.',\n parameters: loadSkillParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { name }, _signal, _onUpdate, context) {\n return text(await skills(context).read(name));\n },\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { Worker } from 'node:worker_threads';\n\nimport {\n type BuildSystemPromptOptions,\n formatSkillsForPrompt,\n getAgentDir,\n loadSkills,\n type Skill,\n stripFrontmatter,\n} from '@earendil-works/pi-coding-agent';\n\nimport type { DeferredSkillLoaderOptions, DeferredSkillSnapshot } from '../../types/skills';\n\nexport type { DeferredSkillLoaderOptions, DeferredSkillSnapshot } from '../../types/skills';\n\nconst PI_PACKAGE = '@earendil-works/pi-coding-agent';\nconst PACKAGE_MANIFEST = 'package.json';\n// A fresh worker takes ~240 ms to import Pi's main entry but ~20 ms for this module alone, and\n// every session spawns one. ponytail: not in Pi's exports map, so a Pi release that moves it\n// falls back to the main entry (slow, still correct); deferredSkills.test.ts pins the path.\nconst PI_SKILLS_MODULE = path.join('dist', 'core', 'skills.js');\nconst SKILL_WORKER_SOURCE = `\nconst { parentPort, workerData } = require('node:worker_threads');\nvoid (async () => {\n try {\n const { loadSkills } = await import(workerData.piModuleUrl);\n parentPort.postMessage({ ok: true, result: loadSkills(workerData.loadOptions) });\n } catch (error) {\n parentPort.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) });\n }\n})();\n`;\n\nfunction installedPiModuleUrl(): string {\n const anchors = [process.argv[1], fileURLToPath(import.meta.url), path.join(process.cwd(), PACKAGE_MANIFEST)];\n for (const anchor of anchors) {\n if (!anchor) continue;\n let directory = path.dirname(path.resolve(anchor));\n while (true) {\n const packageRoot = path.join(directory, 'node_modules', PI_PACKAGE);\n const manifestPath = path.join(packageRoot, PACKAGE_MANIFEST);\n try {\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as { main?: unknown };\n if (typeof manifest.main === 'string') {\n const entry = path.resolve(packageRoot, manifest.main);\n const skills = path.join(packageRoot, PI_SKILLS_MODULE);\n if (fs.existsSync(skills)) return pathToFileURL(skills).href;\n if (fs.existsSync(entry)) return pathToFileURL(entry).href;\n }\n } catch {\n // This anchor does not expose Pi at this level; continue up its module chain.\n }\n const parent = path.dirname(directory);\n if (parent === directory) break;\n directory = parent;\n }\n }\n throw new Error(`Cannot resolve ${PI_PACKAGE} from the Po or host module trees`);\n}\n\ninterface SkillWorkerResult {\n ok: boolean;\n result?: { skills: Skill[]; diagnostics: Array<{ path?: string; message: string }> };\n error?: string;\n}\n\n/**\n * One session's Po-selected skill inventory.\n *\n * Construction is deliberately cheap. `start()` schedules the synchronous Pi\n * walker after session_start returns, while `ready()` is the single promise the\n * first input awaits before Pi expands `/skill:name`.\n */\nexport class DeferredSkillLoader {\n readonly #options: DeferredSkillLoaderOptions;\n #promise: Promise<DeferredSkillSnapshot> | undefined;\n\n constructor(options: DeferredSkillLoaderOptions) {\n this.#options = options;\n }\n\n start(): Promise<DeferredSkillSnapshot> {\n if (this.#promise) return this.#promise;\n\n if (!this.#options.schedule && !this.#options.load) {\n this.#promise = this.#loadInWorker();\n return this.#promise;\n }\n\n const schedule = this.#options.schedule ?? ((load: () => void) => setImmediate(load));\n this.#promise = new Promise((resolve) => {\n const fail = (error: unknown): void => {\n resolve(this.#failure(error));\n };\n try {\n schedule(() => {\n try {\n resolve(this.#load());\n } catch (error) {\n fail(error);\n }\n });\n } catch (error) {\n fail(error);\n }\n });\n return this.#promise;\n }\n\n ready(): Promise<DeferredSkillSnapshot> {\n return this.start();\n }\n\n #loadOptions(): { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean } {\n return {\n cwd: this.#options.cwd,\n agentDir: this.#options.agentDir ?? getAgentDir(),\n skillPaths: [...this.#options.skillPaths],\n includeDefaults: false,\n };\n }\n\n #snapshot(result: {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n }): DeferredSkillSnapshot {\n return {\n skills: result.skills,\n diagnostics: result.diagnostics.map(\n (diagnostic) => `${diagnostic.path ?? this.#options.cwd}: ${diagnostic.message}`,\n ),\n };\n }\n\n #failure(error: unknown): DeferredSkillSnapshot {\n return {\n skills: [],\n diagnostics: [`${this.#options.cwd}: ${error instanceof Error ? error.message : String(error)}`],\n };\n }\n\n #load(): DeferredSkillSnapshot {\n return this.#snapshot((this.#options.load ?? loadSkills)(this.#loadOptions()));\n }\n\n #loadInWorker(): Promise<DeferredSkillSnapshot> {\n return new Promise((resolve) => {\n let settled = false;\n const settle = (snapshot: DeferredSkillSnapshot): void => {\n if (settled) return;\n settled = true;\n resolve(snapshot);\n };\n let worker: Worker;\n try {\n worker = new Worker(SKILL_WORKER_SOURCE, {\n eval: true,\n workerData: {\n piModuleUrl: this.#options.piModuleUrl ?? installedPiModuleUrl(),\n loadOptions: this.#loadOptions(),\n },\n });\n } catch (error) {\n settle(this.#failure(error));\n return;\n }\n worker.unref();\n worker.once('message', (message: SkillWorkerResult) => {\n if (!message.ok || !message.result) {\n settle(this.#failure(message.error ?? 'Skill worker failed'));\n return;\n }\n settle(this.#snapshot(message.result));\n });\n worker.once('error', (error) => settle(this.#failure(error)));\n worker.once('exit', (code) => {\n if (code !== 0) settle(this.#failure(`Skill worker exited with code ${code}`));\n });\n });\n }\n}\n\n/** Appends only the inventory Pi would append for these deferred skills. */\nexport function buildPromptWithDeferredSkills(\n systemPrompt: string,\n options: BuildSystemPromptOptions,\n skills: Skill[],\n): string {\n if (options.selectedTools && !options.selectedTools.includes('read')) return systemPrompt;\n return `${systemPrompt}${formatSkillsForPrompt(skills)}`;\n}\n\n/** Expands a deferred `/skill:name` before Pi consults its synchronous inventory. */\nexport function expandDeferredSkillCommand(text: string, skills: readonly Skill[]): string {\n if (!text.startsWith('/skill:')) return text;\n const spaceIndex = text.indexOf(' ');\n const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);\n const skill = skills.find((candidate) => candidate.name === skillName);\n if (!skill) return text;\n\n try {\n const args = spaceIndex === -1 ? '' : text.slice(spaceIndex + 1).trim();\n const body = stripFrontmatter(fs.readFileSync(skill.filePath, 'utf8')).trim();\n const skillBlock = `<skill name=\"${skill.name}\" location=\"${skill.filePath}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n return args ? `${skillBlock}\\n\\n${args}` : skillBlock;\n } catch {\n // Pi uses the same literal-text fallback if a skill disappears between\n // discovery and submission; the agent can still respond to the command.\n return text;\n }\n}\n","import path from 'node:path';\n\nimport {\n listDomainNamesAsync,\n resolvePluginEntriesAsync,\n resolveSharedSkillsAsync,\n type PluginEntry,\n} from '@agimon-ai/po-config/domains';\nimport { readHarnessState } from '@agimon-ai/po-config/harnessState';\nimport type { PoHeadlessExecutionContext } from '@agimon-ai/po-core/headless';\nimport { BACKGROUND_CONTEXT, nodeHost, withAbortSignal, type FileSystem } from '@agimon-ai/po-core/platform';\nimport { materializePluginEntries } from '@agimon-ai/po-domain/plugins';\nimport { collectResources, discoverSkillsAsync } from '@agimon-ai/po-domain/resources';\nimport { formatSkillsForPrompt } from '@earendil-works/pi-coding-agent';\n\nimport type { ServerSkillInventory } from '../../types/skills';\nimport { DeferredSkillLoader } from '../deferredSkills';\n\nexport type { ServerSkillGroup, ServerSkillInventory } from '../../types/skills';\n\ninterface SkillSource {\n readonly domain?: string;\n readonly paths: readonly string[];\n readonly mcpPaths: readonly string[];\n}\n\nconst LOCAL_ONLY_MCP_PACKAGES = new Set([\n '@agimon-ai/po-author',\n '@agimon-ai/po-git',\n '@agimon-ai/po-goal',\n '@agimon-ai/po-help',\n '@agimon-ai/po-loop',\n '@agimon-ai/po-user-feedback',\n '@agimon-ai/po-voice',\n '@agimon-ai/po-workflow',\n]);\n\ntype IoContext = Parameters<FileSystem['readTextFile']>[1];\n\nasync function isMcpSkillPlugin(fs: FileSystem, entry: PluginEntry, context: IoContext): Promise<boolean> {\n if (entry.source?.type === 'npm') return !LOCAL_ONLY_MCP_PACKAGES.has(entry.source.package);\n // A package manifest is optional for catalog plugins, but a present identity\n // that cannot be read or parsed must never make the plugin remotely eligible.\n const source = await fs.readTextFile(path.join(entry.directory, 'package.json'), context);\n if (!source.ok) return source.error.code === 'not_found';\n try {\n const manifest = JSON.parse(source.value) as unknown;\n if (typeof manifest !== 'object' || manifest === null || Array.isArray(manifest)) return false;\n const name = (manifest as { name?: unknown }).name;\n return typeof name === 'string' && name.length > 0 && name.trim() === name && !LOCAL_ONLY_MCP_PACKAGES.has(name);\n } catch {\n return false;\n }\n}\n\n/**\n * Whether a discovered skill came from one of these paths.\n *\n * A source holds exact SKILL.md files for plugin and shared skills, but a bare\n * directory for `.po/skills`, so membership is containment rather than\n * equality.\n */\nfunction within(filePath: string, roots: readonly string[]): boolean {\n const resolved = path.resolve(filePath);\n return roots.some((root) => {\n const base = path.resolve(root);\n return resolved === base || resolved.startsWith(`${base}${path.sep}`);\n });\n}\n/**\n * Every skill the session could reach, grouped by what activates it.\n *\n * A cockpit `/domains` switch reconciles contributions rather than rebuilding\n * the server plugin: the session declaration is mounted once. So enumerating only the\n * launch selection would freeze the command list at whatever was selected when\n * the session host was built. Enumerating every domain up front and tagging\n * each group with its `when` lets the existing kernel refresh do the work, the\n * same way other host contributions follow selection changes.\n */\nexport async function discoverServerSkills(\n execution: PoHeadlessExecutionContext,\n fs: FileSystem,\n signal: AbortSignal,\n): Promise<ServerSkillInventory> {\n signal.throwIfAborted();\n const context = withAbortSignal(signal, BACKGROUND_CONTEXT);\n const state = readHarnessState(execution.environment);\n const home = execution.environment.HOME ?? nodeHost.homeDirectory();\n const repoSkills = path.join(execution.repoRoot, '.po', 'skills');\n const sources: SkillSource[] = [];\n const diagnostics: string[] = [];\n // A malformed domain is a configuration error in one domain, so it must not\n // remove every other domain's skills. Enumerating the whole manifest is new\n // exposure here: reading the launch selection could not fail this way.\n const domains: string[] = [];\n try {\n domains.push(...(await listDomainNamesAsync(fs, execution.repoRoot, context, home)));\n } catch (error) {\n diagnostics.push(`Domain skills are unavailable: ${error instanceof Error ? error.message : String(error)}`);\n }\n\n // Plugin sources precede shared sources, then repository sources.\n // ponytail: Pi globally name-dedupes before domain gating. A loaded plugin\n // shadows a same-name shared fallback even when its domain is off. This is\n // the current ceiling; upgrade to selection-aware loading if collisions arise.\n for (const domain of domains) {\n signal.throwIfAborted();\n try {\n const plugins = await materializePluginEntries(\n await resolvePluginEntriesAsync(fs, execution.repoRoot, [domain], [], context, home),\n );\n const mcpPluginRoots = (\n await Promise.all(\n plugins.map(async (plugin) =>\n (await isMcpSkillPlugin(fs, plugin, context)) ? path.join(plugin.directory, 'skills') : undefined,\n ),\n )\n ).filter((root): root is string => root !== undefined);\n const collected = await collectResources(execution.repoRoot, plugins, {\n agents: false,\n mcp: false,\n sharedSkills: false,\n });\n try {\n if (collected.skillDirectories.length > 0) {\n const paths = [...collected.skillDirectories];\n sources.push({\n domain,\n paths,\n // Exact collected paths retain this domain's per-plugin skill subset.\n // A plugin root would also admit skills loaded for another domain.\n mcpPaths: paths.filter((skillPath) => within(skillPath, mcpPluginRoots)),\n });\n }\n } finally {\n await collected.cleanup();\n }\n } catch (error) {\n diagnostics.push(`Domain '${domain}': ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n // Shared skills are admitted by any domain that keeps them.\n signal.throwIfAborted();\n const shared = await discoverSkillsAsync(execution.repoRoot, path.join(execution.repoRoot, '.claude', 'skills'));\n if (shared.length > 0) {\n const sharedPaths = shared.map((skill) => skill.path);\n for (const domain of domains) {\n if (await resolveSharedSkillsAsync(fs, execution.repoRoot, [domain], context, home)) {\n sources.push({ domain, paths: sharedPaths, mcpPaths: sharedPaths });\n }\n }\n }\n\n // Repository-local skills belong to the repository, not to a selection.\n sources.push({ paths: [repoSkills], mcpPaths: [repoSkills] });\n\n signal.throwIfAborted();\n // One walk for every group. Pi drops a path it has already loaded, so the\n // loader gets the unique set and each group selects its own skills back out.\n const unique = [...new Set([...sources.flatMap((source) => source.paths), ...(state.skillDirectories ?? [])])];\n const inventory = await new DeferredSkillLoader({ cwd: execution.cwd, skillPaths: unique }).ready();\n signal.throwIfAborted();\n\n // One group per loaded skill, merging every source admitting that exact path.\n // MCP admission is merged separately so local-only sources cannot broaden it.\n const merge = (key: 'paths' | 'mcpPaths') =>\n inventory.skills.flatMap((skill) => {\n const admitting = sources.filter((source) => within(skill.filePath, source[key]));\n if (admitting.length === 0) return [];\n const domains = [...new Set(admitting.flatMap((source) => (source.domain === undefined ? [] : [source.domain])))];\n return [\n {\n ...(admitting.some((source) => source.domain === undefined) ? {} : { domains }),\n skills: [skill],\n },\n ];\n });\n const groups = merge('paths');\n const mcpGroups = merge('mcpPaths');\n\n // The catalog still describes the active selection rather than everything on\n // disk, because it is prompt text rather than a menu.\n const activeSkills = inventory.skills.filter((skill) =>\n within(skill.filePath, [...(state.skillDirectories ?? []), repoSkills]),\n );\n return {\n inventory: { skills: inventory.skills, diagnostics: [...inventory.diagnostics, ...diagnostics] },\n groups,\n mcpGroups,\n catalog: formatSkillsForPrompt(activeSkills) || '(no discovered skills)',\n };\n}\n","import type { PoHeadlessCommand } from '@agimon-ai/po-core/headless';\nimport type { Skill } from '@earendil-works/pi-coding-agent';\n\nimport { expandDeferredSkillCommand, type DeferredSkillSnapshot } from '../../../../../services/deferredSkills';\nimport type { ServerSkillGroup } from '../../../../../services/serverInventory';\nimport { SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX, SKILLS_COMMAND } from '../../../../../types/skills';\n\n/**\n * One command per skill, gated by whatever activates that skill.\n *\n * Expansion always reads the full inventory, not the group: a command is only\n * dispatchable while its condition holds, so by the time one runs its skill is\n * active, and looking it up in the union keeps one lookup table instead of one\n * per domain.\n */\nfunction skillCommand(\n skill: Skill,\n domains: readonly string[] | undefined,\n inventory: DeferredSkillSnapshot,\n): PoHeadlessCommand {\n return {\n ...(domains === undefined ? {} : { when: { domains } }),\n name: `${SKILL_COMMAND_PREFIX}${skill.name}`,\n description: skill.description,\n execute: async (args, execution) => {\n const text = `${SKILL_INVOCATION_PREFIX}${skill.name}${args ? ` ${args}` : ''}`;\n const expanded = expandDeferredSkillCommand(text, inventory.skills);\n if (expanded === text) throw new Error(`Skill '${skill.name}' is no longer readable.`);\n await admit(execution.session, expanded);\n },\n };\n}\n\n/**\n * The dispatching prompt acks when this command returns, so awaiting the whole\n * turn (session.prompt) kept the composer draft until the run ended.\n */\nfunction admit(session: Parameters<PoHeadlessCommand['execute']>[1]['session'], text: string): Promise<void> {\n return session.admitPrompt ? session.admitPrompt(text, 'prompt', 'operator') : session.prompt(text);\n}\nexport function createSkillCommands(\n inventory: DeferredSkillSnapshot,\n catalog: string,\n groups: readonly ServerSkillGroup[],\n): readonly PoHeadlessCommand[] {\n return [\n {\n name: SKILLS_COMMAND,\n description: 'List discovered skills or invoke one by name.',\n async execute(args, execution) {\n const requested = args.trim();\n if (requested) {\n await admit(\n execution.session,\n expandDeferredSkillCommand(`${SKILL_INVOCATION_PREFIX}${requested}`, inventory.skills),\n );\n return;\n }\n await execution.client.notify({\n title: 'Po skills',\n body: catalog,\n level: 'info',\n });\n },\n },\n ...groups.flatMap((group) => group.skills.map((skill) => skillCommand(skill, group.domains, inventory))),\n ];\n}\n","import { readHarnessState } from '@agimon-ai/po-config/harnessState';\nimport type { PoHeadlessHostService } from '@agimon-ai/po-core/headless';\nimport { BACKGROUND_CONTEXT, getOrThrow, nodeHost, type FileSystem } from '@agimon-ai/po-core/platform';\nimport {\n type PoServerResources,\n type PoServerSessionPlugin,\n packageResourcePath,\n readPackageResource,\n} from '@agimon-ai/po-core/serverPlugin';\n\nimport { SKILL_INVENTORY_FRESH_MS } from '../../../../../constants/skillInventory';\nimport { mountMcpSkills } from '../../../../../services/mcpSkills';\nimport { createSessionLoadSkillTool, createSessionSearchSkillsTool } from '../../../../../services/mcpSkillTools';\nimport { discoverServerSkills } from '../../../../../services/serverInventory';\nimport { createSkillCommands } from './skillCommands';\n\nexport async function createSkillServer(\n agent: PoHeadlessHostService,\n fs: FileSystem,\n signal: AbortSignal,\n resources: PoServerResources,\n): Promise<PoServerSessionPlugin> {\n const execution = agent.context;\n // Sessions with the same inputs share one discovery. The key holds every input discoverServerSkills\n // reads besides the disk, so equal keys give equal inventories, and the window bounds how old that\n // read of the disk can be.\n const key = [\n 'inventory',\n execution.repoRoot,\n execution.cwd,\n execution.environment.HOME ?? nodeHost.homeDirectory(),\n ...(readHarnessState(execution.environment).skillDirectories ?? []),\n Math.floor(Date.now() / SKILL_INVENTORY_FRESH_MS),\n ];\n const { inventory, catalog, groups, mcpGroups } = await resources.acquire(key, () =>\n // Every session sharing the discovery waits on it, so none of their signals may abort it.\n discoverServerSkills(execution, fs, new AbortController().signal),\n );\n return {\n commands: createSkillCommands(inventory, catalog, groups),\n tools: [createSessionLoadSkillTool(agent), createSessionSearchSkillsTool(agent)],\n services: [mountMcpSkills(mcpGroups, fs, signal)],\n resources: [\n // Each skill joins the prompt's one <available_skills> list, gated like the rest.\n ...groups.flatMap((group) =>\n group.skills.map((skill) => ({\n name: skill.name,\n description: skill.description,\n path: skill.filePath,\n kind: 'skill' as const,\n ...(group.domains === undefined ? {} : { when: { domains: group.domains } }),\n read: async () => getOrThrow(await fs.readTextFile(skill.filePath, BACKGROUND_CONTEXT)),\n })),\n ),\n ...[\n {\n name: 'po-author-skill',\n description:\n 'Author and distribute Po agent skills. Use when creating a SKILL.md, contributing a runtime skill directory, or publishing activation-gated Help prompts and diagnostic tools.',\n },\n {\n name: 'po-use-skill',\n description:\n \"Use Po's skill catalog and deferred discovery. Diagnose missing or shadowed skills and invoke the narrowest available guidance.\",\n },\n ].map((skill) => ({\n ...skill,\n when: { state: { 'minor-mode': 'help' }, attribution: { kind: 'minor' as const, mode: 'help' } },\n kind: 'skill' as const,\n path: packageResourcePath(import.meta.url, `src/prompts/${skill.name}/SKILL.md`),\n read: () => readPackageResource(import.meta.url, `src/prompts/${skill.name}/SKILL.md`),\n })),\n ],\n };\n}\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport { requirePoFs } from '@agimon-ai/po-core/platform';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nimport { createSkillServer } from './_lib/skillServer';\n\nexport default defineRoot(async ({ agent, context: root, signal, resources }: PoServerPluginContext) => {\n const runtime: PoServerSessionPlugin = agent\n ? await createSkillServer(agent, requirePoFs(root), signal, resources)\n : { commands: [], resources: [] };\n return { value: runtime, services: runtime.services };\n});\n","import { defineRoutedContribution } from '@agimon-ai/po-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, PoServerSessionPlugin>) => context.root.commands!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution } from '@agimon-ai/po-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, PoServerSessionPlugin>) => context.root.resources!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\n/** The session agent's skill tools; the Pi CLI keeps Pi's own skill handling. */\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, PoServerSessionPlugin>) => context.root.tools ?? [],\n { cardinality: 'many' },\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport commandSkills from '../src/extensions/workspaces/sessions/(backend)/command/skills.server';\nimport resourceSkills from '../src/extensions/workspaces/sessions/(backend)/resource/skills.server';\nimport toolLoadSkill from '../src/extensions/workspaces/sessions/(backend)/tool/load_skill.server';\n\ntype ManyFactory<T, C> = (context: C) => T | readonly T[];\nconst many = <T, C>(value: T | readonly T[] | ManyFactory<T, C>, context: C): readonly T[] => {\n const resolved = typeof value === 'function' ? (value as ManyFactory<T, C>)(context) : value;\n return Array.isArray(resolved) ? resolved : [resolved as T];\n};\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-skill',\n providerOverrides: composeServerProviderOverrides(rootSession),\n injects: composeRootInjects({ session: [rootSession] }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): PoServerSessionPlugin['commands'] { return [...many(commandSkills, contextSession)]; },\n get resources(): PoServerSessionPlugin['resources'] { return [...many(resourceSkills, contextSession)]; },\n get tools(): PoServerSessionPlugin['tools'] { return [...many(toolLoadSkill, contextSession)]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";qBAgBa,wDAAY"}
|
|
@@ -31,7 +31,7 @@ function mountMcpSkills(groups, fs, signal) {
|
|
|
31
31
|
const domains = remote.selection.read().domains;
|
|
32
32
|
const skills = /* @__PURE__ */ new Map();
|
|
33
33
|
for (const group of groups) {
|
|
34
|
-
if (group.
|
|
34
|
+
if (group.domains !== void 0 && !group.domains.some((domain) => domains.includes(domain))) continue;
|
|
35
35
|
for (const skill of group.skills) {
|
|
36
36
|
if (skills.has(skill.name)) continue;
|
|
37
37
|
skills.set(skill.name, {
|
|
@@ -51,7 +51,9 @@ function mountMcpSkills(groups, fs, signal) {
|
|
|
51
51
|
} });
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/schemas/mcpSkillTools.ts
|
|
56
|
+
const searchSkillsParameters = Type.Object({ query: Type.Optional(Type.String()) });
|
|
55
57
|
const loadSkillParameters = Type.Object({ name: Type.String() });
|
|
56
58
|
//#endregion
|
|
57
59
|
//#region src/services/mcpSkillTools/index.ts
|
|
@@ -61,15 +63,37 @@ function text(text) {
|
|
|
61
63
|
text
|
|
62
64
|
}] };
|
|
63
65
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
function createSessionSearchSkillsTool(host) {
|
|
67
|
+
return {
|
|
68
|
+
name: "search_skills",
|
|
69
|
+
label: "Search skills",
|
|
70
|
+
description: "Search available session skills by name or description.",
|
|
71
|
+
parameters: searchSkillsParameters,
|
|
72
|
+
annotations: {
|
|
73
|
+
readOnlyHint: true,
|
|
74
|
+
destructiveHint: false,
|
|
75
|
+
openWorldHint: false
|
|
76
|
+
},
|
|
77
|
+
executionMode: "parallel",
|
|
78
|
+
async execute(_toolCallId, { query }) {
|
|
79
|
+
const needle = query?.trim().toLocaleLowerCase();
|
|
80
|
+
const available = host.listSkills().map(({ name, description }) => ({
|
|
81
|
+
name,
|
|
82
|
+
description
|
|
83
|
+
}));
|
|
84
|
+
const matches = needle === void 0 || needle === "" ? available : available.filter(({ name, description }) => `${name}\n${description}`.toLocaleLowerCase().includes(needle));
|
|
85
|
+
return {
|
|
86
|
+
...text(JSON.stringify(matches)),
|
|
87
|
+
structuredContent: { skills: matches }
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
68
92
|
function createSessionLoadSkillTool(host) {
|
|
69
93
|
return {
|
|
70
94
|
name: "load_skill",
|
|
71
95
|
label: "Load skill",
|
|
72
|
-
description: "Load the full instructions of
|
|
96
|
+
description: "Load the full instructions of an available session skill by exact name.",
|
|
73
97
|
parameters: loadSkillParameters,
|
|
74
98
|
executionMode: "parallel",
|
|
75
99
|
async execute(_toolCallId, { name }) {
|
|
@@ -289,7 +313,7 @@ function within(filePath, roots) {
|
|
|
289
313
|
* launch selection would freeze the command list at whatever was selected when
|
|
290
314
|
* the session host was built. Enumerating every domain up front and tagging
|
|
291
315
|
* each group with its `when` lets the existing kernel refresh do the work, the
|
|
292
|
-
* same way
|
|
316
|
+
* same way other host contributions follow selection changes.
|
|
293
317
|
*/
|
|
294
318
|
async function discoverServerSkills(execution, fs, signal) {
|
|
295
319
|
signal.throwIfAborted();
|
|
@@ -338,14 +362,12 @@ async function discoverServerSkills(execution, fs, signal) {
|
|
|
338
362
|
for (const domain of domains) if (await resolveSharedSkillsAsync(fs, execution.repoRoot, [domain], context, home)) sources.push({
|
|
339
363
|
domain,
|
|
340
364
|
paths: sharedPaths,
|
|
341
|
-
mcpPaths: sharedPaths
|
|
342
|
-
listed: true
|
|
365
|
+
mcpPaths: sharedPaths
|
|
343
366
|
});
|
|
344
367
|
}
|
|
345
368
|
sources.push({
|
|
346
369
|
paths: [repoSkills],
|
|
347
|
-
mcpPaths: [repoSkills]
|
|
348
|
-
listed: true
|
|
370
|
+
mcpPaths: [repoSkills]
|
|
349
371
|
});
|
|
350
372
|
signal.throwIfAborted();
|
|
351
373
|
const unique = [.../* @__PURE__ */ new Set([...sources.flatMap((source) => source.paths), ...state.skillDirectories ?? []])];
|
|
@@ -354,18 +376,17 @@ async function discoverServerSkills(execution, fs, signal) {
|
|
|
354
376
|
skillPaths: unique
|
|
355
377
|
}).ready();
|
|
356
378
|
signal.throwIfAborted();
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
})).filter((group) => group.skills.length > 0);
|
|
379
|
+
const merge = (key) => inventory.skills.flatMap((skill) => {
|
|
380
|
+
const admitting = sources.filter((source) => within(skill.filePath, source[key]));
|
|
381
|
+
if (admitting.length === 0) return [];
|
|
382
|
+
const domains = [...new Set(admitting.flatMap((source) => source.domain === void 0 ? [] : [source.domain]))];
|
|
383
|
+
return [{
|
|
384
|
+
...admitting.some((source) => source.domain === void 0) ? {} : { domains },
|
|
385
|
+
skills: [skill]
|
|
386
|
+
}];
|
|
387
|
+
});
|
|
388
|
+
const groups = merge("paths");
|
|
389
|
+
const mcpGroups = merge("mcpPaths");
|
|
369
390
|
const activeSkills = inventory.skills.filter((skill) => within(skill.filePath, [...state.skillDirectories ?? [], repoSkills]));
|
|
370
391
|
return {
|
|
371
392
|
inventory: {
|
|
@@ -374,7 +395,6 @@ async function discoverServerSkills(execution, fs, signal) {
|
|
|
374
395
|
},
|
|
375
396
|
groups,
|
|
376
397
|
mcpGroups,
|
|
377
|
-
listed,
|
|
378
398
|
catalog: formatSkillsForPrompt(activeSkills) || "(no discovered skills)"
|
|
379
399
|
};
|
|
380
400
|
}
|
|
@@ -388,9 +408,9 @@ async function discoverServerSkills(execution, fs, signal) {
|
|
|
388
408
|
* active, and looking it up in the union keeps one lookup table instead of one
|
|
389
409
|
* per domain.
|
|
390
410
|
*/
|
|
391
|
-
function skillCommand(skill,
|
|
411
|
+
function skillCommand(skill, domains, inventory) {
|
|
392
412
|
return {
|
|
393
|
-
...
|
|
413
|
+
...domains === void 0 ? {} : { when: { domains } },
|
|
394
414
|
name: `${SKILL_COMMAND_PREFIX}${skill.name}`,
|
|
395
415
|
description: skill.description,
|
|
396
416
|
execute: async (args, execution) => {
|
|
@@ -424,7 +444,7 @@ function createSkillCommands(inventory, catalog, groups) {
|
|
|
424
444
|
level: "info"
|
|
425
445
|
});
|
|
426
446
|
}
|
|
427
|
-
}, ...groups.flatMap((group) => group.skills.map((skill) => skillCommand(skill, group.
|
|
447
|
+
}, ...groups.flatMap((group) => group.skills.map((skill) => skillCommand(skill, group.domains, inventory)))];
|
|
428
448
|
}
|
|
429
449
|
//#endregion
|
|
430
450
|
//#region src/extensions/workspaces/sessions/(backend)/_lib/skillServer.ts
|
|
@@ -438,23 +458,17 @@ async function createSkillServer(agent, fs, signal, resources) {
|
|
|
438
458
|
...readHarnessState(execution.environment).skillDirectories ?? [],
|
|
439
459
|
Math.floor(Date.now() / SKILL_INVENTORY_FRESH_MS)
|
|
440
460
|
];
|
|
441
|
-
const { inventory, catalog, groups, mcpGroups
|
|
461
|
+
const { inventory, catalog, groups, mcpGroups } = await resources.acquire(key, () => discoverServerSkills(execution, fs, new AbortController().signal));
|
|
442
462
|
return {
|
|
443
463
|
commands: createSkillCommands(inventory, catalog, groups),
|
|
444
|
-
tools: [createSessionLoadSkillTool(agent)],
|
|
464
|
+
tools: [createSessionLoadSkillTool(agent), createSessionSearchSkillsTool(agent)],
|
|
445
465
|
services: [mountMcpSkills(mcpGroups, fs, signal)],
|
|
446
|
-
resources: [...
|
|
466
|
+
resources: [...groups.flatMap((group) => group.skills.map((skill) => ({
|
|
447
467
|
name: skill.name,
|
|
448
468
|
description: skill.description,
|
|
449
469
|
path: skill.filePath,
|
|
450
470
|
kind: "skill",
|
|
451
|
-
...group.
|
|
452
|
-
domain: group.domain,
|
|
453
|
-
attribution: {
|
|
454
|
-
kind: "domain",
|
|
455
|
-
mode: group.domain
|
|
456
|
-
}
|
|
457
|
-
} },
|
|
471
|
+
...group.domains === void 0 ? {} : { when: { domains: group.domains } },
|
|
458
472
|
read: async () => getOrThrow(await fs.readTextFile(skill.filePath, BACKGROUND_CONTEXT))
|
|
459
473
|
}))), ...[{
|
|
460
474
|
name: "po-author-skill",
|
|
@@ -497,21 +511,14 @@ var skills_server_default$1 = defineRoutedContribution((context) => context.root
|
|
|
497
511
|
var skills_server_default = defineRoutedContribution((context) => context.root.resources, { cardinality: "many" });
|
|
498
512
|
//#endregion
|
|
499
513
|
//#region src/extensions/workspaces/sessions/(backend)/tool/load_skill.server.ts
|
|
500
|
-
/** The session agent's skill
|
|
501
|
-
var load_skill_server_default = defineRoutedContribution((context) => context.root.tools
|
|
514
|
+
/** The session agent's skill tools; the Pi CLI keeps Pi's own skill handling. */
|
|
515
|
+
var load_skill_server_default = defineRoutedContribution((context) => context.root.tools ?? [], { cardinality: "many" });
|
|
502
516
|
//#endregion
|
|
503
517
|
//#region generated/server.ts
|
|
504
|
-
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
505
518
|
const many = (value, context) => {
|
|
506
519
|
const resolved = typeof value === "function" ? value(context) : value;
|
|
507
520
|
return Array.isArray(resolved) ? resolved : [resolved];
|
|
508
521
|
};
|
|
509
|
-
/* v8 ignore next -- generated normalization branch */
|
|
510
|
-
const defined = (values) => values.filter((value) => value !== void 0);
|
|
511
|
-
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
512
|
-
...identity,
|
|
513
|
-
...value
|
|
514
|
-
};
|
|
515
522
|
const serverPlugin = defineServerPlugin({
|
|
516
523
|
name: "@agimon-ai/po-skill",
|
|
517
524
|
providerOverrides: composeServerProviderOverrides(root_server_default),
|
|
@@ -531,7 +538,7 @@ const serverPlugin = defineServerPlugin({
|
|
|
531
538
|
return [...many(skills_server_default, contextSession)];
|
|
532
539
|
},
|
|
533
540
|
get tools() {
|
|
534
|
-
return
|
|
541
|
+
return [...many(load_skill_server_default, contextSession)];
|
|
535
542
|
},
|
|
536
543
|
services: [...scopeSession.services ?? []],
|
|
537
544
|
get activities() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.mjs","names":["rootSession","commandSkills","resourceSkills","toolLoadSkill"],"sources":["../../src/constants/skillInventory.ts","../../src/services/mcpSkills/index.ts","../../src/schemas/mcpSkillTools.ts","../../src/services/mcpSkillTools/index.ts","../../src/services/deferredSkills/index.ts","../../src/services/serverInventory/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillCommands.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillServer.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/skills.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/skills.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/load_skill.server.ts","../../generated/server.ts"],"sourcesContent":["/**\n * How long sessions with the same inputs share one skill inventory instead of each reading the disk.\n * ponytail: a skill added on disk reaches new sessions within this window, not at once; watch the\n * skill roots if that lag matters.\n */\nexport const SKILL_INVENTORY_FRESH_MS = 60_000;\n","import type { PoMcpPluginContext, PoMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { BACKGROUND_CONTEXT, getOrThrow, withAbortSignal, type FileSystem } from '@agimon-ai/po-core/platform';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'po/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: PoMcpPluginContext): readonly PoMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], fs: FileSystem, signal: AbortSignal) {\n return (context: Context): void => {\n context.provide(MCP_SKILLS_SERVICE, {\n list(remote) {\n signal.throwIfAborted();\n remote.signal.throwIfAborted();\n const domains = remote.selection.read().domains;\n const skills = new Map<string, PoMcpSkill>();\n for (const group of groups) {\n if (group.domain !== undefined && !domains.includes(group.domain)) continue;\n for (const skill of group.skills) {\n // Match command precedence, including shared copies across domains.\n if (skills.has(skill.name)) continue;\n skills.set(skill.name, {\n name: skill.name,\n description: skill.description,\n async read() {\n const readSignal = AbortSignal.any([signal, remote.signal]);\n readSignal.throwIfAborted();\n const text = getOrThrow(\n await fs.readTextFile(skill.filePath, withAbortSignal(readSignal, BACKGROUND_CONTEXT)),\n );\n readSignal.throwIfAborted();\n return text;\n },\n });\n }\n }\n return [...skills.values()];\n },\n } satisfies McpSkillProvider);\n };\n}\n\nexport function bindMcpSkills(context: PoMcpPluginContext): readonly PoMcpSkill[] {\n const provider = context.services.get<McpSkillProvider>(MCP_SKILLS_SERVICE);\n if (!provider) throw new Error('Discovered MCP skills are unavailable.');\n return provider.list(context);\n}\n","import { Type } from 'typebox';\n\nexport const searchSkillsParameters = Type.Object({ query: Type.Optional(Type.String()) });\nexport const loadSkillParameters = Type.Object({ name: Type.String() });\n","import type { PoHeadlessHostService, PoHeadlessTool } from '@agimon-ai/po-core/headless';\n\nimport { loadSkillParameters, searchSkillsParameters } from '../../schemas/mcpSkillTools';\n\nfunction text(text: string) {\n return { content: [{ type: 'text' as const, text }] };\n}\n\nfunction skills(context: Parameters<PoHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): PoHeadlessTool<typeof searchSkillsParameters> {\n return {\n // web-plugin-tool-renderers: ignore search_skills (remote MCP only)\n name: 'search_skills',\n label: 'Search skills',\n description: 'Discover repository and active-domain skills available to this remote connection.',\n parameters: searchSkillsParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { query }, _signal, _onUpdate, context) {\n const needle = query?.trim().toLocaleLowerCase();\n const available = await skills(context).list();\n const matches =\n needle === undefined || needle === ''\n ? available\n : available.filter(({ name, description }) => `${name}\\n${description}`.toLocaleLowerCase().includes(needle));\n return { ...text(JSON.stringify(matches)), structuredContent: { skills: matches } };\n },\n };\n}\n\n/**\n * The session agent's way into a skill. Its skills are listed in the prompt, which Po\n * controls, so it needs no search; a remote agent's prompt is not ours, hence the pair above.\n */\nexport function createSessionLoadSkillTool(\n host: Pick<PoHeadlessHostService, 'readSkill'>,\n): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (server session only, shown as a generic call)\n // PO_LOAD_SKILL_TOOL, spelled out so the renderer rule can read it.\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full instructions of a skill from the available skills list by exact name.',\n parameters: loadSkillParameters,\n executionMode: 'parallel',\n async execute(_toolCallId, { name }) {\n const body = host.readSkill(name);\n if (body === undefined) return { ...text(`No skill named ${name} is available in this session.`), isError: true };\n return text(body);\n },\n };\n}\n\nexport function createLoadSkillTool(): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (remote MCP only)\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full Markdown instructions for an available remote skill by exact name.',\n parameters: loadSkillParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { name }, _signal, _onUpdate, context) {\n return text(await skills(context).read(name));\n },\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { Worker } from 'node:worker_threads';\n\nimport {\n type BuildSystemPromptOptions,\n formatSkillsForPrompt,\n getAgentDir,\n loadSkills,\n type Skill,\n stripFrontmatter,\n} from '@earendil-works/pi-coding-agent';\n\nimport type { DeferredSkillLoaderOptions, DeferredSkillSnapshot } from '../../types/skills';\n\nexport type { DeferredSkillLoaderOptions, DeferredSkillSnapshot } from '../../types/skills';\n\nconst PI_PACKAGE = '@earendil-works/pi-coding-agent';\nconst PACKAGE_MANIFEST = 'package.json';\n// A fresh worker takes ~240 ms to import Pi's main entry but ~20 ms for this module alone, and\n// every session spawns one. ponytail: not in Pi's exports map, so a Pi release that moves it\n// falls back to the main entry (slow, still correct); deferredSkills.test.ts pins the path.\nconst PI_SKILLS_MODULE = path.join('dist', 'core', 'skills.js');\nconst SKILL_WORKER_SOURCE = `\nconst { parentPort, workerData } = require('node:worker_threads');\nvoid (async () => {\n try {\n const { loadSkills } = await import(workerData.piModuleUrl);\n parentPort.postMessage({ ok: true, result: loadSkills(workerData.loadOptions) });\n } catch (error) {\n parentPort.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) });\n }\n})();\n`;\n\nfunction installedPiModuleUrl(): string {\n const anchors = [process.argv[1], fileURLToPath(import.meta.url), path.join(process.cwd(), PACKAGE_MANIFEST)];\n for (const anchor of anchors) {\n if (!anchor) continue;\n let directory = path.dirname(path.resolve(anchor));\n while (true) {\n const packageRoot = path.join(directory, 'node_modules', PI_PACKAGE);\n const manifestPath = path.join(packageRoot, PACKAGE_MANIFEST);\n try {\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as { main?: unknown };\n if (typeof manifest.main === 'string') {\n const entry = path.resolve(packageRoot, manifest.main);\n const skills = path.join(packageRoot, PI_SKILLS_MODULE);\n if (fs.existsSync(skills)) return pathToFileURL(skills).href;\n if (fs.existsSync(entry)) return pathToFileURL(entry).href;\n }\n } catch {\n // This anchor does not expose Pi at this level; continue up its module chain.\n }\n const parent = path.dirname(directory);\n if (parent === directory) break;\n directory = parent;\n }\n }\n throw new Error(`Cannot resolve ${PI_PACKAGE} from the Po or host module trees`);\n}\n\ninterface SkillWorkerResult {\n ok: boolean;\n result?: { skills: Skill[]; diagnostics: Array<{ path?: string; message: string }> };\n error?: string;\n}\n\n/**\n * One session's Po-selected skill inventory.\n *\n * Construction is deliberately cheap. `start()` schedules the synchronous Pi\n * walker after session_start returns, while `ready()` is the single promise the\n * first input awaits before Pi expands `/skill:name`.\n */\nexport class DeferredSkillLoader {\n readonly #options: DeferredSkillLoaderOptions;\n #promise: Promise<DeferredSkillSnapshot> | undefined;\n\n constructor(options: DeferredSkillLoaderOptions) {\n this.#options = options;\n }\n\n start(): Promise<DeferredSkillSnapshot> {\n if (this.#promise) return this.#promise;\n\n if (!this.#options.schedule && !this.#options.load) {\n this.#promise = this.#loadInWorker();\n return this.#promise;\n }\n\n const schedule = this.#options.schedule ?? ((load: () => void) => setImmediate(load));\n this.#promise = new Promise((resolve) => {\n const fail = (error: unknown): void => {\n resolve(this.#failure(error));\n };\n try {\n schedule(() => {\n try {\n resolve(this.#load());\n } catch (error) {\n fail(error);\n }\n });\n } catch (error) {\n fail(error);\n }\n });\n return this.#promise;\n }\n\n ready(): Promise<DeferredSkillSnapshot> {\n return this.start();\n }\n\n #loadOptions(): { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean } {\n return {\n cwd: this.#options.cwd,\n agentDir: this.#options.agentDir ?? getAgentDir(),\n skillPaths: [...this.#options.skillPaths],\n includeDefaults: false,\n };\n }\n\n #snapshot(result: {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n }): DeferredSkillSnapshot {\n return {\n skills: result.skills,\n diagnostics: result.diagnostics.map(\n (diagnostic) => `${diagnostic.path ?? this.#options.cwd}: ${diagnostic.message}`,\n ),\n };\n }\n\n #failure(error: unknown): DeferredSkillSnapshot {\n return {\n skills: [],\n diagnostics: [`${this.#options.cwd}: ${error instanceof Error ? error.message : String(error)}`],\n };\n }\n\n #load(): DeferredSkillSnapshot {\n return this.#snapshot((this.#options.load ?? loadSkills)(this.#loadOptions()));\n }\n\n #loadInWorker(): Promise<DeferredSkillSnapshot> {\n return new Promise((resolve) => {\n let settled = false;\n const settle = (snapshot: DeferredSkillSnapshot): void => {\n if (settled) return;\n settled = true;\n resolve(snapshot);\n };\n let worker: Worker;\n try {\n worker = new Worker(SKILL_WORKER_SOURCE, {\n eval: true,\n workerData: {\n piModuleUrl: this.#options.piModuleUrl ?? installedPiModuleUrl(),\n loadOptions: this.#loadOptions(),\n },\n });\n } catch (error) {\n settle(this.#failure(error));\n return;\n }\n worker.unref();\n worker.once('message', (message: SkillWorkerResult) => {\n if (!message.ok || !message.result) {\n settle(this.#failure(message.error ?? 'Skill worker failed'));\n return;\n }\n settle(this.#snapshot(message.result));\n });\n worker.once('error', (error) => settle(this.#failure(error)));\n worker.once('exit', (code) => {\n if (code !== 0) settle(this.#failure(`Skill worker exited with code ${code}`));\n });\n });\n }\n}\n\n/** Appends only the inventory Pi would append for these deferred skills. */\nexport function buildPromptWithDeferredSkills(\n systemPrompt: string,\n options: BuildSystemPromptOptions,\n skills: Skill[],\n): string {\n if (options.selectedTools && !options.selectedTools.includes('read')) return systemPrompt;\n return `${systemPrompt}${formatSkillsForPrompt(skills)}`;\n}\n\n/** Expands a deferred `/skill:name` before Pi consults its synchronous inventory. */\nexport function expandDeferredSkillCommand(text: string, skills: readonly Skill[]): string {\n if (!text.startsWith('/skill:')) return text;\n const spaceIndex = text.indexOf(' ');\n const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);\n const skill = skills.find((candidate) => candidate.name === skillName);\n if (!skill) return text;\n\n try {\n const args = spaceIndex === -1 ? '' : text.slice(spaceIndex + 1).trim();\n const body = stripFrontmatter(fs.readFileSync(skill.filePath, 'utf8')).trim();\n const skillBlock = `<skill name=\"${skill.name}\" location=\"${skill.filePath}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n return args ? `${skillBlock}\\n\\n${args}` : skillBlock;\n } catch {\n // Pi uses the same literal-text fallback if a skill disappears between\n // discovery and submission; the agent can still respond to the command.\n return text;\n }\n}\n","import path from 'node:path';\n\nimport {\n listDomainNamesAsync,\n resolvePluginEntriesAsync,\n resolveSharedSkillsAsync,\n type PluginEntry,\n} from '@agimon-ai/po-config/domains';\nimport { readHarnessState } from '@agimon-ai/po-config/harnessState';\nimport type { PoHeadlessExecutionContext } from '@agimon-ai/po-core/headless';\nimport { BACKGROUND_CONTEXT, nodeHost, withAbortSignal, type FileSystem } from '@agimon-ai/po-core/platform';\nimport { materializePluginEntries } from '@agimon-ai/po-domain/plugins';\nimport { collectResources, discoverSkillsAsync } from '@agimon-ai/po-domain/resources';\nimport { formatSkillsForPrompt } from '@earendil-works/pi-coding-agent';\n\nimport type { ServerSkillInventory } from '../../types/skills';\nimport { DeferredSkillLoader } from '../deferredSkills';\n\nexport type { ServerSkillGroup, ServerSkillInventory } from '../../types/skills';\n\ninterface SkillSource {\n readonly domain?: string;\n readonly paths: readonly string[];\n readonly mcpPaths: readonly string[];\n /** Advertised by this package rather than by po-domain. */\n readonly listed?: boolean;\n}\n\nconst LOCAL_ONLY_MCP_PACKAGES = new Set([\n '@agimon-ai/po-author',\n '@agimon-ai/po-git',\n '@agimon-ai/po-goal',\n '@agimon-ai/po-help',\n '@agimon-ai/po-loop',\n '@agimon-ai/po-user-feedback',\n '@agimon-ai/po-voice',\n '@agimon-ai/po-workflow',\n]);\n\ntype IoContext = Parameters<FileSystem['readTextFile']>[1];\n\nasync function isMcpSkillPlugin(fs: FileSystem, entry: PluginEntry, context: IoContext): Promise<boolean> {\n if (entry.source?.type === 'npm') return !LOCAL_ONLY_MCP_PACKAGES.has(entry.source.package);\n // A package manifest is optional for catalog plugins, but a present identity\n // that cannot be read or parsed must never make the plugin remotely eligible.\n const source = await fs.readTextFile(path.join(entry.directory, 'package.json'), context);\n if (!source.ok) return source.error.code === 'not_found';\n try {\n const manifest = JSON.parse(source.value) as unknown;\n if (typeof manifest !== 'object' || manifest === null || Array.isArray(manifest)) return false;\n const name = (manifest as { name?: unknown }).name;\n return typeof name === 'string' && name.length > 0 && name.trim() === name && !LOCAL_ONLY_MCP_PACKAGES.has(name);\n } catch {\n return false;\n }\n}\n\n/**\n * Whether a discovered skill came from one of these paths.\n *\n * A source holds exact SKILL.md files for plugin and shared skills, but a bare\n * directory for `.po/skills`, so membership is containment rather than\n * equality.\n */\nfunction within(filePath: string, roots: readonly string[]): boolean {\n const resolved = path.resolve(filePath);\n return roots.some((root) => {\n const base = path.resolve(root);\n return resolved === base || resolved.startsWith(`${base}${path.sep}`);\n });\n}\n/**\n * Every skill the session could reach, grouped by what activates it.\n *\n * A cockpit `/domains` switch reconciles contributions rather than rebuilding\n * the server plugin: the session declaration is mounted once. So enumerating only the\n * launch selection would freeze the command list at whatever was selected when\n * the session host was built. Enumerating every domain up front and tagging\n * each group with its `when` lets the existing kernel refresh do the work, the\n * same way domainServerResources registers per-domain skill resources.\n */\nexport async function discoverServerSkills(\n execution: PoHeadlessExecutionContext,\n fs: FileSystem,\n signal: AbortSignal,\n): Promise<ServerSkillInventory> {\n signal.throwIfAborted();\n const context = withAbortSignal(signal, BACKGROUND_CONTEXT);\n const state = readHarnessState(execution.environment);\n const home = execution.environment.HOME ?? nodeHost.homeDirectory();\n const repoSkills = path.join(execution.repoRoot, '.po', 'skills');\n const sources: SkillSource[] = [];\n const diagnostics: string[] = [];\n // A malformed domain is a configuration error in one domain, so it must not\n // remove every other domain's skills. Enumerating the whole manifest is new\n // exposure here: reading the launch selection could not fail this way.\n const domains: string[] = [];\n try {\n domains.push(...(await listDomainNamesAsync(fs, execution.repoRoot, context, home)));\n } catch (error) {\n diagnostics.push(`Domain skills are unavailable: ${error instanceof Error ? error.message : String(error)}`);\n }\n\n // Domain-scoped plugin skills come first. The host keeps the first command\n // registered for a name, so this ordering reproduces the collector's rule\n // that a selected plugin outranks a shared fallback of the same name.\n for (const domain of domains) {\n signal.throwIfAborted();\n try {\n const plugins = await materializePluginEntries(\n await resolvePluginEntriesAsync(fs, execution.repoRoot, [domain], [], context, home),\n );\n const mcpPluginRoots = (\n await Promise.all(\n plugins.map(async (plugin) =>\n (await isMcpSkillPlugin(fs, plugin, context)) ? path.join(plugin.directory, 'skills') : undefined,\n ),\n )\n ).filter((root): root is string => root !== undefined);\n const collected = await collectResources(execution.repoRoot, plugins, {\n agents: false,\n mcp: false,\n sharedSkills: false,\n });\n try {\n if (collected.skillDirectories.length > 0) {\n const paths = [...collected.skillDirectories];\n sources.push({\n domain,\n paths,\n // Exact collected paths retain this domain's per-plugin skill subset.\n // A plugin root would also admit skills loaded for another domain.\n mcpPaths: paths.filter((skillPath) => within(skillPath, mcpPluginRoots)),\n });\n }\n } finally {\n await collected.cleanup();\n }\n } catch (error) {\n diagnostics.push(`Domain '${domain}': ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n // Shared .claude/skills are not owned by any one domain, and a domain can opt\n // out of them. `when` cannot express \"unless every selected domain opts out\",\n // but registering a copy per domain that keeps them says the same thing: the\n // group is active if any such domain is selected, which is exactly what\n // resolveSharedSkills computes.\n signal.throwIfAborted();\n const shared = await discoverSkillsAsync(execution.repoRoot, path.join(execution.repoRoot, '.claude', 'skills'));\n if (shared.length > 0) {\n const sharedPaths = shared.map((skill) => skill.path);\n for (const domain of domains) {\n if (await resolveSharedSkillsAsync(fs, execution.repoRoot, [domain], context, home)) {\n sources.push({ domain, paths: sharedPaths, mcpPaths: sharedPaths, listed: true });\n }\n }\n }\n\n // Repository-local skills belong to the repository, not to a selection.\n sources.push({ paths: [repoSkills], mcpPaths: [repoSkills], listed: true });\n\n signal.throwIfAborted();\n // One walk for every group. Pi drops a path it has already loaded, so the\n // loader gets the unique set and each group selects its own skills back out.\n const unique = [...new Set([...sources.flatMap((source) => source.paths), ...(state.skillDirectories ?? [])])];\n const inventory = await new DeferredSkillLoader({ cwd: execution.cwd, skillPaths: unique }).ready();\n signal.throwIfAborted();\n\n const groups = sources\n .map((source) => ({\n ...(source.domain === undefined ? {} : { domain: source.domain }),\n skills: inventory.skills.filter((skill) => within(skill.filePath, source.paths)),\n }))\n .filter((group) => group.skills.length > 0);\n const listed = sources\n .filter((source) => source.listed === true)\n .map((source) => ({\n ...(source.domain === undefined ? {} : { domain: source.domain }),\n skills: inventory.skills.filter((skill) => within(skill.filePath, source.paths)),\n }))\n .filter((group) => group.skills.length > 0);\n const mcpGroups = sources\n .map((source) => ({\n ...(source.domain === undefined ? {} : { domain: source.domain }),\n skills: inventory.skills.filter((skill) => within(skill.filePath, source.mcpPaths)),\n }))\n .filter((group) => group.skills.length > 0);\n\n // The catalog still describes the active selection rather than everything on\n // disk, because it is prompt text rather than a menu.\n const activeSkills = inventory.skills.filter((skill) =>\n within(skill.filePath, [...(state.skillDirectories ?? []), repoSkills]),\n );\n return {\n inventory: { skills: inventory.skills, diagnostics: [...inventory.diagnostics, ...diagnostics] },\n groups,\n mcpGroups,\n listed,\n catalog: formatSkillsForPrompt(activeSkills) || '(no discovered skills)',\n };\n}\n","import type { PoHeadlessCommand } from '@agimon-ai/po-core/headless';\nimport type { Skill } from '@earendil-works/pi-coding-agent';\n\nimport { expandDeferredSkillCommand, type DeferredSkillSnapshot } from '../../../../../services/deferredSkills';\nimport type { ServerSkillGroup } from '../../../../../services/serverInventory';\nimport { SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX, SKILLS_COMMAND } from '../../../../../types/skills';\n\n/**\n * One command per skill, gated by whatever activates that skill.\n *\n * Expansion always reads the full inventory, not the group: a command is only\n * dispatchable while its condition holds, so by the time one runs its skill is\n * active, and looking it up in the union keeps one lookup table instead of one\n * per domain.\n */\nfunction skillCommand(skill: Skill, domain: string | undefined, inventory: DeferredSkillSnapshot): PoHeadlessCommand {\n return {\n ...(domain === undefined ? {} : { when: { domain } }),\n name: `${SKILL_COMMAND_PREFIX}${skill.name}`,\n description: skill.description,\n execute: async (args, execution) => {\n const text = `${SKILL_INVOCATION_PREFIX}${skill.name}${args ? ` ${args}` : ''}`;\n const expanded = expandDeferredSkillCommand(text, inventory.skills);\n if (expanded === text) throw new Error(`Skill '${skill.name}' is no longer readable.`);\n await admit(execution.session, expanded);\n },\n };\n}\n\n/**\n * The dispatching prompt acks when this command returns, so awaiting the whole\n * turn (session.prompt) kept the composer draft until the run ended.\n */\nfunction admit(session: Parameters<PoHeadlessCommand['execute']>[1]['session'], text: string): Promise<void> {\n return session.admitPrompt ? session.admitPrompt(text, 'prompt', 'operator') : session.prompt(text);\n}\nexport function createSkillCommands(\n inventory: DeferredSkillSnapshot,\n catalog: string,\n groups: readonly ServerSkillGroup[],\n): readonly PoHeadlessCommand[] {\n return [\n {\n name: SKILLS_COMMAND,\n description: 'List discovered skills or invoke one by name.',\n async execute(args, execution) {\n const requested = args.trim();\n if (requested) {\n await admit(\n execution.session,\n expandDeferredSkillCommand(`${SKILL_INVOCATION_PREFIX}${requested}`, inventory.skills),\n );\n return;\n }\n await execution.client.notify({\n title: 'Po skills',\n body: catalog,\n level: 'info',\n });\n },\n },\n ...groups.flatMap((group) => group.skills.map((skill) => skillCommand(skill, group.domain, inventory))),\n ];\n}\n","import { readHarnessState } from '@agimon-ai/po-config/harnessState';\nimport type { PoHeadlessHostService } from '@agimon-ai/po-core/headless';\nimport { BACKGROUND_CONTEXT, getOrThrow, nodeHost, type FileSystem } from '@agimon-ai/po-core/platform';\nimport {\n type PoServerResources,\n type PoServerSessionPlugin,\n packageResourcePath,\n readPackageResource,\n} from '@agimon-ai/po-core/serverPlugin';\n\nimport { SKILL_INVENTORY_FRESH_MS } from '../../../../../constants/skillInventory';\nimport { mountMcpSkills } from '../../../../../services/mcpSkills';\nimport { createSessionLoadSkillTool } from '../../../../../services/mcpSkillTools';\nimport { discoverServerSkills } from '../../../../../services/serverInventory';\nimport { createSkillCommands } from './skillCommands';\n\nexport async function createSkillServer(\n agent: PoHeadlessHostService,\n fs: FileSystem,\n signal: AbortSignal,\n resources: PoServerResources,\n): Promise<PoServerSessionPlugin> {\n const execution = agent.context;\n // Sessions with the same inputs share one discovery. The key holds every input discoverServerSkills\n // reads besides the disk, so equal keys give equal inventories, and the window bounds how old that\n // read of the disk can be.\n const key = [\n 'inventory',\n execution.repoRoot,\n execution.cwd,\n execution.environment.HOME ?? nodeHost.homeDirectory(),\n ...(readHarnessState(execution.environment).skillDirectories ?? []),\n Math.floor(Date.now() / SKILL_INVENTORY_FRESH_MS),\n ];\n const { inventory, catalog, groups, mcpGroups, listed } = await resources.acquire(key, () =>\n // Every session sharing the discovery waits on it, so none of their signals may abort it.\n discoverServerSkills(execution, fs, new AbortController().signal),\n );\n return {\n commands: createSkillCommands(inventory, catalog, groups),\n tools: [createSessionLoadSkillTool(agent)],\n services: [mountMcpSkills(mcpGroups, fs, signal)],\n resources: [\n // Each skill joins the prompt's one <available_skills> list, gated like the rest.\n ...listed.flatMap((group) =>\n group.skills.map((skill) => ({\n name: skill.name,\n description: skill.description,\n path: skill.filePath,\n kind: 'skill' as const,\n ...(group.domain === undefined\n ? {}\n : { when: { domain: group.domain, attribution: { kind: 'domain' as const, mode: group.domain } } }),\n read: async () => getOrThrow(await fs.readTextFile(skill.filePath, BACKGROUND_CONTEXT)),\n })),\n ),\n ...[\n {\n name: 'po-author-skill',\n description:\n 'Author and distribute Po agent skills. Use when creating a SKILL.md, contributing a runtime skill directory, or publishing activation-gated Help prompts and diagnostic tools.',\n },\n {\n name: 'po-use-skill',\n description:\n \"Use Po's skill catalog and deferred discovery. Diagnose missing or shadowed skills and invoke the narrowest available guidance.\",\n },\n ].map((skill) => ({\n ...skill,\n when: { state: { 'minor-mode': 'help' }, attribution: { kind: 'minor' as const, mode: 'help' } },\n kind: 'skill' as const,\n path: packageResourcePath(import.meta.url, `src/prompts/${skill.name}/SKILL.md`),\n read: () => readPackageResource(import.meta.url, `src/prompts/${skill.name}/SKILL.md`),\n })),\n ],\n };\n}\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport { requirePoFs } from '@agimon-ai/po-core/platform';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nimport { createSkillServer } from './_lib/skillServer';\n\nexport default defineRoot(async ({ agent, context: root, signal, resources }: PoServerPluginContext) => {\n const runtime: PoServerSessionPlugin = agent\n ? await createSkillServer(agent, requirePoFs(root), signal, resources)\n : { commands: [], resources: [] };\n return { value: runtime, services: runtime.services };\n});\n","import { defineRoutedContribution } from '@agimon-ai/po-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, PoServerSessionPlugin>) => context.root.commands!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution } from '@agimon-ai/po-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, PoServerSessionPlugin>) => context.root.resources!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\n/** The session agent's skill loader; the Pi CLI keeps Pi's own skill handling. */\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, PoServerSessionPlugin>) => context.root.tools?.[0],\n { cardinality: 'optional' },\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport commandSkills from '../src/extensions/workspaces/sessions/(backend)/command/skills.server';\nimport resourceSkills from '../src/extensions/workspaces/sessions/(backend)/resource/skills.server';\nimport toolLoadSkill from '../src/extensions/workspaces/sessions/(backend)/tool/load_skill.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\ntype ManyFactory<T, C> = (context: C) => T | readonly T[];\nconst many = <T, C>(value: T | readonly T[] | ManyFactory<T, C>, context: C): readonly T[] => {\n const resolved = typeof value === 'function' ? (value as ManyFactory<T, C>)(context) : value;\n return Array.isArray(resolved) ? resolved : [resolved as T];\n};\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-skill',\n providerOverrides: composeServerProviderOverrides(rootSession),\n injects: composeRootInjects({ session: [rootSession] }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): PoServerSessionPlugin['commands'] { return [...many(commandSkills, contextSession)]; },\n get resources(): PoServerSessionPlugin['resources'] { return [...many(resourceSkills, contextSession)]; },\n get tools(): PoServerSessionPlugin['tools'] { return defined([via({ name: 'load_skill' }, at(toolLoadSkill, contextSession))]); },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.mjs","names":["rootSession","commandSkills","resourceSkills","toolLoadSkill"],"sources":["../../src/constants/skillInventory.ts","../../src/services/mcpSkills/index.ts","../../src/schemas/mcpSkillTools.ts","../../src/services/mcpSkillTools/index.ts","../../src/services/deferredSkills/index.ts","../../src/services/serverInventory/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillCommands.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillServer.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/skills.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/skills.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/load_skill.server.ts","../../generated/server.ts"],"sourcesContent":["/**\n * How long sessions with the same inputs share one skill inventory instead of each reading the disk.\n * ponytail: a skill added on disk reaches new sessions within this window, not at once; watch the\n * skill roots if that lag matters.\n */\nexport const SKILL_INVENTORY_FRESH_MS = 60_000;\n","import type { PoMcpPluginContext, PoMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { BACKGROUND_CONTEXT, getOrThrow, withAbortSignal, type FileSystem } from '@agimon-ai/po-core/platform';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'po/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: PoMcpPluginContext): readonly PoMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], fs: FileSystem, signal: AbortSignal) {\n return (context: Context): void => {\n context.provide(MCP_SKILLS_SERVICE, {\n list(remote) {\n signal.throwIfAborted();\n remote.signal.throwIfAborted();\n const domains = remote.selection.read().domains;\n const skills = new Map<string, PoMcpSkill>();\n for (const group of groups) {\n if (group.domains !== undefined && !group.domains.some((domain) => domains.includes(domain))) continue;\n for (const skill of group.skills) {\n // Retain name precedence defensively for remote providers.\n if (skills.has(skill.name)) continue;\n skills.set(skill.name, {\n name: skill.name,\n description: skill.description,\n async read() {\n const readSignal = AbortSignal.any([signal, remote.signal]);\n readSignal.throwIfAborted();\n const text = getOrThrow(\n await fs.readTextFile(skill.filePath, withAbortSignal(readSignal, BACKGROUND_CONTEXT)),\n );\n readSignal.throwIfAborted();\n return text;\n },\n });\n }\n }\n return [...skills.values()];\n },\n } satisfies McpSkillProvider);\n };\n}\n\nexport function bindMcpSkills(context: PoMcpPluginContext): readonly PoMcpSkill[] {\n const provider = context.services.get<McpSkillProvider>(MCP_SKILLS_SERVICE);\n if (!provider) throw new Error('Discovered MCP skills are unavailable.');\n return provider.list(context);\n}\n","import { Type } from 'typebox';\n\nexport const searchSkillsParameters = Type.Object({ query: Type.Optional(Type.String()) });\nexport const loadSkillParameters = Type.Object({ name: Type.String() });\n","import type { PoHeadlessHostService, PoHeadlessTool } from '@agimon-ai/po-core/headless';\n\nimport { loadSkillParameters, searchSkillsParameters } from '../../schemas/mcpSkillTools';\n\nfunction text(text: string) {\n return { content: [{ type: 'text' as const, text }] };\n}\n\nfunction skills(context: Parameters<PoHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): PoHeadlessTool<typeof searchSkillsParameters> {\n return {\n // web-plugin-tool-renderers: ignore search_skills (remote MCP only)\n name: 'search_skills',\n label: 'Search skills',\n description: 'Discover repository and active-domain skills available to this remote connection.',\n parameters: searchSkillsParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { query }, _signal, _onUpdate, context) {\n const needle = query?.trim().toLocaleLowerCase();\n const available = await skills(context).list();\n const matches =\n needle === undefined || needle === ''\n ? available\n : available.filter(({ name, description }) => `${name}\\n${description}`.toLocaleLowerCase().includes(needle));\n return { ...text(JSON.stringify(matches)), structuredContent: { skills: matches } };\n },\n };\n}\n\nexport function createSessionSearchSkillsTool(\n host: Pick<PoHeadlessHostService, 'listSkills'>,\n): PoHeadlessTool<typeof searchSkillsParameters> {\n return {\n // web-plugin-tool-renderers: ignore search_skills (server session only, shown as a generic call)\n name: 'search_skills',\n label: 'Search skills',\n description: 'Search available session skills by name or description.',\n parameters: searchSkillsParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n executionMode: 'parallel',\n async execute(_toolCallId, { query }) {\n const needle = query?.trim().toLocaleLowerCase();\n const available = host.listSkills().map(({ name, description }) => ({ name, description }));\n const matches =\n needle === undefined || needle === ''\n ? available\n : available.filter(({ name, description }) => `${name}\\n${description}`.toLocaleLowerCase().includes(needle));\n return { ...text(JSON.stringify(matches)), structuredContent: { skills: matches } };\n },\n };\n}\n\nexport function createSessionLoadSkillTool(\n host: Pick<PoHeadlessHostService, 'readSkill'>,\n): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (server session only, shown as a generic call)\n // PO_LOAD_SKILL_TOOL, spelled out so the renderer rule can read it.\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full instructions of an available session skill by exact name.',\n parameters: loadSkillParameters,\n executionMode: 'parallel',\n async execute(_toolCallId, { name }) {\n const body = host.readSkill(name);\n if (body === undefined) return { ...text(`No skill named ${name} is available in this session.`), isError: true };\n return text(body);\n },\n };\n}\n\nexport function createLoadSkillTool(): PoHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (remote MCP only)\n name: 'load_skill',\n label: 'Load skill',\n description: 'Load the full Markdown instructions for an available remote skill by exact name.',\n parameters: loadSkillParameters,\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n async execute(_toolCallId, { name }, _signal, _onUpdate, context) {\n return text(await skills(context).read(name));\n },\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { Worker } from 'node:worker_threads';\n\nimport {\n type BuildSystemPromptOptions,\n formatSkillsForPrompt,\n getAgentDir,\n loadSkills,\n type Skill,\n stripFrontmatter,\n} from '@earendil-works/pi-coding-agent';\n\nimport type { DeferredSkillLoaderOptions, DeferredSkillSnapshot } from '../../types/skills';\n\nexport type { DeferredSkillLoaderOptions, DeferredSkillSnapshot } from '../../types/skills';\n\nconst PI_PACKAGE = '@earendil-works/pi-coding-agent';\nconst PACKAGE_MANIFEST = 'package.json';\n// A fresh worker takes ~240 ms to import Pi's main entry but ~20 ms for this module alone, and\n// every session spawns one. ponytail: not in Pi's exports map, so a Pi release that moves it\n// falls back to the main entry (slow, still correct); deferredSkills.test.ts pins the path.\nconst PI_SKILLS_MODULE = path.join('dist', 'core', 'skills.js');\nconst SKILL_WORKER_SOURCE = `\nconst { parentPort, workerData } = require('node:worker_threads');\nvoid (async () => {\n try {\n const { loadSkills } = await import(workerData.piModuleUrl);\n parentPort.postMessage({ ok: true, result: loadSkills(workerData.loadOptions) });\n } catch (error) {\n parentPort.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) });\n }\n})();\n`;\n\nfunction installedPiModuleUrl(): string {\n const anchors = [process.argv[1], fileURLToPath(import.meta.url), path.join(process.cwd(), PACKAGE_MANIFEST)];\n for (const anchor of anchors) {\n if (!anchor) continue;\n let directory = path.dirname(path.resolve(anchor));\n while (true) {\n const packageRoot = path.join(directory, 'node_modules', PI_PACKAGE);\n const manifestPath = path.join(packageRoot, PACKAGE_MANIFEST);\n try {\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as { main?: unknown };\n if (typeof manifest.main === 'string') {\n const entry = path.resolve(packageRoot, manifest.main);\n const skills = path.join(packageRoot, PI_SKILLS_MODULE);\n if (fs.existsSync(skills)) return pathToFileURL(skills).href;\n if (fs.existsSync(entry)) return pathToFileURL(entry).href;\n }\n } catch {\n // This anchor does not expose Pi at this level; continue up its module chain.\n }\n const parent = path.dirname(directory);\n if (parent === directory) break;\n directory = parent;\n }\n }\n throw new Error(`Cannot resolve ${PI_PACKAGE} from the Po or host module trees`);\n}\n\ninterface SkillWorkerResult {\n ok: boolean;\n result?: { skills: Skill[]; diagnostics: Array<{ path?: string; message: string }> };\n error?: string;\n}\n\n/**\n * One session's Po-selected skill inventory.\n *\n * Construction is deliberately cheap. `start()` schedules the synchronous Pi\n * walker after session_start returns, while `ready()` is the single promise the\n * first input awaits before Pi expands `/skill:name`.\n */\nexport class DeferredSkillLoader {\n readonly #options: DeferredSkillLoaderOptions;\n #promise: Promise<DeferredSkillSnapshot> | undefined;\n\n constructor(options: DeferredSkillLoaderOptions) {\n this.#options = options;\n }\n\n start(): Promise<DeferredSkillSnapshot> {\n if (this.#promise) return this.#promise;\n\n if (!this.#options.schedule && !this.#options.load) {\n this.#promise = this.#loadInWorker();\n return this.#promise;\n }\n\n const schedule = this.#options.schedule ?? ((load: () => void) => setImmediate(load));\n this.#promise = new Promise((resolve) => {\n const fail = (error: unknown): void => {\n resolve(this.#failure(error));\n };\n try {\n schedule(() => {\n try {\n resolve(this.#load());\n } catch (error) {\n fail(error);\n }\n });\n } catch (error) {\n fail(error);\n }\n });\n return this.#promise;\n }\n\n ready(): Promise<DeferredSkillSnapshot> {\n return this.start();\n }\n\n #loadOptions(): { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean } {\n return {\n cwd: this.#options.cwd,\n agentDir: this.#options.agentDir ?? getAgentDir(),\n skillPaths: [...this.#options.skillPaths],\n includeDefaults: false,\n };\n }\n\n #snapshot(result: {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n }): DeferredSkillSnapshot {\n return {\n skills: result.skills,\n diagnostics: result.diagnostics.map(\n (diagnostic) => `${diagnostic.path ?? this.#options.cwd}: ${diagnostic.message}`,\n ),\n };\n }\n\n #failure(error: unknown): DeferredSkillSnapshot {\n return {\n skills: [],\n diagnostics: [`${this.#options.cwd}: ${error instanceof Error ? error.message : String(error)}`],\n };\n }\n\n #load(): DeferredSkillSnapshot {\n return this.#snapshot((this.#options.load ?? loadSkills)(this.#loadOptions()));\n }\n\n #loadInWorker(): Promise<DeferredSkillSnapshot> {\n return new Promise((resolve) => {\n let settled = false;\n const settle = (snapshot: DeferredSkillSnapshot): void => {\n if (settled) return;\n settled = true;\n resolve(snapshot);\n };\n let worker: Worker;\n try {\n worker = new Worker(SKILL_WORKER_SOURCE, {\n eval: true,\n workerData: {\n piModuleUrl: this.#options.piModuleUrl ?? installedPiModuleUrl(),\n loadOptions: this.#loadOptions(),\n },\n });\n } catch (error) {\n settle(this.#failure(error));\n return;\n }\n worker.unref();\n worker.once('message', (message: SkillWorkerResult) => {\n if (!message.ok || !message.result) {\n settle(this.#failure(message.error ?? 'Skill worker failed'));\n return;\n }\n settle(this.#snapshot(message.result));\n });\n worker.once('error', (error) => settle(this.#failure(error)));\n worker.once('exit', (code) => {\n if (code !== 0) settle(this.#failure(`Skill worker exited with code ${code}`));\n });\n });\n }\n}\n\n/** Appends only the inventory Pi would append for these deferred skills. */\nexport function buildPromptWithDeferredSkills(\n systemPrompt: string,\n options: BuildSystemPromptOptions,\n skills: Skill[],\n): string {\n if (options.selectedTools && !options.selectedTools.includes('read')) return systemPrompt;\n return `${systemPrompt}${formatSkillsForPrompt(skills)}`;\n}\n\n/** Expands a deferred `/skill:name` before Pi consults its synchronous inventory. */\nexport function expandDeferredSkillCommand(text: string, skills: readonly Skill[]): string {\n if (!text.startsWith('/skill:')) return text;\n const spaceIndex = text.indexOf(' ');\n const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);\n const skill = skills.find((candidate) => candidate.name === skillName);\n if (!skill) return text;\n\n try {\n const args = spaceIndex === -1 ? '' : text.slice(spaceIndex + 1).trim();\n const body = stripFrontmatter(fs.readFileSync(skill.filePath, 'utf8')).trim();\n const skillBlock = `<skill name=\"${skill.name}\" location=\"${skill.filePath}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n return args ? `${skillBlock}\\n\\n${args}` : skillBlock;\n } catch {\n // Pi uses the same literal-text fallback if a skill disappears between\n // discovery and submission; the agent can still respond to the command.\n return text;\n }\n}\n","import path from 'node:path';\n\nimport {\n listDomainNamesAsync,\n resolvePluginEntriesAsync,\n resolveSharedSkillsAsync,\n type PluginEntry,\n} from '@agimon-ai/po-config/domains';\nimport { readHarnessState } from '@agimon-ai/po-config/harnessState';\nimport type { PoHeadlessExecutionContext } from '@agimon-ai/po-core/headless';\nimport { BACKGROUND_CONTEXT, nodeHost, withAbortSignal, type FileSystem } from '@agimon-ai/po-core/platform';\nimport { materializePluginEntries } from '@agimon-ai/po-domain/plugins';\nimport { collectResources, discoverSkillsAsync } from '@agimon-ai/po-domain/resources';\nimport { formatSkillsForPrompt } from '@earendil-works/pi-coding-agent';\n\nimport type { ServerSkillInventory } from '../../types/skills';\nimport { DeferredSkillLoader } from '../deferredSkills';\n\nexport type { ServerSkillGroup, ServerSkillInventory } from '../../types/skills';\n\ninterface SkillSource {\n readonly domain?: string;\n readonly paths: readonly string[];\n readonly mcpPaths: readonly string[];\n}\n\nconst LOCAL_ONLY_MCP_PACKAGES = new Set([\n '@agimon-ai/po-author',\n '@agimon-ai/po-git',\n '@agimon-ai/po-goal',\n '@agimon-ai/po-help',\n '@agimon-ai/po-loop',\n '@agimon-ai/po-user-feedback',\n '@agimon-ai/po-voice',\n '@agimon-ai/po-workflow',\n]);\n\ntype IoContext = Parameters<FileSystem['readTextFile']>[1];\n\nasync function isMcpSkillPlugin(fs: FileSystem, entry: PluginEntry, context: IoContext): Promise<boolean> {\n if (entry.source?.type === 'npm') return !LOCAL_ONLY_MCP_PACKAGES.has(entry.source.package);\n // A package manifest is optional for catalog plugins, but a present identity\n // that cannot be read or parsed must never make the plugin remotely eligible.\n const source = await fs.readTextFile(path.join(entry.directory, 'package.json'), context);\n if (!source.ok) return source.error.code === 'not_found';\n try {\n const manifest = JSON.parse(source.value) as unknown;\n if (typeof manifest !== 'object' || manifest === null || Array.isArray(manifest)) return false;\n const name = (manifest as { name?: unknown }).name;\n return typeof name === 'string' && name.length > 0 && name.trim() === name && !LOCAL_ONLY_MCP_PACKAGES.has(name);\n } catch {\n return false;\n }\n}\n\n/**\n * Whether a discovered skill came from one of these paths.\n *\n * A source holds exact SKILL.md files for plugin and shared skills, but a bare\n * directory for `.po/skills`, so membership is containment rather than\n * equality.\n */\nfunction within(filePath: string, roots: readonly string[]): boolean {\n const resolved = path.resolve(filePath);\n return roots.some((root) => {\n const base = path.resolve(root);\n return resolved === base || resolved.startsWith(`${base}${path.sep}`);\n });\n}\n/**\n * Every skill the session could reach, grouped by what activates it.\n *\n * A cockpit `/domains` switch reconciles contributions rather than rebuilding\n * the server plugin: the session declaration is mounted once. So enumerating only the\n * launch selection would freeze the command list at whatever was selected when\n * the session host was built. Enumerating every domain up front and tagging\n * each group with its `when` lets the existing kernel refresh do the work, the\n * same way other host contributions follow selection changes.\n */\nexport async function discoverServerSkills(\n execution: PoHeadlessExecutionContext,\n fs: FileSystem,\n signal: AbortSignal,\n): Promise<ServerSkillInventory> {\n signal.throwIfAborted();\n const context = withAbortSignal(signal, BACKGROUND_CONTEXT);\n const state = readHarnessState(execution.environment);\n const home = execution.environment.HOME ?? nodeHost.homeDirectory();\n const repoSkills = path.join(execution.repoRoot, '.po', 'skills');\n const sources: SkillSource[] = [];\n const diagnostics: string[] = [];\n // A malformed domain is a configuration error in one domain, so it must not\n // remove every other domain's skills. Enumerating the whole manifest is new\n // exposure here: reading the launch selection could not fail this way.\n const domains: string[] = [];\n try {\n domains.push(...(await listDomainNamesAsync(fs, execution.repoRoot, context, home)));\n } catch (error) {\n diagnostics.push(`Domain skills are unavailable: ${error instanceof Error ? error.message : String(error)}`);\n }\n\n // Plugin sources precede shared sources, then repository sources.\n // ponytail: Pi globally name-dedupes before domain gating. A loaded plugin\n // shadows a same-name shared fallback even when its domain is off. This is\n // the current ceiling; upgrade to selection-aware loading if collisions arise.\n for (const domain of domains) {\n signal.throwIfAborted();\n try {\n const plugins = await materializePluginEntries(\n await resolvePluginEntriesAsync(fs, execution.repoRoot, [domain], [], context, home),\n );\n const mcpPluginRoots = (\n await Promise.all(\n plugins.map(async (plugin) =>\n (await isMcpSkillPlugin(fs, plugin, context)) ? path.join(plugin.directory, 'skills') : undefined,\n ),\n )\n ).filter((root): root is string => root !== undefined);\n const collected = await collectResources(execution.repoRoot, plugins, {\n agents: false,\n mcp: false,\n sharedSkills: false,\n });\n try {\n if (collected.skillDirectories.length > 0) {\n const paths = [...collected.skillDirectories];\n sources.push({\n domain,\n paths,\n // Exact collected paths retain this domain's per-plugin skill subset.\n // A plugin root would also admit skills loaded for another domain.\n mcpPaths: paths.filter((skillPath) => within(skillPath, mcpPluginRoots)),\n });\n }\n } finally {\n await collected.cleanup();\n }\n } catch (error) {\n diagnostics.push(`Domain '${domain}': ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n // Shared skills are admitted by any domain that keeps them.\n signal.throwIfAborted();\n const shared = await discoverSkillsAsync(execution.repoRoot, path.join(execution.repoRoot, '.claude', 'skills'));\n if (shared.length > 0) {\n const sharedPaths = shared.map((skill) => skill.path);\n for (const domain of domains) {\n if (await resolveSharedSkillsAsync(fs, execution.repoRoot, [domain], context, home)) {\n sources.push({ domain, paths: sharedPaths, mcpPaths: sharedPaths });\n }\n }\n }\n\n // Repository-local skills belong to the repository, not to a selection.\n sources.push({ paths: [repoSkills], mcpPaths: [repoSkills] });\n\n signal.throwIfAborted();\n // One walk for every group. Pi drops a path it has already loaded, so the\n // loader gets the unique set and each group selects its own skills back out.\n const unique = [...new Set([...sources.flatMap((source) => source.paths), ...(state.skillDirectories ?? [])])];\n const inventory = await new DeferredSkillLoader({ cwd: execution.cwd, skillPaths: unique }).ready();\n signal.throwIfAborted();\n\n // One group per loaded skill, merging every source admitting that exact path.\n // MCP admission is merged separately so local-only sources cannot broaden it.\n const merge = (key: 'paths' | 'mcpPaths') =>\n inventory.skills.flatMap((skill) => {\n const admitting = sources.filter((source) => within(skill.filePath, source[key]));\n if (admitting.length === 0) return [];\n const domains = [...new Set(admitting.flatMap((source) => (source.domain === undefined ? [] : [source.domain])))];\n return [\n {\n ...(admitting.some((source) => source.domain === undefined) ? {} : { domains }),\n skills: [skill],\n },\n ];\n });\n const groups = merge('paths');\n const mcpGroups = merge('mcpPaths');\n\n // The catalog still describes the active selection rather than everything on\n // disk, because it is prompt text rather than a menu.\n const activeSkills = inventory.skills.filter((skill) =>\n within(skill.filePath, [...(state.skillDirectories ?? []), repoSkills]),\n );\n return {\n inventory: { skills: inventory.skills, diagnostics: [...inventory.diagnostics, ...diagnostics] },\n groups,\n mcpGroups,\n catalog: formatSkillsForPrompt(activeSkills) || '(no discovered skills)',\n };\n}\n","import type { PoHeadlessCommand } from '@agimon-ai/po-core/headless';\nimport type { Skill } from '@earendil-works/pi-coding-agent';\n\nimport { expandDeferredSkillCommand, type DeferredSkillSnapshot } from '../../../../../services/deferredSkills';\nimport type { ServerSkillGroup } from '../../../../../services/serverInventory';\nimport { SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX, SKILLS_COMMAND } from '../../../../../types/skills';\n\n/**\n * One command per skill, gated by whatever activates that skill.\n *\n * Expansion always reads the full inventory, not the group: a command is only\n * dispatchable while its condition holds, so by the time one runs its skill is\n * active, and looking it up in the union keeps one lookup table instead of one\n * per domain.\n */\nfunction skillCommand(\n skill: Skill,\n domains: readonly string[] | undefined,\n inventory: DeferredSkillSnapshot,\n): PoHeadlessCommand {\n return {\n ...(domains === undefined ? {} : { when: { domains } }),\n name: `${SKILL_COMMAND_PREFIX}${skill.name}`,\n description: skill.description,\n execute: async (args, execution) => {\n const text = `${SKILL_INVOCATION_PREFIX}${skill.name}${args ? ` ${args}` : ''}`;\n const expanded = expandDeferredSkillCommand(text, inventory.skills);\n if (expanded === text) throw new Error(`Skill '${skill.name}' is no longer readable.`);\n await admit(execution.session, expanded);\n },\n };\n}\n\n/**\n * The dispatching prompt acks when this command returns, so awaiting the whole\n * turn (session.prompt) kept the composer draft until the run ended.\n */\nfunction admit(session: Parameters<PoHeadlessCommand['execute']>[1]['session'], text: string): Promise<void> {\n return session.admitPrompt ? session.admitPrompt(text, 'prompt', 'operator') : session.prompt(text);\n}\nexport function createSkillCommands(\n inventory: DeferredSkillSnapshot,\n catalog: string,\n groups: readonly ServerSkillGroup[],\n): readonly PoHeadlessCommand[] {\n return [\n {\n name: SKILLS_COMMAND,\n description: 'List discovered skills or invoke one by name.',\n async execute(args, execution) {\n const requested = args.trim();\n if (requested) {\n await admit(\n execution.session,\n expandDeferredSkillCommand(`${SKILL_INVOCATION_PREFIX}${requested}`, inventory.skills),\n );\n return;\n }\n await execution.client.notify({\n title: 'Po skills',\n body: catalog,\n level: 'info',\n });\n },\n },\n ...groups.flatMap((group) => group.skills.map((skill) => skillCommand(skill, group.domains, inventory))),\n ];\n}\n","import { readHarnessState } from '@agimon-ai/po-config/harnessState';\nimport type { PoHeadlessHostService } from '@agimon-ai/po-core/headless';\nimport { BACKGROUND_CONTEXT, getOrThrow, nodeHost, type FileSystem } from '@agimon-ai/po-core/platform';\nimport {\n type PoServerResources,\n type PoServerSessionPlugin,\n packageResourcePath,\n readPackageResource,\n} from '@agimon-ai/po-core/serverPlugin';\n\nimport { SKILL_INVENTORY_FRESH_MS } from '../../../../../constants/skillInventory';\nimport { mountMcpSkills } from '../../../../../services/mcpSkills';\nimport { createSessionLoadSkillTool, createSessionSearchSkillsTool } from '../../../../../services/mcpSkillTools';\nimport { discoverServerSkills } from '../../../../../services/serverInventory';\nimport { createSkillCommands } from './skillCommands';\n\nexport async function createSkillServer(\n agent: PoHeadlessHostService,\n fs: FileSystem,\n signal: AbortSignal,\n resources: PoServerResources,\n): Promise<PoServerSessionPlugin> {\n const execution = agent.context;\n // Sessions with the same inputs share one discovery. The key holds every input discoverServerSkills\n // reads besides the disk, so equal keys give equal inventories, and the window bounds how old that\n // read of the disk can be.\n const key = [\n 'inventory',\n execution.repoRoot,\n execution.cwd,\n execution.environment.HOME ?? nodeHost.homeDirectory(),\n ...(readHarnessState(execution.environment).skillDirectories ?? []),\n Math.floor(Date.now() / SKILL_INVENTORY_FRESH_MS),\n ];\n const { inventory, catalog, groups, mcpGroups } = await resources.acquire(key, () =>\n // Every session sharing the discovery waits on it, so none of their signals may abort it.\n discoverServerSkills(execution, fs, new AbortController().signal),\n );\n return {\n commands: createSkillCommands(inventory, catalog, groups),\n tools: [createSessionLoadSkillTool(agent), createSessionSearchSkillsTool(agent)],\n services: [mountMcpSkills(mcpGroups, fs, signal)],\n resources: [\n // Each skill joins the prompt's one <available_skills> list, gated like the rest.\n ...groups.flatMap((group) =>\n group.skills.map((skill) => ({\n name: skill.name,\n description: skill.description,\n path: skill.filePath,\n kind: 'skill' as const,\n ...(group.domains === undefined ? {} : { when: { domains: group.domains } }),\n read: async () => getOrThrow(await fs.readTextFile(skill.filePath, BACKGROUND_CONTEXT)),\n })),\n ),\n ...[\n {\n name: 'po-author-skill',\n description:\n 'Author and distribute Po agent skills. Use when creating a SKILL.md, contributing a runtime skill directory, or publishing activation-gated Help prompts and diagnostic tools.',\n },\n {\n name: 'po-use-skill',\n description:\n \"Use Po's skill catalog and deferred discovery. Diagnose missing or shadowed skills and invoke the narrowest available guidance.\",\n },\n ].map((skill) => ({\n ...skill,\n when: { state: { 'minor-mode': 'help' }, attribution: { kind: 'minor' as const, mode: 'help' } },\n kind: 'skill' as const,\n path: packageResourcePath(import.meta.url, `src/prompts/${skill.name}/SKILL.md`),\n read: () => readPackageResource(import.meta.url, `src/prompts/${skill.name}/SKILL.md`),\n })),\n ],\n };\n}\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport { requirePoFs } from '@agimon-ai/po-core/platform';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nimport { createSkillServer } from './_lib/skillServer';\n\nexport default defineRoot(async ({ agent, context: root, signal, resources }: PoServerPluginContext) => {\n const runtime: PoServerSessionPlugin = agent\n ? await createSkillServer(agent, requirePoFs(root), signal, resources)\n : { commands: [], resources: [] };\n return { value: runtime, services: runtime.services };\n});\n","import { defineRoutedContribution } from '@agimon-ai/po-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, PoServerSessionPlugin>) => context.root.commands!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution } from '@agimon-ai/po-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, PoServerSessionPlugin>) => context.root.resources!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\n/** The session agent's skill tools; the Pi CLI keeps Pi's own skill handling. */\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, PoServerSessionPlugin>) => context.root.tools ?? [],\n { cardinality: 'many' },\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport commandSkills from '../src/extensions/workspaces/sessions/(backend)/command/skills.server';\nimport resourceSkills from '../src/extensions/workspaces/sessions/(backend)/resource/skills.server';\nimport toolLoadSkill from '../src/extensions/workspaces/sessions/(backend)/tool/load_skill.server';\n\ntype ManyFactory<T, C> = (context: C) => T | readonly T[];\nconst many = <T, C>(value: T | readonly T[] | ManyFactory<T, C>, context: C): readonly T[] => {\n const resolved = typeof value === 'function' ? (value as ManyFactory<T, C>)(context) : value;\n return Array.isArray(resolved) ? resolved : [resolved as T];\n};\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-skill',\n providerOverrides: composeServerProviderOverrides(rootSession),\n injects: composeRootInjects({ session: [rootSession] }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): PoServerSessionPlugin['commands'] { return [...many(commandSkills, contextSession)]; },\n get resources(): PoServerSessionPlugin['resources'] { return [...many(resourceSkills, contextSession)]; },\n get tools(): PoServerSessionPlugin['tools'] { return [...many(toolLoadSkill, contextSession)]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills-Ccvg1Hcz.mjs","names":[],"sources":["../src/types/skills.ts"],"sourcesContent":["import type { Skill } from '@earendil-works/pi-coding-agent';\n\nexport const SKILLS_COMMAND = 'skills';\nexport const SKILL_INVOCATION_PREFIX = '/skill:';\nexport const SKILL_COMMAND_PREFIX = 'skill:';\n\nexport interface DeferredSkillSnapshot {\n skills: Skill[];\n diagnostics: string[];\n}\n\nexport interface DeferredSkillLoaderOptions {\n cwd: string;\n skillPaths: readonly string[];\n agentDir?: string;\n piModuleUrl?: string;\n schedule?: (load: () => void) => void;\n load?: (options: { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean }) => {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n };\n}\n\n/**
|
|
1
|
+
{"version":3,"file":"skills-Ccvg1Hcz.mjs","names":[],"sources":["../src/types/skills.ts"],"sourcesContent":["import type { Skill } from '@earendil-works/pi-coding-agent';\n\nexport const SKILLS_COMMAND = 'skills';\nexport const SKILL_INVOCATION_PREFIX = '/skill:';\nexport const SKILL_COMMAND_PREFIX = 'skill:';\n\nexport interface DeferredSkillSnapshot {\n skills: Skill[];\n diagnostics: string[];\n}\n\nexport interface DeferredSkillLoaderOptions {\n cwd: string;\n skillPaths: readonly string[];\n agentDir?: string;\n piModuleUrl?: string;\n schedule?: (load: () => void) => void;\n load?: (options: { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean }) => {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n };\n}\n\n/** One loaded skill and its any-of domain gate, or always-on when absent. */\nexport interface ServerSkillGroup {\n readonly domains?: readonly string[];\n readonly skills: readonly Skill[];\n}\n\nexport interface ServerSkillInventory {\n /** Every skill any selection can reach, so an activated command can still expand. */\n readonly inventory: DeferredSkillSnapshot;\n readonly groups: readonly ServerSkillGroup[];\n readonly mcpGroups: readonly ServerSkillGroup[];\n readonly catalog: string;\n}\n"],"mappings":";AAEA,MAAa,iBAAiB;AAC9B,MAAa,0BAA0B;AACvC,MAAa,uBAAuB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills-ComtgYb1.cjs","names":[],"sources":["../src/types/skills.ts"],"sourcesContent":["import type { Skill } from '@earendil-works/pi-coding-agent';\n\nexport const SKILLS_COMMAND = 'skills';\nexport const SKILL_INVOCATION_PREFIX = '/skill:';\nexport const SKILL_COMMAND_PREFIX = 'skill:';\n\nexport interface DeferredSkillSnapshot {\n skills: Skill[];\n diagnostics: string[];\n}\n\nexport interface DeferredSkillLoaderOptions {\n cwd: string;\n skillPaths: readonly string[];\n agentDir?: string;\n piModuleUrl?: string;\n schedule?: (load: () => void) => void;\n load?: (options: { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean }) => {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n };\n}\n\n/**
|
|
1
|
+
{"version":3,"file":"skills-ComtgYb1.cjs","names":[],"sources":["../src/types/skills.ts"],"sourcesContent":["import type { Skill } from '@earendil-works/pi-coding-agent';\n\nexport const SKILLS_COMMAND = 'skills';\nexport const SKILL_INVOCATION_PREFIX = '/skill:';\nexport const SKILL_COMMAND_PREFIX = 'skill:';\n\nexport interface DeferredSkillSnapshot {\n skills: Skill[];\n diagnostics: string[];\n}\n\nexport interface DeferredSkillLoaderOptions {\n cwd: string;\n skillPaths: readonly string[];\n agentDir?: string;\n piModuleUrl?: string;\n schedule?: (load: () => void) => void;\n load?: (options: { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean }) => {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n };\n}\n\n/** One loaded skill and its any-of domain gate, or always-on when absent. */\nexport interface ServerSkillGroup {\n readonly domains?: readonly string[];\n readonly skills: readonly Skill[];\n}\n\nexport interface ServerSkillInventory {\n /** Every skill any selection can reach, so an activated command can still expand. */\n readonly inventory: DeferredSkillSnapshot;\n readonly groups: readonly ServerSkillGroup[];\n readonly mcpGroups: readonly ServerSkillGroup[];\n readonly catalog: string;\n}\n"],"mappings":";AAEA,MAAa,iBAAiB;AAC9B,MAAa,0BAA0B;AACvC,MAAa,uBAAuB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/po-skill",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.29",
|
|
4
4
|
"description": "Session skill catalogue, deferred skill discovery, and the skill browser for Po.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-skills",
|
|
@@ -65,18 +65,18 @@
|
|
|
65
65
|
"access": "public"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@agimon-ai/po-config": "0.0.0-alpha.
|
|
69
|
-
"@agimon-ai/po-core": "0.0.0-alpha.
|
|
70
|
-
"@agimon-ai/po-domain": "0.0.0-alpha.
|
|
71
|
-
"@agimon-ai/po-telemetry": "0.0.0-alpha.
|
|
72
|
-
"@agimon-ai/po-web-components": "0.0.0-alpha.
|
|
68
|
+
"@agimon-ai/po-config": "0.0.0-alpha.29",
|
|
69
|
+
"@agimon-ai/po-core": "0.0.0-alpha.29",
|
|
70
|
+
"@agimon-ai/po-domain": "0.0.0-alpha.29",
|
|
71
|
+
"@agimon-ai/po-telemetry": "0.0.0-alpha.29",
|
|
72
|
+
"@agimon-ai/po-web-components": "0.0.0-alpha.29",
|
|
73
73
|
"@deepseek-ai/cordis": "4.0.5",
|
|
74
74
|
"gpt-tokenizer": "4.0.0",
|
|
75
75
|
"typebox": "1.3.37"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
|
-
"@agimon-ai/po-build": "0.0.0-alpha.
|
|
79
|
-
"@agimon-ai/vibe-lint-plugin-po-extension": "0.0.0-alpha.
|
|
78
|
+
"@agimon-ai/po-build": "0.0.0-alpha.29",
|
|
79
|
+
"@agimon-ai/vibe-lint-plugin-po-extension": "0.0.0-alpha.29",
|
|
80
80
|
"@deepseek-ai/cordis": "4.0.5",
|
|
81
81
|
"@earendil-works/pi-coding-agent": "1.3.0",
|
|
82
82
|
"@types/node": "26.6.5",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: po-author-skill
|
|
3
|
-
description: Author and distribute Po agent skills. Use when creating a SKILL.md, adding supporting references or scripts,
|
|
3
|
+
description: Author and distribute Po agent skills. Use when creating a SKILL.md, adding supporting references or scripts, configuring a discovered skill source, or publishing activation-gated Help prompts from a Po package.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Author Po skills
|
|
@@ -22,7 +22,7 @@ The frontmatter `name` must match the directory. Make the description specific e
|
|
|
22
22
|
|
|
23
23
|
## Choose the distribution path
|
|
24
24
|
|
|
25
|
-
- For
|
|
25
|
+
- For discovered plugin skills, ship `skills/<name>/SKILL.md` and configure the plugin and optional skill subset in `domains.yaml`. po-skill registers each loaded skill once with an any-of gate for its admitting domains. Shared `.claude/skills` follow domains that keep shared skills; repository `.po/skills` are unconditional. Do not register discovered plugin skills again from the package.
|
|
26
26
|
- For guidance that should appear only while Po Help is active, put it at `src/prompts/<name>/SKILL.md`, publish `src/prompts`, link it from the package's H1-led `llms.txt`, and register a Help descriptor through `PO_HELP_SERVICE`.
|
|
27
27
|
- For repository content selected by domains, place the skill in the plugin or shared skill source configured by `domains.yaml`; do not hard-code that directory into the Skill package.
|
|
28
28
|
|
|
@@ -34,4 +34,4 @@ Package-owned Help tools use ordinary named tool routes, not documentation descr
|
|
|
34
34
|
|
|
35
35
|
## Verification
|
|
36
36
|
|
|
37
|
-
Validate the skill frontmatter, ensure every relative reference resolves, and test the packed package rather than relying on checkout-only files. For
|
|
37
|
+
Validate the skill frontmatter, ensure every relative reference resolves, and test the packed package rather than relying on checkout-only files. For discovered skills, cover domain swaps, shared-skill opt-outs, and duplicate source admission. Pi globally name-deduplicates, so a plugin can shadow a same-name shared fallback even when its domain is off. Test the actual prompt and tool surface before activation, while active, and after deactivation; stale calls must be rejected without removing ordinary capabilities. Use `/skills` or `SPC e s` in an interactive session to confirm the expected owner and invocation name. Do not add remote MCP exposure merely because a local Help contribution exists.
|
|
@@ -7,6 +7,8 @@ description: Use Po's skill catalog and deferred discovery. Use when browsing av
|
|
|
7
7
|
|
|
8
8
|
Open the skill browser with `/skills` or `SPC e s`. The catalog groups normal session skills, package Help skills, plugin skills, and extension-contributed directories by owner.
|
|
9
9
|
|
|
10
|
+
For agent tool calls, use `load_skill` with the exact name. With at most 20 active skills, its description lists the names and descriptions. Above 20, use `search_skills` to find skills by name or description, then load the chosen name. Search returns metadata only, not skill instructions or paths.
|
|
11
|
+
|
|
10
12
|
Invoke a skill with `/skill:<name>`. Po expands deferred skills before Pi handles the input, so a skill can be invokable even when its full body was not placed in the initial prompt. Help skills appear only while Help mode is active.
|
|
11
13
|
|
|
12
14
|
When a skill is missing:
|