@halyard/cli 0.1.2 → 0.2.0
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 +88 -1
- package/dist/cli.js +27 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/api-keys.d.ts +3 -0
- package/dist/commands/api-keys.d.ts.map +1 -0
- package/dist/commands/api-keys.js +90 -0
- package/dist/commands/api-keys.js.map +1 -0
- package/dist/commands/hook.d.ts +6 -0
- package/dist/commands/hook.d.ts.map +1 -0
- package/dist/commands/hook.js +106 -0
- package/dist/commands/hook.js.map +1 -0
- package/dist/commands/login.d.ts +17 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +111 -14
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/orgs.d.ts +3 -0
- package/dist/commands/orgs.d.ts.map +1 -0
- package/dist/commands/orgs.js +102 -0
- package/dist/commands/orgs.js.map +1 -0
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +29 -6
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/sessions.d.ts +3 -0
- package/dist/commands/sessions.d.ts.map +1 -0
- package/dist/commands/sessions.js +139 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +28 -3
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/skills.d.ts +12 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +171 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +51 -49
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/users.d.ts +13 -0
- package/dist/commands/users.d.ts.map +1 -0
- package/dist/commands/users.js +205 -0
- package/dist/commands/users.js.map +1 -0
- package/dist/commands/whoami.d.ts +21 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +77 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/lib/api-client.d.ts +64 -2
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +32 -7
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/auth.d.ts +42 -6
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +101 -27
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/config-files.d.ts +6 -0
- package/dist/lib/config-files.d.ts.map +1 -0
- package/dist/lib/config-files.js +27 -0
- package/dist/lib/config-files.js.map +1 -0
- package/dist/lib/config.d.ts +13 -7
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +22 -8
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/copilot-export.d.ts +31 -0
- package/dist/lib/copilot-export.d.ts.map +1 -0
- package/dist/lib/copilot-export.js +129 -0
- package/dist/lib/copilot-export.js.map +1 -0
- package/dist/lib/discovery.d.ts +60 -2
- package/dist/lib/discovery.d.ts.map +1 -1
- package/dist/lib/discovery.js +187 -15
- package/dist/lib/discovery.js.map +1 -1
- package/dist/lib/hook-discovery.d.ts +43 -0
- package/dist/lib/hook-discovery.d.ts.map +1 -0
- package/dist/lib/hook-discovery.js +90 -0
- package/dist/lib/hook-discovery.js.map +1 -0
- package/dist/lib/hook-input.d.ts +36 -0
- package/dist/lib/hook-input.d.ts.map +1 -0
- package/dist/lib/hook-input.js +161 -0
- package/dist/lib/hook-input.js.map +1 -0
- package/dist/lib/hook-runner.d.ts +48 -0
- package/dist/lib/hook-runner.d.ts.map +1 -0
- package/dist/lib/hook-runner.js +306 -0
- package/dist/lib/hook-runner.js.map +1 -0
- package/dist/lib/http.d.ts +18 -0
- package/dist/lib/http.d.ts.map +1 -0
- package/dist/lib/http.js +54 -0
- package/dist/lib/http.js.map +1 -0
- package/dist/lib/opencode-export.d.ts +22 -0
- package/dist/lib/opencode-export.d.ts.map +1 -0
- package/dist/lib/opencode-export.js +53 -0
- package/dist/lib/opencode-export.js.map +1 -0
- package/dist/lib/output.d.ts +27 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +74 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/preflight.d.ts +35 -0
- package/dist/lib/preflight.d.ts.map +1 -0
- package/dist/lib/preflight.js +54 -0
- package/dist/lib/preflight.js.map +1 -0
- package/dist/lib/repo-binding.d.ts +43 -0
- package/dist/lib/repo-binding.d.ts.map +1 -0
- package/dist/lib/repo-binding.js +158 -0
- package/dist/lib/repo-binding.js.map +1 -0
- package/dist/lib/repo-identity.d.ts +29 -0
- package/dist/lib/repo-identity.d.ts.map +1 -0
- package/dist/lib/repo-identity.js +113 -0
- package/dist/lib/repo-identity.js.map +1 -0
- package/dist/lib/setup-hooks.d.ts +41 -0
- package/dist/lib/setup-hooks.d.ts.map +1 -0
- package/dist/lib/setup-hooks.js +276 -0
- package/dist/lib/setup-hooks.js.map +1 -0
- package/dist/lib/setup.d.ts +4 -1
- package/dist/lib/setup.d.ts.map +1 -1
- package/dist/lib/setup.js +20 -31
- package/dist/lib/setup.js.map +1 -1
- package/dist/lib/skill-batches.d.ts +16 -0
- package/dist/lib/skill-batches.d.ts.map +1 -0
- package/dist/lib/skill-batches.js +35 -0
- package/dist/lib/skill-batches.js.map +1 -0
- package/dist/lib/skill-files.d.ts +4 -0
- package/dist/lib/skill-files.d.ts.map +1 -0
- package/dist/lib/skill-files.js +36 -0
- package/dist/lib/skill-files.js.map +1 -0
- package/dist/lib/skill-fingerprint.d.ts +55 -0
- package/dist/lib/skill-fingerprint.d.ts.map +1 -0
- package/dist/lib/skill-fingerprint.js +204 -0
- package/dist/lib/skill-fingerprint.js.map +1 -0
- package/dist/lib/skills-discovery.d.ts +57 -0
- package/dist/lib/skills-discovery.d.ts.map +1 -0
- package/dist/lib/skills-discovery.js +157 -0
- package/dist/lib/skills-discovery.js.map +1 -0
- package/dist/lib/skills-sync.d.ts +66 -0
- package/dist/lib/skills-sync.d.ts.map +1 -0
- package/dist/lib/skills-sync.js +251 -0
- package/dist/lib/skills-sync.js.map +1 -0
- package/dist/lib/sync-runner.d.ts +56 -0
- package/dist/lib/sync-runner.d.ts.map +1 -0
- package/dist/lib/sync-runner.js +106 -0
- package/dist/lib/sync-runner.js.map +1 -0
- package/dist/lib/sync-state.d.ts +36 -6
- package/dist/lib/sync-state.d.ts.map +1 -1
- package/dist/lib/sync-state.js +41 -11
- package/dist/lib/sync-state.js.map +1 -1
- package/package.json +10 -5
package/dist/lib/setup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/lib/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/lib/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split skill uploads into request-sized batches. Two caps apply at once: a
|
|
3
|
+
* count (the server processes at most 25 skills per call) and a byte budget
|
|
4
|
+
* on the JSON body, because file contents now ride inline and the route's
|
|
5
|
+
* body limit is 8 MiB. A skill whose payload alone exceeds the budget still
|
|
6
|
+
* ships — as a batch of one — so the server, not the client, gets to reject it.
|
|
7
|
+
*/
|
|
8
|
+
export interface BatchLimits {
|
|
9
|
+
maxItems: number;
|
|
10
|
+
/** Budget for the encoded `{"skills":[...]}` body. */
|
|
11
|
+
maxBytes: number;
|
|
12
|
+
}
|
|
13
|
+
/** Size of one item as it will appear in the request body. */
|
|
14
|
+
export declare function encodedBytes(value: unknown): number;
|
|
15
|
+
export declare function planBatches<T>(items: T[], sizeOf: (item: T) => number, limits?: BatchLimits): T[][];
|
|
16
|
+
//# sourceMappingURL=skill-batches.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-batches.d.ts","sourceRoot":"","sources":["../../src/lib/skill-batches.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAA;IAChB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAA;CACjB;AAOD,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEnD;AAED,wBAAgB,WAAW,CAAC,CAAC,EAC3B,KAAK,EAAE,CAAC,EAAE,EACV,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,EAC3B,MAAM,GAAE,WAAkC,GACzC,CAAC,EAAE,EAAE,CAkBP"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split skill uploads into request-sized batches. Two caps apply at once: a
|
|
3
|
+
* count (the server processes at most 25 skills per call) and a byte budget
|
|
4
|
+
* on the JSON body, because file contents now ride inline and the route's
|
|
5
|
+
* body limit is 8 MiB. A skill whose payload alone exceeds the budget still
|
|
6
|
+
* ships — as a batch of one — so the server, not the client, gets to reject it.
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_BATCH_LIMITS = { maxItems: 25, maxBytes: 6 * 1024 * 1024 };
|
|
9
|
+
/** Bytes of `{"skills":[]}` — the envelope around the items. */
|
|
10
|
+
const ENVELOPE_BYTES = Buffer.byteLength('{"skills":[]}');
|
|
11
|
+
/** Size of one item as it will appear in the request body. */
|
|
12
|
+
export function encodedBytes(value) {
|
|
13
|
+
return Buffer.byteLength(JSON.stringify(value));
|
|
14
|
+
}
|
|
15
|
+
export function planBatches(items, sizeOf, limits = DEFAULT_BATCH_LIMITS) {
|
|
16
|
+
const batches = [];
|
|
17
|
+
let current = [];
|
|
18
|
+
let bytes = ENVELOPE_BYTES;
|
|
19
|
+
for (const item of items) {
|
|
20
|
+
const size = sizeOf(item);
|
|
21
|
+
const full = current.length >= limits.maxItems || bytes + size + 1 > limits.maxBytes;
|
|
22
|
+
if (current.length > 0 && full) {
|
|
23
|
+
batches.push(current);
|
|
24
|
+
current = [];
|
|
25
|
+
bytes = ENVELOPE_BYTES;
|
|
26
|
+
}
|
|
27
|
+
// Every item after the first costs its separating comma too.
|
|
28
|
+
bytes += size + (current.length > 0 ? 1 : 0);
|
|
29
|
+
current.push(item);
|
|
30
|
+
}
|
|
31
|
+
if (current.length > 0)
|
|
32
|
+
batches.push(current);
|
|
33
|
+
return batches;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=skill-batches.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-batches.js","sourceRoot":"","sources":["../../src/lib/skill-batches.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,MAAM,oBAAoB,GAAgB,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,CAAA;AAErF,gEAAgE;AAChE,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,CAAA;AAEzD,8DAA8D;AAC9D,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAA;AACjD,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,KAAU,EACV,MAA2B,EAC3B,SAAsB,oBAAoB;IAE1C,MAAM,OAAO,GAAU,EAAE,CAAA;IACzB,IAAI,OAAO,GAAQ,EAAE,CAAA;IACrB,IAAI,KAAK,GAAG,cAAc,CAAA;IAC1B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;QACzB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,IAAI,KAAK,GAAG,IAAI,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAA;QACpF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACrB,OAAO,GAAG,EAAE,CAAA;YACZ,KAAK,GAAG,cAAc,CAAA;QACxB,CAAC;QACD,6DAA6D;QAC7D,KAAK,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC5C,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACpB,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC7C,OAAO,OAAO,CAAA;AAChB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-files.d.ts","sourceRoot":"","sources":["../../src/lib/skill-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AA6B5D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAQxE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { SKILL_FILE } from './skills-discovery.js';
|
|
2
|
+
/**
|
|
3
|
+
* Turn a fingerprinted manifest entry into its wire form. Every supporting
|
|
4
|
+
* file goes up verbatim: as UTF-8 text when it is text, base64 otherwise.
|
|
5
|
+
* SKILL.md is the one exception — its bytes already ride as `frontmatterRaw`
|
|
6
|
+
* + `body`, so its manifest row carries only `path` / `size` / `sha256`.
|
|
7
|
+
*/
|
|
8
|
+
/** A NUL byte this early is the classic binary tell (ELF, woff2, images, UTF-16). */
|
|
9
|
+
const TEXT_SNIFF_BYTES = 8 * 1024;
|
|
10
|
+
/**
|
|
11
|
+
* Strict so that latin-1 or otherwise malformed bytes fall back to base64:
|
|
12
|
+
* the server hashes what it decodes, and only a faithful round-trip matches.
|
|
13
|
+
* `ignoreBOM` keeps a leading BOM in the string so it re-encodes unchanged.
|
|
14
|
+
*/
|
|
15
|
+
const utf8 = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
|
|
16
|
+
function decodeText(bytes) {
|
|
17
|
+
if (bytes.subarray(0, TEXT_SNIFF_BYTES).includes(0))
|
|
18
|
+
return undefined;
|
|
19
|
+
try {
|
|
20
|
+
return utf8.decode(bytes);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function toSkillFileUpload(entry) {
|
|
27
|
+
const { path, size, sha256, bytes } = entry;
|
|
28
|
+
if (path === SKILL_FILE)
|
|
29
|
+
return { path, size, sha256 };
|
|
30
|
+
const text = decodeText(bytes);
|
|
31
|
+
if (text === undefined) {
|
|
32
|
+
return { path, size, sha256, content: bytes.toString('base64'), encoding: 'base64' };
|
|
33
|
+
}
|
|
34
|
+
return { path, size, sha256, content: text, encoding: 'utf8' };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=skill-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-files.js","sourceRoot":"","sources":["../../src/lib/skill-files.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAElD;;;;;GAKG;AAEH,qFAAqF;AACrF,MAAM,gBAAgB,GAAG,CAAC,GAAG,IAAI,CAAA;AAEjC;;;;GAIG;AACH,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;AAEvE,SAAS,UAAU,CAAC,KAAa;IAC/B,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,SAAS,CAAA;IACrE,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAqB;IACrD,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAA;IAC3C,IAAI,IAAI,KAAK,UAAU;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;IACtD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;IAC9B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAA;IACtF,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;AAChE,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content fingerprint for a skill folder plus a minimal SKILL.md front-matter
|
|
3
|
+
* parser. The fingerprint covers the whole tree (skills ship helper scripts
|
|
4
|
+
* and templates next to SKILL.md), so any edit anywhere in the folder is a
|
|
5
|
+
* change; the manifest it is built from travels with the upload so a later
|
|
6
|
+
* write-back can tell which file drifted.
|
|
7
|
+
*/
|
|
8
|
+
export interface SkillFileEntry {
|
|
9
|
+
/** Path relative to the skill directory, posix separators. */
|
|
10
|
+
path: string;
|
|
11
|
+
size: number;
|
|
12
|
+
sha256: string;
|
|
13
|
+
/** The file's bytes, kept so the upload can carry them (see skill-files.ts). */
|
|
14
|
+
bytes: Buffer;
|
|
15
|
+
}
|
|
16
|
+
export interface SkillFingerprint {
|
|
17
|
+
/** sha256 over the sorted manifest (`<path>\0<sha256>\n` per file). */
|
|
18
|
+
treeHash: string;
|
|
19
|
+
files: SkillFileEntry[];
|
|
20
|
+
/** Newest mtime across the tree — the source-side "last modified" time. */
|
|
21
|
+
modifiedAt: Date;
|
|
22
|
+
/** Files left out of the manifest (and so never uploaded) because they exceed MAX_FILE_BYTES. */
|
|
23
|
+
skippedPaths: string[];
|
|
24
|
+
/** Files hashed (`files` may be a prefix of them when the tree is larger than MAX_FILES). */
|
|
25
|
+
fileCount: number;
|
|
26
|
+
/** The walk stopped early (WALK_FILE_BUDGET / WALK_BYTE_BUDGET): the hash covers a prefix of the tree. */
|
|
27
|
+
truncated: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare function fingerprintSkillDir(dir: string): SkillFingerprint;
|
|
30
|
+
export interface ParsedSkillMarkdown {
|
|
31
|
+
/** Top-level front-matter fields, values as written (block scalars joined). */
|
|
32
|
+
frontmatter: Record<string, string>;
|
|
33
|
+
/**
|
|
34
|
+
* The verbatim text between the opening `---` line and the closing `---`
|
|
35
|
+
* line (CRLF normalised, nothing else touched); absent when the file has
|
|
36
|
+
* no front-matter block. The server parses this itself, so nothing the
|
|
37
|
+
* flat `frontmatter` map cannot express is lost.
|
|
38
|
+
*/
|
|
39
|
+
frontmatterRaw?: string;
|
|
40
|
+
/** Markdown after the front-matter block (the whole file when there is none). */
|
|
41
|
+
body: string;
|
|
42
|
+
name?: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
/** Things the author probably wants to know about (e.g. an unterminated front-matter block). */
|
|
45
|
+
warnings: string[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Parse SKILL.md front-matter without a YAML dependency. Handles what skill
|
|
49
|
+
* files actually use: `key: value` scalars (optionally quoted), `|` / `>`
|
|
50
|
+
* block scalars, and nested blocks (`metadata:` maps, lists), which are kept
|
|
51
|
+
* as their raw indented text. Anything unparseable is left out rather than
|
|
52
|
+
* failing the sync.
|
|
53
|
+
*/
|
|
54
|
+
export declare function parseSkillMarkdown(raw: string): ParsedSkillMarkdown;
|
|
55
|
+
//# sourceMappingURL=skill-fingerprint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-fingerprint.d.ts","sourceRoot":"","sources":["../../src/lib/skill-fingerprint.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AAEH,MAAM,WAAW,cAAc;IAC7B,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,cAAc,EAAE,CAAA;IACvB,2EAA2E;IAC3E,UAAU,EAAE,IAAI,CAAA;IAChB,iGAAiG;IACjG,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,6FAA6F;IAC7F,SAAS,EAAE,MAAM,CAAA;IACjB,0GAA0G;IAC1G,SAAS,EAAE,OAAO,CAAA;CACnB;AAgBD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAgEjE;AAuBD,MAAM,WAAW,mBAAmB;IAClC,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gGAAgG;IAChG,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAqCnE"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readdirSync, readFileSync, realpathSync, statSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const IGNORED_DIRS = new Set(['.git', 'node_modules', '__pycache__', '.venv', 'venv']);
|
|
5
|
+
const IGNORED_FILES = new Set(['.DS_Store', 'Thumbs.db']);
|
|
6
|
+
/** Anything larger is not skill content (binaries, datasets); left out of the manifest. */
|
|
7
|
+
const MAX_FILE_BYTES = 1024 * 1024;
|
|
8
|
+
/** Bound on manifest size; the server rejects more. */
|
|
9
|
+
const MAX_FILES = 500;
|
|
10
|
+
/**
|
|
11
|
+
* Hard stop for the walk so a skill that symlinks into a large tree cannot
|
|
12
|
+
* stall the session-start hook: after this many files or bytes the walk ends
|
|
13
|
+
* and the fingerprint is flagged `truncated`.
|
|
14
|
+
*/
|
|
15
|
+
const WALK_FILE_BUDGET = 2000;
|
|
16
|
+
const WALK_BYTE_BUDGET = 32 * 1024 * 1024;
|
|
17
|
+
export function fingerprintSkillDir(dir) {
|
|
18
|
+
const files = [];
|
|
19
|
+
const skippedPaths = [];
|
|
20
|
+
let modifiedAt = 0;
|
|
21
|
+
let bytes = 0;
|
|
22
|
+
let truncated = false;
|
|
23
|
+
const seenDirs = new Set();
|
|
24
|
+
const overBudget = () => {
|
|
25
|
+
if (files.length < WALK_FILE_BUDGET && bytes < WALK_BYTE_BUDGET)
|
|
26
|
+
return false;
|
|
27
|
+
truncated = true;
|
|
28
|
+
return true;
|
|
29
|
+
};
|
|
30
|
+
const walk = (current, prefix) => {
|
|
31
|
+
const real = canonical(current);
|
|
32
|
+
if (seenDirs.has(real))
|
|
33
|
+
return; // symlink cycle
|
|
34
|
+
seenDirs.add(real);
|
|
35
|
+
for (const name of listSorted(current)) {
|
|
36
|
+
if (overBudget())
|
|
37
|
+
return;
|
|
38
|
+
const full = join(current, name);
|
|
39
|
+
let stat;
|
|
40
|
+
try {
|
|
41
|
+
stat = statSync(full);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
continue; // dangling symlink or vanished file
|
|
45
|
+
}
|
|
46
|
+
if (stat.isDirectory()) {
|
|
47
|
+
if (!IGNORED_DIRS.has(name))
|
|
48
|
+
walk(full, `${prefix}${name}/`);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (!stat.isFile() || IGNORED_FILES.has(name))
|
|
52
|
+
continue;
|
|
53
|
+
if (stat.size > MAX_FILE_BYTES) {
|
|
54
|
+
skippedPaths.push(`${prefix}${name}`);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
let content;
|
|
58
|
+
try {
|
|
59
|
+
content = readFileSync(full);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
continue; // unreadable helper file: SKILL.md itself is read (and fails loudly) by the caller
|
|
63
|
+
}
|
|
64
|
+
bytes += content.length;
|
|
65
|
+
modifiedAt = Math.max(modifiedAt, stat.mtimeMs);
|
|
66
|
+
files.push({
|
|
67
|
+
path: `${prefix}${name}`,
|
|
68
|
+
size: stat.size,
|
|
69
|
+
sha256: sha256(content),
|
|
70
|
+
bytes: content,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
walk(dir, '');
|
|
75
|
+
files.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
76
|
+
const manifest = files.map((f) => `${f.path}\0${f.sha256}\n`).join('');
|
|
77
|
+
return {
|
|
78
|
+
treeHash: sha256(Buffer.from(manifest)),
|
|
79
|
+
files: files.slice(0, MAX_FILES),
|
|
80
|
+
modifiedAt: new Date(modifiedAt || Date.now()),
|
|
81
|
+
skippedPaths,
|
|
82
|
+
fileCount: files.length,
|
|
83
|
+
truncated,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** Directory entries in a stable order; an unreadable directory contributes nothing. */
|
|
87
|
+
function listSorted(dir) {
|
|
88
|
+
try {
|
|
89
|
+
return readdirSync(dir).sort();
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function sha256(content) {
|
|
96
|
+
return createHash('sha256').update(content).digest('hex');
|
|
97
|
+
}
|
|
98
|
+
function canonical(path) {
|
|
99
|
+
try {
|
|
100
|
+
return realpathSync(path);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return path;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Parse SKILL.md front-matter without a YAML dependency. Handles what skill
|
|
108
|
+
* files actually use: `key: value` scalars (optionally quoted), `|` / `>`
|
|
109
|
+
* block scalars, and nested blocks (`metadata:` maps, lists), which are kept
|
|
110
|
+
* as their raw indented text. Anything unparseable is left out rather than
|
|
111
|
+
* failing the sync.
|
|
112
|
+
*/
|
|
113
|
+
export function parseSkillMarkdown(raw) {
|
|
114
|
+
const text = raw.replace(/\r\n/g, '\n');
|
|
115
|
+
const frontmatter = {};
|
|
116
|
+
const warnings = [];
|
|
117
|
+
if (!text.startsWith('---\n'))
|
|
118
|
+
return { frontmatter, body: text, warnings };
|
|
119
|
+
// The closing line must be exactly `---` (trailing whitespace allowed).
|
|
120
|
+
const close = /\n---[ \t]*(?:\n|$)/.exec(text.slice(4));
|
|
121
|
+
const end = close ? close.index + 4 : -1;
|
|
122
|
+
if (end === -1) {
|
|
123
|
+
warnings.push('front-matter block is not terminated by a `---` line; treating the whole file as body');
|
|
124
|
+
return { frontmatter, body: text, warnings };
|
|
125
|
+
}
|
|
126
|
+
const block = text.slice(4, end);
|
|
127
|
+
const afterClose = text.indexOf('\n', end + 1);
|
|
128
|
+
const body = afterClose === -1 ? '' : text.slice(afterClose + 1);
|
|
129
|
+
const lines = block.split('\n');
|
|
130
|
+
for (let i = 0; i < lines.length; i++) {
|
|
131
|
+
const line = lines[i] ?? '';
|
|
132
|
+
const match = /^([A-Za-z0-9_-]+):(?:[ \t]+(.*))?$/.exec(line);
|
|
133
|
+
if (!match)
|
|
134
|
+
continue;
|
|
135
|
+
const key = match[1] ?? '';
|
|
136
|
+
const inline = (match[2] ?? '').trim();
|
|
137
|
+
const { value, consumed } = readValue(inline, lines, i + 1);
|
|
138
|
+
frontmatter[key] = value;
|
|
139
|
+
i += consumed;
|
|
140
|
+
}
|
|
141
|
+
const name = frontmatter['name']?.trim() || undefined;
|
|
142
|
+
const description = frontmatter['description']?.trim() || undefined;
|
|
143
|
+
if (!name)
|
|
144
|
+
warnings.push('front-matter has no `name`; using the folder name');
|
|
145
|
+
if (!description)
|
|
146
|
+
warnings.push('front-matter has no `description`');
|
|
147
|
+
return { frontmatter, frontmatterRaw: block, body, name, description, warnings };
|
|
148
|
+
}
|
|
149
|
+
/** Resolve a front-matter value: inline scalar, block scalar, or nested block. */
|
|
150
|
+
function readValue(inline, lines, from) {
|
|
151
|
+
const continuation = collectIndented(lines, from);
|
|
152
|
+
if (inline === '|' || inline === '|-' || inline === '>' || inline === '>-') {
|
|
153
|
+
const stripped = dedent(continuation.lines);
|
|
154
|
+
const joined = inline.startsWith('|') ? stripped.join('\n') : foldLines(stripped);
|
|
155
|
+
return { value: joined.trim(), consumed: continuation.count };
|
|
156
|
+
}
|
|
157
|
+
if (inline === '' && continuation.count > 0) {
|
|
158
|
+
return { value: dedent(continuation.lines).join('\n'), consumed: continuation.count };
|
|
159
|
+
}
|
|
160
|
+
return { value: scalar(inline), consumed: 0 };
|
|
161
|
+
}
|
|
162
|
+
function collectIndented(lines, from) {
|
|
163
|
+
const out = [];
|
|
164
|
+
let i = from;
|
|
165
|
+
while (i < lines.length) {
|
|
166
|
+
const line = lines[i] ?? '';
|
|
167
|
+
if (line.trim() !== '' && !/^[ \t]/.test(line))
|
|
168
|
+
break;
|
|
169
|
+
out.push(line);
|
|
170
|
+
i++;
|
|
171
|
+
}
|
|
172
|
+
// Trailing blank lines belong to the next key, not the block.
|
|
173
|
+
while (out.length > 0 && (out[out.length - 1] ?? '').trim() === '')
|
|
174
|
+
out.pop();
|
|
175
|
+
return { lines: out, count: out.length };
|
|
176
|
+
}
|
|
177
|
+
function dedent(lines) {
|
|
178
|
+
const indents = lines
|
|
179
|
+
.filter((l) => l.trim() !== '')
|
|
180
|
+
.map((l) => /^[ \t]*/.exec(l)?.[0].length ?? 0);
|
|
181
|
+
const min = indents.length > 0 ? Math.min(...indents) : 0;
|
|
182
|
+
return lines.map((l) => l.slice(Math.min(min, /^[ \t]*/.exec(l)?.[0].length ?? 0)));
|
|
183
|
+
}
|
|
184
|
+
/** YAML folded style: newlines become spaces, blank lines become newlines. */
|
|
185
|
+
function foldLines(lines) {
|
|
186
|
+
return lines
|
|
187
|
+
.join('\n')
|
|
188
|
+
.split(/\n{2,}/)
|
|
189
|
+
.map((paragraph) => paragraph.split('\n').join(' ').trim())
|
|
190
|
+
.join('\n');
|
|
191
|
+
}
|
|
192
|
+
/** Inline scalar: strip a trailing comment and surrounding quotes. */
|
|
193
|
+
function scalar(value) {
|
|
194
|
+
const doubleQuoted = /^"((?:[^"\\]|\\.)*)"(?:\s+#.*)?$/.exec(value);
|
|
195
|
+
if (doubleQuoted) {
|
|
196
|
+
return (doubleQuoted[1] ?? '').replace(/\\"/g, '"').replace(/\\n/g, '\n').replace(/\\\\/g, '\\');
|
|
197
|
+
}
|
|
198
|
+
const singleQuoted = /^'((?:[^']|'')*)'(?:\s+#.*)?$/.exec(value);
|
|
199
|
+
if (singleQuoted)
|
|
200
|
+
return (singleQuoted[1] ?? '').replace(/''/g, "'");
|
|
201
|
+
const hash = value.indexOf(' #');
|
|
202
|
+
return (hash === -1 ? value : value.slice(0, hash)).trim();
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=skill-fingerprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-fingerprint.js","sourceRoot":"","sources":["../../src/lib/skill-fingerprint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where coding-agent harnesses look for skills. A skill is a directory that
|
|
3
|
+
* directly contains a `SKILL.md` (the Agent Skills format, agentskills.io).
|
|
4
|
+
* Every harness reads its own `.<tool>/skills` directory; most also read
|
|
5
|
+
* `.agents/skills` (the cross-client convention) and `.claude/skills` for
|
|
6
|
+
* compatibility, so the same folder is routinely reachable from several of
|
|
7
|
+
* these — often through symlinks. Discovery de-duplicates by real path.
|
|
8
|
+
*/
|
|
9
|
+
export type SkillScope = 'user' | 'project';
|
|
10
|
+
export interface SkillLocation {
|
|
11
|
+
/** Harness that reads this directory. */
|
|
12
|
+
harness: string;
|
|
13
|
+
/** Directory relative to the scope root (home dir or repo root), posix separators. */
|
|
14
|
+
dir: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Order is load-bearing: when a skill's real directory lies outside every
|
|
18
|
+
* scanned location (a symlink to somewhere else) the first location that
|
|
19
|
+
* sees it names its `relativePath`, which becomes its ledger key and server
|
|
20
|
+
* identity. Append new harnesses at the end; never reorder.
|
|
21
|
+
*/
|
|
22
|
+
/** User-level skill directories, relative to $HOME. */
|
|
23
|
+
export declare const USER_SKILL_LOCATIONS: readonly SkillLocation[];
|
|
24
|
+
/** Repo-level skill directories, relative to the repository root. */
|
|
25
|
+
export declare const PROJECT_SKILL_LOCATIONS: readonly SkillLocation[];
|
|
26
|
+
export declare const SKILL_FILE = "SKILL.md";
|
|
27
|
+
export interface DiscoveredSkill {
|
|
28
|
+
scope: SkillScope;
|
|
29
|
+
/** Absolute path of the skill directory as found (may sit behind a symlink). */
|
|
30
|
+
dir: string;
|
|
31
|
+
/** Canonical absolute path (symlinks resolved) — the de-dupe key within a scope. */
|
|
32
|
+
realDir: string;
|
|
33
|
+
/** Skill directory relative to the scope root, posix separators. */
|
|
34
|
+
relativePath: string;
|
|
35
|
+
/** Every relative path the skill was found at (symlink aliases), `relativePath` first. */
|
|
36
|
+
locations: string[];
|
|
37
|
+
/** Harnesses whose directories contain the skill. */
|
|
38
|
+
harnesses: string[];
|
|
39
|
+
/** Directory basename; the spec says the frontmatter `name` must match it. */
|
|
40
|
+
folderName: string;
|
|
41
|
+
}
|
|
42
|
+
export interface DiscoverSkillsOptions {
|
|
43
|
+
scope: SkillScope;
|
|
44
|
+
/** Home directory (user scope) or repository root (project scope). */
|
|
45
|
+
rootDir: string;
|
|
46
|
+
/** Defaults to the scope's built-in location table. */
|
|
47
|
+
locations?: readonly SkillLocation[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Discover skills under one scope root. Skill directories sit directly under
|
|
51
|
+
* a location (`.claude/skills/<name>/SKILL.md`); up to two levels of grouping
|
|
52
|
+
* are allowed so grouped layouts (`~/.claude/skills/synced/<uuid>/<name>`,
|
|
53
|
+
* Gemini's nested dirs) are found too. Dot-directories are skipped
|
|
54
|
+
* (`~/.codex/skills/.system` holds Codex's bundled skills, not the user's).
|
|
55
|
+
*/
|
|
56
|
+
export declare function discoverSkills(options: DiscoverSkillsOptions): DiscoveredSkill[];
|
|
57
|
+
//# sourceMappingURL=skills-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills-discovery.d.ts","sourceRoot":"","sources":["../../src/lib/skills-discovery.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AAEH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,CAAA;AAE3C,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAA;IACf,sFAAsF;IACtF,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;;;GAKG;AAEH,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,EAAE,SAAS,aAAa,EAcxD,CAAA;AAED,qEAAqE;AACrE,eAAO,MAAM,uBAAuB,EAAE,SAAS,aAAa,EAa3D,CAAA;AAED,eAAO,MAAM,UAAU,aAAa,CAAA;AAEpC,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,UAAU,CAAA;IACjB,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAA;IACX,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAA;IACf,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAA;IACpB,0FAA0F;IAC1F,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,qDAAqD;IACrD,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,UAAU,CAAA;IACjB,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAA;IACf,uDAAuD;IACvD,SAAS,CAAC,EAAE,SAAS,aAAa,EAAE,CAAA;CACrC;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,eAAe,EAAE,CAyChF"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { existsSync, readdirSync, realpathSync, statSync } from 'node:fs';
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Order is load-bearing: when a skill's real directory lies outside every
|
|
5
|
+
* scanned location (a symlink to somewhere else) the first location that
|
|
6
|
+
* sees it names its `relativePath`, which becomes its ledger key and server
|
|
7
|
+
* identity. Append new harnesses at the end; never reorder.
|
|
8
|
+
*/
|
|
9
|
+
/** User-level skill directories, relative to $HOME. */
|
|
10
|
+
export const USER_SKILL_LOCATIONS = [
|
|
11
|
+
{ harness: 'claude', dir: '.claude/skills' },
|
|
12
|
+
{ harness: 'agents', dir: '.agents/skills' },
|
|
13
|
+
{ harness: 'codex', dir: '.codex/skills' },
|
|
14
|
+
{ harness: 'cursor', dir: '.cursor/skills' },
|
|
15
|
+
{ harness: 'gemini', dir: '.gemini/skills' },
|
|
16
|
+
{ harness: 'copilot', dir: '.copilot/skills' },
|
|
17
|
+
{ harness: 'opencode', dir: '.config/opencode/skills' },
|
|
18
|
+
{ harness: 'amp', dir: '.config/agents/skills' },
|
|
19
|
+
{ harness: 'windsurf', dir: '.codeium/windsurf/skills' },
|
|
20
|
+
{ harness: 'grok', dir: '.grok/skills' },
|
|
21
|
+
{ harness: 'kiro', dir: '.kiro/skills' },
|
|
22
|
+
{ harness: 'cline', dir: '.cline/skills' },
|
|
23
|
+
{ harness: 'factory', dir: '.factory/skills' },
|
|
24
|
+
];
|
|
25
|
+
/** Repo-level skill directories, relative to the repository root. */
|
|
26
|
+
export const PROJECT_SKILL_LOCATIONS = [
|
|
27
|
+
{ harness: 'claude', dir: '.claude/skills' },
|
|
28
|
+
{ harness: 'agents', dir: '.agents/skills' },
|
|
29
|
+
{ harness: 'codex', dir: '.codex/skills' },
|
|
30
|
+
{ harness: 'cursor', dir: '.cursor/skills' },
|
|
31
|
+
{ harness: 'gemini', dir: '.gemini/skills' },
|
|
32
|
+
{ harness: 'copilot', dir: '.github/skills' },
|
|
33
|
+
{ harness: 'opencode', dir: '.opencode/skills' },
|
|
34
|
+
{ harness: 'windsurf', dir: '.windsurf/skills' },
|
|
35
|
+
{ harness: 'grok', dir: '.grok/skills' },
|
|
36
|
+
{ harness: 'kiro', dir: '.kiro/skills' },
|
|
37
|
+
{ harness: 'cline', dir: '.cline/skills' },
|
|
38
|
+
{ harness: 'factory', dir: '.factory/skills' },
|
|
39
|
+
];
|
|
40
|
+
export const SKILL_FILE = 'SKILL.md';
|
|
41
|
+
/**
|
|
42
|
+
* Discover skills under one scope root. Skill directories sit directly under
|
|
43
|
+
* a location (`.claude/skills/<name>/SKILL.md`); up to two levels of grouping
|
|
44
|
+
* are allowed so grouped layouts (`~/.claude/skills/synced/<uuid>/<name>`,
|
|
45
|
+
* Gemini's nested dirs) are found too. Dot-directories are skipped
|
|
46
|
+
* (`~/.codex/skills/.system` holds Codex's bundled skills, not the user's).
|
|
47
|
+
*/
|
|
48
|
+
export function discoverSkills(options) {
|
|
49
|
+
const { scope, rootDir } = options;
|
|
50
|
+
const locations = options.locations ?? (scope === 'user' ? USER_SKILL_LOCATIONS : PROJECT_SKILL_LOCATIONS);
|
|
51
|
+
const byRealDir = new Map();
|
|
52
|
+
// A sighting is the real location (not a symlink alias) when its canonical
|
|
53
|
+
// path is exactly the canonical root plus its relative path.
|
|
54
|
+
const canonicalRoot = canonical(rootDir);
|
|
55
|
+
const primaryIsReal = new Map();
|
|
56
|
+
for (const location of locations) {
|
|
57
|
+
const base = join(rootDir, ...location.dir.split('/'));
|
|
58
|
+
if (!isDirectory(base))
|
|
59
|
+
continue;
|
|
60
|
+
for (const relative of listSkillDirs(base)) {
|
|
61
|
+
const dir = join(base, ...relative.split('/'));
|
|
62
|
+
const realDir = canonical(dir);
|
|
63
|
+
const relativePath = `${location.dir}/${relative}`;
|
|
64
|
+
const isReal = realDir === join(canonicalRoot, ...relativePath.split('/'));
|
|
65
|
+
const existing = byRealDir.get(realDir);
|
|
66
|
+
if (existing) {
|
|
67
|
+
if (isReal && !primaryIsReal.get(realDir)) {
|
|
68
|
+
promote(existing, { dir, relativePath });
|
|
69
|
+
primaryIsReal.set(realDir, true);
|
|
70
|
+
}
|
|
71
|
+
addSighting(existing, relativePath, location.harness);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
primaryIsReal.set(realDir, isReal);
|
|
75
|
+
byRealDir.set(realDir, {
|
|
76
|
+
scope,
|
|
77
|
+
dir,
|
|
78
|
+
realDir,
|
|
79
|
+
relativePath,
|
|
80
|
+
locations: [relativePath],
|
|
81
|
+
harnesses: [location.harness],
|
|
82
|
+
folderName: basename(realDir),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return [...byRealDir.values()].sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
87
|
+
}
|
|
88
|
+
/** Record another path / harness the same skill directory was seen through. */
|
|
89
|
+
function addSighting(skill, relativePath, harness) {
|
|
90
|
+
if (!skill.locations.includes(relativePath))
|
|
91
|
+
skill.locations.push(relativePath);
|
|
92
|
+
if (!skill.harnesses.includes(harness))
|
|
93
|
+
skill.harnesses.push(harness);
|
|
94
|
+
}
|
|
95
|
+
/** Make the real directory (not a symlink alias) the canonical path of a skill. */
|
|
96
|
+
function promote(skill, real) {
|
|
97
|
+
skill.dir = real.dir;
|
|
98
|
+
skill.relativePath = real.relativePath;
|
|
99
|
+
skill.locations = [real.relativePath, ...skill.locations.filter((l) => l !== real.relativePath)];
|
|
100
|
+
}
|
|
101
|
+
/** Grouping levels scanned below a location root (`<group>/<subgroup>/<name>`). */
|
|
102
|
+
const MAX_GROUP_DEPTH = 2;
|
|
103
|
+
/**
|
|
104
|
+
* Relative skill dirs (posix) under a location root: `<name>`, `<group>/<name>`
|
|
105
|
+
* or `<group>/<subgroup>/<name>`. A skill directory is a leaf — its subfolders
|
|
106
|
+
* are its files, never nested skills — and the walk stops at `MAX_GROUP_DEPTH`.
|
|
107
|
+
*/
|
|
108
|
+
function listSkillDirs(base, depth = 0) {
|
|
109
|
+
const found = [];
|
|
110
|
+
for (const entry of listVisibleDirs(base)) {
|
|
111
|
+
const dir = join(base, entry);
|
|
112
|
+
if (hasSkillFile(dir)) {
|
|
113
|
+
found.push(entry);
|
|
114
|
+
}
|
|
115
|
+
else if (depth < MAX_GROUP_DEPTH) {
|
|
116
|
+
for (const nested of listSkillDirs(dir, depth + 1))
|
|
117
|
+
found.push(`${entry}/${nested}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return found;
|
|
121
|
+
}
|
|
122
|
+
function listVisibleDirs(dir) {
|
|
123
|
+
try {
|
|
124
|
+
return readdirSync(dir)
|
|
125
|
+
.filter((name) => !name.startsWith('.'))
|
|
126
|
+
.filter((name) => isDirectory(join(dir, name)))
|
|
127
|
+
.sort();
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function hasSkillFile(dir) {
|
|
134
|
+
try {
|
|
135
|
+
return statSync(join(dir, SKILL_FILE)).isFile();
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function isDirectory(path) {
|
|
142
|
+
try {
|
|
143
|
+
return existsSync(path) && statSync(path).isDirectory();
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function canonical(path) {
|
|
150
|
+
try {
|
|
151
|
+
return realpathSync(path);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return path;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=skills-discovery.js.map
|