@agimon-ai/doompi-skill 0.0.1-alpha.2

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.
Files changed (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +40 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +1 -0
  4. package/dist/adapters/deferredSkills.cjs +12 -0
  5. package/dist/adapters/deferredSkills.cjs.map +1 -0
  6. package/dist/adapters/deferredSkills.mjs +12 -0
  7. package/dist/adapters/deferredSkills.mjs.map +1 -0
  8. package/dist/adapters/helpSkills.cjs +2 -0
  9. package/dist/adapters/helpSkills.cjs.map +1 -0
  10. package/dist/adapters/helpSkills.d.cts +27 -0
  11. package/dist/adapters/helpSkills.d.cts.map +1 -0
  12. package/dist/adapters/helpSkills.d.mts +27 -0
  13. package/dist/adapters/helpSkills.d.mts.map +1 -0
  14. package/dist/adapters/helpSkills.mjs +2 -0
  15. package/dist/adapters/helpSkills.mjs.map +1 -0
  16. package/dist/adapters/pi/extension.cjs +2 -0
  17. package/dist/adapters/pi/extension.cjs.map +1 -0
  18. package/dist/adapters/pi/extension.d.cts +12 -0
  19. package/dist/adapters/pi/extension.d.cts.map +1 -0
  20. package/dist/adapters/pi/extension.d.mts +12 -0
  21. package/dist/adapters/pi/extension.d.mts.map +1 -0
  22. package/dist/adapters/pi/extension.mjs +2 -0
  23. package/dist/adapters/pi/extension.mjs.map +1 -0
  24. package/dist/adapters/pi/skillReadiness.cjs +4 -0
  25. package/dist/adapters/pi/skillReadiness.cjs.map +1 -0
  26. package/dist/adapters/pi/skillReadiness.mjs +4 -0
  27. package/dist/adapters/pi/skillReadiness.mjs.map +1 -0
  28. package/dist/adapters/skillCatalog.cjs +2 -0
  29. package/dist/adapters/skillCatalog.cjs.map +1 -0
  30. package/dist/adapters/skillCatalog.d.cts +58 -0
  31. package/dist/adapters/skillCatalog.d.cts.map +1 -0
  32. package/dist/adapters/skillCatalog.d.mts +58 -0
  33. package/dist/adapters/skillCatalog.d.mts.map +1 -0
  34. package/dist/adapters/skillCatalog.mjs +2 -0
  35. package/dist/adapters/skillCatalog.mjs.map +1 -0
  36. package/dist/adapters/skillSources.cjs +2 -0
  37. package/dist/adapters/skillSources.cjs.map +1 -0
  38. package/dist/adapters/skillSources.d.cts +23 -0
  39. package/dist/adapters/skillSources.d.cts.map +1 -0
  40. package/dist/adapters/skillSources.d.mts +23 -0
  41. package/dist/adapters/skillSources.d.mts.map +1 -0
  42. package/dist/adapters/skillSources.mjs +2 -0
  43. package/dist/adapters/skillSources.mjs.map +1 -0
  44. package/dist/catalog.cjs +1 -0
  45. package/dist/catalog.d.cts +2 -0
  46. package/dist/catalog.d.mts +2 -0
  47. package/dist/catalog.mjs +1 -0
  48. package/dist/commands/skillsCommand.cjs +2 -0
  49. package/dist/commands/skillsCommand.cjs.map +1 -0
  50. package/dist/commands/skillsCommand.mjs +2 -0
  51. package/dist/commands/skillsCommand.mjs.map +1 -0
  52. package/dist/extensions/pi.cjs +1 -0
  53. package/dist/extensions/pi.d.cts +2 -0
  54. package/dist/extensions/pi.d.mts +2 -0
  55. package/dist/extensions/pi.mjs +1 -0
  56. package/dist/index.cjs +1 -0
  57. package/dist/index.d.cts +7 -0
  58. package/dist/index.d.mts +7 -0
  59. package/dist/index.mjs +1 -0
  60. package/dist/services/skillText.cjs +2 -0
  61. package/dist/services/skillText.cjs.map +1 -0
  62. package/dist/services/skillText.d.cts +13 -0
  63. package/dist/services/skillText.d.cts.map +1 -0
  64. package/dist/services/skillText.d.mts +13 -0
  65. package/dist/services/skillText.d.mts.map +1 -0
  66. package/dist/services/skillText.mjs +2 -0
  67. package/dist/services/skillText.mjs.map +1 -0
  68. package/dist/tui/skillsOverlay.cjs +4 -0
  69. package/dist/tui/skillsOverlay.cjs.map +1 -0
  70. package/dist/tui/skillsOverlay.d.cts +18 -0
  71. package/dist/tui/skillsOverlay.d.cts.map +1 -0
  72. package/dist/tui/skillsOverlay.d.mts +18 -0
  73. package/dist/tui/skillsOverlay.d.mts.map +1 -0
  74. package/dist/tui/skillsOverlay.mjs +4 -0
  75. package/dist/tui/skillsOverlay.mjs.map +1 -0
  76. package/dist/types/skills.cjs +2 -0
  77. package/dist/types/skills.cjs.map +1 -0
  78. package/dist/types/skills.d.cts +32 -0
  79. package/dist/types/skills.d.cts.map +1 -0
  80. package/dist/types/skills.d.mts +32 -0
  81. package/dist/types/skills.d.mts.map +1 -0
  82. package/dist/types/skills.mjs +2 -0
  83. package/dist/types/skills.mjs.map +1 -0
  84. package/llms.txt +7 -0
  85. package/package.json +99 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vuong Ngo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # @agimon-ai/doompi-skill
2
+
3
+ The session skill catalogue for [DoomPi](https://www.npmjs.com/package/@agimon-ai/doompi).
4
+
5
+ Pi loads skills from the directories it is given at startup. DoomPi adds two things on top: skills
6
+ that are discovered lazily rather than loaded eagerly, and a registry other extensions publish their
7
+ own skill directories to.
8
+
9
+ ## What it registers
10
+
11
+ | Surface | Behaviour |
12
+ | -------------------- | ------------------------------------------------------------------------------------------------ |
13
+ | `/skills` | a fullscreen browser over everything this session loaded and everything another domain would add |
14
+ | `SPC e s` | the leader binding for the same browser |
15
+ | `input` hook | expands `/skill:<name>` for a skill that was discovered but not loaded |
16
+ | `before_agent_start` | appends discovered skills to the system prompt, and surfaces discovery diagnostics once |
17
+
18
+ ## Deferred discovery
19
+
20
+ Scanning every skill directory is the slowest thing this package does, so it does not block the
21
+ session. Discovery starts on `session_start` as a numbered generation and is awaited only when
22
+ something needs the result. A reload starts a new generation; a snapshot that arrives from a
23
+ superseded one is discarded rather than shown, which is what keeps a fast reload from printing stale
24
+ diagnostics.
25
+
26
+ ## Contributing skill directories
27
+
28
+ Any extension can publish directories into the catalogue over the
29
+ `doom:pi:skills:v1:*` protocol in `@agimon-ai/doompi-extension-contracts/skills`. Contributions are
30
+ keyed by source rather than by handle, so a reload that re-registers the same package replaces its
31
+ entry instead of doubling it.
32
+
33
+ ## Installation
34
+
35
+ DoomPi depends on this package and activates it as fixed host core, so a DoomPi install already has
36
+ it. It is not selectable from `.doom/modes.yaml`.
37
+
38
+ ## License
39
+
40
+ MIT
@@ -0,0 +1 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));exports.__toESM=s;
@@ -0,0 +1,12 @@
1
+ const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:path");n=e.__toESM(n,1);let r=require("@earendil-works/pi-coding-agent"),i=require("node:url"),a=require("node:worker_threads");const o=`@earendil-works/pi-coding-agent`,s=`package.json`;function c(){let e=[process.argv[1],(0,i.fileURLToPath)(require("url").pathToFileURL(__filename).href),n.default.join(process.cwd(),s)];for(let r of e){if(!r)continue;let e=n.default.dirname(n.default.resolve(r));for(;;){let r=n.default.join(e,`node_modules`,o),a=n.default.join(r,s);try{let e=JSON.parse(t.default.readFileSync(a,`utf8`));if(typeof e.main==`string`){let a=n.default.resolve(r,e.main);if(t.default.existsSync(a))return(0,i.pathToFileURL)(a).href}}catch{}let c=n.default.dirname(e);if(c===e)break;e=c}}throw Error(`Cannot resolve ${o} from the DoomPi or host module trees`)}var l=class{#e;#t;constructor(e){this.#e=e}start(){if(this.#t)return this.#t;if(!this.#e.schedule&&!this.#e.load)return this.#t=this.#o(),this.#t;let e=this.#e.schedule??(e=>setImmediate(e));return this.#t=new Promise(t=>{let n=e=>{t(this.#i(e))};try{e(()=>{try{t(this.#a())}catch(e){n(e)}})}catch(e){n(e)}}),this.#t}ready(){return this.start()}#n(){return{cwd:this.#e.cwd,agentDir:this.#e.agentDir??(0,r.getAgentDir)(),skillPaths:[...this.#e.skillPaths],includeDefaults:!1}}#r(e){return{skills:e.skills,diagnostics:e.diagnostics.map(e=>`${e.path??this.#e.cwd}: ${e.message}`)}}#i(e){return{skills:[],diagnostics:[`${this.#e.cwd}: ${e instanceof Error?e.message:String(e)}`]}}#a(){return this.#r((this.#e.load??r.loadSkills)(this.#n()))}#o(){return new Promise(e=>{let t=!1,n=n=>{t||(t=!0,e(n))},r;try{r=new a.Worker(`
2
+ const { parentPort, workerData } = require('node:worker_threads');
3
+ void (async () => {
4
+ try {
5
+ const { loadSkills } = await import(workerData.piModuleUrl);
6
+ parentPort.postMessage({ ok: true, result: loadSkills(workerData.loadOptions) });
7
+ } catch (error) {
8
+ parentPort.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) });
9
+ }
10
+ })();
11
+ `,{eval:!0,workerData:{piModuleUrl:this.#e.piModuleUrl??c(),loadOptions:this.#n()}})}catch(e){n(this.#i(e));return}r.unref(),r.once(`message`,e=>{if(!e.ok||!e.result){n(this.#i(e.error??`Skill worker failed`));return}n(this.#r(e.result))}),r.once(`error`,e=>n(this.#i(e))),r.once(`exit`,e=>{e!==0&&n(this.#i(`Skill worker exited with code ${e}`))})})}};function u(e,t,n){return t.selectedTools&&!t.selectedTools.includes(`read`)?e:`${e}${(0,r.formatSkillsForPrompt)(n)}`}function d(e,n){if(!e.startsWith(`/skill:`))return e;let i=e.indexOf(` `),a=i===-1?e.slice(7):e.slice(7,i),o=n.find(e=>e.name===a);if(!o)return e;try{let n=i===-1?``:e.slice(i+1).trim(),a=(0,r.stripFrontmatter)(t.default.readFileSync(o.filePath,`utf8`)).trim(),s=`<skill name="${o.name}" location="${o.filePath}">\nReferences are relative to ${o.baseDir}.\n\n${a}\n</skill>`;return n?`${s}\n\n${n}`:s}catch{return e}}exports.DeferredSkillLoader=l,exports.buildPromptWithDeferredSkills=u,exports.expandDeferredSkillCommand=d;
12
+ //# sourceMappingURL=deferredSkills.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deferredSkills.cjs","names":["fileURLToPath","path","fs","pathToFileURL","#options","#promise","#loadInWorker","#failure","#load","getAgentDir","#snapshot","loadSkills","#loadOptions","Worker","formatSkillsForPrompt","stripFrontmatter"],"sources":["../../src/adapters/deferredSkills.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { Worker } from 'node:worker_threads';\nimport {\n type BuildSystemPromptOptions,\n formatSkillsForPrompt,\n getAgentDir,\n loadSkills,\n type Skill,\n stripFrontmatter,\n} from '@earendil-works/pi-coding-agent';\n\nexport interface DeferredSkillSnapshot {\n skills: Skill[];\n diagnostics: string[];\n}\n\nconst PI_PACKAGE = '@earendil-works/pi-coding-agent';\nconst PACKAGE_MANIFEST = 'package.json';\nconst SKILL_WORKER_SOURCE = `\nconst { parentPort, workerData } = require('node:worker_threads');\nvoid (async () => {\n try {\n const { loadSkills } = await import(workerData.piModuleUrl);\n parentPort.postMessage({ ok: true, result: loadSkills(workerData.loadOptions) });\n } catch (error) {\n parentPort.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) });\n }\n})();\n`;\n\nfunction installedPiModuleUrl(): string {\n const anchors = [process.argv[1], fileURLToPath(import.meta.url), path.join(process.cwd(), PACKAGE_MANIFEST)];\n for (const anchor of anchors) {\n if (!anchor) continue;\n let directory = path.dirname(path.resolve(anchor));\n while (true) {\n const packageRoot = path.join(directory, 'node_modules', PI_PACKAGE);\n const manifestPath = path.join(packageRoot, PACKAGE_MANIFEST);\n try {\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as { main?: unknown };\n if (typeof manifest.main === 'string') {\n const entry = path.resolve(packageRoot, manifest.main);\n if (fs.existsSync(entry)) return pathToFileURL(entry).href;\n }\n } catch {\n // This anchor does not expose Pi at this level; continue up its module chain.\n }\n const parent = path.dirname(directory);\n if (parent === directory) break;\n directory = parent;\n }\n }\n throw new Error(`Cannot resolve ${PI_PACKAGE} from the DoomPi or host module trees`);\n}\n\ninterface SkillWorkerResult {\n ok: boolean;\n result?: { skills: Skill[]; diagnostics: Array<{ path?: string; message: string }> };\n error?: string;\n}\n\nexport interface DeferredSkillLoaderOptions {\n cwd: string;\n skillPaths: readonly string[];\n agentDir?: string;\n piModuleUrl?: string;\n schedule?: (load: () => void) => void;\n load?: (options: { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean }) => {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n };\n}\n\n/**\n * One session's Doom-selected skill inventory.\n *\n * Construction is deliberately cheap. `start()` schedules the synchronous Pi\n * walker after session_start returns, while `ready()` is the single promise the\n * first input awaits before Pi expands `/skill:name`.\n */\nexport class DeferredSkillLoader {\n readonly #options: DeferredSkillLoaderOptions;\n #promise: Promise<DeferredSkillSnapshot> | undefined;\n\n constructor(options: DeferredSkillLoaderOptions) {\n this.#options = options;\n }\n\n start(): Promise<DeferredSkillSnapshot> {\n if (this.#promise) return this.#promise;\n\n if (!this.#options.schedule && !this.#options.load) {\n this.#promise = this.#loadInWorker();\n return this.#promise;\n }\n\n const schedule = this.#options.schedule ?? ((load: () => void) => setImmediate(load));\n this.#promise = new Promise((resolve) => {\n const fail = (error: unknown): void => {\n resolve(this.#failure(error));\n };\n try {\n schedule(() => {\n try {\n resolve(this.#load());\n } catch (error) {\n fail(error);\n }\n });\n } catch (error) {\n fail(error);\n }\n });\n return this.#promise;\n }\n\n ready(): Promise<DeferredSkillSnapshot> {\n return this.start();\n }\n\n #loadOptions(): { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean } {\n return {\n cwd: this.#options.cwd,\n agentDir: this.#options.agentDir ?? getAgentDir(),\n skillPaths: [...this.#options.skillPaths],\n includeDefaults: false,\n };\n }\n\n #snapshot(result: {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n }): DeferredSkillSnapshot {\n return {\n skills: result.skills,\n diagnostics: result.diagnostics.map(\n (diagnostic) => `${diagnostic.path ?? this.#options.cwd}: ${diagnostic.message}`,\n ),\n };\n }\n\n #failure(error: unknown): DeferredSkillSnapshot {\n return {\n skills: [],\n diagnostics: [`${this.#options.cwd}: ${error instanceof Error ? error.message : String(error)}`],\n };\n }\n\n #load(): DeferredSkillSnapshot {\n return this.#snapshot((this.#options.load ?? loadSkills)(this.#loadOptions()));\n }\n\n #loadInWorker(): Promise<DeferredSkillSnapshot> {\n return new Promise((resolve) => {\n let settled = false;\n const settle = (snapshot: DeferredSkillSnapshot): void => {\n if (settled) return;\n settled = true;\n resolve(snapshot);\n };\n let worker: Worker;\n try {\n worker = new Worker(SKILL_WORKER_SOURCE, {\n eval: true,\n workerData: {\n piModuleUrl: this.#options.piModuleUrl ?? installedPiModuleUrl(),\n loadOptions: this.#loadOptions(),\n },\n });\n } catch (error) {\n settle(this.#failure(error));\n return;\n }\n worker.unref();\n worker.once('message', (message: SkillWorkerResult) => {\n if (!message.ok || !message.result) {\n settle(this.#failure(message.error ?? 'Skill worker failed'));\n return;\n }\n settle(this.#snapshot(message.result));\n });\n worker.once('error', (error) => settle(this.#failure(error)));\n worker.once('exit', (code) => {\n if (code !== 0) settle(this.#failure(`Skill worker exited with code ${code}`));\n });\n });\n }\n}\n\n/** Appends only the inventory Pi would append for these deferred skills. */\nexport function buildPromptWithDeferredSkills(\n systemPrompt: string,\n options: BuildSystemPromptOptions,\n skills: Skill[],\n): string {\n if (options.selectedTools && !options.selectedTools.includes('read')) return systemPrompt;\n return `${systemPrompt}${formatSkillsForPrompt(skills)}`;\n}\n\n/** Expands a deferred `/skill:name` before Pi consults its synchronous inventory. */\nexport function expandDeferredSkillCommand(text: string, skills: readonly Skill[]): string {\n if (!text.startsWith('/skill:')) return text;\n const spaceIndex = text.indexOf(' ');\n const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);\n const skill = skills.find((candidate) => candidate.name === skillName);\n if (!skill) return text;\n\n try {\n const args = spaceIndex === -1 ? '' : text.slice(spaceIndex + 1).trim();\n const body = stripFrontmatter(fs.readFileSync(skill.filePath, 'utf8')).trim();\n const skillBlock = `<skill name=\"${skill.name}\" location=\"${skill.filePath}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n return args ? `${skillBlock}\\n\\n${args}` : skillBlock;\n } catch {\n // Pi uses the same literal-text fallback if a skill disappears between\n // discovery and submission; the agent can still respond to the command.\n return text;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import e from"node:fs";import t from"node:path";import{formatSkillsForPrompt as n,getAgentDir as r,loadSkills as i,stripFrontmatter as a}from"@earendil-works/pi-coding-agent";import{fileURLToPath as o,pathToFileURL as s}from"node:url";import{Worker as c}from"node:worker_threads";const l=`@earendil-works/pi-coding-agent`,u=`package.json`;function d(){let n=[process.argv[1],o(import.meta.url),t.join(process.cwd(),u)];for(let r of n){if(!r)continue;let n=t.dirname(t.resolve(r));for(;;){let r=t.join(n,`node_modules`,l),i=t.join(r,u);try{let n=JSON.parse(e.readFileSync(i,`utf8`));if(typeof n.main==`string`){let i=t.resolve(r,n.main);if(e.existsSync(i))return s(i).href}}catch{}let a=t.dirname(n);if(a===n)break;n=a}}throw Error(`Cannot resolve ${l} from the DoomPi or host module trees`)}var f=class{#e;#t;constructor(e){this.#e=e}start(){if(this.#t)return this.#t;if(!this.#e.schedule&&!this.#e.load)return this.#t=this.#o(),this.#t;let e=this.#e.schedule??(e=>setImmediate(e));return this.#t=new Promise(t=>{let n=e=>{t(this.#i(e))};try{e(()=>{try{t(this.#a())}catch(e){n(e)}})}catch(e){n(e)}}),this.#t}ready(){return this.start()}#n(){return{cwd:this.#e.cwd,agentDir:this.#e.agentDir??r(),skillPaths:[...this.#e.skillPaths],includeDefaults:!1}}#r(e){return{skills:e.skills,diagnostics:e.diagnostics.map(e=>`${e.path??this.#e.cwd}: ${e.message}`)}}#i(e){return{skills:[],diagnostics:[`${this.#e.cwd}: ${e instanceof Error?e.message:String(e)}`]}}#a(){return this.#r((this.#e.load??i)(this.#n()))}#o(){return new Promise(e=>{let t=!1,n=n=>{t||(t=!0,e(n))},r;try{r=new c(`
2
+ const { parentPort, workerData } = require('node:worker_threads');
3
+ void (async () => {
4
+ try {
5
+ const { loadSkills } = await import(workerData.piModuleUrl);
6
+ parentPort.postMessage({ ok: true, result: loadSkills(workerData.loadOptions) });
7
+ } catch (error) {
8
+ parentPort.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) });
9
+ }
10
+ })();
11
+ `,{eval:!0,workerData:{piModuleUrl:this.#e.piModuleUrl??d(),loadOptions:this.#n()}})}catch(e){n(this.#i(e));return}r.unref(),r.once(`message`,e=>{if(!e.ok||!e.result){n(this.#i(e.error??`Skill worker failed`));return}n(this.#r(e.result))}),r.once(`error`,e=>n(this.#i(e))),r.once(`exit`,e=>{e!==0&&n(this.#i(`Skill worker exited with code ${e}`))})})}};function p(e,t,r){return t.selectedTools&&!t.selectedTools.includes(`read`)?e:`${e}${n(r)}`}function m(t,n){if(!t.startsWith(`/skill:`))return t;let r=t.indexOf(` `),i=r===-1?t.slice(7):t.slice(7,r),o=n.find(e=>e.name===i);if(!o)return t;try{let n=r===-1?``:t.slice(r+1).trim(),i=a(e.readFileSync(o.filePath,`utf8`)).trim(),s=`<skill name="${o.name}" location="${o.filePath}">\nReferences are relative to ${o.baseDir}.\n\n${i}\n</skill>`;return n?`${s}\n\n${n}`:s}catch{return t}}export{f as DeferredSkillLoader,p as buildPromptWithDeferredSkills,m as expandDeferredSkillCommand};
12
+ //# sourceMappingURL=deferredSkills.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deferredSkills.mjs","names":["#options","#promise","#loadInWorker","#failure","#load","#snapshot","#loadOptions"],"sources":["../../src/adapters/deferredSkills.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { Worker } from 'node:worker_threads';\nimport {\n type BuildSystemPromptOptions,\n formatSkillsForPrompt,\n getAgentDir,\n loadSkills,\n type Skill,\n stripFrontmatter,\n} from '@earendil-works/pi-coding-agent';\n\nexport interface DeferredSkillSnapshot {\n skills: Skill[];\n diagnostics: string[];\n}\n\nconst PI_PACKAGE = '@earendil-works/pi-coding-agent';\nconst PACKAGE_MANIFEST = 'package.json';\nconst SKILL_WORKER_SOURCE = `\nconst { parentPort, workerData } = require('node:worker_threads');\nvoid (async () => {\n try {\n const { loadSkills } = await import(workerData.piModuleUrl);\n parentPort.postMessage({ ok: true, result: loadSkills(workerData.loadOptions) });\n } catch (error) {\n parentPort.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) });\n }\n})();\n`;\n\nfunction installedPiModuleUrl(): string {\n const anchors = [process.argv[1], fileURLToPath(import.meta.url), path.join(process.cwd(), PACKAGE_MANIFEST)];\n for (const anchor of anchors) {\n if (!anchor) continue;\n let directory = path.dirname(path.resolve(anchor));\n while (true) {\n const packageRoot = path.join(directory, 'node_modules', PI_PACKAGE);\n const manifestPath = path.join(packageRoot, PACKAGE_MANIFEST);\n try {\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as { main?: unknown };\n if (typeof manifest.main === 'string') {\n const entry = path.resolve(packageRoot, manifest.main);\n if (fs.existsSync(entry)) return pathToFileURL(entry).href;\n }\n } catch {\n // This anchor does not expose Pi at this level; continue up its module chain.\n }\n const parent = path.dirname(directory);\n if (parent === directory) break;\n directory = parent;\n }\n }\n throw new Error(`Cannot resolve ${PI_PACKAGE} from the DoomPi or host module trees`);\n}\n\ninterface SkillWorkerResult {\n ok: boolean;\n result?: { skills: Skill[]; diagnostics: Array<{ path?: string; message: string }> };\n error?: string;\n}\n\nexport interface DeferredSkillLoaderOptions {\n cwd: string;\n skillPaths: readonly string[];\n agentDir?: string;\n piModuleUrl?: string;\n schedule?: (load: () => void) => void;\n load?: (options: { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean }) => {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n };\n}\n\n/**\n * One session's Doom-selected skill inventory.\n *\n * Construction is deliberately cheap. `start()` schedules the synchronous Pi\n * walker after session_start returns, while `ready()` is the single promise the\n * first input awaits before Pi expands `/skill:name`.\n */\nexport class DeferredSkillLoader {\n readonly #options: DeferredSkillLoaderOptions;\n #promise: Promise<DeferredSkillSnapshot> | undefined;\n\n constructor(options: DeferredSkillLoaderOptions) {\n this.#options = options;\n }\n\n start(): Promise<DeferredSkillSnapshot> {\n if (this.#promise) return this.#promise;\n\n if (!this.#options.schedule && !this.#options.load) {\n this.#promise = this.#loadInWorker();\n return this.#promise;\n }\n\n const schedule = this.#options.schedule ?? ((load: () => void) => setImmediate(load));\n this.#promise = new Promise((resolve) => {\n const fail = (error: unknown): void => {\n resolve(this.#failure(error));\n };\n try {\n schedule(() => {\n try {\n resolve(this.#load());\n } catch (error) {\n fail(error);\n }\n });\n } catch (error) {\n fail(error);\n }\n });\n return this.#promise;\n }\n\n ready(): Promise<DeferredSkillSnapshot> {\n return this.start();\n }\n\n #loadOptions(): { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean } {\n return {\n cwd: this.#options.cwd,\n agentDir: this.#options.agentDir ?? getAgentDir(),\n skillPaths: [...this.#options.skillPaths],\n includeDefaults: false,\n };\n }\n\n #snapshot(result: {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n }): DeferredSkillSnapshot {\n return {\n skills: result.skills,\n diagnostics: result.diagnostics.map(\n (diagnostic) => `${diagnostic.path ?? this.#options.cwd}: ${diagnostic.message}`,\n ),\n };\n }\n\n #failure(error: unknown): DeferredSkillSnapshot {\n return {\n skills: [],\n diagnostics: [`${this.#options.cwd}: ${error instanceof Error ? error.message : String(error)}`],\n };\n }\n\n #load(): DeferredSkillSnapshot {\n return this.#snapshot((this.#options.load ?? loadSkills)(this.#loadOptions()));\n }\n\n #loadInWorker(): Promise<DeferredSkillSnapshot> {\n return new Promise((resolve) => {\n let settled = false;\n const settle = (snapshot: DeferredSkillSnapshot): void => {\n if (settled) return;\n settled = true;\n resolve(snapshot);\n };\n let worker: Worker;\n try {\n worker = new Worker(SKILL_WORKER_SOURCE, {\n eval: true,\n workerData: {\n piModuleUrl: this.#options.piModuleUrl ?? installedPiModuleUrl(),\n loadOptions: this.#loadOptions(),\n },\n });\n } catch (error) {\n settle(this.#failure(error));\n return;\n }\n worker.unref();\n worker.once('message', (message: SkillWorkerResult) => {\n if (!message.ok || !message.result) {\n settle(this.#failure(message.error ?? 'Skill worker failed'));\n return;\n }\n settle(this.#snapshot(message.result));\n });\n worker.once('error', (error) => settle(this.#failure(error)));\n worker.once('exit', (code) => {\n if (code !== 0) settle(this.#failure(`Skill worker exited with code ${code}`));\n });\n });\n }\n}\n\n/** Appends only the inventory Pi would append for these deferred skills. */\nexport function buildPromptWithDeferredSkills(\n systemPrompt: string,\n options: BuildSystemPromptOptions,\n skills: Skill[],\n): string {\n if (options.selectedTools && !options.selectedTools.includes('read')) return systemPrompt;\n return `${systemPrompt}${formatSkillsForPrompt(skills)}`;\n}\n\n/** Expands a deferred `/skill:name` before Pi consults its synchronous inventory. */\nexport function expandDeferredSkillCommand(text: string, skills: readonly Skill[]): string {\n if (!text.startsWith('/skill:')) return text;\n const spaceIndex = text.indexOf(' ');\n const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);\n const skill = skills.find((candidate) => candidate.name === skillName);\n if (!skill) return text;\n\n try {\n const args = spaceIndex === -1 ? '' : text.slice(spaceIndex + 1).trim();\n const body = stripFrontmatter(fs.readFileSync(skill.filePath, 'utf8')).trim();\n const skillBlock = `<skill name=\"${skill.name}\" location=\"${skill.filePath}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n return args ? `${skillBlock}\\n\\n${args}` : skillBlock;\n } catch {\n // Pi uses the same literal-text fallback if a skill disappears between\n // discovery and submission; the agent can still respond to the command.\n return text;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ let e=require("@earendil-works/pi-coding-agent"),t=require("@agimon-ai/doompi-extension-contracts/help");function n(e){return e.slice(0,t.DOOM_HELP_MAX_DIAGNOSTIC_LENGTH)}function r(t){return{name:t.name,description:t.description,filePath:t.filePath,baseDir:t.baseDir,sourceInfo:(0,e.createSyntheticSourceInfo)(t.filePath,{source:t.source,scope:`temporary`,origin:`package`,baseDir:t.baseDir}),disableModelInvocation:!1}}function i(e){return!e||e.activation===`inactive`?[]:[...e.skills].sort((e,t)=>e.source.localeCompare(t.source)||e.name.localeCompare(t.name)||e.filePath.localeCompare(t.filePath))}function a(e){return e?e.diagnostics.map(e=>n(`Help ${e.source??`host`} [${e.code}]: ${e.message}`)):[]}function o(e,o,s=[]){let c=new Set([...e.normalSkills.map(e=>e.name),...e.deferredSkills.map(e=>e.name),...e.normalSkillNames??[]]),l=[],u=[...a(o),...s.map(n)];for(let e of i(o)){if(c.has(e.name)){u.push(n(`Help ${e.source} [HELP_SKILL_COLLISION]: '${e.name}' was ignored because a normal skill wins.`));continue}c.add(e.name),l.push(r(e))}let d=u.slice(0,t.DOOM_HELP_MAX_DIAGNOSTICS),f=[...e.deferredSkills,...l];return{skills:[...e.normalSkills,...f],additionalSkills:f,helpSkills:l,diagnostics:d,diagnosticKey:`${o?.hostGeneration??`absent`}:${o?.revision??0}:${d.join(``)}`}}function s(e){let r=[],i=(0,t.createDoomHelpSnapshotClient)(e,{source:`@agimon-ai/doompi`,onError(e){let t=e instanceof Error?e.message:String(e);r.splice(0,r.length,n(`Help client [HELP_SNAPSHOT_FAILED]: ${t}`))}}),a=!1;return{merge(e){return o(e,i.getSnapshot(),r)},dispose(){a||(a=!0,i.dispose(),r.splice(0))}}}exports.createActiveHelpSkillView=s,exports.mergeActiveHelpSkills=o;
2
+ //# sourceMappingURL=helpSkills.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpSkills.cjs","names":["DOOM_HELP_MAX_DIAGNOSTIC_LENGTH","createSyntheticSourceInfo","DOOM_HELP_MAX_DIAGNOSTICS","createDoomHelpSnapshotClient"],"sources":["../../src/adapters/helpSkills.ts"],"sourcesContent":["import type { DoomExtensionHost } from '@agimon-ai/doompi-extension-contracts/config';\nimport {\n createDoomHelpSnapshotClient,\n DOOM_HELP_MAX_DIAGNOSTIC_LENGTH,\n DOOM_HELP_MAX_DIAGNOSTICS,\n type DoomHelpSkill,\n type DoomHelpSnapshot,\n type DoomHelpSnapshotClient,\n} from '@agimon-ai/doompi-extension-contracts/help';\nimport { createSyntheticSourceInfo, type ExtensionAPI, type Skill } from '@earendil-works/pi-coding-agent';\n\nconst HELP_CLIENT_SOURCE = '@agimon-ai/doompi';\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 /** Static, deferred, then accepted Help skills in invocation-precedence order. */\n skills: readonly Skill[];\n /** Deferred and Help skills that Doom Pi must append to 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 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 occupiedNames = new Set([\n ...input.normalSkills.map((skill) => skill.name),\n ...input.deferredSkills.map((skill) => skill.name),\n ...(input.normalSkillNames ?? []),\n ]);\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 = [...input.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(pi: Pick<ExtensionAPI, 'events' | 'on'>): ActiveHelpSkillView {\n const clientDiagnostics: string[] = [];\n const client: DoomHelpSnapshotClient = createDoomHelpSnapshotClient(pi as unknown as DoomExtensionHost, {\n source: HELP_CLIENT_SOURCE,\n onError(error) {\n const message = error instanceof Error ? error.message : String(error);\n clientDiagnostics.splice(\n 0,\n clientDiagnostics.length,\n boundedDiagnostic(`Help client [HELP_SNAPSHOT_FAILED]: ${message}`),\n );\n },\n });\n let disposed = false;\n return {\n merge(input) {\n return mergeActiveHelpSkills(input, client.getSnapshot(), clientDiagnostics);\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n client.dispose();\n clientDiagnostics.splice(0);\n },\n };\n}\n"],"mappings":"yGAoCA,SAAS,EAAkB,EAAyB,CAClD,OAAO,EAAQ,MAAM,EAAGA,EAAAA,+BAA+B,CACzD,CAEA,SAAS,EAAQ,EAA6B,CAC5C,MAAO,CACL,KAAM,EAAM,KACZ,YAAa,EAAM,YACnB,SAAU,EAAM,SAChB,QAAS,EAAM,QACf,YAAA,EAAYC,EAAAA,0BAAAA,CAA0B,EAAM,SAAU,CACpD,OAAQ,EAAM,OACd,MAAO,YACP,OAAQ,UACR,QAAS,EAAM,OACjB,CAAC,EACD,uBAAwB,EAC1B,CACF,CAEA,SAAS,EAAiB,EAAyD,CAEjF,MADI,CAAC,GAAY,EAAS,aAAe,WAAmB,CAAC,EACtD,CAAC,GAAG,EAAS,MAAM,CAAC,CAAC,MACzB,EAAM,IACL,EAAK,OAAO,cAAc,EAAM,MAAM,GACtC,EAAK,KAAK,cAAc,EAAM,IAAI,GAClC,EAAK,SAAS,cAAc,EAAM,QAAQ,CAC9C,CACF,CAEA,SAAS,EAAoB,EAAkD,CAE7E,OADK,EACE,EAAS,YAAY,IAAK,GAC/B,EAAkB,QAAQ,EAAM,QAAU,OAAO,IAAI,EAAM,KAAK,KAAK,EAAM,SAAS,CACtF,EAHsB,CAAC,CAIzB,CAEA,SAAgB,EACd,EACA,EACA,EAAuC,CAAC,EAClB,CACtB,IAAM,EAAgB,IAAI,IAAI,CAC5B,GAAG,EAAM,aAAa,IAAK,GAAU,EAAM,IAAI,EAC/C,GAAG,EAAM,eAAe,IAAK,GAAU,EAAM,IAAI,EACjD,GAAI,EAAM,kBAAoB,CAAC,CACjC,CAAC,EACK,EAAsB,CAAC,EACvB,EAAc,CAAC,GAAG,EAAoB,CAAQ,EAAG,GAAG,EAAkB,IAAI,CAAiB,CAAC,EAElG,IAAK,IAAM,KAAa,EAAiB,CAAQ,EAAG,CAClD,GAAI,EAAc,IAAI,EAAU,IAAI,EAAG,CACrC,EAAY,KACV,EACE,QAAQ,EAAU,OAAO,4BAA4B,EAAU,KAAK,2CACtE,CACF,EACA,QACF,CACA,EAAc,IAAI,EAAU,IAAI,EAChC,EAAW,KAAK,EAAQ,CAAS,CAAC,CACpC,CAEA,IAAM,EAAqB,EAAY,MAAM,EAAGC,EAAAA,yBAAyB,EACnE,EAAmB,CAAC,GAAG,EAAM,eAAgB,GAAG,CAAU,EAChE,MAAO,CACL,OAAQ,CAAC,GAAG,EAAM,aAAc,GAAG,CAAgB,EACnD,mBACA,aACA,YAAa,EACb,cAAe,GAAG,GAAU,gBAAkB,SAAS,GAAG,GAAU,UAAY,EAAE,GAAG,EAAmB,KAAK,GAAQ,GACvH,CACF,CAEA,SAAgB,EAA0B,EAA8D,CACtG,IAAM,EAA8B,CAAC,EAC/B,GAAA,EAAiCC,EAAAA,6BAAAA,CAA6B,EAAoC,CACtG,OAAQ,oBACR,QAAQ,EAAO,CACb,IAAM,EAAU,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EACrE,EAAkB,OAChB,EACA,EAAkB,OAClB,EAAkB,uCAAuC,GAAS,CACpE,CACF,CACF,CAAC,EACG,EAAW,GACf,MAAO,CACL,MAAM,EAAO,CACX,OAAO,EAAsB,EAAO,EAAO,YAAY,EAAG,CAAiB,CAC7E,EACA,SAAU,CACJ,IACJ,EAAW,GACX,EAAO,QAAQ,EACf,EAAkB,OAAO,CAAC,EAC5B,CACF,CACF"}
@@ -0,0 +1,27 @@
1
+ import { ExtensionAPI, Skill } from "@earendil-works/pi-coding-agent";
2
+ import "@agimon-ai/doompi-extension-contracts/help";
3
+ //#region src/adapters/helpSkills.d.ts
4
+ interface SkillInventoryInput {
5
+ normalSkills: readonly Skill[];
6
+ deferredSkills: readonly Skill[];
7
+ /** Names from Pi's command catalog when full normal Skill records are unavailable. */
8
+ normalSkillNames?: readonly string[];
9
+ }
10
+ interface MergedSkillInventory {
11
+ /** Static, deferred, then accepted Help skills in invocation-precedence order. */
12
+ skills: readonly Skill[];
13
+ /** Deferred and Help skills that Doom Pi must append to Pi's existing prompt inventory. */
14
+ additionalSkills: readonly Skill[];
15
+ /** Accepted Help skills, retained separately for the Help catalog group. */
16
+ helpSkills: readonly Skill[];
17
+ diagnostics: readonly string[];
18
+ diagnosticKey: string;
19
+ }
20
+ interface ActiveHelpSkillView {
21
+ merge(input: SkillInventoryInput): MergedSkillInventory;
22
+ dispose(): void;
23
+ }
24
+ declare function createActiveHelpSkillView(pi: Pick<ExtensionAPI, 'events' | 'on'>): ActiveHelpSkillView;
25
+ //#endregion
26
+ export { ActiveHelpSkillView, MergedSkillInventory, SkillInventoryInput, createActiveHelpSkillView };
27
+ //# sourceMappingURL=helpSkills.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpSkills.d.cts","names":[],"sources":["../../src/adapters/helpSkills.ts"],"mappings":";;;UAaiB;EACf,uBAAuB;EACvB,yBAAyB;;EAEzB;;UAGe;;EAEf,iBAAiB;;EAEjB,2BAA2B;;EAE3B,qBAAqB;EACrB;EACA;;UAGe;EACf,MAAM,OAAO,sBAAsB;EACnC;;iBA6Ec,0BAA0B,IAAI,KAAK,iCAAiC"}
@@ -0,0 +1,27 @@
1
+ import { ExtensionAPI, Skill } from "@earendil-works/pi-coding-agent";
2
+ import "@agimon-ai/doompi-extension-contracts/help";
3
+ //#region src/adapters/helpSkills.d.ts
4
+ interface SkillInventoryInput {
5
+ normalSkills: readonly Skill[];
6
+ deferredSkills: readonly Skill[];
7
+ /** Names from Pi's command catalog when full normal Skill records are unavailable. */
8
+ normalSkillNames?: readonly string[];
9
+ }
10
+ interface MergedSkillInventory {
11
+ /** Static, deferred, then accepted Help skills in invocation-precedence order. */
12
+ skills: readonly Skill[];
13
+ /** Deferred and Help skills that Doom Pi must append to Pi's existing prompt inventory. */
14
+ additionalSkills: readonly Skill[];
15
+ /** Accepted Help skills, retained separately for the Help catalog group. */
16
+ helpSkills: readonly Skill[];
17
+ diagnostics: readonly string[];
18
+ diagnosticKey: string;
19
+ }
20
+ interface ActiveHelpSkillView {
21
+ merge(input: SkillInventoryInput): MergedSkillInventory;
22
+ dispose(): void;
23
+ }
24
+ declare function createActiveHelpSkillView(pi: Pick<ExtensionAPI, 'events' | 'on'>): ActiveHelpSkillView;
25
+ //#endregion
26
+ export { ActiveHelpSkillView, MergedSkillInventory, SkillInventoryInput, createActiveHelpSkillView };
27
+ //# sourceMappingURL=helpSkills.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpSkills.d.mts","names":[],"sources":["../../src/adapters/helpSkills.ts"],"mappings":";;;UAaiB;EACf,uBAAuB;EACvB,yBAAyB;;EAEzB;;UAGe;;EAEf,iBAAiB;;EAEjB,2BAA2B;;EAE3B,qBAAqB;EACrB;EACA;;UAGe;EACf,MAAM,OAAO,sBAAsB;EACnC;;iBA6Ec,0BAA0B,IAAI,KAAK,iCAAiC"}
@@ -0,0 +1,2 @@
1
+ import{createSyntheticSourceInfo as e}from"@earendil-works/pi-coding-agent";import{DOOM_HELP_MAX_DIAGNOSTICS as t,DOOM_HELP_MAX_DIAGNOSTIC_LENGTH as n,createDoomHelpSnapshotClient as r}from"@agimon-ai/doompi-extension-contracts/help";function i(e){return e.slice(0,n)}function a(t){return{name:t.name,description:t.description,filePath:t.filePath,baseDir:t.baseDir,sourceInfo:e(t.filePath,{source:t.source,scope:`temporary`,origin:`package`,baseDir:t.baseDir}),disableModelInvocation:!1}}function o(e){return!e||e.activation===`inactive`?[]:[...e.skills].sort((e,t)=>e.source.localeCompare(t.source)||e.name.localeCompare(t.name)||e.filePath.localeCompare(t.filePath))}function s(e){return e?e.diagnostics.map(e=>i(`Help ${e.source??`host`} [${e.code}]: ${e.message}`)):[]}function c(e,n,r=[]){let c=new Set([...e.normalSkills.map(e=>e.name),...e.deferredSkills.map(e=>e.name),...e.normalSkillNames??[]]),l=[],u=[...s(n),...r.map(i)];for(let e of o(n)){if(c.has(e.name)){u.push(i(`Help ${e.source} [HELP_SKILL_COLLISION]: '${e.name}' was ignored because a normal skill wins.`));continue}c.add(e.name),l.push(a(e))}let d=u.slice(0,t),f=[...e.deferredSkills,...l];return{skills:[...e.normalSkills,...f],additionalSkills:f,helpSkills:l,diagnostics:d,diagnosticKey:`${n?.hostGeneration??`absent`}:${n?.revision??0}:${d.join(``)}`}}function l(e){let t=[],n=r(e,{source:`@agimon-ai/doompi`,onError(e){let n=e instanceof Error?e.message:String(e);t.splice(0,t.length,i(`Help client [HELP_SNAPSHOT_FAILED]: ${n}`))}}),a=!1;return{merge(e){return c(e,n.getSnapshot(),t)},dispose(){a||(a=!0,n.dispose(),t.splice(0))}}}export{l as createActiveHelpSkillView,c as mergeActiveHelpSkills};
2
+ //# sourceMappingURL=helpSkills.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpSkills.mjs","names":[],"sources":["../../src/adapters/helpSkills.ts"],"sourcesContent":["import type { DoomExtensionHost } from '@agimon-ai/doompi-extension-contracts/config';\nimport {\n createDoomHelpSnapshotClient,\n DOOM_HELP_MAX_DIAGNOSTIC_LENGTH,\n DOOM_HELP_MAX_DIAGNOSTICS,\n type DoomHelpSkill,\n type DoomHelpSnapshot,\n type DoomHelpSnapshotClient,\n} from '@agimon-ai/doompi-extension-contracts/help';\nimport { createSyntheticSourceInfo, type ExtensionAPI, type Skill } from '@earendil-works/pi-coding-agent';\n\nconst HELP_CLIENT_SOURCE = '@agimon-ai/doompi';\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 /** Static, deferred, then accepted Help skills in invocation-precedence order. */\n skills: readonly Skill[];\n /** Deferred and Help skills that Doom Pi must append to 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 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 occupiedNames = new Set([\n ...input.normalSkills.map((skill) => skill.name),\n ...input.deferredSkills.map((skill) => skill.name),\n ...(input.normalSkillNames ?? []),\n ]);\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 = [...input.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(pi: Pick<ExtensionAPI, 'events' | 'on'>): ActiveHelpSkillView {\n const clientDiagnostics: string[] = [];\n const client: DoomHelpSnapshotClient = createDoomHelpSnapshotClient(pi as unknown as DoomExtensionHost, {\n source: HELP_CLIENT_SOURCE,\n onError(error) {\n const message = error instanceof Error ? error.message : String(error);\n clientDiagnostics.splice(\n 0,\n clientDiagnostics.length,\n boundedDiagnostic(`Help client [HELP_SNAPSHOT_FAILED]: ${message}`),\n );\n },\n });\n let disposed = false;\n return {\n merge(input) {\n return mergeActiveHelpSkills(input, client.getSnapshot(), clientDiagnostics);\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n client.dispose();\n clientDiagnostics.splice(0);\n },\n };\n}\n"],"mappings":"0OAoCA,SAAS,EAAkB,EAAyB,CAClD,OAAO,EAAQ,MAAM,EAAG,CAA+B,CACzD,CAEA,SAAS,EAAQ,EAA6B,CAC5C,MAAO,CACL,KAAM,EAAM,KACZ,YAAa,EAAM,YACnB,SAAU,EAAM,SAChB,QAAS,EAAM,QACf,WAAY,EAA0B,EAAM,SAAU,CACpD,OAAQ,EAAM,OACd,MAAO,YACP,OAAQ,UACR,QAAS,EAAM,OACjB,CAAC,EACD,uBAAwB,EAC1B,CACF,CAEA,SAAS,EAAiB,EAAyD,CAEjF,MADI,CAAC,GAAY,EAAS,aAAe,WAAmB,CAAC,EACtD,CAAC,GAAG,EAAS,MAAM,CAAC,CAAC,MACzB,EAAM,IACL,EAAK,OAAO,cAAc,EAAM,MAAM,GACtC,EAAK,KAAK,cAAc,EAAM,IAAI,GAClC,EAAK,SAAS,cAAc,EAAM,QAAQ,CAC9C,CACF,CAEA,SAAS,EAAoB,EAAkD,CAE7E,OADK,EACE,EAAS,YAAY,IAAK,GAC/B,EAAkB,QAAQ,EAAM,QAAU,OAAO,IAAI,EAAM,KAAK,KAAK,EAAM,SAAS,CACtF,EAHsB,CAAC,CAIzB,CAEA,SAAgB,EACd,EACA,EACA,EAAuC,CAAC,EAClB,CACtB,IAAM,EAAgB,IAAI,IAAI,CAC5B,GAAG,EAAM,aAAa,IAAK,GAAU,EAAM,IAAI,EAC/C,GAAG,EAAM,eAAe,IAAK,GAAU,EAAM,IAAI,EACjD,GAAI,EAAM,kBAAoB,CAAC,CACjC,CAAC,EACK,EAAsB,CAAC,EACvB,EAAc,CAAC,GAAG,EAAoB,CAAQ,EAAG,GAAG,EAAkB,IAAI,CAAiB,CAAC,EAElG,IAAK,IAAM,KAAa,EAAiB,CAAQ,EAAG,CAClD,GAAI,EAAc,IAAI,EAAU,IAAI,EAAG,CACrC,EAAY,KACV,EACE,QAAQ,EAAU,OAAO,4BAA4B,EAAU,KAAK,2CACtE,CACF,EACA,QACF,CACA,EAAc,IAAI,EAAU,IAAI,EAChC,EAAW,KAAK,EAAQ,CAAS,CAAC,CACpC,CAEA,IAAM,EAAqB,EAAY,MAAM,EAAG,CAAyB,EACnE,EAAmB,CAAC,GAAG,EAAM,eAAgB,GAAG,CAAU,EAChE,MAAO,CACL,OAAQ,CAAC,GAAG,EAAM,aAAc,GAAG,CAAgB,EACnD,mBACA,aACA,YAAa,EACb,cAAe,GAAG,GAAU,gBAAkB,SAAS,GAAG,GAAU,UAAY,EAAE,GAAG,EAAmB,KAAK,GAAQ,GACvH,CACF,CAEA,SAAgB,EAA0B,EAA8D,CACtG,IAAM,EAA8B,CAAC,EAC/B,EAAiC,EAA6B,EAAoC,CACtG,OAAQ,oBACR,QAAQ,EAAO,CACb,IAAM,EAAU,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EACrE,EAAkB,OAChB,EACA,EAAkB,OAClB,EAAkB,uCAAuC,GAAS,CACpE,CACF,CACF,CAAC,EACG,EAAW,GACf,MAAO,CACL,MAAM,EAAO,CACX,OAAO,EAAsB,EAAO,EAAO,YAAY,EAAG,CAAiB,CAC7E,EACA,SAAU,CACJ,IACJ,EAAW,GACX,EAAO,QAAQ,EACf,EAAkB,OAAO,CAAC,EAC5B,CACF,CACF"}
@@ -0,0 +1,2 @@
1
+ const e=require("../helpSkills.cjs"),t=require("../../types/skills.cjs"),n=require("../../commands/skillsCommand.cjs"),r=require("../skillSources.cjs"),i=require("./skillReadiness.cjs");let a=require("@agimon-ai/doompi-config/harnessStore"),o=require("@agimon-ai/doompi-config/piContext"),s=require("@agimon-ai/doompi-ui/leader"),c=require("@deepseek-ai/cordis");function l(){let e,t,n;return{catalog:()=>e??=Promise.resolve().then(()=>require("../skillCatalog.cjs")),deferred:()=>t??=Promise.resolve().then(()=>require("../deferredSkills.cjs")),overlay:()=>n??=Promise.resolve().then(()=>require("../../tui/skillsOverlay.cjs"))}}async function u(u){let d=new c.Context,f=l(),p,m=()=>(p??=d.fiber.dispose(),p);try{d.effect(function*(){let c=r.provideSkillSources(u);yield()=>c.dispose();let l=e.createActiveHelpSkillView(u);yield()=>l.dispose(),yield(0,s.registerDoomLeaderContribution)(u,t.SKILLS_LEADER_CONTRIBUTION);let d=i.registerSkillReadiness(u,l,f.deferred);n.registerSkillsCommand(u,{extensionSources:()=>c.list(),inventory:i.skillInventory(l,d),repositoryRoot:async e=>(0,a.requireHarnessRoot)((0,o.requireDoomConfigContext)(e).harness),buildCatalog:async e=>(await f.catalog()).buildSkillCatalog(e),openOverlay:async(e,t)=>(await f.overlay()).openSkillsOverlay(e,t)})},t.LEADER_SOURCE)}catch(e){throw await m(),e}u.on(`session_shutdown`,m)}exports.default=u,exports.skillsExtension=u;
2
+ //# sourceMappingURL=extension.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.cjs","names":["Context","provideSkillSources","createActiveHelpSkillView","registerDoomLeaderContribution","SKILLS_LEADER_CONTRIBUTION","registerSkillReadiness","skillInventory","requireHarnessRoot","requireDoomConfigContext","LEADER_SOURCE"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { requireHarnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport { registerDoomLeaderContribution } from '@agimon-ai/doompi-ui/leader';\nimport { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { registerSkillsCommand } from '../../commands/skillsCommand.ts';\nimport { LEADER_SOURCE, SKILLS_LEADER_CONTRIBUTION } from '../../types/skills.ts';\nimport { createActiveHelpSkillView } from '../helpSkills.ts';\nimport { provideSkillSources } from '../skillSources.ts';\nimport { registerSkillReadiness, skillInventory } from './skillReadiness.ts';\n\nconst SESSION_SHUTDOWN_EVENT = 'session_shutdown';\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('../skillCatalog.ts')> | undefined;\n let deferred: Promise<typeof import('../deferredSkills.ts')> | undefined;\n let overlay: Promise<typeof import('../../tui/skillsOverlay.ts')> | undefined;\n return {\n catalog: () => (catalog ??= import('../skillCatalog.ts')),\n deferred: () => (deferred ??= import('../deferredSkills.ts')),\n overlay: () => (overlay ??= import('../../tui/skillsOverlay.ts')),\n };\n}\n\n/**\n * The package's single standard Pi factory, and the owner of the cordis Context.\n *\n * Pi can reload an extension in-process, so every registration is yielded to the\n * fiber and one dispose on session shutdown unwinds them in reverse.\n */\nexport async function skillsExtension(pi: ExtensionAPI): Promise<void> {\n const cordis = new Context();\n const load = lazyModules();\n let disposal: Promise<void> | undefined;\n const dispose = (): Promise<void> => {\n disposal ??= cordis.fiber.dispose();\n return disposal;\n };\n\n try {\n cordis.effect(function* () {\n const sources = provideSkillSources(pi);\n yield () => sources.dispose();\n\n const helpSkillView = createActiveHelpSkillView(pi);\n yield () => helpSkillView.dispose();\n\n yield registerDoomLeaderContribution(pi, SKILLS_LEADER_CONTRIBUTION);\n\n const readiness = registerSkillReadiness(pi, helpSkillView, load.deferred);\n registerSkillsCommand(pi, {\n extensionSources: () => sources.list(),\n inventory: skillInventory(helpSkillView, readiness),\n repositoryRoot: async (ctx: ExtensionContext) => requireHarnessRoot(requireDoomConfigContext(ctx).harness),\n buildCatalog: async (request) => (await load.catalog()).buildSkillCatalog(request),\n openOverlay: async (ctx, options) => (await load.overlay()).openSkillsOverlay(ctx, options),\n });\n }, LEADER_SOURCE);\n } catch (error) {\n await dispose();\n throw error;\n }\n\n pi.on(SESSION_SHUTDOWN_EVENT, dispose);\n}\n\nexport default skillsExtension;\n"],"mappings":"2WAmBA,SAAS,GAAc,CACrB,IAAI,EACA,EACA,EACJ,MAAO,CACL,YAAgB,IAAA,QAAA,QAAA,CAAA,CAAA,SAAA,QAAY,qBAAA,CAAA,EAC5B,aAAiB,IAAA,QAAA,QAAA,CAAA,CAAA,SAAA,QAAa,uBAAA,CAAA,EAC9B,YAAgB,IAAA,QAAA,QAAA,CAAA,CAAA,SAAA,QAAY,6BAAA,CAAA,CAC9B,CACF,CAQA,eAAsB,EAAgB,EAAiC,CACrE,IAAM,EAAS,IAAIA,EAAAA,QACb,EAAO,EAAY,EACrB,EACE,OACJ,IAAa,EAAO,MAAM,QAAQ,EAC3B,GAGT,GAAI,CACF,EAAO,OAAO,WAAa,CACzB,IAAM,EAAUC,EAAAA,oBAAoB,CAAE,EACtC,SAAY,EAAQ,QAAQ,EAE5B,IAAM,EAAgBC,EAAAA,0BAA0B,CAAE,EAClD,SAAY,EAAc,QAAQ,EAElC,MAAA,EAAMC,EAAAA,+BAAAA,CAA+B,EAAIC,EAAAA,0BAA0B,EAEnE,IAAM,EAAYC,EAAAA,uBAAuB,EAAI,EAAe,EAAK,QAAQ,EACzE,EAAA,sBAAsB,EAAI,CACxB,qBAAwB,EAAQ,KAAK,EACrC,UAAWC,EAAAA,eAAe,EAAe,CAAS,EAClD,eAAgB,KAAO,KAAA,EAA0BC,EAAAA,mBAAAA,EAAAA,EAAmBC,EAAAA,yBAAAA,CAAyB,CAAG,CAAC,CAAC,OAAO,EACzG,aAAc,KAAO,KAAa,MAAM,EAAK,QAAQ,EAAA,CAAG,kBAAkB,CAAO,EACjF,YAAa,MAAO,EAAK,KAAa,MAAM,EAAK,QAAQ,EAAA,CAAG,kBAAkB,EAAK,CAAO,CAC5F,CAAC,CACH,EAAGC,EAAAA,aAAa,CAClB,OAAS,EAAO,CAEd,MADA,MAAM,EAAQ,EACR,CACR,CAEA,EAAG,GAAG,mBAAwB,CAAO,CACvC"}
@@ -0,0 +1,12 @@
1
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ //#region src/adapters/pi/extension.d.ts
3
+ /**
4
+ * The package's single standard Pi factory, and the owner of the cordis Context.
5
+ *
6
+ * Pi can reload an extension in-process, so every registration is yielded to the
7
+ * fiber and one dispose on session shutdown unwinds them in reverse.
8
+ */
9
+ declare function skillsExtension(pi: ExtensionAPI): Promise<void>;
10
+ //#endregion
11
+ export { skillsExtension as default, skillsExtension };
12
+ //# sourceMappingURL=extension.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;;;;;iBAoCsB,gBAAgB,IAAI,eAAe"}
@@ -0,0 +1,12 @@
1
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ //#region src/adapters/pi/extension.d.ts
3
+ /**
4
+ * The package's single standard Pi factory, and the owner of the cordis Context.
5
+ *
6
+ * Pi can reload an extension in-process, so every registration is yielded to the
7
+ * fiber and one dispose on session shutdown unwinds them in reverse.
8
+ */
9
+ declare function skillsExtension(pi: ExtensionAPI): Promise<void>;
10
+ //#endregion
11
+ export { skillsExtension as default, skillsExtension };
12
+ //# sourceMappingURL=extension.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;;;;;iBAoCsB,gBAAgB,IAAI,eAAe"}
@@ -0,0 +1,2 @@
1
+ import{createActiveHelpSkillView as e}from"../helpSkills.mjs";import{LEADER_SOURCE as t,SKILLS_LEADER_CONTRIBUTION as n}from"../../types/skills.mjs";import{registerSkillsCommand as r}from"../../commands/skillsCommand.mjs";import{provideSkillSources as i}from"../skillSources.mjs";import{registerSkillReadiness as a,skillInventory as o}from"./skillReadiness.mjs";import{requireHarnessRoot as s}from"@agimon-ai/doompi-config/harnessStore";import{requireDoomConfigContext as c}from"@agimon-ai/doompi-config/piContext";import{registerDoomLeaderContribution as l}from"@agimon-ai/doompi-ui/leader";import{Context as u}from"@deepseek-ai/cordis";function d(){let e,t,n;return{catalog:()=>e??=import(`../skillCatalog.mjs`),deferred:()=>t??=import(`../deferredSkills.mjs`),overlay:()=>n??=import(`../../tui/skillsOverlay.mjs`)}}async function f(f){let p=new u,m=d(),h,g=()=>(h??=p.fiber.dispose(),h);try{p.effect(function*(){let t=i(f);yield()=>t.dispose();let u=e(f);yield()=>u.dispose(),yield l(f,n);let d=a(f,u,m.deferred);r(f,{extensionSources:()=>t.list(),inventory:o(u,d),repositoryRoot:async e=>s(c(e).harness),buildCatalog:async e=>(await m.catalog()).buildSkillCatalog(e),openOverlay:async(e,t)=>(await m.overlay()).openSkillsOverlay(e,t)})},t)}catch(e){throw await g(),e}f.on(`session_shutdown`,g)}export{f as default,f as skillsExtension};
2
+ //# sourceMappingURL=extension.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { requireHarnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport { registerDoomLeaderContribution } from '@agimon-ai/doompi-ui/leader';\nimport { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { registerSkillsCommand } from '../../commands/skillsCommand.ts';\nimport { LEADER_SOURCE, SKILLS_LEADER_CONTRIBUTION } from '../../types/skills.ts';\nimport { createActiveHelpSkillView } from '../helpSkills.ts';\nimport { provideSkillSources } from '../skillSources.ts';\nimport { registerSkillReadiness, skillInventory } from './skillReadiness.ts';\n\nconst SESSION_SHUTDOWN_EVENT = 'session_shutdown';\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('../skillCatalog.ts')> | undefined;\n let deferred: Promise<typeof import('../deferredSkills.ts')> | undefined;\n let overlay: Promise<typeof import('../../tui/skillsOverlay.ts')> | undefined;\n return {\n catalog: () => (catalog ??= import('../skillCatalog.ts')),\n deferred: () => (deferred ??= import('../deferredSkills.ts')),\n overlay: () => (overlay ??= import('../../tui/skillsOverlay.ts')),\n };\n}\n\n/**\n * The package's single standard Pi factory, and the owner of the cordis Context.\n *\n * Pi can reload an extension in-process, so every registration is yielded to the\n * fiber and one dispose on session shutdown unwinds them in reverse.\n */\nexport async function skillsExtension(pi: ExtensionAPI): Promise<void> {\n const cordis = new Context();\n const load = lazyModules();\n let disposal: Promise<void> | undefined;\n const dispose = (): Promise<void> => {\n disposal ??= cordis.fiber.dispose();\n return disposal;\n };\n\n try {\n cordis.effect(function* () {\n const sources = provideSkillSources(pi);\n yield () => sources.dispose();\n\n const helpSkillView = createActiveHelpSkillView(pi);\n yield () => helpSkillView.dispose();\n\n yield registerDoomLeaderContribution(pi, SKILLS_LEADER_CONTRIBUTION);\n\n const readiness = registerSkillReadiness(pi, helpSkillView, load.deferred);\n registerSkillsCommand(pi, {\n extensionSources: () => sources.list(),\n inventory: skillInventory(helpSkillView, readiness),\n repositoryRoot: async (ctx: ExtensionContext) => requireHarnessRoot(requireDoomConfigContext(ctx).harness),\n buildCatalog: async (request) => (await load.catalog()).buildSkillCatalog(request),\n openOverlay: async (ctx, options) => (await load.overlay()).openSkillsOverlay(ctx, options),\n });\n }, LEADER_SOURCE);\n } catch (error) {\n await dispose();\n throw error;\n }\n\n pi.on(SESSION_SHUTDOWN_EVENT, dispose);\n}\n\nexport default skillsExtension;\n"],"mappings":"8nBAmBA,SAAS,GAAc,CACrB,IAAI,EACA,EACA,EACJ,MAAO,CACL,YAAgB,IAAY,OAAO,uBACnC,aAAiB,IAAa,OAAO,yBACrC,YAAgB,IAAY,OAAO,8BACrC,CACF,CAQA,eAAsB,EAAgB,EAAiC,CACrE,IAAM,EAAS,IAAI,EACb,EAAO,EAAY,EACrB,EACE,OACJ,IAAa,EAAO,MAAM,QAAQ,EAC3B,GAGT,GAAI,CACF,EAAO,OAAO,WAAa,CACzB,IAAM,EAAU,EAAoB,CAAE,EACtC,SAAY,EAAQ,QAAQ,EAE5B,IAAM,EAAgB,EAA0B,CAAE,EAClD,SAAY,EAAc,QAAQ,EAElC,MAAM,EAA+B,EAAI,CAA0B,EAEnE,IAAM,EAAY,EAAuB,EAAI,EAAe,EAAK,QAAQ,EACzE,EAAsB,EAAI,CACxB,qBAAwB,EAAQ,KAAK,EACrC,UAAW,EAAe,EAAe,CAAS,EAClD,eAAgB,KAAO,IAA0B,EAAmB,EAAyB,CAAG,CAAC,CAAC,OAAO,EACzG,aAAc,KAAO,KAAa,MAAM,EAAK,QAAQ,EAAA,CAAG,kBAAkB,CAAO,EACjF,YAAa,MAAO,EAAK,KAAa,MAAM,EAAK,QAAQ,EAAA,CAAG,kBAAkB,EAAK,CAAO,CAC5F,CAAC,CACH,EAAG,CAAa,CAClB,OAAS,EAAO,CAEd,MADA,MAAM,EAAQ,EACR,CACR,CAEA,EAAG,GAAG,mBAAwB,CAAO,CACvC"}
@@ -0,0 +1,4 @@
1
+ const e=require("../../types/skills.cjs");let t=require("@agimon-ai/doompi-config/piContext");const n=`warning`;function r(t){return typeof t.getCommands==`function`?t.getCommands().filter(e=>e.source===`skill`&&e.name.startsWith(`skill:`)).map(t=>t.name.slice(e.SKILL_COMMAND_PREFIX.length)):[]}function i(e,t){return{skills:[],diagnostics:[`${e}: ${t instanceof Error?t.message:String(t)}`]}}function a(e,a,o){let s=0,c,l=``,u=async e=>{let t=await e.ready;return c===e?t:void 0};return e.on(`session_start`,(e,n)=>{let r=(0,t.requireDoomConfigContext)(n).harness;c={id:++s,ready:o().then(({DeferredSkillLoader:e})=>new e({cwd:n.cwd,skillPaths:r.skillDirectories}).start()).catch(e=>i(n.cwd,e)),diagnosticsShown:!1},l=``}),e.on(`input`,async t=>{if(!t.text.startsWith(`/skill:`))return{action:`continue`};let n=c;if(!n)return{action:`continue`};let i=await u(n);if(!i)return{action:`continue`};let s=a.merge({normalSkills:[],deferredSkills:i.skills,normalSkillNames:r(e)}),{expandDeferredSkillCommand:l}=await o(),d=l(t.text,s.additionalSkills);return d===t.text?{action:`continue`}:{action:`transform`,text:d,images:t.images}}),e.on(`before_agent_start`,async(e,t)=>{let r=c;if(!r)return;let i=await u(r);if(!i)return;!r.diagnosticsShown&&i.diagnostics.length>0&&(r.diagnosticsShown=!0,t.ui.notify(`Skill discovery:\n${i.diagnostics.join(`
2
+ `)}`,n));let s=a.merge({normalSkills:e.systemPromptOptions.skills??[],deferredSkills:i.skills});if(s.diagnosticKey!==l&&(l=s.diagnosticKey,s.diagnostics.length>0&&t.ui.notify(`Package Help:\n${s.diagnostics.join(`
3
+ `)}`,n)),s.additionalSkills.length===0)return;let{buildPromptWithDeferredSkills:d}=await o();return{systemPrompt:d(e.systemPrompt,e.systemPromptOptions,[...s.additionalSkills])}}),{current:async()=>c?u(c):void 0}}function o(e,t){return async n=>{let r=await t.current(),i=e.merge({normalSkills:n.getSystemPromptOptions().skills??[],deferredSkills:r?.skills??[]});return{helpSkills:i.helpSkills,diagnostics:i.diagnostics}}}exports.registerSkillReadiness=a,exports.skillInventory=o;
4
+ //# sourceMappingURL=skillReadiness.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillReadiness.cjs","names":["SKILL_COMMAND_PREFIX","requireDoomConfigContext"],"sources":["../../../src/adapters/pi/skillReadiness.ts"],"sourcesContent":["import { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { ExtensionAPI, ExtensionContext, Skill } from '@earendil-works/pi-coding-agent';\nimport { SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX } from '../../types/skills.ts';\nimport type { DeferredSkillSnapshot } from '../deferredSkills.ts';\nimport type { createActiveHelpSkillView } from '../helpSkills.ts';\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 /** 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 registerSkillReadiness(\n pi: ExtensionAPI,\n helpSkillView: HelpSkillView,\n loadDeferredSkills: () => Promise<typeof import('../deferredSkills.ts')>,\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 pi.on('session_start', (_event, ctx) => {\n const state = requireDoomConfigContext(ctx).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\n pi.on('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\n pi.on('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 return {\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"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import{SKILL_COMMAND_PREFIX as e}from"../../types/skills.mjs";import{requireDoomConfigContext as t}from"@agimon-ai/doompi-config/piContext";const n=`warning`;function r(t){return typeof t.getCommands==`function`?t.getCommands().filter(e=>e.source===`skill`&&e.name.startsWith(`skill:`)).map(t=>t.name.slice(e.length)):[]}function i(e,t){return{skills:[],diagnostics:[`${e}: ${t instanceof Error?t.message:String(t)}`]}}function a(e,a,o){let s=0,c,l=``,u=async e=>{let t=await e.ready;return c===e?t:void 0};return e.on(`session_start`,(e,n)=>{let r=t(n).harness;c={id:++s,ready:o().then(({DeferredSkillLoader:e})=>new e({cwd:n.cwd,skillPaths:r.skillDirectories}).start()).catch(e=>i(n.cwd,e)),diagnosticsShown:!1},l=``}),e.on(`input`,async t=>{if(!t.text.startsWith(`/skill:`))return{action:`continue`};let n=c;if(!n)return{action:`continue`};let i=await u(n);if(!i)return{action:`continue`};let s=a.merge({normalSkills:[],deferredSkills:i.skills,normalSkillNames:r(e)}),{expandDeferredSkillCommand:l}=await o(),d=l(t.text,s.additionalSkills);return d===t.text?{action:`continue`}:{action:`transform`,text:d,images:t.images}}),e.on(`before_agent_start`,async(e,t)=>{let r=c;if(!r)return;let i=await u(r);if(!i)return;!r.diagnosticsShown&&i.diagnostics.length>0&&(r.diagnosticsShown=!0,t.ui.notify(`Skill discovery:\n${i.diagnostics.join(`
2
+ `)}`,n));let s=a.merge({normalSkills:e.systemPromptOptions.skills??[],deferredSkills:i.skills});if(s.diagnosticKey!==l&&(l=s.diagnosticKey,s.diagnostics.length>0&&t.ui.notify(`Package Help:\n${s.diagnostics.join(`
3
+ `)}`,n)),s.additionalSkills.length===0)return;let{buildPromptWithDeferredSkills:d}=await o();return{systemPrompt:d(e.systemPrompt,e.systemPromptOptions,[...s.additionalSkills])}}),{current:async()=>c?u(c):void 0}}function o(e,t){return async n=>{let r=await t.current(),i=e.merge({normalSkills:n.getSystemPromptOptions().skills??[],deferredSkills:r?.skills??[]});return{helpSkills:i.helpSkills,diagnostics:i.diagnostics}}}export{a as registerSkillReadiness,o as skillInventory};
4
+ //# sourceMappingURL=skillReadiness.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillReadiness.mjs","names":[],"sources":["../../../src/adapters/pi/skillReadiness.ts"],"sourcesContent":["import { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { ExtensionAPI, ExtensionContext, Skill } from '@earendil-works/pi-coding-agent';\nimport { SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX } from '../../types/skills.ts';\nimport type { DeferredSkillSnapshot } from '../deferredSkills.ts';\nimport type { createActiveHelpSkillView } from '../helpSkills.ts';\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 /** 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 registerSkillReadiness(\n pi: ExtensionAPI,\n helpSkillView: HelpSkillView,\n loadDeferredSkills: () => Promise<typeof import('../deferredSkills.ts')>,\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 pi.on('session_start', (_event, ctx) => {\n const state = requireDoomConfigContext(ctx).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\n pi.on('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\n pi.on('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 return {\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"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:path");n=e.__toESM(n,1);let r=require("@agimon-ai/doompi-config/domains"),i=require("@earendil-works/pi-coding-agent"),a=require("@agimon-ai/doompi-domain/plugins");const o=`skills`,s=`.claude/skills`;let c;async function l(){return c??=import(`gpt-tokenizer`),(await c).countTokens}function u(e){let t=e.map(e=>n.default.resolve(e));return e=>{let r=n.default.resolve(e);return t.some(e=>e===r||e.startsWith(`${r}${n.default.sep}`)||r.startsWith(`${e}${n.default.sep}`))}}function d(e,t,r,a,o,s,c=`recursive`){let l=(0,i.loadSkillsFromDir)({dir:e,source:r});for(let t of l.diagnostics)s.push(`${t.path??e}: ${t.message}`);let u=n.default.resolve(e),d=l.skills.filter(e=>{let t=n.default.dirname(e.filePath);return c===`direct-children`&&n.default.dirname(t)!==u?!1:a(t)});return o.push(...d),d.map(e=>({name:e.name,description:e.description,filePath:e.filePath,baseDir:e.baseDir,group:t,owner:r,modelInvocable:!e.disableModelInvocation})).sort((e,t)=>e.name.localeCompare(t.name))}function f(e){return e.sort((e,t)=>e.owner.localeCompare(t.owner))}function p(e,t,n){let r=new Set(t.map(e=>e.name)),i=new Map,a=[...e].sort((e,t)=>e.sourceInfo.source.localeCompare(t.sourceInfo.source)||e.name.localeCompare(t.name)||e.filePath.localeCompare(t.filePath));for(let e of a){if(r.has(e.name)){n.push(`Help ${e.sourceInfo.source} [HELP_SKILL_COLLISION]: '${e.name}' was ignored because a normal skill wins.`);continue}r.add(e.name),t.push(e);let a=e.sourceInfo.source,o=i.get(a)??[];o.push({name:e.name,description:e.description,filePath:e.filePath,baseDir:e.baseDir,group:`help`,owner:a,modelInvocable:!e.disableModelInvocation}),i.set(a,o)}return f([...i.entries()].map(([e,t])=>({owner:e,skills:t.sort((e,t)=>e.name.localeCompare(t.name))})))}async function m(e){let{repoRoot:c}=e,m=u(e.activeSkillDirectories),h=[...e.helpDiagnostics??[]],g=[],_=[];for(let t of e.extensionSources){let e=t.directories.flatMap(e=>d(e,`extensions`,t.source,()=>!0,g,h));e.length>0&&_.push({owner:t.source,skills:e})}let v=[],y=await(0,a.materializePluginEntries)((0,r.resolvePluginEntries)(c,(0,r.listDomainNames)(c),[]));for(let e of y){let r=n.default.join(e.directory,o);if(!t.default.existsSync(r))continue;let i=n.default.basename(e.directory),a=d(r,`plugins`,i,m,g,h,e.skillDiscovery);a.length>0&&v.push({owner:i,skills:a})}let b=d(n.default.join(c,`.claude`,o),`default`,s,m,g,h),x=b.length>0?[{owner:s,skills:b}]:[],S=p(e.helpSkills??[],g,h),C=[{key:`extensions`,label:`extensions`,owners:f(_)},{key:`help`,label:`Help`,owners:S},{key:`plugins`,label:`plugins`,owners:f(v)},{key:`default`,label:`default`,owners:x}],w=await l(),T=(0,i.formatSkillsForPrompt)(g),E=0;for(let e of g)try{E+=w(t.default.readFileSync(e.filePath,`utf8`))}catch(t){h.push(`${e.filePath}: ${t instanceof Error?t.message:String(t)}`)}return{groups:C,skillCount:g.length,promptTokens:T?w(T):0,bodyTokens:E,diagnostics:h}}function h(e,t){for(let n of e.groups)for(let e of n.owners){let n=e.skills.find(e=>e.name===t);if(n)return n}}exports.buildSkillCatalog=m,exports.counter=l,exports.findSkill=h;
2
+ //# sourceMappingURL=skillCatalog.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillCatalog.cjs","names":["path","loadSkillsFromDir","materializePluginEntries","resolvePluginEntries","listDomainNames","fs","formatSkillsForPrompt"],"sources":["../../src/adapters/skillCatalog.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { listDomainNames, type PluginSkillDiscovery, resolvePluginEntries } from '@agimon-ai/doompi-config/domains';\nimport { formatSkillsForPrompt, loadSkillsFromDir, type Skill } from '@earendil-works/pi-coding-agent';\nimport { materializePluginEntries } from '@agimon-ai/doompi-domain/plugins';\nimport type { SkillSourceEntry } from './skillSources.ts';\n\nconst SKILLS_DIRECTORY = 'skills';\nconst SHARED_SKILLS_OWNER = '.claude/skills';\n\nexport type SkillGroupKey = 'extensions' | 'help' | 'plugins' | 'default';\n\nexport interface SkillEntry {\n name: string;\n description: string;\n filePath: string;\n baseDir: string;\n group: SkillGroupKey;\n /** Package name for an extension, plugin directory name, or `.claude/skills`. */\n owner: string;\n modelInvocable: boolean;\n}\n\nexport interface SkillOwnerNode {\n owner: string;\n skills: SkillEntry[];\n}\n\nexport interface SkillGroup {\n key: SkillGroupKey;\n label: string;\n owners: SkillOwnerNode[];\n}\n\nexport interface SkillCatalog {\n groups: SkillGroup[];\n skillCount: number;\n /** Tokens the `<available_skills>` prompt block costs on every request. */\n promptTokens: number;\n /** Tokens every loaded SKILL.md costs in full, if all of them were read. */\n bodyTokens: number;\n /** Frontmatter and read failures, surfaced instead of thrown. */\n diagnostics: string[];\n}\n\nexport interface SkillCatalogOptions {\n repoRoot: string;\n /** Exact files the current selection resolved to, from `HarnessState.skillDirectories`. */\n activeSkillDirectories: readonly string[];\n extensionSources: readonly SkillSourceEntry[];\n /** Activation-gated wrappers already accepted by the merged active-skill view. */\n helpSkills?: readonly Skill[];\n /** Bounded Help activation and collision diagnostics. */\n helpDiagnostics?: readonly string[];\n}\n\n/**\n * The tokenizer, loaded on first use rather than at import.\n *\n * `gpt-tokenizer` carries its whole vocabulary and costs about a tenth of a\n * second to load. This module is reachable from the skills extension, which Pi\n * loads at session start, so a top-level import would charge every session for\n * a surface most of them never open.\n */\nlet tokenizer: Promise<typeof import('gpt-tokenizer')> | undefined;\n\nexport async function counter(): Promise<(text: string) => number> {\n tokenizer ??= import('gpt-tokenizer');\n return (await tokenizer).countTokens;\n}\n\n/**\n * Matches a directory against the active set.\n *\n * A domain that lists a plugin subset puts individual skill directories in the\n * set rather than the plugin's `skills` root, so containment has to be checked\n * both ways: the set may hold an ancestor of the directory, or a descendant of\n * it.\n */\nfunction createActiveMatcher(directories: readonly string[]): (directory: string) => boolean {\n const active = directories.map((directory) => path.resolve(directory));\n return (directory) => {\n const resolved = path.resolve(directory);\n return active.some(\n (entry) =>\n entry === resolved || entry.startsWith(`${resolved}${path.sep}`) || resolved.startsWith(`${entry}${path.sep}`),\n );\n };\n}\n\nfunction collect(\n directory: string,\n group: SkillGroupKey,\n owner: string,\n isActive: (directory: string) => boolean,\n loaded: Skill[],\n diagnostics: string[],\n discovery: PluginSkillDiscovery = 'recursive',\n): SkillEntry[] {\n const result = loadSkillsFromDir({ dir: directory, source: owner });\n for (const diagnostic of result.diagnostics) {\n diagnostics.push(`${diagnostic.path ?? directory}: ${diagnostic.message}`);\n }\n // Checked per skill, not per root: a subset domain selects some of a plugin's\n // skills and leaves the rest on disk, unloaded and therefore not listed.\n const root = path.resolve(directory);\n const kept = result.skills.filter((skill) => {\n const skillDirectory = path.dirname(skill.filePath);\n if (discovery === 'direct-children' && path.dirname(skillDirectory) !== root) return false;\n return isActive(skillDirectory);\n });\n // Kept in their loaded form as well, because the prompt-block cost is measured\n // by handing them back to Pi's own formatter.\n loaded.push(...kept);\n return kept\n .map((skill) => ({\n name: skill.name,\n description: skill.description,\n filePath: skill.filePath,\n baseDir: skill.baseDir,\n group,\n owner,\n modelInvocable: !skill.disableModelInvocation,\n }))\n .sort((left, right) => left.name.localeCompare(right.name));\n}\n\nfunction sortOwners(owners: SkillOwnerNode[]): SkillOwnerNode[] {\n return owners.sort((left, right) => left.owner.localeCompare(right.owner));\n}\n\nfunction collectHelpSkills(skills: readonly Skill[], loaded: Skill[], diagnostics: string[]): SkillOwnerNode[] {\n const occupiedNames = new Set(loaded.map((skill) => skill.name));\n const owners = new Map<string, SkillEntry[]>();\n const candidates = [...skills].sort(\n (left, right) =>\n left.sourceInfo.source.localeCompare(right.sourceInfo.source) ||\n left.name.localeCompare(right.name) ||\n left.filePath.localeCompare(right.filePath),\n );\n for (const skill of candidates) {\n if (occupiedNames.has(skill.name)) {\n diagnostics.push(\n `Help ${skill.sourceInfo.source} [HELP_SKILL_COLLISION]: '${skill.name}' was ignored because a normal skill wins.`,\n );\n continue;\n }\n occupiedNames.add(skill.name);\n loaded.push(skill);\n const owner = skill.sourceInfo.source;\n const entries = owners.get(owner) ?? [];\n entries.push({\n name: skill.name,\n description: skill.description,\n filePath: skill.filePath,\n baseDir: skill.baseDir,\n group: 'help',\n owner,\n modelInvocable: !skill.disableModelInvocation,\n });\n owners.set(owner, entries);\n }\n return sortOwners(\n [...owners.entries()].map(([owner, entries]) => ({\n owner,\n skills: entries.sort((left, right) => left.name.localeCompare(right.name)),\n })),\n );\n}\n\n/**\n * Every skill this session actually loaded, grouped by where it comes from,\n * priced in tokens.\n *\n * Discovery runs over `loadSkillsFromDir` from Pi rather than the harness's own\n * walker so the listing matches what Pi would load, including its ignore-file\n * handling; the same skills are then handed to Pi's `formatSkillsForPrompt` so\n * the always-on figure is the real prompt block rather than a reconstruction of\n * it.\n */\nexport async function buildSkillCatalog(options: SkillCatalogOptions): Promise<SkillCatalog> {\n const { repoRoot } = options;\n const isActive = createActiveMatcher(options.activeSkillDirectories);\n const diagnostics: string[] = [...(options.helpDiagnostics ?? [])];\n const loaded: Skill[] = [];\n\n const extensionOwners: SkillOwnerNode[] = [];\n for (const entry of options.extensionSources) {\n const skills = entry.directories.flatMap((directory) =>\n collect(directory, 'extensions', entry.source, () => true, loaded, diagnostics),\n );\n if (skills.length > 0) extensionOwners.push({ owner: entry.source, skills });\n }\n\n const pluginOwners: SkillOwnerNode[] = [];\n const domainPlugins = await materializePluginEntries(resolvePluginEntries(repoRoot, listDomainNames(repoRoot), []));\n for (const entry of domainPlugins) {\n const directory = path.join(entry.directory, SKILLS_DIRECTORY);\n if (!fs.existsSync(directory)) continue;\n const owner = path.basename(entry.directory);\n const skills = collect(directory, 'plugins', owner, isActive, loaded, diagnostics, entry.skillDiscovery);\n if (skills.length > 0) pluginOwners.push({ owner, skills });\n }\n\n const sharedDirectory = path.join(repoRoot, '.claude', SKILLS_DIRECTORY);\n const sharedSkills = collect(sharedDirectory, 'default', SHARED_SKILLS_OWNER, isActive, loaded, diagnostics);\n const defaultOwners = sharedSkills.length > 0 ? [{ owner: SHARED_SKILLS_OWNER, skills: sharedSkills }] : [];\n const helpOwners = collectHelpSkills(options.helpSkills ?? [], loaded, diagnostics);\n\n const groups: SkillGroup[] = [\n { key: 'extensions', label: 'extensions', owners: sortOwners(extensionOwners) },\n { key: 'help', label: 'Help', owners: helpOwners },\n { key: 'plugins', label: 'plugins', owners: sortOwners(pluginOwners) },\n { key: 'default', label: 'default', owners: defaultOwners },\n ];\n\n const countTokens = await counter();\n const prompt = formatSkillsForPrompt(loaded);\n let bodyTokens = 0;\n for (const skill of loaded) {\n try {\n bodyTokens += countTokens(fs.readFileSync(skill.filePath, 'utf8'));\n } catch (error) {\n diagnostics.push(`${skill.filePath}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n return {\n groups,\n skillCount: loaded.length,\n promptTokens: prompt ? countTokens(prompt) : 0,\n bodyTokens,\n diagnostics,\n };\n}\n\n/** The skills a name resolves to, used to decide what `enter` can invoke. */\nexport function findSkill(catalog: SkillCatalog, name: string): SkillEntry | undefined {\n for (const group of catalog.groups) {\n for (const owner of group.owners) {\n const match = owner.skills.find((skill) => skill.name === name);\n if (match) return match;\n }\n }\n return undefined;\n}\n"],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import { SkillSourceEntry } from "./skillSources.cjs";
2
+ import { Skill } from "@earendil-works/pi-coding-agent";
3
+ //#region src/adapters/skillCatalog.d.ts
4
+ type SkillGroupKey = 'extensions' | 'help' | 'plugins' | 'default';
5
+ interface SkillEntry {
6
+ name: string;
7
+ description: string;
8
+ filePath: string;
9
+ baseDir: string;
10
+ group: SkillGroupKey;
11
+ /** Package name for an extension, plugin directory name, or `.claude/skills`. */
12
+ owner: string;
13
+ modelInvocable: boolean;
14
+ }
15
+ interface SkillOwnerNode {
16
+ owner: string;
17
+ skills: SkillEntry[];
18
+ }
19
+ interface SkillGroup {
20
+ key: SkillGroupKey;
21
+ label: string;
22
+ owners: SkillOwnerNode[];
23
+ }
24
+ interface SkillCatalog {
25
+ groups: SkillGroup[];
26
+ skillCount: number;
27
+ /** Tokens the `<available_skills>` prompt block costs on every request. */
28
+ promptTokens: number;
29
+ /** Tokens every loaded SKILL.md costs in full, if all of them were read. */
30
+ bodyTokens: number;
31
+ /** Frontmatter and read failures, surfaced instead of thrown. */
32
+ diagnostics: string[];
33
+ }
34
+ interface SkillCatalogOptions {
35
+ repoRoot: string;
36
+ /** Exact files the current selection resolved to, from `HarnessState.skillDirectories`. */
37
+ activeSkillDirectories: readonly string[];
38
+ extensionSources: readonly SkillSourceEntry[];
39
+ /** Activation-gated wrappers already accepted by the merged active-skill view. */
40
+ helpSkills?: readonly Skill[];
41
+ /** Bounded Help activation and collision diagnostics. */
42
+ helpDiagnostics?: readonly string[];
43
+ }
44
+ declare function counter(): Promise<(text: string) => number>;
45
+ /**
46
+ * Every skill this session actually loaded, grouped by where it comes from,
47
+ * priced in tokens.
48
+ *
49
+ * Discovery runs over `loadSkillsFromDir` from Pi rather than the harness's own
50
+ * walker so the listing matches what Pi would load, including its ignore-file
51
+ * handling; the same skills are then handed to Pi's `formatSkillsForPrompt` so
52
+ * the always-on figure is the real prompt block rather than a reconstruction of
53
+ * it.
54
+ */
55
+ declare function buildSkillCatalog(options: SkillCatalogOptions): Promise<SkillCatalog>;
56
+ //#endregion
57
+ export { SkillCatalog, SkillCatalogOptions, SkillEntry, SkillGroup, SkillGroupKey, SkillOwnerNode, buildSkillCatalog, counter };
58
+ //# sourceMappingURL=skillCatalog.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillCatalog.d.cts","names":[],"sources":["../../src/adapters/skillCatalog.ts"],"mappings":";;;KAUY;UAEK;EACf;EACA;EACA;EACA;EACA,OAAO;;EAEP;EACA;;UAGe;EACf;EACA,QAAQ;;UAGO;EACf,KAAK;EACL;EACA,QAAQ;;UAGO;EACf,QAAQ;EACR;;EAEA;;EAEA;;EAEA;;UAGe;EACf;;EAEA;EACA,2BAA2B;;EAE3B,sBAAsB;;EAEtB;;iBAaoB,WAAW,SAAS;;;;;;;;;;;iBAkHpB,kBAAkB,SAAS,sBAAsB,QAAQ"}
@@ -0,0 +1,58 @@
1
+ import { SkillSourceEntry } from "./skillSources.mjs";
2
+ import { Skill } from "@earendil-works/pi-coding-agent";
3
+ //#region src/adapters/skillCatalog.d.ts
4
+ type SkillGroupKey = 'extensions' | 'help' | 'plugins' | 'default';
5
+ interface SkillEntry {
6
+ name: string;
7
+ description: string;
8
+ filePath: string;
9
+ baseDir: string;
10
+ group: SkillGroupKey;
11
+ /** Package name for an extension, plugin directory name, or `.claude/skills`. */
12
+ owner: string;
13
+ modelInvocable: boolean;
14
+ }
15
+ interface SkillOwnerNode {
16
+ owner: string;
17
+ skills: SkillEntry[];
18
+ }
19
+ interface SkillGroup {
20
+ key: SkillGroupKey;
21
+ label: string;
22
+ owners: SkillOwnerNode[];
23
+ }
24
+ interface SkillCatalog {
25
+ groups: SkillGroup[];
26
+ skillCount: number;
27
+ /** Tokens the `<available_skills>` prompt block costs on every request. */
28
+ promptTokens: number;
29
+ /** Tokens every loaded SKILL.md costs in full, if all of them were read. */
30
+ bodyTokens: number;
31
+ /** Frontmatter and read failures, surfaced instead of thrown. */
32
+ diagnostics: string[];
33
+ }
34
+ interface SkillCatalogOptions {
35
+ repoRoot: string;
36
+ /** Exact files the current selection resolved to, from `HarnessState.skillDirectories`. */
37
+ activeSkillDirectories: readonly string[];
38
+ extensionSources: readonly SkillSourceEntry[];
39
+ /** Activation-gated wrappers already accepted by the merged active-skill view. */
40
+ helpSkills?: readonly Skill[];
41
+ /** Bounded Help activation and collision diagnostics. */
42
+ helpDiagnostics?: readonly string[];
43
+ }
44
+ declare function counter(): Promise<(text: string) => number>;
45
+ /**
46
+ * Every skill this session actually loaded, grouped by where it comes from,
47
+ * priced in tokens.
48
+ *
49
+ * Discovery runs over `loadSkillsFromDir` from Pi rather than the harness's own
50
+ * walker so the listing matches what Pi would load, including its ignore-file
51
+ * handling; the same skills are then handed to Pi's `formatSkillsForPrompt` so
52
+ * the always-on figure is the real prompt block rather than a reconstruction of
53
+ * it.
54
+ */
55
+ declare function buildSkillCatalog(options: SkillCatalogOptions): Promise<SkillCatalog>;
56
+ //#endregion
57
+ export { SkillCatalog, SkillCatalogOptions, SkillEntry, SkillGroup, SkillGroupKey, SkillOwnerNode, buildSkillCatalog, counter };
58
+ //# sourceMappingURL=skillCatalog.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillCatalog.d.mts","names":[],"sources":["../../src/adapters/skillCatalog.ts"],"mappings":";;;KAUY;UAEK;EACf;EACA;EACA;EACA;EACA,OAAO;;EAEP;EACA;;UAGe;EACf;EACA,QAAQ;;UAGO;EACf,KAAK;EACL;EACA,QAAQ;;UAGO;EACf,QAAQ;EACR;;EAEA;;EAEA;;EAEA;;UAGe;EACf;;EAEA;EACA,2BAA2B;;EAE3B,sBAAsB;;EAEtB;;iBAaoB,WAAW,SAAS;;;;;;;;;;;iBAkHpB,kBAAkB,SAAS,sBAAsB,QAAQ"}
@@ -0,0 +1,2 @@
1
+ import e from"node:fs";import t from"node:path";import{listDomainNames as n,resolvePluginEntries as r}from"@agimon-ai/doompi-config/domains";import{formatSkillsForPrompt as i,loadSkillsFromDir as a}from"@earendil-works/pi-coding-agent";import{materializePluginEntries as o}from"@agimon-ai/doompi-domain/plugins";const s=`skills`,c=`.claude/skills`;let l;async function u(){return l??=import(`gpt-tokenizer`),(await l).countTokens}function d(e){let n=e.map(e=>t.resolve(e));return e=>{let r=t.resolve(e);return n.some(e=>e===r||e.startsWith(`${r}${t.sep}`)||r.startsWith(`${e}${t.sep}`))}}function f(e,n,r,i,o,s,c=`recursive`){let l=a({dir:e,source:r});for(let t of l.diagnostics)s.push(`${t.path??e}: ${t.message}`);let u=t.resolve(e),d=l.skills.filter(e=>{let n=t.dirname(e.filePath);return c===`direct-children`&&t.dirname(n)!==u?!1:i(n)});return o.push(...d),d.map(e=>({name:e.name,description:e.description,filePath:e.filePath,baseDir:e.baseDir,group:n,owner:r,modelInvocable:!e.disableModelInvocation})).sort((e,t)=>e.name.localeCompare(t.name))}function p(e){return e.sort((e,t)=>e.owner.localeCompare(t.owner))}function m(e,t,n){let r=new Set(t.map(e=>e.name)),i=new Map,a=[...e].sort((e,t)=>e.sourceInfo.source.localeCompare(t.sourceInfo.source)||e.name.localeCompare(t.name)||e.filePath.localeCompare(t.filePath));for(let e of a){if(r.has(e.name)){n.push(`Help ${e.sourceInfo.source} [HELP_SKILL_COLLISION]: '${e.name}' was ignored because a normal skill wins.`);continue}r.add(e.name),t.push(e);let a=e.sourceInfo.source,o=i.get(a)??[];o.push({name:e.name,description:e.description,filePath:e.filePath,baseDir:e.baseDir,group:`help`,owner:a,modelInvocable:!e.disableModelInvocation}),i.set(a,o)}return p([...i.entries()].map(([e,t])=>({owner:e,skills:t.sort((e,t)=>e.name.localeCompare(t.name))})))}async function h(a){let{repoRoot:l}=a,h=d(a.activeSkillDirectories),g=[...a.helpDiagnostics??[]],_=[],v=[];for(let e of a.extensionSources){let t=e.directories.flatMap(t=>f(t,`extensions`,e.source,()=>!0,_,g));t.length>0&&v.push({owner:e.source,skills:t})}let y=[],b=await o(r(l,n(l),[]));for(let n of b){let r=t.join(n.directory,s);if(!e.existsSync(r))continue;let i=t.basename(n.directory),a=f(r,`plugins`,i,h,_,g,n.skillDiscovery);a.length>0&&y.push({owner:i,skills:a})}let x=f(t.join(l,`.claude`,s),`default`,c,h,_,g),S=x.length>0?[{owner:c,skills:x}]:[],C=m(a.helpSkills??[],_,g),w=[{key:`extensions`,label:`extensions`,owners:p(v)},{key:`help`,label:`Help`,owners:C},{key:`plugins`,label:`plugins`,owners:p(y)},{key:`default`,label:`default`,owners:S}],T=await u(),E=i(_),D=0;for(let t of _)try{D+=T(e.readFileSync(t.filePath,`utf8`))}catch(e){g.push(`${t.filePath}: ${e instanceof Error?e.message:String(e)}`)}return{groups:w,skillCount:_.length,promptTokens:E?T(E):0,bodyTokens:D,diagnostics:g}}function g(e,t){for(let n of e.groups)for(let e of n.owners){let n=e.skills.find(e=>e.name===t);if(n)return n}}export{h as buildSkillCatalog,u as counter,g as findSkill};
2
+ //# sourceMappingURL=skillCatalog.mjs.map