@agimon-ai/doompi-skill 0.0.1-alpha.60 → 0.0.1-alpha.62

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.
@@ -97,10 +97,10 @@ const many = (value, context) => {
97
97
  const resolved = typeof value === "function" ? value(context) : value;
98
98
  return Array.isArray(resolved) ? resolved : [resolved];
99
99
  };
100
- const via = (identity, value) => ({
100
+ const via = (identity, value) => value === void 0 ? void 0 : {
101
101
  ...identity,
102
102
  ...value
103
- });
103
+ };
104
104
  const mcp = (0, _agimon_ai_doompi_core_mcpFacet.defineMcpPlugin)({
105
105
  name: "@agimon-ai/doompi-skill",
106
106
  session: (context) => ({
@@ -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 => ({ ...identity, ...(value as object) }) as T;\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 [via({ name: 'load_skill' }, at(toolLoadSkill, context)), 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 { 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\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 [via({ name: 'load_skill' }, at(toolLoadSkill, context)), 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.d.mts","names":[],"sources":["../../generated/mcp.ts"],"mappings":";qBAmBa,+CAAG"}
1
+ {"version":3,"file":"mcp.d.mts","names":[],"sources":["../../generated/mcp.ts"],"mappings":";qBAoBa,+CAAG"}
@@ -93,10 +93,10 @@ const many = (value, context) => {
93
93
  const resolved = typeof value === "function" ? value(context) : value;
94
94
  return Array.isArray(resolved) ? resolved : [resolved];
95
95
  };
96
- const via = (identity, value) => ({
96
+ const via = (identity, value) => value === void 0 ? void 0 : {
97
97
  ...identity,
98
98
  ...value
99
- });
99
+ };
100
100
  const mcp = defineMcpPlugin({
101
101
  name: "@agimon-ai/doompi-skill",
102
102
  session: (context) => ({
@@ -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 => ({ ...identity, ...(value as object) }) as T;\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 [via({ name: 'load_skill' }, at(toolLoadSkill, context)), 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 { 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\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 [via({ name: 'load_skill' }, at(toolLoadSkill, context)), via({ name: 'search_skills' }, at(toolSearchSkills, context))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
@@ -294,7 +294,17 @@ function createSkillRuntime({ pi, openOverlay }) {
294
294
  cordis.inject([_agimon_ai_doompi_core_help.DOOM_HELP_SERVICE], (helpContext) => {
295
295
  const help = (0, _agimon_ai_doompi_core_help.requireDoomHelpService)(helpContext);
296
296
  const unbind = helpSkillView.bind(help);
297
+ const unbindInventory = help.bindSkillInventory(async () => {
298
+ const deferred = await readiness.current();
299
+ if (!deferred) return void 0;
300
+ return helpSkillView.merge({
301
+ normalSkills: [],
302
+ normalSkillNames: loadedSkillCommandNames(pi),
303
+ deferredSkills: deferred.skills
304
+ }).helpSkills.map((skill) => skill.filePath);
305
+ });
297
306
  return () => {
307
+ unbindInventory();
298
308
  unbind();
299
309
  };
300
310
  });
@@ -1 +1 @@
1
- {"version":3,"file":"pi.cjs","names":["DOOM_HELP_MAX_DIAGNOSTIC_LENGTH","createSyntheticSourceInfo","DOOM_HELP_MAX_DIAGNOSTICS","SKILL_COMMAND_PREFIX","requireDoomConfigContext","SKILLS_COMMAND","requireDoomConfigContext","skillInvocation","DOOM_CONFIG_SERVICE","DOOM_CORDIS_SESSION_SERVICE","createDoomSkillSourcesService","DOOM_SKILL_SOURCES_SERVICE","DOOM_HELP_SERVICE","requireDoomHelpService","DOOM_UI_HUB_SERVICE","requireDoomUiHub","SKILLS_LEADER_CONTRIBUTION","requireHarnessRoot","requireDoomConfigContext","defineRoot","defineCliCommand","defineCliHook","defineCliHook","defineCliHook","defineResource","LEADER_SOURCE","definePiExtension","rootSession","composeRootHooks","commandSkills","hookBeforeAgentStart","hookInput","hookSessionStart","resourceHelp"],"sources":["../../src/services/helpSkills/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillReadiness.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillsCommand.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillRuntime.ts","../../src/extensions/workspaces/sessions/(backend)/root.cli.ts","../../src/extensions/workspaces/sessions/(backend)/command/skills.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/input.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.cli.ts","../../src/extensions/workspaces/sessions/(backend)/resource/help.cli.ts","../../generated/pi.ts"],"sourcesContent":["import {\n DOOM_HELP_MAX_DIAGNOSTIC_LENGTH,\n DOOM_HELP_MAX_DIAGNOSTICS,\n type DoomHelpService,\n type DoomHelpSkill,\n type DoomHelpSnapshot,\n} from '@agimon-ai/doompi-core/help';\nimport { createSyntheticSourceInfo, type Skill } from '@earendil-works/pi-coding-agent';\n\nexport interface SkillInventoryInput {\n normalSkills: readonly Skill[];\n deferredSkills: readonly Skill[];\n /** Names from Pi's command catalog when full normal Skill records are unavailable. */\n normalSkillNames?: readonly string[];\n}\n\nexport interface MergedSkillInventory {\n /** Pi-native, unique deferred, then accepted Help skills in invocation-precedence order. */\n skills: readonly Skill[];\n /** Deferred and Help skills that are absent from Pi's existing prompt inventory. */\n additionalSkills: readonly Skill[];\n /** Accepted Help skills, retained separately for the Help catalog group. */\n helpSkills: readonly Skill[];\n diagnostics: readonly string[];\n diagnosticKey: string;\n}\n\nexport interface ActiveHelpSkillView {\n bind(service: DoomHelpService): () => void;\n merge(input: SkillInventoryInput): MergedSkillInventory;\n dispose(): void;\n}\n\nfunction boundedDiagnostic(message: string): string {\n return message.slice(0, DOOM_HELP_MAX_DIAGNOSTIC_LENGTH);\n}\n\nfunction toSkill(skill: DoomHelpSkill): Skill {\n return {\n name: skill.name,\n description: skill.description,\n filePath: skill.filePath,\n baseDir: skill.baseDir,\n sourceInfo: createSyntheticSourceInfo(skill.filePath, {\n source: skill.source,\n scope: 'temporary',\n origin: 'package',\n baseDir: skill.baseDir,\n }),\n disableModelInvocation: false,\n };\n}\n\nfunction activeHelpSkills(snapshot: DoomHelpSnapshot | undefined): DoomHelpSkill[] {\n if (!snapshot || snapshot.activation === 'inactive') return [];\n return [...snapshot.skills].sort(\n (left, right) =>\n left.source.localeCompare(right.source) ||\n left.name.localeCompare(right.name) ||\n left.filePath.localeCompare(right.filePath),\n );\n}\n\nfunction snapshotDiagnostics(snapshot: DoomHelpSnapshot | undefined): string[] {\n if (!snapshot) return [];\n return snapshot.diagnostics.map((entry) =>\n boundedDiagnostic(`Help ${entry.source ?? 'host'} [${entry.code}]: ${entry.message}`),\n );\n}\n\nexport function mergeActiveHelpSkills(\n input: SkillInventoryInput,\n snapshot: DoomHelpSnapshot | undefined,\n clientDiagnostics: readonly string[] = [],\n): MergedSkillInventory {\n const nativeNames = new Set([...input.normalSkills.map((skill) => skill.name), ...(input.normalSkillNames ?? [])]);\n const deferredSkills = input.deferredSkills.filter((skill) => !nativeNames.has(skill.name));\n const occupiedNames = new Set([...nativeNames, ...deferredSkills.map((skill) => skill.name)]);\n const helpSkills: Skill[] = [];\n const diagnostics = [...snapshotDiagnostics(snapshot), ...clientDiagnostics.map(boundedDiagnostic)];\n\n for (const candidate of activeHelpSkills(snapshot)) {\n if (occupiedNames.has(candidate.name)) {\n diagnostics.push(\n boundedDiagnostic(\n `Help ${candidate.source} [HELP_SKILL_COLLISION]: '${candidate.name}' was ignored because a normal skill wins.`,\n ),\n );\n continue;\n }\n occupiedNames.add(candidate.name);\n helpSkills.push(toSkill(candidate));\n }\n\n const boundedDiagnostics = diagnostics.slice(0, DOOM_HELP_MAX_DIAGNOSTICS);\n const additionalSkills = [...deferredSkills, ...helpSkills];\n return {\n skills: [...input.normalSkills, ...additionalSkills],\n additionalSkills,\n helpSkills,\n diagnostics: boundedDiagnostics,\n diagnosticKey: `${snapshot?.hostGeneration ?? 'absent'}:${snapshot?.revision ?? 0}:${boundedDiagnostics.join('\\u0001')}`,\n };\n}\n\nexport function createActiveHelpSkillView(): ActiveHelpSkillView {\n let binding: { readonly token: symbol; snapshot: DoomHelpSnapshot; unsubscribe(): void } | undefined;\n let disposed = false;\n return {\n bind(service) {\n if (disposed) throw new Error('The Help skill view is disposed.');\n const token = Symbol(service.generation);\n const current = binding;\n binding = undefined;\n current?.unsubscribe();\n const next: { readonly token: symbol; snapshot: DoomHelpSnapshot; unsubscribe(): void } = {\n token,\n snapshot: service.getSnapshot(),\n unsubscribe: service.subscribeSnapshot((snapshot) => {\n if (binding?.token === token) binding.snapshot = snapshot;\n }),\n };\n binding = next;\n let bindingDisposed = false;\n return () => {\n if (bindingDisposed) return;\n bindingDisposed = true;\n next.unsubscribe();\n if (binding?.token === token) binding = undefined;\n };\n },\n merge(input) {\n return mergeActiveHelpSkills(input, binding?.snapshot);\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n const current = binding;\n binding = undefined;\n current?.unsubscribe();\n },\n };\n}\n","import { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { PiEventHandlers } from '@agimon-ai/doompi-core/piExtension';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext, Skill } from '@earendil-works/pi-coding-agent';\n\nimport type { DeferredSkillSnapshot } from '../../../../../services/deferredSkills';\nimport type { createActiveHelpSkillView } from '../../../../../services/helpSkills';\nimport { SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX } from '../../../../../types/skills';\n\nconst WARNING = 'warning';\n\ntype HelpSkillView = ReturnType<typeof createActiveHelpSkillView>;\n\ninterface SkillReadinessGeneration {\n readonly id: number;\n readonly ready: Promise<DeferredSkillSnapshot>;\n diagnosticsShown: boolean;\n}\n\nexport interface SkillReadiness {\n readonly events: PiEventHandlers;\n onStop(this: void): void;\n /** The snapshot for the live generation, or undefined once it is superseded. */\n current(): Promise<DeferredSkillSnapshot | undefined>;\n}\n\nfunction loadedSkillCommandNames(pi: ExtensionAPI): string[] {\n if (typeof pi.getCommands !== 'function') return [];\n return pi\n .getCommands()\n .filter((command) => command.source === 'skill' && command.name.startsWith(SKILL_COMMAND_PREFIX))\n .map((command) => command.name.slice(SKILL_COMMAND_PREFIX.length));\n}\n\nfunction failedSnapshot(cwd: string, error: unknown): DeferredSkillSnapshot {\n return {\n skills: [],\n diagnostics: [`${cwd}: ${error instanceof Error ? error.message : String(error)}`],\n };\n}\n\n/**\n * Discovery that starts with the session and is awaited only when something\n * needs it.\n *\n * Scanning every skill directory is the slowest thing this package does, so it\n * runs as a generation started on session_start. A reload starts a new one, and\n * a snapshot from a superseded generation is discarded rather than shown.\n */\nexport function createSkillReadiness(\n pi: ExtensionAPI,\n helpSkillView: HelpSkillView,\n loadDeferredSkills: () => Promise<typeof import('../../../../../services/deferredSkills')>,\n cordisContext: () => Context,\n): SkillReadiness {\n let generationSequence = 0;\n let currentGeneration: SkillReadinessGeneration | undefined;\n let shownHelpDiagnosticKey = '';\n\n const currentSnapshot = async (generation: SkillReadinessGeneration): Promise<DeferredSkillSnapshot | undefined> => {\n const snapshot = await generation.ready;\n return currentGeneration === generation ? snapshot : undefined;\n };\n\n return {\n events: {\n session_start: (_event, ctx) => {\n const state = requireDoomConfigContext(cordisContext()).harness;\n const id = ++generationSequence;\n const ready = loadDeferredSkills()\n .then(({ DeferredSkillLoader }) =>\n new DeferredSkillLoader({ cwd: ctx.cwd, skillPaths: state.skillDirectories }).start(),\n )\n .catch((error: unknown) => failedSnapshot(ctx.cwd, error));\n currentGeneration = { id, ready, diagnosticsShown: false };\n shownHelpDiagnosticKey = '';\n },\n input: async (event) => {\n if (!event.text.startsWith(SKILL_INVOCATION_PREFIX)) return { action: 'continue' };\n\n const generation = currentGeneration;\n if (!generation) return { action: 'continue' };\n const snapshot = await currentSnapshot(generation);\n if (!snapshot) return { action: 'continue' };\n\n const inventory = helpSkillView.merge({\n normalSkills: [],\n deferredSkills: snapshot.skills,\n normalSkillNames: loadedSkillCommandNames(pi),\n });\n const { expandDeferredSkillCommand } = await loadDeferredSkills();\n const text = expandDeferredSkillCommand(event.text, inventory.additionalSkills);\n return text === event.text ? { action: 'continue' } : { action: 'transform', text, images: event.images };\n },\n before_agent_start: async (event, ctx) => {\n const generation = currentGeneration;\n if (!generation) return undefined;\n const snapshot = await currentSnapshot(generation);\n if (!snapshot) return undefined;\n\n if (!generation.diagnosticsShown && snapshot.diagnostics.length > 0) {\n generation.diagnosticsShown = true;\n ctx.ui.notify(`Skill discovery:\\n${snapshot.diagnostics.join('\\n')}`, WARNING);\n }\n const inventory = helpSkillView.merge({\n normalSkills: event.systemPromptOptions.skills ?? [],\n deferredSkills: snapshot.skills,\n });\n if (inventory.diagnosticKey !== shownHelpDiagnosticKey) {\n shownHelpDiagnosticKey = inventory.diagnosticKey;\n if (inventory.diagnostics.length > 0) {\n ctx.ui.notify(`Package Help:\\n${inventory.diagnostics.join('\\n')}`, WARNING);\n }\n }\n if (inventory.additionalSkills.length === 0) return undefined;\n\n const { buildPromptWithDeferredSkills } = await loadDeferredSkills();\n return {\n systemPrompt: buildPromptWithDeferredSkills(event.systemPrompt, event.systemPromptOptions, [\n ...inventory.additionalSkills,\n ]),\n };\n },\n },\n onStop() {\n currentGeneration = undefined;\n },\n current: async () => (currentGeneration ? currentSnapshot(currentGeneration) : undefined),\n };\n}\n\n/** The merged skill inventory the /skills catalogue is built from. */\nexport function skillInventory(helpSkillView: HelpSkillView, readiness: SkillReadiness) {\n return async (ctx: ExtensionContext) => {\n const snapshot = await readiness.current();\n const inventory = helpSkillView.merge({\n // Present on the runtime context; absent from the published ExtensionContext type.\n normalSkills:\n (ctx as unknown as { getSystemPromptOptions(): { skills?: Skill[] } }).getSystemPromptOptions().skills ?? [],\n deferredSkills: snapshot?.skills ?? [],\n });\n return { helpSkills: inventory.helpSkills, diagnostics: inventory.diagnostics };\n };\n}\n","import { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport type { SkillCatalog, SkillCatalogOptions } from '../../../../../services/skillCatalog';\nimport { skillInvocation } from '../../../../../services/skillText';\nimport type { SkillsOverlayResult } from '../../../../../tui/skillsOverlay';\nimport { SKILLS_COMMAND } from '../../../../../types/skills';\n\nconst INFO = 'info';\nconst ERROR = 'error';\n\nexport interface SkillsCommandDependencies {\n readonly cordisContext: () => Context;\n /** Skill directories every loaded extension has contributed this session. */\n readonly extensionSources: () => readonly { source: string; directories: readonly string[] }[];\n /** Merged view of normal, deferred and package Help skills. */\n readonly inventory: (ctx: ExtensionContext) => Promise<{\n readonly helpSkills: SkillCatalogOptions['helpSkills'];\n readonly diagnostics: readonly string[];\n }>;\n readonly repositoryRoot: (ctx: ExtensionContext) => Promise<string>;\n readonly buildCatalog: (request: SkillCatalogOptions) => Promise<SkillCatalog>;\n readonly openOverlay: (\n ctx: ExtensionContext,\n options: { catalog: SkillCatalog; repoRoot: string },\n ) => Promise<SkillsOverlayResult | undefined>;\n}\n\nexport function createSkillsCommand(\n dependencies: SkillsCommandDependencies,\n): Parameters<ExtensionAPI['registerCommand']> {\n return [\n SKILLS_COMMAND,\n {\n description: 'Browse the skills this session loaded, and what other domains would add',\n handler: async (_args, ctx) => {\n if (!ctx.hasUI || ctx.mode !== 'tui') {\n ctx.ui.notify('/skills requires interactive mode', ERROR);\n return;\n }\n\n const state = requireDoomConfigContext(dependencies.cordisContext()).harness;\n const [repoRoot, inventory] = await Promise.all([\n dependencies.repositoryRoot(ctx),\n dependencies.inventory(ctx),\n ]);\n const catalog = await dependencies.buildCatalog({\n repoRoot,\n activeSkillDirectories: state.skillDirectories,\n extensionSources: dependencies.extensionSources(),\n helpSkills: inventory.helpSkills,\n helpDiagnostics: [...inventory.diagnostics],\n });\n\n const result = await dependencies.openOverlay(ctx, { catalog, repoRoot });\n if (!result) return;\n ctx.ui.setEditorText(skillInvocation(result.skill.name));\n ctx.ui.notify(`Loaded ${result.skill.name} into the prompt. Press enter to run it.`, INFO);\n },\n },\n ];\n}\n","import { requireDoomConfigContext } from '@agimon-ai/doompi-config';\nimport { requireHarnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport { DOOM_CONFIG_SERVICE } from '@agimon-ai/doompi-core/config';\nimport { DOOM_CORDIS_SESSION_SERVICE, type DoomCordisSessionService } from '@agimon-ai/doompi-core/cordisHost';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-core/help';\nimport { type PiPluginContributions } from '@agimon-ai/doompi-core/piExtension';\nimport {\n createDoomSkillSourcesService,\n DOOM_SKILL_SOURCES_SERVICE,\n type DoomSkillSourcesService,\n} from '@agimon-ai/doompi-core/skills';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-core/uiHub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport { createActiveHelpSkillView } from '../../../../../services/helpSkills';\nimport { SKILLS_LEADER_CONTRIBUTION } from '../../../../../types/skills';\nimport { createSkillReadiness, skillInventory } from './skillReadiness';\nimport { createSkillsCommand, type SkillsCommandDependencies } from './skillsCommand';\n\n/**\n * The catalogue and the overlay load on first use.\n *\n * Discovery has to start with the session, but building the catalogue and\n * rendering it are only reached by `/skills`, so they stay off the startup path.\n */\nfunction lazyModules() {\n let catalog: Promise<typeof import('../../../../../services/skillCatalog')> | undefined;\n let deferred: Promise<typeof import('../../../../../services/deferredSkills')> | undefined;\n return {\n catalog: () => (catalog ??= import('../../../../../services/skillCatalog')),\n deferred: () => (deferred ??= import('../../../../../services/deferredSkills')),\n };\n}\n\ninterface SkillPluginConfig {\n readonly pi: ExtensionAPI;\n readonly openOverlay: SkillsCommandDependencies['openOverlay'];\n}\n\nexport function createSkillRuntime({ pi, openOverlay }: SkillPluginConfig): PiPluginContributions {\n const load = lazyModules();\n let activeContext: Context | undefined;\n let activeSources: DoomSkillSourcesService | undefined;\n const bindConfig = (cordis: Context) => {\n cordis.inject([DOOM_CONFIG_SERVICE], (context) => {\n activeContext = context;\n return () => {\n if (activeContext === context) activeContext = undefined;\n };\n });\n };\n const requireRuntimeContext = (): Context => {\n if (!activeContext) throw new Error('Doom skill runtime is waiting for the session config service.');\n return activeContext;\n };\n const requireSkillSources = (): DoomSkillSourcesService => {\n if (!activeSources) throw new Error('Doom skill sources are waiting for the active session service.');\n return activeSources;\n };\n const bindSources = (cordis: Context) => {\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const service = createDoomSkillSourcesService(`${session.generation}:skill-sources`);\n sessionContext.plugin((providerContext) => {\n providerContext.provide(DOOM_SKILL_SOURCES_SERVICE, service);\n });\n activeSources = service;\n return () => {\n if (activeSources === service) activeSources = undefined;\n service.dispose();\n };\n });\n };\n const helpSkillView = createActiveHelpSkillView();\n\n const bindHelp = (cordis: Context) => {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const help = requireDoomHelpService(helpContext);\n const unbind = helpSkillView.bind(help);\n return () => {\n unbind();\n };\n });\n };\n const bindUi = (cordis: Context) => {\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = requireDoomUiHub(uiContext).registerLeader(SKILLS_LEADER_CONTRIBUTION);\n return () => contribution.dispose();\n });\n };\n const readiness = createSkillReadiness(pi, helpSkillView, load.deferred, requireRuntimeContext);\n return {\n services: [bindConfig, bindSources, bindHelp, bindUi],\n commands: [\n createSkillsCommand({\n cordisContext: requireRuntimeContext,\n extensionSources: () => requireSkillSources().list(),\n inventory: async (ctx) => {\n return skillInventory(helpSkillView, readiness)(ctx);\n },\n repositoryRoot: async (_ctx: ExtensionContext) =>\n requireHarnessRoot(requireDoomConfigContext(requireRuntimeContext()).harness),\n buildCatalog: async (request) => (await load.catalog()).buildSkillCatalog(request),\n openOverlay,\n }),\n ],\n events: readiness.events,\n onStop: readiness.onStop,\n onDispose() {\n helpSkillView.dispose();\n },\n };\n}\n","import { defineRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from './_lib/skillRuntime';\nexport default defineRoot(({ pi }: PiPluginContext) => {\n const runtime = createSkillRuntime({\n pi,\n openOverlay: async (context, options) =>\n (await import('../../../../tui/skillsOverlay')).openSkillsOverlay(context, options),\n });\n return { value: runtime, services: runtime.services, onStop: runtime.onStop, onDispose: runtime.onDispose };\n});\n","import { defineCliCommand } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliCommand(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) => context.root.commands![0]!,\n);\n","import { defineCliHook } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliHook(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) =>\n context.root.events!.before_agent_start,\n);\n","import { defineCliHook } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliHook(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) => context.root.events!.input,\n);\n","import { defineCliHook } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliHook(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) =>\n context.root.events!.session_start,\n);\n","import { defineResource } from '@agimon-ai/doompi-core/extensionFile';\n\nimport { LEADER_SOURCE } from '../../../../../types/skills';\nexport default defineResource({\n source: LEADER_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\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 },\n {\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 },\n ],\n});\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { definePiExtension, type PiPluginContext, type PiPluginContributions } from '@agimon-ai/doompi-core/piExtension';\nimport { composeRootHooks } from '@agimon-ai/doompi-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.cli';\nimport commandSkills from '../src/extensions/workspaces/sessions/(backend)/command/skills.cli';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.cli';\nimport hookInput from '../src/extensions/workspaces/sessions/(backend)/hook/input.cli';\nimport hookSessionStart from '../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.cli';\nimport resourceHelp from '../src/extensions/workspaces/sessions/(backend)/resource/help.cli';\n\ntype OptionsOf<T> = T extends (context: PiPluginContext<infer Options>) => unknown ? Options : never;\ntype InferredOptions = Exclude<OptionsOf<typeof rootSession> | OptionsOf<typeof commandSkills> | OptionsOf<typeof hookBeforeAgentStart> | OptionsOf<typeof hookInput> | OptionsOf<typeof hookSessionStart> | OptionsOf<typeof resourceHelp>, undefined>;\ntype Intersect<Union> =\n (Union extends unknown ? (value: Union) => void : never) extends (value: infer Value) => void ? Value : never;\ntype ExtensionOptions = [InferredOptions] extends [never] ? undefined : Intersect<InferredOptions>;\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;\n\nexport const extension = definePiExtension<ExtensionOptions>('@agimon-ai/doompi-skill', async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): PiPluginContributions['commands'] { return [at(commandSkills, contextSession)]; },\n events: {\n before_agent_start: at(hookBeforeAgentStart, contextSession) as NonNullable<PiPluginContributions['events']>['before_agent_start'],\n input: at(hookInput, contextSession) as NonNullable<PiPluginContributions['events']>['input'],\n session_start: at(hookSessionStart, contextSession) as NonNullable<PiPluginContributions['events']>['session_start'],\n },\n get resources(): PiPluginContributions['resources'] { return [at(resourceHelp, contextSession)]; },\n services: [...(scopeSession.services ?? [])],\n };\n});\n\nexport default extension;\n"],"mappings":"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"}
1
+ {"version":3,"file":"pi.cjs","names":["DOOM_HELP_MAX_DIAGNOSTIC_LENGTH","createSyntheticSourceInfo","DOOM_HELP_MAX_DIAGNOSTICS","SKILL_COMMAND_PREFIX","requireDoomConfigContext","SKILLS_COMMAND","requireDoomConfigContext","skillInvocation","DOOM_CONFIG_SERVICE","DOOM_CORDIS_SESSION_SERVICE","createDoomSkillSourcesService","DOOM_SKILL_SOURCES_SERVICE","DOOM_HELP_SERVICE","requireDoomHelpService","DOOM_UI_HUB_SERVICE","requireDoomUiHub","SKILLS_LEADER_CONTRIBUTION","requireHarnessRoot","requireDoomConfigContext","defineRoot","defineCliCommand","defineCliHook","defineCliHook","defineCliHook","defineResource","LEADER_SOURCE","definePiExtension","rootSession","composeRootHooks","commandSkills","hookBeforeAgentStart","hookInput","hookSessionStart","resourceHelp"],"sources":["../../src/services/helpSkills/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillReadiness.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillsCommand.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillRuntime.ts","../../src/extensions/workspaces/sessions/(backend)/root.cli.ts","../../src/extensions/workspaces/sessions/(backend)/command/skills.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/input.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.cli.ts","../../src/extensions/workspaces/sessions/(backend)/resource/help.cli.ts","../../generated/pi.ts"],"sourcesContent":["import {\n DOOM_HELP_MAX_DIAGNOSTIC_LENGTH,\n DOOM_HELP_MAX_DIAGNOSTICS,\n type DoomHelpService,\n type DoomHelpSkill,\n type DoomHelpSnapshot,\n} from '@agimon-ai/doompi-core/help';\nimport { createSyntheticSourceInfo, type Skill } from '@earendil-works/pi-coding-agent';\n\nexport interface SkillInventoryInput {\n normalSkills: readonly Skill[];\n deferredSkills: readonly Skill[];\n /** Names from Pi's command catalog when full normal Skill records are unavailable. */\n normalSkillNames?: readonly string[];\n}\n\nexport interface MergedSkillInventory {\n /** Pi-native, unique deferred, then accepted Help skills in invocation-precedence order. */\n skills: readonly Skill[];\n /** Deferred and Help skills that are absent from Pi's existing prompt inventory. */\n additionalSkills: readonly Skill[];\n /** Accepted Help skills, retained separately for the Help catalog group. */\n helpSkills: readonly Skill[];\n diagnostics: readonly string[];\n diagnosticKey: string;\n}\n\nexport interface ActiveHelpSkillView {\n bind(service: DoomHelpService): () => void;\n merge(input: SkillInventoryInput): MergedSkillInventory;\n dispose(): void;\n}\n\nfunction boundedDiagnostic(message: string): string {\n return message.slice(0, DOOM_HELP_MAX_DIAGNOSTIC_LENGTH);\n}\n\nfunction toSkill(skill: DoomHelpSkill): Skill {\n return {\n name: skill.name,\n description: skill.description,\n filePath: skill.filePath,\n baseDir: skill.baseDir,\n sourceInfo: createSyntheticSourceInfo(skill.filePath, {\n source: skill.source,\n scope: 'temporary',\n origin: 'package',\n baseDir: skill.baseDir,\n }),\n disableModelInvocation: false,\n };\n}\n\nfunction activeHelpSkills(snapshot: DoomHelpSnapshot | undefined): DoomHelpSkill[] {\n if (!snapshot || snapshot.activation === 'inactive') return [];\n return [...snapshot.skills].sort(\n (left, right) =>\n left.source.localeCompare(right.source) ||\n left.name.localeCompare(right.name) ||\n left.filePath.localeCompare(right.filePath),\n );\n}\n\nfunction snapshotDiagnostics(snapshot: DoomHelpSnapshot | undefined): string[] {\n if (!snapshot) return [];\n return snapshot.diagnostics.map((entry) =>\n boundedDiagnostic(`Help ${entry.source ?? 'host'} [${entry.code}]: ${entry.message}`),\n );\n}\n\nexport function mergeActiveHelpSkills(\n input: SkillInventoryInput,\n snapshot: DoomHelpSnapshot | undefined,\n clientDiagnostics: readonly string[] = [],\n): MergedSkillInventory {\n const nativeNames = new Set([...input.normalSkills.map((skill) => skill.name), ...(input.normalSkillNames ?? [])]);\n const deferredSkills = input.deferredSkills.filter((skill) => !nativeNames.has(skill.name));\n const occupiedNames = new Set([...nativeNames, ...deferredSkills.map((skill) => skill.name)]);\n const helpSkills: Skill[] = [];\n const diagnostics = [...snapshotDiagnostics(snapshot), ...clientDiagnostics.map(boundedDiagnostic)];\n\n for (const candidate of activeHelpSkills(snapshot)) {\n if (occupiedNames.has(candidate.name)) {\n diagnostics.push(\n boundedDiagnostic(\n `Help ${candidate.source} [HELP_SKILL_COLLISION]: '${candidate.name}' was ignored because a normal skill wins.`,\n ),\n );\n continue;\n }\n occupiedNames.add(candidate.name);\n helpSkills.push(toSkill(candidate));\n }\n\n const boundedDiagnostics = diagnostics.slice(0, DOOM_HELP_MAX_DIAGNOSTICS);\n const additionalSkills = [...deferredSkills, ...helpSkills];\n return {\n skills: [...input.normalSkills, ...additionalSkills],\n additionalSkills,\n helpSkills,\n diagnostics: boundedDiagnostics,\n diagnosticKey: `${snapshot?.hostGeneration ?? 'absent'}:${snapshot?.revision ?? 0}:${boundedDiagnostics.join('\\u0001')}`,\n };\n}\n\nexport function createActiveHelpSkillView(): ActiveHelpSkillView {\n let binding: { readonly token: symbol; snapshot: DoomHelpSnapshot; unsubscribe(): void } | undefined;\n let disposed = false;\n return {\n bind(service) {\n if (disposed) throw new Error('The Help skill view is disposed.');\n const token = Symbol(service.generation);\n const current = binding;\n binding = undefined;\n current?.unsubscribe();\n const next: { readonly token: symbol; snapshot: DoomHelpSnapshot; unsubscribe(): void } = {\n token,\n snapshot: service.getSnapshot(),\n unsubscribe: service.subscribeSnapshot((snapshot) => {\n if (binding?.token === token) binding.snapshot = snapshot;\n }),\n };\n binding = next;\n let bindingDisposed = false;\n return () => {\n if (bindingDisposed) return;\n bindingDisposed = true;\n next.unsubscribe();\n if (binding?.token === token) binding = undefined;\n };\n },\n merge(input) {\n return mergeActiveHelpSkills(input, binding?.snapshot);\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n const current = binding;\n binding = undefined;\n current?.unsubscribe();\n },\n };\n}\n","import { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { PiEventHandlers } from '@agimon-ai/doompi-core/piExtension';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext, Skill } from '@earendil-works/pi-coding-agent';\n\nimport type { DeferredSkillSnapshot } from '../../../../../services/deferredSkills';\nimport type { createActiveHelpSkillView } from '../../../../../services/helpSkills';\nimport { SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX } from '../../../../../types/skills';\n\nconst WARNING = 'warning';\n\ntype HelpSkillView = ReturnType<typeof createActiveHelpSkillView>;\n\ninterface SkillReadinessGeneration {\n readonly id: number;\n readonly ready: Promise<DeferredSkillSnapshot>;\n diagnosticsShown: boolean;\n}\n\nexport interface SkillReadiness {\n readonly events: PiEventHandlers;\n onStop(this: void): void;\n /** The snapshot for the live generation, or undefined once it is superseded. */\n current(): Promise<DeferredSkillSnapshot | undefined>;\n}\n\nexport function loadedSkillCommandNames(pi: ExtensionAPI): string[] {\n if (typeof pi.getCommands !== 'function') return [];\n return pi\n .getCommands()\n .filter((command) => command.source === 'skill' && command.name.startsWith(SKILL_COMMAND_PREFIX))\n .map((command) => command.name.slice(SKILL_COMMAND_PREFIX.length));\n}\n\nfunction failedSnapshot(cwd: string, error: unknown): DeferredSkillSnapshot {\n return {\n skills: [],\n diagnostics: [`${cwd}: ${error instanceof Error ? error.message : String(error)}`],\n };\n}\n\n/**\n * Discovery that starts with the session and is awaited only when something\n * needs it.\n *\n * Scanning every skill directory is the slowest thing this package does, so it\n * runs as a generation started on session_start. A reload starts a new one, and\n * a snapshot from a superseded generation is discarded rather than shown.\n */\nexport function createSkillReadiness(\n pi: ExtensionAPI,\n helpSkillView: HelpSkillView,\n loadDeferredSkills: () => Promise<typeof import('../../../../../services/deferredSkills')>,\n cordisContext: () => Context,\n): SkillReadiness {\n let generationSequence = 0;\n let currentGeneration: SkillReadinessGeneration | undefined;\n let shownHelpDiagnosticKey = '';\n\n const currentSnapshot = async (generation: SkillReadinessGeneration): Promise<DeferredSkillSnapshot | undefined> => {\n const snapshot = await generation.ready;\n return currentGeneration === generation ? snapshot : undefined;\n };\n\n return {\n events: {\n session_start: (_event, ctx) => {\n const state = requireDoomConfigContext(cordisContext()).harness;\n const id = ++generationSequence;\n const ready = loadDeferredSkills()\n .then(({ DeferredSkillLoader }) =>\n new DeferredSkillLoader({ cwd: ctx.cwd, skillPaths: state.skillDirectories }).start(),\n )\n .catch((error: unknown) => failedSnapshot(ctx.cwd, error));\n currentGeneration = { id, ready, diagnosticsShown: false };\n shownHelpDiagnosticKey = '';\n },\n input: async (event) => {\n if (!event.text.startsWith(SKILL_INVOCATION_PREFIX)) return { action: 'continue' };\n\n const generation = currentGeneration;\n if (!generation) return { action: 'continue' };\n const snapshot = await currentSnapshot(generation);\n if (!snapshot) return { action: 'continue' };\n\n const inventory = helpSkillView.merge({\n normalSkills: [],\n deferredSkills: snapshot.skills,\n normalSkillNames: loadedSkillCommandNames(pi),\n });\n const { expandDeferredSkillCommand } = await loadDeferredSkills();\n const text = expandDeferredSkillCommand(event.text, inventory.additionalSkills);\n return text === event.text ? { action: 'continue' } : { action: 'transform', text, images: event.images };\n },\n before_agent_start: async (event, ctx) => {\n const generation = currentGeneration;\n if (!generation) return undefined;\n const snapshot = await currentSnapshot(generation);\n if (!snapshot) return undefined;\n\n if (!generation.diagnosticsShown && snapshot.diagnostics.length > 0) {\n generation.diagnosticsShown = true;\n ctx.ui.notify(`Skill discovery:\\n${snapshot.diagnostics.join('\\n')}`, WARNING);\n }\n const inventory = helpSkillView.merge({\n normalSkills: event.systemPromptOptions.skills ?? [],\n deferredSkills: snapshot.skills,\n });\n if (inventory.diagnosticKey !== shownHelpDiagnosticKey) {\n shownHelpDiagnosticKey = inventory.diagnosticKey;\n if (inventory.diagnostics.length > 0) {\n ctx.ui.notify(`Package Help:\\n${inventory.diagnostics.join('\\n')}`, WARNING);\n }\n }\n if (inventory.additionalSkills.length === 0) return undefined;\n\n const { buildPromptWithDeferredSkills } = await loadDeferredSkills();\n return {\n systemPrompt: buildPromptWithDeferredSkills(event.systemPrompt, event.systemPromptOptions, [\n ...inventory.additionalSkills,\n ]),\n };\n },\n },\n onStop() {\n currentGeneration = undefined;\n },\n current: async () => (currentGeneration ? currentSnapshot(currentGeneration) : undefined),\n };\n}\n\n/** The merged skill inventory the /skills catalogue is built from. */\nexport function skillInventory(helpSkillView: HelpSkillView, readiness: SkillReadiness) {\n return async (ctx: ExtensionContext) => {\n const snapshot = await readiness.current();\n const inventory = helpSkillView.merge({\n // Present on the runtime context; absent from the published ExtensionContext type.\n normalSkills:\n (ctx as unknown as { getSystemPromptOptions(): { skills?: Skill[] } }).getSystemPromptOptions().skills ?? [],\n deferredSkills: snapshot?.skills ?? [],\n });\n return { helpSkills: inventory.helpSkills, diagnostics: inventory.diagnostics };\n };\n}\n","import { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport type { SkillCatalog, SkillCatalogOptions } from '../../../../../services/skillCatalog';\nimport { skillInvocation } from '../../../../../services/skillText';\nimport type { SkillsOverlayResult } from '../../../../../tui/skillsOverlay';\nimport { SKILLS_COMMAND } from '../../../../../types/skills';\n\nconst INFO = 'info';\nconst ERROR = 'error';\n\nexport interface SkillsCommandDependencies {\n readonly cordisContext: () => Context;\n /** Skill directories every loaded extension has contributed this session. */\n readonly extensionSources: () => readonly { source: string; directories: readonly string[] }[];\n /** Merged view of normal, deferred and package Help skills. */\n readonly inventory: (ctx: ExtensionContext) => Promise<{\n readonly helpSkills: SkillCatalogOptions['helpSkills'];\n readonly diagnostics: readonly string[];\n }>;\n readonly repositoryRoot: (ctx: ExtensionContext) => Promise<string>;\n readonly buildCatalog: (request: SkillCatalogOptions) => Promise<SkillCatalog>;\n readonly openOverlay: (\n ctx: ExtensionContext,\n options: { catalog: SkillCatalog; repoRoot: string },\n ) => Promise<SkillsOverlayResult | undefined>;\n}\n\nexport function createSkillsCommand(\n dependencies: SkillsCommandDependencies,\n): Parameters<ExtensionAPI['registerCommand']> {\n return [\n SKILLS_COMMAND,\n {\n description: 'Browse the skills this session loaded, and what other domains would add',\n handler: async (_args, ctx) => {\n if (!ctx.hasUI || ctx.mode !== 'tui') {\n ctx.ui.notify('/skills requires interactive mode', ERROR);\n return;\n }\n\n const state = requireDoomConfigContext(dependencies.cordisContext()).harness;\n const [repoRoot, inventory] = await Promise.all([\n dependencies.repositoryRoot(ctx),\n dependencies.inventory(ctx),\n ]);\n const catalog = await dependencies.buildCatalog({\n repoRoot,\n activeSkillDirectories: state.skillDirectories,\n extensionSources: dependencies.extensionSources(),\n helpSkills: inventory.helpSkills,\n helpDiagnostics: [...inventory.diagnostics],\n });\n\n const result = await dependencies.openOverlay(ctx, { catalog, repoRoot });\n if (!result) return;\n ctx.ui.setEditorText(skillInvocation(result.skill.name));\n ctx.ui.notify(`Loaded ${result.skill.name} into the prompt. Press enter to run it.`, INFO);\n },\n },\n ];\n}\n","import { requireDoomConfigContext } from '@agimon-ai/doompi-config';\nimport { requireHarnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport { DOOM_CONFIG_SERVICE } from '@agimon-ai/doompi-core/config';\nimport { DOOM_CORDIS_SESSION_SERVICE, type DoomCordisSessionService } from '@agimon-ai/doompi-core/cordisHost';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-core/help';\nimport { type PiPluginContributions } from '@agimon-ai/doompi-core/piExtension';\nimport {\n createDoomSkillSourcesService,\n DOOM_SKILL_SOURCES_SERVICE,\n type DoomSkillSourcesService,\n} from '@agimon-ai/doompi-core/skills';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-core/uiHub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport { createActiveHelpSkillView } from '../../../../../services/helpSkills';\nimport { SKILLS_LEADER_CONTRIBUTION } from '../../../../../types/skills';\nimport { createSkillReadiness, loadedSkillCommandNames, skillInventory } from './skillReadiness';\nimport { createSkillsCommand, type SkillsCommandDependencies } from './skillsCommand';\n\n/**\n * The catalogue and the overlay load on first use.\n *\n * Discovery has to start with the session, but building the catalogue and\n * rendering it are only reached by `/skills`, so they stay off the startup path.\n */\nfunction lazyModules() {\n let catalog: Promise<typeof import('../../../../../services/skillCatalog')> | undefined;\n let deferred: Promise<typeof import('../../../../../services/deferredSkills')> | undefined;\n return {\n catalog: () => (catalog ??= import('../../../../../services/skillCatalog')),\n deferred: () => (deferred ??= import('../../../../../services/deferredSkills')),\n };\n}\n\ninterface SkillPluginConfig {\n readonly pi: ExtensionAPI;\n readonly openOverlay: SkillsCommandDependencies['openOverlay'];\n}\n\nexport function createSkillRuntime({ pi, openOverlay }: SkillPluginConfig): PiPluginContributions {\n const load = lazyModules();\n let activeContext: Context | undefined;\n let activeSources: DoomSkillSourcesService | undefined;\n const bindConfig = (cordis: Context) => {\n cordis.inject([DOOM_CONFIG_SERVICE], (context) => {\n activeContext = context;\n return () => {\n if (activeContext === context) activeContext = undefined;\n };\n });\n };\n const requireRuntimeContext = (): Context => {\n if (!activeContext) throw new Error('Doom skill runtime is waiting for the session config service.');\n return activeContext;\n };\n const requireSkillSources = (): DoomSkillSourcesService => {\n if (!activeSources) throw new Error('Doom skill sources are waiting for the active session service.');\n return activeSources;\n };\n const bindSources = (cordis: Context) => {\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const service = createDoomSkillSourcesService(`${session.generation}:skill-sources`);\n sessionContext.plugin((providerContext) => {\n providerContext.provide(DOOM_SKILL_SOURCES_SERVICE, service);\n });\n activeSources = service;\n return () => {\n if (activeSources === service) activeSources = undefined;\n service.dispose();\n };\n });\n };\n const helpSkillView = createActiveHelpSkillView();\n\n const bindHelp = (cordis: Context) => {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const help = requireDoomHelpService(helpContext);\n const unbind = helpSkillView.bind(help);\n const unbindInventory = help.bindSkillInventory(async () => {\n const deferred = await readiness.current();\n if (!deferred) return undefined;\n return helpSkillView\n .merge({\n normalSkills: [],\n normalSkillNames: loadedSkillCommandNames(pi),\n deferredSkills: deferred.skills,\n })\n .helpSkills.map((skill) => skill.filePath);\n });\n return () => {\n unbindInventory();\n unbind();\n };\n });\n };\n const bindUi = (cordis: Context) => {\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = requireDoomUiHub(uiContext).registerLeader(SKILLS_LEADER_CONTRIBUTION);\n return () => contribution.dispose();\n });\n };\n const readiness = createSkillReadiness(pi, helpSkillView, load.deferred, requireRuntimeContext);\n return {\n services: [bindConfig, bindSources, bindHelp, bindUi],\n commands: [\n createSkillsCommand({\n cordisContext: requireRuntimeContext,\n extensionSources: () => requireSkillSources().list(),\n inventory: async (ctx) => {\n return skillInventory(helpSkillView, readiness)(ctx);\n },\n repositoryRoot: async (_ctx: ExtensionContext) =>\n requireHarnessRoot(requireDoomConfigContext(requireRuntimeContext()).harness),\n buildCatalog: async (request) => (await load.catalog()).buildSkillCatalog(request),\n openOverlay,\n }),\n ],\n events: readiness.events,\n onStop: readiness.onStop,\n onDispose() {\n helpSkillView.dispose();\n },\n };\n}\n","import { defineRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from './_lib/skillRuntime';\nexport default defineRoot(({ pi }: PiPluginContext) => {\n const runtime = createSkillRuntime({\n pi,\n openOverlay: async (context, options) =>\n (await import('../../../../tui/skillsOverlay')).openSkillsOverlay(context, options),\n });\n return { value: runtime, services: runtime.services, onStop: runtime.onStop, onDispose: runtime.onDispose };\n});\n","import { defineCliCommand } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliCommand(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) => context.root.commands![0]!,\n);\n","import { defineCliHook } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliHook(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) =>\n context.root.events!.before_agent_start,\n);\n","import { defineCliHook } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliHook(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) => context.root.events!.input,\n);\n","import { defineCliHook } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliHook(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) =>\n context.root.events!.session_start,\n);\n","import { defineResource } from '@agimon-ai/doompi-core/extensionFile';\n\nimport { LEADER_SOURCE } from '../../../../../types/skills';\nexport default defineResource({\n source: LEADER_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\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 },\n {\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 },\n ],\n});\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { definePiExtension, type PiPluginContext, type PiPluginContributions } from '@agimon-ai/doompi-core/piExtension';\nimport { composeRootHooks } from '@agimon-ai/doompi-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.cli';\nimport commandSkills from '../src/extensions/workspaces/sessions/(backend)/command/skills.cli';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.cli';\nimport hookInput from '../src/extensions/workspaces/sessions/(backend)/hook/input.cli';\nimport hookSessionStart from '../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.cli';\nimport resourceHelp from '../src/extensions/workspaces/sessions/(backend)/resource/help.cli';\n\ntype OptionsOf<T> = T extends (context: PiPluginContext<infer Options>) => unknown ? Options : never;\ntype InferredOptions = Exclude<OptionsOf<typeof rootSession> | OptionsOf<typeof commandSkills> | OptionsOf<typeof hookBeforeAgentStart> | OptionsOf<typeof hookInput> | OptionsOf<typeof hookSessionStart> | OptionsOf<typeof resourceHelp>, undefined>;\ntype Intersect<Union> =\n (Union extends unknown ? (value: Union) => void : never) extends (value: infer Value) => void ? Value : never;\ntype ExtensionOptions = [InferredOptions] extends [never] ? undefined : Intersect<InferredOptions>;\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;\n\nexport const extension = definePiExtension<ExtensionOptions>('@agimon-ai/doompi-skill', async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): PiPluginContributions['commands'] { return [at(commandSkills, contextSession)]; },\n events: {\n before_agent_start: at(hookBeforeAgentStart, contextSession) as NonNullable<PiPluginContributions['events']>['before_agent_start'],\n input: at(hookInput, contextSession) as NonNullable<PiPluginContributions['events']>['input'],\n session_start: at(hookSessionStart, contextSession) as NonNullable<PiPluginContributions['events']>['session_start'],\n },\n get resources(): PiPluginContributions['resources'] { return [at(resourceHelp, contextSession)]; },\n services: [...(scopeSession.services ?? [])],\n };\n});\n\nexport default extension;\n"],"mappings":"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"}
@@ -290,7 +290,17 @@ function createSkillRuntime({ pi, openOverlay }) {
290
290
  cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {
291
291
  const help = requireDoomHelpService(helpContext);
292
292
  const unbind = helpSkillView.bind(help);
293
+ const unbindInventory = help.bindSkillInventory(async () => {
294
+ const deferred = await readiness.current();
295
+ if (!deferred) return void 0;
296
+ return helpSkillView.merge({
297
+ normalSkills: [],
298
+ normalSkillNames: loadedSkillCommandNames(pi),
299
+ deferredSkills: deferred.skills
300
+ }).helpSkills.map((skill) => skill.filePath);
301
+ });
293
302
  return () => {
303
+ unbindInventory();
294
304
  unbind();
295
305
  };
296
306
  });
@@ -1 +1 @@
1
- {"version":3,"file":"pi.mjs","names":["requireDoomConfigContext","requireDoomConfigContext","rootSession","commandSkills","hookBeforeAgentStart","hookInput","hookSessionStart","resourceHelp"],"sources":["../../src/services/helpSkills/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillReadiness.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillsCommand.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillRuntime.ts","../../src/extensions/workspaces/sessions/(backend)/root.cli.ts","../../src/extensions/workspaces/sessions/(backend)/command/skills.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/input.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.cli.ts","../../src/extensions/workspaces/sessions/(backend)/resource/help.cli.ts","../../generated/pi.ts"],"sourcesContent":["import {\n DOOM_HELP_MAX_DIAGNOSTIC_LENGTH,\n DOOM_HELP_MAX_DIAGNOSTICS,\n type DoomHelpService,\n type DoomHelpSkill,\n type DoomHelpSnapshot,\n} from '@agimon-ai/doompi-core/help';\nimport { createSyntheticSourceInfo, type Skill } from '@earendil-works/pi-coding-agent';\n\nexport interface SkillInventoryInput {\n normalSkills: readonly Skill[];\n deferredSkills: readonly Skill[];\n /** Names from Pi's command catalog when full normal Skill records are unavailable. */\n normalSkillNames?: readonly string[];\n}\n\nexport interface MergedSkillInventory {\n /** Pi-native, unique deferred, then accepted Help skills in invocation-precedence order. */\n skills: readonly Skill[];\n /** Deferred and Help skills that are absent from Pi's existing prompt inventory. */\n additionalSkills: readonly Skill[];\n /** Accepted Help skills, retained separately for the Help catalog group. */\n helpSkills: readonly Skill[];\n diagnostics: readonly string[];\n diagnosticKey: string;\n}\n\nexport interface ActiveHelpSkillView {\n bind(service: DoomHelpService): () => void;\n merge(input: SkillInventoryInput): MergedSkillInventory;\n dispose(): void;\n}\n\nfunction boundedDiagnostic(message: string): string {\n return message.slice(0, DOOM_HELP_MAX_DIAGNOSTIC_LENGTH);\n}\n\nfunction toSkill(skill: DoomHelpSkill): Skill {\n return {\n name: skill.name,\n description: skill.description,\n filePath: skill.filePath,\n baseDir: skill.baseDir,\n sourceInfo: createSyntheticSourceInfo(skill.filePath, {\n source: skill.source,\n scope: 'temporary',\n origin: 'package',\n baseDir: skill.baseDir,\n }),\n disableModelInvocation: false,\n };\n}\n\nfunction activeHelpSkills(snapshot: DoomHelpSnapshot | undefined): DoomHelpSkill[] {\n if (!snapshot || snapshot.activation === 'inactive') return [];\n return [...snapshot.skills].sort(\n (left, right) =>\n left.source.localeCompare(right.source) ||\n left.name.localeCompare(right.name) ||\n left.filePath.localeCompare(right.filePath),\n );\n}\n\nfunction snapshotDiagnostics(snapshot: DoomHelpSnapshot | undefined): string[] {\n if (!snapshot) return [];\n return snapshot.diagnostics.map((entry) =>\n boundedDiagnostic(`Help ${entry.source ?? 'host'} [${entry.code}]: ${entry.message}`),\n );\n}\n\nexport function mergeActiveHelpSkills(\n input: SkillInventoryInput,\n snapshot: DoomHelpSnapshot | undefined,\n clientDiagnostics: readonly string[] = [],\n): MergedSkillInventory {\n const nativeNames = new Set([...input.normalSkills.map((skill) => skill.name), ...(input.normalSkillNames ?? [])]);\n const deferredSkills = input.deferredSkills.filter((skill) => !nativeNames.has(skill.name));\n const occupiedNames = new Set([...nativeNames, ...deferredSkills.map((skill) => skill.name)]);\n const helpSkills: Skill[] = [];\n const diagnostics = [...snapshotDiagnostics(snapshot), ...clientDiagnostics.map(boundedDiagnostic)];\n\n for (const candidate of activeHelpSkills(snapshot)) {\n if (occupiedNames.has(candidate.name)) {\n diagnostics.push(\n boundedDiagnostic(\n `Help ${candidate.source} [HELP_SKILL_COLLISION]: '${candidate.name}' was ignored because a normal skill wins.`,\n ),\n );\n continue;\n }\n occupiedNames.add(candidate.name);\n helpSkills.push(toSkill(candidate));\n }\n\n const boundedDiagnostics = diagnostics.slice(0, DOOM_HELP_MAX_DIAGNOSTICS);\n const additionalSkills = [...deferredSkills, ...helpSkills];\n return {\n skills: [...input.normalSkills, ...additionalSkills],\n additionalSkills,\n helpSkills,\n diagnostics: boundedDiagnostics,\n diagnosticKey: `${snapshot?.hostGeneration ?? 'absent'}:${snapshot?.revision ?? 0}:${boundedDiagnostics.join('\\u0001')}`,\n };\n}\n\nexport function createActiveHelpSkillView(): ActiveHelpSkillView {\n let binding: { readonly token: symbol; snapshot: DoomHelpSnapshot; unsubscribe(): void } | undefined;\n let disposed = false;\n return {\n bind(service) {\n if (disposed) throw new Error('The Help skill view is disposed.');\n const token = Symbol(service.generation);\n const current = binding;\n binding = undefined;\n current?.unsubscribe();\n const next: { readonly token: symbol; snapshot: DoomHelpSnapshot; unsubscribe(): void } = {\n token,\n snapshot: service.getSnapshot(),\n unsubscribe: service.subscribeSnapshot((snapshot) => {\n if (binding?.token === token) binding.snapshot = snapshot;\n }),\n };\n binding = next;\n let bindingDisposed = false;\n return () => {\n if (bindingDisposed) return;\n bindingDisposed = true;\n next.unsubscribe();\n if (binding?.token === token) binding = undefined;\n };\n },\n merge(input) {\n return mergeActiveHelpSkills(input, binding?.snapshot);\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n const current = binding;\n binding = undefined;\n current?.unsubscribe();\n },\n };\n}\n","import { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { PiEventHandlers } from '@agimon-ai/doompi-core/piExtension';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext, Skill } from '@earendil-works/pi-coding-agent';\n\nimport type { DeferredSkillSnapshot } from '../../../../../services/deferredSkills';\nimport type { createActiveHelpSkillView } from '../../../../../services/helpSkills';\nimport { SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX } from '../../../../../types/skills';\n\nconst WARNING = 'warning';\n\ntype HelpSkillView = ReturnType<typeof createActiveHelpSkillView>;\n\ninterface SkillReadinessGeneration {\n readonly id: number;\n readonly ready: Promise<DeferredSkillSnapshot>;\n diagnosticsShown: boolean;\n}\n\nexport interface SkillReadiness {\n readonly events: PiEventHandlers;\n onStop(this: void): void;\n /** The snapshot for the live generation, or undefined once it is superseded. */\n current(): Promise<DeferredSkillSnapshot | undefined>;\n}\n\nfunction loadedSkillCommandNames(pi: ExtensionAPI): string[] {\n if (typeof pi.getCommands !== 'function') return [];\n return pi\n .getCommands()\n .filter((command) => command.source === 'skill' && command.name.startsWith(SKILL_COMMAND_PREFIX))\n .map((command) => command.name.slice(SKILL_COMMAND_PREFIX.length));\n}\n\nfunction failedSnapshot(cwd: string, error: unknown): DeferredSkillSnapshot {\n return {\n skills: [],\n diagnostics: [`${cwd}: ${error instanceof Error ? error.message : String(error)}`],\n };\n}\n\n/**\n * Discovery that starts with the session and is awaited only when something\n * needs it.\n *\n * Scanning every skill directory is the slowest thing this package does, so it\n * runs as a generation started on session_start. A reload starts a new one, and\n * a snapshot from a superseded generation is discarded rather than shown.\n */\nexport function createSkillReadiness(\n pi: ExtensionAPI,\n helpSkillView: HelpSkillView,\n loadDeferredSkills: () => Promise<typeof import('../../../../../services/deferredSkills')>,\n cordisContext: () => Context,\n): SkillReadiness {\n let generationSequence = 0;\n let currentGeneration: SkillReadinessGeneration | undefined;\n let shownHelpDiagnosticKey = '';\n\n const currentSnapshot = async (generation: SkillReadinessGeneration): Promise<DeferredSkillSnapshot | undefined> => {\n const snapshot = await generation.ready;\n return currentGeneration === generation ? snapshot : undefined;\n };\n\n return {\n events: {\n session_start: (_event, ctx) => {\n const state = requireDoomConfigContext(cordisContext()).harness;\n const id = ++generationSequence;\n const ready = loadDeferredSkills()\n .then(({ DeferredSkillLoader }) =>\n new DeferredSkillLoader({ cwd: ctx.cwd, skillPaths: state.skillDirectories }).start(),\n )\n .catch((error: unknown) => failedSnapshot(ctx.cwd, error));\n currentGeneration = { id, ready, diagnosticsShown: false };\n shownHelpDiagnosticKey = '';\n },\n input: async (event) => {\n if (!event.text.startsWith(SKILL_INVOCATION_PREFIX)) return { action: 'continue' };\n\n const generation = currentGeneration;\n if (!generation) return { action: 'continue' };\n const snapshot = await currentSnapshot(generation);\n if (!snapshot) return { action: 'continue' };\n\n const inventory = helpSkillView.merge({\n normalSkills: [],\n deferredSkills: snapshot.skills,\n normalSkillNames: loadedSkillCommandNames(pi),\n });\n const { expandDeferredSkillCommand } = await loadDeferredSkills();\n const text = expandDeferredSkillCommand(event.text, inventory.additionalSkills);\n return text === event.text ? { action: 'continue' } : { action: 'transform', text, images: event.images };\n },\n before_agent_start: async (event, ctx) => {\n const generation = currentGeneration;\n if (!generation) return undefined;\n const snapshot = await currentSnapshot(generation);\n if (!snapshot) return undefined;\n\n if (!generation.diagnosticsShown && snapshot.diagnostics.length > 0) {\n generation.diagnosticsShown = true;\n ctx.ui.notify(`Skill discovery:\\n${snapshot.diagnostics.join('\\n')}`, WARNING);\n }\n const inventory = helpSkillView.merge({\n normalSkills: event.systemPromptOptions.skills ?? [],\n deferredSkills: snapshot.skills,\n });\n if (inventory.diagnosticKey !== shownHelpDiagnosticKey) {\n shownHelpDiagnosticKey = inventory.diagnosticKey;\n if (inventory.diagnostics.length > 0) {\n ctx.ui.notify(`Package Help:\\n${inventory.diagnostics.join('\\n')}`, WARNING);\n }\n }\n if (inventory.additionalSkills.length === 0) return undefined;\n\n const { buildPromptWithDeferredSkills } = await loadDeferredSkills();\n return {\n systemPrompt: buildPromptWithDeferredSkills(event.systemPrompt, event.systemPromptOptions, [\n ...inventory.additionalSkills,\n ]),\n };\n },\n },\n onStop() {\n currentGeneration = undefined;\n },\n current: async () => (currentGeneration ? currentSnapshot(currentGeneration) : undefined),\n };\n}\n\n/** The merged skill inventory the /skills catalogue is built from. */\nexport function skillInventory(helpSkillView: HelpSkillView, readiness: SkillReadiness) {\n return async (ctx: ExtensionContext) => {\n const snapshot = await readiness.current();\n const inventory = helpSkillView.merge({\n // Present on the runtime context; absent from the published ExtensionContext type.\n normalSkills:\n (ctx as unknown as { getSystemPromptOptions(): { skills?: Skill[] } }).getSystemPromptOptions().skills ?? [],\n deferredSkills: snapshot?.skills ?? [],\n });\n return { helpSkills: inventory.helpSkills, diagnostics: inventory.diagnostics };\n };\n}\n","import { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport type { SkillCatalog, SkillCatalogOptions } from '../../../../../services/skillCatalog';\nimport { skillInvocation } from '../../../../../services/skillText';\nimport type { SkillsOverlayResult } from '../../../../../tui/skillsOverlay';\nimport { SKILLS_COMMAND } from '../../../../../types/skills';\n\nconst INFO = 'info';\nconst ERROR = 'error';\n\nexport interface SkillsCommandDependencies {\n readonly cordisContext: () => Context;\n /** Skill directories every loaded extension has contributed this session. */\n readonly extensionSources: () => readonly { source: string; directories: readonly string[] }[];\n /** Merged view of normal, deferred and package Help skills. */\n readonly inventory: (ctx: ExtensionContext) => Promise<{\n readonly helpSkills: SkillCatalogOptions['helpSkills'];\n readonly diagnostics: readonly string[];\n }>;\n readonly repositoryRoot: (ctx: ExtensionContext) => Promise<string>;\n readonly buildCatalog: (request: SkillCatalogOptions) => Promise<SkillCatalog>;\n readonly openOverlay: (\n ctx: ExtensionContext,\n options: { catalog: SkillCatalog; repoRoot: string },\n ) => Promise<SkillsOverlayResult | undefined>;\n}\n\nexport function createSkillsCommand(\n dependencies: SkillsCommandDependencies,\n): Parameters<ExtensionAPI['registerCommand']> {\n return [\n SKILLS_COMMAND,\n {\n description: 'Browse the skills this session loaded, and what other domains would add',\n handler: async (_args, ctx) => {\n if (!ctx.hasUI || ctx.mode !== 'tui') {\n ctx.ui.notify('/skills requires interactive mode', ERROR);\n return;\n }\n\n const state = requireDoomConfigContext(dependencies.cordisContext()).harness;\n const [repoRoot, inventory] = await Promise.all([\n dependencies.repositoryRoot(ctx),\n dependencies.inventory(ctx),\n ]);\n const catalog = await dependencies.buildCatalog({\n repoRoot,\n activeSkillDirectories: state.skillDirectories,\n extensionSources: dependencies.extensionSources(),\n helpSkills: inventory.helpSkills,\n helpDiagnostics: [...inventory.diagnostics],\n });\n\n const result = await dependencies.openOverlay(ctx, { catalog, repoRoot });\n if (!result) return;\n ctx.ui.setEditorText(skillInvocation(result.skill.name));\n ctx.ui.notify(`Loaded ${result.skill.name} into the prompt. Press enter to run it.`, INFO);\n },\n },\n ];\n}\n","import { requireDoomConfigContext } from '@agimon-ai/doompi-config';\nimport { requireHarnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport { DOOM_CONFIG_SERVICE } from '@agimon-ai/doompi-core/config';\nimport { DOOM_CORDIS_SESSION_SERVICE, type DoomCordisSessionService } from '@agimon-ai/doompi-core/cordisHost';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-core/help';\nimport { type PiPluginContributions } from '@agimon-ai/doompi-core/piExtension';\nimport {\n createDoomSkillSourcesService,\n DOOM_SKILL_SOURCES_SERVICE,\n type DoomSkillSourcesService,\n} from '@agimon-ai/doompi-core/skills';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-core/uiHub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport { createActiveHelpSkillView } from '../../../../../services/helpSkills';\nimport { SKILLS_LEADER_CONTRIBUTION } from '../../../../../types/skills';\nimport { createSkillReadiness, skillInventory } from './skillReadiness';\nimport { createSkillsCommand, type SkillsCommandDependencies } from './skillsCommand';\n\n/**\n * The catalogue and the overlay load on first use.\n *\n * Discovery has to start with the session, but building the catalogue and\n * rendering it are only reached by `/skills`, so they stay off the startup path.\n */\nfunction lazyModules() {\n let catalog: Promise<typeof import('../../../../../services/skillCatalog')> | undefined;\n let deferred: Promise<typeof import('../../../../../services/deferredSkills')> | undefined;\n return {\n catalog: () => (catalog ??= import('../../../../../services/skillCatalog')),\n deferred: () => (deferred ??= import('../../../../../services/deferredSkills')),\n };\n}\n\ninterface SkillPluginConfig {\n readonly pi: ExtensionAPI;\n readonly openOverlay: SkillsCommandDependencies['openOverlay'];\n}\n\nexport function createSkillRuntime({ pi, openOverlay }: SkillPluginConfig): PiPluginContributions {\n const load = lazyModules();\n let activeContext: Context | undefined;\n let activeSources: DoomSkillSourcesService | undefined;\n const bindConfig = (cordis: Context) => {\n cordis.inject([DOOM_CONFIG_SERVICE], (context) => {\n activeContext = context;\n return () => {\n if (activeContext === context) activeContext = undefined;\n };\n });\n };\n const requireRuntimeContext = (): Context => {\n if (!activeContext) throw new Error('Doom skill runtime is waiting for the session config service.');\n return activeContext;\n };\n const requireSkillSources = (): DoomSkillSourcesService => {\n if (!activeSources) throw new Error('Doom skill sources are waiting for the active session service.');\n return activeSources;\n };\n const bindSources = (cordis: Context) => {\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const service = createDoomSkillSourcesService(`${session.generation}:skill-sources`);\n sessionContext.plugin((providerContext) => {\n providerContext.provide(DOOM_SKILL_SOURCES_SERVICE, service);\n });\n activeSources = service;\n return () => {\n if (activeSources === service) activeSources = undefined;\n service.dispose();\n };\n });\n };\n const helpSkillView = createActiveHelpSkillView();\n\n const bindHelp = (cordis: Context) => {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const help = requireDoomHelpService(helpContext);\n const unbind = helpSkillView.bind(help);\n return () => {\n unbind();\n };\n });\n };\n const bindUi = (cordis: Context) => {\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = requireDoomUiHub(uiContext).registerLeader(SKILLS_LEADER_CONTRIBUTION);\n return () => contribution.dispose();\n });\n };\n const readiness = createSkillReadiness(pi, helpSkillView, load.deferred, requireRuntimeContext);\n return {\n services: [bindConfig, bindSources, bindHelp, bindUi],\n commands: [\n createSkillsCommand({\n cordisContext: requireRuntimeContext,\n extensionSources: () => requireSkillSources().list(),\n inventory: async (ctx) => {\n return skillInventory(helpSkillView, readiness)(ctx);\n },\n repositoryRoot: async (_ctx: ExtensionContext) =>\n requireHarnessRoot(requireDoomConfigContext(requireRuntimeContext()).harness),\n buildCatalog: async (request) => (await load.catalog()).buildSkillCatalog(request),\n openOverlay,\n }),\n ],\n events: readiness.events,\n onStop: readiness.onStop,\n onDispose() {\n helpSkillView.dispose();\n },\n };\n}\n","import { defineRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from './_lib/skillRuntime';\nexport default defineRoot(({ pi }: PiPluginContext) => {\n const runtime = createSkillRuntime({\n pi,\n openOverlay: async (context, options) =>\n (await import('../../../../tui/skillsOverlay')).openSkillsOverlay(context, options),\n });\n return { value: runtime, services: runtime.services, onStop: runtime.onStop, onDispose: runtime.onDispose };\n});\n","import { defineCliCommand } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliCommand(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) => context.root.commands![0]!,\n);\n","import { defineCliHook } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliHook(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) =>\n context.root.events!.before_agent_start,\n);\n","import { defineCliHook } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliHook(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) => context.root.events!.input,\n);\n","import { defineCliHook } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliHook(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) =>\n context.root.events!.session_start,\n);\n","import { defineResource } from '@agimon-ai/doompi-core/extensionFile';\n\nimport { LEADER_SOURCE } from '../../../../../types/skills';\nexport default defineResource({\n source: LEADER_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\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 },\n {\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 },\n ],\n});\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { definePiExtension, type PiPluginContext, type PiPluginContributions } from '@agimon-ai/doompi-core/piExtension';\nimport { composeRootHooks } from '@agimon-ai/doompi-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.cli';\nimport commandSkills from '../src/extensions/workspaces/sessions/(backend)/command/skills.cli';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.cli';\nimport hookInput from '../src/extensions/workspaces/sessions/(backend)/hook/input.cli';\nimport hookSessionStart from '../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.cli';\nimport resourceHelp from '../src/extensions/workspaces/sessions/(backend)/resource/help.cli';\n\ntype OptionsOf<T> = T extends (context: PiPluginContext<infer Options>) => unknown ? Options : never;\ntype InferredOptions = Exclude<OptionsOf<typeof rootSession> | OptionsOf<typeof commandSkills> | OptionsOf<typeof hookBeforeAgentStart> | OptionsOf<typeof hookInput> | OptionsOf<typeof hookSessionStart> | OptionsOf<typeof resourceHelp>, undefined>;\ntype Intersect<Union> =\n (Union extends unknown ? (value: Union) => void : never) extends (value: infer Value) => void ? Value : never;\ntype ExtensionOptions = [InferredOptions] extends [never] ? undefined : Intersect<InferredOptions>;\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;\n\nexport const extension = definePiExtension<ExtensionOptions>('@agimon-ai/doompi-skill', async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): PiPluginContributions['commands'] { return [at(commandSkills, contextSession)]; },\n events: {\n before_agent_start: at(hookBeforeAgentStart, contextSession) as NonNullable<PiPluginContributions['events']>['before_agent_start'],\n input: at(hookInput, contextSession) as NonNullable<PiPluginContributions['events']>['input'],\n session_start: at(hookSessionStart, contextSession) as NonNullable<PiPluginContributions['events']>['session_start'],\n },\n get resources(): PiPluginContributions['resources'] { return [at(resourceHelp, contextSession)]; },\n services: [...(scopeSession.services ?? [])],\n };\n});\n\nexport default extension;\n"],"mappings":"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"}
1
+ {"version":3,"file":"pi.mjs","names":["requireDoomConfigContext","requireDoomConfigContext","rootSession","commandSkills","hookBeforeAgentStart","hookInput","hookSessionStart","resourceHelp"],"sources":["../../src/services/helpSkills/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillReadiness.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillsCommand.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/skillRuntime.ts","../../src/extensions/workspaces/sessions/(backend)/root.cli.ts","../../src/extensions/workspaces/sessions/(backend)/command/skills.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/input.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.cli.ts","../../src/extensions/workspaces/sessions/(backend)/resource/help.cli.ts","../../generated/pi.ts"],"sourcesContent":["import {\n DOOM_HELP_MAX_DIAGNOSTIC_LENGTH,\n DOOM_HELP_MAX_DIAGNOSTICS,\n type DoomHelpService,\n type DoomHelpSkill,\n type DoomHelpSnapshot,\n} from '@agimon-ai/doompi-core/help';\nimport { createSyntheticSourceInfo, type Skill } from '@earendil-works/pi-coding-agent';\n\nexport interface SkillInventoryInput {\n normalSkills: readonly Skill[];\n deferredSkills: readonly Skill[];\n /** Names from Pi's command catalog when full normal Skill records are unavailable. */\n normalSkillNames?: readonly string[];\n}\n\nexport interface MergedSkillInventory {\n /** Pi-native, unique deferred, then accepted Help skills in invocation-precedence order. */\n skills: readonly Skill[];\n /** Deferred and Help skills that are absent from Pi's existing prompt inventory. */\n additionalSkills: readonly Skill[];\n /** Accepted Help skills, retained separately for the Help catalog group. */\n helpSkills: readonly Skill[];\n diagnostics: readonly string[];\n diagnosticKey: string;\n}\n\nexport interface ActiveHelpSkillView {\n bind(service: DoomHelpService): () => void;\n merge(input: SkillInventoryInput): MergedSkillInventory;\n dispose(): void;\n}\n\nfunction boundedDiagnostic(message: string): string {\n return message.slice(0, DOOM_HELP_MAX_DIAGNOSTIC_LENGTH);\n}\n\nfunction toSkill(skill: DoomHelpSkill): Skill {\n return {\n name: skill.name,\n description: skill.description,\n filePath: skill.filePath,\n baseDir: skill.baseDir,\n sourceInfo: createSyntheticSourceInfo(skill.filePath, {\n source: skill.source,\n scope: 'temporary',\n origin: 'package',\n baseDir: skill.baseDir,\n }),\n disableModelInvocation: false,\n };\n}\n\nfunction activeHelpSkills(snapshot: DoomHelpSnapshot | undefined): DoomHelpSkill[] {\n if (!snapshot || snapshot.activation === 'inactive') return [];\n return [...snapshot.skills].sort(\n (left, right) =>\n left.source.localeCompare(right.source) ||\n left.name.localeCompare(right.name) ||\n left.filePath.localeCompare(right.filePath),\n );\n}\n\nfunction snapshotDiagnostics(snapshot: DoomHelpSnapshot | undefined): string[] {\n if (!snapshot) return [];\n return snapshot.diagnostics.map((entry) =>\n boundedDiagnostic(`Help ${entry.source ?? 'host'} [${entry.code}]: ${entry.message}`),\n );\n}\n\nexport function mergeActiveHelpSkills(\n input: SkillInventoryInput,\n snapshot: DoomHelpSnapshot | undefined,\n clientDiagnostics: readonly string[] = [],\n): MergedSkillInventory {\n const nativeNames = new Set([...input.normalSkills.map((skill) => skill.name), ...(input.normalSkillNames ?? [])]);\n const deferredSkills = input.deferredSkills.filter((skill) => !nativeNames.has(skill.name));\n const occupiedNames = new Set([...nativeNames, ...deferredSkills.map((skill) => skill.name)]);\n const helpSkills: Skill[] = [];\n const diagnostics = [...snapshotDiagnostics(snapshot), ...clientDiagnostics.map(boundedDiagnostic)];\n\n for (const candidate of activeHelpSkills(snapshot)) {\n if (occupiedNames.has(candidate.name)) {\n diagnostics.push(\n boundedDiagnostic(\n `Help ${candidate.source} [HELP_SKILL_COLLISION]: '${candidate.name}' was ignored because a normal skill wins.`,\n ),\n );\n continue;\n }\n occupiedNames.add(candidate.name);\n helpSkills.push(toSkill(candidate));\n }\n\n const boundedDiagnostics = diagnostics.slice(0, DOOM_HELP_MAX_DIAGNOSTICS);\n const additionalSkills = [...deferredSkills, ...helpSkills];\n return {\n skills: [...input.normalSkills, ...additionalSkills],\n additionalSkills,\n helpSkills,\n diagnostics: boundedDiagnostics,\n diagnosticKey: `${snapshot?.hostGeneration ?? 'absent'}:${snapshot?.revision ?? 0}:${boundedDiagnostics.join('\\u0001')}`,\n };\n}\n\nexport function createActiveHelpSkillView(): ActiveHelpSkillView {\n let binding: { readonly token: symbol; snapshot: DoomHelpSnapshot; unsubscribe(): void } | undefined;\n let disposed = false;\n return {\n bind(service) {\n if (disposed) throw new Error('The Help skill view is disposed.');\n const token = Symbol(service.generation);\n const current = binding;\n binding = undefined;\n current?.unsubscribe();\n const next: { readonly token: symbol; snapshot: DoomHelpSnapshot; unsubscribe(): void } = {\n token,\n snapshot: service.getSnapshot(),\n unsubscribe: service.subscribeSnapshot((snapshot) => {\n if (binding?.token === token) binding.snapshot = snapshot;\n }),\n };\n binding = next;\n let bindingDisposed = false;\n return () => {\n if (bindingDisposed) return;\n bindingDisposed = true;\n next.unsubscribe();\n if (binding?.token === token) binding = undefined;\n };\n },\n merge(input) {\n return mergeActiveHelpSkills(input, binding?.snapshot);\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n const current = binding;\n binding = undefined;\n current?.unsubscribe();\n },\n };\n}\n","import { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { PiEventHandlers } from '@agimon-ai/doompi-core/piExtension';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext, Skill } from '@earendil-works/pi-coding-agent';\n\nimport type { DeferredSkillSnapshot } from '../../../../../services/deferredSkills';\nimport type { createActiveHelpSkillView } from '../../../../../services/helpSkills';\nimport { SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX } from '../../../../../types/skills';\n\nconst WARNING = 'warning';\n\ntype HelpSkillView = ReturnType<typeof createActiveHelpSkillView>;\n\ninterface SkillReadinessGeneration {\n readonly id: number;\n readonly ready: Promise<DeferredSkillSnapshot>;\n diagnosticsShown: boolean;\n}\n\nexport interface SkillReadiness {\n readonly events: PiEventHandlers;\n onStop(this: void): void;\n /** The snapshot for the live generation, or undefined once it is superseded. */\n current(): Promise<DeferredSkillSnapshot | undefined>;\n}\n\nexport function loadedSkillCommandNames(pi: ExtensionAPI): string[] {\n if (typeof pi.getCommands !== 'function') return [];\n return pi\n .getCommands()\n .filter((command) => command.source === 'skill' && command.name.startsWith(SKILL_COMMAND_PREFIX))\n .map((command) => command.name.slice(SKILL_COMMAND_PREFIX.length));\n}\n\nfunction failedSnapshot(cwd: string, error: unknown): DeferredSkillSnapshot {\n return {\n skills: [],\n diagnostics: [`${cwd}: ${error instanceof Error ? error.message : String(error)}`],\n };\n}\n\n/**\n * Discovery that starts with the session and is awaited only when something\n * needs it.\n *\n * Scanning every skill directory is the slowest thing this package does, so it\n * runs as a generation started on session_start. A reload starts a new one, and\n * a snapshot from a superseded generation is discarded rather than shown.\n */\nexport function createSkillReadiness(\n pi: ExtensionAPI,\n helpSkillView: HelpSkillView,\n loadDeferredSkills: () => Promise<typeof import('../../../../../services/deferredSkills')>,\n cordisContext: () => Context,\n): SkillReadiness {\n let generationSequence = 0;\n let currentGeneration: SkillReadinessGeneration | undefined;\n let shownHelpDiagnosticKey = '';\n\n const currentSnapshot = async (generation: SkillReadinessGeneration): Promise<DeferredSkillSnapshot | undefined> => {\n const snapshot = await generation.ready;\n return currentGeneration === generation ? snapshot : undefined;\n };\n\n return {\n events: {\n session_start: (_event, ctx) => {\n const state = requireDoomConfigContext(cordisContext()).harness;\n const id = ++generationSequence;\n const ready = loadDeferredSkills()\n .then(({ DeferredSkillLoader }) =>\n new DeferredSkillLoader({ cwd: ctx.cwd, skillPaths: state.skillDirectories }).start(),\n )\n .catch((error: unknown) => failedSnapshot(ctx.cwd, error));\n currentGeneration = { id, ready, diagnosticsShown: false };\n shownHelpDiagnosticKey = '';\n },\n input: async (event) => {\n if (!event.text.startsWith(SKILL_INVOCATION_PREFIX)) return { action: 'continue' };\n\n const generation = currentGeneration;\n if (!generation) return { action: 'continue' };\n const snapshot = await currentSnapshot(generation);\n if (!snapshot) return { action: 'continue' };\n\n const inventory = helpSkillView.merge({\n normalSkills: [],\n deferredSkills: snapshot.skills,\n normalSkillNames: loadedSkillCommandNames(pi),\n });\n const { expandDeferredSkillCommand } = await loadDeferredSkills();\n const text = expandDeferredSkillCommand(event.text, inventory.additionalSkills);\n return text === event.text ? { action: 'continue' } : { action: 'transform', text, images: event.images };\n },\n before_agent_start: async (event, ctx) => {\n const generation = currentGeneration;\n if (!generation) return undefined;\n const snapshot = await currentSnapshot(generation);\n if (!snapshot) return undefined;\n\n if (!generation.diagnosticsShown && snapshot.diagnostics.length > 0) {\n generation.diagnosticsShown = true;\n ctx.ui.notify(`Skill discovery:\\n${snapshot.diagnostics.join('\\n')}`, WARNING);\n }\n const inventory = helpSkillView.merge({\n normalSkills: event.systemPromptOptions.skills ?? [],\n deferredSkills: snapshot.skills,\n });\n if (inventory.diagnosticKey !== shownHelpDiagnosticKey) {\n shownHelpDiagnosticKey = inventory.diagnosticKey;\n if (inventory.diagnostics.length > 0) {\n ctx.ui.notify(`Package Help:\\n${inventory.diagnostics.join('\\n')}`, WARNING);\n }\n }\n if (inventory.additionalSkills.length === 0) return undefined;\n\n const { buildPromptWithDeferredSkills } = await loadDeferredSkills();\n return {\n systemPrompt: buildPromptWithDeferredSkills(event.systemPrompt, event.systemPromptOptions, [\n ...inventory.additionalSkills,\n ]),\n };\n },\n },\n onStop() {\n currentGeneration = undefined;\n },\n current: async () => (currentGeneration ? currentSnapshot(currentGeneration) : undefined),\n };\n}\n\n/** The merged skill inventory the /skills catalogue is built from. */\nexport function skillInventory(helpSkillView: HelpSkillView, readiness: SkillReadiness) {\n return async (ctx: ExtensionContext) => {\n const snapshot = await readiness.current();\n const inventory = helpSkillView.merge({\n // Present on the runtime context; absent from the published ExtensionContext type.\n normalSkills:\n (ctx as unknown as { getSystemPromptOptions(): { skills?: Skill[] } }).getSystemPromptOptions().skills ?? [],\n deferredSkills: snapshot?.skills ?? [],\n });\n return { helpSkills: inventory.helpSkills, diagnostics: inventory.diagnostics };\n };\n}\n","import { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport type { SkillCatalog, SkillCatalogOptions } from '../../../../../services/skillCatalog';\nimport { skillInvocation } from '../../../../../services/skillText';\nimport type { SkillsOverlayResult } from '../../../../../tui/skillsOverlay';\nimport { SKILLS_COMMAND } from '../../../../../types/skills';\n\nconst INFO = 'info';\nconst ERROR = 'error';\n\nexport interface SkillsCommandDependencies {\n readonly cordisContext: () => Context;\n /** Skill directories every loaded extension has contributed this session. */\n readonly extensionSources: () => readonly { source: string; directories: readonly string[] }[];\n /** Merged view of normal, deferred and package Help skills. */\n readonly inventory: (ctx: ExtensionContext) => Promise<{\n readonly helpSkills: SkillCatalogOptions['helpSkills'];\n readonly diagnostics: readonly string[];\n }>;\n readonly repositoryRoot: (ctx: ExtensionContext) => Promise<string>;\n readonly buildCatalog: (request: SkillCatalogOptions) => Promise<SkillCatalog>;\n readonly openOverlay: (\n ctx: ExtensionContext,\n options: { catalog: SkillCatalog; repoRoot: string },\n ) => Promise<SkillsOverlayResult | undefined>;\n}\n\nexport function createSkillsCommand(\n dependencies: SkillsCommandDependencies,\n): Parameters<ExtensionAPI['registerCommand']> {\n return [\n SKILLS_COMMAND,\n {\n description: 'Browse the skills this session loaded, and what other domains would add',\n handler: async (_args, ctx) => {\n if (!ctx.hasUI || ctx.mode !== 'tui') {\n ctx.ui.notify('/skills requires interactive mode', ERROR);\n return;\n }\n\n const state = requireDoomConfigContext(dependencies.cordisContext()).harness;\n const [repoRoot, inventory] = await Promise.all([\n dependencies.repositoryRoot(ctx),\n dependencies.inventory(ctx),\n ]);\n const catalog = await dependencies.buildCatalog({\n repoRoot,\n activeSkillDirectories: state.skillDirectories,\n extensionSources: dependencies.extensionSources(),\n helpSkills: inventory.helpSkills,\n helpDiagnostics: [...inventory.diagnostics],\n });\n\n const result = await dependencies.openOverlay(ctx, { catalog, repoRoot });\n if (!result) return;\n ctx.ui.setEditorText(skillInvocation(result.skill.name));\n ctx.ui.notify(`Loaded ${result.skill.name} into the prompt. Press enter to run it.`, INFO);\n },\n },\n ];\n}\n","import { requireDoomConfigContext } from '@agimon-ai/doompi-config';\nimport { requireHarnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport { DOOM_CONFIG_SERVICE } from '@agimon-ai/doompi-core/config';\nimport { DOOM_CORDIS_SESSION_SERVICE, type DoomCordisSessionService } from '@agimon-ai/doompi-core/cordisHost';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-core/help';\nimport { type PiPluginContributions } from '@agimon-ai/doompi-core/piExtension';\nimport {\n createDoomSkillSourcesService,\n DOOM_SKILL_SOURCES_SERVICE,\n type DoomSkillSourcesService,\n} from '@agimon-ai/doompi-core/skills';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-core/uiHub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport { createActiveHelpSkillView } from '../../../../../services/helpSkills';\nimport { SKILLS_LEADER_CONTRIBUTION } from '../../../../../types/skills';\nimport { createSkillReadiness, loadedSkillCommandNames, skillInventory } from './skillReadiness';\nimport { createSkillsCommand, type SkillsCommandDependencies } from './skillsCommand';\n\n/**\n * The catalogue and the overlay load on first use.\n *\n * Discovery has to start with the session, but building the catalogue and\n * rendering it are only reached by `/skills`, so they stay off the startup path.\n */\nfunction lazyModules() {\n let catalog: Promise<typeof import('../../../../../services/skillCatalog')> | undefined;\n let deferred: Promise<typeof import('../../../../../services/deferredSkills')> | undefined;\n return {\n catalog: () => (catalog ??= import('../../../../../services/skillCatalog')),\n deferred: () => (deferred ??= import('../../../../../services/deferredSkills')),\n };\n}\n\ninterface SkillPluginConfig {\n readonly pi: ExtensionAPI;\n readonly openOverlay: SkillsCommandDependencies['openOverlay'];\n}\n\nexport function createSkillRuntime({ pi, openOverlay }: SkillPluginConfig): PiPluginContributions {\n const load = lazyModules();\n let activeContext: Context | undefined;\n let activeSources: DoomSkillSourcesService | undefined;\n const bindConfig = (cordis: Context) => {\n cordis.inject([DOOM_CONFIG_SERVICE], (context) => {\n activeContext = context;\n return () => {\n if (activeContext === context) activeContext = undefined;\n };\n });\n };\n const requireRuntimeContext = (): Context => {\n if (!activeContext) throw new Error('Doom skill runtime is waiting for the session config service.');\n return activeContext;\n };\n const requireSkillSources = (): DoomSkillSourcesService => {\n if (!activeSources) throw new Error('Doom skill sources are waiting for the active session service.');\n return activeSources;\n };\n const bindSources = (cordis: Context) => {\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const service = createDoomSkillSourcesService(`${session.generation}:skill-sources`);\n sessionContext.plugin((providerContext) => {\n providerContext.provide(DOOM_SKILL_SOURCES_SERVICE, service);\n });\n activeSources = service;\n return () => {\n if (activeSources === service) activeSources = undefined;\n service.dispose();\n };\n });\n };\n const helpSkillView = createActiveHelpSkillView();\n\n const bindHelp = (cordis: Context) => {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const help = requireDoomHelpService(helpContext);\n const unbind = helpSkillView.bind(help);\n const unbindInventory = help.bindSkillInventory(async () => {\n const deferred = await readiness.current();\n if (!deferred) return undefined;\n return helpSkillView\n .merge({\n normalSkills: [],\n normalSkillNames: loadedSkillCommandNames(pi),\n deferredSkills: deferred.skills,\n })\n .helpSkills.map((skill) => skill.filePath);\n });\n return () => {\n unbindInventory();\n unbind();\n };\n });\n };\n const bindUi = (cordis: Context) => {\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = requireDoomUiHub(uiContext).registerLeader(SKILLS_LEADER_CONTRIBUTION);\n return () => contribution.dispose();\n });\n };\n const readiness = createSkillReadiness(pi, helpSkillView, load.deferred, requireRuntimeContext);\n return {\n services: [bindConfig, bindSources, bindHelp, bindUi],\n commands: [\n createSkillsCommand({\n cordisContext: requireRuntimeContext,\n extensionSources: () => requireSkillSources().list(),\n inventory: async (ctx) => {\n return skillInventory(helpSkillView, readiness)(ctx);\n },\n repositoryRoot: async (_ctx: ExtensionContext) =>\n requireHarnessRoot(requireDoomConfigContext(requireRuntimeContext()).harness),\n buildCatalog: async (request) => (await load.catalog()).buildSkillCatalog(request),\n openOverlay,\n }),\n ],\n events: readiness.events,\n onStop: readiness.onStop,\n onDispose() {\n helpSkillView.dispose();\n },\n };\n}\n","import { defineRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from './_lib/skillRuntime';\nexport default defineRoot(({ pi }: PiPluginContext) => {\n const runtime = createSkillRuntime({\n pi,\n openOverlay: async (context, options) =>\n (await import('../../../../tui/skillsOverlay')).openSkillsOverlay(context, options),\n });\n return { value: runtime, services: runtime.services, onStop: runtime.onStop, onDispose: runtime.onDispose };\n});\n","import { defineCliCommand } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliCommand(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) => context.root.commands![0]!,\n);\n","import { defineCliHook } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliHook(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) =>\n context.root.events!.before_agent_start,\n);\n","import { defineCliHook } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliHook(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) => context.root.events!.input,\n);\n","import { defineCliHook } from '@agimon-ai/doompi-core/extensionFile';\nimport type { WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\nimport { createSkillRuntime } from '../_lib/skillRuntime';\nexport default defineCliHook(\n (context: WithRoot<PiPluginContext<unknown>, ReturnType<typeof createSkillRuntime>>) =>\n context.root.events!.session_start,\n);\n","import { defineResource } from '@agimon-ai/doompi-core/extensionFile';\n\nimport { LEADER_SOURCE } from '../../../../../types/skills';\nexport default defineResource({\n source: LEADER_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\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 },\n {\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 },\n ],\n});\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { definePiExtension, type PiPluginContext, type PiPluginContributions } from '@agimon-ai/doompi-core/piExtension';\nimport { composeRootHooks } from '@agimon-ai/doompi-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.cli';\nimport commandSkills from '../src/extensions/workspaces/sessions/(backend)/command/skills.cli';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.cli';\nimport hookInput from '../src/extensions/workspaces/sessions/(backend)/hook/input.cli';\nimport hookSessionStart from '../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.cli';\nimport resourceHelp from '../src/extensions/workspaces/sessions/(backend)/resource/help.cli';\n\ntype OptionsOf<T> = T extends (context: PiPluginContext<infer Options>) => unknown ? Options : never;\ntype InferredOptions = Exclude<OptionsOf<typeof rootSession> | OptionsOf<typeof commandSkills> | OptionsOf<typeof hookBeforeAgentStart> | OptionsOf<typeof hookInput> | OptionsOf<typeof hookSessionStart> | OptionsOf<typeof resourceHelp>, undefined>;\ntype Intersect<Union> =\n (Union extends unknown ? (value: Union) => void : never) extends (value: infer Value) => void ? Value : never;\ntype ExtensionOptions = [InferredOptions] extends [never] ? undefined : Intersect<InferredOptions>;\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;\n\nexport const extension = definePiExtension<ExtensionOptions>('@agimon-ai/doompi-skill', async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): PiPluginContributions['commands'] { return [at(commandSkills, contextSession)]; },\n events: {\n before_agent_start: at(hookBeforeAgentStart, contextSession) as NonNullable<PiPluginContributions['events']>['before_agent_start'],\n input: at(hookInput, contextSession) as NonNullable<PiPluginContributions['events']>['input'],\n session_start: at(hookSessionStart, contextSession) as NonNullable<PiPluginContributions['events']>['session_start'],\n },\n get resources(): PiPluginContributions['resources'] { return [at(resourceHelp, contextSession)]; },\n services: [...(scopeSession.services ?? [])],\n };\n});\n\nexport default extension;\n"],"mappings":"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"}
@@ -229,10 +229,24 @@ async function createSkillServer(agent, signal) {
229
229
  name: "doompi/skills",
230
230
  kind: "skill",
231
231
  read: () => catalog
232
- }, ...["doompi-author-skill", "doompi-use-skill"].map((name) => ({
233
- name,
232
+ }, ...[{
233
+ name: "doompi-author-skill",
234
+ 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
+ }, {
236
+ name: "doompi-use-skill",
237
+ description: "Use DoomPi's skill catalog and deferred discovery. Diagnose missing or shadowed skills and invoke the narrowest available guidance."
238
+ }].map((skill) => ({
239
+ ...skill,
240
+ when: {
241
+ state: { "minor-mode": "help" },
242
+ attribution: {
243
+ kind: "minor",
244
+ mode: "help"
245
+ }
246
+ },
234
247
  kind: "skill",
235
- read: () => (0, _agimon_ai_doompi_core_serverFacet.readPackageResource)(require("url").pathToFileURL(__filename).href, `src/prompts/${name}/SKILL.md`)
248
+ path: (0, _agimon_ai_doompi_core_serverFacet.packageResourcePath)(require("url").pathToFileURL(__filename).href, `src/prompts/${skill.name}/SKILL.md`),
249
+ read: () => (0, _agimon_ai_doompi_core_serverFacet.readPackageResource)(require("url").pathToFileURL(__filename).href, `src/prompts/${skill.name}/SKILL.md`)
236
250
  }))]
237
251
  };
238
252
  }
@@ -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","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 { type DoomServerSessionPlugin, readPackageResource } from '@agimon-ai/doompi-core/serverFacet';\n\nimport { mountMcpSkills } from '../../../../../services/mcpSkills';\nimport { discoverServerSkills } from '../../../../../services/serverInventory';\nimport { createSkillCommands } from './skillCommands';\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 ...['doompi-author-skill', 'doompi-use-skill'].map((name) => ({\n name,\n kind: 'skill' as const,\n read: () => readPackageResource(import.meta.url, `src/prompts/${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","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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"}
@@ -5,7 +5,7 @@ import { listDomainNames, resolvePluginEntries, resolveSharedSkills } from "@agi
5
5
  import { materializePluginEntries } from "@agimon-ai/doompi-domain/plugins";
6
6
  import { formatSkillsForPrompt } from "@earendil-works/pi-coding-agent";
7
7
  import { composeRootHooks, defineRoot, defineRoutedContribution } from "@agimon-ai/doompi-core/extensionFile";
8
- import { defineServerPlugin, readPackageResource } from "@agimon-ai/doompi-core/serverFacet";
8
+ import { defineServerPlugin, packageResourcePath, readPackageResource } from "@agimon-ai/doompi-core/serverFacet";
9
9
  import { readFile } from "node:fs/promises";
10
10
  import os from "node:os";
11
11
  import { readHarnessState } from "@agimon-ai/doompi-config/harnessState";
@@ -222,10 +222,24 @@ async function createSkillServer(agent, signal) {
222
222
  name: "doompi/skills",
223
223
  kind: "skill",
224
224
  read: () => catalog
225
- }, ...["doompi-author-skill", "doompi-use-skill"].map((name) => ({
226
- name,
225
+ }, ...[{
226
+ name: "doompi-author-skill",
227
+ 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
+ }, {
229
+ name: "doompi-use-skill",
230
+ description: "Use DoomPi's skill catalog and deferred discovery. Diagnose missing or shadowed skills and invoke the narrowest available guidance."
231
+ }].map((skill) => ({
232
+ ...skill,
233
+ when: {
234
+ state: { "minor-mode": "help" },
235
+ attribution: {
236
+ kind: "minor",
237
+ mode: "help"
238
+ }
239
+ },
227
240
  kind: "skill",
228
- read: () => readPackageResource(import.meta.url, `src/prompts/${name}/SKILL.md`)
241
+ path: packageResourcePath(import.meta.url, `src/prompts/${skill.name}/SKILL.md`),
242
+ read: () => readPackageResource(import.meta.url, `src/prompts/${skill.name}/SKILL.md`)
229
243
  }))]
230
244
  };
231
245
  }
@@ -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 { type DoomServerSessionPlugin, readPackageResource } from '@agimon-ai/doompi-core/serverFacet';\n\nimport { mountMcpSkills } from '../../../../../services/mcpSkills';\nimport { discoverServerSkills } from '../../../../../services/serverInventory';\nimport { createSkillCommands } from './skillCommands';\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 ...['doompi-author-skill', 'doompi-use-skill'].map((name) => ({\n name,\n kind: 'skill' as const,\n read: () => readPackageResource(import.meta.url, `src/prompts/${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"],"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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-skill",
3
- "version": "0.0.1-alpha.60",
3
+ "version": "0.0.1-alpha.62",
4
4
  "description": "Session skill catalogue, deferred skill discovery, and the skill browser for DoomPi.",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -70,18 +70,18 @@
70
70
  "access": "public"
71
71
  },
72
72
  "dependencies": {
73
- "@agimon-ai/doompi-config": "0.0.1-alpha.81",
74
- "@agimon-ai/doompi-core": "0.0.1-alpha.82",
75
- "@agimon-ai/doompi-domain": "0.0.1-alpha.60",
76
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.79",
77
- "@agimon-ai/doompi-ui": "0.0.1-alpha.82",
73
+ "@agimon-ai/doompi-config": "0.0.1-alpha.83",
74
+ "@agimon-ai/doompi-core": "0.0.1-alpha.84",
75
+ "@agimon-ai/doompi-domain": "0.0.1-alpha.62",
76
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.80",
77
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.84",
78
78
  "@deepseek-ai/cordis": "4.0.2",
79
79
  "gpt-tokenizer": "4.0.0",
80
80
  "typebox": "1.3.34"
81
81
  },
82
82
  "devDependencies": {
83
- "@agimon-ai/doompi-build": "0.0.1-alpha.11",
84
- "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.77",
83
+ "@agimon-ai/doompi-build": "0.0.1-alpha.12",
84
+ "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.78",
85
85
  "@deepseek-ai/cordis": "4.0.2",
86
86
  "@earendil-works/pi-coding-agent": "0.87.1",
87
87
  "@earendil-works/pi-tui": "0.87.1",
@@ -26,8 +26,12 @@ The frontmatter `name` must match the directory. Make the description specific e
26
26
  - For guidance that should appear only while DoomPi Help is active, put it at `src/prompts/<name>/SKILL.md`, publish `src/prompts`, link it from the package's H1-led `llms.txt`, and register a Help descriptor through `DOOM_HELP_SERVICE`.
27
27
  - For repository content selected by domains, place the skill in the plugin or shared skill source configured by `domains.yaml`; do not hard-code that directory into the Skill package.
28
28
 
29
- Help descriptors carry only `name` and `description`. The Help service resolves the owning package from the contribution's exact package `source` and `moduleUrl`, then wraps its `llms.txt`. Keep every index link inside the published package.
29
+ Pi Help descriptors carry only `name` and `description`. The Help service resolves the owning package from the contribution's exact package `source` and `moduleUrl`, then wraps its `llms.txt`. Keep every index link inside the published package.
30
+
31
+ On the server host, contribute the same Help skill through a named `resource/*.server.ts` route with `when: DOOM_HELP_WHEN` from `@agimon-ai/doompi-core/help`. Include its useful description and an absolute `path` resolved with `packageResourcePath(import.meta.url, ...)`; a reader alone does not make the skill discoverable in the agent prompt. Keep ordinary runtime skills under their existing conditions.
32
+
33
+ Package-owned Help tools use ordinary named tool routes, not documentation descriptors. Server tools use the existing Help condition and execution guards. Pi tools also need `createPiHelpToolGate(packageName, toolNames)`: install its services and onStart in the owning root, then authorize each invocation with `gate.assertActive`. Portable `when` metadata alone does not enforce the Pi gate. The Help README and Log package's `diagnose_agent` contribution show the complete pattern. Bound diagnostics to the current session, omit secrets, and use existing approval mechanisms for repairs.
30
34
 
31
35
  ## Verification
32
36
 
33
- Validate the skill frontmatter, ensure every relative reference resolves, and test the packed package rather than relying on checkout-only files. For Cordis contributions, cover late provider arrival, provider replacement, withdrawal, and package shutdown. Use `/skills` or `SPC e s` in an interactive session to confirm the expected owner and invocation name.
37
+ Validate the skill frontmatter, ensure every relative reference resolves, and test the packed package rather than relying on checkout-only files. For Cordis contributions, cover late provider arrival, provider replacement, withdrawal, and package shutdown. Test the actual prompt and tool surface before activation, while active, and after deactivation; stale calls must be rejected without removing ordinary capabilities. Use `/skills` or `SPC e s` in an interactive session to confirm the expected owner and invocation name. Do not add remote MCP exposure merely because a local Help contribution exists.