@hasna/skills 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -0
- package/bin/index.js +809 -592
- package/bin/mcp.js +23 -2
- package/bin/migrate.js +1 -1
- package/bin/server.js +2 -3
- package/bin/worker.js +2 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +556 -361
- package/dist/lib/agent-discovery-bytes.d.ts +6 -0
- package/dist/lib/agent-discovery-directories.d.ts +8 -0
- package/dist/lib/agent-discovery.d.ts +7 -0
- package/dist/lib/agent-integration.d.ts +2 -0
- package/dist/lib/agent-policy-limits.d.ts +5 -0
- package/dist/sdk/index.js +579 -386
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10421,7 +10421,7 @@ function getAllTags() {
|
|
|
10421
10421
|
}
|
|
10422
10422
|
// src/lib/installer.ts
|
|
10423
10423
|
import { existsSync as existsSync13, readFileSync as readFileSync14, rmSync as rmSync3 } from "fs";
|
|
10424
|
-
import { dirname as
|
|
10424
|
+
import { dirname as dirname11, join as join19 } from "path";
|
|
10425
10425
|
import { homedir as homedir4 } from "os";
|
|
10426
10426
|
import { fileURLToPath } from "url";
|
|
10427
10427
|
|
|
@@ -10429,7 +10429,7 @@ import { fileURLToPath } from "url";
|
|
|
10429
10429
|
import {
|
|
10430
10430
|
cpSync as cpSync3,
|
|
10431
10431
|
existsSync as existsSync11,
|
|
10432
|
-
lstatSync as
|
|
10432
|
+
lstatSync as lstatSync11,
|
|
10433
10433
|
mkdirSync as mkdirSync6,
|
|
10434
10434
|
mkdtempSync as mkdtempSync2,
|
|
10435
10435
|
readFileSync as readFileSync12,
|
|
@@ -10440,12 +10440,12 @@ import {
|
|
|
10440
10440
|
writeFileSync as writeFileSync6
|
|
10441
10441
|
} from "fs";
|
|
10442
10442
|
import { homedir as homedir3 } from "os";
|
|
10443
|
-
import { basename as basename3, dirname as
|
|
10443
|
+
import { basename as basename3, dirname as dirname10, join as join17 } from "path";
|
|
10444
10444
|
|
|
10445
10445
|
// src/lib/agent-integration.ts
|
|
10446
|
-
import { createHash as
|
|
10447
|
-
import { existsSync as existsSync10, lstatSync as
|
|
10448
|
-
import { dirname as
|
|
10446
|
+
import { createHash as createHash8, randomUUID as randomUUID2 } from "crypto";
|
|
10447
|
+
import { existsSync as existsSync10, lstatSync as lstatSync10, statSync as statSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync11, readdirSync as readdirSync10, opendirSync as opendirSync3, readlinkSync, realpathSync as realpathSync4, renameSync as renameSync3, rmdirSync, writeFileSync as writeFileSync5, unlinkSync as unlinkSync2, chmodSync, openSync as openSync4, closeSync as closeSync4, fsyncSync, fstatSync as fstatSync4, readSync as readSync4, constants as constants4, linkSync } from "fs";
|
|
10448
|
+
import { dirname as dirname9, join as join16, resolve as resolve8, relative as relative4, isAbsolute as isAbsolute7, sep as sep4 } from "path";
|
|
10449
10449
|
import { homedir as homedir2 } from "os";
|
|
10450
10450
|
|
|
10451
10451
|
// src/lib/managed-policy.ts
|
|
@@ -11336,7 +11336,7 @@ function readSkillSession(sessionId, options = {}) {
|
|
|
11336
11336
|
}
|
|
11337
11337
|
|
|
11338
11338
|
// src/lib/agent-policy-limits.ts
|
|
11339
|
-
var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRoots: 512, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
|
|
11339
|
+
var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRawSourceBytes: 64 * 1024 * 1024, discoveryRawTotalBytes: 256 * 1024 * 1024, discoveryRoots: 512, discoveryDirectories: 64, discoveryDirectoryEntries: 20000, discoveryDirectoryBytes: 8 * 1024 * 1024, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
|
|
11340
11340
|
function object(value) {
|
|
11341
11341
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
11342
11342
|
}
|
|
@@ -11400,12 +11400,20 @@ function assertAgentPolicyCollections(policy) {
|
|
|
11400
11400
|
requireBound(object(source));
|
|
11401
11401
|
text(source.path);
|
|
11402
11402
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
11403
|
+
if (source.hashMode !== undefined)
|
|
11404
|
+
requireBound(source.hashMode === "bytes" && source.format === undefined && source.fields === undefined);
|
|
11403
11405
|
if (source.format !== undefined)
|
|
11404
11406
|
requireBound(["json", "toml", "yaml"].includes(source.format));
|
|
11405
11407
|
if (source.fields !== undefined)
|
|
11406
11408
|
for (const field of array(source.fields, AGENT_POLICY_LIMITS.fields))
|
|
11407
11409
|
text(field, 256);
|
|
11408
11410
|
}
|
|
11411
|
+
if (value.directories !== undefined)
|
|
11412
|
+
for (const directory of array(value.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
|
|
11413
|
+
requireBound(object(directory));
|
|
11414
|
+
text(directory.path);
|
|
11415
|
+
requireBound(directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256));
|
|
11416
|
+
}
|
|
11409
11417
|
if (value.builtinNames !== undefined)
|
|
11410
11418
|
for (const name of array(value.builtinNames, AGENT_POLICY_LIMITS.builtinNames))
|
|
11411
11419
|
text(name, 128);
|
|
@@ -11534,9 +11542,9 @@ function isOwnedCliBridge(path, expectedPaths) {
|
|
|
11534
11542
|
}
|
|
11535
11543
|
|
|
11536
11544
|
// src/lib/agent-discovery.ts
|
|
11537
|
-
import { createHash as
|
|
11538
|
-
import { lstatSync as
|
|
11539
|
-
import { dirname as
|
|
11545
|
+
import { createHash as createHash7 } from "crypto";
|
|
11546
|
+
import { lstatSync as lstatSync9, readFileSync as readFileSync10, readdirSync as readdirSync9, realpathSync as realpathSync3 } from "fs";
|
|
11547
|
+
import { dirname as dirname8, isAbsolute as isAbsolute6, join as join15, resolve as resolve7, sep as sep3 } from "path";
|
|
11540
11548
|
|
|
11541
11549
|
// ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/index.js
|
|
11542
11550
|
var composer = require_composer();
|
|
@@ -11654,6 +11662,10 @@ function configureHermesHooks(text2, supervisor, previous) {
|
|
|
11654
11662
|
function assertHermesEnvironment(home) {
|
|
11655
11663
|
if (process.env.TERMINAL_CWD)
|
|
11656
11664
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes TERMINAL_CWD requires a separately reviewed effective project; unset it before using this adapter");
|
|
11665
|
+
for (const variable of ["HERMES_BUNDLED_PLUGINS", "HERMES_BUNDLED_SKILLS"]) {
|
|
11666
|
+
if (process.env[variable])
|
|
11667
|
+
throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
|
|
11668
|
+
}
|
|
11657
11669
|
const selected = process.env.HERMES_HOME?.trim();
|
|
11658
11670
|
if (selected && (!isAbsolute3(selected) || resolve4(selected) !== join13(resolve4(home), ".hermes")))
|
|
11659
11671
|
throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
|
|
@@ -11745,8 +11757,147 @@ if (failed) process.exitCode = 2;
|
|
|
11745
11757
|
`;
|
|
11746
11758
|
}
|
|
11747
11759
|
|
|
11760
|
+
// src/lib/agent-discovery-directories.ts
|
|
11761
|
+
import { createHash as createHash5 } from "crypto";
|
|
11762
|
+
import { lstatSync as lstatSync7, opendirSync as opendirSync2 } from "fs";
|
|
11763
|
+
import { dirname as dirname6, isAbsolute as isAbsolute4, join as join14, resolve as resolve5 } from "path";
|
|
11764
|
+
function need(value, message) {
|
|
11765
|
+
if (!value)
|
|
11766
|
+
throw new Error(message);
|
|
11767
|
+
}
|
|
11768
|
+
function safe(path) {
|
|
11769
|
+
need(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute4(path) && resolve5(path) === path && !path.includes("\x00"), "Expected a canonical absolute discovery directory path");
|
|
11770
|
+
for (let cursor = path;; cursor = dirname6(cursor)) {
|
|
11771
|
+
need(!lstatSync7(cursor, { throwIfNoEntry: false })?.isSymbolicLink(), "Refusing symlink native discovery directory");
|
|
11772
|
+
if (dirname6(cursor) === cursor)
|
|
11773
|
+
break;
|
|
11774
|
+
}
|
|
11775
|
+
}
|
|
11776
|
+
function same(before, after) {
|
|
11777
|
+
return Boolean(after && after.isDirectory() && before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs);
|
|
11778
|
+
}
|
|
11779
|
+
function capture(path, budget) {
|
|
11780
|
+
safe(path);
|
|
11781
|
+
const root = lstatSync7(path, { throwIfNoEntry: false });
|
|
11782
|
+
if (!root)
|
|
11783
|
+
return { path, sha256: null };
|
|
11784
|
+
need(root.isDirectory(), "Unsupported native discovery directory input");
|
|
11785
|
+
const hash2 = createHash5("sha256");
|
|
11786
|
+
function walk2(current, relative4, before, depth) {
|
|
11787
|
+
need(depth <= 64, "Native discovery directory depth limit exceeded");
|
|
11788
|
+
const names = [], directory = opendirSync2(current);
|
|
11789
|
+
try {
|
|
11790
|
+
for (let entry = directory.readSync();entry; entry = directory.readSync()) {
|
|
11791
|
+
need(++budget.entries <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Native discovery directory entry limit exceeded");
|
|
11792
|
+
names.push(entry.name);
|
|
11793
|
+
}
|
|
11794
|
+
} finally {
|
|
11795
|
+
directory.closeSync();
|
|
11796
|
+
}
|
|
11797
|
+
need(new Set(names).size === names.length, "Ambiguous native discovery directory names");
|
|
11798
|
+
for (const name of names.sort()) {
|
|
11799
|
+
const child = join14(current, name), childRelative = relative4 ? `${relative4}/${name}` : name;
|
|
11800
|
+
need(child.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
|
|
11801
|
+
const stat = lstatSync7(child);
|
|
11802
|
+
need(stat.isFile() || stat.isDirectory(), "Refusing symlink or special native discovery directory member");
|
|
11803
|
+
const row = JSON.stringify([childRelative, stat.isDirectory() ? "directory" : "file"]) + `
|
|
11804
|
+
`;
|
|
11805
|
+
budget.bytes += Buffer.byteLength(row);
|
|
11806
|
+
need(budget.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
|
|
11807
|
+
hash2.update(row);
|
|
11808
|
+
if (stat.isDirectory())
|
|
11809
|
+
walk2(child, childRelative, stat, depth + 1);
|
|
11810
|
+
}
|
|
11811
|
+
need(same(before, lstatSync7(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
|
|
11812
|
+
}
|
|
11813
|
+
walk2(path, "", root, 0);
|
|
11814
|
+
safe(path);
|
|
11815
|
+
return { path, sha256: hash2.digest("hex") };
|
|
11816
|
+
}
|
|
11817
|
+
function captureDiscoveryDirectories(paths) {
|
|
11818
|
+
need(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
|
|
11819
|
+
const budget = { entries: 0, bytes: 0 };
|
|
11820
|
+
return paths.map((path) => capture(path, budget));
|
|
11821
|
+
}
|
|
11822
|
+
function verifyDiscoveryDirectories(directories) {
|
|
11823
|
+
need(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
|
|
11824
|
+
for (const directory of directories)
|
|
11825
|
+
need(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
|
|
11826
|
+
const current = captureDiscoveryDirectories(directories.map((directory) => directory.path));
|
|
11827
|
+
for (let i = 0;i < directories.length; i++)
|
|
11828
|
+
need(current[i].sha256 === directories[i].sha256, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i].path}`);
|
|
11829
|
+
}
|
|
11830
|
+
|
|
11831
|
+
// src/lib/agent-discovery-bytes.ts
|
|
11832
|
+
import { createHash as createHash6 } from "crypto";
|
|
11833
|
+
import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync3, lstatSync as lstatSync8, openSync as openSync3, readSync as readSync3 } from "fs";
|
|
11834
|
+
import { dirname as dirname7, isAbsolute as isAbsolute5, resolve as resolve6 } from "path";
|
|
11835
|
+
function discoveryByteBudget() {
|
|
11836
|
+
return { remaining: AGENT_POLICY_LIMITS.discoveryRawTotalBytes };
|
|
11837
|
+
}
|
|
11838
|
+
function safe2(path) {
|
|
11839
|
+
if (typeof path !== "string" || !isAbsolute5(path) || path !== resolve6(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
|
|
11840
|
+
throw new Error("Expected a canonical absolute raw discovery path");
|
|
11841
|
+
for (let cursor = path;; cursor = dirname7(cursor)) {
|
|
11842
|
+
if (lstatSync8(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
11843
|
+
throw new Error("Refusing symlink raw discovery input");
|
|
11844
|
+
if (dirname7(cursor) === cursor)
|
|
11845
|
+
break;
|
|
11846
|
+
}
|
|
11847
|
+
}
|
|
11848
|
+
function same2(left, right) {
|
|
11849
|
+
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
|
|
11850
|
+
}
|
|
11851
|
+
function consume(budget, bytes) {
|
|
11852
|
+
if (bytes > budget.remaining)
|
|
11853
|
+
throw new Error("Raw discovery aggregate byte limit exceeded");
|
|
11854
|
+
budget.remaining -= bytes;
|
|
11855
|
+
}
|
|
11856
|
+
function hashRawDiscoveryFile(path, budget, changes) {
|
|
11857
|
+
safe2(path);
|
|
11858
|
+
if (changes?.has(path)) {
|
|
11859
|
+
const text2 = changes.get(path), bytes = Buffer.byteLength(text2);
|
|
11860
|
+
if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
|
|
11861
|
+
throw new Error("Raw discovery file byte limit exceeded");
|
|
11862
|
+
consume(budget, bytes);
|
|
11863
|
+
return createHash6("sha256").update(text2).digest("hex");
|
|
11864
|
+
}
|
|
11865
|
+
const initial = lstatSync8(path, { throwIfNoEntry: false });
|
|
11866
|
+
if (!initial)
|
|
11867
|
+
return null;
|
|
11868
|
+
if (!initial.isFile() || initial.size > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
|
|
11869
|
+
throw new Error("Unsupported or oversized raw discovery input");
|
|
11870
|
+
if (initial.size > budget.remaining)
|
|
11871
|
+
throw new Error("Raw discovery aggregate byte limit exceeded");
|
|
11872
|
+
const fd = openSync3(path, constants3.O_RDONLY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
|
|
11873
|
+
try {
|
|
11874
|
+
const opened = fstatSync3(fd);
|
|
11875
|
+
if (!opened.isFile() || !same2(initial, opened))
|
|
11876
|
+
throw new Error("Raw discovery input changed during open");
|
|
11877
|
+
const hash2 = createHash6("sha256"), buffer = Buffer.alloc(64 * 1024);
|
|
11878
|
+
let bytes = 0;
|
|
11879
|
+
while (true) {
|
|
11880
|
+
const count = readSync3(fd, buffer, 0, Math.min(buffer.length, AGENT_POLICY_LIMITS.discoveryRawSourceBytes - bytes + 1), null);
|
|
11881
|
+
if (!count)
|
|
11882
|
+
break;
|
|
11883
|
+
bytes += count;
|
|
11884
|
+
if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
|
|
11885
|
+
throw new Error("Raw discovery file byte limit exceeded");
|
|
11886
|
+
consume(budget, count);
|
|
11887
|
+
hash2.update(buffer.subarray(0, count));
|
|
11888
|
+
}
|
|
11889
|
+
const final = fstatSync3(fd), current = lstatSync8(path, { throwIfNoEntry: false });
|
|
11890
|
+
safe2(path);
|
|
11891
|
+
if (!current?.isFile() || !same2(opened, final) || !same2(final, current) || bytes !== final.size)
|
|
11892
|
+
throw new Error("Raw discovery input changed during read");
|
|
11893
|
+
return hash2.digest("hex");
|
|
11894
|
+
} finally {
|
|
11895
|
+
closeSync3(fd);
|
|
11896
|
+
}
|
|
11897
|
+
}
|
|
11898
|
+
|
|
11748
11899
|
// src/lib/agent-discovery.ts
|
|
11749
|
-
var digest = (text2) =>
|
|
11900
|
+
var digest = (text2) => createHash7("sha256").update(text2).digest("hex");
|
|
11750
11901
|
function parseConfig(text2, path, toml = false) {
|
|
11751
11902
|
try {
|
|
11752
11903
|
const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
|
|
@@ -11757,28 +11908,35 @@ function parseConfig(text2, path, toml = false) {
|
|
|
11757
11908
|
throw new Error(`Invalid native discovery configuration: ${path}`);
|
|
11758
11909
|
}
|
|
11759
11910
|
}
|
|
11760
|
-
function
|
|
11761
|
-
if (!
|
|
11911
|
+
function safe3(path) {
|
|
11912
|
+
if (!isAbsolute6(path) || path.includes("\x00"))
|
|
11762
11913
|
throw new Error("Expected an absolute native discovery path");
|
|
11763
|
-
for (let cursor = path;; cursor =
|
|
11764
|
-
if (
|
|
11914
|
+
for (let cursor = path;; cursor = dirname8(cursor)) {
|
|
11915
|
+
if (lstatSync9(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
11765
11916
|
throw new Error(`Refusing symlink native discovery input: ${cursor}`);
|
|
11766
|
-
if (
|
|
11917
|
+
if (dirname8(cursor) === cursor)
|
|
11767
11918
|
break;
|
|
11768
11919
|
}
|
|
11769
11920
|
}
|
|
11770
11921
|
function read(path, changes) {
|
|
11771
|
-
|
|
11922
|
+
safe3(path);
|
|
11772
11923
|
if (changes?.has(path))
|
|
11773
11924
|
return changes.get(path);
|
|
11774
|
-
const stat =
|
|
11925
|
+
const stat = lstatSync9(path, { throwIfNoEntry: false });
|
|
11775
11926
|
if (!stat)
|
|
11776
11927
|
return null;
|
|
11777
11928
|
if (!stat.isFile() || stat.size > 16 * 1024 * 1024)
|
|
11778
11929
|
throw new Error(`Unsupported or oversized native discovery input: ${path}`);
|
|
11779
11930
|
return readFileSync10(path, "utf8");
|
|
11780
11931
|
}
|
|
11781
|
-
function projected(source, changes) {
|
|
11932
|
+
function projected(source, changes, budget = discoveryByteBudget()) {
|
|
11933
|
+
if (source.hashMode !== undefined) {
|
|
11934
|
+
if (source.hashMode !== "bytes")
|
|
11935
|
+
throw new Error("Invalid native discovery hash mode");
|
|
11936
|
+
if (source.format !== undefined || source.fields !== undefined)
|
|
11937
|
+
throw new Error("Raw discovery witnesses cannot project configuration fields");
|
|
11938
|
+
return hashRawDiscoveryFile(source.path, budget, changes);
|
|
11939
|
+
}
|
|
11782
11940
|
const text2 = read(source.path, changes);
|
|
11783
11941
|
if (text2 === null)
|
|
11784
11942
|
return null;
|
|
@@ -11792,22 +11950,34 @@ function projected(source, changes) {
|
|
|
11792
11950
|
function verifyAgentDiscovery(binding) {
|
|
11793
11951
|
if (!binding || !Array.isArray(binding.sources) || !Array.isArray(binding.roots) || binding.sources.length > AGENT_POLICY_LIMITS.discoverySources || binding.roots.length > AGENT_POLICY_LIMITS.discoveryRoots)
|
|
11794
11952
|
throw new Error("Invalid native discovery binding");
|
|
11953
|
+
if (binding.agent === "hermes" && !binding.directories?.length)
|
|
11954
|
+
throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
|
|
11955
|
+
if (binding.directories !== undefined)
|
|
11956
|
+
verifyDiscoveryDirectories(binding.directories);
|
|
11957
|
+
const budget = discoveryByteBudget();
|
|
11795
11958
|
for (const source of binding.sources) {
|
|
11796
11959
|
if (source.format !== undefined && (!["json", "toml", "yaml"].includes(source.format) || !Array.isArray(source.fields) || !source.fields.length || source.fields.length > 64 || source.fields.some((field) => typeof field !== "string" || !field)))
|
|
11797
11960
|
throw new Error("Invalid native discovery projection");
|
|
11798
11961
|
if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
11799
11962
|
throw new Error("Invalid native discovery digest");
|
|
11800
|
-
if (projected(source) !== source.sha256)
|
|
11963
|
+
if (projected(source, undefined, budget) !== source.sha256)
|
|
11801
11964
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
11802
11965
|
}
|
|
11803
11966
|
for (const root of binding.roots)
|
|
11804
|
-
|
|
11967
|
+
safe3(root);
|
|
11805
11968
|
}
|
|
11806
11969
|
function rebindAgentDiscovery(binding, changes) {
|
|
11807
|
-
|
|
11970
|
+
const budget = discoveryByteBudget();
|
|
11971
|
+
return { ...binding, sources: binding.sources.map((source) => ({ ...source, sha256: projected(source, changes, budget) })) };
|
|
11972
|
+
}
|
|
11973
|
+
function captureDiscoveryByteSources(paths) {
|
|
11974
|
+
if (!Array.isArray(paths) || paths.length > AGENT_POLICY_LIMITS.discoverySources || new Set(paths).size !== paths.length)
|
|
11975
|
+
throw new Error("Invalid raw discovery source collection");
|
|
11976
|
+
const budget = discoveryByteBudget();
|
|
11977
|
+
return paths.map((path) => ({ path, hashMode: "bytes", sha256: hashRawDiscoveryFile(path, budget) }));
|
|
11808
11978
|
}
|
|
11809
11979
|
function resolveAgentDiscovery(options) {
|
|
11810
|
-
const canonical = options.canonical ??
|
|
11980
|
+
const canonical = options.canonical ?? resolve7, home = resolve7(options.home), agent = options.agent;
|
|
11811
11981
|
const sources = [], roots = new Set, builtinNames = [];
|
|
11812
11982
|
const witness = (path, format, fields) => {
|
|
11813
11983
|
const source = { path: canonical(path), sha256: null, ...format ? { format, fields } : {} };
|
|
@@ -11817,14 +11987,14 @@ function resolveAgentDiscovery(options) {
|
|
|
11817
11987
|
};
|
|
11818
11988
|
if (agent === "hermes")
|
|
11819
11989
|
assertHermesEnvironment(home);
|
|
11820
|
-
const configPath =
|
|
11990
|
+
const configPath = join15(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
|
|
11821
11991
|
const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? ["plugins", "marketplaces", "skills"] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : ["version"]);
|
|
11822
11992
|
const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
|
|
11823
11993
|
const unresolved = (detail) => {
|
|
11824
11994
|
throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
|
|
11825
11995
|
};
|
|
11826
11996
|
if (agent === "hermes") {
|
|
11827
|
-
const profile = witness(
|
|
11997
|
+
const profile = witness(join15(home, ".hermes/active_profile"));
|
|
11828
11998
|
if (profile?.trim() && profile.trim() !== "default")
|
|
11829
11999
|
unresolved("a non-default profile is active");
|
|
11830
12000
|
const declared = config.skills?.external_dirs ?? [], paths = typeof declared === "string" ? [declared] : declared;
|
|
@@ -11838,8 +12008,8 @@ function resolveAgentDiscovery(options) {
|
|
|
11838
12008
|
continue;
|
|
11839
12009
|
if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
|
|
11840
12010
|
unresolved("user-specific tilde expansion requires a separate discovery adapter");
|
|
11841
|
-
const path = normalized === "~" ? home : normalized.startsWith("~/") ?
|
|
11842
|
-
|
|
12011
|
+
const path = normalized === "~" ? home : normalized.startsWith("~/") ? join15(home, normalized.slice(2)) : resolve7(home, ".hermes", normalized);
|
|
12012
|
+
safe3(path);
|
|
11843
12013
|
roots.add(path);
|
|
11844
12014
|
}
|
|
11845
12015
|
}
|
|
@@ -11847,24 +12017,24 @@ function resolveAgentDiscovery(options) {
|
|
|
11847
12017
|
const executable = Bun.which("gemini");
|
|
11848
12018
|
if (executable) {
|
|
11849
12019
|
let packageRoot;
|
|
11850
|
-
for (let directory =
|
|
11851
|
-
const path =
|
|
12020
|
+
for (let directory = dirname8(realpathSync3(executable)), depth = 0;depth < 8; directory = dirname8(directory), depth++) {
|
|
12021
|
+
const path = join15(directory, "package.json"), raw = read(path);
|
|
11852
12022
|
if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
|
|
11853
12023
|
packageRoot = directory;
|
|
11854
12024
|
witness(path);
|
|
11855
12025
|
break;
|
|
11856
12026
|
}
|
|
11857
|
-
if (
|
|
12027
|
+
if (dirname8(directory) === directory)
|
|
11858
12028
|
break;
|
|
11859
12029
|
}
|
|
11860
12030
|
if (!packageRoot)
|
|
11861
12031
|
unresolved("installed Gemini package layout is not recognized");
|
|
11862
|
-
const builtinRoot =
|
|
11863
|
-
|
|
11864
|
-
if (!
|
|
12032
|
+
const builtinRoot = join15(packageRoot, "bundle/builtin");
|
|
12033
|
+
safe3(builtinRoot);
|
|
12034
|
+
if (!lstatSync9(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
|
|
11865
12035
|
unresolved("installed Gemini builtin root is missing");
|
|
11866
12036
|
for (const name of readdirSync9(builtinRoot).sort()) {
|
|
11867
|
-
const text2 = witness(
|
|
12037
|
+
const text2 = witness(join15(builtinRoot, name, "SKILL.md"));
|
|
11868
12038
|
const match = text2?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
|
|
11869
12039
|
if (!match?.[1] || builtinNames.includes(match[1]))
|
|
11870
12040
|
unresolved("installed Gemini builtin name is missing or ambiguous");
|
|
@@ -11878,7 +12048,7 @@ function resolveAgentDiscovery(options) {
|
|
|
11878
12048
|
if (review) {
|
|
11879
12049
|
if (review.pluginHooks !== "reviewed-no-skill-injection" || !Array.isArray(review.sources) || !review.sources.length || !Array.isArray(review.roots))
|
|
11880
12050
|
throw new Error("Discovery review must bind sources and confirm plugin hooks do not inject retired skills");
|
|
11881
|
-
const supplied = { agent, roots: review.roots, sources: review.sources, method: "reviewed" };
|
|
12051
|
+
const supplied = { agent, roots: review.roots, sources: review.sources, ...review.directories !== undefined ? { directories: review.directories } : {}, method: "reviewed" };
|
|
11882
12052
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
11883
12053
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
11884
12054
|
verifyAgentDiscovery(supplied);
|
|
@@ -11888,40 +12058,40 @@ function resolveAgentDiscovery(options) {
|
|
|
11888
12058
|
}
|
|
11889
12059
|
function plugin(root) {
|
|
11890
12060
|
root = canonical(root);
|
|
11891
|
-
|
|
11892
|
-
const manifestPath =
|
|
12061
|
+
safe3(root);
|
|
12062
|
+
const manifestPath = join15(root, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
11893
12063
|
const raw = witness(manifestPath);
|
|
11894
12064
|
if (raw === null)
|
|
11895
12065
|
unresolved("plugin manifest missing");
|
|
11896
12066
|
const manifest = parseConfig(raw, manifestPath);
|
|
11897
|
-
const hooks = witness(
|
|
12067
|
+
const hooks = witness(join15(root, "hooks/hooks.json"));
|
|
11898
12068
|
if (manifest.hooks !== undefined || hooks !== null)
|
|
11899
12069
|
unresolved("plugin hooks require a separate no-skill-injection review");
|
|
11900
|
-
const commands =
|
|
11901
|
-
|
|
11902
|
-
if (agent === "claude" && (manifest.commands !== undefined ||
|
|
12070
|
+
const commands = join15(root, "commands");
|
|
12071
|
+
safe3(commands);
|
|
12072
|
+
if (agent === "claude" && (manifest.commands !== undefined || lstatSync9(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length))
|
|
11903
12073
|
unresolved("legacy command discovery requires retirement or a dedicated format adapter");
|
|
11904
|
-
const rootSkill = witness(
|
|
12074
|
+
const rootSkill = witness(join15(root, "SKILL.md"));
|
|
11905
12075
|
if (rootSkill !== null)
|
|
11906
12076
|
roots.add(root);
|
|
11907
12077
|
const declared = manifest.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
|
|
11908
12078
|
if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
|
|
11909
12079
|
unresolved("unsupported plugin skill paths");
|
|
11910
12080
|
if (agent === "claude")
|
|
11911
|
-
roots.add(
|
|
12081
|
+
roots.add(join15(root, "skills"));
|
|
11912
12082
|
for (const value of declared) {
|
|
11913
|
-
const path =
|
|
12083
|
+
const path = resolve7(root, value);
|
|
11914
12084
|
if (path !== root && !path.startsWith(root + sep3))
|
|
11915
12085
|
unresolved("plugin skill path escapes its root");
|
|
11916
|
-
roots.add(path.endsWith(`${sep3}SKILL.md`) ?
|
|
12086
|
+
roots.add(path.endsWith(`${sep3}SKILL.md`) ? dirname8(path) : path);
|
|
11917
12087
|
}
|
|
11918
12088
|
}
|
|
11919
12089
|
if (agent === "claude") {
|
|
11920
|
-
const registrations = witness(
|
|
11921
|
-
const installed = registrations === null ? {} : parseConfig(registrations,
|
|
11922
|
-
const commands = canonical(
|
|
11923
|
-
|
|
11924
|
-
if (
|
|
12090
|
+
const registrations = witness(join15(home, ".claude/plugins/installed_plugins.json"));
|
|
12091
|
+
const installed = registrations === null ? {} : parseConfig(registrations, join15(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
12092
|
+
const commands = canonical(join15(home, ".claude/commands"));
|
|
12093
|
+
safe3(commands);
|
|
12094
|
+
if (lstatSync9(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length)
|
|
11925
12095
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
11926
12096
|
for (const [id, enabled] of Object.entries(config.enabledPlugins ?? {})) {
|
|
11927
12097
|
if (enabled === false)
|
|
@@ -11941,34 +12111,34 @@ function resolveAgentDiscovery(options) {
|
|
|
11941
12111
|
unresolved("unsupported plugin enablement");
|
|
11942
12112
|
const split = id.lastIndexOf("@"), name = id.slice(0, split), marketplace = id.slice(split + 1);
|
|
11943
12113
|
const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
|
|
11944
|
-
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !
|
|
12114
|
+
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute6(market.source))
|
|
11945
12115
|
unresolved("enabled plugin marketplace is not an explicit local source");
|
|
11946
|
-
const catalogText = witness(
|
|
12116
|
+
const catalogText = witness(join15(market.source, ".agents/plugins/marketplace.json"));
|
|
11947
12117
|
if (catalogText === null)
|
|
11948
12118
|
unresolved("marketplace catalog missing");
|
|
11949
|
-
const catalog = parseConfig(catalogText,
|
|
12119
|
+
const catalog = parseConfig(catalogText, join15(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
|
|
11950
12120
|
if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
|
|
11951
12121
|
unresolved("plugin catalog source is missing or ambiguous");
|
|
11952
|
-
plugin(
|
|
12122
|
+
plugin(resolve7(market.source, matches[0].source.path));
|
|
11953
12123
|
}
|
|
11954
12124
|
} else if (agent === "opencode") {
|
|
11955
|
-
if (witness(
|
|
12125
|
+
if (witness(join15(home, ".config/opencode/opencode.jsonc")) !== null || witness(join15(home, ".config/opencode/config.json")) !== null)
|
|
11956
12126
|
unresolved("additional config layers require review");
|
|
11957
12127
|
if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
|
|
11958
12128
|
unresolved("external plugin or remote skill source");
|
|
11959
12129
|
for (const path of config.skills?.paths ?? []) {
|
|
11960
|
-
if (typeof path !== "string" || !
|
|
12130
|
+
if (typeof path !== "string" || !isAbsolute6(path))
|
|
11961
12131
|
unresolved("relative or malformed added skill root");
|
|
11962
12132
|
roots.add(path);
|
|
11963
12133
|
}
|
|
11964
|
-
const localPlugins =
|
|
11965
|
-
|
|
11966
|
-
if (
|
|
12134
|
+
const localPlugins = join15(home, ".config/opencode/plugins");
|
|
12135
|
+
safe3(localPlugins);
|
|
12136
|
+
if (lstatSync9(localPlugins, { throwIfNoEntry: false }) && readdirSync9(localPlugins).some((name) => name !== "skills-cli.js"))
|
|
11967
12137
|
unresolved("additional local plugin hooks require review");
|
|
11968
12138
|
} else if (agent === "hermes") {
|
|
11969
|
-
for (const directory of [
|
|
11970
|
-
|
|
11971
|
-
if (
|
|
12139
|
+
for (const directory of [join15(home, ".hermes/plugins"), join15(home, ".hermes/hermes-agent")]) {
|
|
12140
|
+
safe3(directory);
|
|
12141
|
+
if (lstatSync9(directory, { throwIfNoEntry: false }))
|
|
11972
12142
|
unresolved("installed runtime/plugin discovery requires reviewed source bindings");
|
|
11973
12143
|
}
|
|
11974
12144
|
if (Bun.which("hermes") || Object.keys(config.plugins ?? {}).length)
|
|
@@ -11976,24 +12146,24 @@ function resolveAgentDiscovery(options) {
|
|
|
11976
12146
|
} else if (agent === "gemini") {
|
|
11977
12147
|
if (Object.keys(config.extensions ?? {}).length || config.skills?.paths?.length)
|
|
11978
12148
|
unresolved("custom extension or skill paths");
|
|
11979
|
-
const directory =
|
|
11980
|
-
|
|
11981
|
-
if (
|
|
12149
|
+
const directory = join15(home, ".gemini/extensions");
|
|
12150
|
+
safe3(directory);
|
|
12151
|
+
if (lstatSync9(directory, { throwIfNoEntry: false }))
|
|
11982
12152
|
for (const name of readdirSync9(directory)) {
|
|
11983
|
-
const root =
|
|
11984
|
-
|
|
11985
|
-
if (!
|
|
12153
|
+
const root = join15(directory, name);
|
|
12154
|
+
safe3(root);
|
|
12155
|
+
if (!lstatSync9(root)?.isDirectory())
|
|
11986
12156
|
continue;
|
|
11987
|
-
const raw = witness(
|
|
12157
|
+
const raw = witness(join15(root, "gemini-extension.json"));
|
|
11988
12158
|
if (raw === null)
|
|
11989
12159
|
unresolved("extension registration missing");
|
|
11990
|
-
const manifest = parseConfig(raw,
|
|
11991
|
-
if (manifest.hooks !== undefined || witness(
|
|
12160
|
+
const manifest = parseConfig(raw, join15(root, "gemini-extension.json"));
|
|
12161
|
+
if (manifest.hooks !== undefined || witness(join15(root, "hooks/hooks.json")) !== null || witness(join15(root, ".gemini-extension-install.json")) !== null)
|
|
11992
12162
|
unresolved("extension hooks or linked registration require review");
|
|
11993
|
-
roots.add(
|
|
12163
|
+
roots.add(join15(root, "skills"));
|
|
11994
12164
|
}
|
|
11995
12165
|
}
|
|
11996
|
-
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {} };
|
|
12166
|
+
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join15(home, ".hermes/plugins"), join15(home, ".hermes/hermes-agent")].map((path) => canonical(path))) } : {} };
|
|
11997
12167
|
}
|
|
11998
12168
|
|
|
11999
12169
|
// src/lib/agent-adapters.ts
|
|
@@ -12058,7 +12228,7 @@ export default async ({ directory }) => {
|
|
|
12058
12228
|
}
|
|
12059
12229
|
|
|
12060
12230
|
// src/lib/agent-integration.ts
|
|
12061
|
-
var sha = (data) =>
|
|
12231
|
+
var sha = (data) => createHash8("sha256").update(data).digest("hex");
|
|
12062
12232
|
var ROOTS = [
|
|
12063
12233
|
["claude", ".claude/skills"],
|
|
12064
12234
|
["codex", ".codex/skills"],
|
|
@@ -12077,11 +12247,11 @@ var ROOTS = [
|
|
|
12077
12247
|
["copilot", ".github/skills"]
|
|
12078
12248
|
];
|
|
12079
12249
|
function assertSafePath(path) {
|
|
12080
|
-
let cursor =
|
|
12250
|
+
let cursor = resolve8(path);
|
|
12081
12251
|
while (true) {
|
|
12082
|
-
if (
|
|
12252
|
+
if (lstatSync10(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
12083
12253
|
throw new Error(`Refusing symlink path: ${cursor}`);
|
|
12084
|
-
const parent =
|
|
12254
|
+
const parent = dirname9(cursor);
|
|
12085
12255
|
if (parent === cursor)
|
|
12086
12256
|
return;
|
|
12087
12257
|
cursor = parent;
|
|
@@ -12089,17 +12259,17 @@ function assertSafePath(path) {
|
|
|
12089
12259
|
}
|
|
12090
12260
|
function readRootAlias(home, agent) {
|
|
12091
12261
|
assertSafePath(home);
|
|
12092
|
-
const realHome = realpathSync4(home), alias =
|
|
12093
|
-
const stat =
|
|
12262
|
+
const realHome = realpathSync4(home), alias = join16(realHome, `.${agent}`);
|
|
12263
|
+
const stat = lstatSync10(alias, { throwIfNoEntry: false, bigint: true });
|
|
12094
12264
|
if (!stat?.isSymbolicLink())
|
|
12095
12265
|
throw new Error(`Agent root alias changed after planning: ${alias}`);
|
|
12096
|
-
const link = readlinkSync(alias), targetPath =
|
|
12266
|
+
const link = readlinkSync(alias), targetPath = resolve8(dirname9(alias), link);
|
|
12097
12267
|
assertSafePath(targetPath);
|
|
12098
|
-
const targetStat =
|
|
12268
|
+
const targetStat = lstatSync10(targetPath, { throwIfNoEntry: false });
|
|
12099
12269
|
if (!targetStat?.isDirectory())
|
|
12100
12270
|
throw new Error(`Agent root alias must target an existing directory: ${alias}`);
|
|
12101
12271
|
const target = realpathSync4(targetPath), inside = relative4(realHome, target);
|
|
12102
|
-
if (!inside || inside === ".." || inside.startsWith(`..${sep4}`) ||
|
|
12272
|
+
if (!inside || inside === ".." || inside.startsWith(`..${sep4}`) || isAbsolute7(inside))
|
|
12103
12273
|
throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
|
|
12104
12274
|
return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
|
|
12105
12275
|
}
|
|
@@ -12108,19 +12278,19 @@ function rootAliases(home, allowed = false) {
|
|
|
12108
12278
|
return [];
|
|
12109
12279
|
const result = [];
|
|
12110
12280
|
for (const agent of ["claude", "codex"])
|
|
12111
|
-
if (
|
|
12281
|
+
if (lstatSync10(join16(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
|
|
12112
12282
|
result.push(readRootAlias(home, agent));
|
|
12113
12283
|
if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep4) || other.target.startsWith(item.target + sep4))))
|
|
12114
12284
|
throw new Error("Agent root alias targets must not overlap");
|
|
12115
12285
|
return result;
|
|
12116
12286
|
}
|
|
12117
12287
|
function canonicalAgentPath(path, aliases) {
|
|
12118
|
-
const absolute =
|
|
12119
|
-
return binding ?
|
|
12288
|
+
const absolute = resolve8(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep4));
|
|
12289
|
+
return binding ? join16(binding.target, relative4(binding.alias, absolute)) : absolute;
|
|
12120
12290
|
}
|
|
12121
12291
|
function recheckRootAliases(aliases) {
|
|
12122
12292
|
for (const binding of aliases) {
|
|
12123
|
-
if (!["claude", "codex"].includes(binding.agent) || binding.alias !==
|
|
12293
|
+
if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join16(binding.home, `.${binding.agent}`))
|
|
12124
12294
|
throw new Error("Invalid agent root alias binding");
|
|
12125
12295
|
const current = readRootAlias(binding.home, binding.agent);
|
|
12126
12296
|
if (JSON.stringify(current) !== JSON.stringify(binding))
|
|
@@ -12129,42 +12299,42 @@ function recheckRootAliases(aliases) {
|
|
|
12129
12299
|
}
|
|
12130
12300
|
function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
12131
12301
|
assertSafePath(path);
|
|
12132
|
-
const before =
|
|
12302
|
+
const before = lstatSync10(path);
|
|
12133
12303
|
if (!before.isFile() || before.size > maximum)
|
|
12134
12304
|
throw new Error("Unsupported or oversized native skill file");
|
|
12135
|
-
const descriptor =
|
|
12305
|
+
const descriptor = openSync4(path, constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
|
|
12136
12306
|
try {
|
|
12137
|
-
const opened =
|
|
12307
|
+
const opened = fstatSync4(descriptor);
|
|
12138
12308
|
if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size !== before.size)
|
|
12139
12309
|
throw new Error("Native skill file changed while reading");
|
|
12140
12310
|
const bytes = Buffer.allocUnsafe(before.size + 1);
|
|
12141
12311
|
let length = 0;
|
|
12142
12312
|
while (length < bytes.length) {
|
|
12143
|
-
const count =
|
|
12313
|
+
const count = readSync4(descriptor, bytes, length, bytes.length - length, null);
|
|
12144
12314
|
if (!count)
|
|
12145
12315
|
break;
|
|
12146
12316
|
length += count;
|
|
12147
12317
|
}
|
|
12148
|
-
const after =
|
|
12318
|
+
const after = fstatSync4(descriptor);
|
|
12149
12319
|
if (length !== before.size || after.size !== before.size || after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs)
|
|
12150
12320
|
throw new Error("Native skill file changed while reading");
|
|
12151
12321
|
return bytes.subarray(0, length);
|
|
12152
12322
|
} finally {
|
|
12153
|
-
|
|
12323
|
+
closeSync4(descriptor);
|
|
12154
12324
|
}
|
|
12155
12325
|
}
|
|
12156
12326
|
function treeHash(root) {
|
|
12157
|
-
const hash2 =
|
|
12327
|
+
const hash2 = createHash8("sha256");
|
|
12158
12328
|
let bytes = 0, files = 0;
|
|
12159
12329
|
function visit2(path) {
|
|
12160
|
-
const stat =
|
|
12330
|
+
const stat = lstatSync10(path);
|
|
12161
12331
|
if (stat.isSymbolicLink())
|
|
12162
12332
|
throw new Error(`Refusing symlink in skill: ${path}`);
|
|
12163
12333
|
const rel = relative4(root, path);
|
|
12164
12334
|
if (stat.isDirectory()) {
|
|
12165
12335
|
hash2.update(`d\x00${rel}\x00`);
|
|
12166
12336
|
for (const name of readdirSync10(path).sort())
|
|
12167
|
-
visit2(
|
|
12337
|
+
visit2(join16(path, name));
|
|
12168
12338
|
return;
|
|
12169
12339
|
}
|
|
12170
12340
|
if (!stat.isFile())
|
|
@@ -12182,37 +12352,50 @@ function treeHash(root) {
|
|
|
12182
12352
|
}
|
|
12183
12353
|
function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
12184
12354
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
12185
|
-
const roots = ROOTS.map(([agent, path]) => [agent, canonicalAgentPath(
|
|
12186
|
-
const bridgePaths = Object.values(AGENT_ADAPTERS).map((adapter) => canonicalAgentPath(
|
|
12187
|
-
for (const project of new Set([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []].map((path) =>
|
|
12355
|
+
const roots = ROOTS.map(([agent, path]) => [agent, canonicalAgentPath(join16(home, path), aliases)]);
|
|
12356
|
+
const bridgePaths = Object.values(AGENT_ADAPTERS).map((adapter) => canonicalAgentPath(join16(home, adapter.root, CLI_BRIDGE_NAME), aliases));
|
|
12357
|
+
for (const project of new Set([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []].map((path) => resolve8(path)))) {
|
|
12188
12358
|
for (const [agent, path] of ROOTS)
|
|
12189
|
-
roots.push([agent, canonicalAgentPath(
|
|
12359
|
+
roots.push([agent, canonicalAgentPath(join16(project, path), aliases)]);
|
|
12190
12360
|
}
|
|
12191
12361
|
const entries = [], seen = new Set;
|
|
12192
12362
|
const cacheScans = new Map, maxAliasProofEntries = 1e4;
|
|
12363
|
+
let discoveryEntries = 0, discoveryPathBytes = 0;
|
|
12364
|
+
function admit(path) {
|
|
12365
|
+
if (++discoveryEntries > 20000)
|
|
12366
|
+
throw new Error("Native skill discovery entry limit exceeded");
|
|
12367
|
+
discoveryPathBytes += Buffer.byteLength(path, "utf8");
|
|
12368
|
+
if (discoveryPathBytes > 4 * 1024 * 1024)
|
|
12369
|
+
throw new Error("Native skill discovery metadata limit exceeded");
|
|
12370
|
+
}
|
|
12193
12371
|
function emptySiblingCacheAlias(path, parent) {
|
|
12194
|
-
const link = readlinkSync(path), target =
|
|
12195
|
-
if (
|
|
12372
|
+
const link = readlinkSync(path), target = resolve8(parent, link);
|
|
12373
|
+
if (dirname9(target) !== parent || (isAbsolute7(link) ? link !== target : dirname9(link) !== "."))
|
|
12196
12374
|
return false;
|
|
12197
|
-
if (!
|
|
12375
|
+
if (!lstatSync10(target, { throwIfNoEntry: false })?.isDirectory())
|
|
12198
12376
|
return false;
|
|
12199
12377
|
const scan = cacheScans.get(target);
|
|
12200
12378
|
return Boolean(scan?.complete && !scan.hasSkills && scan.entries <= maxAliasProofEntries);
|
|
12201
12379
|
}
|
|
12202
|
-
function visit2(agent, path, vendor = false, depth = 0, pluginCache = false) {
|
|
12380
|
+
function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false) {
|
|
12381
|
+
if (!admitted)
|
|
12382
|
+
admit(path);
|
|
12203
12383
|
const scan = { complete: true, hasSkills: false, entries: 1 };
|
|
12204
12384
|
assertSafePath(path);
|
|
12205
12385
|
if (!existsSync10(path))
|
|
12206
12386
|
return scan;
|
|
12207
|
-
|
|
12387
|
+
const stat = lstatSync10(path);
|
|
12388
|
+
if (stat.isFile())
|
|
12208
12389
|
return scan;
|
|
12209
|
-
if (
|
|
12390
|
+
if (!stat.isDirectory())
|
|
12391
|
+
throw new Error(`Unsupported native discovery entry: ${path}`);
|
|
12392
|
+
if (existsSync10(join16(path, "SKILL.md"))) {
|
|
12210
12393
|
scan.hasSkills = true;
|
|
12211
12394
|
if (seen.has(path))
|
|
12212
12395
|
return scan;
|
|
12213
12396
|
seen.add(path);
|
|
12214
12397
|
let managed = false;
|
|
12215
|
-
const marker =
|
|
12398
|
+
const marker = join16(path, ".hasna-skills.json");
|
|
12216
12399
|
if (existsSync10(marker)) {
|
|
12217
12400
|
try {
|
|
12218
12401
|
managed = JSON.parse(readFileSync11(marker, "utf8")).managedBy === "@hasna/skills";
|
|
@@ -12220,12 +12403,22 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
12220
12403
|
}
|
|
12221
12404
|
const bridge = !vendor && isOwnedCliBridge(path, bridgePaths);
|
|
12222
12405
|
const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep4));
|
|
12223
|
-
entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(
|
|
12406
|
+
entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(join16(home, ".codex", "skills", ".system"), aliases) + sep4) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve8(home) } : {}, ...rootAlias ? { rootAlias } : {} });
|
|
12224
12407
|
return scan;
|
|
12225
12408
|
}
|
|
12226
|
-
if (depth > (vendor ?
|
|
12409
|
+
if (depth > (vendor ? 32 : 3))
|
|
12227
12410
|
return { ...scan, complete: false };
|
|
12228
|
-
const children =
|
|
12411
|
+
const children = [], directory = opendirSync3(path);
|
|
12412
|
+
try {
|
|
12413
|
+
let child;
|
|
12414
|
+
while ((child = directory.readSync()) !== null) {
|
|
12415
|
+
admit(join16(path, child.name));
|
|
12416
|
+
children.push(child);
|
|
12417
|
+
}
|
|
12418
|
+
} finally {
|
|
12419
|
+
directory.closeSync();
|
|
12420
|
+
}
|
|
12421
|
+
children.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
12229
12422
|
if (pluginCache)
|
|
12230
12423
|
children.sort((a, b) => Number(a.isSymbolicLink()) - Number(b.isSymbolicLink()));
|
|
12231
12424
|
for (const { name } of children) {
|
|
@@ -12236,15 +12429,15 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
12236
12429
|
const isVendor = vendor || name === ".system";
|
|
12237
12430
|
if (isVendor && !options.includeVendor)
|
|
12238
12431
|
continue;
|
|
12239
|
-
const child =
|
|
12240
|
-
if (isVendor &&
|
|
12432
|
+
const child = join16(path, name);
|
|
12433
|
+
if (isVendor && lstatSync10(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
12241
12434
|
const target = statSync8(child, { throwIfNoEntry: false });
|
|
12242
12435
|
if (target?.isFile() || pluginCache && target?.isDirectory() && emptySiblingCacheAlias(child, path)) {
|
|
12243
12436
|
scan.entries++;
|
|
12244
12437
|
continue;
|
|
12245
12438
|
}
|
|
12246
12439
|
}
|
|
12247
|
-
const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache);
|
|
12440
|
+
const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true);
|
|
12248
12441
|
scan.complete &&= childScan.complete;
|
|
12249
12442
|
scan.hasSkills ||= childScan.hasSkills;
|
|
12250
12443
|
scan.entries += childScan.entries;
|
|
@@ -12263,8 +12456,8 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
12263
12456
|
scanRoot(agent, path);
|
|
12264
12457
|
if (options.includeVendor) {
|
|
12265
12458
|
for (const agent of ["codex", "claude"])
|
|
12266
|
-
scanRoot(agent, canonicalAgentPath(
|
|
12267
|
-
scanRoot("gemini",
|
|
12459
|
+
scanRoot(agent, canonicalAgentPath(join16(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
|
|
12460
|
+
scanRoot("gemini", join16(home, ".gemini", "extensions"), true);
|
|
12268
12461
|
}
|
|
12269
12462
|
const configured = options.configured ? INTEGRATION_AGENTS.flatMap((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }).roots.map((path) => ({ agent, path }))) : [];
|
|
12270
12463
|
for (const root of [...options.agentRoots ?? [], ...configured])
|
|
@@ -12362,12 +12555,12 @@ function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
|
12362
12555
|
let result = text2;
|
|
12363
12556
|
const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
|
|
12364
12557
|
for (const skill of selections) {
|
|
12365
|
-
const path =
|
|
12558
|
+
const path = join16(skill.path, "SKILL.md");
|
|
12366
12559
|
let found = false;
|
|
12367
12560
|
result = result.replace(/^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm, (section) => {
|
|
12368
12561
|
const parsed = Bun.TOML.parse(section);
|
|
12369
12562
|
const declared = parsed.skills?.config?.[0]?.path;
|
|
12370
|
-
if (!declared || ![
|
|
12563
|
+
if (!declared || ![resolve8(path), resolve8(skill.path)].includes(canonicalAgentPath(declared, aliases)))
|
|
12371
12564
|
return section;
|
|
12372
12565
|
found = true;
|
|
12373
12566
|
return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
|
|
@@ -12391,7 +12584,7 @@ function planAgentIntegration(options) {
|
|
|
12391
12584
|
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(profileId) || profileId.includes(".."))
|
|
12392
12585
|
throw new Error("Invalid selection profile id");
|
|
12393
12586
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
12394
|
-
const policyPath =
|
|
12587
|
+
const policyPath = join16(dataDir2, "agent-policy.json");
|
|
12395
12588
|
assertSafePath(policyPath);
|
|
12396
12589
|
const priorSnapshot = readManagedSkillPolicySnapshot(dataDir2), previousPolicy = priorSnapshot?.text ?? null, policy = priorSnapshot?.value ?? {};
|
|
12397
12590
|
if (policy.bridge !== undefined && (!policy.bridge || typeof policy.bridge !== "object" || Array.isArray(policy.bridge)))
|
|
@@ -12408,19 +12601,19 @@ function planAgentIntegration(options) {
|
|
|
12408
12601
|
if (!INTEGRATION_AGENTS.includes(agent))
|
|
12409
12602
|
throw new Error(`Unsupported agent: ${agent}`);
|
|
12410
12603
|
const adapter = AGENT_ADAPTERS[agent];
|
|
12411
|
-
const bridgePath = canonicalAgentPath(
|
|
12604
|
+
const bridgePath = canonicalAgentPath(join16(home, adapter.root, CLI_BRIDGE_NAME), aliases);
|
|
12412
12605
|
assertSafePath(bridgePath);
|
|
12413
12606
|
if (existsSync10(bridgePath) && !isOwnedCliBridge(bridgePath, [bridgePath]))
|
|
12414
12607
|
throw new Error(`Refusing to overwrite an unrecognized or modified Skills bridge: ${bridgePath}`);
|
|
12415
12608
|
for (const [name, after2] of Object.entries(CLI_BRIDGE_FILES)) {
|
|
12416
|
-
const path2 =
|
|
12609
|
+
const path2 = join16(bridgePath, name), before2 = readOptional(path2);
|
|
12417
12610
|
if (before2 !== after2)
|
|
12418
12611
|
changes.push({ path: path2, before: before2, after: after2 });
|
|
12419
12612
|
}
|
|
12420
|
-
const path = canonicalAgentPath(
|
|
12613
|
+
const path = canonicalAgentPath(join16(home, adapter.config), aliases);
|
|
12421
12614
|
const before = readOptional(path);
|
|
12422
12615
|
if (agent === "hermes") {
|
|
12423
|
-
const supervisorPath =
|
|
12616
|
+
const supervisorPath = join16(dataDir2, "agent-hooks", "hermes.js"), supervisorBefore = readOptional(supervisorPath), supervisorAfter = renderHermesSupervisor(options.command ?? "skills", profileId);
|
|
12424
12617
|
const priorCommand = policy.bridge?.commands?.hermes, priorProfile = policy.bridge?.profiles?.hermes ?? policy.profileId;
|
|
12425
12618
|
const previous = policy.bridge?.supervisors?.hermes;
|
|
12426
12619
|
if (supervisorBefore !== null && supervisorBefore !== supervisorAfter && !(previous?.path === supervisorPath && typeof priorCommand === "string" && typeof priorProfile === "string" && supervisorBefore === renderHermesSupervisor(priorCommand, priorProfile)))
|
|
@@ -12431,7 +12624,7 @@ function planAgentIntegration(options) {
|
|
|
12431
12624
|
const after2 = configureHermesHooks(before, supervisor, previous);
|
|
12432
12625
|
if (before !== after2)
|
|
12433
12626
|
changes.push({ path, before, after: after2 });
|
|
12434
|
-
const optOut =
|
|
12627
|
+
const optOut = join16(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
|
|
12435
12628
|
if (optOutBefore === null)
|
|
12436
12629
|
changes.push({ path: optOut, before: null, after: `Managed by @hasna/skills: bundled native skill reseeding is disabled.
|
|
12437
12630
|
` });
|
|
@@ -12443,7 +12636,7 @@ function planAgentIntegration(options) {
|
|
|
12443
12636
|
if (!permission || typeof permission !== "object" || Array.isArray(permission))
|
|
12444
12637
|
throw new Error("Expected OpenCode permission object");
|
|
12445
12638
|
config.permission = { ...permission, skill: { "*": "deny", [CLI_BRIDGE_NAME]: "allow" } };
|
|
12446
|
-
const pluginPath =
|
|
12639
|
+
const pluginPath = join16(home, ".config", "opencode", "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
|
|
12447
12640
|
const pluginAfter = renderOpenCodePlugin(options.command ?? "skills", profileId);
|
|
12448
12641
|
const priorCommand = policy.bridge?.commands?.opencode;
|
|
12449
12642
|
const ownedPrevious = typeof priorCommand === "string" && typeof policy.profileId === "string" && pluginBefore === renderOpenCodePlugin(priorCommand, policy.bridge?.profiles?.opencode ?? policy.profileId);
|
|
@@ -12460,7 +12653,7 @@ function planAgentIntegration(options) {
|
|
|
12460
12653
|
if (before !== after)
|
|
12461
12654
|
changes.push({ path, before, after });
|
|
12462
12655
|
if (agent === "codex") {
|
|
12463
|
-
const configPath = canonicalAgentPath(
|
|
12656
|
+
const configPath = canonicalAgentPath(join16(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
|
|
12464
12657
|
const next = disableCodexSkills(previous ?? "", nativeSkills, aliases, bridgePath);
|
|
12465
12658
|
if (next !== (previous ?? ""))
|
|
12466
12659
|
changes.push({ path: configPath, before: previous, after: next });
|
|
@@ -12473,12 +12666,12 @@ function planAgentIntegration(options) {
|
|
|
12473
12666
|
version: CLI_BRIDGE_VERSION,
|
|
12474
12667
|
digest: CLI_BRIDGE_DIGEST,
|
|
12475
12668
|
agents: [...new Set([...priorAgents, ...options.agents])].sort(),
|
|
12476
|
-
home:
|
|
12669
|
+
home: resolve8(home),
|
|
12477
12670
|
includeVendor: true,
|
|
12478
|
-
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path:
|
|
12671
|
+
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join16(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha(renderHermesSupervisor(options.command ?? "skills", profileId)) } } } : {},
|
|
12479
12672
|
commands: { ...policy.bridge?.commands, ...Object.fromEntries(options.agents.map((agent) => [agent, options.command ?? "skills"])) },
|
|
12480
12673
|
profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, options.agents.includes(agent) ? profileId : policy.bridge?.profiles?.[agent] ?? policy.profileId])),
|
|
12481
|
-
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(
|
|
12674
|
+
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join16(home, ".codex", "skills", ".system"), aliases) + sep4)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
|
|
12482
12675
|
rootAliases: aliases,
|
|
12483
12676
|
discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
|
|
12484
12677
|
} };
|
|
@@ -12490,7 +12683,7 @@ function planAgentIntegration(options) {
|
|
|
12490
12683
|
}
|
|
12491
12684
|
function atomicWrite2(path, content) {
|
|
12492
12685
|
assertSafePath(path);
|
|
12493
|
-
mkdirSync5(
|
|
12686
|
+
mkdirSync5(dirname9(path), { recursive: true, mode: 448 });
|
|
12494
12687
|
const temporary = `${path}.skills-${randomUUID2()}`;
|
|
12495
12688
|
try {
|
|
12496
12689
|
writeFileSync5(temporary, content, { mode: 384, flag: "wx" });
|
|
@@ -12501,12 +12694,12 @@ function atomicWrite2(path, content) {
|
|
|
12501
12694
|
}
|
|
12502
12695
|
}
|
|
12503
12696
|
function applyAgentIntegration(plan) {
|
|
12504
|
-
const policyPath =
|
|
12697
|
+
const policyPath = join16(plan.dataDir, "agent-policy.json");
|
|
12505
12698
|
assertSafePath(policyPath);
|
|
12506
|
-
const currentText = (path) =>
|
|
12699
|
+
const currentText = (path) => resolve8(path) === resolve8(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
|
|
12507
12700
|
readManagedSkillPolicySnapshot(plan.dataDir);
|
|
12508
12701
|
for (const change of plan.changes)
|
|
12509
|
-
if (
|
|
12702
|
+
if (resolve8(change.path) === resolve8(policyPath))
|
|
12510
12703
|
parseManagedSkillPolicy(change.after);
|
|
12511
12704
|
const aliases = plan.rootAliases ?? [];
|
|
12512
12705
|
recheckRootAliases(aliases);
|
|
@@ -12517,7 +12710,7 @@ function applyAgentIntegration(plan) {
|
|
|
12517
12710
|
for (const change of plan.changes)
|
|
12518
12711
|
if (currentText(change.path) !== change.before)
|
|
12519
12712
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
12520
|
-
const backupRoot =
|
|
12713
|
+
const backupRoot = join16(plan.dataDir, "migration", randomUUID2()), backups = [], written = [], createdDirectories = new Set;
|
|
12521
12714
|
if (!plan.changes.length) {
|
|
12522
12715
|
recheckRootAliases(aliases);
|
|
12523
12716
|
return { changed: [], backups, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
@@ -12527,7 +12720,7 @@ function applyAgentIntegration(plan) {
|
|
|
12527
12720
|
chmodSync(backupRoot, 448);
|
|
12528
12721
|
for (const [index, change] of plan.changes.entries())
|
|
12529
12722
|
if (change.before !== null) {
|
|
12530
|
-
const backup =
|
|
12723
|
+
const backup = join16(backupRoot, `${index}-${sha(change.path).slice(0, 12)}.backup`);
|
|
12531
12724
|
writeFileSync5(backup, change.before, { mode: 384, flag: "wx" });
|
|
12532
12725
|
backups.push(backup);
|
|
12533
12726
|
}
|
|
@@ -12536,7 +12729,7 @@ function applyAgentIntegration(plan) {
|
|
|
12536
12729
|
recheckRootAliases(aliases);
|
|
12537
12730
|
if (currentText(change.path) !== change.before)
|
|
12538
12731
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
12539
|
-
for (let directory =
|
|
12732
|
+
for (let directory = dirname9(change.path);!existsSync10(directory); directory = dirname9(directory))
|
|
12540
12733
|
createdDirectories.add(directory);
|
|
12541
12734
|
const after = change.after;
|
|
12542
12735
|
atomicWrite2(change.path, after);
|
|
@@ -12545,7 +12738,7 @@ function applyAgentIntegration(plan) {
|
|
|
12545
12738
|
recheckRootAliases(aliases);
|
|
12546
12739
|
for (const binding of plan.discoveryAfter ?? [])
|
|
12547
12740
|
verifyAgentDiscovery(binding);
|
|
12548
|
-
atomicWrite2(
|
|
12741
|
+
atomicWrite2(join16(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha(change.before), afterHash: sha(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
|
|
12549
12742
|
`);
|
|
12550
12743
|
} catch (error) {
|
|
12551
12744
|
for (const change of written.reverse()) {
|
|
@@ -12576,11 +12769,11 @@ function applyAgentIntegration(plan) {
|
|
|
12576
12769
|
}
|
|
12577
12770
|
function syncArchiveDirectory(path) {
|
|
12578
12771
|
assertSafePath(path);
|
|
12579
|
-
const descriptor =
|
|
12772
|
+
const descriptor = openSync4(path, constants4.O_RDONLY | constants4.O_DIRECTORY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
|
|
12580
12773
|
try {
|
|
12581
12774
|
fsyncSync(descriptor);
|
|
12582
12775
|
} finally {
|
|
12583
|
-
|
|
12776
|
+
closeSync4(descriptor);
|
|
12584
12777
|
}
|
|
12585
12778
|
}
|
|
12586
12779
|
function writeArchiveJournal(path, value) {
|
|
@@ -12588,18 +12781,18 @@ function writeArchiveJournal(path, value) {
|
|
|
12588
12781
|
const temporary = `${path}.skills-${randomUUID2()}`;
|
|
12589
12782
|
let descriptor;
|
|
12590
12783
|
try {
|
|
12591
|
-
descriptor =
|
|
12784
|
+
descriptor = openSync4(temporary, constants4.O_WRONLY | constants4.O_CREAT | constants4.O_EXCL | constants4.O_NOFOLLOW, 384);
|
|
12592
12785
|
writeFileSync5(descriptor, `${JSON.stringify(value)}
|
|
12593
12786
|
`);
|
|
12594
12787
|
fsyncSync(descriptor);
|
|
12595
|
-
|
|
12788
|
+
closeSync4(descriptor);
|
|
12596
12789
|
descriptor = undefined;
|
|
12597
12790
|
renameSync3(temporary, path);
|
|
12598
|
-
syncArchiveDirectory(
|
|
12791
|
+
syncArchiveDirectory(dirname9(path));
|
|
12599
12792
|
} finally {
|
|
12600
12793
|
if (descriptor !== undefined)
|
|
12601
|
-
|
|
12602
|
-
if (
|
|
12794
|
+
closeSync4(descriptor);
|
|
12795
|
+
if (lstatSync10(temporary, { throwIfNoEntry: false }))
|
|
12603
12796
|
unlinkSync2(temporary);
|
|
12604
12797
|
}
|
|
12605
12798
|
}
|
|
@@ -12610,7 +12803,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
12610
12803
|
recheckRootAliases(aliases);
|
|
12611
12804
|
for (const entry of inventory.filter((entry2) => entry2.bridge)) {
|
|
12612
12805
|
const adapter = AGENT_ADAPTERS[entry.agent];
|
|
12613
|
-
const expected = adapter && entry.bridgeHome ? canonicalAgentPath(
|
|
12806
|
+
const expected = adapter && entry.bridgeHome ? canonicalAgentPath(join16(entry.bridgeHome, adapter.root, CLI_BRIDGE_NAME), aliases) : undefined;
|
|
12614
12807
|
if (!expected || treeHash(entry.path) !== entry.hash || !isOwnedCliBridge(entry.path, [expected]))
|
|
12615
12808
|
throw new Error(`Skills bridge changed after planning: ${entry.path}`);
|
|
12616
12809
|
}
|
|
@@ -12620,11 +12813,11 @@ function archiveNativeSkills(inventory, options) {
|
|
|
12620
12813
|
throw new Error(`Native skill changed after planning: ${entry.path}`);
|
|
12621
12814
|
if (!selected.length)
|
|
12622
12815
|
return { entries: [], ...aliases.length ? { rootAliases: aliases } : {} };
|
|
12623
|
-
const operationId = randomUUID2(), archiveRoot =
|
|
12816
|
+
const operationId = randomUUID2(), archiveRoot = join16(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join16(archiveRoot, "receipt.json");
|
|
12624
12817
|
const entries = selected.map((entry, index) => ({
|
|
12625
|
-
source: entry.vendor ?
|
|
12626
|
-
archive:
|
|
12627
|
-
hash: entry.vendor ? sha(readNativeBytes(
|
|
12818
|
+
source: entry.vendor ? join16(entry.path, "SKILL.md") : entry.path,
|
|
12819
|
+
archive: join16(archiveRoot, `${index}-${sha(entry.path).slice(0, 12)}`),
|
|
12820
|
+
hash: entry.vendor ? sha(readNativeBytes(join16(entry.path, "SKILL.md"))) : entry.hash,
|
|
12628
12821
|
...entry.vendor ? { discoveryOnly: true } : {},
|
|
12629
12822
|
status: "planned"
|
|
12630
12823
|
}));
|
|
@@ -12632,7 +12825,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
12632
12825
|
const moved = [];
|
|
12633
12826
|
const verifyArchive = (entry) => {
|
|
12634
12827
|
assertSafePath(entry.archive);
|
|
12635
|
-
const stat =
|
|
12828
|
+
const stat = lstatSync10(entry.archive);
|
|
12636
12829
|
if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
|
|
12637
12830
|
throw new Error("Archive type changed");
|
|
12638
12831
|
if ((entry.discoveryOnly ? sha(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
|
|
@@ -12640,14 +12833,14 @@ function archiveNativeSkills(inventory, options) {
|
|
|
12640
12833
|
};
|
|
12641
12834
|
assertSafePath(archiveRoot);
|
|
12642
12835
|
const created = [];
|
|
12643
|
-
for (let path = archiveRoot;!existsSync10(path); path =
|
|
12836
|
+
for (let path = archiveRoot;!existsSync10(path); path = dirname9(path))
|
|
12644
12837
|
created.push(path);
|
|
12645
12838
|
mkdirSync5(archiveRoot, { recursive: true, mode: 448 });
|
|
12646
12839
|
try {
|
|
12647
12840
|
for (const directory of created)
|
|
12648
12841
|
syncArchiveDirectory(directory);
|
|
12649
12842
|
if (created.length)
|
|
12650
|
-
syncArchiveDirectory(
|
|
12843
|
+
syncArchiveDirectory(dirname9(created.at(-1)));
|
|
12651
12844
|
writeArchiveJournal(receiptPath, journal);
|
|
12652
12845
|
for (const [index, entry] of selected.entries()) {
|
|
12653
12846
|
recheckRootAliases(aliases);
|
|
@@ -12659,7 +12852,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
12659
12852
|
writeArchiveJournal(receiptPath, journal);
|
|
12660
12853
|
renameSync3(move.source, move.archive);
|
|
12661
12854
|
moved.push(move);
|
|
12662
|
-
syncArchiveDirectory(
|
|
12855
|
+
syncArchiveDirectory(dirname9(move.source));
|
|
12663
12856
|
syncArchiveDirectory(archiveRoot);
|
|
12664
12857
|
verifyArchive(move);
|
|
12665
12858
|
move.status = "archived";
|
|
@@ -12680,7 +12873,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
12680
12873
|
recheckRootAliases(aliases);
|
|
12681
12874
|
assertSafePath(entry.source);
|
|
12682
12875
|
assertSafePath(entry.archive);
|
|
12683
|
-
if (
|
|
12876
|
+
if (lstatSync10(entry.source, { throwIfNoEntry: false })) {
|
|
12684
12877
|
entry.status = "recovery-required";
|
|
12685
12878
|
entry.conflict = "source-occupied";
|
|
12686
12879
|
continue;
|
|
@@ -12694,10 +12887,10 @@ function archiveNativeSkills(inventory, options) {
|
|
|
12694
12887
|
}
|
|
12695
12888
|
if (entry.discoveryOnly) {
|
|
12696
12889
|
linkSync(entry.archive, entry.source);
|
|
12697
|
-
syncArchiveDirectory(
|
|
12890
|
+
syncArchiveDirectory(dirname9(entry.source));
|
|
12698
12891
|
unlinkSync2(entry.archive);
|
|
12699
12892
|
} else {
|
|
12700
|
-
if (
|
|
12893
|
+
if (lstatSync10(entry.source, { throwIfNoEntry: false })) {
|
|
12701
12894
|
entry.status = "recovery-required";
|
|
12702
12895
|
entry.conflict = "source-occupied";
|
|
12703
12896
|
continue;
|
|
@@ -12705,7 +12898,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
12705
12898
|
renameSync3(entry.archive, entry.source);
|
|
12706
12899
|
}
|
|
12707
12900
|
entry.status = "restored";
|
|
12708
|
-
syncArchiveDirectory(
|
|
12901
|
+
syncArchiveDirectory(dirname9(entry.source));
|
|
12709
12902
|
syncArchiveDirectory(archiveRoot);
|
|
12710
12903
|
} catch {
|
|
12711
12904
|
entry.status = "recovery-required";
|
|
@@ -12742,9 +12935,9 @@ function isSkillsOwnershipMarker(marker) {
|
|
|
12742
12935
|
return typeof marker === "object" && marker !== null && Object.hasOwn(marker, "managedBy") && marker.managedBy === SYNC_MARKER_MANAGED_BY;
|
|
12743
12936
|
}
|
|
12744
12937
|
function hasSkillsOwnershipMarker(dir) {
|
|
12745
|
-
const path =
|
|
12938
|
+
const path = join17(dir, SYNC_MARKER_FILE);
|
|
12746
12939
|
try {
|
|
12747
|
-
if (!
|
|
12940
|
+
if (!lstatSync11(path).isFile())
|
|
12748
12941
|
return false;
|
|
12749
12942
|
const marker = JSON.parse(readFileSync12(path, "utf8"));
|
|
12750
12943
|
return isSkillsOwnershipMarker(marker);
|
|
@@ -12766,9 +12959,9 @@ function resolveSyncAgents(arg) {
|
|
|
12766
12959
|
function agentGlobalSkillsDir(agent, homeDir = homedir3()) {
|
|
12767
12960
|
switch (agent) {
|
|
12768
12961
|
case "opencode":
|
|
12769
|
-
return
|
|
12962
|
+
return join17(homeDir, ".config", "opencode", "skills");
|
|
12770
12963
|
default:
|
|
12771
|
-
return
|
|
12964
|
+
return join17(homeDir, `.${agent}`, "skills");
|
|
12772
12965
|
}
|
|
12773
12966
|
}
|
|
12774
12967
|
function adaptSkillMdForAgent(skillMd, agent) {
|
|
@@ -12827,7 +13020,7 @@ function resolveSyncCorpus(options = {}) {
|
|
|
12827
13020
|
function packageSourceRoots(source) {
|
|
12828
13021
|
const roots = [];
|
|
12829
13022
|
for (const sub of ["skills"]) {
|
|
12830
|
-
const candidate =
|
|
13023
|
+
const candidate = join17(source, sub);
|
|
12831
13024
|
if (existsSync11(candidate) && isDirectory(candidate))
|
|
12832
13025
|
roots.push(candidate);
|
|
12833
13026
|
}
|
|
@@ -12843,10 +13036,10 @@ function containsSkillDirectories(path) {
|
|
|
12843
13036
|
return false;
|
|
12844
13037
|
}
|
|
12845
13038
|
return entries.some((entry) => {
|
|
12846
|
-
const candidate =
|
|
13039
|
+
const candidate = join17(path, entry);
|
|
12847
13040
|
if (!isDirectory(candidate))
|
|
12848
13041
|
return false;
|
|
12849
|
-
return existsSync11(
|
|
13042
|
+
return existsSync11(join17(candidate, "SKILL.md")) || existsSync11(join17(candidate, "skill.json")) || existsSync11(join17(candidate, "package.json"));
|
|
12850
13043
|
});
|
|
12851
13044
|
}
|
|
12852
13045
|
function isDirectory(path) {
|
|
@@ -12886,7 +13079,7 @@ function syncSkillsToAgents(options = {}) {
|
|
|
12886
13079
|
actions.push({
|
|
12887
13080
|
skill: name,
|
|
12888
13081
|
agent,
|
|
12889
|
-
path:
|
|
13082
|
+
path: join17(agentGlobalSkillsDir(agent, homeDir), name, "SKILL.md"),
|
|
12890
13083
|
action: "skip",
|
|
12891
13084
|
reason: "not found in this machine's corpus"
|
|
12892
13085
|
});
|
|
@@ -12916,7 +13109,7 @@ function syncSkillsToAgents(options = {}) {
|
|
|
12916
13109
|
}
|
|
12917
13110
|
function writeManagedAgentSkill(params) {
|
|
12918
13111
|
const homeDir = params.homeDir ?? homedir3();
|
|
12919
|
-
const dir =
|
|
13112
|
+
const dir = join17(agentGlobalSkillsDir(params.agent, homeDir), params.skill);
|
|
12920
13113
|
const result = writeManagedSkillDir(dir, params.skillMd, {
|
|
12921
13114
|
skill: params.skill,
|
|
12922
13115
|
source: params.source,
|
|
@@ -12933,8 +13126,8 @@ function writeManagedAgentSkill(params) {
|
|
|
12933
13126
|
};
|
|
12934
13127
|
}
|
|
12935
13128
|
function writeManagedSkillDir(dir, skillMd, options) {
|
|
12936
|
-
const skillMdPath =
|
|
12937
|
-
const markerPath =
|
|
13129
|
+
const skillMdPath = join17(dir, "SKILL.md");
|
|
13130
|
+
const markerPath = join17(dir, SYNC_MARKER_FILE);
|
|
12938
13131
|
const dirExists = existsSync11(dir);
|
|
12939
13132
|
const managed = hasSkillsOwnershipMarker(dir);
|
|
12940
13133
|
const hasSkillMd = existsSync11(skillMdPath);
|
|
@@ -12970,13 +13163,13 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
12970
13163
|
const action = dirExists ? "update" : "create";
|
|
12971
13164
|
if (options.dryRun)
|
|
12972
13165
|
return { action, path: skillMdPath };
|
|
12973
|
-
const parentDir =
|
|
13166
|
+
const parentDir = dirname10(dir);
|
|
12974
13167
|
mkdirSync6(parentDir, { recursive: true });
|
|
12975
|
-
const transactionDir = mkdtempSync2(
|
|
12976
|
-
const candidateDir =
|
|
12977
|
-
const backupDir =
|
|
12978
|
-
const candidateSkillMdPath =
|
|
12979
|
-
const candidateMarkerPath =
|
|
13168
|
+
const transactionDir = mkdtempSync2(join17(parentDir, `.hasna-skills-write-${basename3(dir)}-`));
|
|
13169
|
+
const candidateDir = join17(transactionDir, "candidate");
|
|
13170
|
+
const backupDir = join17(transactionDir, "backup");
|
|
13171
|
+
const candidateSkillMdPath = join17(candidateDir, "SKILL.md");
|
|
13172
|
+
const candidateMarkerPath = join17(candidateDir, SYNC_MARKER_FILE);
|
|
12980
13173
|
const marker = {
|
|
12981
13174
|
managedBy: SYNC_MARKER_MANAGED_BY,
|
|
12982
13175
|
skill: options.skill,
|
|
@@ -13025,7 +13218,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
13025
13218
|
return { action, path: skillMdPath };
|
|
13026
13219
|
}
|
|
13027
13220
|
function removeManagedAgentSkill(skill, agent, homeDir = homedir3()) {
|
|
13028
|
-
const dir =
|
|
13221
|
+
const dir = join17(agentGlobalSkillsDir(agent, homeDir), skill);
|
|
13029
13222
|
if (!hasSkillsOwnershipMarker(dir))
|
|
13030
13223
|
return false;
|
|
13031
13224
|
rmSync2(dir, { recursive: true, force: true });
|
|
@@ -13033,7 +13226,7 @@ function removeManagedAgentSkill(skill, agent, homeDir = homedir3()) {
|
|
|
13033
13226
|
}
|
|
13034
13227
|
function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = false) {
|
|
13035
13228
|
if (kind === undefined || kind === "instruction" || preferBundledDocs) {
|
|
13036
|
-
const skillMdPath =
|
|
13229
|
+
const skillMdPath = join17(skillPath, "SKILL.md");
|
|
13037
13230
|
if (existsSync11(skillMdPath))
|
|
13038
13231
|
return readFileSync12(skillMdPath, "utf-8");
|
|
13039
13232
|
}
|
|
@@ -13066,7 +13259,7 @@ function normalizeSkillName(name) {
|
|
|
13066
13259
|
|
|
13067
13260
|
// src/lib/project-state.ts
|
|
13068
13261
|
import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
|
|
13069
|
-
import { join as
|
|
13262
|
+
import { join as join18 } from "path";
|
|
13070
13263
|
var VALID_PIN_SOURCES = [
|
|
13071
13264
|
"official",
|
|
13072
13265
|
"custom",
|
|
@@ -13081,10 +13274,10 @@ var SKILLS_PROJECT_DIR = ".skills";
|
|
|
13081
13274
|
var PROJECT_CONFIG_FILE = "project.json";
|
|
13082
13275
|
var DEFAULT_EXPORT_DIR = ".skills/exports";
|
|
13083
13276
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
13084
|
-
return
|
|
13277
|
+
return join18(targetDir, SKILLS_PROJECT_DIR);
|
|
13085
13278
|
}
|
|
13086
13279
|
function getProjectConfigPath(targetDir = process.cwd()) {
|
|
13087
|
-
return
|
|
13280
|
+
return join18(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
|
|
13088
13281
|
}
|
|
13089
13282
|
function loadProjectConfig(targetDir = process.cwd()) {
|
|
13090
13283
|
const path = getProjectConfigPath(targetDir);
|
|
@@ -13205,16 +13398,16 @@ function isPinSource(value) {
|
|
|
13205
13398
|
}
|
|
13206
13399
|
|
|
13207
13400
|
// src/lib/installer.ts
|
|
13208
|
-
var __dirname2 =
|
|
13401
|
+
var __dirname2 = dirname11(fileURLToPath(import.meta.url));
|
|
13209
13402
|
function findSkillsDir() {
|
|
13210
13403
|
let dir = __dirname2;
|
|
13211
13404
|
for (let i = 0;i < 5; i++) {
|
|
13212
|
-
const candidate =
|
|
13405
|
+
const candidate = join19(dir, "skills");
|
|
13213
13406
|
if (existsSync13(candidate) && !dir.includes(".skills"))
|
|
13214
13407
|
return candidate;
|
|
13215
|
-
dir =
|
|
13408
|
+
dir = dirname11(dir);
|
|
13216
13409
|
}
|
|
13217
|
-
return
|
|
13410
|
+
return join19(__dirname2, "..", "skills");
|
|
13218
13411
|
}
|
|
13219
13412
|
var SKILLS_DIR = findSkillsDir();
|
|
13220
13413
|
function getSkillPath(name) {
|
|
@@ -13222,13 +13415,13 @@ function getSkillPath(name) {
|
|
|
13222
13415
|
const portable = findPortableSkill(skillName);
|
|
13223
13416
|
if (portable)
|
|
13224
13417
|
return portable.path;
|
|
13225
|
-
const legacyCustomPath =
|
|
13418
|
+
const legacyCustomPath = join19(getDataDir(), "custom", skillName);
|
|
13226
13419
|
if (existsSync13(legacyCustomPath))
|
|
13227
13420
|
return legacyCustomPath;
|
|
13228
13421
|
const extensionPath = findExtensionSkillPath(skillName);
|
|
13229
13422
|
if (extensionPath)
|
|
13230
13423
|
return extensionPath;
|
|
13231
|
-
return
|
|
13424
|
+
return join19(SKILLS_DIR, skillName);
|
|
13232
13425
|
}
|
|
13233
13426
|
function getCanonicalSkillName(name) {
|
|
13234
13427
|
return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
|
|
@@ -13291,7 +13484,7 @@ function createLocalSkillManifest(name, generateSkillMd) {
|
|
|
13291
13484
|
if (!existsSync13(sourcePath))
|
|
13292
13485
|
return null;
|
|
13293
13486
|
let skillMd = "";
|
|
13294
|
-
const skillMdPath =
|
|
13487
|
+
const skillMdPath = join19(sourcePath, "SKILL.md");
|
|
13295
13488
|
if (existsSync13(skillMdPath)) {
|
|
13296
13489
|
skillMd = readFileSync14(skillMdPath, "utf-8");
|
|
13297
13490
|
} else if (generateSkillMd) {
|
|
@@ -13365,16 +13558,16 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
|
13365
13558
|
const base = projectDir || process.cwd();
|
|
13366
13559
|
switch (agent) {
|
|
13367
13560
|
case "pi":
|
|
13368
|
-
return scope === "project" ?
|
|
13561
|
+
return scope === "project" ? join19(base, ".pi", "skills") : join19(homedir4(), ".pi", "agent", "skills");
|
|
13369
13562
|
case "opencode":
|
|
13370
|
-
return scope === "project" ?
|
|
13563
|
+
return scope === "project" ? join19(base, ".opencode", "skills") : join19(homedir4(), ".config", "opencode", "skills");
|
|
13371
13564
|
default:
|
|
13372
|
-
return scope === "project" ?
|
|
13565
|
+
return scope === "project" ? join19(base, `.${agent}`, "skills") : join19(homedir4(), `.${agent}`, "skills");
|
|
13373
13566
|
}
|
|
13374
13567
|
}
|
|
13375
13568
|
function getAgentSkillPath(name, agent, scope = "global", projectDir) {
|
|
13376
13569
|
const skillName = normalizeSkillName(getCanonicalSkillName(name));
|
|
13377
|
-
return
|
|
13570
|
+
return join19(getAgentSkillsDir(agent, scope, projectDir), skillName);
|
|
13378
13571
|
}
|
|
13379
13572
|
function installSkillForAgent(name, options, generateSkillMd) {
|
|
13380
13573
|
assertNativeExportAllowed();
|
|
@@ -13410,7 +13603,7 @@ function removeSkillForAgent(name, options) {
|
|
|
13410
13603
|
}
|
|
13411
13604
|
function resolveAgentSkillMd(name, generateSkillMd) {
|
|
13412
13605
|
const sourcePath = getSkillPath(name);
|
|
13413
|
-
const skillMdPath =
|
|
13606
|
+
const skillMdPath = join19(sourcePath, "SKILL.md");
|
|
13414
13607
|
if (existsSync13(skillMdPath))
|
|
13415
13608
|
return readFileSync14(skillMdPath, "utf-8");
|
|
13416
13609
|
if (generateSkillMd)
|
|
@@ -13445,7 +13638,7 @@ function generateMinimalSkillMd(name) {
|
|
|
13445
13638
|
"---",
|
|
13446
13639
|
""
|
|
13447
13640
|
].filter(Boolean);
|
|
13448
|
-
const fallbackDoc = readFileIfExists(
|
|
13641
|
+
const fallbackDoc = readFileIfExists(join19(sourcePath, "README.md")) || readFileIfExists(join19(sourcePath, "CLAUDE.md"));
|
|
13449
13642
|
if (fallbackDoc)
|
|
13450
13643
|
return `${frontmatter.join(`
|
|
13451
13644
|
`)}${fallbackDoc.trim()}
|
|
@@ -13464,7 +13657,7 @@ skills run ${canonicalName}
|
|
|
13464
13657
|
`;
|
|
13465
13658
|
}
|
|
13466
13659
|
function readBundledSkillVersion(name) {
|
|
13467
|
-
const pkgPath =
|
|
13660
|
+
const pkgPath = join19(getSkillPath(name), "package.json");
|
|
13468
13661
|
if (!existsSync13(pkgPath))
|
|
13469
13662
|
return "unknown";
|
|
13470
13663
|
try {
|
|
@@ -13481,21 +13674,21 @@ function loadProjectConfigCompat(targetDir) {
|
|
|
13481
13674
|
return loadProjectConfig(targetDir);
|
|
13482
13675
|
}
|
|
13483
13676
|
// src/lib/run-state.ts
|
|
13484
|
-
import { createHash as
|
|
13677
|
+
import { createHash as createHash9, randomBytes } from "crypto";
|
|
13485
13678
|
import { existsSync as existsSync14, mkdirSync as mkdirSync8, readFileSync as readFileSync15, readdirSync as readdirSync12, statSync as statSync10, writeFileSync as writeFileSync8 } from "fs";
|
|
13486
|
-
import { extname, join as
|
|
13679
|
+
import { extname, join as join20, relative as relative5 } from "path";
|
|
13487
13680
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
13488
13681
|
const now = new Date;
|
|
13489
13682
|
const id = createRunId(now);
|
|
13490
13683
|
const day = now.toISOString().slice(0, 10);
|
|
13491
13684
|
const skillName = normalizeSkillName(params.skill);
|
|
13492
13685
|
const root = getProjectStateDir(targetDir);
|
|
13493
|
-
const runDir =
|
|
13494
|
-
const logsDir =
|
|
13495
|
-
const exportDir =
|
|
13686
|
+
const runDir = join20(root, "runs", day, id);
|
|
13687
|
+
const logsDir = join20(runDir, "logs");
|
|
13688
|
+
const exportDir = join20(root, "exports", skillName, id);
|
|
13496
13689
|
mkdirSync8(logsDir, { recursive: true });
|
|
13497
13690
|
mkdirSync8(exportDir, { recursive: true });
|
|
13498
|
-
mkdirSync8(
|
|
13691
|
+
mkdirSync8(join20(root, "tmp"), { recursive: true });
|
|
13499
13692
|
const record2 = {
|
|
13500
13693
|
id,
|
|
13501
13694
|
skill: skillName,
|
|
@@ -13547,27 +13740,27 @@ function updateSkillRun(context, patch) {
|
|
|
13547
13740
|
return context.record;
|
|
13548
13741
|
}
|
|
13549
13742
|
function writeRunLogs(context, stdout = "", stderr = "") {
|
|
13550
|
-
writeFileSync8(
|
|
13551
|
-
writeFileSync8(
|
|
13743
|
+
writeFileSync8(join20(context.logsDir, "stdout.log"), stdout);
|
|
13744
|
+
writeFileSync8(join20(context.logsDir, "stderr.log"), stderr);
|
|
13552
13745
|
}
|
|
13553
13746
|
function appendRunEvent(context, event, data = {}) {
|
|
13554
13747
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
13555
13748
|
`;
|
|
13556
|
-
const path =
|
|
13749
|
+
const path = join20(context.runDir, "events.ndjson");
|
|
13557
13750
|
const previous = existsSync14(path) ? readFileSync15(path, "utf-8") : "";
|
|
13558
13751
|
writeFileSync8(path, previous + line);
|
|
13559
13752
|
}
|
|
13560
13753
|
function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
13561
|
-
const runsRoot =
|
|
13754
|
+
const runsRoot = join20(getProjectStateDir(targetDir), "runs");
|
|
13562
13755
|
if (!existsSync14(runsRoot))
|
|
13563
13756
|
return [];
|
|
13564
13757
|
const records = [];
|
|
13565
13758
|
for (const day of readdirSync12(runsRoot).sort().reverse()) {
|
|
13566
|
-
const dayDir =
|
|
13759
|
+
const dayDir = join20(runsRoot, day);
|
|
13567
13760
|
if (!statSync10(dayDir).isDirectory())
|
|
13568
13761
|
continue;
|
|
13569
13762
|
for (const runId of readdirSync12(dayDir).sort().reverse()) {
|
|
13570
|
-
const record2 = readRunRecord(
|
|
13763
|
+
const record2 = readRunRecord(join20(dayDir, runId));
|
|
13571
13764
|
if (record2)
|
|
13572
13765
|
records.push(record2);
|
|
13573
13766
|
if (records.length >= limit)
|
|
@@ -13577,25 +13770,25 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
|
13577
13770
|
return records;
|
|
13578
13771
|
}
|
|
13579
13772
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
13580
|
-
const runsRoot =
|
|
13773
|
+
const runsRoot = join20(getProjectStateDir(targetDir), "runs");
|
|
13581
13774
|
if (!existsSync14(runsRoot))
|
|
13582
13775
|
return null;
|
|
13583
13776
|
for (const day of readdirSync12(runsRoot)) {
|
|
13584
|
-
const record2 = readRunRecord(
|
|
13777
|
+
const record2 = readRunRecord(join20(runsRoot, day, runId));
|
|
13585
13778
|
if (record2)
|
|
13586
13779
|
return record2;
|
|
13587
13780
|
}
|
|
13588
13781
|
return null;
|
|
13589
13782
|
}
|
|
13590
13783
|
function getRunExportDir(runId, skill, targetDir = process.cwd()) {
|
|
13591
|
-
return
|
|
13784
|
+
return join20(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
|
|
13592
13785
|
}
|
|
13593
13786
|
function writeRunRecord(context) {
|
|
13594
|
-
writeFileSync8(
|
|
13787
|
+
writeFileSync8(join20(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
|
|
13595
13788
|
`);
|
|
13596
13789
|
}
|
|
13597
13790
|
function writeArtifactsManifest(context, artifacts) {
|
|
13598
|
-
writeFileSync8(
|
|
13791
|
+
writeFileSync8(join20(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
|
|
13599
13792
|
`);
|
|
13600
13793
|
}
|
|
13601
13794
|
function collectRunArtifacts(context) {
|
|
@@ -13608,14 +13801,14 @@ function collectRunArtifacts(context) {
|
|
|
13608
13801
|
artifacts.push({
|
|
13609
13802
|
path: toProjectRelative(context.targetDir, path),
|
|
13610
13803
|
mime: mimeForPath(path),
|
|
13611
|
-
sha256:
|
|
13804
|
+
sha256: createHash9("sha256").update(bytes).digest("hex"),
|
|
13612
13805
|
sizeBytes: stat.size
|
|
13613
13806
|
});
|
|
13614
13807
|
}
|
|
13615
13808
|
return artifacts.sort((a, b) => a.path.localeCompare(b.path));
|
|
13616
13809
|
}
|
|
13617
13810
|
function readRunRecord(runDir) {
|
|
13618
|
-
const path =
|
|
13811
|
+
const path = join20(runDir, "run.json");
|
|
13619
13812
|
if (!existsSync14(path))
|
|
13620
13813
|
return null;
|
|
13621
13814
|
try {
|
|
@@ -13627,7 +13820,7 @@ function readRunRecord(runDir) {
|
|
|
13627
13820
|
function walkFiles(dir) {
|
|
13628
13821
|
const files = [];
|
|
13629
13822
|
for (const entry of readdirSync12(dir)) {
|
|
13630
|
-
const full =
|
|
13823
|
+
const full = join20(dir, entry);
|
|
13631
13824
|
if (statSync10(full).isDirectory())
|
|
13632
13825
|
files.push(...walkFiles(full));
|
|
13633
13826
|
else
|
|
@@ -13673,8 +13866,8 @@ function mimeForPath(path) {
|
|
|
13673
13866
|
}
|
|
13674
13867
|
}
|
|
13675
13868
|
// src/lib/dependency-preparation.ts
|
|
13676
|
-
import { constants as
|
|
13677
|
-
import { join as
|
|
13869
|
+
import { constants as constants5, closeSync as closeSync5, fstatSync as fstatSync5, lstatSync as lstatSync12, mkdirSync as mkdirSync9, openSync as openSync5, readFileSync as readFileSync16, readdirSync as readdirSync13, rmdirSync as rmdirSync2, unlinkSync as unlinkSync3, ftruncateSync, writeSync } from "fs";
|
|
13870
|
+
import { join as join21 } from "path";
|
|
13678
13871
|
|
|
13679
13872
|
// src/lib/preparation-process.ts
|
|
13680
13873
|
function capturePreparationProcess(child, platform = process.platform, signalGroup = (pid, signal) => process.kill(pid, signal)) {
|
|
@@ -13721,15 +13914,15 @@ var DEPENDENCY_PREPARATION_MARKER = ".skills-dependency-preparation";
|
|
|
13721
13914
|
var RECOVERY = "Skill dependency preparation is incomplete. Confirm no installer is running, successfully run bun install --no-save in the skill directory, then remove only .skills-dependency-preparation. See docs/skill-standard.md#recovering-interrupted-dependency-preparation.";
|
|
13722
13915
|
function hasDependencyPreparationMarker(skillPath) {
|
|
13723
13916
|
try {
|
|
13724
|
-
|
|
13917
|
+
lstatSync12(join21(skillPath, DEPENDENCY_PREPARATION_MARKER));
|
|
13725
13918
|
return true;
|
|
13726
13919
|
} catch (error) {
|
|
13727
13920
|
return error.code !== "ENOENT";
|
|
13728
13921
|
}
|
|
13729
13922
|
}
|
|
13730
13923
|
function claimPreparation(skillPath) {
|
|
13731
|
-
const marker =
|
|
13732
|
-
const state =
|
|
13924
|
+
const marker = join21(skillPath, DEPENDENCY_PREPARATION_MARKER);
|
|
13925
|
+
const state = join21(marker, "state.json"), active = join21(marker, "active");
|
|
13733
13926
|
let created = false;
|
|
13734
13927
|
try {
|
|
13735
13928
|
mkdirSync9(marker, { mode: 448 });
|
|
@@ -13740,21 +13933,21 @@ function claimPreparation(skillPath) {
|
|
|
13740
13933
|
}
|
|
13741
13934
|
let fd;
|
|
13742
13935
|
try {
|
|
13743
|
-
if (!
|
|
13936
|
+
if (!lstatSync12(marker).isDirectory() || lstatSync12(marker).isSymbolicLink())
|
|
13744
13937
|
return;
|
|
13745
13938
|
if (!created) {
|
|
13746
13939
|
if (readdirSync13(marker).some((name) => name !== "state.json"))
|
|
13747
13940
|
return;
|
|
13748
|
-
if (!
|
|
13941
|
+
if (!lstatSync12(state).isFile() || lstatSync12(state).isSymbolicLink() || lstatSync12(state).size > 1024)
|
|
13749
13942
|
return;
|
|
13750
13943
|
const previous = JSON.parse(readFileSync16(state, "utf8"));
|
|
13751
13944
|
if (previous.version !== 1 || previous.status !== "failed" || Object.keys(previous).length !== 2)
|
|
13752
13945
|
return;
|
|
13753
13946
|
}
|
|
13754
13947
|
mkdirSync9(active, { mode: 448 });
|
|
13755
|
-
fd =
|
|
13756
|
-
if (!
|
|
13757
|
-
|
|
13948
|
+
fd = openSync5(state, constants5.O_RDWR | constants5.O_NOFOLLOW | (created ? constants5.O_CREAT | constants5.O_EXCL : 0), 384);
|
|
13949
|
+
if (!fstatSync5(fd).isFile()) {
|
|
13950
|
+
closeSync5(fd);
|
|
13758
13951
|
return;
|
|
13759
13952
|
}
|
|
13760
13953
|
const descriptor = fd;
|
|
@@ -13766,7 +13959,7 @@ function claimPreparation(skillPath) {
|
|
|
13766
13959
|
record2("pending");
|
|
13767
13960
|
return { abandon() {
|
|
13768
13961
|
try {
|
|
13769
|
-
|
|
13962
|
+
closeSync5(descriptor);
|
|
13770
13963
|
} catch {}
|
|
13771
13964
|
}, finish(success) {
|
|
13772
13965
|
try {
|
|
@@ -13774,7 +13967,7 @@ function claimPreparation(skillPath) {
|
|
|
13774
13967
|
unlinkSync3(state);
|
|
13775
13968
|
else
|
|
13776
13969
|
record2("failed");
|
|
13777
|
-
|
|
13970
|
+
closeSync5(descriptor);
|
|
13778
13971
|
fd = undefined;
|
|
13779
13972
|
rmdirSync2(active);
|
|
13780
13973
|
if (success)
|
|
@@ -13782,7 +13975,7 @@ function claimPreparation(skillPath) {
|
|
|
13782
13975
|
return true;
|
|
13783
13976
|
} catch {
|
|
13784
13977
|
try {
|
|
13785
|
-
|
|
13978
|
+
closeSync5(descriptor);
|
|
13786
13979
|
} catch {}
|
|
13787
13980
|
return false;
|
|
13788
13981
|
}
|
|
@@ -13790,7 +13983,7 @@ function claimPreparation(skillPath) {
|
|
|
13790
13983
|
} catch {
|
|
13791
13984
|
if (fd !== undefined)
|
|
13792
13985
|
try {
|
|
13793
|
-
|
|
13986
|
+
closeSync5(fd);
|
|
13794
13987
|
} catch {}
|
|
13795
13988
|
return;
|
|
13796
13989
|
}
|
|
@@ -13881,11 +14074,11 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
|
|
|
13881
14074
|
|
|
13882
14075
|
// src/lib/skillinfo.ts
|
|
13883
14076
|
import { existsSync as existsSync15, readFileSync as readFileSync17 } from "fs";
|
|
13884
|
-
import { join as
|
|
14077
|
+
import { join as join22 } from "path";
|
|
13885
14078
|
function isInstructionSkillDir(skillPath, meta) {
|
|
13886
14079
|
if (meta?.kind === "instruction")
|
|
13887
14080
|
return true;
|
|
13888
|
-
const skillMdPath =
|
|
14081
|
+
const skillMdPath = join22(skillPath, "SKILL.md");
|
|
13889
14082
|
if (!existsSync15(skillMdPath))
|
|
13890
14083
|
return false;
|
|
13891
14084
|
try {
|
|
@@ -13915,9 +14108,9 @@ function getSkillDocs(name) {
|
|
|
13915
14108
|
if (!existsSync15(skillPath))
|
|
13916
14109
|
return null;
|
|
13917
14110
|
return {
|
|
13918
|
-
skillMd: readIfExists(
|
|
13919
|
-
readme: readIfExists(
|
|
13920
|
-
claudeMd: readIfExists(
|
|
14111
|
+
skillMd: readIfExists(join22(skillPath, "SKILL.md")),
|
|
14112
|
+
readme: readIfExists(join22(skillPath, "README.md")),
|
|
14113
|
+
claudeMd: readIfExists(join22(skillPath, "CLAUDE.md"))
|
|
13921
14114
|
};
|
|
13922
14115
|
}
|
|
13923
14116
|
function getSkillBestDoc(name) {
|
|
@@ -13932,14 +14125,14 @@ function getSkillRequirements(name) {
|
|
|
13932
14125
|
return null;
|
|
13933
14126
|
const texts = [];
|
|
13934
14127
|
for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
13935
|
-
const content = readIfExists(
|
|
14128
|
+
const content = readIfExists(join22(skillPath, file));
|
|
13936
14129
|
if (content)
|
|
13937
14130
|
texts.push(content);
|
|
13938
14131
|
}
|
|
13939
14132
|
const meta = getSkill(name);
|
|
13940
14133
|
let dependencies = {};
|
|
13941
14134
|
try {
|
|
13942
|
-
dependencies = JSON.parse(readFileSync17(
|
|
14135
|
+
dependencies = JSON.parse(readFileSync17(join22(skillPath, "package.json"), "utf8")).dependencies || {};
|
|
13943
14136
|
} catch {}
|
|
13944
14137
|
return getSkillRequirementsFromContent(name, texts, dependencies, meta);
|
|
13945
14138
|
}
|
|
@@ -14001,7 +14194,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
14001
14194
|
error: `Skill '${name}' is an instruction skill (kind: instruction) and is not runnable. Instruction skills are consumed by coding agents via SKILL.md, not executed with 'skills run'.`
|
|
14002
14195
|
};
|
|
14003
14196
|
}
|
|
14004
|
-
const pkgPath =
|
|
14197
|
+
const pkgPath = join22(skillPath, "package.json");
|
|
14005
14198
|
if (!existsSync15(pkgPath)) {
|
|
14006
14199
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
14007
14200
|
}
|
|
@@ -14021,12 +14214,12 @@ async function runSkill(name, args, options = {}) {
|
|
|
14021
14214
|
} catch {
|
|
14022
14215
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
14023
14216
|
}
|
|
14024
|
-
const entryPath =
|
|
14217
|
+
const entryPath = join22(skillPath, entryPoint);
|
|
14025
14218
|
if (!existsSync15(entryPath)) {
|
|
14026
14219
|
return { exitCode: 1, error: `Entry point '${entryPoint}' not found in skill '${name}'` };
|
|
14027
14220
|
}
|
|
14028
14221
|
const env = { ...process.env, ...options.env };
|
|
14029
|
-
if (!existsSync15(
|
|
14222
|
+
if (!existsSync15(join22(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
|
|
14030
14223
|
const failure = await prepareSkillDependencies(skillPath, env, options.preparationTimeoutMs);
|
|
14031
14224
|
if (failure)
|
|
14032
14225
|
return failure;
|
|
@@ -14104,10 +14297,10 @@ function generateSkillMd(name) {
|
|
|
14104
14297
|
"---"
|
|
14105
14298
|
].join(`
|
|
14106
14299
|
`);
|
|
14107
|
-
const readme = readIfExists(
|
|
14108
|
-
const claudeMd = readIfExists(
|
|
14300
|
+
const readme = readIfExists(join22(skillPath, "README.md"));
|
|
14301
|
+
const claudeMd = readIfExists(join22(skillPath, "CLAUDE.md"));
|
|
14109
14302
|
let cliCommand = null;
|
|
14110
|
-
const pkgPath =
|
|
14303
|
+
const pkgPath = join22(skillPath, "package.json");
|
|
14111
14304
|
if (existsSync15(pkgPath)) {
|
|
14112
14305
|
try {
|
|
14113
14306
|
const pkg = JSON.parse(readFileSync17(pkgPath, "utf-8"));
|
|
@@ -18167,11 +18360,11 @@ var NEVER = INVALID;
|
|
|
18167
18360
|
// ../contracts/dist/client/transport.js
|
|
18168
18361
|
import { isIP } from "net";
|
|
18169
18362
|
import { spawnSync } from "child_process";
|
|
18170
|
-
import { closeSync as
|
|
18363
|
+
import { closeSync as closeSync6, fstatSync as fstatSync6, openSync as openSync6, readFileSync as readFileSync18 } from "fs";
|
|
18171
18364
|
import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
|
|
18172
18365
|
import { createRequire } from "module";
|
|
18173
18366
|
import { hostname as osHostname } from "os";
|
|
18174
|
-
import { isAbsolute as
|
|
18367
|
+
import { isAbsolute as isAbsolute8, join as join23 } from "path";
|
|
18175
18368
|
function envToken(name) {
|
|
18176
18369
|
return name.toUpperCase().replace(/-/g, "_");
|
|
18177
18370
|
}
|
|
@@ -18230,21 +18423,21 @@ function homeDir(env) {
|
|
|
18230
18423
|
}
|
|
18231
18424
|
function absoluteOverride(env, key) {
|
|
18232
18425
|
const value = env[key]?.trim();
|
|
18233
|
-
return value &&
|
|
18426
|
+
return value && isAbsolute8(value) ? value : null;
|
|
18234
18427
|
}
|
|
18235
18428
|
function hasnaHomeDir(env) {
|
|
18236
18429
|
const override = absoluteOverride(env, HASNA_HOME_ENV_KEY);
|
|
18237
18430
|
if (override)
|
|
18238
18431
|
return override;
|
|
18239
18432
|
const home = homeDir(env);
|
|
18240
|
-
return home ?
|
|
18433
|
+
return home ? join23(home, HASNA_HOME_DIR) : null;
|
|
18241
18434
|
}
|
|
18242
18435
|
function appConfigDir(name, env) {
|
|
18243
18436
|
const configRoot = absoluteOverride(env, HASNA_CONFIG_HOME_ENV_KEY);
|
|
18244
18437
|
if (configRoot)
|
|
18245
|
-
return
|
|
18438
|
+
return join23(configRoot, name);
|
|
18246
18439
|
const root = hasnaHomeDir(env);
|
|
18247
|
-
return root ?
|
|
18440
|
+
return root ? join23(root, name, CONFIG_SUBDIR) : null;
|
|
18248
18441
|
}
|
|
18249
18442
|
function credentialDiskSourceList(name, env, profile = null) {
|
|
18250
18443
|
if (!SAFE_APP_SLUG.test(name))
|
|
@@ -18253,7 +18446,7 @@ function credentialDiskSourceList(name, env, profile = null) {
|
|
|
18253
18446
|
if (!directory)
|
|
18254
18447
|
return [];
|
|
18255
18448
|
const file = profile ? `${CREDENTIALS_FILE}-${profile}` : CREDENTIALS_FILE;
|
|
18256
|
-
return [{ path:
|
|
18449
|
+
return [{ path: join23(directory, file), tier: "disk" }];
|
|
18257
18450
|
}
|
|
18258
18451
|
function credentialDiskSources(name, env) {
|
|
18259
18452
|
return credentialDiskSourceList(name, env, null).map((s) => s.path);
|
|
@@ -18307,7 +18500,7 @@ function readAppConfigFile(path) {
|
|
|
18307
18500
|
};
|
|
18308
18501
|
let fd = -1;
|
|
18309
18502
|
try {
|
|
18310
|
-
fd =
|
|
18503
|
+
fd = openSync6(path, O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
|
|
18311
18504
|
} catch (error) {
|
|
18312
18505
|
const code = error.code;
|
|
18313
18506
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
@@ -18317,7 +18510,7 @@ function readAppConfigFile(path) {
|
|
|
18317
18510
|
unsafe(`the path could not be opened (${code ?? "unknown error"})`);
|
|
18318
18511
|
}
|
|
18319
18512
|
try {
|
|
18320
|
-
const before =
|
|
18513
|
+
const before = fstatSync6(fd);
|
|
18321
18514
|
if (!before.isFile())
|
|
18322
18515
|
unsafe("the path is not a regular file");
|
|
18323
18516
|
if (!configFileModeAllowed(before.mode)) {
|
|
@@ -18329,14 +18522,14 @@ function readAppConfigFile(path) {
|
|
|
18329
18522
|
if (before.size > MAX_CREDENTIAL_FILE_BYTES)
|
|
18330
18523
|
unsafe("the file exceeds the size limit");
|
|
18331
18524
|
const bytes = readFileSync18(fd);
|
|
18332
|
-
const after =
|
|
18525
|
+
const after = fstatSync6(fd);
|
|
18333
18526
|
if (!configFileReadsCoherent(before, after)) {
|
|
18334
18527
|
unsafe("the file changed while being read");
|
|
18335
18528
|
}
|
|
18336
18529
|
return parseEnvFile(bytes.toString("utf8"));
|
|
18337
18530
|
} finally {
|
|
18338
18531
|
if (fd !== -1)
|
|
18339
|
-
|
|
18532
|
+
closeSync6(fd);
|
|
18340
18533
|
}
|
|
18341
18534
|
}
|
|
18342
18535
|
function readCredentialFile(path, apiKeyKeys) {
|
|
@@ -18851,7 +19044,7 @@ var AUTHORITY_OVERRIDE_HEADERS = new Set([
|
|
|
18851
19044
|
]);
|
|
18852
19045
|
|
|
18853
19046
|
// src/lib/instance-credentials.ts
|
|
18854
|
-
import { closeSync as
|
|
19047
|
+
import { closeSync as closeSync7, constants as constants6, fstatSync as fstatSync7, lstatSync as lstatSync13, openSync as openSync7, readSync as readSync5 } from "fs";
|
|
18855
19048
|
var SKILLS_BOUND_API_URL = "HASNA_SKILLS_BOUND_API_URL";
|
|
18856
19049
|
function selectedSkillsProfile(env, explicit) {
|
|
18857
19050
|
const selected = explicit ?? env.HASNA_PROFILE;
|
|
@@ -18867,7 +19060,7 @@ function skillsProfileCredentialFiles(env, explicit) {
|
|
|
18867
19060
|
}
|
|
18868
19061
|
function fileIdentity(file) {
|
|
18869
19062
|
try {
|
|
18870
|
-
return
|
|
19063
|
+
return lstatSync13(file);
|
|
18871
19064
|
} catch (error) {
|
|
18872
19065
|
if (["ENOENT", "ENOTDIR"].includes(error.code ?? ""))
|
|
18873
19066
|
return null;
|
|
@@ -18888,14 +19081,14 @@ function captureSkillsCredentialFiles(files) {
|
|
|
18888
19081
|
function readMetadataText(file) {
|
|
18889
19082
|
let fd;
|
|
18890
19083
|
try {
|
|
18891
|
-
fd =
|
|
19084
|
+
fd = openSync7(file, constants6.O_RDONLY | constants6.O_NOFOLLOW | constants6.O_NONBLOCK);
|
|
18892
19085
|
} catch (error) {
|
|
18893
19086
|
if (["ENOENT", "ENOTDIR"].includes(error.code ?? ""))
|
|
18894
19087
|
return null;
|
|
18895
19088
|
throw new Error("Cannot safely read Skills instance configuration");
|
|
18896
19089
|
}
|
|
18897
19090
|
try {
|
|
18898
|
-
const before =
|
|
19091
|
+
const before = fstatSync7(fd);
|
|
18899
19092
|
const uid = process.getuid?.() ?? process.geteuid?.();
|
|
18900
19093
|
if (!before.isFile() || ![256, 384].includes(before.mode & 4095) || uid !== undefined && before.uid !== uid || before.size > 64 * 1024) {
|
|
18901
19094
|
throw new Error("Unsafe Skills instance configuration; expected a bounded owner-only regular file");
|
|
@@ -18903,17 +19096,17 @@ function readMetadataText(file) {
|
|
|
18903
19096
|
const bytes = Buffer.alloc(64 * 1024 + 1);
|
|
18904
19097
|
let length = 0;
|
|
18905
19098
|
while (length < bytes.length) {
|
|
18906
|
-
const count =
|
|
19099
|
+
const count = readSync5(fd, bytes, length, bytes.length - length, null);
|
|
18907
19100
|
if (!count)
|
|
18908
19101
|
break;
|
|
18909
19102
|
length += count;
|
|
18910
19103
|
}
|
|
18911
|
-
if (length > 64 * 1024 || !unchanged(before,
|
|
19104
|
+
if (length > 64 * 1024 || !unchanged(before, fstatSync7(fd)) || !unchanged(before, fileIdentity(file))) {
|
|
18912
19105
|
throw new Error("Skills instance configuration changed while reading");
|
|
18913
19106
|
}
|
|
18914
19107
|
return bytes.subarray(0, length).toString("utf8");
|
|
18915
19108
|
} finally {
|
|
18916
|
-
|
|
19109
|
+
closeSync7(fd);
|
|
18917
19110
|
}
|
|
18918
19111
|
}
|
|
18919
19112
|
function readSkillsInstanceMetadata(file) {
|
|
@@ -20547,7 +20740,7 @@ function object3(value) {
|
|
|
20547
20740
|
}
|
|
20548
20741
|
|
|
20549
20742
|
// src/lib/remote-files.ts
|
|
20550
|
-
import { createHash as
|
|
20743
|
+
import { createHash as createHash10 } from "crypto";
|
|
20551
20744
|
var MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
|
|
20552
20745
|
function describeRemoteFiles(files) {
|
|
20553
20746
|
if (files.length > 10)
|
|
@@ -20565,7 +20758,7 @@ function describeRemoteFiles(files) {
|
|
|
20565
20758
|
});
|
|
20566
20759
|
}
|
|
20567
20760
|
function sha256(bytes) {
|
|
20568
|
-
return
|
|
20761
|
+
return createHash10("sha256").update(bytes).digest("hex");
|
|
20569
20762
|
}
|
|
20570
20763
|
async function readBoundedResponse(response, maximum) {
|
|
20571
20764
|
if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
|
|
@@ -20658,10 +20851,10 @@ async function readQuoteUnavailableCode(response) {
|
|
|
20658
20851
|
let timer;
|
|
20659
20852
|
let deadlineExceeded = false;
|
|
20660
20853
|
const expired = Symbol("quote body deadline");
|
|
20661
|
-
const deadline = new Promise((
|
|
20854
|
+
const deadline = new Promise((resolve9) => {
|
|
20662
20855
|
timer = setTimeout(() => {
|
|
20663
20856
|
deadlineExceeded = true;
|
|
20664
|
-
|
|
20857
|
+
resolve9(expired);
|
|
20665
20858
|
reader.cancel().catch(() => {});
|
|
20666
20859
|
}, 1500);
|
|
20667
20860
|
});
|
|
@@ -21476,9 +21669,9 @@ async function createRemoteSkillsClient(env = process.env) {
|
|
|
21476
21669
|
}
|
|
21477
21670
|
// src/lib/scheduler.ts
|
|
21478
21671
|
import { existsSync as existsSync16, readFileSync as readFileSync19, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10 } from "fs";
|
|
21479
|
-
import { join as
|
|
21672
|
+
import { join as join24 } from "path";
|
|
21480
21673
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
21481
|
-
return
|
|
21674
|
+
return join24(targetDir, ".skills", "schedules.json");
|
|
21482
21675
|
}
|
|
21483
21676
|
function loadSchedules(targetDir = process.cwd()) {
|
|
21484
21677
|
const path = getSchedulesPath(targetDir);
|
|
@@ -21491,7 +21684,7 @@ function loadSchedules(targetDir = process.cwd()) {
|
|
|
21491
21684
|
}
|
|
21492
21685
|
function saveSchedules(data, targetDir = process.cwd()) {
|
|
21493
21686
|
const path = getSchedulesPath(targetDir);
|
|
21494
|
-
const dir =
|
|
21687
|
+
const dir = join24(targetDir, ".skills");
|
|
21495
21688
|
if (!existsSync16(dir))
|
|
21496
21689
|
mkdirSync10(dir, { recursive: true });
|
|
21497
21690
|
writeFileSync9(path, JSON.stringify(data, null, 2));
|
|
@@ -21679,7 +21872,7 @@ function recordScheduleRun(id, status, targetDir) {
|
|
|
21679
21872
|
saveSchedules(data, targetDir);
|
|
21680
21873
|
}
|
|
21681
21874
|
// src/lib/revision.ts
|
|
21682
|
-
import { createHash as
|
|
21875
|
+
import { createHash as createHash11 } from "crypto";
|
|
21683
21876
|
var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
|
|
21684
21877
|
function revisionIdOf(content) {
|
|
21685
21878
|
const canonical = JSON.stringify({
|
|
@@ -21695,14 +21888,14 @@ function revisionIdOf(content) {
|
|
|
21695
21888
|
bundleSha256: content.bundleSha256 ?? null,
|
|
21696
21889
|
bundleByteSize: content.bundleByteSize ?? null
|
|
21697
21890
|
});
|
|
21698
|
-
return
|
|
21891
|
+
return createHash11("sha256").update(canonical).digest("hex");
|
|
21699
21892
|
}
|
|
21700
21893
|
function revisionIdOfRecord(record5) {
|
|
21701
21894
|
return revisionIdOf(record5);
|
|
21702
21895
|
}
|
|
21703
21896
|
// src/lib/pull.ts
|
|
21704
21897
|
import { existsSync as existsSync17, mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync3, readFileSync as readFileSync20, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
21705
|
-
import { dirname as
|
|
21898
|
+
import { dirname as dirname12, join as join25 } from "path";
|
|
21706
21899
|
|
|
21707
21900
|
// src/lib/skill-bundles.ts
|
|
21708
21901
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
@@ -21796,7 +21989,7 @@ async function pullOne(client, rawName, corpusOptions, verify) {
|
|
|
21796
21989
|
return reconcileTombstone(slug, corpusOptions);
|
|
21797
21990
|
}
|
|
21798
21991
|
if (bundleResponse.status === 404) {
|
|
21799
|
-
const marker = readPullMarker(
|
|
21992
|
+
const marker = readPullMarker(join25(getPortableSkillsRoot(corpusOptions), slug));
|
|
21800
21993
|
if (isPublishedInstallMarker(marker)) {
|
|
21801
21994
|
return { name: slug, success: true, purged: true, removed: false };
|
|
21802
21995
|
}
|
|
@@ -21835,7 +22028,7 @@ async function pullOne(client, rawName, corpusOptions, verify) {
|
|
|
21835
22028
|
return { name: slug, success: false, error: `Skill '${slug}' was not found on the configured Skills instance.` };
|
|
21836
22029
|
}
|
|
21837
22030
|
if (!meta?.revisionId) {
|
|
21838
|
-
const marker = readPullMarker(
|
|
22031
|
+
const marker = readPullMarker(join25(getPortableSkillsRoot(corpusOptions), slug));
|
|
21839
22032
|
if (isPublishedInstallMarker(marker)) {
|
|
21840
22033
|
return { name: slug, success: true, purged: true, removed: false };
|
|
21841
22034
|
}
|
|
@@ -21892,7 +22085,7 @@ function provenRevision(meta, slug, bundle) {
|
|
|
21892
22085
|
return declared;
|
|
21893
22086
|
}
|
|
21894
22087
|
function reconcileTombstone(slug, corpusOptions) {
|
|
21895
|
-
const target =
|
|
22088
|
+
const target = join25(getPortableSkillsRoot(corpusOptions), slug);
|
|
21896
22089
|
if (!hasSkillsOwnershipMarker(target)) {
|
|
21897
22090
|
return { name: slug, success: true, tombstoned: true, removed: false, leftInPlace: true };
|
|
21898
22091
|
}
|
|
@@ -21901,7 +22094,7 @@ function reconcileTombstone(slug, corpusOptions) {
|
|
|
21901
22094
|
}
|
|
21902
22095
|
function readPullMarker(dir) {
|
|
21903
22096
|
try {
|
|
21904
|
-
return JSON.parse(readFileSync20(
|
|
22097
|
+
return JSON.parse(readFileSync20(join25(dir, PULL_MARKER_FILE), "utf-8"));
|
|
21905
22098
|
} catch {
|
|
21906
22099
|
return null;
|
|
21907
22100
|
}
|
|
@@ -22036,15 +22229,15 @@ function verifyBundleResponseBytes(buffer, response, verify = {}) {
|
|
|
22036
22229
|
function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
22037
22230
|
const root = getPortableSkillsRoot(options);
|
|
22038
22231
|
mkdirSync11(root, { recursive: true });
|
|
22039
|
-
const target =
|
|
22232
|
+
const target = join25(root, name);
|
|
22040
22233
|
const created = !existsSync17(target);
|
|
22041
|
-
const staging = mkdtempSync3(
|
|
22234
|
+
const staging = mkdtempSync3(join25(root, `.pull-${name}-`));
|
|
22042
22235
|
let moved = false;
|
|
22043
22236
|
let backup = null;
|
|
22044
22237
|
try {
|
|
22045
22238
|
for (const entry of entries) {
|
|
22046
|
-
const destination =
|
|
22047
|
-
mkdirSync11(
|
|
22239
|
+
const destination = join25(staging, entry.path);
|
|
22240
|
+
mkdirSync11(dirname12(destination), { recursive: true });
|
|
22048
22241
|
writeFileSync10(destination, entry.bytes, { mode: entry.mode });
|
|
22049
22242
|
}
|
|
22050
22243
|
writePullMarker(staging, {
|
|
@@ -22056,8 +22249,8 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
|
22056
22249
|
...marker.revisionId ? { revisionId: marker.revisionId } : {}
|
|
22057
22250
|
});
|
|
22058
22251
|
if (existsSync17(target)) {
|
|
22059
|
-
backup = mkdtempSync3(
|
|
22060
|
-
renameSync5(target,
|
|
22252
|
+
backup = mkdtempSync3(join25(root, `.pull-backup-${name}-`));
|
|
22253
|
+
renameSync5(target, join25(backup, name));
|
|
22061
22254
|
moved = true;
|
|
22062
22255
|
}
|
|
22063
22256
|
renameSync5(staging, target);
|
|
@@ -22065,9 +22258,9 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
|
22065
22258
|
rmSync4(backup, { recursive: true, force: true });
|
|
22066
22259
|
} catch (error) {
|
|
22067
22260
|
rmSync4(staging, { recursive: true, force: true });
|
|
22068
|
-
if (moved && backup && existsSync17(
|
|
22261
|
+
if (moved && backup && existsSync17(join25(backup, name))) {
|
|
22069
22262
|
try {
|
|
22070
|
-
renameSync5(
|
|
22263
|
+
renameSync5(join25(backup, name), target);
|
|
22071
22264
|
} catch {}
|
|
22072
22265
|
}
|
|
22073
22266
|
throw error;
|
|
@@ -22086,7 +22279,7 @@ function writePullMarker(dir, record5) {
|
|
|
22086
22279
|
...record5.revisionId ? { revisionId: record5.revisionId } : {},
|
|
22087
22280
|
syncedAt: new Date().toISOString()
|
|
22088
22281
|
};
|
|
22089
|
-
writeFileSync10(
|
|
22282
|
+
writeFileSync10(join25(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
|
|
22090
22283
|
`);
|
|
22091
22284
|
}
|
|
22092
22285
|
async function safeMeta(client, slug) {
|
|
@@ -22242,11 +22435,11 @@ function findSkillsParityForMcpTool(tool) {
|
|
|
22242
22435
|
}
|
|
22243
22436
|
// src/lib/registry-sync.ts
|
|
22244
22437
|
import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync11 } from "fs";
|
|
22245
|
-
import { dirname as
|
|
22438
|
+
import { dirname as dirname13, relative as relative6 } from "path";
|
|
22246
22439
|
// package.json
|
|
22247
22440
|
var package_default = {
|
|
22248
22441
|
name: "@hasna/skills",
|
|
22249
|
-
version: "0.7.
|
|
22442
|
+
version: "0.7.2",
|
|
22250
22443
|
description: "Skills library for AI coding agents",
|
|
22251
22444
|
type: "module",
|
|
22252
22445
|
bin: {
|
|
@@ -22428,7 +22621,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
22428
22621
|
};
|
|
22429
22622
|
}
|
|
22430
22623
|
function writeRegistrySyncArtifact(path, artifact) {
|
|
22431
|
-
mkdirSync12(
|
|
22624
|
+
mkdirSync12(dirname13(path), { recursive: true });
|
|
22432
22625
|
writeFileSync11(path, `${JSON.stringify(artifact, null, 2)}
|
|
22433
22626
|
`);
|
|
22434
22627
|
}
|
|
@@ -23502,14 +23695,14 @@ function clone2(value) {
|
|
|
23502
23695
|
}
|
|
23503
23696
|
// src/lib/feedback.ts
|
|
23504
23697
|
import { appendFileSync, existsSync as existsSync18, mkdirSync as mkdirSync13 } from "fs";
|
|
23505
|
-
import { dirname as
|
|
23698
|
+
import { dirname as dirname14, join as join26 } from "path";
|
|
23506
23699
|
import { Database } from "bun:sqlite";
|
|
23507
23700
|
function getFeedbackDbPath() {
|
|
23508
|
-
return
|
|
23701
|
+
return join26(getDataDir(), "skills.db");
|
|
23509
23702
|
}
|
|
23510
23703
|
function getFeedbackDb() {
|
|
23511
23704
|
const dbPath = getFeedbackDbPath();
|
|
23512
|
-
const dir =
|
|
23705
|
+
const dir = dirname14(dbPath);
|
|
23513
23706
|
if (!existsSync18(dir))
|
|
23514
23707
|
mkdirSync13(dir, { recursive: true });
|
|
23515
23708
|
const db = new Database(dbPath);
|
|
@@ -23537,8 +23730,8 @@ function saveFeedback(input) {
|
|
|
23537
23730
|
throw new Error("Feedback message is required");
|
|
23538
23731
|
const category = input.category ?? "general";
|
|
23539
23732
|
if (isApiMode()) {
|
|
23540
|
-
const path =
|
|
23541
|
-
const dir =
|
|
23733
|
+
const path = join26(getDataDir(), "feedback.jsonl");
|
|
23734
|
+
const dir = dirname14(path);
|
|
23542
23735
|
if (!existsSync18(dir))
|
|
23543
23736
|
mkdirSync13(dir, { recursive: true });
|
|
23544
23737
|
appendFileSync(path, JSON.stringify({ message, category, email: input.email ?? null, agent: input.agent ?? null, version: input.version ?? null, createdAt: new Date().toISOString() }) + `
|
|
@@ -23561,7 +23754,7 @@ function isApiMode(env = process.env) {
|
|
|
23561
23754
|
}
|
|
23562
23755
|
}
|
|
23563
23756
|
// src/lib/native-storage.ts
|
|
23564
|
-
import { createHash as
|
|
23757
|
+
import { createHash as createHash12, createHmac as createHmac2 } from "crypto";
|
|
23565
23758
|
import {
|
|
23566
23759
|
existsSync as existsSync19,
|
|
23567
23760
|
mkdirSync as mkdirSync14,
|
|
@@ -23570,7 +23763,7 @@ import {
|
|
|
23570
23763
|
statSync as statSync11,
|
|
23571
23764
|
writeFileSync as writeFileSync12
|
|
23572
23765
|
} from "fs";
|
|
23573
|
-
import { dirname as
|
|
23766
|
+
import { dirname as dirname15, join as join27, normalize as normalize3, relative as relative7, sep as sep5 } from "path";
|
|
23574
23767
|
var SKILLS_STORAGE_TABLES = [
|
|
23575
23768
|
"skills_sync_records",
|
|
23576
23769
|
"skills_sync_cursors"
|
|
@@ -23707,7 +23900,7 @@ function getSkillsNativeStorageStatus(options = {}) {
|
|
|
23707
23900
|
local: {
|
|
23708
23901
|
dataDir: getDataDir(),
|
|
23709
23902
|
projectStateDir: getProjectStateDir(targetDir),
|
|
23710
|
-
feedbackDbPath:
|
|
23903
|
+
feedbackDbPath: join27(getDataDir(), "skills.db")
|
|
23711
23904
|
},
|
|
23712
23905
|
remote: {
|
|
23713
23906
|
databaseConfigured: Boolean(config.databaseUrl),
|
|
@@ -23737,7 +23930,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
23737
23930
|
files.push({
|
|
23738
23931
|
path: relativePath,
|
|
23739
23932
|
sizeBytes: bytes.byteLength,
|
|
23740
|
-
sha256:
|
|
23933
|
+
sha256: createHash12("sha256").update(bytes).digest("hex"),
|
|
23741
23934
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
23742
23935
|
});
|
|
23743
23936
|
}
|
|
@@ -23762,11 +23955,11 @@ function importSkillsLocalSnapshot(snapshot, targetDir = process.cwd(), options
|
|
|
23762
23955
|
continue;
|
|
23763
23956
|
}
|
|
23764
23957
|
const bytes = Buffer.from(file.contentBase64, "base64");
|
|
23765
|
-
const hash2 =
|
|
23958
|
+
const hash2 = createHash12("sha256").update(bytes).digest("hex");
|
|
23766
23959
|
if (hash2 !== file.sha256) {
|
|
23767
23960
|
throw new Error(`Snapshot file checksum mismatch: ${file.path}`);
|
|
23768
23961
|
}
|
|
23769
|
-
mkdirSync14(
|
|
23962
|
+
mkdirSync14(dirname15(absolutePath), { recursive: true });
|
|
23770
23963
|
writeFileSync12(absolutePath, bytes);
|
|
23771
23964
|
written += 1;
|
|
23772
23965
|
}
|
|
@@ -24064,7 +24257,7 @@ function parsePositiveInteger(value) {
|
|
|
24064
24257
|
function walkFiles2(dir) {
|
|
24065
24258
|
const files = [];
|
|
24066
24259
|
for (const entry of readdirSync14(dir)) {
|
|
24067
|
-
const full =
|
|
24260
|
+
const full = join27(dir, entry);
|
|
24068
24261
|
const stats = statSync11(full);
|
|
24069
24262
|
if (stats.isDirectory())
|
|
24070
24263
|
files.push(...walkFiles2(full));
|
|
@@ -24081,7 +24274,7 @@ function resolveSnapshotPath(targetDir, snapshotPath) {
|
|
|
24081
24274
|
if (!toPosix(normalizedPath).startsWith(".skills/")) {
|
|
24082
24275
|
throw new Error(`Snapshot path must stay inside .skills: ${snapshotPath}`);
|
|
24083
24276
|
}
|
|
24084
|
-
return
|
|
24277
|
+
return join27(targetDir, normalizedPath);
|
|
24085
24278
|
}
|
|
24086
24279
|
function normalizeS3Prefix(prefix) {
|
|
24087
24280
|
return (prefix ?? "").trim().replace(/^\/+|\/+$/g, "");
|
|
@@ -24094,7 +24287,7 @@ function toIsoString(value) {
|
|
|
24094
24287
|
return Number.isNaN(date.getTime()) ? value : date.toISOString();
|
|
24095
24288
|
}
|
|
24096
24289
|
function sha256Hex2(value) {
|
|
24097
|
-
return
|
|
24290
|
+
return createHash12("sha256").update(value).digest("hex");
|
|
24098
24291
|
}
|
|
24099
24292
|
function normalizeHeaders(headers) {
|
|
24100
24293
|
const result = {};
|
|
@@ -24146,7 +24339,7 @@ function toArrayBuffer(bytes) {
|
|
|
24146
24339
|
return buffer;
|
|
24147
24340
|
}
|
|
24148
24341
|
// src/lib/station-snapshot.ts
|
|
24149
|
-
import { createHash as
|
|
24342
|
+
import { createHash as createHash13 } from "crypto";
|
|
24150
24343
|
import {
|
|
24151
24344
|
copyFileSync as copyFileSync2,
|
|
24152
24345
|
mkdirSync as mkdirSync15,
|
|
@@ -24154,12 +24347,12 @@ import {
|
|
|
24154
24347
|
statSync as statSync13,
|
|
24155
24348
|
writeFileSync as writeFileSync13
|
|
24156
24349
|
} from "fs";
|
|
24157
|
-
import { dirname as
|
|
24350
|
+
import { dirname as dirname16, isAbsolute as isAbsolute9, relative as relative8, resolve as resolve9, sep as sep7 } from "path";
|
|
24158
24351
|
|
|
24159
24352
|
// src/lib/portable-snapshot-filter.ts
|
|
24160
24353
|
import { readdirSync as readdirSync15, statSync as statSync12 } from "fs";
|
|
24161
24354
|
import { homedir as homedir5 } from "os";
|
|
24162
|
-
import { join as
|
|
24355
|
+
import { join as join28, sep as sep6 } from "path";
|
|
24163
24356
|
var SYNC_HOMES = [
|
|
24164
24357
|
{ name: "skills", subClass: "skills", agent: null },
|
|
24165
24358
|
{ name: "custom", subClass: "custom", agent: null },
|
|
@@ -24252,16 +24445,16 @@ function isPortableWithinSkill(relativeParts) {
|
|
|
24252
24445
|
function homePathFor(definition, homesRoot) {
|
|
24253
24446
|
const home = homesRoot ?? homedir5();
|
|
24254
24447
|
if (definition.subClass === "skills" || definition.subClass === "custom") {
|
|
24255
|
-
return
|
|
24448
|
+
return join28(skillsDataRootForHome(home), definition.name);
|
|
24256
24449
|
}
|
|
24257
24450
|
if (definition.agent === "opencode") {
|
|
24258
|
-
return
|
|
24451
|
+
return join28(home, ".config", "opencode", "skills");
|
|
24259
24452
|
}
|
|
24260
|
-
return
|
|
24453
|
+
return join28(home, `.${definition.agent}`, "skills");
|
|
24261
24454
|
}
|
|
24262
24455
|
function destinationFor(definition, stationId, relativePath) {
|
|
24263
|
-
const category = definition.subClass === "agent-homes" ?
|
|
24264
|
-
return
|
|
24456
|
+
const category = definition.subClass === "agent-homes" ? join28("agent-homes", definition.agent ?? "") : definition.name;
|
|
24457
|
+
return join28("resources", stationId, "skills", category, ...relativePath.split(sep6));
|
|
24265
24458
|
}
|
|
24266
24459
|
function walkEntries(absoluteRoot) {
|
|
24267
24460
|
let entries;
|
|
@@ -24272,7 +24465,7 @@ function walkEntries(absoluteRoot) {
|
|
|
24272
24465
|
}
|
|
24273
24466
|
const output = [];
|
|
24274
24467
|
for (const entry of entries) {
|
|
24275
|
-
const childFull =
|
|
24468
|
+
const childFull = join28(absoluteRoot, entry.name);
|
|
24276
24469
|
if (entry.isSymbolicLink()) {
|
|
24277
24470
|
output.push({ kind: "symlink", relativePath: entry.name, fullPath: childFull });
|
|
24278
24471
|
continue;
|
|
@@ -24283,7 +24476,7 @@ function walkEntries(absoluteRoot) {
|
|
|
24283
24476
|
}
|
|
24284
24477
|
const nested = walkEntries(childFull);
|
|
24285
24478
|
for (const item of nested) {
|
|
24286
|
-
output.push({ ...item, relativePath:
|
|
24479
|
+
output.push({ ...item, relativePath: join28(entry.name, item.relativePath) });
|
|
24287
24480
|
}
|
|
24288
24481
|
continue;
|
|
24289
24482
|
}
|
|
@@ -24321,7 +24514,7 @@ function validateStationId(stationId) {
|
|
|
24321
24514
|
}
|
|
24322
24515
|
}
|
|
24323
24516
|
function sha256File(filePath) {
|
|
24324
|
-
return
|
|
24517
|
+
return createHash13("sha256").update(readFileSync22(filePath)).digest("hex");
|
|
24325
24518
|
}
|
|
24326
24519
|
function scanHome(definition, homesRoot) {
|
|
24327
24520
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -24392,7 +24585,7 @@ function humanHomes(scanned) {
|
|
|
24392
24585
|
}));
|
|
24393
24586
|
}
|
|
24394
24587
|
function writeStationSnapshot(options) {
|
|
24395
|
-
const repoRoot =
|
|
24588
|
+
const repoRoot = resolve9(options.repoRoot ?? process.cwd());
|
|
24396
24589
|
const { scanned, plans, totalBytes } = planStationSnapshot(options);
|
|
24397
24590
|
const manifestFiles = plans.map((plan) => ({
|
|
24398
24591
|
relativePath: plan.source.relativePath,
|
|
@@ -24418,9 +24611,9 @@ function writeStationSnapshot(options) {
|
|
|
24418
24611
|
const conflicts = [];
|
|
24419
24612
|
const untouched = [];
|
|
24420
24613
|
for (const plan of plans) {
|
|
24421
|
-
const destination =
|
|
24614
|
+
const destination = resolve9(repoRoot, plan.destination);
|
|
24422
24615
|
const destinationRelative = relative8(repoRoot, destination);
|
|
24423
|
-
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep7) ||
|
|
24616
|
+
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep7) || isAbsolute9(destinationRelative)) {
|
|
24424
24617
|
throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
|
|
24425
24618
|
}
|
|
24426
24619
|
let existingDigest = null;
|
|
@@ -24441,8 +24634,8 @@ function writeStationSnapshot(options) {
|
|
|
24441
24634
|
}
|
|
24442
24635
|
let written = 0;
|
|
24443
24636
|
for (const plan of untouched) {
|
|
24444
|
-
const destination =
|
|
24445
|
-
mkdirSync15(
|
|
24637
|
+
const destination = resolve9(repoRoot, plan.destination);
|
|
24638
|
+
mkdirSync15(dirname16(destination), { recursive: true });
|
|
24446
24639
|
copyFileSync2(plan.source.fullPath, destination);
|
|
24447
24640
|
written += 1;
|
|
24448
24641
|
}
|
|
@@ -24460,8 +24653,8 @@ function writeStationSnapshot(options) {
|
|
|
24460
24653
|
},
|
|
24461
24654
|
files: manifestFiles
|
|
24462
24655
|
};
|
|
24463
|
-
const manifestPath =
|
|
24464
|
-
mkdirSync15(
|
|
24656
|
+
const manifestPath = resolve9(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
|
|
24657
|
+
mkdirSync15(dirname16(manifestPath), { recursive: true });
|
|
24465
24658
|
writeFileSync13(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
24466
24659
|
`);
|
|
24467
24660
|
return {
|
|
@@ -24472,7 +24665,7 @@ function writeStationSnapshot(options) {
|
|
|
24472
24665
|
};
|
|
24473
24666
|
}
|
|
24474
24667
|
// src/lib/station-hydrate.ts
|
|
24475
|
-
import { createHash as
|
|
24668
|
+
import { createHash as createHash14 } from "crypto";
|
|
24476
24669
|
import {
|
|
24477
24670
|
copyFileSync as copyFileSync3,
|
|
24478
24671
|
mkdirSync as mkdirSync16,
|
|
@@ -24481,7 +24674,7 @@ import {
|
|
|
24481
24674
|
statSync as statSync14,
|
|
24482
24675
|
writeFileSync as writeFileSync14
|
|
24483
24676
|
} from "fs";
|
|
24484
|
-
import { dirname as
|
|
24677
|
+
import { dirname as dirname17, join as join29, resolve as resolve10, sep as sep8 } from "path";
|
|
24485
24678
|
var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
|
|
24486
24679
|
var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
|
|
24487
24680
|
var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
|
|
@@ -24489,11 +24682,11 @@ function fail(code, message, detail = []) {
|
|
|
24489
24682
|
throw new StationSnapshotError(code, message, detail);
|
|
24490
24683
|
}
|
|
24491
24684
|
function snapshotRootFor(repoRoot, stationId) {
|
|
24492
|
-
return
|
|
24685
|
+
return join29(repoRoot, "resources", stationId, "skills");
|
|
24493
24686
|
}
|
|
24494
24687
|
function readSnapshotManifest(repoRoot, stationId) {
|
|
24495
24688
|
const snapshotRoot = snapshotRootFor(repoRoot, stationId);
|
|
24496
|
-
const manifestPath =
|
|
24689
|
+
const manifestPath = join29(snapshotRoot, "sync-manifest.json");
|
|
24497
24690
|
let manifest;
|
|
24498
24691
|
try {
|
|
24499
24692
|
manifest = JSON.parse(readFileSync23(manifestPath, "utf8"));
|
|
@@ -24520,7 +24713,7 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
24520
24713
|
const hashMismatches = [];
|
|
24521
24714
|
const skippedByRule = [];
|
|
24522
24715
|
for (const agent of SYNC_AGENTS) {
|
|
24523
|
-
const agentRoot =
|
|
24716
|
+
const agentRoot = join29(snapshotRoot, "agent-homes", agent);
|
|
24524
24717
|
let identEntries;
|
|
24525
24718
|
try {
|
|
24526
24719
|
identEntries = readdirSync16(agentRoot, { withFileTypes: true });
|
|
@@ -24531,7 +24724,7 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
24531
24724
|
if (!identEntry.isDirectory() || identEntry.name.startsWith(".")) {
|
|
24532
24725
|
continue;
|
|
24533
24726
|
}
|
|
24534
|
-
const identRoot =
|
|
24727
|
+
const identRoot = join29(agentRoot, identEntry.name);
|
|
24535
24728
|
const entries = walkEntries(identRoot);
|
|
24536
24729
|
for (const entry of entries) {
|
|
24537
24730
|
const relativeParts = [identEntry.name, ...entry.relativePath.split(sep8)];
|
|
@@ -24678,12 +24871,12 @@ function skillSha256(skill) {
|
|
|
24678
24871
|
return sha256File(skill.files[0].winner.fullPath);
|
|
24679
24872
|
}
|
|
24680
24873
|
const joined = skill.files.map((file) => sha256File(file.winner.fullPath));
|
|
24681
|
-
return
|
|
24874
|
+
return createHash14("sha256").update(joined.sort().join(`
|
|
24682
24875
|
`)).digest("hex");
|
|
24683
24876
|
}
|
|
24684
24877
|
function writeStationHydration(options) {
|
|
24685
|
-
const repoRoot =
|
|
24686
|
-
const cacheRoot =
|
|
24878
|
+
const repoRoot = resolve10(options.repoRoot ?? process.cwd());
|
|
24879
|
+
const cacheRoot = resolve10(options.cacheRoot ?? resolveCorpusRoot());
|
|
24687
24880
|
const plan = planStationHydration(options.stationId, repoRoot);
|
|
24688
24881
|
const resultSkills = plan.winners.map((skill) => ({
|
|
24689
24882
|
ident: skill.ident,
|
|
@@ -24716,7 +24909,7 @@ function writeStationHydration(options) {
|
|
|
24716
24909
|
const toWrite = [];
|
|
24717
24910
|
for (const skill of plan.winners) {
|
|
24718
24911
|
for (const file of skill.files) {
|
|
24719
|
-
const destination =
|
|
24912
|
+
const destination = join29(cacheRoot, skill.ident, file.withinIdent);
|
|
24720
24913
|
const digest2 = sha256File(file.winner.fullPath);
|
|
24721
24914
|
let existingDigest = null;
|
|
24722
24915
|
try {
|
|
@@ -24737,7 +24930,7 @@ function writeStationHydration(options) {
|
|
|
24737
24930
|
}
|
|
24738
24931
|
let written = 0;
|
|
24739
24932
|
for (const entry of toWrite) {
|
|
24740
|
-
mkdirSync16(
|
|
24933
|
+
mkdirSync16(dirname17(entry.destination), { recursive: true });
|
|
24741
24934
|
copyFileSync3(entry.fullPath, entry.destination);
|
|
24742
24935
|
written += 1;
|
|
24743
24936
|
}
|
|
@@ -24758,8 +24951,8 @@ function writeStationHydration(options) {
|
|
|
24758
24951
|
},
|
|
24759
24952
|
skills: resultSkills
|
|
24760
24953
|
};
|
|
24761
|
-
const hydrationManifestPath =
|
|
24762
|
-
mkdirSync16(
|
|
24954
|
+
const hydrationManifestPath = join29(dirname17(cacheRoot), `hydration-${options.stationId}.json`);
|
|
24955
|
+
mkdirSync16(dirname17(hydrationManifestPath), { recursive: true });
|
|
24763
24956
|
writeFileSync14(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
|
|
24764
24957
|
`);
|
|
24765
24958
|
return {
|
|
@@ -24865,7 +25058,7 @@ async function requestInvitationEmail(origin, action, input) {
|
|
|
24865
25058
|
}
|
|
24866
25059
|
|
|
24867
25060
|
// src/lib/remote-private-publications.ts
|
|
24868
|
-
import { createHash as
|
|
25061
|
+
import { createHash as createHash15 } from "crypto";
|
|
24869
25062
|
var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
|
|
24870
25063
|
var failures = {
|
|
24871
25064
|
INVALID_REQUEST: [400, "The publication request is invalid."],
|
|
@@ -24912,7 +25105,7 @@ var hash2 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
|
|
|
24912
25105
|
var record6 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
24913
25106
|
var exact = (v, keys) => record6(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
|
|
24914
25107
|
var date = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v));
|
|
24915
|
-
var publicationSha256 = (bytes) =>
|
|
25108
|
+
var publicationSha256 = (bytes) => createHash15("sha256").update(bytes).digest("hex");
|
|
24916
25109
|
function checkedPublicationDeclaration(value) {
|
|
24917
25110
|
if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash2(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
|
|
24918
25111
|
return bad();
|
|
@@ -25157,12 +25350,12 @@ class RemotePrivatePublicationsClient {
|
|
|
25157
25350
|
previous = view;
|
|
25158
25351
|
if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
|
|
25159
25352
|
return view;
|
|
25160
|
-
await new Promise((
|
|
25353
|
+
await new Promise((resolve11) => {
|
|
25161
25354
|
const timer = setTimeout(done, Math.min(1000, until - Date.now()));
|
|
25162
25355
|
function done() {
|
|
25163
25356
|
clearTimeout(timer);
|
|
25164
25357
|
options.signal?.removeEventListener("abort", done);
|
|
25165
|
-
|
|
25358
|
+
resolve11();
|
|
25166
25359
|
}
|
|
25167
25360
|
options.signal?.addEventListener("abort", done, { once: true });
|
|
25168
25361
|
});
|
|
@@ -25394,23 +25587,23 @@ class RemoteSkillsAuthClient {
|
|
|
25394
25587
|
}
|
|
25395
25588
|
}
|
|
25396
25589
|
// src/lib/private-publication-recovery.ts
|
|
25397
|
-
import { constants as
|
|
25398
|
-
import { dirname as
|
|
25590
|
+
import { constants as constants7, closeSync as closeSync8, fsyncSync as fsyncSync2, fstatSync as fstatSync8, lstatSync as lstatSync14, mkdirSync as mkdirSync17, openSync as openSync8, readSync as readSync6, realpathSync as realpathSync5, renameSync as renameSync6, unlinkSync as unlinkSync4, writeFileSync as writeFileSync15 } from "fs";
|
|
25591
|
+
import { dirname as dirname18, isAbsolute as isAbsolute10, join as join30, resolve as resolve11 } from "path";
|
|
25399
25592
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
25400
25593
|
var fail2 = () => {
|
|
25401
25594
|
throw new PrivatePublicationError("PUBLICATION_RECOVERY_INVALID", "The recovery directory is invalid or changed. Preserve it and inspect the existing intent; do not start a replacement automatically.");
|
|
25402
25595
|
};
|
|
25403
25596
|
function safeDirectory(directory) {
|
|
25404
|
-
if (!
|
|
25597
|
+
if (!isAbsolute10(directory) || directory !== resolve11(directory) || realpathSync5(directory) !== directory)
|
|
25405
25598
|
return fail2();
|
|
25406
|
-
for (let path = directory;; path =
|
|
25407
|
-
const stat =
|
|
25599
|
+
for (let path = directory;; path = dirname18(path)) {
|
|
25600
|
+
const stat = lstatSync14(path);
|
|
25408
25601
|
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
25409
25602
|
return fail2();
|
|
25410
|
-
if (path ===
|
|
25603
|
+
if (path === dirname18(path))
|
|
25411
25604
|
break;
|
|
25412
25605
|
}
|
|
25413
|
-
const own =
|
|
25606
|
+
const own = lstatSync14(directory);
|
|
25414
25607
|
if ((own.mode & 63) !== 0 || process.getuid && own.uid !== process.getuid())
|
|
25415
25608
|
return fail2();
|
|
25416
25609
|
return { dev: own.dev, ino: own.ino };
|
|
@@ -25421,36 +25614,36 @@ function unchangedDirectory(directory, identity2) {
|
|
|
25421
25614
|
return fail2();
|
|
25422
25615
|
}
|
|
25423
25616
|
function readOwned(directory, name, max) {
|
|
25424
|
-
const identity2 = safeDirectory(directory), file =
|
|
25425
|
-
const fd =
|
|
25617
|
+
const identity2 = safeDirectory(directory), file = join30(directory, name);
|
|
25618
|
+
const fd = openSync8(file, constants7.O_RDONLY | constants7.O_NOFOLLOW);
|
|
25426
25619
|
try {
|
|
25427
|
-
const stat =
|
|
25620
|
+
const stat = fstatSync8(fd);
|
|
25428
25621
|
if (!stat.isFile() || stat.nlink !== 1 || stat.size > max || stat.size < 1 || (stat.mode & 63) !== 0 || process.getuid && stat.uid !== process.getuid())
|
|
25429
25622
|
return fail2();
|
|
25430
25623
|
const buffer = Buffer.alloc(Math.min(max, stat.size) + 1);
|
|
25431
25624
|
let length = 0;
|
|
25432
25625
|
while (length < buffer.length) {
|
|
25433
|
-
const count =
|
|
25626
|
+
const count = readSync6(fd, buffer, length, buffer.length - length, length);
|
|
25434
25627
|
if (count === 0)
|
|
25435
25628
|
break;
|
|
25436
25629
|
length += count;
|
|
25437
25630
|
}
|
|
25438
|
-
const bytes = buffer.subarray(0, length), after =
|
|
25631
|
+
const bytes = buffer.subarray(0, length), after = fstatSync8(fd);
|
|
25439
25632
|
unchangedDirectory(directory, identity2);
|
|
25440
25633
|
if (bytes.length !== stat.size || stat.size !== after.size || stat.mtimeMs !== after.mtimeMs || stat.ctimeMs !== after.ctimeMs)
|
|
25441
25634
|
return fail2();
|
|
25442
25635
|
return bytes;
|
|
25443
25636
|
} finally {
|
|
25444
|
-
|
|
25637
|
+
closeSync8(fd);
|
|
25445
25638
|
}
|
|
25446
25639
|
}
|
|
25447
25640
|
function writeOwned(directory, name, bytes) {
|
|
25448
|
-
const fd =
|
|
25641
|
+
const fd = openSync8(join30(directory, name), constants7.O_WRONLY | constants7.O_CREAT | constants7.O_EXCL | constants7.O_NOFOLLOW, 384);
|
|
25449
25642
|
try {
|
|
25450
25643
|
writeFileSync15(fd, bytes);
|
|
25451
25644
|
fsyncSync2(fd);
|
|
25452
25645
|
} finally {
|
|
25453
|
-
|
|
25646
|
+
closeSync8(fd);
|
|
25454
25647
|
}
|
|
25455
25648
|
}
|
|
25456
25649
|
function save(directory, value) {
|
|
@@ -25458,12 +25651,12 @@ function save(directory, value) {
|
|
|
25458
25651
|
writeOwned(directory, temporary, JSON.stringify(value) + `
|
|
25459
25652
|
`);
|
|
25460
25653
|
unchangedDirectory(directory, identity2);
|
|
25461
|
-
renameSync6(
|
|
25462
|
-
const fd =
|
|
25654
|
+
renameSync6(join30(directory, temporary), join30(directory, "receipt.json"));
|
|
25655
|
+
const fd = openSync8(directory, constants7.O_RDONLY | constants7.O_NOFOLLOW);
|
|
25463
25656
|
try {
|
|
25464
25657
|
fsyncSync2(fd);
|
|
25465
25658
|
} finally {
|
|
25466
|
-
|
|
25659
|
+
closeSync8(fd);
|
|
25467
25660
|
}
|
|
25468
25661
|
}
|
|
25469
25662
|
function bind(client, receipt) {
|
|
@@ -25471,23 +25664,23 @@ function bind(client, receipt) {
|
|
|
25471
25664
|
throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
|
|
25472
25665
|
}
|
|
25473
25666
|
async function locked(directory, action) {
|
|
25474
|
-
const identity2 = safeDirectory(directory), file =
|
|
25667
|
+
const identity2 = safeDirectory(directory), file = join30(directory, "operation.lock");
|
|
25475
25668
|
let fd;
|
|
25476
25669
|
try {
|
|
25477
|
-
fd =
|
|
25670
|
+
fd = openSync8(file, constants7.O_WRONLY | constants7.O_CREAT | constants7.O_EXCL | constants7.O_NOFOLLOW, 384);
|
|
25478
25671
|
} catch {
|
|
25479
25672
|
throw new PrivatePublicationError("PUBLICATION_RECOVERY_BUSY", "Another operation holds this recovery directory. If it crashed, confirm that process has stopped before explicitly removing operation.lock and resuming.");
|
|
25480
25673
|
}
|
|
25481
|
-
const lock =
|
|
25674
|
+
const lock = fstatSync8(fd);
|
|
25482
25675
|
try {
|
|
25483
25676
|
writeFileSync15(fd, JSON.stringify({ pid: process.pid }) + `
|
|
25484
25677
|
`);
|
|
25485
25678
|
fsyncSync2(fd);
|
|
25486
25679
|
return await action();
|
|
25487
25680
|
} finally {
|
|
25488
|
-
|
|
25681
|
+
closeSync8(fd);
|
|
25489
25682
|
unchangedDirectory(directory, identity2);
|
|
25490
|
-
const now =
|
|
25683
|
+
const now = lstatSync14(file);
|
|
25491
25684
|
if (now.dev !== lock.dev || now.ino !== lock.ino || !now.isFile() || now.isSymbolicLink())
|
|
25492
25685
|
fail2();
|
|
25493
25686
|
unlinkSync4(file);
|
|
@@ -25539,15 +25732,15 @@ async function preparePrivatePublication(client, sourceDirectory, recoveryDirect
|
|
|
25539
25732
|
phase: "prepared",
|
|
25540
25733
|
intent: null
|
|
25541
25734
|
};
|
|
25542
|
-
if (!
|
|
25735
|
+
if (!isAbsolute10(recoveryDirectory) || resolve11(recoveryDirectory) !== recoveryDirectory || realpathSync5(dirname18(recoveryDirectory)) !== dirname18(recoveryDirectory))
|
|
25543
25736
|
return fail2();
|
|
25544
25737
|
mkdirSync17(recoveryDirectory, { mode: 448 });
|
|
25545
25738
|
safeDirectory(recoveryDirectory);
|
|
25546
|
-
const parent =
|
|
25739
|
+
const parent = openSync8(dirname18(recoveryDirectory), constants7.O_RDONLY | constants7.O_NOFOLLOW);
|
|
25547
25740
|
try {
|
|
25548
25741
|
fsyncSync2(parent);
|
|
25549
25742
|
} finally {
|
|
25550
|
-
|
|
25743
|
+
closeSync8(parent);
|
|
25551
25744
|
}
|
|
25552
25745
|
writeOwned(recoveryDirectory, "bundle.tgz", packed.bytes);
|
|
25553
25746
|
save(recoveryDirectory, receipt);
|
|
@@ -25757,8 +25950,8 @@ async function createProfileClient() {
|
|
|
25757
25950
|
return new HttpProfileClient(connection.apiKey, connection.apiOrigin);
|
|
25758
25951
|
}
|
|
25759
25952
|
// src/lib/skill-context.ts
|
|
25760
|
-
import { createHash as
|
|
25761
|
-
import { resolve as
|
|
25953
|
+
import { createHash as createHash16 } from "crypto";
|
|
25954
|
+
import { resolve as resolve12 } from "path";
|
|
25762
25955
|
|
|
25763
25956
|
// src/lib/selected-document.ts
|
|
25764
25957
|
var DOCUMENT_PATHS = ["SKILL.md", "README.md", "CLAUDE.md"];
|
|
@@ -25905,7 +26098,7 @@ async function buildSkillContext(input, options = {}) {
|
|
|
25905
26098
|
}
|
|
25906
26099
|
const promptWords = words(prompt);
|
|
25907
26100
|
const loaded = new Set(resolved.session?.loaded ?? []);
|
|
25908
|
-
const paths = [...input.paths ?? [], ...input.cwd ? [input.cwd,
|
|
26101
|
+
const paths = [...input.paths ?? [], ...input.cwd ? [input.cwd, resolve12(input.cwd)] : []];
|
|
25909
26102
|
const candidates = profile.selections.map((selection) => {
|
|
25910
26103
|
const key = selectionKey(selection);
|
|
25911
26104
|
if (input.restore && loaded.has(key))
|
|
@@ -25957,7 +26150,7 @@ ${content}`;
|
|
|
25957
26150
|
`);
|
|
25958
26151
|
const receipt = {
|
|
25959
26152
|
schemaVersion: 1,
|
|
25960
|
-
id:
|
|
26153
|
+
id: createHash16("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
|
|
25961
26154
|
profileId: profile.profileId,
|
|
25962
26155
|
profileRevision: profile.profileRevision,
|
|
25963
26156
|
authority: profile.authority,
|
|
@@ -26158,6 +26351,8 @@ export {
|
|
|
26158
26351
|
computeContentHash,
|
|
26159
26352
|
completeSkillRun,
|
|
26160
26353
|
clearRegistryCache,
|
|
26354
|
+
captureDiscoveryDirectories,
|
|
26355
|
+
captureDiscoveryByteSources,
|
|
26161
26356
|
canonicalizeManifest,
|
|
26162
26357
|
buildSkillsS3ObjectUrl,
|
|
26163
26358
|
buildSkillsApiUrl,
|