@agimon-ai/doompi-skill 0.0.1-alpha.3 → 0.0.1-alpha.5

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 (70) hide show
  1. package/package.json +8 -8
  2. package/dist/_virtual/_rolldown/runtime.cjs +0 -1
  3. package/dist/adapters/deferredSkills.cjs +0 -12
  4. package/dist/adapters/deferredSkills.cjs.map +0 -1
  5. package/dist/adapters/deferredSkills.mjs +0 -12
  6. package/dist/adapters/deferredSkills.mjs.map +0 -1
  7. package/dist/adapters/helpSkills.cjs +0 -2
  8. package/dist/adapters/helpSkills.cjs.map +0 -1
  9. package/dist/adapters/helpSkills.mjs +0 -2
  10. package/dist/adapters/helpSkills.mjs.map +0 -1
  11. package/dist/adapters/pi/extension.cjs +0 -2
  12. package/dist/adapters/pi/extension.cjs.map +0 -1
  13. package/dist/adapters/pi/extension.d.cts +0 -7
  14. package/dist/adapters/pi/extension.d.cts.map +0 -1
  15. package/dist/adapters/pi/extension.d.mts +0 -7
  16. package/dist/adapters/pi/extension.d.mts.map +0 -1
  17. package/dist/adapters/pi/extension.mjs +0 -2
  18. package/dist/adapters/pi/extension.mjs.map +0 -1
  19. package/dist/adapters/pi/skillReadiness.cjs +0 -4
  20. package/dist/adapters/pi/skillReadiness.cjs.map +0 -1
  21. package/dist/adapters/pi/skillReadiness.mjs +0 -4
  22. package/dist/adapters/pi/skillReadiness.mjs.map +0 -1
  23. package/dist/adapters/skillCatalog.cjs +0 -2
  24. package/dist/adapters/skillCatalog.cjs.map +0 -1
  25. package/dist/adapters/skillCatalog.d.cts +0 -58
  26. package/dist/adapters/skillCatalog.d.cts.map +0 -1
  27. package/dist/adapters/skillCatalog.d.mts +0 -58
  28. package/dist/adapters/skillCatalog.d.mts.map +0 -1
  29. package/dist/adapters/skillCatalog.mjs +0 -2
  30. package/dist/adapters/skillCatalog.mjs.map +0 -1
  31. package/dist/catalog.cjs +0 -1
  32. package/dist/catalog.d.cts +0 -2
  33. package/dist/catalog.d.mts +0 -2
  34. package/dist/catalog.mjs +0 -1
  35. package/dist/commands/skillsCommand.cjs +0 -2
  36. package/dist/commands/skillsCommand.cjs.map +0 -1
  37. package/dist/commands/skillsCommand.mjs +0 -2
  38. package/dist/commands/skillsCommand.mjs.map +0 -1
  39. package/dist/extensions/pi.cjs +0 -1
  40. package/dist/extensions/pi.d.cts +0 -2
  41. package/dist/extensions/pi.d.mts +0 -2
  42. package/dist/extensions/pi.mjs +0 -1
  43. package/dist/index.cjs +0 -1
  44. package/dist/index.d.cts +0 -5
  45. package/dist/index.d.mts +0 -5
  46. package/dist/index.mjs +0 -1
  47. package/dist/services/skillText.cjs +0 -2
  48. package/dist/services/skillText.cjs.map +0 -1
  49. package/dist/services/skillText.d.cts +0 -13
  50. package/dist/services/skillText.d.cts.map +0 -1
  51. package/dist/services/skillText.d.mts +0 -13
  52. package/dist/services/skillText.d.mts.map +0 -1
  53. package/dist/services/skillText.mjs +0 -2
  54. package/dist/services/skillText.mjs.map +0 -1
  55. package/dist/tui/skillsOverlay.cjs +0 -4
  56. package/dist/tui/skillsOverlay.cjs.map +0 -1
  57. package/dist/tui/skillsOverlay.d.cts +0 -18
  58. package/dist/tui/skillsOverlay.d.cts.map +0 -1
  59. package/dist/tui/skillsOverlay.d.mts +0 -18
  60. package/dist/tui/skillsOverlay.d.mts.map +0 -1
  61. package/dist/tui/skillsOverlay.mjs +0 -4
  62. package/dist/tui/skillsOverlay.mjs.map +0 -1
  63. package/dist/types/skills.cjs +0 -2
  64. package/dist/types/skills.cjs.map +0 -1
  65. package/dist/types/skills.d.cts +0 -32
  66. package/dist/types/skills.d.cts.map +0 -1
  67. package/dist/types/skills.d.mts +0 -32
  68. package/dist/types/skills.d.mts.map +0 -1
  69. package/dist/types/skills.mjs +0 -2
  70. package/dist/types/skills.mjs.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-skill",
3
- "version": "0.0.1-alpha.3",
3
+ "version": "0.0.1-alpha.5",
4
4
  "description": "Session skill catalogue, deferred skill discovery, and the skill browser for DoomPi.",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -52,13 +52,14 @@
52
52
  "dependencies": {
53
53
  "@deepseek-ai/cordis": "4.0.1",
54
54
  "gpt-tokenizer": "3.4.0",
55
- "@agimon-ai/doompi-domain": "0.0.1-alpha.3",
56
- "@agimon-ai/doompi-config": "0.0.1-alpha.25",
57
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.25",
58
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.25",
59
- "@agimon-ai/doompi-ui": "0.0.1-alpha.25"
55
+ "@agimon-ai/doompi-config": "0.0.1-alpha.27",
56
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.27",
57
+ "@agimon-ai/doompi-domain": "0.0.1-alpha.5",
58
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.27",
59
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.27"
60
60
  },
61
61
  "devDependencies": {
62
+ "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.26",
62
63
  "@deepseek-ai/cordis": "4.0.1",
63
64
  "@earendil-works/pi-coding-agent": "0.84.2",
64
65
  "@earendil-works/pi-tui": "0.84.2",
@@ -66,8 +67,7 @@
66
67
  "@vitest/coverage-v8": "4.1.10",
67
68
  "tsdown": "0.22.14",
68
69
  "typescript": "6.0.3",
69
- "vitest": "4.1.10",
70
- "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.25"
70
+ "vitest": "4.1.10"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "@earendil-works/pi-coding-agent": "0.84.2",
@@ -1 +0,0 @@
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;
@@ -1,12 +0,0 @@
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
@@ -1 +0,0 @@
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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"}
@@ -1,12 +0,0 @@
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
@@ -1 +0,0 @@
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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"}
@@ -1,2 +0,0 @@
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(){let e,t=!1;return{bind(n){if(t)throw Error(`The Help skill view is disposed.`);let r=Symbol(n.generation),i=e;e=void 0,i?.unsubscribe();let a={token:r,snapshot:n.getSnapshot(),unsubscribe:n.subscribeSnapshot(t=>{e?.token===r&&(e.snapshot=t)})};e=a;let o=!1;return()=>{o||(o=!0,a.unsubscribe(),e?.token===r&&(e=void 0))}},merge(t){return o(t,e?.snapshot)},dispose(){if(t)return;t=!0;let n=e;e=void 0,n?.unsubscribe()}}}exports.createActiveHelpSkillView=s,exports.mergeActiveHelpSkills=o;
2
- //# sourceMappingURL=helpSkills.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"helpSkills.cjs","names":["DOOM_HELP_MAX_DIAGNOSTIC_LENGTH","createSyntheticSourceInfo","DOOM_HELP_MAX_DIAGNOSTICS"],"sources":["../../src/adapters/helpSkills.ts"],"sourcesContent":["import {\n DOOM_HELP_MAX_DIAGNOSTIC_LENGTH,\n DOOM_HELP_MAX_DIAGNOSTICS,\n type DoomHelpService,\n type DoomHelpSkill,\n type DoomHelpSnapshot,\n} from '@agimon-ai/doompi-extension-contracts/help';\nimport { createSyntheticSourceInfo, type Skill } from '@earendil-works/pi-coding-agent';\n\nexport interface SkillInventoryInput {\n normalSkills: readonly Skill[];\n deferredSkills: readonly Skill[];\n /** Names from Pi's command catalog when full normal Skill records are unavailable. */\n normalSkillNames?: readonly string[];\n}\n\nexport interface MergedSkillInventory {\n /** 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 bind(service: DoomHelpService): () => void;\n merge(input: SkillInventoryInput): MergedSkillInventory;\n dispose(): void;\n}\n\nfunction boundedDiagnostic(message: string): string {\n return message.slice(0, DOOM_HELP_MAX_DIAGNOSTIC_LENGTH);\n}\n\nfunction toSkill(skill: DoomHelpSkill): Skill {\n return {\n name: skill.name,\n description: skill.description,\n filePath: skill.filePath,\n baseDir: skill.baseDir,\n sourceInfo: createSyntheticSourceInfo(skill.filePath, {\n source: skill.source,\n scope: 'temporary',\n origin: 'package',\n baseDir: skill.baseDir,\n }),\n disableModelInvocation: false,\n };\n}\n\nfunction activeHelpSkills(snapshot: DoomHelpSnapshot | undefined): DoomHelpSkill[] {\n if (!snapshot || snapshot.activation === 'inactive') return [];\n return [...snapshot.skills].sort(\n (left, right) =>\n left.source.localeCompare(right.source) ||\n left.name.localeCompare(right.name) ||\n left.filePath.localeCompare(right.filePath),\n );\n}\n\nfunction snapshotDiagnostics(snapshot: DoomHelpSnapshot | undefined): string[] {\n if (!snapshot) return [];\n return snapshot.diagnostics.map((entry) =>\n boundedDiagnostic(`Help ${entry.source ?? 'host'} [${entry.code}]: ${entry.message}`),\n );\n}\n\nexport function mergeActiveHelpSkills(\n input: SkillInventoryInput,\n snapshot: DoomHelpSnapshot | undefined,\n clientDiagnostics: readonly string[] = [],\n): MergedSkillInventory {\n const 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(): ActiveHelpSkillView {\n let binding: { readonly token: symbol; snapshot: DoomHelpSnapshot; unsubscribe(): void } | undefined;\n let disposed = false;\n return {\n bind(service) {\n if (disposed) throw new Error('The Help skill view is disposed.');\n const token = Symbol(service.generation);\n const current = binding;\n binding = undefined;\n current?.unsubscribe();\n const next: { readonly token: symbol; snapshot: DoomHelpSnapshot; unsubscribe(): void } = {\n token,\n snapshot: service.getSnapshot(),\n unsubscribe: service.subscribeSnapshot((snapshot) => {\n if (binding?.token === token) binding.snapshot = snapshot;\n }),\n };\n binding = next;\n let bindingDisposed = false;\n return () => {\n if (bindingDisposed) return;\n bindingDisposed = true;\n next.unsubscribe();\n if (binding?.token === token) binding = undefined;\n };\n },\n merge(input) {\n return mergeActiveHelpSkills(input, binding?.snapshot);\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n const current = binding;\n binding = undefined;\n current?.unsubscribe();\n },\n };\n}\n"],"mappings":"yGAiCA,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,GAAiD,CAC/D,IAAI,EACA,EAAW,GACf,MAAO,CACL,KAAK,EAAS,CACZ,GAAI,EAAU,MAAU,MAAM,kCAAkC,EAChE,IAAM,EAAQ,OAAO,EAAQ,UAAU,EACjC,EAAU,EAChB,EAAU,IAAA,GACV,GAAS,YAAY,EACrB,IAAM,EAAoF,CACxF,QACA,SAAU,EAAQ,YAAY,EAC9B,YAAa,EAAQ,kBAAmB,GAAa,CAC/C,GAAS,QAAU,IAAO,EAAQ,SAAW,EACnD,CAAC,CACH,EACA,EAAU,EACV,IAAI,EAAkB,GACtB,UAAa,CACP,IACJ,EAAkB,GAClB,EAAK,YAAY,EACb,GAAS,QAAU,IAAO,EAAU,IAAA,IAC1C,CACF,EACA,MAAM,EAAO,CACX,OAAO,EAAsB,EAAO,GAAS,QAAQ,CACvD,EACA,SAAU,CACR,GAAI,EAAU,OACd,EAAW,GACX,IAAM,EAAU,EAChB,EAAU,IAAA,GACV,GAAS,YAAY,CACvB,CACF,CACF"}
@@ -1,2 +0,0 @@
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}from"@agimon-ai/doompi-extension-contracts/help";function r(e){return e.slice(0,n)}function i(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 a(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 o(e){return e?e.diagnostics.map(e=>r(`Help ${e.source??`host`} [${e.code}]: ${e.message}`)):[]}function s(e,n,s=[]){let c=new Set([...e.normalSkills.map(e=>e.name),...e.deferredSkills.map(e=>e.name),...e.normalSkillNames??[]]),l=[],u=[...o(n),...s.map(r)];for(let e of a(n)){if(c.has(e.name)){u.push(r(`Help ${e.source} [HELP_SKILL_COLLISION]: '${e.name}' was ignored because a normal skill wins.`));continue}c.add(e.name),l.push(i(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 c(){let e,t=!1;return{bind(n){if(t)throw Error(`The Help skill view is disposed.`);let r=Symbol(n.generation),i=e;e=void 0,i?.unsubscribe();let a={token:r,snapshot:n.getSnapshot(),unsubscribe:n.subscribeSnapshot(t=>{e?.token===r&&(e.snapshot=t)})};e=a;let o=!1;return()=>{o||(o=!0,a.unsubscribe(),e?.token===r&&(e=void 0))}},merge(t){return s(t,e?.snapshot)},dispose(){if(t)return;t=!0;let n=e;e=void 0,n?.unsubscribe()}}}export{c as createActiveHelpSkillView,s as mergeActiveHelpSkills};
2
- //# sourceMappingURL=helpSkills.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"helpSkills.mjs","names":[],"sources":["../../src/adapters/helpSkills.ts"],"sourcesContent":["import {\n DOOM_HELP_MAX_DIAGNOSTIC_LENGTH,\n DOOM_HELP_MAX_DIAGNOSTICS,\n type DoomHelpService,\n type DoomHelpSkill,\n type DoomHelpSnapshot,\n} from '@agimon-ai/doompi-extension-contracts/help';\nimport { createSyntheticSourceInfo, type Skill } from '@earendil-works/pi-coding-agent';\n\nexport interface SkillInventoryInput {\n normalSkills: readonly Skill[];\n deferredSkills: readonly Skill[];\n /** Names from Pi's command catalog when full normal Skill records are unavailable. */\n normalSkillNames?: readonly string[];\n}\n\nexport interface MergedSkillInventory {\n /** 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 bind(service: DoomHelpService): () => void;\n merge(input: SkillInventoryInput): MergedSkillInventory;\n dispose(): void;\n}\n\nfunction boundedDiagnostic(message: string): string {\n return message.slice(0, DOOM_HELP_MAX_DIAGNOSTIC_LENGTH);\n}\n\nfunction toSkill(skill: DoomHelpSkill): Skill {\n return {\n name: skill.name,\n description: skill.description,\n filePath: skill.filePath,\n baseDir: skill.baseDir,\n sourceInfo: createSyntheticSourceInfo(skill.filePath, {\n source: skill.source,\n scope: 'temporary',\n origin: 'package',\n baseDir: skill.baseDir,\n }),\n disableModelInvocation: false,\n };\n}\n\nfunction activeHelpSkills(snapshot: DoomHelpSnapshot | undefined): DoomHelpSkill[] {\n if (!snapshot || snapshot.activation === 'inactive') return [];\n return [...snapshot.skills].sort(\n (left, right) =>\n left.source.localeCompare(right.source) ||\n left.name.localeCompare(right.name) ||\n left.filePath.localeCompare(right.filePath),\n );\n}\n\nfunction snapshotDiagnostics(snapshot: DoomHelpSnapshot | undefined): string[] {\n if (!snapshot) return [];\n return snapshot.diagnostics.map((entry) =>\n boundedDiagnostic(`Help ${entry.source ?? 'host'} [${entry.code}]: ${entry.message}`),\n );\n}\n\nexport function mergeActiveHelpSkills(\n input: SkillInventoryInput,\n snapshot: DoomHelpSnapshot | undefined,\n clientDiagnostics: readonly string[] = [],\n): MergedSkillInventory {\n const 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(): ActiveHelpSkillView {\n let binding: { readonly token: symbol; snapshot: DoomHelpSnapshot; unsubscribe(): void } | undefined;\n let disposed = false;\n return {\n bind(service) {\n if (disposed) throw new Error('The Help skill view is disposed.');\n const token = Symbol(service.generation);\n const current = binding;\n binding = undefined;\n current?.unsubscribe();\n const next: { readonly token: symbol; snapshot: DoomHelpSnapshot; unsubscribe(): void } = {\n token,\n snapshot: service.getSnapshot(),\n unsubscribe: service.subscribeSnapshot((snapshot) => {\n if (binding?.token === token) binding.snapshot = snapshot;\n }),\n };\n binding = next;\n let bindingDisposed = false;\n return () => {\n if (bindingDisposed) return;\n bindingDisposed = true;\n next.unsubscribe();\n if (binding?.token === token) binding = undefined;\n };\n },\n merge(input) {\n return mergeActiveHelpSkills(input, binding?.snapshot);\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n const current = binding;\n binding = undefined;\n current?.unsubscribe();\n },\n };\n}\n"],"mappings":"wMAiCA,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,GAAiD,CAC/D,IAAI,EACA,EAAW,GACf,MAAO,CACL,KAAK,EAAS,CACZ,GAAI,EAAU,MAAU,MAAM,kCAAkC,EAChE,IAAM,EAAQ,OAAO,EAAQ,UAAU,EACjC,EAAU,EAChB,EAAU,IAAA,GACV,GAAS,YAAY,EACrB,IAAM,EAAoF,CACxF,QACA,SAAU,EAAQ,YAAY,EAC9B,YAAa,EAAQ,kBAAmB,GAAa,CAC/C,GAAS,QAAU,IAAO,EAAQ,SAAW,EACnD,CAAC,CACH,EACA,EAAU,EACV,IAAI,EAAkB,GACtB,UAAa,CACP,IACJ,EAAkB,GAClB,EAAK,YAAY,EACb,GAAS,QAAU,IAAO,EAAU,IAAA,IAC1C,CACF,EACA,MAAM,EAAO,CACX,OAAO,EAAsB,EAAO,GAAS,QAAQ,CACvD,EACA,SAAU,CACR,GAAI,EAAU,OACd,EAAW,GACX,IAAM,EAAU,EAChB,EAAU,IAAA,GACV,GAAS,YAAY,CACvB,CACF,CACF"}
@@ -1,2 +0,0 @@
1
- const e=require("../../types/skills.cjs"),t=require("../../commands/skillsCommand.cjs"),n=require("../helpSkills.cjs"),r=require("./skillReadiness.cjs");let i=require("@agimon-ai/doompi-config/harnessStore"),a=require("@agimon-ai/doompi-config"),o=require("@agimon-ai/doompi-extension-contracts/config"),s=require("@agimon-ai/doompi-extension-contracts/cordis-host"),c=require("@agimon-ai/doompi-extension-contracts/help"),l=require("@agimon-ai/doompi-extension-contracts/skills"),u=require("@agimon-ai/doompi-extension-contracts/ui-hub");function d(){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"))}}function f(f,{pi:p}){let m=d(),h,g;f.inject([o.DOOM_CONFIG_SERVICE],e=>(h=e,()=>{h===e&&(h=void 0)}));let _=()=>{if(!h)throw Error(`Doom skill runtime is waiting for the session config service.`);return h},v=()=>{if(!g)throw Error(`Doom skill sources are waiting for the active session service.`);return g};f.inject([s.DOOM_CORDIS_SESSION_SERVICE],e=>{let t=e.get(s.DOOM_CORDIS_SESSION_SERVICE),n=(0,l.createDoomSkillSourcesService)(`${t.generation}:skill-sources`);return e.provide(l.DOOM_SKILL_SOURCES_SERVICE,n),g=n,()=>{g===n&&(g=void 0),n.dispose()}});let y=n.createActiveHelpSkillView();f.effect(()=>()=>y.dispose(),`${e.LEADER_SOURCE}/help`),f.inject([c.DOOM_HELP_SERVICE],e=>y.bind((0,c.requireDoomHelpService)(e)));let b=r.registerSkillReadiness(p,y,m.deferred,_);t.registerSkillsCommand(p,{cordisContext:_,extensionSources:()=>v().list(),inventory:r.skillInventory(y,b),repositoryRoot:async e=>(0,i.requireHarnessRoot)((0,a.requireDoomConfigContext)(_()).harness),buildCatalog:async e=>(await m.catalog()).buildSkillCatalog(e),openOverlay:async(e,t)=>(await m.overlay()).openSkillsOverlay(e,t)}),f.inject([u.DOOM_UI_HUB_SERVICE],t=>{let n=(0,u.requireDoomUiHub)(t).registerLeader(e.SKILLS_LEADER_CONTRIBUTION);return()=>n.dispose()})}async function p(t){let n=await(0,s.connectDoomCordisHost)(t,e.LEADER_SOURCE),r=n.root.plugin(f,{pi:t});try{await r}catch(e){try{await r.dispose()}finally{await n.dispose()}throw e}let i;t.on(`session_shutdown`,()=>i??=(async()=>{try{await r.dispose()}finally{await n.dispose()}})())}exports.default=p,exports.skillsExtension=p;
2
- //# sourceMappingURL=extension.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"extension.cjs","names":["DOOM_CONFIG_SERVICE","DOOM_CORDIS_SESSION_SERVICE","createDoomSkillSourcesService","DOOM_SKILL_SOURCES_SERVICE","createActiveHelpSkillView","LEADER_SOURCE","DOOM_HELP_SERVICE","requireDoomHelpService","registerSkillReadiness","skillInventory","requireHarnessRoot","requireDoomConfigContext","DOOM_UI_HUB_SERVICE","requireDoomUiHub","SKILLS_LEADER_CONTRIBUTION","connectDoomCordisHost"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { requireHarnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport { requireDoomConfigContext } from '@agimon-ai/doompi-config';\nimport { DOOM_CONFIG_SERVICE } from '@agimon-ai/doompi-extension-contracts/config';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n createDoomSkillSourcesService,\n DOOM_SKILL_SOURCES_SERVICE,\n type DoomSkillSourcesService,\n} from '@agimon-ai/doompi-extension-contracts/skills';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport type { 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 { registerSkillReadiness, skillInventory } from './skillReadiness.ts';\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\ninterface SkillPluginConfig {\n readonly pi: ExtensionAPI;\n}\n\nfunction skillPlugin(cordis: Context, { pi }: SkillPluginConfig): void {\n const load = lazyModules();\n let activeContext: Context | undefined;\n let activeSources: DoomSkillSourcesService | undefined;\n cordis.inject([DOOM_CONFIG_SERVICE], (context) => {\n activeContext = context;\n return () => {\n if (activeContext === context) activeContext = undefined;\n };\n });\n const requireRuntimeContext = (): Context => {\n if (!activeContext) throw new Error('Doom skill runtime is waiting for the session config service.');\n return activeContext;\n };\n const requireSkillSources = (): DoomSkillSourcesService => {\n if (!activeSources) throw new Error('Doom skill sources are waiting for the active session service.');\n return activeSources;\n };\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const service = createDoomSkillSourcesService(`${session.generation}:skill-sources`);\n sessionContext.provide(DOOM_SKILL_SOURCES_SERVICE, service);\n activeSources = service;\n return () => {\n if (activeSources === service) activeSources = undefined;\n service.dispose();\n };\n });\n\n const helpSkillView = createActiveHelpSkillView();\n cordis.effect(() => () => helpSkillView.dispose(), `${LEADER_SOURCE}/help`);\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => helpSkillView.bind(requireDoomHelpService(helpContext)));\n const readiness = registerSkillReadiness(pi, helpSkillView, load.deferred, requireRuntimeContext);\n registerSkillsCommand(pi, {\n cordisContext: requireRuntimeContext,\n extensionSources: () => requireSkillSources().list(),\n inventory: skillInventory(helpSkillView, readiness),\n repositoryRoot: async (_ctx: ExtensionContext) =>\n requireHarnessRoot(requireDoomConfigContext(requireRuntimeContext()).harness),\n buildCatalog: async (request) => (await load.catalog()).buildSkillCatalog(request),\n openOverlay: async (ctx, options) => (await load.overlay()).openSkillsOverlay(ctx, options),\n });\n\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = requireDoomUiHub(uiContext).registerLeader(SKILLS_LEADER_CONTRIBUTION);\n return () => contribution.dispose();\n });\n}\n\n/** The package's single standard Pi factory. */\nexport async function skillsExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, LEADER_SOURCE);\n const fiber = connection.root.plugin(skillPlugin, { pi });\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\nexport default skillsExtension;\n"],"mappings":"2hBA4BA,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,CAMA,SAAS,EAAY,EAAiB,CAAE,MAA+B,CACrE,IAAM,EAAO,EAAY,EACrB,EACA,EACJ,EAAO,OAAO,CAACA,EAAAA,mBAAmB,EAAI,IACpC,EAAgB,MACH,CACP,IAAkB,IAAS,EAAgB,IAAA,GACjD,EACD,EACD,IAAM,MAAuC,CAC3C,GAAI,CAAC,EAAe,MAAU,MAAM,+DAA+D,EACnG,OAAO,CACT,EACM,MAAqD,CACzD,GAAI,CAAC,EAAe,MAAU,MAAM,gEAAgE,EACpG,OAAO,CACT,EACA,EAAO,OAAO,CAACC,EAAAA,2BAA2B,EAAI,GAAmB,CAC/D,IAAM,EAAU,EAAe,IAAIA,EAAAA,2BAA2B,EACxD,GAAA,EAAUC,EAAAA,8BAAAA,CAA8B,GAAG,EAAQ,WAAW,eAAe,EAGnF,OAFA,EAAe,QAAQC,EAAAA,2BAA4B,CAAO,EAC1D,EAAgB,MACH,CACP,IAAkB,IAAS,EAAgB,IAAA,IAC/C,EAAQ,QAAQ,CAClB,CACF,CAAC,EAED,IAAM,EAAgBC,EAAAA,0BAA0B,EAChD,EAAO,eAAmB,EAAc,QAAQ,EAAG,GAAGC,EAAAA,cAAc,MAAM,EAC1E,EAAO,OAAO,CAACC,EAAAA,iBAAiB,EAAI,GAAgB,EAAc,MAAA,EAAKC,EAAAA,uBAAAA,CAAuB,CAAW,CAAC,CAAC,EAC3G,IAAM,EAAYC,EAAAA,uBAAuB,EAAI,EAAe,EAAK,SAAU,CAAqB,EAChG,EAAA,sBAAsB,EAAI,CACxB,cAAe,EACf,qBAAwB,EAAoB,CAAC,CAAC,KAAK,EACnD,UAAWC,EAAAA,eAAe,EAAe,CAAS,EAClD,eAAgB,KAAO,KAAA,EACrBC,EAAAA,mBAAAA,EAAAA,EAAmBC,EAAAA,yBAAAA,CAAyB,EAAsB,CAAC,CAAC,CAAC,OAAO,EAC9E,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,EAED,EAAO,OAAO,CAACC,EAAAA,mBAAmB,EAAI,GAAc,CAClD,IAAM,GAAA,EAAeC,EAAAA,iBAAAA,CAAiB,CAAS,CAAC,CAAC,eAAeC,EAAAA,0BAA0B,EAC1F,UAAa,EAAa,QAAQ,CACpC,CAAC,CACH,CAGA,eAAsB,EAAgB,EAAiC,CACrE,IAAM,EAAa,MAAA,EAAMC,EAAAA,sBAAAA,CAAsB,EAAIV,EAAAA,aAAa,EAC1D,EAAQ,EAAW,KAAK,OAAO,EAAa,CAAE,IAAG,CAAC,EACxD,GAAI,CACF,MAAM,CACR,OAAS,EAAO,CACd,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACA,MAAM,CACR,CACA,IAAI,EACJ,EAAG,GACD,uBAEG,KAAc,SAAY,CACzB,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACF,EAAA,CAAG,CACP,CACF"}
@@ -1,7 +0,0 @@
1
- import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- //#region src/adapters/pi/extension.d.ts
3
- /** The package's single standard Pi factory. */
4
- declare function skillsExtension(pi: ExtensionAPI): Promise<void>;
5
- //#endregion
6
- export { skillsExtension as default, skillsExtension };
7
- //# sourceMappingURL=extension.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;iBA6FsB,gBAAgB,IAAI,eAAe"}
@@ -1,7 +0,0 @@
1
- import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- //#region src/adapters/pi/extension.d.ts
3
- /** The package's single standard Pi factory. */
4
- declare function skillsExtension(pi: ExtensionAPI): Promise<void>;
5
- //#endregion
6
- export { skillsExtension as default, skillsExtension };
7
- //# sourceMappingURL=extension.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;iBA6FsB,gBAAgB,IAAI,eAAe"}
@@ -1,2 +0,0 @@
1
- import{LEADER_SOURCE as e,SKILLS_LEADER_CONTRIBUTION as t}from"../../types/skills.mjs";import{registerSkillsCommand as n}from"../../commands/skillsCommand.mjs";import{createActiveHelpSkillView as r}from"../helpSkills.mjs";import{registerSkillReadiness as i,skillInventory as a}from"./skillReadiness.mjs";import{requireHarnessRoot as o}from"@agimon-ai/doompi-config/harnessStore";import{requireDoomConfigContext as s}from"@agimon-ai/doompi-config";import{DOOM_CONFIG_SERVICE as c}from"@agimon-ai/doompi-extension-contracts/config";import{DOOM_CORDIS_SESSION_SERVICE as l,connectDoomCordisHost as u}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_HELP_SERVICE as d,requireDoomHelpService as f}from"@agimon-ai/doompi-extension-contracts/help";import{DOOM_SKILL_SOURCES_SERVICE as p,createDoomSkillSourcesService as m}from"@agimon-ai/doompi-extension-contracts/skills";import{DOOM_UI_HUB_SERVICE as h,requireDoomUiHub as g}from"@agimon-ai/doompi-extension-contracts/ui-hub";function _(){let e,t,n;return{catalog:()=>e??=import(`../skillCatalog.mjs`),deferred:()=>t??=import(`../deferredSkills.mjs`),overlay:()=>n??=import(`../../tui/skillsOverlay.mjs`)}}function v(u,{pi:v}){let y=_(),b,x;u.inject([c],e=>(b=e,()=>{b===e&&(b=void 0)}));let S=()=>{if(!b)throw Error(`Doom skill runtime is waiting for the session config service.`);return b},C=()=>{if(!x)throw Error(`Doom skill sources are waiting for the active session service.`);return x};u.inject([l],e=>{let t=e.get(l),n=m(`${t.generation}:skill-sources`);return e.provide(p,n),x=n,()=>{x===n&&(x=void 0),n.dispose()}});let w=r();u.effect(()=>()=>w.dispose(),`${e}/help`),u.inject([d],e=>w.bind(f(e)));let T=i(v,w,y.deferred,S);n(v,{cordisContext:S,extensionSources:()=>C().list(),inventory:a(w,T),repositoryRoot:async e=>o(s(S()).harness),buildCatalog:async e=>(await y.catalog()).buildSkillCatalog(e),openOverlay:async(e,t)=>(await y.overlay()).openSkillsOverlay(e,t)}),u.inject([h],e=>{let n=g(e).registerLeader(t);return()=>n.dispose()})}async function y(t){let n=await u(t,e),r=n.root.plugin(v,{pi:t});try{await r}catch(e){try{await r.dispose()}finally{await n.dispose()}throw e}let i;t.on(`session_shutdown`,()=>i??=(async()=>{try{await r.dispose()}finally{await n.dispose()}})())}export{y as default,y as skillsExtension};
2
- //# sourceMappingURL=extension.mjs.map
@@ -1 +0,0 @@
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';\nimport { DOOM_CONFIG_SERVICE } from '@agimon-ai/doompi-extension-contracts/config';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n createDoomSkillSourcesService,\n DOOM_SKILL_SOURCES_SERVICE,\n type DoomSkillSourcesService,\n} from '@agimon-ai/doompi-extension-contracts/skills';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport type { 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 { registerSkillReadiness, skillInventory } from './skillReadiness.ts';\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\ninterface SkillPluginConfig {\n readonly pi: ExtensionAPI;\n}\n\nfunction skillPlugin(cordis: Context, { pi }: SkillPluginConfig): void {\n const load = lazyModules();\n let activeContext: Context | undefined;\n let activeSources: DoomSkillSourcesService | undefined;\n cordis.inject([DOOM_CONFIG_SERVICE], (context) => {\n activeContext = context;\n return () => {\n if (activeContext === context) activeContext = undefined;\n };\n });\n const requireRuntimeContext = (): Context => {\n if (!activeContext) throw new Error('Doom skill runtime is waiting for the session config service.');\n return activeContext;\n };\n const requireSkillSources = (): DoomSkillSourcesService => {\n if (!activeSources) throw new Error('Doom skill sources are waiting for the active session service.');\n return activeSources;\n };\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const service = createDoomSkillSourcesService(`${session.generation}:skill-sources`);\n sessionContext.provide(DOOM_SKILL_SOURCES_SERVICE, service);\n activeSources = service;\n return () => {\n if (activeSources === service) activeSources = undefined;\n service.dispose();\n };\n });\n\n const helpSkillView = createActiveHelpSkillView();\n cordis.effect(() => () => helpSkillView.dispose(), `${LEADER_SOURCE}/help`);\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => helpSkillView.bind(requireDoomHelpService(helpContext)));\n const readiness = registerSkillReadiness(pi, helpSkillView, load.deferred, requireRuntimeContext);\n registerSkillsCommand(pi, {\n cordisContext: requireRuntimeContext,\n extensionSources: () => requireSkillSources().list(),\n inventory: skillInventory(helpSkillView, readiness),\n repositoryRoot: async (_ctx: ExtensionContext) =>\n requireHarnessRoot(requireDoomConfigContext(requireRuntimeContext()).harness),\n buildCatalog: async (request) => (await load.catalog()).buildSkillCatalog(request),\n openOverlay: async (ctx, options) => (await load.overlay()).openSkillsOverlay(ctx, options),\n });\n\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = requireDoomUiHub(uiContext).registerLeader(SKILLS_LEADER_CONTRIBUTION);\n return () => contribution.dispose();\n });\n}\n\n/** The package's single standard Pi factory. */\nexport async function skillsExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, LEADER_SOURCE);\n const fiber = connection.root.plugin(skillPlugin, { pi });\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\nexport default skillsExtension;\n"],"mappings":"89BA4BA,SAAS,GAAc,CACrB,IAAI,EACA,EACA,EACJ,MAAO,CACL,YAAgB,IAAY,OAAO,uBACnC,aAAiB,IAAa,OAAO,yBACrC,YAAgB,IAAY,OAAO,8BACrC,CACF,CAMA,SAAS,EAAY,EAAiB,CAAE,MAA+B,CACrE,IAAM,EAAO,EAAY,EACrB,EACA,EACJ,EAAO,OAAO,CAAC,CAAmB,EAAI,IACpC,EAAgB,MACH,CACP,IAAkB,IAAS,EAAgB,IAAA,GACjD,EACD,EACD,IAAM,MAAuC,CAC3C,GAAI,CAAC,EAAe,MAAU,MAAM,+DAA+D,EACnG,OAAO,CACT,EACM,MAAqD,CACzD,GAAI,CAAC,EAAe,MAAU,MAAM,gEAAgE,EACpG,OAAO,CACT,EACA,EAAO,OAAO,CAAC,CAA2B,EAAI,GAAmB,CAC/D,IAAM,EAAU,EAAe,IAAI,CAA2B,EACxD,EAAU,EAA8B,GAAG,EAAQ,WAAW,eAAe,EAGnF,OAFA,EAAe,QAAQ,EAA4B,CAAO,EAC1D,EAAgB,MACH,CACP,IAAkB,IAAS,EAAgB,IAAA,IAC/C,EAAQ,QAAQ,CAClB,CACF,CAAC,EAED,IAAM,EAAgB,EAA0B,EAChD,EAAO,eAAmB,EAAc,QAAQ,EAAG,GAAG,EAAc,MAAM,EAC1E,EAAO,OAAO,CAAC,CAAiB,EAAI,GAAgB,EAAc,KAAK,EAAuB,CAAW,CAAC,CAAC,EAC3G,IAAM,EAAY,EAAuB,EAAI,EAAe,EAAK,SAAU,CAAqB,EAChG,EAAsB,EAAI,CACxB,cAAe,EACf,qBAAwB,EAAoB,CAAC,CAAC,KAAK,EACnD,UAAW,EAAe,EAAe,CAAS,EAClD,eAAgB,KAAO,IACrB,EAAmB,EAAyB,EAAsB,CAAC,CAAC,CAAC,OAAO,EAC9E,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,EAED,EAAO,OAAO,CAAC,CAAmB,EAAI,GAAc,CAClD,IAAM,EAAe,EAAiB,CAAS,CAAC,CAAC,eAAe,CAA0B,EAC1F,UAAa,EAAa,QAAQ,CACpC,CAAC,CACH,CAGA,eAAsB,EAAgB,EAAiC,CACrE,IAAM,EAAa,MAAM,EAAsB,EAAI,CAAa,EAC1D,EAAQ,EAAW,KAAK,OAAO,EAAa,CAAE,IAAG,CAAC,EACxD,GAAI,CACF,MAAM,CACR,OAAS,EAAO,CACd,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACA,MAAM,CACR,CACA,IAAI,EACJ,EAAG,GACD,uBAEG,KAAc,SAAY,CACzB,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACF,EAAA,CAAG,CACP,CACF"}
@@ -1,4 +0,0 @@
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,s){let c=0,l,u=``,d=async e=>{let t=await e.ready;return l===e?t:void 0};return e.on(`session_start`,(e,n)=>{let r=(0,t.requireDoomConfigContext)(s()).harness;l={id:++c,ready:o().then(({DeferredSkillLoader:e})=>new e({cwd:n.cwd,skillPaths:r.skillDirectories}).start()).catch(e=>i(n.cwd,e)),diagnosticsShown:!1},u=``}),e.on(`input`,async t=>{if(!t.text.startsWith(`/skill:`))return{action:`continue`};let n=l;if(!n)return{action:`continue`};let i=await d(n);if(!i)return{action:`continue`};let s=a.merge({normalSkills:[],deferredSkills:i.skills,normalSkillNames:r(e)}),{expandDeferredSkillCommand:c}=await o(),u=c(t.text,s.additionalSkills);return u===t.text?{action:`continue`}:{action:`transform`,text:u,images:t.images}}),e.on(`before_agent_start`,async(e,t)=>{let r=l;if(!r)return;let i=await d(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!==u&&(u=s.diagnosticKey,s.diagnostics.length>0&&t.ui.notify(`Package Help:\n${s.diagnostics.join(`
3
- `)}`,n)),s.additionalSkills.length===0)return;let{buildPromptWithDeferredSkills:c}=await o();return{systemPrompt:c(e.systemPrompt,e.systemPromptOptions,[...s.additionalSkills])}}),{current:async()=>l?d(l):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
@@ -1 +0,0 @@
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 type { Context } from '@deepseek-ai/cordis';\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 cordisContext: () => Context,\n): SkillReadiness {\n let generationSequence = 0;\n let currentGeneration: SkillReadinessGeneration | undefined;\n let shownHelpDiagnosticKey = '';\n\n const currentSnapshot = async (generation: SkillReadinessGeneration): Promise<DeferredSkillSnapshot | undefined> => {\n const snapshot = await generation.ready;\n return currentGeneration === generation ? snapshot : undefined;\n };\n\n pi.on('session_start', (_event, ctx) => {\n const state = requireDoomConfigContext(cordisContext()).harness;\n const id = ++generationSequence;\n const ready = loadDeferredSkills()\n .then(({ DeferredSkillLoader }) =>\n new DeferredSkillLoader({ cwd: ctx.cwd, skillPaths: state.skillDirectories }).start(),\n )\n .catch((error: unknown) => failedSnapshot(ctx.cwd, error));\n currentGeneration = { id, ready, diagnosticsShown: false };\n shownHelpDiagnosticKey = '';\n });\n\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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"}
@@ -1,4 +0,0 @@
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,s){let c=0,l,u=``,d=async e=>{let t=await e.ready;return l===e?t:void 0};return e.on(`session_start`,(e,n)=>{let r=t(s()).harness;l={id:++c,ready:o().then(({DeferredSkillLoader:e})=>new e({cwd:n.cwd,skillPaths:r.skillDirectories}).start()).catch(e=>i(n.cwd,e)),diagnosticsShown:!1},u=``}),e.on(`input`,async t=>{if(!t.text.startsWith(`/skill:`))return{action:`continue`};let n=l;if(!n)return{action:`continue`};let i=await d(n);if(!i)return{action:`continue`};let s=a.merge({normalSkills:[],deferredSkills:i.skills,normalSkillNames:r(e)}),{expandDeferredSkillCommand:c}=await o(),u=c(t.text,s.additionalSkills);return u===t.text?{action:`continue`}:{action:`transform`,text:u,images:t.images}}),e.on(`before_agent_start`,async(e,t)=>{let r=l;if(!r)return;let i=await d(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!==u&&(u=s.diagnosticKey,s.diagnostics.length>0&&t.ui.notify(`Package Help:\n${s.diagnostics.join(`
3
- `)}`,n)),s.additionalSkills.length===0)return;let{buildPromptWithDeferredSkills:c}=await o();return{systemPrompt:c(e.systemPrompt,e.systemPromptOptions,[...s.additionalSkills])}}),{current:async()=>l?d(l):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
@@ -1 +0,0 @@
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 type { Context } from '@deepseek-ai/cordis';\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 cordisContext: () => Context,\n): SkillReadiness {\n let generationSequence = 0;\n let currentGeneration: SkillReadinessGeneration | undefined;\n let shownHelpDiagnosticKey = '';\n\n const currentSnapshot = async (generation: SkillReadinessGeneration): Promise<DeferredSkillSnapshot | undefined> => {\n const snapshot = await generation.ready;\n return currentGeneration === generation ? snapshot : undefined;\n };\n\n pi.on('session_start', (_event, ctx) => {\n const state = requireDoomConfigContext(cordisContext()).harness;\n const id = ++generationSequence;\n const ready = loadDeferredSkills()\n .then(({ DeferredSkillLoader }) =>\n new DeferredSkillLoader({ cwd: ctx.cwd, skillPaths: state.skillDirectories }).start(),\n )\n .catch((error: unknown) => failedSnapshot(ctx.cwd, error));\n currentGeneration = { id, ready, diagnosticsShown: false };\n shownHelpDiagnosticKey = '';\n });\n\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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"}
@@ -1,2 +0,0 @@
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
@@ -1 +0,0 @@
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 type { SkillSourceContribution } from '@agimon-ai/doompi-extension-contracts/skills';\nimport { formatSkillsForPrompt, loadSkillsFromDir, type Skill } from '@earendil-works/pi-coding-agent';\nimport { materializePluginEntries } from '@agimon-ai/doompi-domain/plugins';\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 SkillSourceContribution[];\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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"}
@@ -1,58 +0,0 @@
1
- import { SkillSourceContribution } from "@agimon-ai/doompi-extension-contracts/skills";
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 SkillSourceContribution[];
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
@@ -1 +0,0 @@
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"}
@@ -1,58 +0,0 @@
1
- import { Skill } from "@earendil-works/pi-coding-agent";
2
- import { SkillSourceContribution } from "@agimon-ai/doompi-extension-contracts/skills";
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 SkillSourceContribution[];
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
@@ -1 +0,0 @@
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"}
@@ -1,2 +0,0 @@
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
@@ -1 +0,0 @@
1
- {"version":3,"file":"skillCatalog.mjs","names":[],"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 type { SkillSourceContribution } from '@agimon-ai/doompi-extension-contracts/skills';\nimport { formatSkillsForPrompt, loadSkillsFromDir, type Skill } from '@earendil-works/pi-coding-agent';\nimport { materializePluginEntries } from '@agimon-ai/doompi-domain/plugins';\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 SkillSourceContribution[];\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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"}
package/dist/catalog.cjs DELETED
@@ -1 +0,0 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./adapters/skillCatalog.cjs");exports.buildSkillCatalog=e.buildSkillCatalog,exports.counter=e.counter;
@@ -1,2 +0,0 @@
1
- import { SkillCatalog, SkillCatalogOptions, SkillEntry, SkillGroup, SkillOwnerNode, buildSkillCatalog, counter } from "./adapters/skillCatalog.cjs";
2
- export { type SkillCatalog, type SkillCatalogOptions, type SkillEntry, type SkillGroup, type SkillOwnerNode, buildSkillCatalog, counter };
@@ -1,2 +0,0 @@
1
- import { SkillCatalog, SkillCatalogOptions, SkillEntry, SkillGroup, SkillOwnerNode, buildSkillCatalog, counter } from "./adapters/skillCatalog.mjs";
2
- export { type SkillCatalog, type SkillCatalogOptions, type SkillEntry, type SkillGroup, type SkillOwnerNode, buildSkillCatalog, counter };
package/dist/catalog.mjs DELETED
@@ -1 +0,0 @@
1
- import{buildSkillCatalog as e,counter as t}from"./adapters/skillCatalog.mjs";export{e as buildSkillCatalog,t as counter};
@@ -1,2 +0,0 @@
1
- const e=require("../types/skills.cjs"),t=require("../services/skillText.cjs");let n=require("@agimon-ai/doompi-config/piContext");function r(r,i){r.registerCommand(e.SKILLS_COMMAND,{description:`Browse the skills this session loaded, and what other domains would add`,handler:async(e,r)=>{if(!r.hasUI||r.mode!==`tui`){r.ui.notify(`/skills requires interactive mode`,`error`);return}let a=(0,n.requireDoomConfigContext)(i.cordisContext()).harness,[o,s]=await Promise.all([i.repositoryRoot(r),i.inventory(r)]),c=await i.buildCatalog({repoRoot:o,activeSkillDirectories:a.skillDirectories,extensionSources:i.extensionSources(),helpSkills:s.helpSkills,helpDiagnostics:[...s.diagnostics]}),l=await i.openOverlay(r,{catalog:c,repoRoot:o});l&&(r.ui.setEditorText(t.skillInvocation(l.skill.name)),r.ui.notify(`Loaded ${l.skill.name} into the prompt. Press enter to run it.`,`info`))}})}exports.registerSkillsCommand=r;
2
- //# sourceMappingURL=skillsCommand.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skillsCommand.cjs","names":["SKILLS_COMMAND","requireDoomConfigContext","skillInvocation"],"sources":["../../src/commands/skillsCommand.ts"],"sourcesContent":["import { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport type { Context } from '@deepseek-ai/cordis';\nimport { skillInvocation } from '../services/skillText.ts';\nimport type { SkillCatalog, SkillCatalogOptions } from '../adapters/skillCatalog.ts';\nimport type { SkillsOverlayResult } from '../tui/skillsOverlay.ts';\nimport { SKILLS_COMMAND } from '../types/skills.ts';\n\nconst INFO = 'info';\nconst ERROR = 'error';\n\nexport interface SkillsCommandDependencies {\n readonly cordisContext: () => Context;\n /** Skill directories every loaded extension has contributed this session. */\n readonly extensionSources: () => readonly { source: string; directories: readonly string[] }[];\n /** Merged view of normal, deferred and package Help skills. */\n readonly inventory: (ctx: ExtensionContext) => Promise<{\n readonly helpSkills: SkillCatalogOptions['helpSkills'];\n readonly diagnostics: readonly string[];\n }>;\n readonly repositoryRoot: (ctx: ExtensionContext) => Promise<string>;\n readonly buildCatalog: (request: SkillCatalogOptions) => Promise<SkillCatalog>;\n readonly openOverlay: (\n ctx: ExtensionContext,\n options: { catalog: SkillCatalog; repoRoot: string },\n ) => Promise<SkillsOverlayResult | undefined>;\n}\n\nexport function registerSkillsCommand(pi: ExtensionAPI, dependencies: SkillsCommandDependencies): void {\n pi.registerCommand(SKILLS_COMMAND, {\n description: 'Browse the skills this session loaded, and what other domains would add',\n handler: async (_args, ctx) => {\n if (!ctx.hasUI || ctx.mode !== 'tui') {\n ctx.ui.notify('/skills requires interactive mode', ERROR);\n return;\n }\n\n const state = requireDoomConfigContext(dependencies.cordisContext()).harness;\n const [repoRoot, inventory] = await Promise.all([dependencies.repositoryRoot(ctx), dependencies.inventory(ctx)]);\n const catalog = await dependencies.buildCatalog({\n repoRoot,\n activeSkillDirectories: state.skillDirectories,\n extensionSources: dependencies.extensionSources(),\n helpSkills: inventory.helpSkills,\n helpDiagnostics: [...inventory.diagnostics],\n });\n\n const result = await dependencies.openOverlay(ctx, { catalog, repoRoot });\n if (!result) return;\n ctx.ui.setEditorText(skillInvocation(result.skill.name));\n ctx.ui.notify(`Loaded ${result.skill.name} into the prompt. Press enter to run it.`, INFO);\n },\n });\n}\n"],"mappings":"kIA4BA,SAAgB,EAAsB,EAAkB,EAA+C,CACrG,EAAG,gBAAgBA,EAAAA,eAAgB,CACjC,YAAa,0EACb,QAAS,MAAO,EAAO,IAAQ,CAC7B,GAAI,CAAC,EAAI,OAAS,EAAI,OAAS,MAAO,CACpC,EAAI,GAAG,OAAO,oCAAqC,OAAK,EACxD,MACF,CAEA,IAAM,GAAA,EAAQC,EAAAA,yBAAAA,CAAyB,EAAa,cAAc,CAAC,CAAC,CAAC,QAC/D,CAAC,EAAU,GAAa,MAAM,QAAQ,IAAI,CAAC,EAAa,eAAe,CAAG,EAAG,EAAa,UAAU,CAAG,CAAC,CAAC,EACzG,EAAU,MAAM,EAAa,aAAa,CAC9C,WACA,uBAAwB,EAAM,iBAC9B,iBAAkB,EAAa,iBAAiB,EAChD,WAAY,EAAU,WACtB,gBAAiB,CAAC,GAAG,EAAU,WAAW,CAC5C,CAAC,EAEK,EAAS,MAAM,EAAa,YAAY,EAAK,CAAE,UAAS,UAAS,CAAC,EACnE,IACL,EAAI,GAAG,cAAcC,EAAAA,gBAAgB,EAAO,MAAM,IAAI,CAAC,EACvD,EAAI,GAAG,OAAO,UAAU,EAAO,MAAM,KAAK,0CAA2C,MAAI,EAC3F,CACF,CAAC,CACH"}
@@ -1,2 +0,0 @@
1
- import{SKILLS_COMMAND as e}from"../types/skills.mjs";import{skillInvocation as t}from"../services/skillText.mjs";import{requireDoomConfigContext as n}from"@agimon-ai/doompi-config/piContext";function r(r,i){r.registerCommand(e,{description:`Browse the skills this session loaded, and what other domains would add`,handler:async(e,r)=>{if(!r.hasUI||r.mode!==`tui`){r.ui.notify(`/skills requires interactive mode`,`error`);return}let a=n(i.cordisContext()).harness,[o,s]=await Promise.all([i.repositoryRoot(r),i.inventory(r)]),c=await i.buildCatalog({repoRoot:o,activeSkillDirectories:a.skillDirectories,extensionSources:i.extensionSources(),helpSkills:s.helpSkills,helpDiagnostics:[...s.diagnostics]}),l=await i.openOverlay(r,{catalog:c,repoRoot:o});l&&(r.ui.setEditorText(t(l.skill.name)),r.ui.notify(`Loaded ${l.skill.name} into the prompt. Press enter to run it.`,`info`))}})}export{r as registerSkillsCommand};
2
- //# sourceMappingURL=skillsCommand.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skillsCommand.mjs","names":[],"sources":["../../src/commands/skillsCommand.ts"],"sourcesContent":["import { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport type { Context } from '@deepseek-ai/cordis';\nimport { skillInvocation } from '../services/skillText.ts';\nimport type { SkillCatalog, SkillCatalogOptions } from '../adapters/skillCatalog.ts';\nimport type { SkillsOverlayResult } from '../tui/skillsOverlay.ts';\nimport { SKILLS_COMMAND } from '../types/skills.ts';\n\nconst INFO = 'info';\nconst ERROR = 'error';\n\nexport interface SkillsCommandDependencies {\n readonly cordisContext: () => Context;\n /** Skill directories every loaded extension has contributed this session. */\n readonly extensionSources: () => readonly { source: string; directories: readonly string[] }[];\n /** Merged view of normal, deferred and package Help skills. */\n readonly inventory: (ctx: ExtensionContext) => Promise<{\n readonly helpSkills: SkillCatalogOptions['helpSkills'];\n readonly diagnostics: readonly string[];\n }>;\n readonly repositoryRoot: (ctx: ExtensionContext) => Promise<string>;\n readonly buildCatalog: (request: SkillCatalogOptions) => Promise<SkillCatalog>;\n readonly openOverlay: (\n ctx: ExtensionContext,\n options: { catalog: SkillCatalog; repoRoot: string },\n ) => Promise<SkillsOverlayResult | undefined>;\n}\n\nexport function registerSkillsCommand(pi: ExtensionAPI, dependencies: SkillsCommandDependencies): void {\n pi.registerCommand(SKILLS_COMMAND, {\n description: 'Browse the skills this session loaded, and what other domains would add',\n handler: async (_args, ctx) => {\n if (!ctx.hasUI || ctx.mode !== 'tui') {\n ctx.ui.notify('/skills requires interactive mode', ERROR);\n return;\n }\n\n const state = requireDoomConfigContext(dependencies.cordisContext()).harness;\n const [repoRoot, inventory] = await Promise.all([dependencies.repositoryRoot(ctx), dependencies.inventory(ctx)]);\n const catalog = await dependencies.buildCatalog({\n repoRoot,\n activeSkillDirectories: state.skillDirectories,\n extensionSources: dependencies.extensionSources(),\n helpSkills: inventory.helpSkills,\n helpDiagnostics: [...inventory.diagnostics],\n });\n\n const result = await dependencies.openOverlay(ctx, { catalog, repoRoot });\n if (!result) return;\n ctx.ui.setEditorText(skillInvocation(result.skill.name));\n ctx.ui.notify(`Loaded ${result.skill.name} into the prompt. Press enter to run it.`, INFO);\n },\n });\n}\n"],"mappings":"+LA4BA,SAAgB,EAAsB,EAAkB,EAA+C,CACrG,EAAG,gBAAgB,EAAgB,CACjC,YAAa,0EACb,QAAS,MAAO,EAAO,IAAQ,CAC7B,GAAI,CAAC,EAAI,OAAS,EAAI,OAAS,MAAO,CACpC,EAAI,GAAG,OAAO,oCAAqC,OAAK,EACxD,MACF,CAEA,IAAM,EAAQ,EAAyB,EAAa,cAAc,CAAC,CAAC,CAAC,QAC/D,CAAC,EAAU,GAAa,MAAM,QAAQ,IAAI,CAAC,EAAa,eAAe,CAAG,EAAG,EAAa,UAAU,CAAG,CAAC,CAAC,EACzG,EAAU,MAAM,EAAa,aAAa,CAC9C,WACA,uBAAwB,EAAM,iBAC9B,iBAAkB,EAAa,iBAAiB,EAChD,WAAY,EAAU,WACtB,gBAAiB,CAAC,GAAG,EAAU,WAAW,CAC5C,CAAC,EAEK,EAAS,MAAM,EAAa,YAAY,EAAK,CAAE,UAAS,UAAS,CAAC,EACnE,IACL,EAAI,GAAG,cAAc,EAAgB,EAAO,MAAM,IAAI,CAAC,EACvD,EAAI,GAAG,OAAO,UAAU,EAAO,MAAM,KAAK,0CAA2C,MAAI,EAC3F,CACF,CAAC,CACH"}
@@ -1 +0,0 @@
1
- Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../adapters/pi/extension.cjs");exports.default=e.default,exports.skillsExtension=e.default;
@@ -1,2 +0,0 @@
1
- import skillsExtension from "../adapters/pi/extension.cjs";
2
- export { skillsExtension as default, skillsExtension };
@@ -1,2 +0,0 @@
1
- import skillsExtension from "../adapters/pi/extension.mjs";
2
- export { skillsExtension as default, skillsExtension };
@@ -1 +0,0 @@
1
- import e from"../adapters/pi/extension.mjs";export{e as default,e as skillsExtension};
package/dist/index.cjs DELETED
@@ -1 +0,0 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./types/skills.cjs"),t=require("./services/skillText.cjs"),n=require("./adapters/pi/extension.cjs"),r=require("./tui/skillsOverlay.cjs");exports.LEADER_SOURCE=e.LEADER_SOURCE,exports.SKILLS_COMMAND=e.SKILLS_COMMAND,exports.SKILLS_LEADER_CONTRIBUTION=e.SKILLS_LEADER_CONTRIBUTION,exports.SKILL_COMMAND_PREFIX=e.SKILL_COMMAND_PREFIX,exports.SKILL_INVOCATION_PREFIX=e.SKILL_INVOCATION_PREFIX,exports.openSkillsOverlay=r.openSkillsOverlay,exports.skillInvocation=t.skillInvocation,exports.skillsExtension=n.default;
package/dist/index.d.cts DELETED
@@ -1,5 +0,0 @@
1
- import skillsExtension from "./adapters/pi/extension.cjs";
2
- import { skillInvocation } from "./services/skillText.cjs";
3
- import { SkillsOverlayResult, openSkillsOverlay } from "./tui/skillsOverlay.cjs";
4
- import { LEADER_SOURCE, SKILLS_COMMAND, SKILLS_LEADER_CONTRIBUTION, SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX } from "./types/skills.cjs";
5
- export { LEADER_SOURCE, SKILLS_COMMAND, SKILLS_LEADER_CONTRIBUTION, SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX, type SkillsOverlayResult, openSkillsOverlay, skillInvocation, skillsExtension };
package/dist/index.d.mts DELETED
@@ -1,5 +0,0 @@
1
- import skillsExtension from "./adapters/pi/extension.mjs";
2
- import { skillInvocation } from "./services/skillText.mjs";
3
- import { SkillsOverlayResult, openSkillsOverlay } from "./tui/skillsOverlay.mjs";
4
- import { LEADER_SOURCE, SKILLS_COMMAND, SKILLS_LEADER_CONTRIBUTION, SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX } from "./types/skills.mjs";
5
- export { LEADER_SOURCE, SKILLS_COMMAND, SKILLS_LEADER_CONTRIBUTION, SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX, type SkillsOverlayResult, openSkillsOverlay, skillInvocation, skillsExtension };
package/dist/index.mjs DELETED
@@ -1 +0,0 @@
1
- import{LEADER_SOURCE as e,SKILLS_COMMAND as t,SKILLS_LEADER_CONTRIBUTION as n,SKILL_COMMAND_PREFIX as r,SKILL_INVOCATION_PREFIX as i}from"./types/skills.mjs";import{skillInvocation as a}from"./services/skillText.mjs";import o from"./adapters/pi/extension.mjs";import{openSkillsOverlay as s}from"./tui/skillsOverlay.mjs";export{e as LEADER_SOURCE,t as SKILLS_COMMAND,n as SKILLS_LEADER_CONTRIBUTION,r as SKILL_COMMAND_PREFIX,i as SKILL_INVOCATION_PREFIX,s as openSkillsOverlay,a as skillInvocation,o as skillsExtension};
@@ -1,2 +0,0 @@
1
- const e=require("../types/skills.cjs");function t(t){return`${e.SKILL_INVOCATION_PREFIX}${t} `}exports.skillInvocation=t;
2
- //# sourceMappingURL=skillText.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skillText.cjs","names":["SKILL_INVOCATION_PREFIX"],"sources":["../../src/services/skillText.ts"],"sourcesContent":["import { SKILL_INVOCATION_PREFIX } from '../types/skills.ts';\n\n/**\n * Prefills the draft rather than submitting it.\n *\n * `/skill:<name>` is expanded by Pi inside `prompt()`, which only the editor's\n * own submission path reaches: `sendUserMessage` sets `expandPromptTemplates`\n * false and would deliver the literal text. Leaving the command in the editor\n * keeps the expansion and costs one keystroke.\n */\nexport function skillInvocation(name: string): string {\n return `${SKILL_INVOCATION_PREFIX}${name} `;\n}\n"],"mappings":"uCAUA,SAAgB,EAAgB,EAAsB,CACpD,MAAO,GAAGA,EAAAA,0BAA0B,EAAK,EAC3C"}
@@ -1,13 +0,0 @@
1
- //#region src/services/skillText.d.ts
2
- /**
3
- * Prefills the draft rather than submitting it.
4
- *
5
- * `/skill:<name>` is expanded by Pi inside `prompt()`, which only the editor's
6
- * own submission path reaches: `sendUserMessage` sets `expandPromptTemplates`
7
- * false and would deliver the literal text. Leaving the command in the editor
8
- * keeps the expansion and costs one keystroke.
9
- */
10
- declare function skillInvocation(name: string): string;
11
- //#endregion
12
- export { skillInvocation };
13
- //# sourceMappingURL=skillText.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skillText.d.cts","names":[],"sources":["../../src/services/skillText.ts"],"mappings":";;;;;;;;;iBAUgB,gBAAgB"}
@@ -1,13 +0,0 @@
1
- //#region src/services/skillText.d.ts
2
- /**
3
- * Prefills the draft rather than submitting it.
4
- *
5
- * `/skill:<name>` is expanded by Pi inside `prompt()`, which only the editor's
6
- * own submission path reaches: `sendUserMessage` sets `expandPromptTemplates`
7
- * false and would deliver the literal text. Leaving the command in the editor
8
- * keeps the expansion and costs one keystroke.
9
- */
10
- declare function skillInvocation(name: string): string;
11
- //#endregion
12
- export { skillInvocation };
13
- //# sourceMappingURL=skillText.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skillText.d.mts","names":[],"sources":["../../src/services/skillText.ts"],"mappings":";;;;;;;;;iBAUgB,gBAAgB"}
@@ -1,2 +0,0 @@
1
- import{SKILL_INVOCATION_PREFIX as e}from"../types/skills.mjs";function t(t){return`${e}${t} `}export{t as skillInvocation};
2
- //# sourceMappingURL=skillText.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skillText.mjs","names":[],"sources":["../../src/services/skillText.ts"],"sourcesContent":["import { SKILL_INVOCATION_PREFIX } from '../types/skills.ts';\n\n/**\n * Prefills the draft rather than submitting it.\n *\n * `/skill:<name>` is expanded by Pi inside `prompt()`, which only the editor's\n * own submission path reaches: `sendUserMessage` sets `expandPromptTemplates`\n * false and would deliver the literal text. Leaving the command in the editor\n * keeps the expansion and costs one keystroke.\n */\nexport function skillInvocation(name: string): string {\n return `${SKILL_INVOCATION_PREFIX}${name} `;\n}\n"],"mappings":"8DAUA,SAAgB,EAAgB,EAAsB,CACpD,MAAO,GAAG,IAA0B,EAAK,EAC3C"}
@@ -1,4 +0,0 @@
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-ui/components/doomOverlay"),i=require("@agimon-ai/doompi-ui/rendering"),a=require("@earendil-works/pi-tui");const o=`CATALOG`,s=[[`↑↓`,`select`],[`enter`,`use skill`],[`tab`,`detail pane`],[`/`,`filter`],[`esc`,`close`]],c=[[`pgup/pgdn`,`scroll`],[`tab`,`tree`],[`esc`,`close`]],l=[[`type`,`to filter`],[`enter`,`keep`],[`esc`,`clear`]];function u(e,t){return(0,i.fitStyledLine)(e,t,`…`)}function d(e,t){if(t<=0)return[];let n=[],r=``;for(let i of e.split(/\s+/).filter(Boolean))r.length===0?r=i:(0,a.visibleWidth)(r)+1+(0,a.visibleWidth)(i)<=t?r+=` ${i}`:(n.push(r),r=i);return r.length>0&&n.push(r),n}function f(e){let t=e.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?([\s\S]*)$/);return(t?t[1]??``:e).replace(/^\s+/,``)}function p(e,t){let n=t.toLowerCase();return e.name.toLowerCase().includes(n)||e.description.toLowerCase().includes(n)}var m=class extends r.DoomOverlay{options;done;readFile;bodyCache=new Map;selected=0;focus=`tree`;filtering=!1;query=``;bodyOffset=0;constructor(e,n,r,i){super(e,n),this.options=r,this.done=i,this.readFile=r.readFile??(e=>t.default.readFileSync(e,`utf8`))}rows(){let e=this.query.trim(),t=[];for(let n of this.options.catalog.groups){let r=e?n.owners.map(t=>({...t,skills:t.skills.filter(t=>p(t,e))})).filter(e=>e.skills.length>0):n.owners;if(r.length===0)continue;let i=r.reduce((e,t)=>e+t.skills.length,0);t.push({kind:`group`,depth:0,label:n.label,tag:String(i)});for(let e of r){t.push({kind:`owner`,depth:1,label:e.owner,tag:String(e.skills.length)});for(let n of e.skills)t.push({kind:`skill`,depth:2,label:n.name,tag:``,skill:n})}}return t}selectable(e){let t=[];for(let[n,r]of e.entries())r.skill&&t.push(n);return t}currentRow(){let e=this.rows(),t=this.selectable(e);if(t.length!==0)return e[t[Math.min(this.selected,t.length-1)]]}move(e){let t=this.selectable(this.rows()).length;t!==0&&(this.selected=Math.max(0,Math.min(t-1,this.selected+e)),this.bodyOffset=0,this.invalidate())}bodyLines(e,t){let n=this.bodyCache.get(e.filePath);if(!n){let t;try{t=f(this.readFile(e.filePath))}catch{t=`Could not read SKILL.md.`}n=t.split(/\r?\n/),this.bodyCache.set(e.filePath,n)}return n.flatMap(e=>e.trim()===``?[``]:d(e,t))}handleInput(e){if(this.filtering){this.handleFilterInput(e);return}if((0,a.matchesKey)(e,`escape`)){if(this.query){this.query=``,this.selected=0,this.invalidate();return}this.done(void 0);return}if(e===` `||(0,a.matchesKey)(e,`tab`)){this.focus=this.focus===`tree`?`body`:`tree`,this.invalidate();return}if((0,a.matchesKey)(e,`pageUp`)){this.bodyOffset=Math.max(0,this.bodyOffset-10),this.invalidate();return}if((0,a.matchesKey)(e,`pageDown`)){this.bodyOffset+=10,this.invalidate();return}if(this.focus!==`body`){if(e===`/`){this.filtering=!0,this.invalidate();return}if((0,a.matchesKey)(e,`up`)||(0,a.matchesKey)(e,`k`)){this.move(-1);return}if((0,a.matchesKey)(e,`down`)||(0,a.matchesKey)(e,`j`)){this.move(1);return}if(e===`\r`||e===`
2
- `||(0,a.matchesKey)(e,`enter`)){let e=this.currentRow()?.skill;e&&this.done({kind:`invoke`,skill:e})}}}handleFilterInput(e){if((0,a.matchesKey)(e,`escape`)){this.filtering=!1,this.query=``,this.selected=0,this.invalidate();return}if(e===`\r`||e===`
3
- `){this.filtering=!1,this.invalidate();return}e===``?this.query=this.query.slice(0,-1):e.length===1&&e>=` `&&(this.query+=e),this.selected=0,this.invalidate()}getChrome(){let{skillCount:e,promptTokens:t,bodyTokens:n}=this.options.catalog,a=this.options.catalog.groups.filter(e=>e.owners.length>0).length,o=this.filtering?l:this.focus===`body`?c:s;return{title:`SKILLS`,accent:r.DOOM_OVERLAY_ACCENT,breadcrumb:`SPC › e / extension › s / skills`,headerRight:`${e} skills · ${a} sources · ${(0,i.formatTokens)(t)} always-on · ${(0,i.formatTokens)(n)} full`,footer:this.hintLine(o)}}badge(e,t,n=`text`){return this.theme.bg(t,this.theme.fg(n,` ${e} `))}hintLine(e){return e.map(([e,t])=>`${this.badge(e,`selectedBg`)}${this.theme.fg(`dim`,` ${t}`)}`).join(this.theme.fg(`dim`,` `))}renderBody(e,t){if(e<60)return this.renderStacked(e,t);let n=Math.max(20,Math.floor((e-1)*.3333333333333333)),r=Math.max(1,e-n-1),i=Math.max(1,n-1),o=Math.max(1,r-1),s=this.treeLines(i,t),c=this.detailLines(o,t),l=this.theme.fg(`borderMuted`,`│`);return Array.from({length:t},(t,n)=>{let r=`${u(s[n]??``,i)} ${l} ${u(c[n]??``,o)}`;return(0,a.truncateToWidth)(r,e,`…`)})}renderStacked(e,t){let n=Math.max(1,Math.floor((t-1)/2)),r=Math.max(0,t-n-1),i=this.treeLines(e,n),a=this.detailLines(e,r);return[...i.slice(0,n),...Array.from({length:Math.max(0,n-i.length)},()=>``),this.theme.fg(`borderMuted`,`─`.repeat(e)),...a.slice(0,r)]}treeLines(e,t){let n=this.filtering||this.query?`${o} /${this.query}`:o,r=[this.theme.bold(this.theme.fg(`accent`,n)),``],i=this.rows();if(i.length===0)return r.push(this.theme.fg(`dim`,`No skills discovered.`)),r;let s=Math.max(1,t-r.length),c=this.selectable(i),l=c[Math.min(this.selected,c.length-1)]??-1,d=Math.max(0,Math.min(l-s+1,Math.max(0,i.length-s)));for(let[t,n]of i.slice(d,d+s).entries()){let i=d+t===l,o=i?this.theme.fg(`accent`,`›`):` `,s=` `.repeat(n.depth*2),c=n.kind===`group`?`accent`:n.kind===`owner`?`dim`:`text`,f=this.theme.fg(c,`${s}${n.label}`);(n.kind===`group`||i)&&(f=this.theme.bold(f));let p=n.tag?(0,a.truncateToWidth)(n.tag,Math.floor(e*.4),`…`):``,m=p?(0,a.visibleWidth)(p)+1:0,h=u(`${o} ${f}`,Math.max(1,e-m)),g=p?`${h} ${this.theme.fg(`dim`,p)}`:h;r.push(i?this.theme.bg(`selectedBg`,u(g,e)):g)}return r}field(e,t,n,r){return d(t,Math.max(1,r-13)).map((t,r)=>`${this.theme.fg(`dim`,u(r===0?e:``,13))}${this.theme.fg(n,t)}`)}detailLines(e,t){let r=this.currentRow()?.skill;if(!r)return[``,``,this.theme.fg(`dim`,`Select a skill to read it.`)];let i=n.default.relative(this.options.repoRoot,r.filePath)||r.filePath,a=[this.theme.bold(this.theme.fg(`accent`,r.name)),``,...this.field(`description`,r.description,`text`,e),...this.field(`source`,`${r.group} › ${r.owner}`,`success`,e),...this.field(`path`,i,`text`,e),...this.field(`invocable`,r.modelInvocable?`model · /skill:${r.name}`:`/skill:${r.name} only`,`success`,e)],o=this.bodyLines(r,e),s=Math.max(0,t-a.length-2);this.bodyOffset=Math.min(this.bodyOffset,Math.max(0,o.length-s));let c=o.slice(this.bodyOffset,this.bodyOffset+s),l=o.length>s?`lines ${this.bodyOffset+1}-${this.bodyOffset+c.length} / ${o.length}`:`SKILL.md`,u=this.focus===`body`?this.theme.fg(`accent`,` ◀ focused`):``;a.push(``,`${this.theme.fg(`dim`,l)}${u}`);for(let e of c)a.push(this.theme.fg(`dim`,e));return a}};async function h(e,t){return e.ui.custom((e,n,r,i)=>new m(e,n,t,i),r.DOOM_FULLSCREEN_UI_OPTIONS)}exports.SkillsOverlayComponent=m,exports.openSkillsOverlay=h,exports.skillBody=f;
4
- //# sourceMappingURL=skillsOverlay.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skillsOverlay.cjs","names":["fitStyledLine","visibleWidth","DoomOverlay","fs","matchesKey","DOOM_OVERLAY_ACCENT","formatTokens","truncateToWidth","path","DOOM_FULLSCREEN_UI_OPTIONS"],"sources":["../../src/tui/skillsOverlay.ts"],"sourcesContent":["/**\n * Skills: the `SPC e s` overlay.\n *\n * A tree of the skills this session loaded on the left, the selected skill's\n * frontmatter and body on the right. Everything listed is loaded, so the\n * surface answers \"what do I have, and what does it cost\" rather than doubling\n * as a domain browser.\n *\n * The tree is flat and always open: groups and owners are headings, only skills\n * take the selection. Rendering is pure: the catalogue is resolved once by the\n * caller and passed in, so `render(width)` returns lines with no side effects\n * and the whole surface can be asserted as text without a live terminal.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/components/doomOverlay';\nimport { fitStyledLine, formatTokens } from '@agimon-ai/doompi-ui/rendering';\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\nimport type { SkillCatalog, SkillEntry } from '../adapters/skillCatalog.ts';\n\ntype ThemeColor = Parameters<Theme['fg']>[0];\ntype ThemeBg = Parameters<Theme['bg']>[0];\n\nconst MIN_WIDTH = 60;\n/** A third for the tree, two thirds for the skill being read. */\nconst TREE_PANE_RATIO = 1 / 3;\n/** One blank column each side of the pane divider so neither column touches it. */\nconst PANE_GUTTER = 1;\nconst MIN_PANE_WIDTH = 20;\nconst LABEL_COLUMN = 13;\n/** Ceiling on the columns a row's right-hand tag may take from its label. */\nconst TAG_SHARE = 0.4;\nconst INDENT = 2;\nconst ELLIPSIS = '…';\nconst SELECTION_MARKER = '›';\nconst RULE = '─';\nconst KEY_TAB = '\\t';\nconst KEY_ENTER = '\\r';\nconst KEY_NEWLINE = '\\n';\nconst KEY_BACKSPACE = '\\x7f';\nconst FILTER_KEY = '/';\nconst BODY_PAGE = 10;\n\nconst TITLE = 'SKILLS';\nconst BREADCRUMB = 'SPC › e / extension › s / skills';\nconst HEADING = 'CATALOG';\nconst EMPTY_TREE = 'No skills discovered.';\nconst NO_SELECTION = 'Select a skill to read it.';\nconst UNREADABLE = 'Could not read SKILL.md.';\n\nconst TREE_HINTS: readonly (readonly [string, string])[] = [\n ['↑↓', 'select'],\n ['enter', 'use skill'],\n ['tab', 'detail pane'],\n ['/', 'filter'],\n ['esc', 'close'],\n];\nconst BODY_HINTS: readonly (readonly [string, string])[] = [\n ['pgup/pgdn', 'scroll'],\n ['tab', 'tree'],\n ['esc', 'close'],\n];\nconst FILTER_HINTS: readonly (readonly [string, string])[] = [\n ['type', 'to filter'],\n ['enter', 'keep'],\n ['esc', 'clear'],\n];\n\nexport type SkillsOverlayResult = { kind: 'invoke'; skill: SkillEntry } | undefined;\n\nexport interface SkillsOverlayOptions {\n catalog: SkillCatalog;\n /** Repo root, so paths render relative rather than as absolute noise. */\n repoRoot: string;\n readFile?: (filePath: string) => string;\n}\n\ntype RowKind = 'group' | 'owner' | 'skill';\n\ninterface TreeRow {\n kind: RowKind;\n depth: number;\n label: string;\n tag: string;\n /** Only a skill row carries one; group and owner rows are headings. */\n skill?: SkillEntry;\n}\n\nfunction fitRow(text: string, width: number): string {\n return fitStyledLine(text, width, ELLIPSIS);\n}\n\nfunction wrap(text: string, width: number): string[] {\n if (width <= 0) return [];\n const lines: string[] = [];\n let current = '';\n for (const word of text.split(/\\s+/).filter(Boolean)) {\n if (current.length === 0) current = word;\n else if (visibleWidth(current) + 1 + visibleWidth(word) <= width) current += ` ${word}`;\n else {\n lines.push(current);\n current = word;\n }\n }\n if (current.length > 0) lines.push(current);\n return lines;\n}\n\n/** Drops YAML frontmatter so the preview starts at the prose, as Pi does. */\nexport function skillBody(content: string): string {\n const match = content.match(/^---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n return (match ? (match[1] ?? '') : content).replace(/^\\s+/, '');\n}\n\nfunction matches(skill: SkillEntry, query: string): boolean {\n const needle = query.toLowerCase();\n return skill.name.toLowerCase().includes(needle) || skill.description.toLowerCase().includes(needle);\n}\n\nexport class SkillsOverlayComponent extends DoomOverlay {\n private readonly options: SkillsOverlayOptions;\n private readonly done: (result: SkillsOverlayResult) => void;\n private readonly readFile: (filePath: string) => string;\n private readonly bodyCache = new Map<string, string[]>();\n\n /** Index into the skill rows, not into every row: headings are not selectable. */\n private selected = 0;\n private focus: 'tree' | 'body' = 'tree';\n private filtering = false;\n private query = '';\n private bodyOffset = 0;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n options: SkillsOverlayOptions,\n done: (result: SkillsOverlayResult) => void,\n ) {\n super(tui, theme);\n this.options = options;\n this.done = done;\n this.readFile = options.readFile ?? ((filePath) => fs.readFileSync(filePath, 'utf8'));\n }\n\n /** Every row, honouring the active filter. The tree does not fold. */\n private rows(): TreeRow[] {\n const query = this.query.trim();\n const rows: TreeRow[] = [];\n for (const group of this.options.catalog.groups) {\n const owners = query\n ? group.owners\n .map((owner) => ({ ...owner, skills: owner.skills.filter((skill) => matches(skill, query)) }))\n .filter((owner) => owner.skills.length > 0)\n : group.owners;\n if (owners.length === 0) continue;\n const count = owners.reduce((total, owner) => total + owner.skills.length, 0);\n rows.push({ kind: 'group', depth: 0, label: group.label, tag: String(count) });\n for (const owner of owners) {\n rows.push({ kind: 'owner', depth: 1, label: owner.owner, tag: String(owner.skills.length) });\n for (const skill of owner.skills) {\n rows.push({ kind: 'skill', depth: 2, label: skill.name, tag: '', skill });\n }\n }\n }\n return rows;\n }\n\n /**\n * Where the skill rows sit in `rows()`.\n *\n * The selection indexes this list rather than `rows()`, which is what keeps\n * headings unselectable without the key handler having to hunt past them, and\n * what puts the opening selection on a skill rather than on the first group.\n */\n private selectable(rows: TreeRow[]): number[] {\n const indexes: number[] = [];\n for (const [index, row] of rows.entries()) if (row.skill) indexes.push(index);\n return indexes;\n }\n\n private currentRow(): TreeRow | undefined {\n const rows = this.rows();\n const indexes = this.selectable(rows);\n if (indexes.length === 0) return undefined;\n return rows[indexes[Math.min(this.selected, indexes.length - 1)] as number];\n }\n\n private move(delta: number): void {\n const count = this.selectable(this.rows()).length;\n if (count === 0) return;\n this.selected = Math.max(0, Math.min(count - 1, this.selected + delta));\n this.bodyOffset = 0;\n this.invalidate();\n }\n\n private bodyLines(skill: SkillEntry, width: number): string[] {\n let raw = this.bodyCache.get(skill.filePath);\n if (!raw) {\n let source: string;\n try {\n source = skillBody(this.readFile(skill.filePath));\n } catch {\n source = UNREADABLE;\n }\n raw = source.split(/\\r?\\n/);\n this.bodyCache.set(skill.filePath, raw);\n }\n // Wrapped at render width rather than at read time, so a resize reflows the\n // same cached file instead of re-reading it.\n return raw.flatMap((line) => (line.trim() === '' ? [''] : wrap(line, width)));\n }\n\n handleInput(data: string): void {\n if (this.filtering) {\n this.handleFilterInput(data);\n return;\n }\n\n if (matchesKey(data, 'escape')) {\n // The filter clears before the overlay closes: escaping straight out of a\n // narrowed tree loses the search with no way to see what it hid.\n if (this.query) {\n this.query = '';\n this.selected = 0;\n this.invalidate();\n return;\n }\n this.done(undefined);\n return;\n }\n if (data === KEY_TAB || matchesKey(data, 'tab')) {\n this.focus = this.focus === 'tree' ? 'body' : 'tree';\n this.invalidate();\n return;\n }\n if (matchesKey(data, 'pageUp')) {\n this.bodyOffset = Math.max(0, this.bodyOffset - BODY_PAGE);\n this.invalidate();\n return;\n }\n if (matchesKey(data, 'pageDown')) {\n this.bodyOffset += BODY_PAGE;\n this.invalidate();\n return;\n }\n if (this.focus === 'body') return;\n\n if (data === FILTER_KEY) {\n this.filtering = true;\n this.invalidate();\n return;\n }\n if (matchesKey(data, 'up') || matchesKey(data, 'k')) {\n this.move(-1);\n return;\n }\n if (matchesKey(data, 'down') || matchesKey(data, 'j')) {\n this.move(1);\n return;\n }\n\n if (data === KEY_ENTER || data === KEY_NEWLINE || matchesKey(data, 'enter')) {\n const skill = this.currentRow()?.skill;\n if (skill) this.done({ kind: 'invoke', skill });\n }\n }\n\n private handleFilterInput(data: string): void {\n if (matchesKey(data, 'escape')) {\n this.filtering = false;\n this.query = '';\n this.selected = 0;\n this.invalidate();\n return;\n }\n if (data === KEY_ENTER || data === KEY_NEWLINE) {\n this.filtering = false;\n this.invalidate();\n return;\n }\n if (data === KEY_BACKSPACE) this.query = this.query.slice(0, -1);\n // Control sequences are dropped rather than appended: an arrow key would\n // otherwise land in the query as a raw escape sequence.\n else if (data.length === 1 && data >= ' ') this.query += data;\n this.selected = 0;\n this.invalidate();\n }\n\n protected getChrome(): DoomOverlayChrome {\n const { skillCount, promptTokens, bodyTokens } = this.options.catalog;\n const sources = this.options.catalog.groups.filter((group) => group.owners.length > 0).length;\n const hints = this.filtering ? FILTER_HINTS : this.focus === 'body' ? BODY_HINTS : TREE_HINTS;\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: BREADCRUMB,\n // Two token figures, because they answer different questions: what the\n // catalogue costs on every request, and what reading all of it would cost.\n headerRight: `${skillCount} skills · ${sources} sources · ${formatTokens(promptTokens)} always-on · ${formatTokens(bodyTokens)} full`,\n footer: this.hintLine(hints),\n };\n }\n\n private badge(text: string, background: ThemeBg, colour: ThemeColor = 'text'): string {\n return this.theme.bg(background, this.theme.fg(colour, ` ${text} `));\n }\n\n /** Footer hints as capped keys: `[ enter ] use skill`, per the mockup. */\n private hintLine(hints: readonly (readonly [string, string])[]): string {\n return hints\n .map(([key, label]) => `${this.badge(key, 'selectedBg')}${this.theme.fg('dim', ` ${label}`)}`)\n .join(this.theme.fg('dim', ' '));\n }\n\n protected renderBody(width: number, height: number): string[] {\n if (width < MIN_WIDTH) return this.renderStacked(width, height);\n\n const treeWidth = Math.max(MIN_PANE_WIDTH, Math.floor((width - 1) * TREE_PANE_RATIO));\n const detailWidth = Math.max(1, width - treeWidth - 1);\n const treeContent = Math.max(1, treeWidth - PANE_GUTTER);\n const detailContent = Math.max(1, detailWidth - PANE_GUTTER);\n\n const left = this.treeLines(treeContent, height);\n const right = this.detailLines(detailContent, height);\n const divider = this.theme.fg('borderMuted', '│');\n return Array.from({ length: height }, (_, index) => {\n const row = `${fitRow(left[index] ?? '', treeContent)} ${divider} ${fitRow(right[index] ?? '', detailContent)}`;\n return truncateToWidth(row, width, ELLIPSIS);\n });\n }\n\n private renderStacked(width: number, height: number): string[] {\n const topHeight = Math.max(1, Math.floor((height - 1) / 2));\n const bottomHeight = Math.max(0, height - topHeight - 1);\n const top = this.treeLines(width, topHeight);\n const bottom = this.detailLines(width, bottomHeight);\n return [\n ...top.slice(0, topHeight),\n ...Array.from({ length: Math.max(0, topHeight - top.length) }, () => ''),\n this.theme.fg('borderMuted', RULE.repeat(width)),\n ...bottom.slice(0, bottomHeight),\n ];\n }\n\n private treeLines(width: number, height: number): string[] {\n const heading = this.filtering || this.query ? `${HEADING} ${FILTER_KEY}${this.query}` : HEADING;\n const lines = [this.theme.bold(this.theme.fg('accent', heading)), ''];\n const rows = this.rows();\n if (rows.length === 0) {\n lines.push(this.theme.fg('dim', EMPTY_TREE));\n return lines;\n }\n\n const budget = Math.max(1, height - lines.length);\n const indexes = this.selectable(rows);\n // No skill rows means the filter matched nothing, so nothing is highlighted.\n const selected = indexes[Math.min(this.selected, indexes.length - 1)] ?? -1;\n const start = Math.max(0, Math.min(selected - budget + 1, Math.max(0, rows.length - budget)));\n for (const [offset, row] of rows.slice(start, start + budget).entries()) {\n const isSelected = start + offset === selected;\n const marker = isSelected ? this.theme.fg('accent', SELECTION_MARKER) : ' ';\n const indent = ' '.repeat(row.depth * INDENT);\n // Three tiers rather than two: the group heading leads, the owner beneath\n // it recedes to a caption, and the skills it holds carry the reading\n // weight because they are the only rows that do anything.\n const colour: ThemeColor = row.kind === 'group' ? 'accent' : row.kind === 'owner' ? 'dim' : 'text';\n let label = this.theme.fg(colour, `${indent}${row.label}`);\n if (row.kind === 'group' || isSelected) label = this.theme.bold(label);\n // The tag is capped before it claims columns: an owner in many domains\n // carries a long `+a,b,c` hint that would otherwise leave the name with\n // no room at all and render the row as a bare count.\n const tag = row.tag ? truncateToWidth(row.tag, Math.floor(width * TAG_SHARE), ELLIPSIS) : '';\n const tagWidth = tag ? visibleWidth(tag) + 1 : 0;\n const body = fitRow(`${marker} ${label}`, Math.max(1, width - tagWidth));\n const line = tag ? `${body} ${this.theme.fg('dim', tag)}` : body;\n lines.push(isSelected ? this.theme.bg('selectedBg', fitRow(line, width)) : line);\n }\n return lines;\n }\n\n private field(label: string, value: string, colour: ThemeColor, width: number): string[] {\n const valueWidth = Math.max(1, width - LABEL_COLUMN);\n return wrap(value, valueWidth).map(\n (line, index) =>\n `${this.theme.fg('dim', fitRow(index === 0 ? label : '', LABEL_COLUMN))}${this.theme.fg(colour, line)}`,\n );\n }\n\n private detailLines(width: number, height: number): string[] {\n const skill = this.currentRow()?.skill;\n if (!skill) return ['', '', this.theme.fg('dim', NO_SELECTION)];\n\n const relative = path.relative(this.options.repoRoot, skill.filePath) || skill.filePath;\n const lines = [\n this.theme.bold(this.theme.fg('accent', skill.name)),\n '',\n ...this.field('description', skill.description, 'text', width),\n ...this.field('source', `${skill.group} › ${skill.owner}`, 'success', width),\n ...this.field('path', relative, 'text', width),\n ...this.field(\n 'invocable',\n skill.modelInvocable ? `model · /skill:${skill.name}` : `/skill:${skill.name} only`,\n 'success',\n width,\n ),\n ];\n\n const body = this.bodyLines(skill, width);\n const remaining = Math.max(0, height - lines.length - 2);\n // Clamped here rather than in the key handler, because how far the body can\n // scroll depends on the width it has just wrapped to.\n this.bodyOffset = Math.min(this.bodyOffset, Math.max(0, body.length - remaining));\n const shown = body.slice(this.bodyOffset, this.bodyOffset + remaining);\n const position =\n body.length > remaining\n ? `lines ${this.bodyOffset + 1}-${this.bodyOffset + shown.length} / ${body.length}`\n : 'SKILL.md';\n const focus = this.focus === 'body' ? this.theme.fg('accent', ' ◀ focused') : '';\n\n lines.push('', `${this.theme.fg('dim', position)}${focus}`);\n for (const line of shown) lines.push(this.theme.fg('dim', line));\n return lines;\n }\n}\n\nexport async function openSkillsOverlay(\n ctx: ExtensionContext,\n options: SkillsOverlayOptions,\n): Promise<SkillsOverlayResult> {\n return ctx.ui.custom<SkillsOverlayResult>(\n (tui, theme, _keybindings, done) => new SkillsOverlayComponent(tui, theme, options, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}
@@ -1,18 +0,0 @@
1
- import { SkillCatalog, SkillEntry } from "../adapters/skillCatalog.cjs";
2
- import { ExtensionContext } from "@earendil-works/pi-coding-agent";
3
- import "@agimon-ai/doompi-ui/components/doomOverlay";
4
- //#region src/tui/skillsOverlay.d.ts
5
- type SkillsOverlayResult = {
6
- kind: 'invoke';
7
- skill: SkillEntry;
8
- } | undefined;
9
- interface SkillsOverlayOptions {
10
- catalog: SkillCatalog;
11
- /** Repo root, so paths render relative rather than as absolute noise. */
12
- repoRoot: string;
13
- readFile?: (filePath: string) => string;
14
- }
15
- declare function openSkillsOverlay(ctx: ExtensionContext, options: SkillsOverlayOptions): Promise<SkillsOverlayResult>;
16
- //#endregion
17
- export { SkillsOverlayOptions, SkillsOverlayResult, openSkillsOverlay };
18
- //# sourceMappingURL=skillsOverlay.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skillsOverlay.d.cts","names":[],"sources":["../../src/tui/skillsOverlay.ts"],"mappings":";;;;KA4EY;EAAwB;EAAgB,OAAO;;UAE1C;EACf,SAAS;;EAET;EACA,YAAY;;iBA+VQ,kBACpB,KAAK,kBACL,SAAS,uBACR,QAAQ"}
@@ -1,18 +0,0 @@
1
- import { SkillCatalog, SkillEntry } from "../adapters/skillCatalog.mjs";
2
- import { ExtensionContext } from "@earendil-works/pi-coding-agent";
3
- import { DoomOverlay } from "@agimon-ai/doompi-ui/components/doomOverlay";
4
- //#region src/tui/skillsOverlay.d.ts
5
- type SkillsOverlayResult = {
6
- kind: 'invoke';
7
- skill: SkillEntry;
8
- } | undefined;
9
- interface SkillsOverlayOptions {
10
- catalog: SkillCatalog;
11
- /** Repo root, so paths render relative rather than as absolute noise. */
12
- repoRoot: string;
13
- readFile?: (filePath: string) => string;
14
- }
15
- declare function openSkillsOverlay(ctx: ExtensionContext, options: SkillsOverlayOptions): Promise<SkillsOverlayResult>;
16
- //#endregion
17
- export { SkillsOverlayOptions, SkillsOverlayResult, openSkillsOverlay };
18
- //# sourceMappingURL=skillsOverlay.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skillsOverlay.d.mts","names":[],"sources":["../../src/tui/skillsOverlay.ts"],"mappings":";;;;KA4EY;EAAwB;EAAgB,OAAO;;UAE1C;EACf,SAAS;;EAET;EACA,YAAY;;iBA+VQ,kBACpB,KAAK,kBACL,SAAS,uBACR,QAAQ"}
@@ -1,4 +0,0 @@
1
- import e from"node:fs";import t from"node:path";import{DOOM_FULLSCREEN_UI_OPTIONS as n,DOOM_OVERLAY_ACCENT as r,DoomOverlay as i}from"@agimon-ai/doompi-ui/components/doomOverlay";import{fitStyledLine as a,formatTokens as o}from"@agimon-ai/doompi-ui/rendering";import{matchesKey as s,truncateToWidth as c,visibleWidth as l}from"@earendil-works/pi-tui";const u=`CATALOG`,d=[[`↑↓`,`select`],[`enter`,`use skill`],[`tab`,`detail pane`],[`/`,`filter`],[`esc`,`close`]],f=[[`pgup/pgdn`,`scroll`],[`tab`,`tree`],[`esc`,`close`]],p=[[`type`,`to filter`],[`enter`,`keep`],[`esc`,`clear`]];function m(e,t){return a(e,t,`…`)}function h(e,t){if(t<=0)return[];let n=[],r=``;for(let i of e.split(/\s+/).filter(Boolean))r.length===0?r=i:l(r)+1+l(i)<=t?r+=` ${i}`:(n.push(r),r=i);return r.length>0&&n.push(r),n}function g(e){let t=e.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?([\s\S]*)$/);return(t?t[1]??``:e).replace(/^\s+/,``)}function _(e,t){let n=t.toLowerCase();return e.name.toLowerCase().includes(n)||e.description.toLowerCase().includes(n)}var v=class extends i{options;done;readFile;bodyCache=new Map;selected=0;focus=`tree`;filtering=!1;query=``;bodyOffset=0;constructor(t,n,r,i){super(t,n),this.options=r,this.done=i,this.readFile=r.readFile??(t=>e.readFileSync(t,`utf8`))}rows(){let e=this.query.trim(),t=[];for(let n of this.options.catalog.groups){let r=e?n.owners.map(t=>({...t,skills:t.skills.filter(t=>_(t,e))})).filter(e=>e.skills.length>0):n.owners;if(r.length===0)continue;let i=r.reduce((e,t)=>e+t.skills.length,0);t.push({kind:`group`,depth:0,label:n.label,tag:String(i)});for(let e of r){t.push({kind:`owner`,depth:1,label:e.owner,tag:String(e.skills.length)});for(let n of e.skills)t.push({kind:`skill`,depth:2,label:n.name,tag:``,skill:n})}}return t}selectable(e){let t=[];for(let[n,r]of e.entries())r.skill&&t.push(n);return t}currentRow(){let e=this.rows(),t=this.selectable(e);if(t.length!==0)return e[t[Math.min(this.selected,t.length-1)]]}move(e){let t=this.selectable(this.rows()).length;t!==0&&(this.selected=Math.max(0,Math.min(t-1,this.selected+e)),this.bodyOffset=0,this.invalidate())}bodyLines(e,t){let n=this.bodyCache.get(e.filePath);if(!n){let t;try{t=g(this.readFile(e.filePath))}catch{t=`Could not read SKILL.md.`}n=t.split(/\r?\n/),this.bodyCache.set(e.filePath,n)}return n.flatMap(e=>e.trim()===``?[``]:h(e,t))}handleInput(e){if(this.filtering){this.handleFilterInput(e);return}if(s(e,`escape`)){if(this.query){this.query=``,this.selected=0,this.invalidate();return}this.done(void 0);return}if(e===` `||s(e,`tab`)){this.focus=this.focus===`tree`?`body`:`tree`,this.invalidate();return}if(s(e,`pageUp`)){this.bodyOffset=Math.max(0,this.bodyOffset-10),this.invalidate();return}if(s(e,`pageDown`)){this.bodyOffset+=10,this.invalidate();return}if(this.focus!==`body`){if(e===`/`){this.filtering=!0,this.invalidate();return}if(s(e,`up`)||s(e,`k`)){this.move(-1);return}if(s(e,`down`)||s(e,`j`)){this.move(1);return}if(e===`\r`||e===`
2
- `||s(e,`enter`)){let e=this.currentRow()?.skill;e&&this.done({kind:`invoke`,skill:e})}}}handleFilterInput(e){if(s(e,`escape`)){this.filtering=!1,this.query=``,this.selected=0,this.invalidate();return}if(e===`\r`||e===`
3
- `){this.filtering=!1,this.invalidate();return}e===``?this.query=this.query.slice(0,-1):e.length===1&&e>=` `&&(this.query+=e),this.selected=0,this.invalidate()}getChrome(){let{skillCount:e,promptTokens:t,bodyTokens:n}=this.options.catalog,i=this.options.catalog.groups.filter(e=>e.owners.length>0).length,a=this.filtering?p:this.focus===`body`?f:d;return{title:`SKILLS`,accent:r,breadcrumb:`SPC › e / extension › s / skills`,headerRight:`${e} skills · ${i} sources · ${o(t)} always-on · ${o(n)} full`,footer:this.hintLine(a)}}badge(e,t,n=`text`){return this.theme.bg(t,this.theme.fg(n,` ${e} `))}hintLine(e){return e.map(([e,t])=>`${this.badge(e,`selectedBg`)}${this.theme.fg(`dim`,` ${t}`)}`).join(this.theme.fg(`dim`,` `))}renderBody(e,t){if(e<60)return this.renderStacked(e,t);let n=Math.max(20,Math.floor((e-1)*.3333333333333333)),r=Math.max(1,e-n-1),i=Math.max(1,n-1),a=Math.max(1,r-1),o=this.treeLines(i,t),s=this.detailLines(a,t),l=this.theme.fg(`borderMuted`,`│`);return Array.from({length:t},(t,n)=>{let r=`${m(o[n]??``,i)} ${l} ${m(s[n]??``,a)}`;return c(r,e,`…`)})}renderStacked(e,t){let n=Math.max(1,Math.floor((t-1)/2)),r=Math.max(0,t-n-1),i=this.treeLines(e,n),a=this.detailLines(e,r);return[...i.slice(0,n),...Array.from({length:Math.max(0,n-i.length)},()=>``),this.theme.fg(`borderMuted`,`─`.repeat(e)),...a.slice(0,r)]}treeLines(e,t){let n=this.filtering||this.query?`${u} /${this.query}`:u,r=[this.theme.bold(this.theme.fg(`accent`,n)),``],i=this.rows();if(i.length===0)return r.push(this.theme.fg(`dim`,`No skills discovered.`)),r;let a=Math.max(1,t-r.length),o=this.selectable(i),s=o[Math.min(this.selected,o.length-1)]??-1,d=Math.max(0,Math.min(s-a+1,Math.max(0,i.length-a)));for(let[t,n]of i.slice(d,d+a).entries()){let i=d+t===s,a=i?this.theme.fg(`accent`,`›`):` `,o=` `.repeat(n.depth*2),u=n.kind===`group`?`accent`:n.kind===`owner`?`dim`:`text`,f=this.theme.fg(u,`${o}${n.label}`);(n.kind===`group`||i)&&(f=this.theme.bold(f));let p=n.tag?c(n.tag,Math.floor(e*.4),`…`):``,h=p?l(p)+1:0,g=m(`${a} ${f}`,Math.max(1,e-h)),_=p?`${g} ${this.theme.fg(`dim`,p)}`:g;r.push(i?this.theme.bg(`selectedBg`,m(_,e)):_)}return r}field(e,t,n,r){return h(t,Math.max(1,r-13)).map((t,r)=>`${this.theme.fg(`dim`,m(r===0?e:``,13))}${this.theme.fg(n,t)}`)}detailLines(e,n){let r=this.currentRow()?.skill;if(!r)return[``,``,this.theme.fg(`dim`,`Select a skill to read it.`)];let i=t.relative(this.options.repoRoot,r.filePath)||r.filePath,a=[this.theme.bold(this.theme.fg(`accent`,r.name)),``,...this.field(`description`,r.description,`text`,e),...this.field(`source`,`${r.group} › ${r.owner}`,`success`,e),...this.field(`path`,i,`text`,e),...this.field(`invocable`,r.modelInvocable?`model · /skill:${r.name}`:`/skill:${r.name} only`,`success`,e)],o=this.bodyLines(r,e),s=Math.max(0,n-a.length-2);this.bodyOffset=Math.min(this.bodyOffset,Math.max(0,o.length-s));let c=o.slice(this.bodyOffset,this.bodyOffset+s),l=o.length>s?`lines ${this.bodyOffset+1}-${this.bodyOffset+c.length} / ${o.length}`:`SKILL.md`,u=this.focus===`body`?this.theme.fg(`accent`,` ◀ focused`):``;a.push(``,`${this.theme.fg(`dim`,l)}${u}`);for(let e of c)a.push(this.theme.fg(`dim`,e));return a}};async function y(e,t){return e.ui.custom((e,n,r,i)=>new v(e,n,t,i),n)}export{v as SkillsOverlayComponent,y as openSkillsOverlay,g as skillBody};
4
- //# sourceMappingURL=skillsOverlay.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skillsOverlay.mjs","names":[],"sources":["../../src/tui/skillsOverlay.ts"],"sourcesContent":["/**\n * Skills: the `SPC e s` overlay.\n *\n * A tree of the skills this session loaded on the left, the selected skill's\n * frontmatter and body on the right. Everything listed is loaded, so the\n * surface answers \"what do I have, and what does it cost\" rather than doubling\n * as a domain browser.\n *\n * The tree is flat and always open: groups and owners are headings, only skills\n * take the selection. Rendering is pure: the catalogue is resolved once by the\n * caller and passed in, so `render(width)` returns lines with no side effects\n * and the whole surface can be asserted as text without a live terminal.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/components/doomOverlay';\nimport { fitStyledLine, formatTokens } from '@agimon-ai/doompi-ui/rendering';\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\nimport type { SkillCatalog, SkillEntry } from '../adapters/skillCatalog.ts';\n\ntype ThemeColor = Parameters<Theme['fg']>[0];\ntype ThemeBg = Parameters<Theme['bg']>[0];\n\nconst MIN_WIDTH = 60;\n/** A third for the tree, two thirds for the skill being read. */\nconst TREE_PANE_RATIO = 1 / 3;\n/** One blank column each side of the pane divider so neither column touches it. */\nconst PANE_GUTTER = 1;\nconst MIN_PANE_WIDTH = 20;\nconst LABEL_COLUMN = 13;\n/** Ceiling on the columns a row's right-hand tag may take from its label. */\nconst TAG_SHARE = 0.4;\nconst INDENT = 2;\nconst ELLIPSIS = '…';\nconst SELECTION_MARKER = '›';\nconst RULE = '─';\nconst KEY_TAB = '\\t';\nconst KEY_ENTER = '\\r';\nconst KEY_NEWLINE = '\\n';\nconst KEY_BACKSPACE = '\\x7f';\nconst FILTER_KEY = '/';\nconst BODY_PAGE = 10;\n\nconst TITLE = 'SKILLS';\nconst BREADCRUMB = 'SPC › e / extension › s / skills';\nconst HEADING = 'CATALOG';\nconst EMPTY_TREE = 'No skills discovered.';\nconst NO_SELECTION = 'Select a skill to read it.';\nconst UNREADABLE = 'Could not read SKILL.md.';\n\nconst TREE_HINTS: readonly (readonly [string, string])[] = [\n ['↑↓', 'select'],\n ['enter', 'use skill'],\n ['tab', 'detail pane'],\n ['/', 'filter'],\n ['esc', 'close'],\n];\nconst BODY_HINTS: readonly (readonly [string, string])[] = [\n ['pgup/pgdn', 'scroll'],\n ['tab', 'tree'],\n ['esc', 'close'],\n];\nconst FILTER_HINTS: readonly (readonly [string, string])[] = [\n ['type', 'to filter'],\n ['enter', 'keep'],\n ['esc', 'clear'],\n];\n\nexport type SkillsOverlayResult = { kind: 'invoke'; skill: SkillEntry } | undefined;\n\nexport interface SkillsOverlayOptions {\n catalog: SkillCatalog;\n /** Repo root, so paths render relative rather than as absolute noise. */\n repoRoot: string;\n readFile?: (filePath: string) => string;\n}\n\ntype RowKind = 'group' | 'owner' | 'skill';\n\ninterface TreeRow {\n kind: RowKind;\n depth: number;\n label: string;\n tag: string;\n /** Only a skill row carries one; group and owner rows are headings. */\n skill?: SkillEntry;\n}\n\nfunction fitRow(text: string, width: number): string {\n return fitStyledLine(text, width, ELLIPSIS);\n}\n\nfunction wrap(text: string, width: number): string[] {\n if (width <= 0) return [];\n const lines: string[] = [];\n let current = '';\n for (const word of text.split(/\\s+/).filter(Boolean)) {\n if (current.length === 0) current = word;\n else if (visibleWidth(current) + 1 + visibleWidth(word) <= width) current += ` ${word}`;\n else {\n lines.push(current);\n current = word;\n }\n }\n if (current.length > 0) lines.push(current);\n return lines;\n}\n\n/** Drops YAML frontmatter so the preview starts at the prose, as Pi does. */\nexport function skillBody(content: string): string {\n const match = content.match(/^---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n return (match ? (match[1] ?? '') : content).replace(/^\\s+/, '');\n}\n\nfunction matches(skill: SkillEntry, query: string): boolean {\n const needle = query.toLowerCase();\n return skill.name.toLowerCase().includes(needle) || skill.description.toLowerCase().includes(needle);\n}\n\nexport class SkillsOverlayComponent extends DoomOverlay {\n private readonly options: SkillsOverlayOptions;\n private readonly done: (result: SkillsOverlayResult) => void;\n private readonly readFile: (filePath: string) => string;\n private readonly bodyCache = new Map<string, string[]>();\n\n /** Index into the skill rows, not into every row: headings are not selectable. */\n private selected = 0;\n private focus: 'tree' | 'body' = 'tree';\n private filtering = false;\n private query = '';\n private bodyOffset = 0;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n options: SkillsOverlayOptions,\n done: (result: SkillsOverlayResult) => void,\n ) {\n super(tui, theme);\n this.options = options;\n this.done = done;\n this.readFile = options.readFile ?? ((filePath) => fs.readFileSync(filePath, 'utf8'));\n }\n\n /** Every row, honouring the active filter. The tree does not fold. */\n private rows(): TreeRow[] {\n const query = this.query.trim();\n const rows: TreeRow[] = [];\n for (const group of this.options.catalog.groups) {\n const owners = query\n ? group.owners\n .map((owner) => ({ ...owner, skills: owner.skills.filter((skill) => matches(skill, query)) }))\n .filter((owner) => owner.skills.length > 0)\n : group.owners;\n if (owners.length === 0) continue;\n const count = owners.reduce((total, owner) => total + owner.skills.length, 0);\n rows.push({ kind: 'group', depth: 0, label: group.label, tag: String(count) });\n for (const owner of owners) {\n rows.push({ kind: 'owner', depth: 1, label: owner.owner, tag: String(owner.skills.length) });\n for (const skill of owner.skills) {\n rows.push({ kind: 'skill', depth: 2, label: skill.name, tag: '', skill });\n }\n }\n }\n return rows;\n }\n\n /**\n * Where the skill rows sit in `rows()`.\n *\n * The selection indexes this list rather than `rows()`, which is what keeps\n * headings unselectable without the key handler having to hunt past them, and\n * what puts the opening selection on a skill rather than on the first group.\n */\n private selectable(rows: TreeRow[]): number[] {\n const indexes: number[] = [];\n for (const [index, row] of rows.entries()) if (row.skill) indexes.push(index);\n return indexes;\n }\n\n private currentRow(): TreeRow | undefined {\n const rows = this.rows();\n const indexes = this.selectable(rows);\n if (indexes.length === 0) return undefined;\n return rows[indexes[Math.min(this.selected, indexes.length - 1)] as number];\n }\n\n private move(delta: number): void {\n const count = this.selectable(this.rows()).length;\n if (count === 0) return;\n this.selected = Math.max(0, Math.min(count - 1, this.selected + delta));\n this.bodyOffset = 0;\n this.invalidate();\n }\n\n private bodyLines(skill: SkillEntry, width: number): string[] {\n let raw = this.bodyCache.get(skill.filePath);\n if (!raw) {\n let source: string;\n try {\n source = skillBody(this.readFile(skill.filePath));\n } catch {\n source = UNREADABLE;\n }\n raw = source.split(/\\r?\\n/);\n this.bodyCache.set(skill.filePath, raw);\n }\n // Wrapped at render width rather than at read time, so a resize reflows the\n // same cached file instead of re-reading it.\n return raw.flatMap((line) => (line.trim() === '' ? [''] : wrap(line, width)));\n }\n\n handleInput(data: string): void {\n if (this.filtering) {\n this.handleFilterInput(data);\n return;\n }\n\n if (matchesKey(data, 'escape')) {\n // The filter clears before the overlay closes: escaping straight out of a\n // narrowed tree loses the search with no way to see what it hid.\n if (this.query) {\n this.query = '';\n this.selected = 0;\n this.invalidate();\n return;\n }\n this.done(undefined);\n return;\n }\n if (data === KEY_TAB || matchesKey(data, 'tab')) {\n this.focus = this.focus === 'tree' ? 'body' : 'tree';\n this.invalidate();\n return;\n }\n if (matchesKey(data, 'pageUp')) {\n this.bodyOffset = Math.max(0, this.bodyOffset - BODY_PAGE);\n this.invalidate();\n return;\n }\n if (matchesKey(data, 'pageDown')) {\n this.bodyOffset += BODY_PAGE;\n this.invalidate();\n return;\n }\n if (this.focus === 'body') return;\n\n if (data === FILTER_KEY) {\n this.filtering = true;\n this.invalidate();\n return;\n }\n if (matchesKey(data, 'up') || matchesKey(data, 'k')) {\n this.move(-1);\n return;\n }\n if (matchesKey(data, 'down') || matchesKey(data, 'j')) {\n this.move(1);\n return;\n }\n\n if (data === KEY_ENTER || data === KEY_NEWLINE || matchesKey(data, 'enter')) {\n const skill = this.currentRow()?.skill;\n if (skill) this.done({ kind: 'invoke', skill });\n }\n }\n\n private handleFilterInput(data: string): void {\n if (matchesKey(data, 'escape')) {\n this.filtering = false;\n this.query = '';\n this.selected = 0;\n this.invalidate();\n return;\n }\n if (data === KEY_ENTER || data === KEY_NEWLINE) {\n this.filtering = false;\n this.invalidate();\n return;\n }\n if (data === KEY_BACKSPACE) this.query = this.query.slice(0, -1);\n // Control sequences are dropped rather than appended: an arrow key would\n // otherwise land in the query as a raw escape sequence.\n else if (data.length === 1 && data >= ' ') this.query += data;\n this.selected = 0;\n this.invalidate();\n }\n\n protected getChrome(): DoomOverlayChrome {\n const { skillCount, promptTokens, bodyTokens } = this.options.catalog;\n const sources = this.options.catalog.groups.filter((group) => group.owners.length > 0).length;\n const hints = this.filtering ? FILTER_HINTS : this.focus === 'body' ? BODY_HINTS : TREE_HINTS;\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: BREADCRUMB,\n // Two token figures, because they answer different questions: what the\n // catalogue costs on every request, and what reading all of it would cost.\n headerRight: `${skillCount} skills · ${sources} sources · ${formatTokens(promptTokens)} always-on · ${formatTokens(bodyTokens)} full`,\n footer: this.hintLine(hints),\n };\n }\n\n private badge(text: string, background: ThemeBg, colour: ThemeColor = 'text'): string {\n return this.theme.bg(background, this.theme.fg(colour, ` ${text} `));\n }\n\n /** Footer hints as capped keys: `[ enter ] use skill`, per the mockup. */\n private hintLine(hints: readonly (readonly [string, string])[]): string {\n return hints\n .map(([key, label]) => `${this.badge(key, 'selectedBg')}${this.theme.fg('dim', ` ${label}`)}`)\n .join(this.theme.fg('dim', ' '));\n }\n\n protected renderBody(width: number, height: number): string[] {\n if (width < MIN_WIDTH) return this.renderStacked(width, height);\n\n const treeWidth = Math.max(MIN_PANE_WIDTH, Math.floor((width - 1) * TREE_PANE_RATIO));\n const detailWidth = Math.max(1, width - treeWidth - 1);\n const treeContent = Math.max(1, treeWidth - PANE_GUTTER);\n const detailContent = Math.max(1, detailWidth - PANE_GUTTER);\n\n const left = this.treeLines(treeContent, height);\n const right = this.detailLines(detailContent, height);\n const divider = this.theme.fg('borderMuted', '│');\n return Array.from({ length: height }, (_, index) => {\n const row = `${fitRow(left[index] ?? '', treeContent)} ${divider} ${fitRow(right[index] ?? '', detailContent)}`;\n return truncateToWidth(row, width, ELLIPSIS);\n });\n }\n\n private renderStacked(width: number, height: number): string[] {\n const topHeight = Math.max(1, Math.floor((height - 1) / 2));\n const bottomHeight = Math.max(0, height - topHeight - 1);\n const top = this.treeLines(width, topHeight);\n const bottom = this.detailLines(width, bottomHeight);\n return [\n ...top.slice(0, topHeight),\n ...Array.from({ length: Math.max(0, topHeight - top.length) }, () => ''),\n this.theme.fg('borderMuted', RULE.repeat(width)),\n ...bottom.slice(0, bottomHeight),\n ];\n }\n\n private treeLines(width: number, height: number): string[] {\n const heading = this.filtering || this.query ? `${HEADING} ${FILTER_KEY}${this.query}` : HEADING;\n const lines = [this.theme.bold(this.theme.fg('accent', heading)), ''];\n const rows = this.rows();\n if (rows.length === 0) {\n lines.push(this.theme.fg('dim', EMPTY_TREE));\n return lines;\n }\n\n const budget = Math.max(1, height - lines.length);\n const indexes = this.selectable(rows);\n // No skill rows means the filter matched nothing, so nothing is highlighted.\n const selected = indexes[Math.min(this.selected, indexes.length - 1)] ?? -1;\n const start = Math.max(0, Math.min(selected - budget + 1, Math.max(0, rows.length - budget)));\n for (const [offset, row] of rows.slice(start, start + budget).entries()) {\n const isSelected = start + offset === selected;\n const marker = isSelected ? this.theme.fg('accent', SELECTION_MARKER) : ' ';\n const indent = ' '.repeat(row.depth * INDENT);\n // Three tiers rather than two: the group heading leads, the owner beneath\n // it recedes to a caption, and the skills it holds carry the reading\n // weight because they are the only rows that do anything.\n const colour: ThemeColor = row.kind === 'group' ? 'accent' : row.kind === 'owner' ? 'dim' : 'text';\n let label = this.theme.fg(colour, `${indent}${row.label}`);\n if (row.kind === 'group' || isSelected) label = this.theme.bold(label);\n // The tag is capped before it claims columns: an owner in many domains\n // carries a long `+a,b,c` hint that would otherwise leave the name with\n // no room at all and render the row as a bare count.\n const tag = row.tag ? truncateToWidth(row.tag, Math.floor(width * TAG_SHARE), ELLIPSIS) : '';\n const tagWidth = tag ? visibleWidth(tag) + 1 : 0;\n const body = fitRow(`${marker} ${label}`, Math.max(1, width - tagWidth));\n const line = tag ? `${body} ${this.theme.fg('dim', tag)}` : body;\n lines.push(isSelected ? this.theme.bg('selectedBg', fitRow(line, width)) : line);\n }\n return lines;\n }\n\n private field(label: string, value: string, colour: ThemeColor, width: number): string[] {\n const valueWidth = Math.max(1, width - LABEL_COLUMN);\n return wrap(value, valueWidth).map(\n (line, index) =>\n `${this.theme.fg('dim', fitRow(index === 0 ? label : '', LABEL_COLUMN))}${this.theme.fg(colour, line)}`,\n );\n }\n\n private detailLines(width: number, height: number): string[] {\n const skill = this.currentRow()?.skill;\n if (!skill) return ['', '', this.theme.fg('dim', NO_SELECTION)];\n\n const relative = path.relative(this.options.repoRoot, skill.filePath) || skill.filePath;\n const lines = [\n this.theme.bold(this.theme.fg('accent', skill.name)),\n '',\n ...this.field('description', skill.description, 'text', width),\n ...this.field('source', `${skill.group} › ${skill.owner}`, 'success', width),\n ...this.field('path', relative, 'text', width),\n ...this.field(\n 'invocable',\n skill.modelInvocable ? `model · /skill:${skill.name}` : `/skill:${skill.name} only`,\n 'success',\n width,\n ),\n ];\n\n const body = this.bodyLines(skill, width);\n const remaining = Math.max(0, height - lines.length - 2);\n // Clamped here rather than in the key handler, because how far the body can\n // scroll depends on the width it has just wrapped to.\n this.bodyOffset = Math.min(this.bodyOffset, Math.max(0, body.length - remaining));\n const shown = body.slice(this.bodyOffset, this.bodyOffset + remaining);\n const position =\n body.length > remaining\n ? `lines ${this.bodyOffset + 1}-${this.bodyOffset + shown.length} / ${body.length}`\n : 'SKILL.md';\n const focus = this.focus === 'body' ? this.theme.fg('accent', ' ◀ focused') : '';\n\n lines.push('', `${this.theme.fg('dim', position)}${focus}`);\n for (const line of shown) lines.push(this.theme.fg('dim', line));\n return lines;\n }\n}\n\nexport async function openSkillsOverlay(\n ctx: ExtensionContext,\n options: SkillsOverlayOptions,\n): Promise<SkillsOverlayResult> {\n return ctx.ui.custom<SkillsOverlayResult>(\n (tui, theme, _keybindings, done) => new SkillsOverlayComponent(tui, theme, options, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- const e=`skills`,t=`@agimon-ai/doompi-skill`,n={source:t,bindings:[{id:`skills.browse`,path:[{key:`e`,label:`extension`,order:50},{key:`s`,label:`skills`,detail:`browse catalog`}],command:{name:e}}]};exports.LEADER_SOURCE=t,exports.SKILLS_COMMAND=e,exports.SKILLS_LEADER_CONTRIBUTION=n,exports.SKILL_COMMAND_PREFIX=`skill:`,exports.SKILL_INVOCATION_PREFIX=`/skill:`;
2
- //# sourceMappingURL=skills.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skills.cjs","names":[],"sources":["../../src/types/skills.ts"],"sourcesContent":["import type { LeaderContribution } from '@agimon-ai/doompi-extension-contracts/leader';\n\nexport const SKILLS_COMMAND = 'skills';\nexport const SKILL_INVOCATION_PREFIX = '/skill:';\nexport const SKILL_COMMAND_PREFIX = 'skill:';\nexport const LEADER_SOURCE = '@agimon-ai/doompi-skill';\n\n/**\n * The `SPC e s` binding.\n *\n * The label and order repeat the core extension group exactly; the registry\n * rejects a contribution whose shared prefix disagrees.\n */\nexport const SKILLS_LEADER_CONTRIBUTION = {\n source: LEADER_SOURCE,\n bindings: [\n {\n id: 'skills.browse',\n path: [\n { key: 'e', label: 'extension', order: 50 },\n { key: 's', label: 'skills', detail: 'browse catalog' },\n ],\n command: { name: SKILLS_COMMAND },\n },\n ],\n} satisfies LeaderContribution;\n"],"mappings":"AAEA,MAAa,EAAiB,SAGjB,EAAgB,0BAQhB,EAA6B,CACxC,OAAQ,EACR,SAAU,CACR,CACE,GAAI,gBACJ,KAAM,CACJ,CAAE,IAAK,IAAK,MAAO,YAAa,MAAO,EAAG,EAC1C,CAAE,IAAK,IAAK,MAAO,SAAU,OAAQ,gBAAiB,CACxD,EACA,QAAS,CAAE,KAAM,CAAe,CAClC,CACF,CACF"}
@@ -1,32 +0,0 @@
1
- //#region src/types/skills.d.ts
2
- declare const SKILLS_COMMAND = "skills";
3
- declare const SKILL_INVOCATION_PREFIX = "/skill:";
4
- declare const SKILL_COMMAND_PREFIX = "skill:";
5
- declare const LEADER_SOURCE = "@agimon-ai/doompi-skill";
6
- /**
7
- * The `SPC e s` binding.
8
- *
9
- * The label and order repeat the core extension group exactly; the registry
10
- * rejects a contribution whose shared prefix disagrees.
11
- */
12
- declare const SKILLS_LEADER_CONTRIBUTION: {
13
- source: string;
14
- bindings: {
15
- id: string;
16
- path: [{
17
- key: string;
18
- label: string;
19
- order: number;
20
- }, {
21
- key: string;
22
- label: string;
23
- detail: string;
24
- }];
25
- command: {
26
- name: string;
27
- };
28
- }[];
29
- };
30
- //#endregion
31
- export { LEADER_SOURCE, SKILLS_COMMAND, SKILLS_LEADER_CONTRIBUTION, SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX };
32
- //# sourceMappingURL=skills.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skills.d.cts","names":[],"sources":["../../src/types/skills.ts"],"mappings":";cAEa;cACA;cACA;cACA;;;;;;;cAQA"}
@@ -1,32 +0,0 @@
1
- //#region src/types/skills.d.ts
2
- declare const SKILLS_COMMAND = "skills";
3
- declare const SKILL_INVOCATION_PREFIX = "/skill:";
4
- declare const SKILL_COMMAND_PREFIX = "skill:";
5
- declare const LEADER_SOURCE = "@agimon-ai/doompi-skill";
6
- /**
7
- * The `SPC e s` binding.
8
- *
9
- * The label and order repeat the core extension group exactly; the registry
10
- * rejects a contribution whose shared prefix disagrees.
11
- */
12
- declare const SKILLS_LEADER_CONTRIBUTION: {
13
- source: string;
14
- bindings: {
15
- id: string;
16
- path: [{
17
- key: string;
18
- label: string;
19
- order: number;
20
- }, {
21
- key: string;
22
- label: string;
23
- detail: string;
24
- }];
25
- command: {
26
- name: string;
27
- };
28
- }[];
29
- };
30
- //#endregion
31
- export { LEADER_SOURCE, SKILLS_COMMAND, SKILLS_LEADER_CONTRIBUTION, SKILL_COMMAND_PREFIX, SKILL_INVOCATION_PREFIX };
32
- //# sourceMappingURL=skills.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skills.d.mts","names":[],"sources":["../../src/types/skills.ts"],"mappings":";cAEa;cACA;cACA;cACA;;;;;;;cAQA"}
@@ -1,2 +0,0 @@
1
- const e=`skills`,t=`/skill:`,n=`skill:`,r=`@agimon-ai/doompi-skill`,i={source:r,bindings:[{id:`skills.browse`,path:[{key:`e`,label:`extension`,order:50},{key:`s`,label:`skills`,detail:`browse catalog`}],command:{name:e}}]};export{r as LEADER_SOURCE,e as SKILLS_COMMAND,i as SKILLS_LEADER_CONTRIBUTION,n as SKILL_COMMAND_PREFIX,t as SKILL_INVOCATION_PREFIX};
2
- //# sourceMappingURL=skills.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skills.mjs","names":[],"sources":["../../src/types/skills.ts"],"sourcesContent":["import type { LeaderContribution } from '@agimon-ai/doompi-extension-contracts/leader';\n\nexport const SKILLS_COMMAND = 'skills';\nexport const SKILL_INVOCATION_PREFIX = '/skill:';\nexport const SKILL_COMMAND_PREFIX = 'skill:';\nexport const LEADER_SOURCE = '@agimon-ai/doompi-skill';\n\n/**\n * The `SPC e s` binding.\n *\n * The label and order repeat the core extension group exactly; the registry\n * rejects a contribution whose shared prefix disagrees.\n */\nexport const SKILLS_LEADER_CONTRIBUTION = {\n source: LEADER_SOURCE,\n bindings: [\n {\n id: 'skills.browse',\n path: [\n { key: 'e', label: 'extension', order: 50 },\n { key: 's', label: 'skills', detail: 'browse catalog' },\n ],\n command: { name: SKILLS_COMMAND },\n },\n ],\n} satisfies LeaderContribution;\n"],"mappings":"AAEA,MAAa,EAAiB,SACjB,EAA0B,UAC1B,EAAuB,SACvB,EAAgB,0BAQhB,EAA6B,CACxC,OAAQ,EACR,SAAU,CACR,CACE,GAAI,gBACJ,KAAM,CACJ,CAAE,IAAK,IAAK,MAAO,YAAa,MAAO,EAAG,EAC1C,CAAE,IAAK,IAAK,MAAO,SAAU,OAAQ,gBAAiB,CACxD,EACA,QAAS,CAAE,KAAM,CAAe,CAClC,CACF,CACF"}