@agimon-ai/doompi-skill 0.0.1-alpha.70 → 0.0.1-alpha.72

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.
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.cjs","names":["defineRoutedContribution","defineMcpSkill","readPackageResource","defineMcpSkill","readPackageResource","Type","defineMcpTool","defineMcpTool","defineMcpPlugin","skillDiscovered","skillDoompiAuthorSkill","skillDoompiUseSkill","toolLoadSkill","toolSearchSkills"],"sources":["../../src/services/mcpSkills/index.ts","../../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/doompi-author-skill.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/doompi-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 { readFile } from 'node:fs/promises';\n\nimport type { DoomMcpPluginContext, DoomMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'doom/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: DoomMcpPluginContext): readonly DoomMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], 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, DoomMcpSkill>();\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 = await readFile(skill.filePath, { encoding: 'utf8', signal: readSignal });\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: DoomMcpPluginContext): readonly DoomMcpSkill[] {\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/doompi-core/extensionFile';\n\nimport { bindMcpSkills } from '../../../../../services/mcpSkills';\n\nexport default defineRoutedContribution(bindMcpSkills, { cardinality: 'many' });\n","import { defineMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineMcpSkill({\n name: 'doompi-author-skill',\n description:\n 'Author and distribute DoomPi 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 DoomPi package.',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-author-skill/SKILL.md'),\n});\n","import { defineMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineMcpSkill({\n name: 'doompi-use-skill',\n description:\n \"Use DoomPi'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/doompi-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 { DoomHeadlessTool } from '@agimon-ai/doompi-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<DoomHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): DoomHeadlessTool<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 createLoadSkillTool(): DoomHeadlessTool<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/doompi-core/mcpFacet';\n\nimport { createLoadSkillTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createLoadSkillTool());\n","import { defineMcpTool } from '@agimon-ai/doompi-core/mcpFacet';\n\nimport { createSearchSkillsTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createSearchSkillsTool());\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/doompi-core/mcpFacet';\n\nimport skillDiscovered from '../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp';\nimport skillDoompiAuthorSkill from '../src/extensions/workspaces/sessions/(backend)/skill/doompi-author-skill.mcp';\nimport skillDoompiUseSkill from '../src/extensions/workspaces/sessions/(backend)/skill/doompi-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, [\"doompi/widget\"]: key },\n});\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/doompi-skill',\n session: (context) => ({\n get skills(): DoomMcpSessionPlugin['skills'] { return [...many(skillDiscovered, context), at(skillDoompiAuthorSkill, context), at(skillDoompiUseSkill, context)]; },\n get tools(): DoomMcpSessionPlugin['tools'] { return [withWidget(\"@agimon-ai/doompi-skill/load_skill\", via({ name: 'load_skill' }, at(toolLoadSkill, context))), withWidget(\"@agimon-ai/doompi-skill/search_skills\", via({ name: 'search_skills' }, at(toolSearchSkills, context)))]; },\n }) satisfies DoomMcpSessionPlugin,\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","skillDoompiAuthorSkill","skillDoompiUseSkill","toolLoadSkill","toolSearchSkills"],"sources":["../../src/services/mcpSkills/index.ts","../../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/doompi-author-skill.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/doompi-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 { readFile } from 'node:fs/promises';\n\nimport type { DoomMcpPluginContext, DoomMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'doom/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: DoomMcpPluginContext): readonly DoomMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], 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, DoomMcpSkill>();\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 = await readFile(skill.filePath, { encoding: 'utf8', signal: readSignal });\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: DoomMcpPluginContext): readonly DoomMcpSkill[] {\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/doompi-core/extensionFile';\n\nimport { bindMcpSkills } from '../../../../../services/mcpSkills';\n\nexport default defineRoutedContribution(bindMcpSkills, { cardinality: 'many' });\n","import { defineMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineMcpSkill({\n name: 'doompi-author-skill',\n description:\n 'Author and distribute DoomPi 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 DoomPi package.',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-author-skill/SKILL.md'),\n});\n","import { defineMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineMcpSkill({\n name: 'doompi-use-skill',\n description:\n \"Use DoomPi'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/doompi-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 { DoomHeadlessHostService, DoomHeadlessTool } from '@agimon-ai/doompi-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<DoomHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): DoomHeadlessTool<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 DoomPi\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<DoomHeadlessHostService, 'readSkill'>,\n): DoomHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (server session only, shown as a generic call)\n // DOOM_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(): DoomHeadlessTool<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/doompi-core/mcpFacet';\n\nimport { createLoadSkillTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createLoadSkillTool());\n","import { defineMcpTool } from '@agimon-ai/doompi-core/mcpFacet';\n\nimport { createSearchSkillsTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createSearchSkillsTool());\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/doompi-core/mcpFacet';\n\nimport skillDiscovered from '../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp';\nimport skillDoompiAuthorSkill from '../src/extensions/workspaces/sessions/(backend)/skill/doompi-author-skill.mcp';\nimport skillDoompiUseSkill from '../src/extensions/workspaces/sessions/(backend)/skill/doompi-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, [\"doompi/widget\"]: key },\n});\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/doompi-skill',\n session: (context) => ({\n get skills(): DoomMcpSessionPlugin['skills'] { return [...many(skillDiscovered, context), at(skillDoompiAuthorSkill, context), at(skillDoompiUseSkill, context)]; },\n get tools(): DoomMcpSessionPlugin['tools'] { return [withWidget(\"@agimon-ai/doompi-skill/load_skill\", via({ name: 'load_skill' }, at(toolLoadSkill, context))), withWidget(\"@agimon-ai/doompi-skill/search_skills\", via({ name: 'search_skills' }, at(toolSearchSkills, context)))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.mjs","names":["skillDiscovered","skillDoompiAuthorSkill","skillDoompiUseSkill","toolLoadSkill","toolSearchSkills"],"sources":["../../src/services/mcpSkills/index.ts","../../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/doompi-author-skill.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/doompi-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 { readFile } from 'node:fs/promises';\n\nimport type { DoomMcpPluginContext, DoomMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'doom/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: DoomMcpPluginContext): readonly DoomMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], 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, DoomMcpSkill>();\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 = await readFile(skill.filePath, { encoding: 'utf8', signal: readSignal });\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: DoomMcpPluginContext): readonly DoomMcpSkill[] {\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/doompi-core/extensionFile';\n\nimport { bindMcpSkills } from '../../../../../services/mcpSkills';\n\nexport default defineRoutedContribution(bindMcpSkills, { cardinality: 'many' });\n","import { defineMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineMcpSkill({\n name: 'doompi-author-skill',\n description:\n 'Author and distribute DoomPi 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 DoomPi package.',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-author-skill/SKILL.md'),\n});\n","import { defineMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineMcpSkill({\n name: 'doompi-use-skill',\n description:\n \"Use DoomPi'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/doompi-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 { DoomHeadlessTool } from '@agimon-ai/doompi-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<DoomHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): DoomHeadlessTool<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 createLoadSkillTool(): DoomHeadlessTool<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/doompi-core/mcpFacet';\n\nimport { createLoadSkillTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createLoadSkillTool());\n","import { defineMcpTool } from '@agimon-ai/doompi-core/mcpFacet';\n\nimport { createSearchSkillsTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createSearchSkillsTool());\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/doompi-core/mcpFacet';\n\nimport skillDiscovered from '../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp';\nimport skillDoompiAuthorSkill from '../src/extensions/workspaces/sessions/(backend)/skill/doompi-author-skill.mcp';\nimport skillDoompiUseSkill from '../src/extensions/workspaces/sessions/(backend)/skill/doompi-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, [\"doompi/widget\"]: key },\n});\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/doompi-skill',\n session: (context) => ({\n get skills(): DoomMcpSessionPlugin['skills'] { return [...many(skillDiscovered, context), at(skillDoompiAuthorSkill, context), at(skillDoompiUseSkill, context)]; },\n get tools(): DoomMcpSessionPlugin['tools'] { return [withWidget(\"@agimon-ai/doompi-skill/load_skill\", via({ name: 'load_skill' }, at(toolLoadSkill, context))), withWidget(\"@agimon-ai/doompi-skill/search_skills\", via({ name: 'search_skills' }, at(toolSearchSkills, context)))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
1
+ {"version":3,"file":"mcp.mjs","names":["skillDiscovered","skillDoompiAuthorSkill","skillDoompiUseSkill","toolLoadSkill","toolSearchSkills"],"sources":["../../src/services/mcpSkills/index.ts","../../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/doompi-author-skill.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/skill/doompi-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 { readFile } from 'node:fs/promises';\n\nimport type { DoomMcpPluginContext, DoomMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'doom/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: DoomMcpPluginContext): readonly DoomMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], 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, DoomMcpSkill>();\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 = await readFile(skill.filePath, { encoding: 'utf8', signal: readSignal });\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: DoomMcpPluginContext): readonly DoomMcpSkill[] {\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/doompi-core/extensionFile';\n\nimport { bindMcpSkills } from '../../../../../services/mcpSkills';\n\nexport default defineRoutedContribution(bindMcpSkills, { cardinality: 'many' });\n","import { defineMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineMcpSkill({\n name: 'doompi-author-skill',\n description:\n 'Author and distribute DoomPi 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 DoomPi package.',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-author-skill/SKILL.md'),\n});\n","import { defineMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineMcpSkill({\n name: 'doompi-use-skill',\n description:\n \"Use DoomPi'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/doompi-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 { DoomHeadlessHostService, DoomHeadlessTool } from '@agimon-ai/doompi-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<DoomHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): DoomHeadlessTool<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 DoomPi\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<DoomHeadlessHostService, 'readSkill'>,\n): DoomHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (server session only, shown as a generic call)\n // DOOM_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(): DoomHeadlessTool<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/doompi-core/mcpFacet';\n\nimport { createLoadSkillTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createLoadSkillTool());\n","import { defineMcpTool } from '@agimon-ai/doompi-core/mcpFacet';\n\nimport { createSearchSkillsTool } from '../../../../../services/mcpSkillTools';\n\nexport default defineMcpTool(createSearchSkillsTool());\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/doompi-core/mcpFacet';\n\nimport skillDiscovered from '../src/extensions/workspaces/sessions/(backend)/skill/discovered.mcp';\nimport skillDoompiAuthorSkill from '../src/extensions/workspaces/sessions/(backend)/skill/doompi-author-skill.mcp';\nimport skillDoompiUseSkill from '../src/extensions/workspaces/sessions/(backend)/skill/doompi-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, [\"doompi/widget\"]: key },\n});\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/doompi-skill',\n session: (context) => ({\n get skills(): DoomMcpSessionPlugin['skills'] { return [...many(skillDiscovered, context), at(skillDoompiAuthorSkill, context), at(skillDoompiUseSkill, context)]; },\n get tools(): DoomMcpSessionPlugin['tools'] { return [withWidget(\"@agimon-ai/doompi-skill/load_skill\", via({ name: 'load_skill' }, at(toolLoadSkill, context))), withWidget(\"@agimon-ai/doompi-skill/search_skills\", via({ name: 'search_skills' }, at(toolSearchSkills, context)))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
@@ -13,6 +13,7 @@ let _earendil_works_pi_coding_agent = require("@earendil-works/pi-coding-agent")
13
13
  let _agimon_ai_doompi_core_extensionFile = require("@agimon-ai/doompi-core/extensionFile");
14
14
  let _agimon_ai_doompi_core_serverFacet = require("@agimon-ai/doompi-core/serverFacet");
15
15
  let node_fs_promises = require("node:fs/promises");
16
+ let typebox = require("typebox");
16
17
  let node_os = require("node:os");
17
18
  node_os = require_rolldown_runtime.__toESM(node_os, 1);
18
19
  let _agimon_ai_doompi_config_harnessState = require("@agimon-ai/doompi-config/harnessState");
@@ -51,6 +52,37 @@ function mountMcpSkills(groups, signal) {
51
52
  } });
52
53
  };
53
54
  }
55
+ typebox.Type.Object({ query: typebox.Type.Optional(typebox.Type.String()) });
56
+ const loadSkillParameters = typebox.Type.Object({ name: typebox.Type.String() });
57
+ //#endregion
58
+ //#region src/services/mcpSkillTools/index.ts
59
+ function text(text) {
60
+ return { content: [{
61
+ type: "text",
62
+ text
63
+ }] };
64
+ }
65
+ /**
66
+ * The session agent's way into a skill. Its skills are listed in the prompt, which DoomPi
67
+ * controls, so it needs no search; a remote agent's prompt is not ours, hence the pair above.
68
+ */
69
+ function createSessionLoadSkillTool(host) {
70
+ return {
71
+ name: "load_skill",
72
+ label: "Load skill",
73
+ description: "Load the full instructions of a skill from the available skills list by exact name.",
74
+ parameters: loadSkillParameters,
75
+ executionMode: "parallel",
76
+ async execute(_toolCallId, { name }) {
77
+ const body = host.readSkill(name);
78
+ if (body === void 0) return {
79
+ ...text(`No skill named ${name} is available in this session.`),
80
+ isError: true
81
+ };
82
+ return text(body);
83
+ }
84
+ };
85
+ }
54
86
  //#endregion
55
87
  //#region src/services/serverInventory/index.ts
56
88
  const LOCAL_ONLY_MCP_PACKAGES = /* @__PURE__ */ new Set([
@@ -144,12 +176,14 @@ async function discoverServerSkills(execution, signal) {
144
176
  for (const domain of domains) if ((0, _agimon_ai_doompi_config_domains.resolveSharedSkills)(execution.repoRoot, [domain], home)) sources.push({
145
177
  domain,
146
178
  paths: sharedPaths,
147
- mcpPaths: sharedPaths
179
+ mcpPaths: sharedPaths,
180
+ listed: true
148
181
  });
149
182
  }
150
183
  sources.push({
151
184
  paths: [repoSkills],
152
- mcpPaths: [repoSkills]
185
+ mcpPaths: [repoSkills],
186
+ listed: true
153
187
  });
154
188
  signal.throwIfAborted();
155
189
  const unique = [.../* @__PURE__ */ new Set([...sources.flatMap((source) => source.paths), ...state.skillDirectories ?? []])];
@@ -162,6 +196,10 @@ async function discoverServerSkills(execution, signal) {
162
196
  ...source.domain === void 0 ? {} : { domain: source.domain },
163
197
  skills: inventory.skills.filter((skill) => within(skill.filePath, source.paths))
164
198
  })).filter((group) => group.skills.length > 0);
199
+ const listed = sources.filter((source) => source.listed === true).map((source) => ({
200
+ ...source.domain === void 0 ? {} : { domain: source.domain },
201
+ skills: inventory.skills.filter((skill) => within(skill.filePath, source.paths))
202
+ })).filter((group) => group.skills.length > 0);
165
203
  const mcpGroups = sources.map((source) => ({
166
204
  ...source.domain === void 0 ? {} : { domain: source.domain },
167
205
  skills: inventory.skills.filter((skill) => within(skill.filePath, source.mcpPaths))
@@ -174,6 +212,7 @@ async function discoverServerSkills(execution, signal) {
174
212
  },
175
213
  groups,
176
214
  mcpGroups,
215
+ listed,
177
216
  catalog: (0, _earendil_works_pi_coding_agent.formatSkillsForPrompt)(activeSkills) || "(no discovered skills)"
178
217
  };
179
218
  }
@@ -221,15 +260,25 @@ function createSkillCommands(inventory, catalog, groups) {
221
260
  //#endregion
222
261
  //#region src/extensions/workspaces/sessions/(backend)/_lib/skillServer.ts
223
262
  async function createSkillServer(agent, signal) {
224
- const { inventory, catalog, groups, mcpGroups } = await discoverServerSkills(agent.context, signal);
263
+ const { inventory, catalog, groups, mcpGroups, listed } = await discoverServerSkills(agent.context, signal);
225
264
  return {
226
265
  commands: createSkillCommands(inventory, catalog, groups),
266
+ tools: [createSessionLoadSkillTool(agent)],
227
267
  services: [mountMcpSkills(mcpGroups, signal)],
228
- resources: [{
229
- name: "doompi/skills",
268
+ resources: [...listed.flatMap((group) => group.skills.map((skill) => ({
269
+ name: skill.name,
270
+ description: skill.description,
271
+ path: skill.filePath,
230
272
  kind: "skill",
231
- read: () => catalog
232
- }, ...[{
273
+ ...group.domain === void 0 ? {} : { when: {
274
+ domain: group.domain,
275
+ attribution: {
276
+ kind: "domain",
277
+ mode: group.domain
278
+ }
279
+ } },
280
+ read: () => (0, node_fs_promises.readFile)(skill.filePath, "utf8")
281
+ }))), ...[{
233
282
  name: "doompi-author-skill",
234
283
  description: "Author and distribute DoomPi agent skills. Use when creating a SKILL.md, contributing a runtime skill directory, or publishing activation-gated Help prompts and diagnostic tools."
235
284
  }, {
@@ -269,11 +318,22 @@ var skills_server_default$1 = (0, _agimon_ai_doompi_core_extensionFile.defineRou
269
318
  //#region src/extensions/workspaces/sessions/(backend)/resource/skills.server.ts
270
319
  var skills_server_default = (0, _agimon_ai_doompi_core_extensionFile.defineRoutedContribution)((context) => context.root.resources, { cardinality: "many" });
271
320
  //#endregion
321
+ //#region src/extensions/workspaces/sessions/(backend)/tool/load_skill.server.ts
322
+ /** The session agent's skill loader; the Pi CLI keeps Pi's own skill handling. */
323
+ var load_skill_server_default = (0, _agimon_ai_doompi_core_extensionFile.defineRoutedContribution)((context) => context.root.tools?.[0], { cardinality: "optional" });
324
+ //#endregion
272
325
  //#region generated/server.ts
326
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
273
327
  const many = (value, context) => {
274
328
  const resolved = typeof value === "function" ? value(context) : value;
275
329
  return Array.isArray(resolved) ? resolved : [resolved];
276
330
  };
331
+ /* v8 ignore next -- generated normalization branch */
332
+ const defined = (values) => values.filter((value) => value !== void 0);
333
+ const via = (identity, value) => value === void 0 ? void 0 : {
334
+ ...identity,
335
+ ...value
336
+ };
277
337
  const facet = (0, _agimon_ai_doompi_core_serverFacet.defineServerPlugin)({
278
338
  name: "@agimon-ai/doompi-skill",
279
339
  session: async (context) => {
@@ -290,6 +350,9 @@ const facet = (0, _agimon_ai_doompi_core_serverFacet.defineServerPlugin)({
290
350
  get resources() {
291
351
  return [...many(skills_server_default, contextSession)];
292
352
  },
353
+ get tools() {
354
+ return defined([via({ name: "load_skill" }, at(load_skill_server_default, contextSession))]);
355
+ },
293
356
  services: [...scopeSession.services ?? []],
294
357
  get activities() {
295
358
  return [...scopeSession.activities ?? []];
@@ -1 +1 @@
1
- {"version":3,"file":"server.cjs","names":["readFile","readFile","path","readHarnessState","os","listDomainNames","materializePluginEntries","resolvePluginEntries","collectResources","discoverSkillsAsync","resolveSharedSkills","DeferredSkillLoader","formatSkillsForPrompt","SKILL_COMMAND_PREFIX","SKILL_INVOCATION_PREFIX","expandDeferredSkillCommand","SKILLS_COMMAND","packageResourcePath","readPackageResource","defineRoot","defineRoutedContribution","defineRoutedContribution","defineServerPlugin","rootSession","composeRootHooks","commandSkills","resourceSkills"],"sources":["../../src/services/mcpSkills/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","../../generated/server.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\n\nimport type { DoomMcpPluginContext, DoomMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'doom/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: DoomMcpPluginContext): readonly DoomMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], 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, DoomMcpSkill>();\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 = await readFile(skill.filePath, { encoding: 'utf8', signal: readSignal });\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: DoomMcpPluginContext): readonly DoomMcpSkill[] {\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 { readFile } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport {\n listDomainNames,\n resolvePluginEntries,\n resolveSharedSkills,\n type PluginEntry,\n} from '@agimon-ai/doompi-config/domains';\nimport { readHarnessState } from '@agimon-ai/doompi-config/harnessState';\nimport type { DoomHeadlessExecutionContext } from '@agimon-ai/doompi-core/headless';\nimport { materializePluginEntries } from '@agimon-ai/doompi-domain/plugins';\nimport { collectResources, discoverSkillsAsync } from '@agimon-ai/doompi-domain/resources';\nimport { formatSkillsForPrompt, type Skill } from '@earendil-works/pi-coding-agent';\n\nimport { DeferredSkillLoader, type DeferredSkillSnapshot } from '../deferredSkills';\n\n/** Skills and the domain whose selection activates them, or always-on when absent. */\nexport interface ServerSkillGroup {\n readonly domain?: 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\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/doompi-author',\n '@agimon-ai/doompi-git',\n '@agimon-ai/doompi-goal',\n '@agimon-ai/doompi-help',\n '@agimon-ai/doompi-loop',\n '@agimon-ai/doompi-user-feedback',\n '@agimon-ai/doompi-voice',\n '@agimon-ai/doompi-workflow',\n]);\n\nasync function isMcpSkillPlugin(entry: PluginEntry): Promise<boolean> {\n if (entry.source?.type === 'npm') return !LOCAL_ONLY_MCP_PACKAGES.has(entry.source.package);\n try {\n const manifest = JSON.parse(await readFile(path.join(entry.directory, 'package.json'), 'utf8')) 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 (error) {\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 return (error as NodeJS.ErrnoException).code === 'ENOENT';\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 `.doom/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 facet: 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: DoomHeadlessExecutionContext,\n signal: AbortSignal,\n): Promise<ServerSkillInventory> {\n signal.throwIfAborted();\n const state = readHarnessState(execution.environment);\n const home = execution.environment.HOME ?? os.homedir();\n const repoSkills = path.join(execution.repoRoot, '.doom', '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(...listDomainNames(execution.repoRoot, 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(resolvePluginEntries(execution.repoRoot, [domain], [], home));\n const mcpPluginRoots = (\n await Promise.all(\n plugins.map(async (plugin) =>\n (await isMcpSkillPlugin(plugin)) ? 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 (resolveSharedSkills(execution.repoRoot, [domain], 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 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 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 catalog: formatSkillsForPrompt(activeSkills) || '(no discovered skills)',\n };\n}\n","import type { DoomHeadlessCommand } from '@agimon-ai/doompi-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): DoomHeadlessCommand {\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 execution.session.prompt(expanded);\n },\n };\n}\n\nexport function createSkillCommands(\n inventory: DeferredSkillSnapshot,\n catalog: string,\n groups: readonly ServerSkillGroup[],\n): readonly DoomHeadlessCommand[] {\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 execution.session.prompt(\n expandDeferredSkillCommand(`${SKILL_INVOCATION_PREFIX}${requested}`, inventory.skills),\n );\n return;\n }\n await execution.client.notify({\n title: 'DoomPi 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 type { DoomHeadlessHostService } from '@agimon-ai/doompi-core/headless';\nimport {\n type DoomServerSessionPlugin,\n packageResourcePath,\n readPackageResource,\n} from '@agimon-ai/doompi-core/serverFacet';\n\nimport { mountMcpSkills } from '../../../../../services/mcpSkills';\nimport { discoverServerSkills } from '../../../../../services/serverInventory';\nimport { createSkillCommands } from './skillCommands';\n\nexport async function createSkillServer(\n agent: DoomHeadlessHostService,\n signal: AbortSignal,\n): Promise<DoomServerSessionPlugin> {\n const { inventory, catalog, groups, mcpGroups } = await discoverServerSkills(agent.context, signal);\n return {\n commands: createSkillCommands(inventory, catalog, groups),\n services: [mountMcpSkills(mcpGroups, signal)],\n resources: [\n { name: 'doompi/skills', kind: 'skill', read: () => catalog },\n ...[\n {\n name: 'doompi-author-skill',\n description:\n 'Author and distribute DoomPi 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: 'doompi-use-skill',\n description:\n \"Use DoomPi'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/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nimport { createSkillServer } from './_lib/skillServer';\n\nexport default defineRoot(async ({ agent, signal }: DoomServerPluginContext) => {\n const runtime: DoomServerSessionPlugin = agent\n ? await createSkillServer(agent, signal)\n : { commands: [], resources: [] };\n return { value: runtime, services: runtime.services };\n});\n","import { defineRoutedContribution } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, DoomServerSessionPlugin>) => context.root.commands!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, DoomServerSessionPlugin>) => context.root.resources!,\n { cardinality: 'many' },\n);\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/doompi-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';\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 facet = defineServerPlugin({\n name: '@agimon-ai/doompi-skill',\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): DoomServerSessionPlugin['commands'] { return [...many(commandSkills, contextSession)]; },\n get resources(): DoomServerSessionPlugin['resources'] { return [...many(resourceSkills, contextSession)]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.cjs","names":["readFile","Type","readFile","path","readHarnessState","os","listDomainNames","materializePluginEntries","resolvePluginEntries","collectResources","discoverSkillsAsync","resolveSharedSkills","DeferredSkillLoader","formatSkillsForPrompt","SKILL_COMMAND_PREFIX","SKILL_INVOCATION_PREFIX","expandDeferredSkillCommand","SKILLS_COMMAND","readFile","packageResourcePath","readPackageResource","defineRoot","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineServerPlugin","rootSession","composeRootHooks","commandSkills","resourceSkills","toolLoadSkill"],"sources":["../../src/services/mcpSkills/index.ts","../../src/schemas/mcpSkillTools.ts","../../src/services/mcpSkillTools/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":["import { readFile } from 'node:fs/promises';\n\nimport type { DoomMcpPluginContext, DoomMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'doom/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: DoomMcpPluginContext): readonly DoomMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], 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, DoomMcpSkill>();\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 = await readFile(skill.filePath, { encoding: 'utf8', signal: readSignal });\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: DoomMcpPluginContext): readonly DoomMcpSkill[] {\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 { DoomHeadlessHostService, DoomHeadlessTool } from '@agimon-ai/doompi-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<DoomHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): DoomHeadlessTool<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 DoomPi\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<DoomHeadlessHostService, 'readSkill'>,\n): DoomHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (server session only, shown as a generic call)\n // DOOM_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(): DoomHeadlessTool<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 { readFile } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport {\n listDomainNames,\n resolvePluginEntries,\n resolveSharedSkills,\n type PluginEntry,\n} from '@agimon-ai/doompi-config/domains';\nimport { readHarnessState } from '@agimon-ai/doompi-config/harnessState';\nimport type { DoomHeadlessExecutionContext } from '@agimon-ai/doompi-core/headless';\nimport { materializePluginEntries } from '@agimon-ai/doompi-domain/plugins';\nimport { collectResources, discoverSkillsAsync } from '@agimon-ai/doompi-domain/resources';\nimport { formatSkillsForPrompt, type Skill } from '@earendil-works/pi-coding-agent';\n\nimport { DeferredSkillLoader, type DeferredSkillSnapshot } from '../deferredSkills';\n\n/** Skills and the domain whose selection activates them, or always-on when absent. */\nexport interface ServerSkillGroup {\n readonly domain?: 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 /**\n * The shared and repository skills this package advertises itself. Domain plugin skills are\n * left out: doompi-domain already contributes each one, and listing them twice was noise.\n */\n readonly listed: readonly ServerSkillGroup[];\n readonly catalog: string;\n}\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 doompi-domain. */\n readonly listed?: boolean;\n}\n\nconst LOCAL_ONLY_MCP_PACKAGES = new Set([\n '@agimon-ai/doompi-author',\n '@agimon-ai/doompi-git',\n '@agimon-ai/doompi-goal',\n '@agimon-ai/doompi-help',\n '@agimon-ai/doompi-loop',\n '@agimon-ai/doompi-user-feedback',\n '@agimon-ai/doompi-voice',\n '@agimon-ai/doompi-workflow',\n]);\n\nasync function isMcpSkillPlugin(entry: PluginEntry): Promise<boolean> {\n if (entry.source?.type === 'npm') return !LOCAL_ONLY_MCP_PACKAGES.has(entry.source.package);\n try {\n const manifest = JSON.parse(await readFile(path.join(entry.directory, 'package.json'), 'utf8')) 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 (error) {\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 return (error as NodeJS.ErrnoException).code === 'ENOENT';\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 `.doom/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 facet: 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: DoomHeadlessExecutionContext,\n signal: AbortSignal,\n): Promise<ServerSkillInventory> {\n signal.throwIfAborted();\n const state = readHarnessState(execution.environment);\n const home = execution.environment.HOME ?? os.homedir();\n const repoSkills = path.join(execution.repoRoot, '.doom', '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(...listDomainNames(execution.repoRoot, 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(resolvePluginEntries(execution.repoRoot, [domain], [], home));\n const mcpPluginRoots = (\n await Promise.all(\n plugins.map(async (plugin) =>\n (await isMcpSkillPlugin(plugin)) ? 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 (resolveSharedSkills(execution.repoRoot, [domain], 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 { DoomHeadlessCommand } from '@agimon-ai/doompi-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): DoomHeadlessCommand {\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 execution.session.prompt(expanded);\n },\n };\n}\n\nexport function createSkillCommands(\n inventory: DeferredSkillSnapshot,\n catalog: string,\n groups: readonly ServerSkillGroup[],\n): readonly DoomHeadlessCommand[] {\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 execution.session.prompt(\n expandDeferredSkillCommand(`${SKILL_INVOCATION_PREFIX}${requested}`, inventory.skills),\n );\n return;\n }\n await execution.client.notify({\n title: 'DoomPi 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 { readFile } from 'node:fs/promises';\n\nimport type { DoomHeadlessHostService } from '@agimon-ai/doompi-core/headless';\nimport {\n type DoomServerSessionPlugin,\n packageResourcePath,\n readPackageResource,\n} from '@agimon-ai/doompi-core/serverFacet';\n\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: DoomHeadlessHostService,\n signal: AbortSignal,\n): Promise<DoomServerSessionPlugin> {\n const { inventory, catalog, groups, mcpGroups, listed } = await discoverServerSkills(agent.context, signal);\n return {\n commands: createSkillCommands(inventory, catalog, groups),\n tools: [createSessionLoadSkillTool(agent)],\n services: [mountMcpSkills(mcpGroups, 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: () => readFile(skill.filePath, 'utf8'),\n })),\n ),\n ...[\n {\n name: 'doompi-author-skill',\n description:\n 'Author and distribute DoomPi 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: 'doompi-use-skill',\n description:\n \"Use DoomPi'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/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nimport { createSkillServer } from './_lib/skillServer';\n\nexport default defineRoot(async ({ agent, signal }: DoomServerPluginContext) => {\n const runtime: DoomServerSessionPlugin = agent\n ? await createSkillServer(agent, signal)\n : { commands: [], resources: [] };\n return { value: runtime, services: runtime.services };\n});\n","import { defineRoutedContribution } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, DoomServerSessionPlugin>) => context.root.commands!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, DoomServerSessionPlugin>) => context.root.resources!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\n/** The session agent's skill loader; the Pi CLI keeps Pi's own skill handling. */\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, DoomServerSessionPlugin>) => context.root.tools?.[0],\n { cardinality: 'optional' },\n);\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/doompi-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 facet = defineServerPlugin({\n name: '@agimon-ai/doompi-skill',\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): DoomServerSessionPlugin['commands'] { return [...many(commandSkills, contextSession)]; },\n get resources(): DoomServerSessionPlugin['resources'] { return [...many(resourceSkills, contextSession)]; },\n get tools(): DoomServerSessionPlugin['tools'] { return defined([via({ name: 'load_skill' }, at(toolLoadSkill, contextSession))]); },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";qBAca,oDAAK"}
1
+ {"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";qBAuBa,oDAAK"}
@@ -7,6 +7,7 @@ import { formatSkillsForPrompt } from "@earendil-works/pi-coding-agent";
7
7
  import { composeRootHooks, defineRoot, defineRoutedContribution } from "@agimon-ai/doompi-core/extensionFile";
8
8
  import { defineServerPlugin, packageResourcePath, readPackageResource } from "@agimon-ai/doompi-core/serverFacet";
9
9
  import { readFile } from "node:fs/promises";
10
+ import { Type } from "typebox";
10
11
  import os from "node:os";
11
12
  import { readHarnessState } from "@agimon-ai/doompi-config/harnessState";
12
13
  import { collectResources, discoverSkillsAsync } from "@agimon-ai/doompi-domain/resources";
@@ -44,6 +45,37 @@ function mountMcpSkills(groups, signal) {
44
45
  } });
45
46
  };
46
47
  }
48
+ Type.Object({ query: Type.Optional(Type.String()) });
49
+ const loadSkillParameters = Type.Object({ name: Type.String() });
50
+ //#endregion
51
+ //#region src/services/mcpSkillTools/index.ts
52
+ function text(text) {
53
+ return { content: [{
54
+ type: "text",
55
+ text
56
+ }] };
57
+ }
58
+ /**
59
+ * The session agent's way into a skill. Its skills are listed in the prompt, which DoomPi
60
+ * controls, so it needs no search; a remote agent's prompt is not ours, hence the pair above.
61
+ */
62
+ function createSessionLoadSkillTool(host) {
63
+ return {
64
+ name: "load_skill",
65
+ label: "Load skill",
66
+ description: "Load the full instructions of a skill from the available skills list by exact name.",
67
+ parameters: loadSkillParameters,
68
+ executionMode: "parallel",
69
+ async execute(_toolCallId, { name }) {
70
+ const body = host.readSkill(name);
71
+ if (body === void 0) return {
72
+ ...text(`No skill named ${name} is available in this session.`),
73
+ isError: true
74
+ };
75
+ return text(body);
76
+ }
77
+ };
78
+ }
47
79
  //#endregion
48
80
  //#region src/services/serverInventory/index.ts
49
81
  const LOCAL_ONLY_MCP_PACKAGES = /* @__PURE__ */ new Set([
@@ -137,12 +169,14 @@ async function discoverServerSkills(execution, signal) {
137
169
  for (const domain of domains) if (resolveSharedSkills(execution.repoRoot, [domain], home)) sources.push({
138
170
  domain,
139
171
  paths: sharedPaths,
140
- mcpPaths: sharedPaths
172
+ mcpPaths: sharedPaths,
173
+ listed: true
141
174
  });
142
175
  }
143
176
  sources.push({
144
177
  paths: [repoSkills],
145
- mcpPaths: [repoSkills]
178
+ mcpPaths: [repoSkills],
179
+ listed: true
146
180
  });
147
181
  signal.throwIfAborted();
148
182
  const unique = [.../* @__PURE__ */ new Set([...sources.flatMap((source) => source.paths), ...state.skillDirectories ?? []])];
@@ -155,6 +189,10 @@ async function discoverServerSkills(execution, signal) {
155
189
  ...source.domain === void 0 ? {} : { domain: source.domain },
156
190
  skills: inventory.skills.filter((skill) => within(skill.filePath, source.paths))
157
191
  })).filter((group) => group.skills.length > 0);
192
+ const listed = sources.filter((source) => source.listed === true).map((source) => ({
193
+ ...source.domain === void 0 ? {} : { domain: source.domain },
194
+ skills: inventory.skills.filter((skill) => within(skill.filePath, source.paths))
195
+ })).filter((group) => group.skills.length > 0);
158
196
  const mcpGroups = sources.map((source) => ({
159
197
  ...source.domain === void 0 ? {} : { domain: source.domain },
160
198
  skills: inventory.skills.filter((skill) => within(skill.filePath, source.mcpPaths))
@@ -167,6 +205,7 @@ async function discoverServerSkills(execution, signal) {
167
205
  },
168
206
  groups,
169
207
  mcpGroups,
208
+ listed,
170
209
  catalog: formatSkillsForPrompt(activeSkills) || "(no discovered skills)"
171
210
  };
172
211
  }
@@ -214,15 +253,25 @@ function createSkillCommands(inventory, catalog, groups) {
214
253
  //#endregion
215
254
  //#region src/extensions/workspaces/sessions/(backend)/_lib/skillServer.ts
216
255
  async function createSkillServer(agent, signal) {
217
- const { inventory, catalog, groups, mcpGroups } = await discoverServerSkills(agent.context, signal);
256
+ const { inventory, catalog, groups, mcpGroups, listed } = await discoverServerSkills(agent.context, signal);
218
257
  return {
219
258
  commands: createSkillCommands(inventory, catalog, groups),
259
+ tools: [createSessionLoadSkillTool(agent)],
220
260
  services: [mountMcpSkills(mcpGroups, signal)],
221
- resources: [{
222
- name: "doompi/skills",
261
+ resources: [...listed.flatMap((group) => group.skills.map((skill) => ({
262
+ name: skill.name,
263
+ description: skill.description,
264
+ path: skill.filePath,
223
265
  kind: "skill",
224
- read: () => catalog
225
- }, ...[{
266
+ ...group.domain === void 0 ? {} : { when: {
267
+ domain: group.domain,
268
+ attribution: {
269
+ kind: "domain",
270
+ mode: group.domain
271
+ }
272
+ } },
273
+ read: () => readFile(skill.filePath, "utf8")
274
+ }))), ...[{
226
275
  name: "doompi-author-skill",
227
276
  description: "Author and distribute DoomPi agent skills. Use when creating a SKILL.md, contributing a runtime skill directory, or publishing activation-gated Help prompts and diagnostic tools."
228
277
  }, {
@@ -262,11 +311,22 @@ var skills_server_default$1 = defineRoutedContribution((context) => context.root
262
311
  //#region src/extensions/workspaces/sessions/(backend)/resource/skills.server.ts
263
312
  var skills_server_default = defineRoutedContribution((context) => context.root.resources, { cardinality: "many" });
264
313
  //#endregion
314
+ //#region src/extensions/workspaces/sessions/(backend)/tool/load_skill.server.ts
315
+ /** The session agent's skill loader; the Pi CLI keeps Pi's own skill handling. */
316
+ var load_skill_server_default = defineRoutedContribution((context) => context.root.tools?.[0], { cardinality: "optional" });
317
+ //#endregion
265
318
  //#region generated/server.ts
319
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
266
320
  const many = (value, context) => {
267
321
  const resolved = typeof value === "function" ? value(context) : value;
268
322
  return Array.isArray(resolved) ? resolved : [resolved];
269
323
  };
324
+ /* v8 ignore next -- generated normalization branch */
325
+ const defined = (values) => values.filter((value) => value !== void 0);
326
+ const via = (identity, value) => value === void 0 ? void 0 : {
327
+ ...identity,
328
+ ...value
329
+ };
270
330
  const facet = defineServerPlugin({
271
331
  name: "@agimon-ai/doompi-skill",
272
332
  session: async (context) => {
@@ -283,6 +343,9 @@ const facet = defineServerPlugin({
283
343
  get resources() {
284
344
  return [...many(skills_server_default, contextSession)];
285
345
  },
346
+ get tools() {
347
+ return defined([via({ name: "load_skill" }, at(load_skill_server_default, contextSession))]);
348
+ },
286
349
  services: [...scopeSession.services ?? []],
287
350
  get activities() {
288
351
  return [...scopeSession.activities ?? []];
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","names":["rootSession","commandSkills","resourceSkills"],"sources":["../../src/services/mcpSkills/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","../../generated/server.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\n\nimport type { DoomMcpPluginContext, DoomMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'doom/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: DoomMcpPluginContext): readonly DoomMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], 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, DoomMcpSkill>();\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 = await readFile(skill.filePath, { encoding: 'utf8', signal: readSignal });\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: DoomMcpPluginContext): readonly DoomMcpSkill[] {\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 { readFile } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport {\n listDomainNames,\n resolvePluginEntries,\n resolveSharedSkills,\n type PluginEntry,\n} from '@agimon-ai/doompi-config/domains';\nimport { readHarnessState } from '@agimon-ai/doompi-config/harnessState';\nimport type { DoomHeadlessExecutionContext } from '@agimon-ai/doompi-core/headless';\nimport { materializePluginEntries } from '@agimon-ai/doompi-domain/plugins';\nimport { collectResources, discoverSkillsAsync } from '@agimon-ai/doompi-domain/resources';\nimport { formatSkillsForPrompt, type Skill } from '@earendil-works/pi-coding-agent';\n\nimport { DeferredSkillLoader, type DeferredSkillSnapshot } from '../deferredSkills';\n\n/** Skills and the domain whose selection activates them, or always-on when absent. */\nexport interface ServerSkillGroup {\n readonly domain?: 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\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/doompi-author',\n '@agimon-ai/doompi-git',\n '@agimon-ai/doompi-goal',\n '@agimon-ai/doompi-help',\n '@agimon-ai/doompi-loop',\n '@agimon-ai/doompi-user-feedback',\n '@agimon-ai/doompi-voice',\n '@agimon-ai/doompi-workflow',\n]);\n\nasync function isMcpSkillPlugin(entry: PluginEntry): Promise<boolean> {\n if (entry.source?.type === 'npm') return !LOCAL_ONLY_MCP_PACKAGES.has(entry.source.package);\n try {\n const manifest = JSON.parse(await readFile(path.join(entry.directory, 'package.json'), 'utf8')) 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 (error) {\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 return (error as NodeJS.ErrnoException).code === 'ENOENT';\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 `.doom/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 facet: 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: DoomHeadlessExecutionContext,\n signal: AbortSignal,\n): Promise<ServerSkillInventory> {\n signal.throwIfAborted();\n const state = readHarnessState(execution.environment);\n const home = execution.environment.HOME ?? os.homedir();\n const repoSkills = path.join(execution.repoRoot, '.doom', '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(...listDomainNames(execution.repoRoot, 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(resolvePluginEntries(execution.repoRoot, [domain], [], home));\n const mcpPluginRoots = (\n await Promise.all(\n plugins.map(async (plugin) =>\n (await isMcpSkillPlugin(plugin)) ? 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 (resolveSharedSkills(execution.repoRoot, [domain], 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 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 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 catalog: formatSkillsForPrompt(activeSkills) || '(no discovered skills)',\n };\n}\n","import type { DoomHeadlessCommand } from '@agimon-ai/doompi-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): DoomHeadlessCommand {\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 execution.session.prompt(expanded);\n },\n };\n}\n\nexport function createSkillCommands(\n inventory: DeferredSkillSnapshot,\n catalog: string,\n groups: readonly ServerSkillGroup[],\n): readonly DoomHeadlessCommand[] {\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 execution.session.prompt(\n expandDeferredSkillCommand(`${SKILL_INVOCATION_PREFIX}${requested}`, inventory.skills),\n );\n return;\n }\n await execution.client.notify({\n title: 'DoomPi 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 type { DoomHeadlessHostService } from '@agimon-ai/doompi-core/headless';\nimport {\n type DoomServerSessionPlugin,\n packageResourcePath,\n readPackageResource,\n} from '@agimon-ai/doompi-core/serverFacet';\n\nimport { mountMcpSkills } from '../../../../../services/mcpSkills';\nimport { discoverServerSkills } from '../../../../../services/serverInventory';\nimport { createSkillCommands } from './skillCommands';\n\nexport async function createSkillServer(\n agent: DoomHeadlessHostService,\n signal: AbortSignal,\n): Promise<DoomServerSessionPlugin> {\n const { inventory, catalog, groups, mcpGroups } = await discoverServerSkills(agent.context, signal);\n return {\n commands: createSkillCommands(inventory, catalog, groups),\n services: [mountMcpSkills(mcpGroups, signal)],\n resources: [\n { name: 'doompi/skills', kind: 'skill', read: () => catalog },\n ...[\n {\n name: 'doompi-author-skill',\n description:\n 'Author and distribute DoomPi 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: 'doompi-use-skill',\n description:\n \"Use DoomPi'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/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nimport { createSkillServer } from './_lib/skillServer';\n\nexport default defineRoot(async ({ agent, signal }: DoomServerPluginContext) => {\n const runtime: DoomServerSessionPlugin = agent\n ? await createSkillServer(agent, signal)\n : { commands: [], resources: [] };\n return { value: runtime, services: runtime.services };\n});\n","import { defineRoutedContribution } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, DoomServerSessionPlugin>) => context.root.commands!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, DoomServerSessionPlugin>) => context.root.resources!,\n { cardinality: 'many' },\n);\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/doompi-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';\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 facet = defineServerPlugin({\n name: '@agimon-ai/doompi-skill',\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): DoomServerSessionPlugin['commands'] { return [...many(commandSkills, contextSession)]; },\n get resources(): DoomServerSessionPlugin['resources'] { return [...many(resourceSkills, contextSession)]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.mjs","names":["rootSession","commandSkills","resourceSkills","toolLoadSkill"],"sources":["../../src/services/mcpSkills/index.ts","../../src/schemas/mcpSkillTools.ts","../../src/services/mcpSkillTools/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":["import { readFile } from 'node:fs/promises';\n\nimport type { DoomMcpPluginContext, DoomMcpSkill } from '@agimon-ai/doompi-core/mcpFacet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport type { ServerSkillGroup } from '../serverInventory';\n\nexport const MCP_SKILLS_SERVICE = 'doom/discovered-mcp-skills';\n\nexport interface McpSkillProvider {\n list(context: DoomMcpPluginContext): readonly DoomMcpSkill[];\n}\n\n/** Explicit remote provider, never a projection of the local resource registry. */\nexport function mountMcpSkills(groups: readonly ServerSkillGroup[], 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, DoomMcpSkill>();\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 = await readFile(skill.filePath, { encoding: 'utf8', signal: readSignal });\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: DoomMcpPluginContext): readonly DoomMcpSkill[] {\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 { DoomHeadlessHostService, DoomHeadlessTool } from '@agimon-ai/doompi-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<DoomHeadlessTool['execute']>[4]) {\n if (context.mcpSkills === undefined) throw new Error('Remote skill access is unavailable.');\n return context.mcpSkills;\n}\n\nexport function createSearchSkillsTool(): DoomHeadlessTool<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 DoomPi\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<DoomHeadlessHostService, 'readSkill'>,\n): DoomHeadlessTool<typeof loadSkillParameters> {\n return {\n // web-plugin-tool-renderers: ignore load_skill (server session only, shown as a generic call)\n // DOOM_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(): DoomHeadlessTool<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 { readFile } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport {\n listDomainNames,\n resolvePluginEntries,\n resolveSharedSkills,\n type PluginEntry,\n} from '@agimon-ai/doompi-config/domains';\nimport { readHarnessState } from '@agimon-ai/doompi-config/harnessState';\nimport type { DoomHeadlessExecutionContext } from '@agimon-ai/doompi-core/headless';\nimport { materializePluginEntries } from '@agimon-ai/doompi-domain/plugins';\nimport { collectResources, discoverSkillsAsync } from '@agimon-ai/doompi-domain/resources';\nimport { formatSkillsForPrompt, type Skill } from '@earendil-works/pi-coding-agent';\n\nimport { DeferredSkillLoader, type DeferredSkillSnapshot } from '../deferredSkills';\n\n/** Skills and the domain whose selection activates them, or always-on when absent. */\nexport interface ServerSkillGroup {\n readonly domain?: 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 /**\n * The shared and repository skills this package advertises itself. Domain plugin skills are\n * left out: doompi-domain already contributes each one, and listing them twice was noise.\n */\n readonly listed: readonly ServerSkillGroup[];\n readonly catalog: string;\n}\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 doompi-domain. */\n readonly listed?: boolean;\n}\n\nconst LOCAL_ONLY_MCP_PACKAGES = new Set([\n '@agimon-ai/doompi-author',\n '@agimon-ai/doompi-git',\n '@agimon-ai/doompi-goal',\n '@agimon-ai/doompi-help',\n '@agimon-ai/doompi-loop',\n '@agimon-ai/doompi-user-feedback',\n '@agimon-ai/doompi-voice',\n '@agimon-ai/doompi-workflow',\n]);\n\nasync function isMcpSkillPlugin(entry: PluginEntry): Promise<boolean> {\n if (entry.source?.type === 'npm') return !LOCAL_ONLY_MCP_PACKAGES.has(entry.source.package);\n try {\n const manifest = JSON.parse(await readFile(path.join(entry.directory, 'package.json'), 'utf8')) 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 (error) {\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 return (error as NodeJS.ErrnoException).code === 'ENOENT';\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 `.doom/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 facet: 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: DoomHeadlessExecutionContext,\n signal: AbortSignal,\n): Promise<ServerSkillInventory> {\n signal.throwIfAborted();\n const state = readHarnessState(execution.environment);\n const home = execution.environment.HOME ?? os.homedir();\n const repoSkills = path.join(execution.repoRoot, '.doom', '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(...listDomainNames(execution.repoRoot, 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(resolvePluginEntries(execution.repoRoot, [domain], [], home));\n const mcpPluginRoots = (\n await Promise.all(\n plugins.map(async (plugin) =>\n (await isMcpSkillPlugin(plugin)) ? 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 (resolveSharedSkills(execution.repoRoot, [domain], 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 { DoomHeadlessCommand } from '@agimon-ai/doompi-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): DoomHeadlessCommand {\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 execution.session.prompt(expanded);\n },\n };\n}\n\nexport function createSkillCommands(\n inventory: DeferredSkillSnapshot,\n catalog: string,\n groups: readonly ServerSkillGroup[],\n): readonly DoomHeadlessCommand[] {\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 execution.session.prompt(\n expandDeferredSkillCommand(`${SKILL_INVOCATION_PREFIX}${requested}`, inventory.skills),\n );\n return;\n }\n await execution.client.notify({\n title: 'DoomPi 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 { readFile } from 'node:fs/promises';\n\nimport type { DoomHeadlessHostService } from '@agimon-ai/doompi-core/headless';\nimport {\n type DoomServerSessionPlugin,\n packageResourcePath,\n readPackageResource,\n} from '@agimon-ai/doompi-core/serverFacet';\n\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: DoomHeadlessHostService,\n signal: AbortSignal,\n): Promise<DoomServerSessionPlugin> {\n const { inventory, catalog, groups, mcpGroups, listed } = await discoverServerSkills(agent.context, signal);\n return {\n commands: createSkillCommands(inventory, catalog, groups),\n tools: [createSessionLoadSkillTool(agent)],\n services: [mountMcpSkills(mcpGroups, 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: () => readFile(skill.filePath, 'utf8'),\n })),\n ),\n ...[\n {\n name: 'doompi-author-skill',\n description:\n 'Author and distribute DoomPi 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: 'doompi-use-skill',\n description:\n \"Use DoomPi'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/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nimport { createSkillServer } from './_lib/skillServer';\n\nexport default defineRoot(async ({ agent, signal }: DoomServerPluginContext) => {\n const runtime: DoomServerSessionPlugin = agent\n ? await createSkillServer(agent, signal)\n : { commands: [], resources: [] };\n return { value: runtime, services: runtime.services };\n});\n","import { defineRoutedContribution } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, DoomServerSessionPlugin>) => context.root.commands!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, DoomServerSessionPlugin>) => context.root.resources!,\n { cardinality: 'many' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\n/** The session agent's skill loader; the Pi CLI keeps Pi's own skill handling. */\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, DoomServerSessionPlugin>) => context.root.tools?.[0],\n { cardinality: 'optional' },\n);\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/doompi-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 facet = defineServerPlugin({\n name: '@agimon-ai/doompi-skill',\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): DoomServerSessionPlugin['commands'] { return [...many(commandSkills, contextSession)]; },\n get resources(): DoomServerSessionPlugin['resources'] { return [...many(resourceSkills, contextSession)]; },\n get tools(): DoomServerSessionPlugin['tools'] { return defined([via({ name: 'load_skill' }, at(toolLoadSkill, contextSession))]); },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-skill",
3
- "version": "0.0.1-alpha.70",
3
+ "version": "0.0.1-alpha.72",
4
4
  "description": "Session skill catalogue, deferred skill discovery, and the skill browser for DoomPi.",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -70,19 +70,19 @@
70
70
  "access": "public"
71
71
  },
72
72
  "dependencies": {
73
- "@agimon-ai/doompi-config": "0.0.1-alpha.91",
74
- "@agimon-ai/doompi-core": "0.0.1-alpha.92",
75
- "@agimon-ai/doompi-domain": "0.0.1-alpha.70",
76
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.83",
77
- "@agimon-ai/doompi-ui": "0.0.1-alpha.92",
78
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.50",
73
+ "@agimon-ai/doompi-config": "0.0.1-alpha.93",
74
+ "@agimon-ai/doompi-core": "0.0.1-alpha.94",
75
+ "@agimon-ai/doompi-domain": "0.0.1-alpha.72",
76
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.85",
77
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.94",
78
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.52",
79
79
  "@deepseek-ai/cordis": "4.0.2",
80
80
  "gpt-tokenizer": "4.0.0",
81
81
  "typebox": "1.3.34"
82
82
  },
83
83
  "devDependencies": {
84
- "@agimon-ai/doompi-build": "0.0.1-alpha.15",
85
- "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.81",
84
+ "@agimon-ai/doompi-build": "0.0.1-alpha.17",
85
+ "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.83",
86
86
  "@deepseek-ai/cordis": "4.0.2",
87
87
  "@earendil-works/pi-coding-agent": "0.87.1",
88
88
  "@earendil-works/pi-tui": "0.87.1",