@agimon-ai/doompi-skill 0.0.1-alpha.6 → 0.0.1-alpha.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -10
- package/dist/apiContracts.cjs +17 -0
- package/dist/apiContracts.cjs.map +1 -0
- package/dist/apiContracts.d.mts +53 -0
- package/dist/apiContracts.d.mts.map +1 -0
- package/dist/apiContracts.mjs +13 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/catalog.cjs +4 -1
- package/dist/catalog.d.mts +1 -1
- package/dist/catalog.mjs +2 -1
- package/dist/deferredSkills-BzhyXnJ4.mjs +176 -0
- package/dist/{adapters/deferredSkills.cjs.map → deferredSkills-BzhyXnJ4.mjs.map} +1 -1
- package/dist/deferredSkills-DF6UCAsk.cjs +195 -0
- package/dist/deferredSkills-DF6UCAsk.cjs.map +1 -0
- package/dist/extensions/mcp.cjs +123 -0
- package/dist/extensions/mcp.cjs.map +1 -0
- package/dist/extensions/mcp.d.mts +5 -0
- package/dist/extensions/mcp.d.mts.map +1 -0
- package/dist/extensions/mcp.mjs +118 -0
- package/dist/extensions/mcp.mjs.map +1 -0
- package/dist/extensions/pi.cjs +401 -1
- package/dist/extensions/pi.cjs.map +1 -0
- package/dist/extensions/pi.d.mts +5 -2
- package/dist/extensions/pi.d.mts.map +1 -0
- package/dist/extensions/pi.mjs +396 -1
- package/dist/extensions/pi.mjs.map +1 -0
- package/dist/extensions/server.cjs +290 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +282 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/{adapters/skillCatalog.d.cts → index-6I1f9Ola.d.mts} +12 -4
- package/dist/index-6I1f9Ola.d.mts.map +1 -0
- package/dist/index.cjs +11 -1
- package/dist/index.d.mts +60 -5
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +4 -1
- package/dist/rolldown-runtime-C0BPl7ul.cjs +43 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/skillCatalog-DLuYFuS6.cjs +213 -0
- package/dist/skillCatalog-DLuYFuS6.cjs.map +1 -0
- package/dist/skillCatalog-DYpoSsdD.mjs +194 -0
- package/dist/skillCatalog-DYpoSsdD.mjs.map +1 -0
- package/dist/skillText-B8muK9HZ.mjs +17 -0
- package/dist/skillText-B8muK9HZ.mjs.map +1 -0
- package/dist/skillText-C5OuRtBY.cjs +22 -0
- package/dist/skillText-C5OuRtBY.cjs.map +1 -0
- package/dist/skills-CjL1THn-.cjs +61 -0
- package/dist/skills-CjL1THn-.cjs.map +1 -0
- package/dist/skills-DAabEcff.mjs +32 -0
- package/dist/skills-DAabEcff.mjs.map +1 -0
- package/dist/skillsOverlay-C-VBauyr.mjs +362 -0
- package/dist/skillsOverlay-C-VBauyr.mjs.map +1 -0
- package/dist/skillsOverlay-CYRvtxbp.cjs +375 -0
- package/dist/skillsOverlay-CYRvtxbp.cjs.map +1 -0
- package/llms.txt +10 -2
- package/package.json +59 -18
- package/src/prompts/doompi-author-skill/SKILL.md +33 -0
- package/src/prompts/doompi-use-skill/SKILL.md +18 -0
- package/dist/_virtual/_rolldown/runtime.cjs +0 -1
- package/dist/adapters/deferredSkills.cjs +0 -12
- package/dist/adapters/deferredSkills.mjs +0 -12
- package/dist/adapters/deferredSkills.mjs.map +0 -1
- package/dist/adapters/helpSkills.cjs +0 -2
- package/dist/adapters/helpSkills.cjs.map +0 -1
- package/dist/adapters/helpSkills.mjs +0 -2
- package/dist/adapters/helpSkills.mjs.map +0 -1
- package/dist/adapters/pi/extension.cjs +0 -2
- package/dist/adapters/pi/extension.cjs.map +0 -1
- package/dist/adapters/pi/extension.d.cts +0 -7
- package/dist/adapters/pi/extension.d.cts.map +0 -1
- package/dist/adapters/pi/extension.d.mts +0 -7
- package/dist/adapters/pi/extension.d.mts.map +0 -1
- package/dist/adapters/pi/extension.mjs +0 -2
- package/dist/adapters/pi/extension.mjs.map +0 -1
- package/dist/adapters/pi/skillReadiness.cjs +0 -4
- package/dist/adapters/pi/skillReadiness.cjs.map +0 -1
- package/dist/adapters/pi/skillReadiness.mjs +0 -4
- package/dist/adapters/pi/skillReadiness.mjs.map +0 -1
- package/dist/adapters/skillCatalog.cjs +0 -2
- package/dist/adapters/skillCatalog.cjs.map +0 -1
- package/dist/adapters/skillCatalog.d.cts.map +0 -1
- package/dist/adapters/skillCatalog.d.mts +0 -58
- package/dist/adapters/skillCatalog.d.mts.map +0 -1
- package/dist/adapters/skillCatalog.mjs +0 -2
- package/dist/adapters/skillCatalog.mjs.map +0 -1
- package/dist/catalog.d.cts +0 -2
- package/dist/commands/skillsCommand.cjs +0 -2
- package/dist/commands/skillsCommand.cjs.map +0 -1
- package/dist/commands/skillsCommand.mjs +0 -2
- package/dist/commands/skillsCommand.mjs.map +0 -1
- package/dist/extensions/pi.d.cts +0 -2
- package/dist/index.d.cts +0 -5
- package/dist/services/skillText.cjs +0 -2
- package/dist/services/skillText.cjs.map +0 -1
- package/dist/services/skillText.d.cts +0 -13
- package/dist/services/skillText.d.cts.map +0 -1
- package/dist/services/skillText.d.mts +0 -13
- package/dist/services/skillText.d.mts.map +0 -1
- package/dist/services/skillText.mjs +0 -2
- package/dist/services/skillText.mjs.map +0 -1
- package/dist/tui/skillsOverlay.cjs +0 -4
- package/dist/tui/skillsOverlay.cjs.map +0 -1
- package/dist/tui/skillsOverlay.d.cts +0 -18
- package/dist/tui/skillsOverlay.d.cts.map +0 -1
- package/dist/tui/skillsOverlay.d.mts +0 -18
- package/dist/tui/skillsOverlay.d.mts.map +0 -1
- package/dist/tui/skillsOverlay.mjs +0 -4
- package/dist/tui/skillsOverlay.mjs.map +0 -1
- package/dist/types/skills.cjs +0 -2
- package/dist/types/skills.cjs.map +0 -1
- package/dist/types/skills.d.cts +0 -32
- package/dist/types/skills.d.cts.map +0 -1
- package/dist/types/skills.d.mts +0 -32
- package/dist/types/skills.d.mts.map +0 -1
- package/dist/types/skills.mjs +0 -2
- package/dist/types/skills.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# @agimon-ai/doompi-skill
|
|
2
2
|
|
|
3
|
-
The session skill
|
|
3
|
+
The session skill catalog for [DoomPi](https://www.npmjs.com/package/@agimon-ai/doompi).
|
|
4
4
|
|
|
5
5
|
Pi loads skills from the directories it is given at startup. DoomPi adds two things on top: skills
|
|
6
|
-
that are discovered lazily rather than loaded eagerly, and a registry other extensions
|
|
7
|
-
own skill directories
|
|
6
|
+
that are discovered lazily rather than loaded eagerly, and a registry where other extensions
|
|
7
|
+
publish their own skill directories.
|
|
8
8
|
|
|
9
9
|
## What it registers
|
|
10
10
|
|
|
11
|
-
| Surface |
|
|
12
|
-
| -------------------- |
|
|
13
|
-
| `/skills` | a fullscreen browser over everything this session loaded and everything
|
|
14
|
-
| `SPC e s` | the leader binding for the same browser
|
|
15
|
-
| `input` hook | expands `/skill:<name>` for a skill that was discovered but not loaded
|
|
16
|
-
| `before_agent_start` | appends discovered skills to the system prompt, and surfaces discovery diagnostics once
|
|
11
|
+
| Surface | Behavior |
|
|
12
|
+
| -------------------- | -------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| `/skills` | a fullscreen browser over everything this session loaded and everything any other domain would add |
|
|
14
|
+
| `SPC e s` | the leader binding for the same browser |
|
|
15
|
+
| `input` hook | expands `/skill:<name>` for a skill that was discovered but not loaded |
|
|
16
|
+
| `before_agent_start` | appends discovered skills to the system prompt, and surfaces discovery diagnostics once |
|
|
17
17
|
|
|
18
18
|
## Deferred discovery
|
|
19
19
|
|
|
@@ -26,11 +26,17 @@ diagnostics.
|
|
|
26
26
|
## Contributing skill directories
|
|
27
27
|
|
|
28
28
|
The package provides the session-owned `doom/skill-sources` Cordis service from
|
|
29
|
-
`@agimon-ai/doompi-
|
|
29
|
+
`@agimon-ai/doompi-core/skills`. Extensions register their directories inside
|
|
30
30
|
`ctx.inject([DOOM_SKILL_SOURCES_SERVICE], ...)`; the returned handle is disposed automatically when
|
|
31
31
|
either the contributor or provider unloads. Contributions are keyed by source, so a replacement
|
|
32
32
|
generation takes the previous package slot without doubling it.
|
|
33
33
|
|
|
34
|
+
## Help guidance
|
|
35
|
+
|
|
36
|
+
While the Help minor mode is active, this package contributes `doompi-author-skill` for creating
|
|
37
|
+
and distributing skills and `doompi-use-skill` for browsing, invoking, and diagnosing them. Both
|
|
38
|
+
prompts are published under `src/prompts` and routed through this package's [`llms.txt`](./llms.txt).
|
|
39
|
+
|
|
34
40
|
## Installation
|
|
35
41
|
|
|
36
42
|
DoomPi depends on this package and activates it as fixed host core, so a DoomPi install already has
|
|
@@ -39,3 +45,5 @@ it. It is not selectable from `.doom/modes.yaml`.
|
|
|
39
45
|
## License
|
|
40
46
|
|
|
41
47
|
MIT
|
|
48
|
+
|
|
49
|
+
Plugin entries live in `src/extensions`, host events and commands in routed surfaces, and catalog and discovery logic in named `src/services` folders. `src/exports` contains public helpers and types. Pi declares owned service bindings and native events. Its `onStop` invalidates pending discovery generations; final disposal releases the Help view. The server awaits its async declaration factory to discover named skill commands before registration and startup.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
//#region src/schemas/apiContracts.ts
|
|
6
|
+
/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
|
|
7
|
+
const apiContracts = (0, require("@agimon-ai/doompi-core/apiContracts").defineApiContract)({
|
|
8
|
+
version: 1,
|
|
9
|
+
http: [],
|
|
10
|
+
sockets: [],
|
|
11
|
+
dynamic: []
|
|
12
|
+
});
|
|
13
|
+
//#endregion
|
|
14
|
+
exports.apiContracts = apiContracts;
|
|
15
|
+
exports.default = apiContracts;
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=apiContracts.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiContracts.cjs","names":["defineApiContract"],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract } from '@agimon-ai/doompi-core/apiContracts';\n\n/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */\nexport const apiContracts = defineApiContract({ version: 1, http: [], sockets: [], dynamic: [] });\nexport default apiContracts;\n"],"mappings":";;;;;;AAGA,MAAa,gBAAA,gDAAeA,CAAAA,CAAAA,kBAAAA,CAAkB;CAAE,SAAS;CAAG,MAAM,CAAC;CAAG,SAAS,CAAC;CAAG,SAAS,CAAC;AAAE,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/schemas/apiContracts.d.ts
|
|
2
|
+
/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
|
|
3
|
+
export declare const apiContracts: {
|
|
4
|
+
version: 1;
|
|
5
|
+
protocols?: Record<string, string> | undefined;
|
|
6
|
+
schemas?: Record<string, boolean | object> | undefined;
|
|
7
|
+
http: {
|
|
8
|
+
id: string;
|
|
9
|
+
scope: "global" | "session" | "workspace";
|
|
10
|
+
basePath?: string | undefined;
|
|
11
|
+
path: string;
|
|
12
|
+
method: "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT";
|
|
13
|
+
description: string;
|
|
14
|
+
availability?: string | undefined;
|
|
15
|
+
authentication: "device" | "none" | "owner";
|
|
16
|
+
parameters?: {
|
|
17
|
+
name: string;
|
|
18
|
+
in: "header" | "path" | "query";
|
|
19
|
+
required: boolean;
|
|
20
|
+
schema: boolean | object;
|
|
21
|
+
}[] | undefined;
|
|
22
|
+
body?: {
|
|
23
|
+
contentType: string;
|
|
24
|
+
contentTypes?: string[] | undefined;
|
|
25
|
+
required: boolean;
|
|
26
|
+
schema: boolean | object;
|
|
27
|
+
} | undefined;
|
|
28
|
+
responses: Record<string, {
|
|
29
|
+
description: string;
|
|
30
|
+
contentType?: string | undefined;
|
|
31
|
+
schema?: boolean | object | undefined;
|
|
32
|
+
events?: Record<string, boolean | object> | undefined;
|
|
33
|
+
}>;
|
|
34
|
+
}[];
|
|
35
|
+
sockets: {
|
|
36
|
+
id: string;
|
|
37
|
+
scope: "global" | "session" | "workspace";
|
|
38
|
+
description: string;
|
|
39
|
+
availability?: string | undefined;
|
|
40
|
+
service: string;
|
|
41
|
+
member: string;
|
|
42
|
+
direction: "client-to-server" | "server-to-client";
|
|
43
|
+
kind: "channel" | "method" | "state";
|
|
44
|
+
input: boolean | object;
|
|
45
|
+
output?: boolean | object | undefined;
|
|
46
|
+
errors?: boolean | object | undefined;
|
|
47
|
+
}[];
|
|
48
|
+
dynamic: string[];
|
|
49
|
+
gaps?: string[] | undefined;
|
|
50
|
+
};
|
|
51
|
+
//#endregion
|
|
52
|
+
export { apiContracts as default };
|
|
53
|
+
//# sourceMappingURL=apiContracts.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiContracts.d.mts","names":[],"sources":["../src/schemas/apiContracts.ts"],"mappings":";;qBAGa"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineApiContract } from "@agimon-ai/doompi-core/apiContracts";
|
|
2
|
+
//#region src/schemas/apiContracts.ts
|
|
3
|
+
/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
|
|
4
|
+
const apiContracts = defineApiContract({
|
|
5
|
+
version: 1,
|
|
6
|
+
http: [],
|
|
7
|
+
sockets: [],
|
|
8
|
+
dynamic: []
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export { apiContracts, apiContracts as default };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=apiContracts.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiContracts.mjs","names":[],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract } from '@agimon-ai/doompi-core/apiContracts';\n\n/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */\nexport const apiContracts = defineApiContract({ version: 1, http: [], sockets: [], dynamic: [] });\nexport default apiContracts;\n"],"mappings":";;;AAGA,MAAa,eAAe,kBAAkB;CAAE,SAAS;CAAG,MAAM,CAAC;CAAG,SAAS,CAAC;CAAG,SAAS,CAAC;AAAE,CAAC"}
|
package/dist/catalog.cjs
CHANGED
|
@@ -1 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_skillCatalog = require("./skillCatalog-DLuYFuS6.cjs");
|
|
3
|
+
exports.buildSkillCatalog = require_skillCatalog.buildSkillCatalog;
|
|
4
|
+
exports.counter = require_skillCatalog.counter;
|
package/dist/catalog.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as SkillOwnerNode, i as SkillGroup, n as SkillCatalogOptions, o as buildSkillCatalog, r as SkillEntry, s as counter, t as SkillCatalog } from "./index-6I1f9Ola.mjs";
|
|
2
2
|
export { type SkillCatalog, type SkillCatalogOptions, type SkillEntry, type SkillGroup, type SkillOwnerNode, buildSkillCatalog, counter };
|
package/dist/catalog.mjs
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import { n as counter, t as buildSkillCatalog } from "./skillCatalog-DYpoSsdD.mjs";
|
|
2
|
+
export { buildSkillCatalog, counter };
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { formatSkillsForPrompt, getAgentDir, loadSkills, stripFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
|
+
import { Worker } from "node:worker_threads";
|
|
7
|
+
//#region src/services/deferredSkills/index.ts
|
|
8
|
+
var deferredSkills_exports = /* @__PURE__ */ __exportAll({
|
|
9
|
+
DeferredSkillLoader: () => DeferredSkillLoader,
|
|
10
|
+
buildPromptWithDeferredSkills: () => buildPromptWithDeferredSkills,
|
|
11
|
+
expandDeferredSkillCommand: () => expandDeferredSkillCommand
|
|
12
|
+
});
|
|
13
|
+
const PI_PACKAGE = "@earendil-works/pi-coding-agent";
|
|
14
|
+
const PACKAGE_MANIFEST = "package.json";
|
|
15
|
+
const SKILL_WORKER_SOURCE = `
|
|
16
|
+
const { parentPort, workerData } = require('node:worker_threads');
|
|
17
|
+
void (async () => {
|
|
18
|
+
try {
|
|
19
|
+
const { loadSkills } = await import(workerData.piModuleUrl);
|
|
20
|
+
parentPort.postMessage({ ok: true, result: loadSkills(workerData.loadOptions) });
|
|
21
|
+
} catch (error) {
|
|
22
|
+
parentPort.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
23
|
+
}
|
|
24
|
+
})();
|
|
25
|
+
`;
|
|
26
|
+
function installedPiModuleUrl() {
|
|
27
|
+
const anchors = [
|
|
28
|
+
process.argv[1],
|
|
29
|
+
fileURLToPath(import.meta.url),
|
|
30
|
+
path.join(process.cwd(), PACKAGE_MANIFEST)
|
|
31
|
+
];
|
|
32
|
+
for (const anchor of anchors) {
|
|
33
|
+
if (!anchor) continue;
|
|
34
|
+
let directory = path.dirname(path.resolve(anchor));
|
|
35
|
+
while (true) {
|
|
36
|
+
const packageRoot = path.join(directory, "node_modules", PI_PACKAGE);
|
|
37
|
+
const manifestPath = path.join(packageRoot, PACKAGE_MANIFEST);
|
|
38
|
+
try {
|
|
39
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
40
|
+
if (typeof manifest.main === "string") {
|
|
41
|
+
const entry = path.resolve(packageRoot, manifest.main);
|
|
42
|
+
if (fs.existsSync(entry)) return pathToFileURL(entry).href;
|
|
43
|
+
}
|
|
44
|
+
} catch {}
|
|
45
|
+
const parent = path.dirname(directory);
|
|
46
|
+
if (parent === directory) break;
|
|
47
|
+
directory = parent;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`Cannot resolve ${PI_PACKAGE} from the DoomPi or host module trees`);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* One session's Doom-selected skill inventory.
|
|
54
|
+
*
|
|
55
|
+
* Construction is deliberately cheap. `start()` schedules the synchronous Pi
|
|
56
|
+
* walker after session_start returns, while `ready()` is the single promise the
|
|
57
|
+
* first input awaits before Pi expands `/skill:name`.
|
|
58
|
+
*/
|
|
59
|
+
var DeferredSkillLoader = class {
|
|
60
|
+
#options;
|
|
61
|
+
#promise;
|
|
62
|
+
constructor(options) {
|
|
63
|
+
this.#options = options;
|
|
64
|
+
}
|
|
65
|
+
start() {
|
|
66
|
+
if (this.#promise) return this.#promise;
|
|
67
|
+
if (!this.#options.schedule && !this.#options.load) {
|
|
68
|
+
this.#promise = this.#loadInWorker();
|
|
69
|
+
return this.#promise;
|
|
70
|
+
}
|
|
71
|
+
const schedule = this.#options.schedule ?? ((load) => setImmediate(load));
|
|
72
|
+
this.#promise = new Promise((resolve) => {
|
|
73
|
+
const fail = (error) => {
|
|
74
|
+
resolve(this.#failure(error));
|
|
75
|
+
};
|
|
76
|
+
try {
|
|
77
|
+
schedule(() => {
|
|
78
|
+
try {
|
|
79
|
+
resolve(this.#load());
|
|
80
|
+
} catch (error) {
|
|
81
|
+
fail(error);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
} catch (error) {
|
|
85
|
+
fail(error);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return this.#promise;
|
|
89
|
+
}
|
|
90
|
+
ready() {
|
|
91
|
+
return this.start();
|
|
92
|
+
}
|
|
93
|
+
#loadOptions() {
|
|
94
|
+
return {
|
|
95
|
+
cwd: this.#options.cwd,
|
|
96
|
+
agentDir: this.#options.agentDir ?? getAgentDir(),
|
|
97
|
+
skillPaths: [...this.#options.skillPaths],
|
|
98
|
+
includeDefaults: false
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
#snapshot(result) {
|
|
102
|
+
return {
|
|
103
|
+
skills: result.skills,
|
|
104
|
+
diagnostics: result.diagnostics.map((diagnostic) => `${diagnostic.path ?? this.#options.cwd}: ${diagnostic.message}`)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
#failure(error) {
|
|
108
|
+
return {
|
|
109
|
+
skills: [],
|
|
110
|
+
diagnostics: [`${this.#options.cwd}: ${error instanceof Error ? error.message : String(error)}`]
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
#load() {
|
|
114
|
+
return this.#snapshot((this.#options.load ?? loadSkills)(this.#loadOptions()));
|
|
115
|
+
}
|
|
116
|
+
#loadInWorker() {
|
|
117
|
+
return new Promise((resolve) => {
|
|
118
|
+
let settled = false;
|
|
119
|
+
const settle = (snapshot) => {
|
|
120
|
+
if (settled) return;
|
|
121
|
+
settled = true;
|
|
122
|
+
resolve(snapshot);
|
|
123
|
+
};
|
|
124
|
+
let worker;
|
|
125
|
+
try {
|
|
126
|
+
worker = new Worker(SKILL_WORKER_SOURCE, {
|
|
127
|
+
eval: true,
|
|
128
|
+
workerData: {
|
|
129
|
+
piModuleUrl: this.#options.piModuleUrl ?? installedPiModuleUrl(),
|
|
130
|
+
loadOptions: this.#loadOptions()
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
} catch (error) {
|
|
134
|
+
settle(this.#failure(error));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
worker.unref();
|
|
138
|
+
worker.once("message", (message) => {
|
|
139
|
+
if (!message.ok || !message.result) {
|
|
140
|
+
settle(this.#failure(message.error ?? "Skill worker failed"));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
settle(this.#snapshot(message.result));
|
|
144
|
+
});
|
|
145
|
+
worker.once("error", (error) => settle(this.#failure(error)));
|
|
146
|
+
worker.once("exit", (code) => {
|
|
147
|
+
if (code !== 0) settle(this.#failure(`Skill worker exited with code ${code}`));
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
/** Appends only the inventory Pi would append for these deferred skills. */
|
|
153
|
+
function buildPromptWithDeferredSkills(systemPrompt, options, skills) {
|
|
154
|
+
if (options.selectedTools && !options.selectedTools.includes("read")) return systemPrompt;
|
|
155
|
+
return `${systemPrompt}${formatSkillsForPrompt(skills)}`;
|
|
156
|
+
}
|
|
157
|
+
/** Expands a deferred `/skill:name` before Pi consults its synchronous inventory. */
|
|
158
|
+
function expandDeferredSkillCommand(text, skills) {
|
|
159
|
+
if (!text.startsWith("/skill:")) return text;
|
|
160
|
+
const spaceIndex = text.indexOf(" ");
|
|
161
|
+
const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);
|
|
162
|
+
const skill = skills.find((candidate) => candidate.name === skillName);
|
|
163
|
+
if (!skill) return text;
|
|
164
|
+
try {
|
|
165
|
+
const args = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
|
|
166
|
+
const body = stripFrontmatter(fs.readFileSync(skill.filePath, "utf8")).trim();
|
|
167
|
+
const skillBlock = `<skill name="${skill.name}" location="${skill.filePath}">\nReferences are relative to ${skill.baseDir}.\n\n${body}\n</skill>`;
|
|
168
|
+
return args ? `${skillBlock}\n\n${args}` : skillBlock;
|
|
169
|
+
} catch {
|
|
170
|
+
return text;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
export { deferredSkills_exports as n, expandDeferredSkillCommand as r, DeferredSkillLoader as t };
|
|
175
|
+
|
|
176
|
+
//# sourceMappingURL=deferredSkills-BzhyXnJ4.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deferredSkills.cjs","names":["fileURLToPath","path","fs","pathToFileURL","#options","#promise","#loadInWorker","#failure","#load","getAgentDir","#snapshot","loadSkills","#loadOptions","Worker","formatSkillsForPrompt","stripFrontmatter"],"sources":["../../src/adapters/deferredSkills.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { Worker } from 'node:worker_threads';\nimport {\n type BuildSystemPromptOptions,\n formatSkillsForPrompt,\n getAgentDir,\n loadSkills,\n type Skill,\n stripFrontmatter,\n} from '@earendil-works/pi-coding-agent';\n\nexport interface DeferredSkillSnapshot {\n skills: Skill[];\n diagnostics: string[];\n}\n\nconst PI_PACKAGE = '@earendil-works/pi-coding-agent';\nconst PACKAGE_MANIFEST = 'package.json';\nconst SKILL_WORKER_SOURCE = `\nconst { parentPort, workerData } = require('node:worker_threads');\nvoid (async () => {\n try {\n const { loadSkills } = await import(workerData.piModuleUrl);\n parentPort.postMessage({ ok: true, result: loadSkills(workerData.loadOptions) });\n } catch (error) {\n parentPort.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) });\n }\n})();\n`;\n\nfunction installedPiModuleUrl(): string {\n const anchors = [process.argv[1], fileURLToPath(import.meta.url), path.join(process.cwd(), PACKAGE_MANIFEST)];\n for (const anchor of anchors) {\n if (!anchor) continue;\n let directory = path.dirname(path.resolve(anchor));\n while (true) {\n const packageRoot = path.join(directory, 'node_modules', PI_PACKAGE);\n const manifestPath = path.join(packageRoot, PACKAGE_MANIFEST);\n try {\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as { main?: unknown };\n if (typeof manifest.main === 'string') {\n const entry = path.resolve(packageRoot, manifest.main);\n if (fs.existsSync(entry)) return pathToFileURL(entry).href;\n }\n } catch {\n // This anchor does not expose Pi at this level; continue up its module chain.\n }\n const parent = path.dirname(directory);\n if (parent === directory) break;\n directory = parent;\n }\n }\n throw new Error(`Cannot resolve ${PI_PACKAGE} from the DoomPi or host module trees`);\n}\n\ninterface SkillWorkerResult {\n ok: boolean;\n result?: { skills: Skill[]; diagnostics: Array<{ path?: string; message: string }> };\n error?: string;\n}\n\nexport interface DeferredSkillLoaderOptions {\n cwd: string;\n skillPaths: readonly string[];\n agentDir?: string;\n piModuleUrl?: string;\n schedule?: (load: () => void) => void;\n load?: (options: { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean }) => {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n };\n}\n\n/**\n * One session's Doom-selected skill inventory.\n *\n * Construction is deliberately cheap. `start()` schedules the synchronous Pi\n * walker after session_start returns, while `ready()` is the single promise the\n * first input awaits before Pi expands `/skill:name`.\n */\nexport class DeferredSkillLoader {\n readonly #options: DeferredSkillLoaderOptions;\n #promise: Promise<DeferredSkillSnapshot> | undefined;\n\n constructor(options: DeferredSkillLoaderOptions) {\n this.#options = options;\n }\n\n start(): Promise<DeferredSkillSnapshot> {\n if (this.#promise) return this.#promise;\n\n if (!this.#options.schedule && !this.#options.load) {\n this.#promise = this.#loadInWorker();\n return this.#promise;\n }\n\n const schedule = this.#options.schedule ?? ((load: () => void) => setImmediate(load));\n this.#promise = new Promise((resolve) => {\n const fail = (error: unknown): void => {\n resolve(this.#failure(error));\n };\n try {\n schedule(() => {\n try {\n resolve(this.#load());\n } catch (error) {\n fail(error);\n }\n });\n } catch (error) {\n fail(error);\n }\n });\n return this.#promise;\n }\n\n ready(): Promise<DeferredSkillSnapshot> {\n return this.start();\n }\n\n #loadOptions(): { cwd: string; agentDir: string; skillPaths: string[]; includeDefaults: boolean } {\n return {\n cwd: this.#options.cwd,\n agentDir: this.#options.agentDir ?? getAgentDir(),\n skillPaths: [...this.#options.skillPaths],\n includeDefaults: false,\n };\n }\n\n #snapshot(result: {\n skills: Skill[];\n diagnostics: Array<{ path?: string; message: string }>;\n }): DeferredSkillSnapshot {\n return {\n skills: result.skills,\n diagnostics: result.diagnostics.map(\n (diagnostic) => `${diagnostic.path ?? this.#options.cwd}: ${diagnostic.message}`,\n ),\n };\n }\n\n #failure(error: unknown): DeferredSkillSnapshot {\n return {\n skills: [],\n diagnostics: [`${this.#options.cwd}: ${error instanceof Error ? error.message : String(error)}`],\n };\n }\n\n #load(): DeferredSkillSnapshot {\n return this.#snapshot((this.#options.load ?? loadSkills)(this.#loadOptions()));\n }\n\n #loadInWorker(): Promise<DeferredSkillSnapshot> {\n return new Promise((resolve) => {\n let settled = false;\n const settle = (snapshot: DeferredSkillSnapshot): void => {\n if (settled) return;\n settled = true;\n resolve(snapshot);\n };\n let worker: Worker;\n try {\n worker = new Worker(SKILL_WORKER_SOURCE, {\n eval: true,\n workerData: {\n piModuleUrl: this.#options.piModuleUrl ?? installedPiModuleUrl(),\n loadOptions: this.#loadOptions(),\n },\n });\n } catch (error) {\n settle(this.#failure(error));\n return;\n }\n worker.unref();\n worker.once('message', (message: SkillWorkerResult) => {\n if (!message.ok || !message.result) {\n settle(this.#failure(message.error ?? 'Skill worker failed'));\n return;\n }\n settle(this.#snapshot(message.result));\n });\n worker.once('error', (error) => settle(this.#failure(error)));\n worker.once('exit', (code) => {\n if (code !== 0) settle(this.#failure(`Skill worker exited with code ${code}`));\n });\n });\n }\n}\n\n/** Appends only the inventory Pi would append for these deferred skills. */\nexport function buildPromptWithDeferredSkills(\n systemPrompt: string,\n options: BuildSystemPromptOptions,\n skills: Skill[],\n): string {\n if (options.selectedTools && !options.selectedTools.includes('read')) return systemPrompt;\n return `${systemPrompt}${formatSkillsForPrompt(skills)}`;\n}\n\n/** Expands a deferred `/skill:name` before Pi consults its synchronous inventory. */\nexport function expandDeferredSkillCommand(text: string, skills: readonly Skill[]): string {\n if (!text.startsWith('/skill:')) return text;\n const spaceIndex = text.indexOf(' ');\n const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);\n const skill = skills.find((candidate) => candidate.name === skillName);\n if (!skill) return text;\n\n try {\n const args = spaceIndex === -1 ? '' : text.slice(spaceIndex + 1).trim();\n const body = stripFrontmatter(fs.readFileSync(skill.filePath, 'utf8')).trim();\n const skillBlock = `<skill name=\"${skill.name}\" location=\"${skill.filePath}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n return args ? `${skillBlock}\\n\\n${args}` : skillBlock;\n } catch {\n // Pi uses the same literal-text fallback if a skill disappears between\n // discovery and submission; the agent can still respond to the command.\n return text;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"deferredSkills-BzhyXnJ4.mjs","names":[],"sources":["../src/services/deferredSkills/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { Worker } from 'node:worker_threads';\n\nimport {\n type BuildSystemPromptOptions,\n formatSkillsForPrompt,\n getAgentDir,\n loadSkills,\n type Skill,\n stripFrontmatter,\n} from '@earendil-works/pi-coding-agent';\n\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":";;;;;;;;;;;;AAmBA,MAAM,aAAa;AACnB,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;;;;;;;;;;;AAY5B,SAAS,uBAA+B;CACtC,MAAM,UAAU;EAAC,QAAQ,KAAK;EAAI,cAAc,YAAY,GAAG;EAAG,KAAK,KAAK,QAAQ,IAAI,GAAG,gBAAgB;CAAC;CAC5G,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,CAAC,QAAQ;EACb,IAAI,YAAY,KAAK,QAAQ,KAAK,QAAQ,MAAM,CAAC;EACjD,OAAO,MAAM;GACX,MAAM,cAAc,KAAK,KAAK,WAAW,gBAAgB,UAAU;GACnE,MAAM,eAAe,KAAK,KAAK,aAAa,gBAAgB;GAC5D,IAAI;IACF,MAAM,WAAW,KAAK,MAAM,GAAG,aAAa,cAAc,MAAM,CAAC;IACjE,IAAI,OAAO,SAAS,SAAS,UAAU;KACrC,MAAM,QAAQ,KAAK,QAAQ,aAAa,SAAS,IAAI;KACrD,IAAI,GAAG,WAAW,KAAK,GAAG,OAAO,cAAc,KAAK,CAAC,CAAC;IACxD;GACF,QAAQ,CAER;GACA,MAAM,SAAS,KAAK,QAAQ,SAAS;GACrC,IAAI,WAAW,WAAW;GAC1B,YAAY;EACd;CACF;CACA,MAAM,IAAI,MAAM,kBAAkB,WAAW,sCAAsC;AACrF;;;;;;;;AA2BA,IAAa,sBAAb,MAAiC;CAC/B;CACA;CAEA,YAAY,SAAqC;EAC/C,KAAK,WAAW;CAClB;CAEA,QAAwC;EACtC,IAAI,KAAK,UAAU,OAAO,KAAK;EAE/B,IAAI,CAAC,KAAK,SAAS,YAAY,CAAC,KAAK,SAAS,MAAM;GAClD,KAAK,WAAW,KAAK,cAAc;GACnC,OAAO,KAAK;EACd;EAEA,MAAM,WAAW,KAAK,SAAS,cAAc,SAAqB,aAAa,IAAI;EACnF,KAAK,WAAW,IAAI,SAAS,YAAY;GACvC,MAAM,QAAQ,UAAyB;IACrC,QAAQ,KAAK,SAAS,KAAK,CAAC;GAC9B;GACA,IAAI;IACF,eAAe;KACb,IAAI;MACF,QAAQ,KAAK,MAAM,CAAC;KACtB,SAAS,OAAO;MACd,KAAK,KAAK;KACZ;IACF,CAAC;GACH,SAAS,OAAO;IACd,KAAK,KAAK;GACZ;EACF,CAAC;EACD,OAAO,KAAK;CACd;CAEA,QAAwC;EACtC,OAAO,KAAK,MAAM;CACpB;CAEA,eAAkG;EAChG,OAAO;GACL,KAAK,KAAK,SAAS;GACnB,UAAU,KAAK,SAAS,YAAY,YAAY;GAChD,YAAY,CAAC,GAAG,KAAK,SAAS,UAAU;GACxC,iBAAiB;EACnB;CACF;CAEA,UAAU,QAGgB;EACxB,OAAO;GACL,QAAQ,OAAO;GACf,aAAa,OAAO,YAAY,KAC7B,eAAe,GAAG,WAAW,QAAQ,KAAK,SAAS,IAAI,IAAI,WAAW,SACzE;EACF;CACF;CAEA,SAAS,OAAuC;EAC9C,OAAO;GACL,QAAQ,CAAC;GACT,aAAa,CAAC,GAAG,KAAK,SAAS,IAAI,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;EACjG;CACF;CAEA,QAA+B;EAC7B,OAAO,KAAK,WAAW,KAAK,SAAS,QAAQ,WAAA,CAAY,KAAK,aAAa,CAAC,CAAC;CAC/E;CAEA,gBAAgD;EAC9C,OAAO,IAAI,SAAS,YAAY;GAC9B,IAAI,UAAU;GACd,MAAM,UAAU,aAA0C;IACxD,IAAI,SAAS;IACb,UAAU;IACV,QAAQ,QAAQ;GAClB;GACA,IAAI;GACJ,IAAI;IACF,SAAS,IAAI,OAAO,qBAAqB;KACvC,MAAM;KACN,YAAY;MACV,aAAa,KAAK,SAAS,eAAe,qBAAqB;MAC/D,aAAa,KAAK,aAAa;KACjC;IACF,CAAC;GACH,SAAS,OAAO;IACd,OAAO,KAAK,SAAS,KAAK,CAAC;IAC3B;GACF;GACA,OAAO,MAAM;GACb,OAAO,KAAK,YAAY,YAA+B;IACrD,IAAI,CAAC,QAAQ,MAAM,CAAC,QAAQ,QAAQ;KAClC,OAAO,KAAK,SAAS,QAAQ,SAAS,qBAAqB,CAAC;KAC5D;IACF;IACA,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;GACvC,CAAC;GACD,OAAO,KAAK,UAAU,UAAU,OAAO,KAAK,SAAS,KAAK,CAAC,CAAC;GAC5D,OAAO,KAAK,SAAS,SAAS;IAC5B,IAAI,SAAS,GAAG,OAAO,KAAK,SAAS,iCAAiC,MAAM,CAAC;GAC/E,CAAC;EACH,CAAC;CACH;AACF;;AAGA,SAAgB,8BACd,cACA,SACA,QACQ;CACR,IAAI,QAAQ,iBAAiB,CAAC,QAAQ,cAAc,SAAS,MAAM,GAAG,OAAO;CAC7E,OAAO,GAAG,eAAe,sBAAsB,MAAM;AACvD;;AAGA,SAAgB,2BAA2B,MAAc,QAAkC;CACzF,IAAI,CAAC,KAAK,WAAW,SAAS,GAAG,OAAO;CACxC,MAAM,aAAa,KAAK,QAAQ,GAAG;CACnC,MAAM,YAAY,eAAe,KAAK,KAAK,MAAM,CAAC,IAAI,KAAK,MAAM,GAAG,UAAU;CAC9E,MAAM,QAAQ,OAAO,MAAM,cAAc,UAAU,SAAS,SAAS;CACrE,IAAI,CAAC,OAAO,OAAO;CAEnB,IAAI;EACF,MAAM,OAAO,eAAe,KAAK,KAAK,KAAK,MAAM,aAAa,CAAC,CAAC,CAAC,KAAK;EACtE,MAAM,OAAO,iBAAiB,GAAG,aAAa,MAAM,UAAU,MAAM,CAAC,CAAC,CAAC,KAAK;EAC5E,MAAM,aAAa,gBAAgB,MAAM,KAAK,cAAc,MAAM,SAAS,iCAAiC,MAAM,QAAQ,OAAO,KAAK;EACtI,OAAO,OAAO,GAAG,WAAW,MAAM,SAAS;CAC7C,QAAQ;EAGN,OAAO;CACT;AACF"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
|
|
2
|
+
let node_fs = require("node:fs");
|
|
3
|
+
node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
6
|
+
let _earendil_works_pi_coding_agent = require("@earendil-works/pi-coding-agent");
|
|
7
|
+
let node_url = require("node:url");
|
|
8
|
+
let node_worker_threads = require("node:worker_threads");
|
|
9
|
+
//#region src/services/deferredSkills/index.ts
|
|
10
|
+
var deferredSkills_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
11
|
+
DeferredSkillLoader: () => DeferredSkillLoader,
|
|
12
|
+
buildPromptWithDeferredSkills: () => buildPromptWithDeferredSkills,
|
|
13
|
+
expandDeferredSkillCommand: () => expandDeferredSkillCommand
|
|
14
|
+
});
|
|
15
|
+
const PI_PACKAGE = "@earendil-works/pi-coding-agent";
|
|
16
|
+
const PACKAGE_MANIFEST = "package.json";
|
|
17
|
+
const SKILL_WORKER_SOURCE = `
|
|
18
|
+
const { parentPort, workerData } = require('node:worker_threads');
|
|
19
|
+
void (async () => {
|
|
20
|
+
try {
|
|
21
|
+
const { loadSkills } = await import(workerData.piModuleUrl);
|
|
22
|
+
parentPort.postMessage({ ok: true, result: loadSkills(workerData.loadOptions) });
|
|
23
|
+
} catch (error) {
|
|
24
|
+
parentPort.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
`;
|
|
28
|
+
function installedPiModuleUrl() {
|
|
29
|
+
const anchors = [
|
|
30
|
+
process.argv[1],
|
|
31
|
+
(0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href),
|
|
32
|
+
node_path.default.join(process.cwd(), PACKAGE_MANIFEST)
|
|
33
|
+
];
|
|
34
|
+
for (const anchor of anchors) {
|
|
35
|
+
if (!anchor) continue;
|
|
36
|
+
let directory = node_path.default.dirname(node_path.default.resolve(anchor));
|
|
37
|
+
while (true) {
|
|
38
|
+
const packageRoot = node_path.default.join(directory, "node_modules", PI_PACKAGE);
|
|
39
|
+
const manifestPath = node_path.default.join(packageRoot, PACKAGE_MANIFEST);
|
|
40
|
+
try {
|
|
41
|
+
const manifest = JSON.parse(node_fs.default.readFileSync(manifestPath, "utf8"));
|
|
42
|
+
if (typeof manifest.main === "string") {
|
|
43
|
+
const entry = node_path.default.resolve(packageRoot, manifest.main);
|
|
44
|
+
if (node_fs.default.existsSync(entry)) return (0, node_url.pathToFileURL)(entry).href;
|
|
45
|
+
}
|
|
46
|
+
} catch {}
|
|
47
|
+
const parent = node_path.default.dirname(directory);
|
|
48
|
+
if (parent === directory) break;
|
|
49
|
+
directory = parent;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
throw new Error(`Cannot resolve ${PI_PACKAGE} from the DoomPi or host module trees`);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* One session's Doom-selected skill inventory.
|
|
56
|
+
*
|
|
57
|
+
* Construction is deliberately cheap. `start()` schedules the synchronous Pi
|
|
58
|
+
* walker after session_start returns, while `ready()` is the single promise the
|
|
59
|
+
* first input awaits before Pi expands `/skill:name`.
|
|
60
|
+
*/
|
|
61
|
+
var DeferredSkillLoader = class {
|
|
62
|
+
#options;
|
|
63
|
+
#promise;
|
|
64
|
+
constructor(options) {
|
|
65
|
+
this.#options = options;
|
|
66
|
+
}
|
|
67
|
+
start() {
|
|
68
|
+
if (this.#promise) return this.#promise;
|
|
69
|
+
if (!this.#options.schedule && !this.#options.load) {
|
|
70
|
+
this.#promise = this.#loadInWorker();
|
|
71
|
+
return this.#promise;
|
|
72
|
+
}
|
|
73
|
+
const schedule = this.#options.schedule ?? ((load) => setImmediate(load));
|
|
74
|
+
this.#promise = new Promise((resolve) => {
|
|
75
|
+
const fail = (error) => {
|
|
76
|
+
resolve(this.#failure(error));
|
|
77
|
+
};
|
|
78
|
+
try {
|
|
79
|
+
schedule(() => {
|
|
80
|
+
try {
|
|
81
|
+
resolve(this.#load());
|
|
82
|
+
} catch (error) {
|
|
83
|
+
fail(error);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
} catch (error) {
|
|
87
|
+
fail(error);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
return this.#promise;
|
|
91
|
+
}
|
|
92
|
+
ready() {
|
|
93
|
+
return this.start();
|
|
94
|
+
}
|
|
95
|
+
#loadOptions() {
|
|
96
|
+
return {
|
|
97
|
+
cwd: this.#options.cwd,
|
|
98
|
+
agentDir: this.#options.agentDir ?? (0, _earendil_works_pi_coding_agent.getAgentDir)(),
|
|
99
|
+
skillPaths: [...this.#options.skillPaths],
|
|
100
|
+
includeDefaults: false
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
#snapshot(result) {
|
|
104
|
+
return {
|
|
105
|
+
skills: result.skills,
|
|
106
|
+
diagnostics: result.diagnostics.map((diagnostic) => `${diagnostic.path ?? this.#options.cwd}: ${diagnostic.message}`)
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
#failure(error) {
|
|
110
|
+
return {
|
|
111
|
+
skills: [],
|
|
112
|
+
diagnostics: [`${this.#options.cwd}: ${error instanceof Error ? error.message : String(error)}`]
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
#load() {
|
|
116
|
+
return this.#snapshot((this.#options.load ?? _earendil_works_pi_coding_agent.loadSkills)(this.#loadOptions()));
|
|
117
|
+
}
|
|
118
|
+
#loadInWorker() {
|
|
119
|
+
return new Promise((resolve) => {
|
|
120
|
+
let settled = false;
|
|
121
|
+
const settle = (snapshot) => {
|
|
122
|
+
if (settled) return;
|
|
123
|
+
settled = true;
|
|
124
|
+
resolve(snapshot);
|
|
125
|
+
};
|
|
126
|
+
let worker;
|
|
127
|
+
try {
|
|
128
|
+
worker = new node_worker_threads.Worker(SKILL_WORKER_SOURCE, {
|
|
129
|
+
eval: true,
|
|
130
|
+
workerData: {
|
|
131
|
+
piModuleUrl: this.#options.piModuleUrl ?? installedPiModuleUrl(),
|
|
132
|
+
loadOptions: this.#loadOptions()
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
} catch (error) {
|
|
136
|
+
settle(this.#failure(error));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
worker.unref();
|
|
140
|
+
worker.once("message", (message) => {
|
|
141
|
+
if (!message.ok || !message.result) {
|
|
142
|
+
settle(this.#failure(message.error ?? "Skill worker failed"));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
settle(this.#snapshot(message.result));
|
|
146
|
+
});
|
|
147
|
+
worker.once("error", (error) => settle(this.#failure(error)));
|
|
148
|
+
worker.once("exit", (code) => {
|
|
149
|
+
if (code !== 0) settle(this.#failure(`Skill worker exited with code ${code}`));
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
/** Appends only the inventory Pi would append for these deferred skills. */
|
|
155
|
+
function buildPromptWithDeferredSkills(systemPrompt, options, skills) {
|
|
156
|
+
if (options.selectedTools && !options.selectedTools.includes("read")) return systemPrompt;
|
|
157
|
+
return `${systemPrompt}${(0, _earendil_works_pi_coding_agent.formatSkillsForPrompt)(skills)}`;
|
|
158
|
+
}
|
|
159
|
+
/** Expands a deferred `/skill:name` before Pi consults its synchronous inventory. */
|
|
160
|
+
function expandDeferredSkillCommand(text, skills) {
|
|
161
|
+
if (!text.startsWith("/skill:")) return text;
|
|
162
|
+
const spaceIndex = text.indexOf(" ");
|
|
163
|
+
const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);
|
|
164
|
+
const skill = skills.find((candidate) => candidate.name === skillName);
|
|
165
|
+
if (!skill) return text;
|
|
166
|
+
try {
|
|
167
|
+
const args = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
|
|
168
|
+
const body = (0, _earendil_works_pi_coding_agent.stripFrontmatter)(node_fs.default.readFileSync(skill.filePath, "utf8")).trim();
|
|
169
|
+
const skillBlock = `<skill name="${skill.name}" location="${skill.filePath}">\nReferences are relative to ${skill.baseDir}.\n\n${body}\n</skill>`;
|
|
170
|
+
return args ? `${skillBlock}\n\n${args}` : skillBlock;
|
|
171
|
+
} catch {
|
|
172
|
+
return text;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
Object.defineProperty(exports, "DeferredSkillLoader", {
|
|
177
|
+
enumerable: true,
|
|
178
|
+
get: function() {
|
|
179
|
+
return DeferredSkillLoader;
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
Object.defineProperty(exports, "deferredSkills_exports", {
|
|
183
|
+
enumerable: true,
|
|
184
|
+
get: function() {
|
|
185
|
+
return deferredSkills_exports;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
Object.defineProperty(exports, "expandDeferredSkillCommand", {
|
|
189
|
+
enumerable: true,
|
|
190
|
+
get: function() {
|
|
191
|
+
return expandDeferredSkillCommand;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
//# sourceMappingURL=deferredSkills-DF6UCAsk.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deferredSkills-DF6UCAsk.cjs","names":["fileURLToPath","path","fs","pathToFileURL","getAgentDir","loadSkills","Worker","formatSkillsForPrompt","stripFrontmatter"],"sources":["../src/services/deferredSkills/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { Worker } from 'node:worker_threads';\n\nimport {\n type BuildSystemPromptOptions,\n formatSkillsForPrompt,\n getAgentDir,\n loadSkills,\n type Skill,\n stripFrontmatter,\n} from '@earendil-works/pi-coding-agent';\n\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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"}
|