@hasna/skills 0.10.28 → 0.10.30
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 +37 -0
- package/bin/index.js +1123 -920
- package/bin/mcp.js +357 -278
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +658 -470
- package/dist/lib/agent-adapters.d.ts +7 -1
- package/dist/lib/agent-sumi.d.ts +15 -0
- package/dist/lib/native-discovery-roots.d.ts +1 -1
- package/dist/lib/registry.d.ts +20 -3
- package/dist/lib/selection-resolver.d.ts +1 -0
- package/dist/lib/skill-context.d.ts +2 -0
- package/dist/sdk/index.js +650 -466
- package/package.json +1 -1
package/dist/sdk/index.js
CHANGED
|
@@ -8209,7 +8209,7 @@ var require_client = __commonJS((exports) => {
|
|
|
8209
8209
|
};
|
|
8210
8210
|
};
|
|
8211
8211
|
var sleep = (seconds) => {
|
|
8212
|
-
return new Promise((
|
|
8212
|
+
return new Promise((resolve20) => setTimeout(resolve20, seconds * 1000));
|
|
8213
8213
|
};
|
|
8214
8214
|
var waiterServiceDefaults = {
|
|
8215
8215
|
minDelay: 2,
|
|
@@ -8338,8 +8338,8 @@ var require_client = __commonJS((exports) => {
|
|
|
8338
8338
|
};
|
|
8339
8339
|
var abortTimeout = (abortSignal) => {
|
|
8340
8340
|
let onAbort;
|
|
8341
|
-
const promise = new Promise((
|
|
8342
|
-
onAbort = () =>
|
|
8341
|
+
const promise = new Promise((resolve20) => {
|
|
8342
|
+
onAbort = () => resolve20({ state: WaiterState.ABORTED });
|
|
8343
8343
|
if (typeof abortSignal.addEventListener === "function") {
|
|
8344
8344
|
abortSignal.addEventListener("abort", onAbort);
|
|
8345
8345
|
} else {
|
|
@@ -9477,7 +9477,7 @@ var require_config = __commonJS((exports) => {
|
|
|
9477
9477
|
};
|
|
9478
9478
|
var imdsRequest = async (options) => {
|
|
9479
9479
|
const { request: request2 } = __require("http");
|
|
9480
|
-
return new Promise((
|
|
9480
|
+
return new Promise((resolve20, reject) => {
|
|
9481
9481
|
const req = request2({
|
|
9482
9482
|
hostname: options.hostname,
|
|
9483
9483
|
port: options.port,
|
|
@@ -9505,7 +9505,7 @@ var require_config = __commonJS((exports) => {
|
|
|
9505
9505
|
const chunks = [];
|
|
9506
9506
|
res.on("data", (chunk) => chunks.push(chunk));
|
|
9507
9507
|
res.on("end", () => {
|
|
9508
|
-
|
|
9508
|
+
resolve20(Buffer.concat(chunks));
|
|
9509
9509
|
req.destroy();
|
|
9510
9510
|
});
|
|
9511
9511
|
});
|
|
@@ -10514,7 +10514,7 @@ var require_serde = __commonJS((exports) => {
|
|
|
10514
10514
|
var { createHmac: createHmac2, createHash: createHash21, getRandomValues } = __require("crypto");
|
|
10515
10515
|
var { hasOwn: hasOwn2, HttpResponse } = require_transport();
|
|
10516
10516
|
exports.hasOwn = hasOwn2;
|
|
10517
|
-
var { ReadStream, lstatSync:
|
|
10517
|
+
var { ReadStream, lstatSync: lstatSync24, fstatSync: fstatSync4 } = __require("fs");
|
|
10518
10518
|
var { toEndpointV1 } = require_endpoints();
|
|
10519
10519
|
var { Readable, Writable, PassThrough } = __require("stream");
|
|
10520
10520
|
var isArrayBuffer = (arg) => typeof ArrayBuffer === "function" && arg instanceof ArrayBuffer || Object.prototype.toString.call(arg) === "[object ArrayBuffer]";
|
|
@@ -11280,7 +11280,7 @@ var require_serde = __commonJS((exports) => {
|
|
|
11280
11280
|
return body.end + 1 - body.start;
|
|
11281
11281
|
} else if (body instanceof ReadStream) {
|
|
11282
11282
|
if (body.path != null) {
|
|
11283
|
-
return
|
|
11283
|
+
return lstatSync24(body.path).size;
|
|
11284
11284
|
} else if (typeof body.fd === "number") {
|
|
11285
11285
|
return fstatSync4(body.fd).size;
|
|
11286
11286
|
}
|
|
@@ -11861,7 +11861,7 @@ ${value}\r
|
|
|
11861
11861
|
if (isReadableStream(stream)) {
|
|
11862
11862
|
return headStream$1(stream, bytes);
|
|
11863
11863
|
}
|
|
11864
|
-
return new Promise((
|
|
11864
|
+
return new Promise((resolve20, reject) => {
|
|
11865
11865
|
const collector = new Collector$1;
|
|
11866
11866
|
collector.limit = bytes;
|
|
11867
11867
|
stream.pipe(collector);
|
|
@@ -11872,7 +11872,7 @@ ${value}\r
|
|
|
11872
11872
|
collector.on("error", reject);
|
|
11873
11873
|
collector.on("finish", function() {
|
|
11874
11874
|
const bytes2 = concatBytes(this.buffers);
|
|
11875
|
-
|
|
11875
|
+
resolve20(bytes2);
|
|
11876
11876
|
});
|
|
11877
11877
|
});
|
|
11878
11878
|
};
|
|
@@ -11986,7 +11986,7 @@ ${value}\r
|
|
|
11986
11986
|
if (isReadableStream(stream)) {
|
|
11987
11987
|
return collectReadableStream(stream);
|
|
11988
11988
|
}
|
|
11989
|
-
return new Promise((
|
|
11989
|
+
return new Promise((resolve20, reject) => {
|
|
11990
11990
|
const collector = new Collector;
|
|
11991
11991
|
const nodeStream = stream;
|
|
11992
11992
|
nodeStream.pipe(collector);
|
|
@@ -11997,7 +11997,7 @@ ${value}\r
|
|
|
11997
11997
|
collector.on("error", reject);
|
|
11998
11998
|
collector.on("finish", function() {
|
|
11999
11999
|
const bytes = concatBytes(this.bufferedBytes);
|
|
12000
|
-
|
|
12000
|
+
resolve20(bytes);
|
|
12001
12001
|
});
|
|
12002
12002
|
});
|
|
12003
12003
|
};
|
|
@@ -12189,7 +12189,7 @@ var require_checksum = __commonJS((exports) => {
|
|
|
12189
12189
|
callback();
|
|
12190
12190
|
}
|
|
12191
12191
|
}
|
|
12192
|
-
var fileStreamHasher = (hashCtor, fileStream) => new Promise((
|
|
12192
|
+
var fileStreamHasher = (hashCtor, fileStream) => new Promise((resolve20, reject) => {
|
|
12193
12193
|
if (!isReadStream(fileStream)) {
|
|
12194
12194
|
reject(new Error("Unable to calculate hash for non-file streams."));
|
|
12195
12195
|
return;
|
|
@@ -12207,7 +12207,7 @@ var require_checksum = __commonJS((exports) => {
|
|
|
12207
12207
|
});
|
|
12208
12208
|
hashCalculator.on("error", reject);
|
|
12209
12209
|
hashCalculator.on("finish", function() {
|
|
12210
|
-
hash2.digest().then(
|
|
12210
|
+
hash2.digest().then(resolve20).catch(reject);
|
|
12211
12211
|
});
|
|
12212
12212
|
});
|
|
12213
12213
|
var isReadStream = (stream) => typeof stream.path === "string";
|
|
@@ -12218,14 +12218,14 @@ var require_checksum = __commonJS((exports) => {
|
|
|
12218
12218
|
const hash2 = new hashCtor;
|
|
12219
12219
|
const hashCalculator = new HashCalculator(hash2);
|
|
12220
12220
|
readableStream.pipe(hashCalculator);
|
|
12221
|
-
return new Promise((
|
|
12221
|
+
return new Promise((resolve20, reject) => {
|
|
12222
12222
|
readableStream.on("error", (err) => {
|
|
12223
12223
|
hashCalculator.end();
|
|
12224
12224
|
reject(err);
|
|
12225
12225
|
});
|
|
12226
12226
|
hashCalculator.on("error", reject);
|
|
12227
12227
|
hashCalculator.on("finish", () => {
|
|
12228
|
-
hash2.digest().then(
|
|
12228
|
+
hash2.digest().then(resolve20).catch(reject);
|
|
12229
12229
|
});
|
|
12230
12230
|
});
|
|
12231
12231
|
};
|
|
@@ -13334,7 +13334,7 @@ var require_event_streams = __commonJS((exports) => {
|
|
|
13334
13334
|
streamEnded = true;
|
|
13335
13335
|
});
|
|
13336
13336
|
while (!generationEnded) {
|
|
13337
|
-
const value = await new Promise((
|
|
13337
|
+
const value = await new Promise((resolve20) => setTimeout(() => resolve20(records.shift()), 0));
|
|
13338
13338
|
if (value) {
|
|
13339
13339
|
yield value;
|
|
13340
13340
|
}
|
|
@@ -14852,7 +14852,7 @@ var require_retry = __commonJS((exports) => {
|
|
|
14852
14852
|
}
|
|
14853
14853
|
};
|
|
14854
14854
|
}
|
|
14855
|
-
var cooldown = (ms) => new Promise((
|
|
14855
|
+
var cooldown = (ms) => new Promise((resolve20) => setTimeout(resolve20, ms));
|
|
14856
14856
|
var isRetryStrategyV2 = (retryStrategy) => typeof retryStrategy.acquireInitialRetryToken !== "undefined" && typeof retryStrategy.refreshRetryTokenForRetry !== "undefined" && typeof retryStrategy.recordSuccess !== "undefined";
|
|
14857
14857
|
var getRetryErrorInfo = (error, logger) => {
|
|
14858
14858
|
const errorInfo = {
|
|
@@ -14951,7 +14951,7 @@ var require_retry = __commonJS((exports) => {
|
|
|
14951
14951
|
this.refillTokenBucket();
|
|
14952
14952
|
while (amount > this.availableTokens) {
|
|
14953
14953
|
const delay = (amount - this.availableTokens) / this.fillRate * 1000;
|
|
14954
|
-
await new Promise((
|
|
14954
|
+
await new Promise((resolve20) => DefaultRateLimiter.setTimeoutFn(resolve20, delay));
|
|
14955
14955
|
this.refillTokenBucket();
|
|
14956
14956
|
}
|
|
14957
14957
|
this.availableTokens = this.availableTokens - amount;
|
|
@@ -15293,7 +15293,7 @@ var require_retry = __commonJS((exports) => {
|
|
|
15293
15293
|
const delayFromResponse = getDelayFromRetryAfterHeader(err.$response);
|
|
15294
15294
|
const delay = Math.max(delayFromResponse || 0, delayFromDecider);
|
|
15295
15295
|
totalDelay += delay;
|
|
15296
|
-
await new Promise((
|
|
15296
|
+
await new Promise((resolve20) => setTimeout(resolve20, delay));
|
|
15297
15297
|
continue;
|
|
15298
15298
|
}
|
|
15299
15299
|
if (!err.$metadata) {
|
|
@@ -24646,7 +24646,7 @@ var init_node_http = () => {};
|
|
|
24646
24646
|
|
|
24647
24647
|
// node_modules/@smithy/credential-provider-imds/dist-es/remoteProvider/httpRequest.js
|
|
24648
24648
|
function httpRequest(options) {
|
|
24649
|
-
return new Promise((
|
|
24649
|
+
return new Promise((resolve20, reject) => {
|
|
24650
24650
|
const req = node_http.request({
|
|
24651
24651
|
method: "GET",
|
|
24652
24652
|
...options,
|
|
@@ -24671,7 +24671,7 @@ function httpRequest(options) {
|
|
|
24671
24671
|
chunks.push(chunk);
|
|
24672
24672
|
});
|
|
24673
24673
|
res.on("end", () => {
|
|
24674
|
-
|
|
24674
|
+
resolve20(Buffer.concat(chunks));
|
|
24675
24675
|
req.destroy();
|
|
24676
24676
|
});
|
|
24677
24677
|
});
|
|
@@ -25168,21 +25168,21 @@ var require_dist_cjs6 = __commonJS((exports) => {
|
|
|
25168
25168
|
let sendBody = true;
|
|
25169
25169
|
if (!externalAgent && expect === "100-continue") {
|
|
25170
25170
|
sendBody = await Promise.race([
|
|
25171
|
-
new Promise((
|
|
25172
|
-
timeoutId = Number(timing.setTimeout(() =>
|
|
25171
|
+
new Promise((resolve20) => {
|
|
25172
|
+
timeoutId = Number(timing.setTimeout(() => resolve20(true), Math.max(MIN_WAIT_TIME, maxContinueTimeoutMs)));
|
|
25173
25173
|
}),
|
|
25174
|
-
new Promise((
|
|
25174
|
+
new Promise((resolve20) => {
|
|
25175
25175
|
httpRequest2.on("continue", () => {
|
|
25176
25176
|
timing.clearTimeout(timeoutId);
|
|
25177
|
-
|
|
25177
|
+
resolve20(true);
|
|
25178
25178
|
});
|
|
25179
25179
|
httpRequest2.on("response", () => {
|
|
25180
25180
|
timing.clearTimeout(timeoutId);
|
|
25181
|
-
|
|
25181
|
+
resolve20(false);
|
|
25182
25182
|
});
|
|
25183
25183
|
httpRequest2.on("error", () => {
|
|
25184
25184
|
timing.clearTimeout(timeoutId);
|
|
25185
|
-
|
|
25185
|
+
resolve20(false);
|
|
25186
25186
|
});
|
|
25187
25187
|
})
|
|
25188
25188
|
]);
|
|
@@ -25259,13 +25259,13 @@ or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler conf
|
|
|
25259
25259
|
return socketWarningTimestamp;
|
|
25260
25260
|
}
|
|
25261
25261
|
constructor(options) {
|
|
25262
|
-
this.configProvider = new Promise((
|
|
25262
|
+
this.configProvider = new Promise((resolve20, reject) => {
|
|
25263
25263
|
if (typeof options === "function") {
|
|
25264
25264
|
options().then((_options) => {
|
|
25265
|
-
|
|
25265
|
+
resolve20(this.resolveDefaultConfig(_options));
|
|
25266
25266
|
}).catch(reject);
|
|
25267
25267
|
} else {
|
|
25268
|
-
|
|
25268
|
+
resolve20(this.resolveDefaultConfig(options));
|
|
25269
25269
|
}
|
|
25270
25270
|
});
|
|
25271
25271
|
}
|
|
@@ -25297,7 +25297,7 @@ or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler conf
|
|
|
25297
25297
|
timing.clearTimeout(socketTimeoutId);
|
|
25298
25298
|
timing.clearTimeout(keepAliveTimeoutId);
|
|
25299
25299
|
};
|
|
25300
|
-
const
|
|
25300
|
+
const resolve20 = async (arg) => {
|
|
25301
25301
|
await writeRequestBodyPromise;
|
|
25302
25302
|
clearTimeouts();
|
|
25303
25303
|
_resolve(arg);
|
|
@@ -25361,7 +25361,7 @@ or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler conf
|
|
|
25361
25361
|
headers: getTransformedHeaders(res.headers),
|
|
25362
25362
|
body: res
|
|
25363
25363
|
});
|
|
25364
|
-
|
|
25364
|
+
resolve20({ response: httpResponse });
|
|
25365
25365
|
});
|
|
25366
25366
|
req.on("error", (err) => {
|
|
25367
25367
|
if (NODEJS_TIMEOUT_ERROR_CODES.includes(err.code)) {
|
|
@@ -25693,13 +25693,13 @@ or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler conf
|
|
|
25693
25693
|
return new NodeHttp2Handler(instanceOrOptions);
|
|
25694
25694
|
}
|
|
25695
25695
|
constructor(options) {
|
|
25696
|
-
this.configProvider = new Promise((
|
|
25696
|
+
this.configProvider = new Promise((resolve20, reject) => {
|
|
25697
25697
|
if (typeof options === "function") {
|
|
25698
25698
|
options().then((opts) => {
|
|
25699
|
-
|
|
25699
|
+
resolve20(opts || {});
|
|
25700
25700
|
}).catch(reject);
|
|
25701
25701
|
} else {
|
|
25702
|
-
|
|
25702
|
+
resolve20(options || {});
|
|
25703
25703
|
}
|
|
25704
25704
|
});
|
|
25705
25705
|
}
|
|
@@ -25724,7 +25724,7 @@ or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler conf
|
|
|
25724
25724
|
return new Promise((_resolve, _reject) => {
|
|
25725
25725
|
let fulfilled = false;
|
|
25726
25726
|
let writeRequestBodyPromise = undefined;
|
|
25727
|
-
const
|
|
25727
|
+
const resolve20 = async (arg) => {
|
|
25728
25728
|
await writeRequestBodyPromise;
|
|
25729
25729
|
_resolve(arg);
|
|
25730
25730
|
};
|
|
@@ -25809,7 +25809,7 @@ or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler conf
|
|
|
25809
25809
|
body: clientHttp2Stream
|
|
25810
25810
|
});
|
|
25811
25811
|
fulfilled = true;
|
|
25812
|
-
|
|
25812
|
+
resolve20({ response: httpResponse });
|
|
25813
25813
|
if (useIsolatedSession) {
|
|
25814
25814
|
session.close();
|
|
25815
25815
|
clientHttp2Stream.on("end", () => {
|
|
@@ -25942,7 +25942,7 @@ var retryWrapper = (toRetry, maxRetries, delayMs) => {
|
|
|
25942
25942
|
try {
|
|
25943
25943
|
return await toRetry();
|
|
25944
25944
|
} catch (e3) {
|
|
25945
|
-
await new Promise((
|
|
25945
|
+
await new Promise((resolve20) => setTimeout(resolve20, delayMs));
|
|
25946
25946
|
}
|
|
25947
25947
|
}
|
|
25948
25948
|
return await toRetry();
|
|
@@ -29446,7 +29446,7 @@ var require_signin = __commonJS((exports) => {
|
|
|
29446
29446
|
import { createHash as createHash21, createPrivateKey, createPublicKey, sign } from "crypto";
|
|
29447
29447
|
import { promises as fs2 } from "fs";
|
|
29448
29448
|
import { homedir as homedir7 } from "os";
|
|
29449
|
-
import { dirname as dirname21, join as
|
|
29449
|
+
import { dirname as dirname21, join as join34 } from "path";
|
|
29450
29450
|
var import_config28, import_protocols3, LoginCredentialsFetcher;
|
|
29451
29451
|
var init_LoginCredentialsFetcher = __esm(() => {
|
|
29452
29452
|
import_config28 = __toESM(require_config(), 1);
|
|
@@ -29613,10 +29613,10 @@ var init_LoginCredentialsFetcher = __esm(() => {
|
|
|
29613
29613
|
await fs2.writeFile(tokenFilePath, JSON.stringify(token, null, 2), "utf8");
|
|
29614
29614
|
}
|
|
29615
29615
|
getTokenFilePath() {
|
|
29616
|
-
const directory = process.env.AWS_LOGIN_CACHE_DIRECTORY ??
|
|
29616
|
+
const directory = process.env.AWS_LOGIN_CACHE_DIRECTORY ?? join34(homedir7(), ".aws", "login", "cache");
|
|
29617
29617
|
const loginSessionBytes = Buffer.from(this.loginSession, "utf8");
|
|
29618
29618
|
const loginSessionSha256 = createHash21("sha256").update(loginSessionBytes).digest("hex");
|
|
29619
|
-
return
|
|
29619
|
+
return join34(directory, `${loginSessionSha256}.json`);
|
|
29620
29620
|
}
|
|
29621
29621
|
derToRawSignature(derSignature) {
|
|
29622
29622
|
let offset = 2;
|
|
@@ -31065,9 +31065,9 @@ import { chmod, mkdir, readFile, rename, writeFile as writeFile2 } from "fs/prom
|
|
|
31065
31065
|
import { Buffer as Buffer2 } from "buffer";
|
|
31066
31066
|
import { existsSync as existsSync22 } from "fs";
|
|
31067
31067
|
import { join as join210 } from "path";
|
|
31068
|
-
import { existsSync as
|
|
31068
|
+
import { existsSync as existsSync17 } from "fs";
|
|
31069
31069
|
import { homedir as homedir8 } from "os";
|
|
31070
|
-
import { join as
|
|
31070
|
+
import { join as join38, resolve as resolve20 } from "path";
|
|
31071
31071
|
import { homedir as pathsResolverHomedir2 } from "os";
|
|
31072
31072
|
import { join as pathsResolverJoin2 } from "path";
|
|
31073
31073
|
import { createHmac as createHmac4, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
@@ -31228,7 +31228,7 @@ function effectiveHome2() {
|
|
|
31228
31228
|
return process.env["HOME"] || process.env["USERPROFILE"] || homedir8();
|
|
31229
31229
|
}
|
|
31230
31230
|
function legacyHomeDir() {
|
|
31231
|
-
return
|
|
31231
|
+
return join38(effectiveHome2(), ".hasna", "events");
|
|
31232
31232
|
}
|
|
31233
31233
|
function resolverHome() {
|
|
31234
31234
|
return dataDir2({ app: "events", home: effectiveHome2() || undefined });
|
|
@@ -31237,7 +31237,7 @@ function adoptResolverHome(resolved, env = process.env) {
|
|
|
31237
31237
|
const dataOverride = env.HASNA_DATA_HOME;
|
|
31238
31238
|
if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
|
|
31239
31239
|
return true;
|
|
31240
|
-
return
|
|
31240
|
+
return existsSync17(join38(resolved, EVENTS_STORE_SENTINEL_FILE));
|
|
31241
31241
|
}
|
|
31242
31242
|
function exactEventsHome() {
|
|
31243
31243
|
const dir = process.env[HASNA_EVENTS_DIR_ENV];
|
|
@@ -31251,9 +31251,9 @@ function exactEventsHome() {
|
|
|
31251
31251
|
function getEventsHome() {
|
|
31252
31252
|
const exact = exactEventsHome();
|
|
31253
31253
|
if (exact)
|
|
31254
|
-
return
|
|
31254
|
+
return resolve20(exact);
|
|
31255
31255
|
const resolved = resolverHome();
|
|
31256
|
-
return adoptResolverHome(resolved) ?
|
|
31256
|
+
return adoptResolverHome(resolved) ? resolve20(resolved) : resolve20(legacyHomeDir());
|
|
31257
31257
|
}
|
|
31258
31258
|
function getEventsDataDir(override) {
|
|
31259
31259
|
return override || getEventsHome();
|
|
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
|
|
|
32373
32373
|
// package.json
|
|
32374
32374
|
var package_default = {
|
|
32375
32375
|
name: "@hasna/skills",
|
|
32376
|
-
version: "0.10.
|
|
32376
|
+
version: "0.10.30",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|
|
@@ -37457,8 +37457,8 @@ async function reconcileSkillSession(input, options = {}) {
|
|
|
37457
37457
|
// src/lib/selected-run.ts
|
|
37458
37458
|
import { createHash as createHash20 } from "crypto";
|
|
37459
37459
|
import { spawn as spawn2 } from "child_process";
|
|
37460
|
-
import { mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync9, realpathSync as
|
|
37461
|
-
import { dirname as dirname20, join as
|
|
37460
|
+
import { mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync9, realpathSync as realpathSync12 } from "fs";
|
|
37461
|
+
import { dirname as dirname20, join as join33, resolve as resolve19 } from "path";
|
|
37462
37462
|
import { tmpdir, hostname as stationHostname2 } from "os";
|
|
37463
37463
|
|
|
37464
37464
|
// src/lib/dependency-preparation.ts
|
|
@@ -37669,19 +37669,19 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
|
|
|
37669
37669
|
}
|
|
37670
37670
|
|
|
37671
37671
|
// src/lib/skillinfo.ts
|
|
37672
|
-
import { existsSync as
|
|
37673
|
-
import { join as
|
|
37672
|
+
import { existsSync as existsSync15, readFileSync as readFileSync21 } from "fs";
|
|
37673
|
+
import { join as join32 } from "path";
|
|
37674
37674
|
|
|
37675
37675
|
// src/lib/installer.ts
|
|
37676
|
-
import { existsSync as
|
|
37677
|
-
import { join as
|
|
37676
|
+
import { existsSync as existsSync14, readFileSync as readFileSync20, rmSync as rmSync4 } from "fs";
|
|
37677
|
+
import { join as join31 } from "path";
|
|
37678
37678
|
import { homedir as homedir6 } from "os";
|
|
37679
37679
|
|
|
37680
37680
|
// src/lib/agent-sync.ts
|
|
37681
37681
|
import {
|
|
37682
37682
|
cpSync as cpSync2,
|
|
37683
|
-
existsSync as
|
|
37684
|
-
lstatSync as
|
|
37683
|
+
existsSync as existsSync11,
|
|
37684
|
+
lstatSync as lstatSync23,
|
|
37685
37685
|
mkdirSync as mkdirSync8,
|
|
37686
37686
|
mkdtempSync as mkdtempSync2,
|
|
37687
37687
|
readFileSync as readFileSync17,
|
|
@@ -37692,7 +37692,7 @@ import {
|
|
|
37692
37692
|
writeFileSync as writeFileSync7
|
|
37693
37693
|
} from "fs";
|
|
37694
37694
|
import { homedir as homedir5 } from "os";
|
|
37695
|
-
import { basename as basename11, dirname as dirname19, join as
|
|
37695
|
+
import { basename as basename11, dirname as dirname19, join as join28 } from "path";
|
|
37696
37696
|
|
|
37697
37697
|
// src/lib/codex-local-mcp-controls.ts
|
|
37698
37698
|
function isCodexLocalStdioMcpServer(name, server) {
|
|
@@ -39072,6 +39072,8 @@ var NATIVE_SKILL_ROOTS = [
|
|
|
39072
39072
|
["codex", ".codex/skills"],
|
|
39073
39073
|
["codex", ".agents/skills"],
|
|
39074
39074
|
["gemini", ".gemini/skills"],
|
|
39075
|
+
["sumi", ".sumi/skills"],
|
|
39076
|
+
["sumi", ".sumi/skill"],
|
|
39075
39077
|
["codewith", ".codewith/skills"],
|
|
39076
39078
|
["opencode", ".config/opencode/skills"],
|
|
39077
39079
|
["opencode", ".opencode/skills"],
|
|
@@ -39088,8 +39090,8 @@ var NATIVE_SKILL_ROOTS = [
|
|
|
39088
39090
|
// src/lib/agent-integration.ts
|
|
39089
39091
|
import { createHash as createHash18, randomUUID as randomUUID3 } from "crypto";
|
|
39090
39092
|
import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
|
|
39091
|
-
import { existsSync as
|
|
39092
|
-
import { dirname as dirname15, join as
|
|
39093
|
+
import { existsSync as existsSync7, lstatSync as lstatSync20, statSync as statSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync14, readdirSync as readdirSync8, opendirSync as opendirSync5, readlinkSync as readlinkSync4, realpathSync as realpathSync8, renameSync as renameSync3, rmdirSync as rmdirSync3, writeFileSync as writeFileSync4, unlinkSync as unlinkSync4, chmodSync, openSync as openSync16, closeSync as closeSync16, fsyncSync as fsyncSync4, fstatSync as fstatSync16, readSync as readSync11, constants as constants12, linkSync as linkSync2 } from "fs";
|
|
39094
|
+
import { dirname as dirname15, join as join23, resolve as resolve16, relative as relative4, isAbsolute as isAbsolute17, sep as sep5 } from "path";
|
|
39093
39095
|
import { homedir as homedir3 } from "os";
|
|
39094
39096
|
|
|
39095
39097
|
// src/lib/managed-policy.ts
|
|
@@ -39368,8 +39370,8 @@ function isOwnedCliBridge(path, expectedPaths) {
|
|
|
39368
39370
|
|
|
39369
39371
|
// src/lib/agent-discovery.ts
|
|
39370
39372
|
import { createHash as createHash17 } from "crypto";
|
|
39371
|
-
import { lstatSync as
|
|
39372
|
-
import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute16, join as
|
|
39373
|
+
import { lstatSync as lstatSync19, readFileSync as readFileSync13, readdirSync as readdirSync7, realpathSync as realpathSync7 } from "fs";
|
|
39374
|
+
import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute16, join as join22, resolve as resolve15, sep as sep4 } from "path";
|
|
39373
39375
|
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
39374
39376
|
|
|
39375
39377
|
// node_modules/yaml/dist/index.js
|
|
@@ -41680,6 +41682,114 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
41680
41682
|
return true;
|
|
41681
41683
|
}
|
|
41682
41684
|
|
|
41685
|
+
// src/lib/agent-sumi.ts
|
|
41686
|
+
import { existsSync as existsSync6, lstatSync as lstatSync18, realpathSync as realpathSync6 } from "fs";
|
|
41687
|
+
import { join as join21, resolve as resolve14 } from "path";
|
|
41688
|
+
function sumiConfigDirectory(home, env = process.env) {
|
|
41689
|
+
for (const key of ["SUMI_CONFIG", "SUMI_CONFIG_CONTENT"])
|
|
41690
|
+
if (env[key] !== undefined)
|
|
41691
|
+
throw new Error(`Sumi ${key} requires a dedicated discovery adapter; unset it before Skills integration`);
|
|
41692
|
+
const selected = (key) => env[key]?.trim() ? env[key] : undefined;
|
|
41693
|
+
const expand = (value) => resolve14(value === "~" ? home : value.startsWith("~/") ? join21(home, value.slice(2)) : value);
|
|
41694
|
+
const explicit = selected("SUMI_CONFIG_DIR");
|
|
41695
|
+
const xdg = selected("XDG_CONFIG_HOME");
|
|
41696
|
+
const custom = selected("SUMI_HOME");
|
|
41697
|
+
const canonical6 = explicit ? expand(explicit) : xdg ? join21(expand(xdg), "sumi") : join21(custom ? expand(custom) : join21(home, ".hasna-internal/sumi"), "config");
|
|
41698
|
+
if (explicit || xdg || custom)
|
|
41699
|
+
return canonical6;
|
|
41700
|
+
const legacy = join21(home, ".config/sumi");
|
|
41701
|
+
const a = lstatSync18(canonical6, { throwIfNoEntry: false }), b = lstatSync18(legacy, { throwIfNoEntry: false });
|
|
41702
|
+
if (a && b && realpathSync6(canonical6) !== realpathSync6(legacy))
|
|
41703
|
+
throw new Error("Sumi config roots conflict; complete native home adoption before Skills integration");
|
|
41704
|
+
const path = a ? canonical6 : b ? legacy : canonical6;
|
|
41705
|
+
if (!existsSync6(path))
|
|
41706
|
+
return path;
|
|
41707
|
+
const physical = realpathSync6(path);
|
|
41708
|
+
if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![canonical6, legacy].includes(physical))
|
|
41709
|
+
throw new Error("Sumi config alias must resolve to its canonical or legacy root");
|
|
41710
|
+
return physical;
|
|
41711
|
+
}
|
|
41712
|
+
function sumiBridgeRoot(home) {
|
|
41713
|
+
return join21(sumiConfigDirectory(home), "skills");
|
|
41714
|
+
}
|
|
41715
|
+
function sumiConfigPath(home) {
|
|
41716
|
+
return join21(sumiConfigDirectory(home), "sumi.json");
|
|
41717
|
+
}
|
|
41718
|
+
function renderSumiPlugin(command, profile) {
|
|
41719
|
+
return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
|
|
41720
|
+
import { spawn } from "node:child_process";
|
|
41721
|
+
const command = ${JSON.stringify(command)};
|
|
41722
|
+
const profile = ${JSON.stringify(profile)};
|
|
41723
|
+
function context(input) {
|
|
41724
|
+
return new Promise((resolve, reject) => {
|
|
41725
|
+
const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
41726
|
+
const chunks = []; let bytes = 0, failure;
|
|
41727
|
+
const timer = setTimeout(() => { failure = new Error("Skills prompt hook timed out"); child.kill("SIGKILL"); }, 15000);
|
|
41728
|
+
child.stdout.on("data", chunk => { bytes += chunk.length; if (bytes > 1048576) { failure = new Error("Skills prompt hook output exceeded its limit"); child.kill("SIGKILL"); } else chunks.push(chunk); });
|
|
41729
|
+
child.stderr.on("data", () => {});
|
|
41730
|
+
child.stdin.on("error", () => {});
|
|
41731
|
+
child.on("error", error => { clearTimeout(timer); reject(error); });
|
|
41732
|
+
child.on("close", code => {
|
|
41733
|
+
clearTimeout(timer);
|
|
41734
|
+
if (failure || code !== 0) return reject(failure ?? new Error("Skills prompt hook failed"));
|
|
41735
|
+
try {
|
|
41736
|
+
const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
41737
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) throw new Error("Invalid Skills hook response");
|
|
41738
|
+
if (result.decision === "block" || result.continue === false) throw new Error("Skills prompt hook refused");
|
|
41739
|
+
if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw new Error("Invalid Skills hook response");
|
|
41740
|
+
const text = result.hookSpecificOutput?.additionalContext ?? "";
|
|
41741
|
+
if (typeof text !== "string") throw new Error("Invalid Skills prompt context");
|
|
41742
|
+
resolve(text);
|
|
41743
|
+
} catch (error) { reject(error); }
|
|
41744
|
+
});
|
|
41745
|
+
child.stdin.end(JSON.stringify(input));
|
|
41746
|
+
});
|
|
41747
|
+
}
|
|
41748
|
+
export default {
|
|
41749
|
+
id: "local.hasna-skills-cli",
|
|
41750
|
+
async setup(ctx) {
|
|
41751
|
+
if (!ctx?.session?.hook || !ctx?.session?.get || !ctx?.skill?.transform || !ctx?.tool?.hook) throw new Error("Unsupported Sumi native Skills contract");
|
|
41752
|
+
const registrations = [];
|
|
41753
|
+
const sessions = new Set();
|
|
41754
|
+
async function input(sessionID, prompt, first) {
|
|
41755
|
+
// The Promise adapter unwraps single-data endpoint responses.
|
|
41756
|
+
const session = await ctx.session.get({ sessionID });
|
|
41757
|
+
if (!session || session.id !== sessionID || typeof session.location?.directory !== "string" || (session.parentID !== undefined && typeof session.parentID !== "string") || session.parentID === sessionID) throw new Error("Invalid Sumi session custody");
|
|
41758
|
+
return { hook_event_name: first ? session.parentID ? "SubagentStart" : "SessionStart" : "UserPromptSubmit", cwd: session.location.directory, session_id: sessionID, parent_session_id: session.parentID ?? null, prompt, restore: true };
|
|
41759
|
+
}
|
|
41760
|
+
registrations.push(await ctx.skill.transform(editor => {
|
|
41761
|
+
for (const skill of editor.list()) if (skill.id !== "skills-cli" || skill.name !== "skills-cli") editor.remove(skill.id);
|
|
41762
|
+
}));
|
|
41763
|
+
// Native config transforms may add skills after this transform. Guard
|
|
41764
|
+
// both direct tool calls and prompt attachments at the consumption edge.
|
|
41765
|
+
registrations.push(await ctx.tool.hook("execute.before", event => {
|
|
41766
|
+
if (event.tool === "skill" && event.input?.id !== "skills-cli") throw new Error("Native Sumi skill payload refused; use Skills CLI");
|
|
41767
|
+
}));
|
|
41768
|
+
registrations.push(await ctx.session.hook("prompt", async event => {
|
|
41769
|
+
if (typeof event.prompt?.text !== "string") throw new Error("Invalid Sumi prompt");
|
|
41770
|
+
if (event.prompt.skills !== undefined && (!Array.isArray(event.prompt.skills) || event.prompt.skills.some(skill => skill?.id !== "skills-cli"))) throw new Error("Native Sumi skill attachment refused; use Skills CLI");
|
|
41771
|
+
await context(await input(event.sessionID, event.prompt.text, !sessions.has(event.sessionID)));
|
|
41772
|
+
sessions.add(event.sessionID);
|
|
41773
|
+
}));
|
|
41774
|
+
registrations.push(await ctx.session.hook("context", async event => {
|
|
41775
|
+
const last = event.messages.findLast(message => message.role === "user");
|
|
41776
|
+
const prompt = (last?.content ?? []).filter(part => part.type === "text").map(part => part.text).join("\\n");
|
|
41777
|
+
// Recheck on every primary request, including resumed and synthetic
|
|
41778
|
+
// sessions. The CLI owns optional failures versus explicit refusals.
|
|
41779
|
+
const text = await context(await input(event.sessionID, prompt, !sessions.has(event.sessionID)));
|
|
41780
|
+
if (text) event.system.push({ type: "text", text });
|
|
41781
|
+
sessions.add(event.sessionID);
|
|
41782
|
+
}));
|
|
41783
|
+
return async () => { for (const registration of registrations) await registration.dispose(); };
|
|
41784
|
+
},
|
|
41785
|
+
};
|
|
41786
|
+
`;
|
|
41787
|
+
}
|
|
41788
|
+
var SUMI_SKILL_PERMISSIONS = [
|
|
41789
|
+
{ action: "skill", resource: "*", effect: "deny" },
|
|
41790
|
+
{ action: "skill", resource: "skills-cli", effect: "allow" }
|
|
41791
|
+
];
|
|
41792
|
+
|
|
41683
41793
|
// src/lib/agent-discovery.ts
|
|
41684
41794
|
var digest2 = (text2) => createHash17("sha256").update(text2).digest("hex");
|
|
41685
41795
|
function parseConfig(text2, path, toml = false) {
|
|
@@ -41696,7 +41806,7 @@ function safe4(path) {
|
|
|
41696
41806
|
if (!isAbsolute16(path) || path.includes("\x00"))
|
|
41697
41807
|
throw new Error("Expected an absolute native discovery path");
|
|
41698
41808
|
for (let cursor = path;; cursor = dirname14(cursor)) {
|
|
41699
|
-
if (
|
|
41809
|
+
if (lstatSync19(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
41700
41810
|
throw new Error(`Refusing symlink native discovery input: ${cursor}`);
|
|
41701
41811
|
if (dirname14(cursor) === cursor)
|
|
41702
41812
|
break;
|
|
@@ -41706,7 +41816,7 @@ function read2(path, changes) {
|
|
|
41706
41816
|
safe4(path);
|
|
41707
41817
|
if (changes?.has(path))
|
|
41708
41818
|
return changes.get(path);
|
|
41709
|
-
const stat2 =
|
|
41819
|
+
const stat2 = lstatSync19(path, { throwIfNoEntry: false });
|
|
41710
41820
|
if (!stat2)
|
|
41711
41821
|
return null;
|
|
41712
41822
|
if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
|
|
@@ -41817,12 +41927,12 @@ function codexInstallationRoots(binding) {
|
|
|
41817
41927
|
if (binding.agent !== "codex" || binding.method !== "reviewed" || proof.version !== "codex-cli 0.160.0" || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
|
|
41818
41928
|
throw new Error("Invalid native installation input proof");
|
|
41819
41929
|
const cache = dirname14(dirname14(proof.plugins[0].pluginParent));
|
|
41820
|
-
const config =
|
|
41930
|
+
const config = join22(dirname14(dirname14(cache)), "config.toml"), home = dirname14(dirname14(dirname14(cache)));
|
|
41821
41931
|
const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
|
|
41822
|
-
const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) =>
|
|
41932
|
+
const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join22(home, path));
|
|
41823
41933
|
if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep4) || input.sourceRoot.startsWith(root2 + sep4)) || binding.sources.some((source) => basename7(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep4)))))
|
|
41824
41934
|
throw new Error("Native installation input overlaps configuration or loading roots");
|
|
41825
|
-
if (proof.plugins.some((input) => dirname14(dirname14(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path ===
|
|
41935
|
+
if (proof.plugins.some((input) => dirname14(dirname14(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join22(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
|
|
41826
41936
|
throw new Error("Missing native installation input source witness");
|
|
41827
41937
|
return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
|
|
41828
41938
|
const text2 = read2(path);
|
|
@@ -41864,19 +41974,19 @@ function captureDiscoveryPathSources(paths) {
|
|
|
41864
41974
|
const budget = discoveryByteBudget();
|
|
41865
41975
|
return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget) }));
|
|
41866
41976
|
}
|
|
41867
|
-
function assertProjectDiscovery(agent, directories, home, canonical6 =
|
|
41977
|
+
function assertProjectDiscovery(agent, directories, home, canonical6 = resolve15, reviewed) {
|
|
41868
41978
|
const homeConfigPath = canonical6(agentDiscoveryConfigPath(home, agent));
|
|
41869
41979
|
for (const directory of directories) {
|
|
41870
|
-
const isHome =
|
|
41980
|
+
const isHome = resolve15(directory) === resolve15(home);
|
|
41871
41981
|
if (agent === "hermes") {
|
|
41872
41982
|
assertHermesEnvironment(home);
|
|
41873
41983
|
continue;
|
|
41874
41984
|
}
|
|
41875
|
-
if (isHome && agent !== "claude")
|
|
41985
|
+
if (isHome && agent !== "claude" && agent !== "sumi")
|
|
41876
41986
|
continue;
|
|
41877
|
-
const paths = agent === "claude" ? [...isHome ? [] : [".claude/settings.json"], ".claude/settings.local.json"] : agent === "codex" ? [".codex/config.toml"] : agent === "gemini" ? [".gemini/settings.json"] : agent === "opencode" ? ["opencode.json", "opencode.jsonc", ".opencode/opencode.json", ".opencode/opencode.jsonc"] : [".cursor/hooks.json"];
|
|
41987
|
+
const paths = agent === "claude" ? [...isHome ? [] : [".claude/settings.json"], ".claude/settings.local.json"] : agent === "codex" ? [".codex/config.toml"] : agent === "gemini" ? [".gemini/settings.json"] : agent === "opencode" ? ["opencode.json", "opencode.jsonc", ".opencode/opencode.json", ".opencode/opencode.jsonc"] : agent === "sumi" ? ["sumi.json", "sumi.jsonc", ".sumi/sumi.json", ".sumi/sumi.jsonc"] : [".cursor/hooks.json"];
|
|
41878
41988
|
for (const suffix of paths) {
|
|
41879
|
-
const path = canonical6(
|
|
41989
|
+
const path = canonical6(join22(directory, suffix)), raw = read2(path);
|
|
41880
41990
|
if (raw === null)
|
|
41881
41991
|
continue;
|
|
41882
41992
|
const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
|
|
@@ -41885,7 +41995,7 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve14
|
|
|
41885
41995
|
if (suffix.endsWith(".jsonc"))
|
|
41886
41996
|
throw new Error(`NATIVE_SKILL_DRIFT: project JSONC discovery configuration requires review: ${path}`);
|
|
41887
41997
|
const config = parseConfig(raw, path, suffix.endsWith(".toml"));
|
|
41888
|
-
const keys2 = agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces", "skillOverrides"] : agent === "codex" ? ["plugins", "marketplaces", "skills"] : agent === "gemini" ? ["skills", "extensions"] : agent === "opencode" ? ["plugin", "skills"] : ["hooks"];
|
|
41998
|
+
const keys2 = agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces", "skillOverrides"] : agent === "codex" ? ["plugins", "marketplaces", "skills"] : agent === "gemini" ? ["skills", "extensions"] : agent === "opencode" ? ["plugin", "skills"] : agent === "sumi" ? ["plugin", "plugins", "skills", "permissions"] : ["hooks"];
|
|
41889
41999
|
let admittedPlugins = false;
|
|
41890
42000
|
if (agent === "claude" && reviewed?.agent === agent && reviewed.method === "reviewed" && config.enabledPlugins && typeof config.enabledPlugins === "object" && !Array.isArray(config.enabledPlugins)) {
|
|
41891
42001
|
const exact = reviewed.sources.find((source) => source.path === path && source.format === undefined && source.fields === undefined && (source.hashMode === undefined || source.hashMode === "bytes"));
|
|
@@ -41898,19 +42008,28 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve14
|
|
|
41898
42008
|
if (keys2.some((key) => config[key] !== undefined && !(key === "enabledPlugins" && admittedPlugins)) || config.disableAllHooks === true || config.disableBundledSkills === false || config.hooksConfig?.enabled === false || config.permission?.skill !== undefined || config.permissions?.deny?.some((rule) => typeof rule === "string" && /^Skill(?:\(|$)/.test(rule)))
|
|
41899
42009
|
throw new Error(`NATIVE_SKILL_DRIFT: higher-precedence project skill or hook configuration requires review: ${path}`);
|
|
41900
42010
|
}
|
|
42011
|
+
if (agent === "sumi")
|
|
42012
|
+
for (const name of ["plugin", "plugins"]) {
|
|
42013
|
+
const path = canonical6(join22(directory, ".sumi", name));
|
|
42014
|
+
safe4(path);
|
|
42015
|
+
if (lstatSync19(path, { throwIfNoEntry: false })?.isDirectory() && readdirSync7(path).length)
|
|
42016
|
+
throw new Error(`NATIVE_SKILL_DRIFT: project Sumi plugins require a dedicated discovery review: ${path}`);
|
|
42017
|
+
}
|
|
41901
42018
|
if (agent === "claude") {
|
|
41902
|
-
const commands = canonical6(
|
|
42019
|
+
const commands = canonical6(join22(directory, ".claude/commands"));
|
|
41903
42020
|
safe4(commands);
|
|
41904
|
-
if (
|
|
42021
|
+
if (lstatSync19(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync7(commands).length)
|
|
41905
42022
|
throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
|
|
41906
42023
|
}
|
|
41907
42024
|
}
|
|
41908
42025
|
}
|
|
41909
42026
|
function agentDiscoveryConfigPath(home, agent) {
|
|
41910
|
-
|
|
42027
|
+
if (agent === "sumi")
|
|
42028
|
+
return sumiConfigPath(home);
|
|
42029
|
+
return join22(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
|
|
41911
42030
|
}
|
|
41912
42031
|
function resolveAgentDiscovery(options) {
|
|
41913
|
-
const canonical6 = options.canonical ??
|
|
42032
|
+
const canonical6 = options.canonical ?? resolve15, home = resolve15(options.home), agent = options.agent;
|
|
41914
42033
|
const sources = [], roots = new Set, builtinNames = [];
|
|
41915
42034
|
const witness = (path, format, fields2) => {
|
|
41916
42035
|
const source = { path: canonical6(path), sha256: null, ...format ? { format, fields: fields2 } : {} };
|
|
@@ -41921,13 +42040,49 @@ function resolveAgentDiscovery(options) {
|
|
|
41921
42040
|
if (agent === "hermes")
|
|
41922
42041
|
assertHermesEnvironment(home);
|
|
41923
42042
|
const configPath = agentDiscoveryConfigPath(home, agent);
|
|
41924
|
-
const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [...CODEX_DISCOVERY_PROJECTION_FIELDS] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : ["version"]);
|
|
42043
|
+
const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [...CODEX_DISCOVERY_PROJECTION_FIELDS] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : agent === "sumi" ? ["skills", "plugin", "plugins", "permissions"] : ["version"]);
|
|
41925
42044
|
const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
|
|
41926
42045
|
const unresolved = (detail) => {
|
|
41927
42046
|
throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
|
|
41928
42047
|
};
|
|
42048
|
+
if (agent === "sumi") {
|
|
42049
|
+
const directory = sumiConfigDirectory(home);
|
|
42050
|
+
if (witness(join22(directory, "sumi.jsonc")) !== null)
|
|
42051
|
+
unresolved("JSONC configuration requires a supported format adapter");
|
|
42052
|
+
for (const root2 of [directory, join22(home, ".claude"), join22(home, ".agents")])
|
|
42053
|
+
for (const name of ["skill", "skills"])
|
|
42054
|
+
roots.add(join22(root2, name));
|
|
42055
|
+
if (config.skills !== undefined && (!Array.isArray(config.skills) || config.skills.some((path) => typeof path !== "string")))
|
|
42056
|
+
unresolved("skills must be a string array");
|
|
42057
|
+
for (const value of config.skills ?? []) {
|
|
42058
|
+
if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(isAbsolute16(value) || value.startsWith("~/")))
|
|
42059
|
+
unresolved("remote or relative skill sources need a dedicated discovery adapter");
|
|
42060
|
+
roots.add(value.startsWith("~/") ? join22(home, value.slice(2)) : value);
|
|
42061
|
+
}
|
|
42062
|
+
for (const key of ["plugin", "plugins"])
|
|
42063
|
+
if (config[key] !== undefined && !Array.isArray(config[key]))
|
|
42064
|
+
unresolved(`${key} must be an array`);
|
|
42065
|
+
const plugins = [join22(directory, "plugin"), join22(directory, "plugins")];
|
|
42066
|
+
for (const path of plugins) {
|
|
42067
|
+
safe4(path);
|
|
42068
|
+
if (lstatSync19(path, { throwIfNoEntry: false })?.isDirectory())
|
|
42069
|
+
for (const name of readdirSync7(path).sort()) {
|
|
42070
|
+
if (name === "skills-cli.js" && path === plugins[1])
|
|
42071
|
+
continue;
|
|
42072
|
+
const target = join22(path, name);
|
|
42073
|
+
if (!lstatSync19(target).isFile())
|
|
42074
|
+
unresolved("local plugin packages need a dedicated discovery adapter");
|
|
42075
|
+
witness(target);
|
|
42076
|
+
}
|
|
42077
|
+
}
|
|
42078
|
+
for (const root2 of roots) {
|
|
42079
|
+
safe4(root2);
|
|
42080
|
+
if (lstatSync19(root2, { throwIfNoEntry: false })?.isDirectory() && readdirSync7(root2).some((name) => name.endsWith(".md")))
|
|
42081
|
+
unresolved("flat markdown skills require a dedicated native migration adapter");
|
|
42082
|
+
}
|
|
42083
|
+
}
|
|
41929
42084
|
if (agent === "hermes") {
|
|
41930
|
-
const profile = witness(
|
|
42085
|
+
const profile = witness(join22(home, ".hermes/active_profile"));
|
|
41931
42086
|
if (profile?.trim() && profile.trim() !== "default")
|
|
41932
42087
|
unresolved("a non-default profile is active");
|
|
41933
42088
|
const declared = config.skills?.external_dirs ?? [], paths = typeof declared === "string" ? [declared] : declared;
|
|
@@ -41941,7 +42096,7 @@ function resolveAgentDiscovery(options) {
|
|
|
41941
42096
|
continue;
|
|
41942
42097
|
if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
|
|
41943
42098
|
unresolved("user-specific tilde expansion requires a separate discovery adapter");
|
|
41944
|
-
const path = normalized === "~" ? home : normalized.startsWith("~/") ?
|
|
42099
|
+
const path = normalized === "~" ? home : normalized.startsWith("~/") ? join22(home, normalized.slice(2)) : resolve15(home, ".hermes", normalized);
|
|
41945
42100
|
safe4(path);
|
|
41946
42101
|
roots.add(path);
|
|
41947
42102
|
}
|
|
@@ -41950,8 +42105,8 @@ function resolveAgentDiscovery(options) {
|
|
|
41950
42105
|
const executable = Bun.which("gemini");
|
|
41951
42106
|
if (executable) {
|
|
41952
42107
|
let packageRoot;
|
|
41953
|
-
for (let directory = dirname14(
|
|
41954
|
-
const path =
|
|
42108
|
+
for (let directory = dirname14(realpathSync7(executable)), depth = 0;depth < 8; directory = dirname14(directory), depth++) {
|
|
42109
|
+
const path = join22(directory, "package.json"), raw = read2(path);
|
|
41955
42110
|
if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
|
|
41956
42111
|
packageRoot = directory;
|
|
41957
42112
|
witness(path);
|
|
@@ -41962,12 +42117,12 @@ function resolveAgentDiscovery(options) {
|
|
|
41962
42117
|
}
|
|
41963
42118
|
if (!packageRoot)
|
|
41964
42119
|
unresolved("installed Gemini package layout is not recognized");
|
|
41965
|
-
const builtinRoot =
|
|
42120
|
+
const builtinRoot = join22(packageRoot, "bundle/builtin");
|
|
41966
42121
|
safe4(builtinRoot);
|
|
41967
|
-
if (!
|
|
42122
|
+
if (!lstatSync19(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
|
|
41968
42123
|
unresolved("installed Gemini builtin root is missing");
|
|
41969
42124
|
for (const name of readdirSync7(builtinRoot).sort()) {
|
|
41970
|
-
const text2 = witness(
|
|
42125
|
+
const text2 = witness(join22(builtinRoot, name, "SKILL.md"));
|
|
41971
42126
|
const match = text2?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
|
|
41972
42127
|
if (!match?.[1] || builtinNames.includes(match[1]))
|
|
41973
42128
|
unresolved("installed Gemini builtin name is missing or ambiguous");
|
|
@@ -42009,39 +42164,39 @@ function resolveAgentDiscovery(options) {
|
|
|
42009
42164
|
function plugin(root2) {
|
|
42010
42165
|
root2 = canonical6(root2);
|
|
42011
42166
|
safe4(root2);
|
|
42012
|
-
const manifestPath =
|
|
42167
|
+
const manifestPath = join22(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
42013
42168
|
const raw = witness(manifestPath);
|
|
42014
42169
|
if (raw === null)
|
|
42015
42170
|
unresolved("plugin manifest missing");
|
|
42016
42171
|
const manifest = parseConfig(raw, manifestPath);
|
|
42017
|
-
const hooks = witness(
|
|
42172
|
+
const hooks = witness(join22(root2, "hooks/hooks.json"));
|
|
42018
42173
|
if (manifest.hooks !== undefined || hooks !== null)
|
|
42019
42174
|
unresolved("plugin hooks require a separate no-skill-injection review");
|
|
42020
|
-
const commands =
|
|
42175
|
+
const commands = join22(root2, "commands");
|
|
42021
42176
|
safe4(commands);
|
|
42022
|
-
if (agent === "claude" && (manifest.commands !== undefined ||
|
|
42177
|
+
if (agent === "claude" && (manifest.commands !== undefined || lstatSync19(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync7(commands).length))
|
|
42023
42178
|
unresolved("legacy command discovery requires retirement or a dedicated format adapter");
|
|
42024
|
-
const rootSkill = witness(
|
|
42179
|
+
const rootSkill = witness(join22(root2, "SKILL.md"));
|
|
42025
42180
|
if (rootSkill !== null)
|
|
42026
42181
|
roots.add(root2);
|
|
42027
42182
|
const declared = manifest.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
|
|
42028
42183
|
if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
|
|
42029
42184
|
unresolved("unsupported plugin skill paths");
|
|
42030
42185
|
if (agent === "claude")
|
|
42031
|
-
roots.add(
|
|
42186
|
+
roots.add(join22(root2, "skills"));
|
|
42032
42187
|
for (const value of declared) {
|
|
42033
|
-
const path =
|
|
42188
|
+
const path = resolve15(root2, value);
|
|
42034
42189
|
if (path !== root2 && !path.startsWith(root2 + sep4))
|
|
42035
42190
|
unresolved("plugin skill path escapes its root");
|
|
42036
42191
|
roots.add(path.endsWith(`${sep4}SKILL.md`) ? dirname14(path) : path);
|
|
42037
42192
|
}
|
|
42038
42193
|
}
|
|
42039
42194
|
if (agent === "claude") {
|
|
42040
|
-
const registrations = witness(
|
|
42041
|
-
const installed = registrations === null ? {} : parseConfig(registrations,
|
|
42042
|
-
const commands = canonical6(
|
|
42195
|
+
const registrations = witness(join22(home, ".claude/plugins/installed_plugins.json"));
|
|
42196
|
+
const installed = registrations === null ? {} : parseConfig(registrations, join22(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
42197
|
+
const commands = canonical6(join22(home, ".claude/commands"));
|
|
42043
42198
|
safe4(commands);
|
|
42044
|
-
if (
|
|
42199
|
+
if (lstatSync19(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync7(commands).length)
|
|
42045
42200
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
42046
42201
|
for (const [id2, enabled] of Object.entries(config.enabledPlugins ?? {})) {
|
|
42047
42202
|
if (enabled === false)
|
|
@@ -42063,16 +42218,16 @@ function resolveAgentDiscovery(options) {
|
|
|
42063
42218
|
const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
|
|
42064
42219
|
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute16(market.source))
|
|
42065
42220
|
unresolved("enabled plugin marketplace is not an explicit local source");
|
|
42066
|
-
const catalogText = witness(
|
|
42221
|
+
const catalogText = witness(join22(market.source, ".agents/plugins/marketplace.json"));
|
|
42067
42222
|
if (catalogText === null)
|
|
42068
42223
|
unresolved("marketplace catalog missing");
|
|
42069
|
-
const catalog = parseConfig(catalogText,
|
|
42224
|
+
const catalog = parseConfig(catalogText, join22(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
|
|
42070
42225
|
if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
|
|
42071
42226
|
unresolved("plugin catalog source is missing or ambiguous");
|
|
42072
|
-
plugin(
|
|
42227
|
+
plugin(resolve15(market.source, matches[0].source.path));
|
|
42073
42228
|
}
|
|
42074
42229
|
} else if (agent === "opencode") {
|
|
42075
|
-
if (witness(
|
|
42230
|
+
if (witness(join22(home, ".config/opencode/opencode.jsonc")) !== null || witness(join22(home, ".config/opencode/config.json")) !== null)
|
|
42076
42231
|
unresolved("additional config layers require review");
|
|
42077
42232
|
if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
|
|
42078
42233
|
unresolved("external plugin or remote skill source");
|
|
@@ -42081,14 +42236,17 @@ function resolveAgentDiscovery(options) {
|
|
|
42081
42236
|
unresolved("relative or malformed added skill root");
|
|
42082
42237
|
roots.add(path);
|
|
42083
42238
|
}
|
|
42084
|
-
const localPlugins =
|
|
42239
|
+
const localPlugins = join22(home, ".config/opencode/plugins");
|
|
42085
42240
|
safe4(localPlugins);
|
|
42086
|
-
if (
|
|
42241
|
+
if (lstatSync19(localPlugins, { throwIfNoEntry: false }) && readdirSync7(localPlugins).some((name) => name !== "skills-cli.js"))
|
|
42087
42242
|
unresolved("additional local plugin hooks require review");
|
|
42243
|
+
} else if (agent === "sumi") {
|
|
42244
|
+
if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical6(configPath) && source.path !== canonical6(join22(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null))
|
|
42245
|
+
unresolved("external or foreign local plugins require review");
|
|
42088
42246
|
} else if (agent === "hermes") {
|
|
42089
|
-
for (const directory of [
|
|
42247
|
+
for (const directory of [join22(home, ".hermes/plugins"), join22(home, ".hermes/hermes-agent")]) {
|
|
42090
42248
|
safe4(directory);
|
|
42091
|
-
if (
|
|
42249
|
+
if (lstatSync19(directory, { throwIfNoEntry: false }))
|
|
42092
42250
|
unresolved("installed runtime/plugin discovery requires reviewed source bindings");
|
|
42093
42251
|
}
|
|
42094
42252
|
if (Bun.which("hermes") || Object.keys(config.plugins ?? {}).length)
|
|
@@ -42096,28 +42254,28 @@ function resolveAgentDiscovery(options) {
|
|
|
42096
42254
|
} else if (agent === "gemini") {
|
|
42097
42255
|
if (Object.keys(config.extensions ?? {}).length || config.skills?.paths?.length)
|
|
42098
42256
|
unresolved("custom extension or skill paths");
|
|
42099
|
-
const directory =
|
|
42257
|
+
const directory = join22(home, ".gemini/extensions");
|
|
42100
42258
|
safe4(directory);
|
|
42101
|
-
if (
|
|
42259
|
+
if (lstatSync19(directory, { throwIfNoEntry: false }))
|
|
42102
42260
|
for (const name of readdirSync7(directory)) {
|
|
42103
|
-
const root2 =
|
|
42261
|
+
const root2 = join22(directory, name);
|
|
42104
42262
|
safe4(root2);
|
|
42105
|
-
if (!
|
|
42263
|
+
if (!lstatSync19(root2)?.isDirectory())
|
|
42106
42264
|
continue;
|
|
42107
|
-
const raw = witness(
|
|
42265
|
+
const raw = witness(join22(root2, "gemini-extension.json"));
|
|
42108
42266
|
if (raw === null)
|
|
42109
42267
|
unresolved("extension registration missing");
|
|
42110
|
-
const manifest = parseConfig(raw,
|
|
42111
|
-
if (manifest.hooks !== undefined || witness(
|
|
42268
|
+
const manifest = parseConfig(raw, join22(root2, "gemini-extension.json"));
|
|
42269
|
+
if (manifest.hooks !== undefined || witness(join22(root2, "hooks/hooks.json")) !== null || witness(join22(root2, ".gemini-extension-install.json")) !== null)
|
|
42112
42270
|
unresolved("extension hooks or linked registration require review");
|
|
42113
|
-
roots.add(
|
|
42271
|
+
roots.add(join22(root2, "skills"));
|
|
42114
42272
|
}
|
|
42115
42273
|
}
|
|
42116
|
-
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([
|
|
42274
|
+
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join22(home, ".hermes/plugins"), join22(home, ".hermes/hermes-agent")].map((path) => canonical6(path))) } : {} };
|
|
42117
42275
|
}
|
|
42118
42276
|
|
|
42119
42277
|
// src/lib/agent-adapters.ts
|
|
42120
|
-
var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes"];
|
|
42278
|
+
var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes", "sumi"];
|
|
42121
42279
|
function renderAgentHookCommand(command, agent, profileId, event) {
|
|
42122
42280
|
if (typeof profileId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(profileId) || profileId.includes(".."))
|
|
42123
42281
|
throw new Error("Invalid selection profile id");
|
|
@@ -42128,6 +42286,7 @@ var AGENT_ADAPTERS = {
|
|
|
42128
42286
|
codex: { root: ".codex/skills", config: ".codex/hooks.json", events: ["UserPromptSubmit", "SessionStart", "SubagentStart"], promptContext: true },
|
|
42129
42287
|
gemini: { root: ".gemini/skills", config: ".gemini/settings.json", events: ["BeforeAgent", "SessionStart"], promptContext: true },
|
|
42130
42288
|
opencode: { root: ".config/opencode/skills", config: ".config/opencode/opencode.json", events: ["chat.message"], promptContext: true },
|
|
42289
|
+
sumi: { root: ".hasna-internal/sumi/config/skills", config: ".hasna-internal/sumi/config/sumi.json", events: ["prompt", "context"], promptContext: true },
|
|
42131
42290
|
hermes: { root: ".hermes/skills", config: ".hermes/config.yaml", events: ["pre_llm_call", "pre_tool_call"], promptContext: true, promptFailureMode: "open", toolFailureMode: "supervised-child-errors-block", requiresNativeTrust: true },
|
|
42132
42291
|
cursor: { root: ".cursor/skills", config: ".cursor/hooks.json", events: ["beforeSubmitPrompt", "sessionStart"], promptContext: false }
|
|
42133
42292
|
};
|
|
@@ -42484,11 +42643,11 @@ function isSystemRootAlias(path) {
|
|
|
42484
42643
|
const target = `/private${path}`, link = `private${path}`;
|
|
42485
42644
|
const identity4 = (stat2) => `${stat2.dev}:${stat2.ino}:${stat2.ctimeNs}:${stat2.uid}:${stat2.mode}`;
|
|
42486
42645
|
try {
|
|
42487
|
-
const before =
|
|
42646
|
+
const before = lstatSync20(path, { bigint: true });
|
|
42488
42647
|
if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync4(path) !== link)
|
|
42489
42648
|
return false;
|
|
42490
42649
|
const directories = ["/", "/private", target].map((directory) => {
|
|
42491
|
-
const stat2 =
|
|
42650
|
+
const stat2 = lstatSync20(directory, { bigint: true });
|
|
42492
42651
|
if (!stat2.isDirectory() || stat2.uid !== 0n)
|
|
42493
42652
|
throw new Error("Untrusted system alias directory");
|
|
42494
42653
|
if (directory !== "/private/tmp") {
|
|
@@ -42505,23 +42664,23 @@ function isSystemRootAlias(path) {
|
|
|
42505
42664
|
}
|
|
42506
42665
|
return { directory, identity: identity4(stat2) };
|
|
42507
42666
|
});
|
|
42508
|
-
return
|
|
42667
|
+
return realpathSync8(path) === target && readlinkSync4(path) === link && identity4(lstatSync20(path, { bigint: true })) === identity4(before) && directories.every((item) => identity4(lstatSync20(item.directory, { bigint: true })) === item.identity);
|
|
42509
42668
|
} catch {
|
|
42510
42669
|
return false;
|
|
42511
42670
|
}
|
|
42512
42671
|
}
|
|
42513
42672
|
function canonicalSystemPath(path) {
|
|
42514
|
-
const absolute =
|
|
42515
|
-
if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root2) || !
|
|
42673
|
+
const absolute = resolve16(path), root2 = `/${absolute.split(sep5)[1]}`;
|
|
42674
|
+
if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root2) || !lstatSync20(root2, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
42516
42675
|
return absolute;
|
|
42517
42676
|
if (!isSystemRootAlias(root2))
|
|
42518
42677
|
throw new Error(`Refusing symlink path: ${root2}`);
|
|
42519
42678
|
return `/private${absolute}`;
|
|
42520
42679
|
}
|
|
42521
42680
|
function assertSafePath(path) {
|
|
42522
|
-
let cursor =
|
|
42681
|
+
let cursor = resolve16(path);
|
|
42523
42682
|
while (true) {
|
|
42524
|
-
if (
|
|
42683
|
+
if (lstatSync20(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
|
|
42525
42684
|
throw new Error(`Refusing symlink path: ${cursor}`);
|
|
42526
42685
|
const parent = dirname15(cursor);
|
|
42527
42686
|
if (parent === cursor)
|
|
@@ -42531,16 +42690,16 @@ function assertSafePath(path) {
|
|
|
42531
42690
|
}
|
|
42532
42691
|
function readRootAlias(home, agent) {
|
|
42533
42692
|
assertSafePath(home);
|
|
42534
|
-
const realHome =
|
|
42535
|
-
const stat2 =
|
|
42693
|
+
const realHome = realpathSync8(home), alias = join23(realHome, `.${agent}`);
|
|
42694
|
+
const stat2 = lstatSync20(alias, { throwIfNoEntry: false, bigint: true });
|
|
42536
42695
|
if (!stat2?.isSymbolicLink())
|
|
42537
42696
|
throw new Error(`Agent root alias changed after planning: ${alias}`);
|
|
42538
|
-
const link = readlinkSync4(alias), targetPath =
|
|
42697
|
+
const link = readlinkSync4(alias), targetPath = resolve16(dirname15(alias), link);
|
|
42539
42698
|
assertSafePath(targetPath);
|
|
42540
|
-
const targetStat =
|
|
42699
|
+
const targetStat = lstatSync20(targetPath, { throwIfNoEntry: false });
|
|
42541
42700
|
if (!targetStat?.isDirectory())
|
|
42542
42701
|
throw new Error(`Agent root alias must target an existing directory: ${alias}`);
|
|
42543
|
-
const target =
|
|
42702
|
+
const target = realpathSync8(targetPath), inside = relative4(realHome, target);
|
|
42544
42703
|
if (!inside || inside === ".." || inside.startsWith(`..${sep5}`) || isAbsolute17(inside))
|
|
42545
42704
|
throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
|
|
42546
42705
|
return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat2.dev}:${stat2.ino}:${stat2.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
|
|
@@ -42550,7 +42709,7 @@ function rootAliases(home, allowed = false) {
|
|
|
42550
42709
|
return [];
|
|
42551
42710
|
const result = [];
|
|
42552
42711
|
for (const agent of ["claude", "codex"])
|
|
42553
|
-
if (
|
|
42712
|
+
if (lstatSync20(join23(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
|
|
42554
42713
|
result.push(readRootAlias(home, agent));
|
|
42555
42714
|
if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep5) || other.target.startsWith(item.target + sep5))))
|
|
42556
42715
|
throw new Error("Agent root alias targets must not overlap");
|
|
@@ -42558,7 +42717,7 @@ function rootAliases(home, allowed = false) {
|
|
|
42558
42717
|
}
|
|
42559
42718
|
function canonicalAgentPath(path, aliases) {
|
|
42560
42719
|
const absolute = canonicalSystemPath(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep5));
|
|
42561
|
-
return binding ?
|
|
42720
|
+
return binding ? join23(binding.target, relative4(binding.alias, absolute)) : absolute;
|
|
42562
42721
|
}
|
|
42563
42722
|
function disabledCodexSkillPaths(config, aliases) {
|
|
42564
42723
|
const skills = config.skills;
|
|
@@ -42577,7 +42736,7 @@ function disabledCodexSkillPaths(config, aliases) {
|
|
|
42577
42736
|
continue;
|
|
42578
42737
|
let document2;
|
|
42579
42738
|
try {
|
|
42580
|
-
document2 =
|
|
42739
|
+
document2 = realpathSync8(canonicalAgentPath(entry.path, aliases));
|
|
42581
42740
|
} catch {
|
|
42582
42741
|
continue;
|
|
42583
42742
|
}
|
|
@@ -42589,7 +42748,7 @@ function disabledCodexSkillPaths(config, aliases) {
|
|
|
42589
42748
|
}
|
|
42590
42749
|
function recheckRootAliases(aliases) {
|
|
42591
42750
|
for (const binding of aliases) {
|
|
42592
|
-
if (!["claude", "codex"].includes(binding.agent) || binding.alias !==
|
|
42751
|
+
if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join23(binding.home, `.${binding.agent}`))
|
|
42593
42752
|
throw new Error("Invalid agent root alias binding");
|
|
42594
42753
|
const current = readRootAlias(binding.home, binding.agent);
|
|
42595
42754
|
if (JSON.stringify(current) !== JSON.stringify(binding))
|
|
@@ -42598,7 +42757,7 @@ function recheckRootAliases(aliases) {
|
|
|
42598
42757
|
}
|
|
42599
42758
|
function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
42600
42759
|
assertSafePath(path);
|
|
42601
|
-
const before =
|
|
42760
|
+
const before = lstatSync20(path);
|
|
42602
42761
|
if (!before.isFile() || before.size > maximum)
|
|
42603
42762
|
throw new Error("Unsupported or oversized native skill file");
|
|
42604
42763
|
const descriptor = openSync16(path, constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK);
|
|
@@ -42614,7 +42773,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
|
42614
42773
|
break;
|
|
42615
42774
|
length += count;
|
|
42616
42775
|
}
|
|
42617
|
-
const after = fstatSync16(descriptor), current =
|
|
42776
|
+
const after = fstatSync16(descriptor), current = lstatSync20(path);
|
|
42618
42777
|
if (current.dev !== opened.dev || current.ino !== opened.ino || current.size !== after.size || current.mtimeMs !== after.mtimeMs || current.ctimeMs !== after.ctimeMs || length !== before.size || after.size !== before.size || after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs)
|
|
42619
42778
|
throw new Error("Native skill file changed while reading");
|
|
42620
42779
|
return bytes.subarray(0, length);
|
|
@@ -42626,14 +42785,14 @@ function treeHash(root2) {
|
|
|
42626
42785
|
const hash2 = createHash18("sha256");
|
|
42627
42786
|
let bytes = 0, files = 0;
|
|
42628
42787
|
function visit2(path) {
|
|
42629
|
-
const stat2 =
|
|
42788
|
+
const stat2 = lstatSync20(path);
|
|
42630
42789
|
if (stat2.isSymbolicLink())
|
|
42631
42790
|
throw new Error(`Refusing symlink in skill: ${path}`);
|
|
42632
42791
|
const rel = relative4(root2, path);
|
|
42633
42792
|
if (stat2.isDirectory()) {
|
|
42634
42793
|
hash2.update(`d\x00${rel}\x00`);
|
|
42635
42794
|
for (const name of readdirSync8(path).sort())
|
|
42636
|
-
visit2(
|
|
42795
|
+
visit2(join23(path, name));
|
|
42637
42796
|
return;
|
|
42638
42797
|
}
|
|
42639
42798
|
if (!stat2.isFile())
|
|
@@ -42655,10 +42814,10 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
42655
42814
|
const identities = new Set;
|
|
42656
42815
|
const selected = [];
|
|
42657
42816
|
for (const target of manifest.targets) {
|
|
42658
|
-
if (!isAbsolute17(target.projectRoot) ||
|
|
42817
|
+
if (!isAbsolute17(target.projectRoot) || resolve16(target.projectRoot) !== target.projectRoot)
|
|
42659
42818
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
42660
|
-
const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative4(projectRoot,
|
|
42661
|
-
if (!
|
|
42819
|
+
const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative4(projectRoot, resolve16(projectRoot, target.path));
|
|
42820
|
+
if (!existsSync7(projectRoot) || !lstatSync20(projectRoot).isDirectory())
|
|
42662
42821
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
42663
42822
|
assertSafePath(projectRoot);
|
|
42664
42823
|
if (isAbsolute17(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep5}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
|
|
@@ -42667,8 +42826,8 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
42667
42826
|
if (identities.has(identity4))
|
|
42668
42827
|
throw new Error(`Duplicate native migration target: ${target.path}`);
|
|
42669
42828
|
identities.add(identity4);
|
|
42670
|
-
const expected = canonicalSystemPath(
|
|
42671
|
-
const matches = inventory.filter((entry2) => entry2.agent === target.agent &&
|
|
42829
|
+
const expected = canonicalSystemPath(resolve16(target.projectRoot, target.path));
|
|
42830
|
+
const matches = inventory.filter((entry2) => entry2.agent === target.agent && resolve16(entry2.path) === expected);
|
|
42672
42831
|
if (matches.length !== 1)
|
|
42673
42832
|
throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
|
|
42674
42833
|
const entry = matches[0];
|
|
@@ -42697,9 +42856,9 @@ function projectAncestorDirectories(projects) {
|
|
|
42697
42856
|
}
|
|
42698
42857
|
function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
42699
42858
|
home = canonicalSystemPath(home);
|
|
42700
|
-
if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute17(options.reviewedCacheAlias) ||
|
|
42859
|
+
if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute17(options.reviewedCacheAlias) || resolve16(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
|
|
42701
42860
|
throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
|
|
42702
|
-
if (options.disabledVendorPaths?.some((path) => !isAbsolute17(path) ||
|
|
42861
|
+
if (options.disabledVendorPaths?.some((path) => !isAbsolute17(path) || resolve16(path) !== path))
|
|
42703
42862
|
throw new Error("Disabled vendor paths must be exact absolute paths");
|
|
42704
42863
|
const reviewedCacheAlias = options.reviewedCacheAlias === undefined ? undefined : canonicalSystemPath(options.reviewedCacheAlias);
|
|
42705
42864
|
const disabledVendorPaths = new Set((options.disabledVendorPaths ?? []).map(canonicalSystemPath));
|
|
@@ -42707,14 +42866,18 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
42707
42866
|
const selectedAgents = options.agents ? new Set(options.agents) : undefined;
|
|
42708
42867
|
const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
|
|
42709
42868
|
const rootDefinitions = selectedAgents ? ROOTS.filter(([agent, path]) => selectedAgents.has(agent) || selectedAgents.has("hermes") && agent === "codex" && path === ".agents/skills") : ROOTS;
|
|
42710
|
-
const roots = rootDefinitions.map(([agent, path]) => [agent, canonicalAgentPath(
|
|
42711
|
-
const bridgePaths = Object.
|
|
42869
|
+
const roots = rootDefinitions.map(([agent, path]) => [agent, canonicalAgentPath(join23(home, path), aliases)]);
|
|
42870
|
+
const bridgePaths = Object.entries(AGENT_ADAPTERS).filter(([agent]) => agent !== "sumi" || includesAgent("sumi")).map(([agent, adapter]) => canonicalAgentPath(join23(agent === "sumi" ? sumiBridgeRoot(home) : join23(home, adapter.root), CLI_BRIDGE_NAME), aliases));
|
|
42712
42871
|
for (const project of projectAncestorDirectories([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []])) {
|
|
42713
42872
|
for (const [agent, path] of rootDefinitions)
|
|
42714
|
-
roots.push([agent, canonicalAgentPath(
|
|
42873
|
+
roots.push([agent, canonicalAgentPath(join23(project, path), aliases)]);
|
|
42874
|
+
if (selectedAgents?.has("sumi"))
|
|
42875
|
+
for (const directory of [".claude", ".agents"])
|
|
42876
|
+
for (const name of ["skill", "skills"])
|
|
42877
|
+
roots.push(["sumi", canonicalAgentPath(join23(project, directory, name), aliases)]);
|
|
42715
42878
|
}
|
|
42716
42879
|
const installationInputs = options.codexInstallationInputRoots ?? [];
|
|
42717
|
-
if (installationInputs.some((input) =>
|
|
42880
|
+
if (installationInputs.some((input) => resolve16(input) !== input || realpathSync8(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep5) || input.startsWith(path + sep5)))))
|
|
42718
42881
|
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
42719
42882
|
const entries = [], seen = new Set;
|
|
42720
42883
|
const cacheScans = new Map, maxAliasProofEntries = 1e4;
|
|
@@ -42728,13 +42891,13 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
42728
42891
|
}
|
|
42729
42892
|
let reviewedCacheAliasFound = false;
|
|
42730
42893
|
function verifiedSiblingCacheAlias(agent, path, parent) {
|
|
42731
|
-
const link = readlinkSync4(path), lexicalTarget =
|
|
42894
|
+
const link = readlinkSync4(path), lexicalTarget = resolve16(parent, link);
|
|
42732
42895
|
if (isAbsolute17(link) ? link !== lexicalTarget : dirname15(link) !== ".")
|
|
42733
42896
|
return false;
|
|
42734
42897
|
const target = canonicalSystemPath(lexicalTarget);
|
|
42735
42898
|
if (dirname15(target) !== parent)
|
|
42736
42899
|
return false;
|
|
42737
|
-
if (!
|
|
42900
|
+
if (!lstatSync20(target, { throwIfNoEntry: false })?.isDirectory())
|
|
42738
42901
|
return false;
|
|
42739
42902
|
const scan = cacheScans.get(target);
|
|
42740
42903
|
if (!scan?.complete || scan.entries > maxAliasProofEntries)
|
|
@@ -42757,37 +42920,40 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
42757
42920
|
assertSafePath(path);
|
|
42758
42921
|
if (configuredRoot && agent === "codex" && installationInputs.some((root2) => path === root2 || path.startsWith(`${root2}${sep5}`)))
|
|
42759
42922
|
return scan;
|
|
42760
|
-
if (!
|
|
42923
|
+
if (!existsSync7(path))
|
|
42761
42924
|
return scan;
|
|
42762
|
-
const stat2 =
|
|
42763
|
-
if (stat2.isFile())
|
|
42925
|
+
const stat2 = lstatSync20(path);
|
|
42926
|
+
if (stat2.isFile()) {
|
|
42927
|
+
if (agent === "sumi" && depth === 1 && path.endsWith(".md"))
|
|
42928
|
+
throw new Error("NATIVE_SKILL_DRIFT: flat Sumi skill documents require a dedicated migration adapter");
|
|
42764
42929
|
return scan;
|
|
42930
|
+
}
|
|
42765
42931
|
if (!stat2.isDirectory())
|
|
42766
42932
|
throw new Error(`Unsupported native discovery entry: ${path}`);
|
|
42767
|
-
if (
|
|
42933
|
+
if (existsSync7(join23(path, "SKILL.md"))) {
|
|
42768
42934
|
scan.hasSkills = true;
|
|
42769
42935
|
if (seen.has(path))
|
|
42770
42936
|
return scan;
|
|
42771
42937
|
seen.add(path);
|
|
42772
42938
|
let managed = false;
|
|
42773
|
-
const marker =
|
|
42774
|
-
if (
|
|
42939
|
+
const marker = join23(path, ".hasna-skills.json");
|
|
42940
|
+
if (existsSync7(marker)) {
|
|
42775
42941
|
try {
|
|
42776
42942
|
managed = JSON.parse(readFileSync14(marker, "utf8")).managedBy === "@hasna/skills";
|
|
42777
42943
|
} catch {}
|
|
42778
42944
|
}
|
|
42779
|
-
const bridge = !vendor && isOwnedCliBridge(path, bridgePaths);
|
|
42945
|
+
const bridge = (!vendor || agent === "sumi") && isOwnedCliBridge(path, bridgePaths);
|
|
42780
42946
|
const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep5));
|
|
42781
|
-
entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(
|
|
42947
|
+
entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep5) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve16(home) } : {}, ...rootAlias ? { rootAlias } : {} });
|
|
42782
42948
|
return scan;
|
|
42783
42949
|
}
|
|
42784
|
-
if (depth > (vendor ? 32 : 3))
|
|
42950
|
+
if (depth > (agent === "sumi" ? 32 : vendor ? 32 : 3))
|
|
42785
42951
|
return { ...scan, complete: false };
|
|
42786
42952
|
const children = [], directory = opendirSync5(path);
|
|
42787
42953
|
try {
|
|
42788
42954
|
let child;
|
|
42789
42955
|
while ((child = directory.readSync()) !== null) {
|
|
42790
|
-
admit(
|
|
42956
|
+
admit(join23(path, child.name));
|
|
42791
42957
|
children.push(child);
|
|
42792
42958
|
}
|
|
42793
42959
|
} finally {
|
|
@@ -42799,13 +42965,13 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
42799
42965
|
for (const { name } of children) {
|
|
42800
42966
|
if (name === "node_modules")
|
|
42801
42967
|
continue;
|
|
42802
|
-
if (name.startsWith(".") && name !== ".system" && agent !== "hermes" && !(options.guardHermes && `${path}${sep5}`.includes(`${sep5}.agents${sep5}skills${sep5}`)))
|
|
42968
|
+
if (name.startsWith(".") && name !== ".system" && agent !== "hermes" && agent !== "sumi" && !(options.guardHermes && `${path}${sep5}`.includes(`${sep5}.agents${sep5}skills${sep5}`)))
|
|
42803
42969
|
continue;
|
|
42804
42970
|
const isVendor = vendor || name === ".system";
|
|
42805
42971
|
if (isVendor && !options.includeVendor)
|
|
42806
42972
|
continue;
|
|
42807
|
-
const child =
|
|
42808
|
-
if (isVendor &&
|
|
42973
|
+
const child = join23(path, name);
|
|
42974
|
+
if (isVendor && lstatSync20(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
42809
42975
|
const target = statSync6(child, { throwIfNoEntry: false });
|
|
42810
42976
|
if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child, path)) {
|
|
42811
42977
|
scan.entries++;
|
|
@@ -42831,11 +42997,11 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
42831
42997
|
scanRoot(agent, path);
|
|
42832
42998
|
if (options.includeVendor) {
|
|
42833
42999
|
for (const agent of ["codex", "claude"].filter(includesAgent))
|
|
42834
|
-
scanRoot(agent, canonicalAgentPath(
|
|
43000
|
+
scanRoot(agent, canonicalAgentPath(join23(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
|
|
42835
43001
|
if (includesAgent("claude"))
|
|
42836
|
-
scanRoot("claude", canonicalAgentPath(
|
|
43002
|
+
scanRoot("claude", canonicalAgentPath(join23(home, ".claude", "plugins", "synced"), aliases), true);
|
|
42837
43003
|
if (includesAgent("gemini"))
|
|
42838
|
-
scanRoot("gemini",
|
|
43004
|
+
scanRoot("gemini", join23(home, ".gemini", "extensions"), true);
|
|
42839
43005
|
}
|
|
42840
43006
|
const configured = options.configured ? INTEGRATION_AGENTS.filter(includesAgent).flatMap((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }).roots.map((path) => ({ agent, path }))) : [];
|
|
42841
43007
|
for (const root2 of [...options.agentRoots ?? [], ...configured])
|
|
@@ -42848,7 +43014,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
42848
43014
|
}
|
|
42849
43015
|
function readOptional(path) {
|
|
42850
43016
|
assertSafePath(path);
|
|
42851
|
-
return
|
|
43017
|
+
return existsSync7(path) ? readFileSync14(path, "utf8") : null;
|
|
42852
43018
|
}
|
|
42853
43019
|
function jsonObject(text2, path) {
|
|
42854
43020
|
if (text2 === null)
|
|
@@ -42948,19 +43114,19 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
42948
43114
|
function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
42949
43115
|
const bundled = disableCodexBundledSkills(text2);
|
|
42950
43116
|
let result = normalizeCodexInlinePathConfig(bundled);
|
|
42951
|
-
const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [
|
|
43117
|
+
const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve16(bridgePath), resolve16(join23(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
|
|
42952
43118
|
const previousConfig = Bun.TOML.parse(bundled).skills?.config;
|
|
42953
43119
|
if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
|
|
42954
43120
|
throw new Error("Codex skill controls require one path or name selector per [[skills.config]] entry; review ambiguous entries before running skills hook install");
|
|
42955
43121
|
const bridgeNeedsRepair = previousConfig?.some((entry) => entry.enabled === false && bridgeSelector(entry));
|
|
42956
43122
|
const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
|
|
42957
43123
|
for (const skill of selections) {
|
|
42958
|
-
const path =
|
|
43124
|
+
const path = join23(skill.path, "SKILL.md");
|
|
42959
43125
|
let found = false;
|
|
42960
43126
|
result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
|
|
42961
43127
|
const parsed = Bun.TOML.parse(section);
|
|
42962
43128
|
const declared = parsed.skills?.config?.[0];
|
|
42963
|
-
if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [
|
|
43129
|
+
if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve16(path), resolve16(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
|
|
42964
43130
|
return section;
|
|
42965
43131
|
found = true;
|
|
42966
43132
|
return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
|
|
@@ -42981,14 +43147,14 @@ enabled = false
|
|
|
42981
43147
|
return result;
|
|
42982
43148
|
}
|
|
42983
43149
|
function assertCodexInstallationInputs(home, projectDirs, inputs) {
|
|
42984
|
-
const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) =>
|
|
43150
|
+
const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve16(directory, path)));
|
|
42985
43151
|
if (inputs.some((input) => nativeRoots.some((root2) => root2 === input || root2.startsWith(input + sep5) || input.startsWith(root2 + sep5))))
|
|
42986
43152
|
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
42987
43153
|
}
|
|
42988
43154
|
function planAgentIntegration(options) {
|
|
42989
43155
|
const home = options.home ?? homedir3(), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
42990
43156
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
42991
|
-
const policyPath =
|
|
43157
|
+
const policyPath = join23(dataDir2, "agent-policy.json");
|
|
42992
43158
|
assertSafePath(policyPath);
|
|
42993
43159
|
const priorSnapshot = readManagedSkillPolicySnapshot(dataDir2), previousPolicy = priorSnapshot?.text ?? null, policy = priorSnapshot?.value ?? {};
|
|
42994
43160
|
if (policy.bridge !== undefined && (!policy.bridge || typeof policy.bridge !== "object" || Array.isArray(policy.bridge)))
|
|
@@ -43020,10 +43186,10 @@ function planAgentIntegration(options) {
|
|
|
43020
43186
|
}
|
|
43021
43187
|
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
43022
43188
|
});
|
|
43023
|
-
const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(
|
|
43189
|
+
const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases)) ?? "") : {};
|
|
43024
43190
|
const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias, disabledVendorPaths: disabledCodexSkillPaths(codexConfig, aliases) });
|
|
43025
43191
|
const observedNativeSources = [];
|
|
43026
|
-
const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) =>
|
|
43192
|
+
const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join23(entry.path, "SKILL.md")), canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), resolve16(options.projectDir ?? home), (path) => {
|
|
43027
43193
|
const bytes = readNativeBytes(path, 1024 * 1024);
|
|
43028
43194
|
observedNativeSources.push({ path, sha256: sha4(bytes) });
|
|
43029
43195
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
@@ -43031,7 +43197,7 @@ function planAgentIntegration(options) {
|
|
|
43031
43197
|
const codexPluginSkills = codexPluginControls.skills;
|
|
43032
43198
|
const codexInactivePlugins = codexPluginControls.inactivePlugins;
|
|
43033
43199
|
const codexDiscovery = discoveries.find((binding) => binding.agent === "codex");
|
|
43034
|
-
const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep5) || input.sourceRoot.startsWith(root2 + sep5)) && codexDiscovery.sources.some((source) => source.path ===
|
|
43200
|
+
const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep5) || input.sourceRoot.startsWith(root2 + sep5)) && codexDiscovery.sources.some((source) => source.path === join23(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
|
|
43035
43201
|
assertCodexInstallationInputs(home, [options.projectDir ?? home], codexPluginSourceInputs.map((input) => input.sourceRoot));
|
|
43036
43202
|
const changes = [];
|
|
43037
43203
|
for (const agent of [...new Set(options.agents)]) {
|
|
@@ -43039,19 +43205,19 @@ function planAgentIntegration(options) {
|
|
|
43039
43205
|
throw new Error(`Unsupported agent: ${agent}`);
|
|
43040
43206
|
const { command, profileId: profileId2 } = bindings.get(agent);
|
|
43041
43207
|
const adapter = AGENT_ADAPTERS[agent];
|
|
43042
|
-
const bridgePath = canonicalAgentPath(
|
|
43208
|
+
const bridgePath = canonicalAgentPath(join23(agent === "sumi" ? sumiBridgeRoot(home) : join23(home, adapter.root), CLI_BRIDGE_NAME), aliases);
|
|
43043
43209
|
assertSafePath(bridgePath);
|
|
43044
|
-
if (
|
|
43210
|
+
if (existsSync7(bridgePath) && !isOwnedCliBridge(bridgePath, [bridgePath]))
|
|
43045
43211
|
throw new Error(`Refusing to overwrite an unrecognized or modified Skills bridge: ${bridgePath}`);
|
|
43046
43212
|
for (const [name, after2] of Object.entries(CLI_BRIDGE_FILES)) {
|
|
43047
|
-
const path2 =
|
|
43213
|
+
const path2 = join23(bridgePath, name), before2 = readOptional(path2);
|
|
43048
43214
|
if (before2 !== after2)
|
|
43049
43215
|
changes.push({ path: path2, before: before2, after: after2 });
|
|
43050
43216
|
}
|
|
43051
|
-
const path = canonicalAgentPath(
|
|
43217
|
+
const path = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) : join23(home, adapter.config), aliases);
|
|
43052
43218
|
const before = readOptional(path);
|
|
43053
43219
|
if (agent === "hermes") {
|
|
43054
|
-
const supervisorPath =
|
|
43220
|
+
const supervisorPath = join23(dataDir2, "agent-hooks", "hermes.js"), supervisorBefore = readOptional(supervisorPath), supervisorAfter = renderHermesSupervisor(command, profileId2);
|
|
43055
43221
|
const priorCommand = policy.bridge?.commands?.hermes, priorProfile = policy.bridge?.profiles?.hermes ?? policy.profileId;
|
|
43056
43222
|
const previous = policy.bridge?.supervisors?.hermes;
|
|
43057
43223
|
if (supervisorBefore !== null && supervisorBefore !== supervisorAfter && !(previous?.path === supervisorPath && typeof priorCommand === "string" && typeof priorProfile === "string" && supervisorBefore === renderHermesSupervisor(priorCommand, priorProfile)))
|
|
@@ -43062,19 +43228,30 @@ function planAgentIntegration(options) {
|
|
|
43062
43228
|
const after2 = configureHermesHooks(before, supervisor, previous);
|
|
43063
43229
|
if (before !== after2)
|
|
43064
43230
|
changes.push({ path, before, after: after2 });
|
|
43065
|
-
const optOut =
|
|
43231
|
+
const optOut = join23(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
|
|
43066
43232
|
if (optOutBefore === null)
|
|
43067
43233
|
changes.push({ path: optOut, before: null, after: `Managed by @hasna/skills: bundled native skill reseeding is disabled.
|
|
43068
43234
|
` });
|
|
43069
43235
|
continue;
|
|
43070
43236
|
}
|
|
43071
43237
|
const config = jsonObject(before, path);
|
|
43072
|
-
if (agent === "
|
|
43238
|
+
if (agent === "sumi") {
|
|
43239
|
+
if (config.permissions !== undefined && (!Array.isArray(config.permissions) || config.permissions.some((rule) => !rule || typeof rule.action !== "string" || typeof rule.resource !== "string" || !["allow", "deny", "ask"].includes(rule.effect))))
|
|
43240
|
+
throw new Error("Expected Sumi native permission rules");
|
|
43241
|
+
config.permissions = [...(config.permissions ?? []).filter((rule) => rule.action !== "skill"), ...SUMI_SKILL_PERMISSIONS];
|
|
43242
|
+
const pluginPath = join23(sumiConfigDirectory(home), "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
|
|
43243
|
+
const pluginAfter = renderSumiPlugin(command, profileId2);
|
|
43244
|
+
const priorCommand = policy.bridge?.commands?.sumi, priorProfile = policy.bridge?.profiles?.sumi ?? policy.profileId;
|
|
43245
|
+
if (pluginBefore !== null && pluginBefore !== pluginAfter && !(typeof priorCommand === "string" && typeof priorProfile === "string" && pluginBefore === renderSumiPlugin(priorCommand, priorProfile)))
|
|
43246
|
+
throw new Error("Refusing to overwrite a modified Sumi Skills plugin; preserve and review it first");
|
|
43247
|
+
if (pluginBefore !== pluginAfter)
|
|
43248
|
+
changes.push({ path: pluginPath, before: pluginBefore, after: pluginAfter });
|
|
43249
|
+
} else if (agent === "opencode") {
|
|
43073
43250
|
const permission = config.permission ?? {};
|
|
43074
43251
|
if (!permission || typeof permission !== "object" || Array.isArray(permission))
|
|
43075
43252
|
throw new Error("Expected OpenCode permission object");
|
|
43076
43253
|
config.permission = { ...permission, skill: { "*": "deny", [CLI_BRIDGE_NAME]: "allow" } };
|
|
43077
|
-
const pluginPath =
|
|
43254
|
+
const pluginPath = join23(home, ".config", "opencode", "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
|
|
43078
43255
|
const pluginAfter = renderOpenCodePlugin(command, profileId2);
|
|
43079
43256
|
const priorCommand = policy.bridge?.commands?.opencode;
|
|
43080
43257
|
const ownedPrevious = typeof priorCommand === "string" && typeof policy.profileId === "string" && pluginBefore === renderOpenCodePlugin(priorCommand, policy.bridge?.profiles?.opencode ?? policy.profileId);
|
|
@@ -43091,13 +43268,13 @@ function planAgentIntegration(options) {
|
|
|
43091
43268
|
if (before !== after)
|
|
43092
43269
|
changes.push({ path, before, after });
|
|
43093
43270
|
if (agent === "codex") {
|
|
43094
|
-
const configPath = canonicalAgentPath(
|
|
43271
|
+
const configPath = canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
|
|
43095
43272
|
const next = disableReviewedCodexPluginNames(disableCodexSkills(previous ?? "", nativeSkills, aliases, bridgePath), codexPluginSkills);
|
|
43096
43273
|
if (next !== (previous ?? ""))
|
|
43097
43274
|
changes.push({ path: configPath, before: previous, after: next });
|
|
43098
43275
|
}
|
|
43099
43276
|
}
|
|
43100
|
-
const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha4(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(
|
|
43277
|
+
const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha4(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join23(home, ".codex/config.toml"), aliases))?.before === null ? null : sha4(readOptional(canonicalAgentPath(join23(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview;
|
|
43101
43278
|
const discoveryAfter = discoveries.map((binding) => {
|
|
43102
43279
|
const rebound = rebindAgentDiscovery(binding, new Map(changes.map((change) => [change.path, change.after])));
|
|
43103
43280
|
if (binding.agent !== "codex")
|
|
@@ -43112,12 +43289,12 @@ function planAgentIntegration(options) {
|
|
|
43112
43289
|
version: CLI_BRIDGE_VERSION,
|
|
43113
43290
|
digest: CLI_BRIDGE_DIGEST,
|
|
43114
43291
|
agents: [...new Set([...priorAgents, ...options.agents])].sort(),
|
|
43115
|
-
home:
|
|
43292
|
+
home: resolve16(home),
|
|
43116
43293
|
includeVendor: true,
|
|
43117
|
-
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path:
|
|
43294
|
+
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join23(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha4(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
|
|
43118
43295
|
commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
|
|
43119
43296
|
profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
|
|
43120
|
-
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(
|
|
43297
|
+
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep5)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
|
|
43121
43298
|
rootAliases: aliases,
|
|
43122
43299
|
...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
|
|
43123
43300
|
discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
|
|
@@ -43140,7 +43317,7 @@ function claudeSettingsConsumers(home, path, discoveries, aliases) {
|
|
|
43140
43317
|
const distance = relative4(canonical6(root2), path);
|
|
43141
43318
|
return distance === "" || distance !== ".." && !distance.startsWith(`..${sep5}`) && !isAbsolute17(distance);
|
|
43142
43319
|
};
|
|
43143
|
-
return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical6(
|
|
43320
|
+
return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical6(join23(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical6(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
|
|
43144
43321
|
}
|
|
43145
43322
|
function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
43146
43323
|
try {
|
|
@@ -43157,8 +43334,8 @@ function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
|
43157
43334
|
}
|
|
43158
43335
|
}
|
|
43159
43336
|
function planClaudeHookEventsUpdate(options) {
|
|
43160
|
-
const home =
|
|
43161
|
-
const policyPath =
|
|
43337
|
+
const home = resolve16(options.home ?? homedir3()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
43338
|
+
const policyPath = join23(dataDir2, "agent-policy.json");
|
|
43162
43339
|
assertSafePath(policyPath);
|
|
43163
43340
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
43164
43341
|
if (!snapshot2)
|
|
@@ -43169,9 +43346,9 @@ function planClaudeHookEventsUpdate(options) {
|
|
|
43169
43346
|
}
|
|
43170
43347
|
const aliases = bridge.rootAliases ?? [];
|
|
43171
43348
|
recheckRootAliases(aliases);
|
|
43172
|
-
const path = canonicalAgentPath(
|
|
43349
|
+
const path = canonicalAgentPath(join23(home, ".claude", "settings.json"), aliases);
|
|
43173
43350
|
assertSafePath(path);
|
|
43174
|
-
const before =
|
|
43351
|
+
const before = lstatSync20(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
|
|
43175
43352
|
if ((before === null ? "absent" : sha4(before)) !== options.expectedSettingsSha256)
|
|
43176
43353
|
throw new Error("Claude settings preimage changed");
|
|
43177
43354
|
assertClaudeHookEventsReplacement(before, options.replacement, options.events);
|
|
@@ -43231,14 +43408,14 @@ function atomicWrite2(path, content) {
|
|
|
43231
43408
|
writeFileSync4(temporary, content, { mode: 384, flag: "wx" });
|
|
43232
43409
|
renameSync3(temporary, path);
|
|
43233
43410
|
} finally {
|
|
43234
|
-
if (
|
|
43411
|
+
if (existsSync7(temporary))
|
|
43235
43412
|
unlinkSync4(temporary);
|
|
43236
43413
|
}
|
|
43237
43414
|
}
|
|
43238
43415
|
function planAgentSettingsWitnessUpgrade(options) {
|
|
43239
43416
|
if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3].includes(options.targetCodexVersion)))
|
|
43240
43417
|
throw new Error("Invalid Codex witness target version");
|
|
43241
|
-
const home =
|
|
43418
|
+
const home = resolve16(options.home ?? homedir3()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
43242
43419
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
43243
43420
|
if (!snapshot2 || sha4(snapshot2.text) !== options.expectedPolicySha256)
|
|
43244
43421
|
throw new Error("Managed policy preimage changed");
|
|
@@ -43247,7 +43424,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
43247
43424
|
const binding = policy.bridge?.discovery?.[options.agent];
|
|
43248
43425
|
if (!binding || binding.agent !== options.agent || binding.method !== "reviewed")
|
|
43249
43426
|
throw new Error("Settings witness upgrade requires an existing reviewed native binding");
|
|
43250
|
-
const configPath = canonicalAgentPath(
|
|
43427
|
+
const configPath = canonicalAgentPath(join23(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
|
|
43251
43428
|
const settings = readOptional(configPath);
|
|
43252
43429
|
if (settings === null || sha4(settings) !== options.expectedSettingsSha256)
|
|
43253
43430
|
throw new Error("Native settings preimage changed");
|
|
@@ -43286,7 +43463,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
43286
43463
|
const replacement = { ...binding, sources: rebound };
|
|
43287
43464
|
verifyAgentDiscovery(replacement);
|
|
43288
43465
|
const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
|
|
43289
|
-
return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path:
|
|
43466
|
+
return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join23(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join23(dataDir2, "agent-policy.json"), before: snapshot2.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
|
|
43290
43467
|
}
|
|
43291
43468
|
function applyAgentIntegration(plan) {
|
|
43292
43469
|
for (const source of plan.observedNativeSources ?? [])
|
|
@@ -43294,12 +43471,12 @@ function applyAgentIntegration(plan) {
|
|
|
43294
43471
|
throw new Error("Native identity source changed after planning");
|
|
43295
43472
|
if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
|
|
43296
43473
|
throw new Error("Native settings changed after witness planning");
|
|
43297
|
-
const policyPath =
|
|
43474
|
+
const policyPath = join23(plan.dataDir, "agent-policy.json");
|
|
43298
43475
|
assertSafePath(policyPath);
|
|
43299
|
-
const currentText = (path) =>
|
|
43476
|
+
const currentText = (path) => resolve16(path) === resolve16(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
|
|
43300
43477
|
readManagedSkillPolicySnapshot(plan.dataDir);
|
|
43301
43478
|
for (const change of plan.changes)
|
|
43302
|
-
if (
|
|
43479
|
+
if (resolve16(change.path) === resolve16(policyPath))
|
|
43303
43480
|
parseManagedSkillPolicy(change.after);
|
|
43304
43481
|
const aliases = plan.rootAliases ?? [];
|
|
43305
43482
|
recheckRootAliases(aliases);
|
|
@@ -43328,7 +43505,7 @@ function applyAgentIntegration(plan) {
|
|
|
43328
43505
|
for (const change of plan.changes)
|
|
43329
43506
|
if (currentText(change.path) !== change.before)
|
|
43330
43507
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
43331
|
-
const backupRoot =
|
|
43508
|
+
const backupRoot = join23(plan.dataDir, "migration", randomUUID3()), backups = [], written = [], createdDirectories = new Set;
|
|
43332
43509
|
if (!plan.changes.length) {
|
|
43333
43510
|
recheckRootAliases(aliases);
|
|
43334
43511
|
return { changed: [], backups, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
@@ -43338,7 +43515,7 @@ function applyAgentIntegration(plan) {
|
|
|
43338
43515
|
chmodSync(backupRoot, 448);
|
|
43339
43516
|
for (const [index, change] of plan.changes.entries())
|
|
43340
43517
|
if (change.before !== null) {
|
|
43341
|
-
const backup =
|
|
43518
|
+
const backup = join23(backupRoot, `${index}-${sha4(change.path).slice(0, 12)}.backup`);
|
|
43342
43519
|
writeFileSync4(backup, change.before, { mode: 384, flag: "wx" });
|
|
43343
43520
|
if (readFileSync14(backup, "utf8") !== change.before)
|
|
43344
43521
|
throw new Error("Native configuration preservation readback failed");
|
|
@@ -43349,7 +43526,7 @@ function applyAgentIntegration(plan) {
|
|
|
43349
43526
|
recheckRootAliases(aliases);
|
|
43350
43527
|
if (currentText(change.path) !== change.before)
|
|
43351
43528
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
43352
|
-
for (let directory = dirname15(change.path);!
|
|
43529
|
+
for (let directory = dirname15(change.path);!existsSync7(directory); directory = dirname15(directory))
|
|
43353
43530
|
createdDirectories.add(directory);
|
|
43354
43531
|
const after = change.after;
|
|
43355
43532
|
atomicWrite2(change.path, after);
|
|
@@ -43369,7 +43546,7 @@ function applyAgentIntegration(plan) {
|
|
|
43369
43546
|
}
|
|
43370
43547
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
43371
43548
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
43372
|
-
atomicWrite2(
|
|
43549
|
+
atomicWrite2(join23(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha4(change.before), afterHash: sha4(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
|
|
43373
43550
|
`);
|
|
43374
43551
|
} catch (error) {
|
|
43375
43552
|
for (const change of written.reverse()) {
|
|
@@ -43423,7 +43600,7 @@ function writeArchiveJournal(path, value) {
|
|
|
43423
43600
|
} finally {
|
|
43424
43601
|
if (descriptor !== undefined)
|
|
43425
43602
|
closeSync16(descriptor);
|
|
43426
|
-
if (
|
|
43603
|
+
if (lstatSync20(temporary, { throwIfNoEntry: false }))
|
|
43427
43604
|
unlinkSync4(temporary);
|
|
43428
43605
|
}
|
|
43429
43606
|
}
|
|
@@ -43434,7 +43611,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
43434
43611
|
recheckRootAliases(aliases);
|
|
43435
43612
|
for (const entry of inventory.filter((entry2) => entry2.bridge)) {
|
|
43436
43613
|
const adapter = AGENT_ADAPTERS[entry.agent];
|
|
43437
|
-
const expected = adapter && entry.bridgeHome ? canonicalAgentPath(
|
|
43614
|
+
const expected = adapter && entry.bridgeHome ? canonicalAgentPath(join23(entry.agent === "sumi" ? sumiBridgeRoot(entry.bridgeHome) : join23(entry.bridgeHome, adapter.root), CLI_BRIDGE_NAME), aliases) : undefined;
|
|
43438
43615
|
if (!expected || treeHash(entry.path) !== entry.hash || !isOwnedCliBridge(entry.path, [expected]))
|
|
43439
43616
|
throw new Error(`Skills bridge changed after planning: ${entry.path}`);
|
|
43440
43617
|
}
|
|
@@ -43445,11 +43622,11 @@ function archiveNativeSkills(inventory, options) {
|
|
|
43445
43622
|
const targetManifest = options.targetManifest ? { schema: options.targetManifest.schema, digest: options.targetManifest.digest, targetCount: options.targetManifest.targets.length } : undefined;
|
|
43446
43623
|
if (!selected.length)
|
|
43447
43624
|
return { entries: [], ...targetManifest ? { targetManifest } : {}, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
43448
|
-
const operationId = randomUUID3(), archiveRoot =
|
|
43625
|
+
const operationId = randomUUID3(), archiveRoot = join23(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join23(archiveRoot, "receipt.json");
|
|
43449
43626
|
const entries = selected.map((entry, index) => ({
|
|
43450
|
-
source: entry.vendor ?
|
|
43451
|
-
archive:
|
|
43452
|
-
hash: entry.vendor ? sha4(readNativeBytes(
|
|
43627
|
+
source: entry.vendor ? join23(entry.path, "SKILL.md") : entry.path,
|
|
43628
|
+
archive: join23(archiveRoot, `${index}-${sha4(entry.path).slice(0, 12)}`),
|
|
43629
|
+
hash: entry.vendor ? sha4(readNativeBytes(join23(entry.path, "SKILL.md"))) : entry.hash,
|
|
43453
43630
|
...entry.vendor ? { discoveryOnly: true } : {},
|
|
43454
43631
|
status: "planned"
|
|
43455
43632
|
}));
|
|
@@ -43457,7 +43634,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
43457
43634
|
const moved = [];
|
|
43458
43635
|
const verifyArchive = (entry) => {
|
|
43459
43636
|
assertSafePath(entry.archive);
|
|
43460
|
-
const stat2 =
|
|
43637
|
+
const stat2 = lstatSync20(entry.archive);
|
|
43461
43638
|
if (entry.discoveryOnly ? !stat2.isFile() : !stat2.isDirectory())
|
|
43462
43639
|
throw new Error("Archive type changed");
|
|
43463
43640
|
if ((entry.discoveryOnly ? sha4(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
|
|
@@ -43465,7 +43642,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
43465
43642
|
};
|
|
43466
43643
|
assertSafePath(archiveRoot);
|
|
43467
43644
|
const created = [];
|
|
43468
|
-
for (let path = archiveRoot;!
|
|
43645
|
+
for (let path = archiveRoot;!existsSync7(path); path = dirname15(path))
|
|
43469
43646
|
created.push(path);
|
|
43470
43647
|
mkdirSync5(archiveRoot, { recursive: true, mode: 448 });
|
|
43471
43648
|
try {
|
|
@@ -43505,7 +43682,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
43505
43682
|
recheckRootAliases(aliases);
|
|
43506
43683
|
assertSafePath(entry.source);
|
|
43507
43684
|
assertSafePath(entry.archive);
|
|
43508
|
-
if (
|
|
43685
|
+
if (lstatSync20(entry.source, { throwIfNoEntry: false })) {
|
|
43509
43686
|
entry.status = "recovery-required";
|
|
43510
43687
|
entry.conflict = "source-occupied";
|
|
43511
43688
|
continue;
|
|
@@ -43522,7 +43699,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
43522
43699
|
syncArchiveDirectory(dirname15(entry.source));
|
|
43523
43700
|
unlinkSync4(entry.archive);
|
|
43524
43701
|
} else {
|
|
43525
|
-
if (
|
|
43702
|
+
if (lstatSync20(entry.source, { throwIfNoEntry: false })) {
|
|
43526
43703
|
entry.status = "recovery-required";
|
|
43527
43704
|
entry.conflict = "source-occupied";
|
|
43528
43705
|
continue;
|
|
@@ -43553,8 +43730,8 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
43553
43730
|
assertManagedAgentBridgeWithDiscovery(agent, options);
|
|
43554
43731
|
}
|
|
43555
43732
|
function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
|
|
43556
|
-
const home =
|
|
43557
|
-
assertSafePath(
|
|
43733
|
+
const home = resolve16(options.home ?? homedir3()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
43734
|
+
assertSafePath(join23(dataDir2, "agent-policy.json"));
|
|
43558
43735
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
43559
43736
|
if (!snapshot2)
|
|
43560
43737
|
throw new Error("NATIVE_SKILL_DRIFT: install the Skills bridge with skills hook install");
|
|
@@ -43565,14 +43742,14 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
43565
43742
|
if (!Array.isArray(aliases))
|
|
43566
43743
|
throw new Error("NATIVE_SKILL_DRIFT: invalid root alias binding");
|
|
43567
43744
|
recheckRootAliases(aliases);
|
|
43568
|
-
const expected = canonicalAgentPath(
|
|
43745
|
+
const expected = canonicalAgentPath(join23(agent === "sumi" ? sumiBridgeRoot(home) : join23(home, AGENT_ADAPTERS[agent].root), CLI_BRIDGE_NAME), aliases);
|
|
43569
43746
|
if (!isOwnedCliBridge(expected, [expected]))
|
|
43570
43747
|
throw new Error("NATIVE_SKILL_DRIFT: the native Skills bridge is missing or modified; repair it before continuing");
|
|
43571
43748
|
const roots = projectAncestorDirectories([options.projectDir ?? process.cwd(), ...options.projectDirs ?? [], ...agent === "hermes" && process.env.TERMINAL_CWD ? [process.cwd()] : []]);
|
|
43572
|
-
const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes"].includes(agent) && entry.path.includes(`${sep5}.agents${sep5}skills${sep5}`) ||
|
|
43749
|
+
const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes", "sumi"].includes(agent) && entry.path.includes(`${sep5}.agents${sep5}skills${sep5}`) || ["opencode", "sumi"].includes(agent) && entry.agent === "claude";
|
|
43573
43750
|
const discovery = provenDiscovery ?? binding.discovery?.[agent];
|
|
43574
43751
|
assertProjectDiscovery(agent, [...roots], home, (path) => canonicalAgentPath(path, aliases), discovery);
|
|
43575
|
-
const configPath = canonicalAgentPath(
|
|
43752
|
+
const configPath = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) : join23(home, AGENT_ADAPTERS[agent].config), aliases), config = agent === "hermes" ? parseHermesConfig(readOptional(configPath)) : jsonObject(readOptional(configPath), configPath);
|
|
43576
43753
|
const command = binding.commands?.[agent], profile = binding.profiles?.[agent];
|
|
43577
43754
|
if (typeof command !== "string" || typeof profile !== "string")
|
|
43578
43755
|
throw new Error("NATIVE_SKILL_DRIFT: the native hook command/profile binding is missing");
|
|
@@ -43580,11 +43757,14 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
43580
43757
|
throw new Error("NATIVE_SKILL_DRIFT: the hook selection profile differs from its managed binding; restart the native client after reviewing skills hook install");
|
|
43581
43758
|
if (agent === "hermes") {
|
|
43582
43759
|
const supervisor = binding.supervisors?.hermes;
|
|
43583
|
-
if (supervisor?.path !==
|
|
43760
|
+
if (supervisor?.path !== join23(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha4(renderHermesSupervisor(command, profile)))
|
|
43584
43761
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
|
|
43585
43762
|
assertHermesProtection(home, config, command, profile, supervisor);
|
|
43763
|
+
} else if (agent === "sumi") {
|
|
43764
|
+
if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || readOptional(join23(sumiConfigDirectory(home), "plugins", "skills-cli.js")) !== renderSumiPlugin(command, profile))
|
|
43765
|
+
throw new Error("NATIVE_SKILL_DRIFT: Sumi bridge protection changed; run skills hook install");
|
|
43586
43766
|
} else if (agent === "opencode") {
|
|
43587
|
-
if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(
|
|
43767
|
+
if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(join23(home, ".config", "opencode", "plugins", "skills-cli.js")) !== renderOpenCodePlugin(command, profile))
|
|
43588
43768
|
throw new Error("NATIVE_SKILL_DRIFT: OpenCode bridge protection changed; run skills hook install");
|
|
43589
43769
|
} else {
|
|
43590
43770
|
const required = {};
|
|
@@ -43601,21 +43781,21 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
43601
43781
|
if (agent === "gemini" && (config.hooksConfig?.enabled === false || config.skills?.enabled !== true || !["antigravity-support", "skill-creator"].every((name) => config.skills?.disabled?.includes(name)) || config.skills?.disabled?.includes(CLI_BRIDGE_NAME)))
|
|
43602
43782
|
throw new Error("NATIVE_SKILL_DRIFT: Gemini bridge or bundled-skill protection changed; run skills hook install");
|
|
43603
43783
|
}
|
|
43604
|
-
const codexPath = canonicalAgentPath(
|
|
43784
|
+
const codexPath = canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases);
|
|
43605
43785
|
if (options.codexDiscoveryRecovery && (agent !== "codex" || options.codexDiscoveryRecovery.configPath !== codexPath))
|
|
43606
43786
|
throw new Error("NATIVE_SKILL_DRIFT: Codex trust recovery names a different configuration root");
|
|
43607
43787
|
const codexConfig = agent === "codex" ? Bun.TOML.parse(readOptional(codexPath) ?? "") : {};
|
|
43608
43788
|
if (agent === "codex" && codexConfig.skills?.bundled?.enabled !== false)
|
|
43609
43789
|
throw new Error("NATIVE_SKILL_DRIFT: Codex bundled skill reseeding is not disabled (skills.bundled.enabled); run skills hook install");
|
|
43610
|
-
const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path,
|
|
43790
|
+
const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join23(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
|
|
43611
43791
|
if (agent === "codex" && [...setting(expected), ...(codexConfig.skills?.config ?? []).filter((entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME)].some((entry) => entry.enabled === false))
|
|
43612
43792
|
throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
|
|
43613
|
-
if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(
|
|
43793
|
+
if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024))))
|
|
43614
43794
|
throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
|
|
43615
43795
|
const disabledPlugin = (entry) => {
|
|
43616
43796
|
if (agent !== "codex" || !entry.vendor)
|
|
43617
43797
|
return false;
|
|
43618
|
-
const document2 =
|
|
43798
|
+
const document2 = join23(entry.path, "SKILL.md"), cache = canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), read3 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024));
|
|
43619
43799
|
return isReviewedCodexPluginInactive(document2, cache, binding.codexInactivePlugins ?? [], codexConfig.plugins, codexConfig.skills?.config ?? [], read3) || isReviewedCodexPluginSkillDisabled(document2, cache, binding.codexPluginSkills ?? [], codexConfig.skills?.config, read3);
|
|
43620
43800
|
};
|
|
43621
43801
|
const disabledPaths = new Set(disabledCodexSkillPaths(codexConfig, aliases));
|
|
@@ -43628,15 +43808,15 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
43628
43808
|
return true;
|
|
43629
43809
|
if (!disabledPaths.has(entry.path))
|
|
43630
43810
|
return false;
|
|
43631
|
-
if (entry.path.startsWith(canonicalAgentPath(
|
|
43811
|
+
if (entry.path.startsWith(canonicalAgentPath(join23(home, ".codex", "plugins", "cache"), aliases) + sep5))
|
|
43632
43812
|
return true;
|
|
43633
|
-
const runtimeCache = canonicalAgentPath(
|
|
43813
|
+
const runtimeCache = canonicalAgentPath(join23(home, ".cache", "codex-runtimes"), aliases);
|
|
43634
43814
|
if (entry.path.startsWith(runtimeCache + sep5) && discovery?.method === "automatic" && discovery.roots.some((root2) => entry.path.startsWith(root2 + sep5)))
|
|
43635
43815
|
return true;
|
|
43636
|
-
const bundledMarketplaces = canonicalAgentPath(
|
|
43816
|
+
const bundledMarketplaces = canonicalAgentPath(join23(home, ".codex", ".tmp", "bundled-marketplaces"), aliases);
|
|
43637
43817
|
if (entry.path.startsWith(bundledMarketplaces + sep5) && discovery?.method === "automatic" && discovery.roots.some((root2) => entry.path.startsWith(root2 + sep5)))
|
|
43638
43818
|
return true;
|
|
43639
|
-
if (!entry.path.startsWith(canonicalAgentPath(
|
|
43819
|
+
if (!entry.path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep5))
|
|
43640
43820
|
return false;
|
|
43641
43821
|
return Array.isArray(binding.disabledBuiltins) && binding.disabledBuiltins.some((item) => item.path === entry.path && item.hash === entry.hash);
|
|
43642
43822
|
};
|
|
@@ -43646,6 +43826,8 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
43646
43826
|
throw new Error("NATIVE_SKILL_DRIFT: an installed Gemini builtin is not disabled");
|
|
43647
43827
|
try {
|
|
43648
43828
|
verifyAgentDiscovery(discovery, options.codexDiscoveryRecovery);
|
|
43829
|
+
if (agent === "sumi" && discovery.method === "reviewed")
|
|
43830
|
+
resolveAgentDiscovery({ home, agent, retainedReview: discovery, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
43649
43831
|
if (discovery.method === "automatic") {
|
|
43650
43832
|
const current = resolveAgentDiscovery({ home, agent, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
43651
43833
|
if (JSON.stringify(current) !== JSON.stringify(discovery))
|
|
@@ -43657,7 +43839,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
43657
43839
|
let installationInputRoots = [];
|
|
43658
43840
|
if (agent === "codex") {
|
|
43659
43841
|
try {
|
|
43660
|
-
installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(
|
|
43842
|
+
installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), discovery.codexInstallationInputs?.plugins ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
|
|
43661
43843
|
} catch {
|
|
43662
43844
|
throw new Error("NATIVE_SKILL_DRIFT: native installation input identity changed; review discovery and catalog");
|
|
43663
43845
|
}
|
|
@@ -43681,25 +43863,25 @@ function assertNativeExportAllowed(dataDir2 = getDataDirReadOnly()) {
|
|
|
43681
43863
|
|
|
43682
43864
|
// src/lib/portable-skills.ts
|
|
43683
43865
|
import {
|
|
43684
|
-
existsSync as
|
|
43866
|
+
existsSync as existsSync10,
|
|
43685
43867
|
mkdirSync as mkdirSync7,
|
|
43686
43868
|
mkdtempSync,
|
|
43687
43869
|
readdirSync as readdirSync10,
|
|
43688
|
-
realpathSync as
|
|
43870
|
+
realpathSync as realpathSync11,
|
|
43689
43871
|
renameSync as renameSync4,
|
|
43690
43872
|
rmSync as rmSync2,
|
|
43691
43873
|
statSync as statSync9,
|
|
43692
43874
|
writeFileSync as writeFileSync6
|
|
43693
43875
|
} from "fs";
|
|
43694
|
-
import { basename as basename10, dirname as dirname18, isAbsolute as isAbsolute18, join as
|
|
43876
|
+
import { basename as basename10, dirname as dirname18, isAbsolute as isAbsolute18, join as join27, normalize as normalize4, relative as relative6, resolve as resolve18, sep as sep8 } from "path";
|
|
43695
43877
|
|
|
43696
43878
|
// src/lib/registry-data/index.ts
|
|
43697
43879
|
var SKILLS = [];
|
|
43698
43880
|
|
|
43699
43881
|
// src/lib/skill-hash.ts
|
|
43700
43882
|
import { createHash as createHash19 } from "crypto";
|
|
43701
|
-
import { existsSync as
|
|
43702
|
-
import { join as
|
|
43883
|
+
import { existsSync as existsSync8, readdirSync as readdirSync9, readFileSync as readFileSync15, statSync as statSync7 } from "fs";
|
|
43884
|
+
import { join as join24, sep as sep6 } from "path";
|
|
43703
43885
|
var CONTENT_HASH_ALGORITHM = "sha256";
|
|
43704
43886
|
var HASH_EXCLUDE_DIRS = new Set([".git", "node_modules", "dist", "build", ".turbo"]);
|
|
43705
43887
|
function excludedHashEntry(name, directory) {
|
|
@@ -43761,8 +43943,8 @@ function collectBundleFiles(skillPath) {
|
|
|
43761
43943
|
if (seen.has(entry))
|
|
43762
43944
|
continue;
|
|
43763
43945
|
seen.add(entry);
|
|
43764
|
-
const absolute =
|
|
43765
|
-
if (!
|
|
43946
|
+
const absolute = join24(skillPath, entry);
|
|
43947
|
+
if (!existsSync8(absolute))
|
|
43766
43948
|
continue;
|
|
43767
43949
|
if (statSync7(absolute).isDirectory())
|
|
43768
43950
|
collectDirectory(files, absolute, entry);
|
|
@@ -43775,7 +43957,7 @@ function collectDirectory(files, dir, rel) {
|
|
|
43775
43957
|
for (const entry of readdirSync9(dir).sort()) {
|
|
43776
43958
|
if (entry.startsWith("."))
|
|
43777
43959
|
continue;
|
|
43778
|
-
const absolute =
|
|
43960
|
+
const absolute = join24(dir, entry);
|
|
43779
43961
|
const childRel = `${rel}/${entry}`;
|
|
43780
43962
|
let stats;
|
|
43781
43963
|
try {
|
|
@@ -43836,7 +44018,7 @@ async function hashBundleFilesCooperatively(files, check) {
|
|
|
43836
44018
|
hash2.update(chunk);
|
|
43837
44019
|
bytesSinceYield += chunk.byteLength;
|
|
43838
44020
|
if (bytesSinceYield >= 256 * 1024) {
|
|
43839
|
-
await new Promise((
|
|
44021
|
+
await new Promise((resolve17) => setImmediate(resolve17));
|
|
43840
44022
|
bytesSinceYield = 0;
|
|
43841
44023
|
}
|
|
43842
44024
|
}
|
|
@@ -43989,7 +44171,7 @@ async function hashContentEntries(entries, options) {
|
|
|
43989
44171
|
const normalized = [];
|
|
43990
44172
|
let normalizedBytes = 0;
|
|
43991
44173
|
let manifest;
|
|
43992
|
-
await new Promise((
|
|
44174
|
+
await new Promise((resolve17) => setImmediate(resolve17));
|
|
43993
44175
|
for (const entry of snapshot2) {
|
|
43994
44176
|
check();
|
|
43995
44177
|
if (!coveredContentPath(entry.path))
|
|
@@ -44002,7 +44184,7 @@ async function hashContentEntries(entries, options) {
|
|
|
44002
44184
|
contentLimit("Content hash normalized byte limit exceeded");
|
|
44003
44185
|
normalizedBytes += file.content.byteLength;
|
|
44004
44186
|
normalized.push(file);
|
|
44005
|
-
await new Promise((
|
|
44187
|
+
await new Promise((resolve17) => setImmediate(resolve17));
|
|
44006
44188
|
}
|
|
44007
44189
|
normalized.sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0);
|
|
44008
44190
|
check();
|
|
@@ -44209,14 +44391,14 @@ function validateRuntimeContract(manifest, issues, strict) {
|
|
|
44209
44391
|
// src/lib/portable-skills-files.ts
|
|
44210
44392
|
import {
|
|
44211
44393
|
cpSync,
|
|
44212
|
-
existsSync as
|
|
44213
|
-
lstatSync as
|
|
44394
|
+
existsSync as existsSync9,
|
|
44395
|
+
lstatSync as lstatSync21,
|
|
44214
44396
|
mkdirSync as mkdirSync6,
|
|
44215
44397
|
readFileSync as readFileSync16,
|
|
44216
|
-
realpathSync as
|
|
44398
|
+
realpathSync as realpathSync9,
|
|
44217
44399
|
writeFileSync as writeFileSync5
|
|
44218
44400
|
} from "fs";
|
|
44219
|
-
import { basename as basename8, dirname as dirname16, join as
|
|
44401
|
+
import { basename as basename8, dirname as dirname16, join as join25, relative as relative5 } from "path";
|
|
44220
44402
|
var ANY_SEGMENT_COPY_EXCLUDES = new Set([
|
|
44221
44403
|
".git",
|
|
44222
44404
|
".DS_Store",
|
|
@@ -44273,12 +44455,12 @@ function readPortableSkillManifestForImport(skillPath) {
|
|
|
44273
44455
|
return readManifest(skillPath, basename8(skillPath), normalizeNewPortableSkillName);
|
|
44274
44456
|
}
|
|
44275
44457
|
function readManifest(skillPath, fallbackName, normalizeName) {
|
|
44276
|
-
const skillJsonPath =
|
|
44277
|
-
const skillMdPath =
|
|
44278
|
-
const pkgPath =
|
|
44279
|
-
const jsonManifest =
|
|
44280
|
-
const frontmatter =
|
|
44281
|
-
const pkg =
|
|
44458
|
+
const skillJsonPath = join25(skillPath, "skill.json");
|
|
44459
|
+
const skillMdPath = join25(skillPath, "SKILL.md");
|
|
44460
|
+
const pkgPath = join25(skillPath, "package.json");
|
|
44461
|
+
const jsonManifest = existsSync9(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
|
|
44462
|
+
const frontmatter = existsSync9(skillMdPath) ? parseSkillFrontmatter(readFileSync16(skillMdPath, "utf-8")) ?? undefined : undefined;
|
|
44463
|
+
const pkg = existsSync9(pkgPath) ? readJsonObject(pkgPath) : undefined;
|
|
44282
44464
|
const name = normalizeName(stringField(jsonManifest, "name") ?? frontmatter?.name ?? stringValue(pkg?.name) ?? fallbackName);
|
|
44283
44465
|
const description = stringField(jsonManifest, "description") ?? frontmatter?.description ?? stringValue(pkg?.description) ?? `${name} skill`;
|
|
44284
44466
|
const version = readDeclaredSkillVersion(skillPath) ?? PORTABLE_SKILL_DEFAULT_VERSION;
|
|
@@ -44307,12 +44489,12 @@ function parseSkillKind(value) {
|
|
|
44307
44489
|
return;
|
|
44308
44490
|
}
|
|
44309
44491
|
function readDeclaredSkillVersion(skillPath) {
|
|
44310
|
-
const skillJsonPath =
|
|
44311
|
-
const skillMdPath =
|
|
44312
|
-
const pkgPath =
|
|
44313
|
-
const jsonManifest =
|
|
44314
|
-
const frontmatter =
|
|
44315
|
-
const pkg =
|
|
44492
|
+
const skillJsonPath = join25(skillPath, "skill.json");
|
|
44493
|
+
const skillMdPath = join25(skillPath, "SKILL.md");
|
|
44494
|
+
const pkgPath = join25(skillPath, "package.json");
|
|
44495
|
+
const jsonManifest = existsSync9(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
|
|
44496
|
+
const frontmatter = existsSync9(skillMdPath) ? parseSkillFrontmatter(readFileSync16(skillMdPath, "utf-8")) ?? undefined : undefined;
|
|
44497
|
+
const pkg = existsSync9(pkgPath) ? readJsonObject(pkgPath) : undefined;
|
|
44316
44498
|
return stringField(jsonManifest, "version") ?? frontmatter?.version ?? stringValue(pkg?.version);
|
|
44317
44499
|
}
|
|
44318
44500
|
function createInstructionManifest(name, options) {
|
|
@@ -44333,7 +44515,7 @@ function createInstructionManifest(name, options) {
|
|
|
44333
44515
|
}
|
|
44334
44516
|
function writeInstructionSkillTemplate(skillPath, manifest) {
|
|
44335
44517
|
mkdirSync6(skillPath, { recursive: true });
|
|
44336
|
-
writeFileSync5(
|
|
44518
|
+
writeFileSync5(join25(skillPath, "SKILL.md"), renderInstructionSkillMd(manifest));
|
|
44337
44519
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
44338
44520
|
}
|
|
44339
44521
|
function renderInstructionSkillMd(manifest) {
|
|
@@ -44383,12 +44565,12 @@ function createPortableManifest(name, options) {
|
|
|
44383
44565
|
};
|
|
44384
44566
|
}
|
|
44385
44567
|
function writePortableSkillTemplate(skillPath, manifest) {
|
|
44386
|
-
mkdirSync6(
|
|
44387
|
-
writeFileSync5(
|
|
44388
|
-
writeFileSync5(
|
|
44389
|
-
writeFileSync5(
|
|
44390
|
-
writeFileSync5(
|
|
44391
|
-
writeFileSync5(
|
|
44568
|
+
mkdirSync6(join25(skillPath, "src"), { recursive: true });
|
|
44569
|
+
writeFileSync5(join25(skillPath, "SKILL.md"), renderSkillMd(manifest));
|
|
44570
|
+
writeFileSync5(join25(skillPath, "AGENTS.md"), renderAgentsMd(manifest));
|
|
44571
|
+
writeFileSync5(join25(skillPath, "package.json"), renderPackageJson(manifest));
|
|
44572
|
+
writeFileSync5(join25(skillPath, "tsconfig.json"), renderTsconfig());
|
|
44573
|
+
writeFileSync5(join25(skillPath, "src", "index.ts"), renderEntrypoint(manifest));
|
|
44392
44574
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
44393
44575
|
}
|
|
44394
44576
|
function fillContractDefaults(manifest, entrypoint) {
|
|
@@ -44413,7 +44595,7 @@ function writeSkillJsonWithHash(skillPath, manifest) {
|
|
|
44413
44595
|
content_hash: undefined
|
|
44414
44596
|
}
|
|
44415
44597
|
};
|
|
44416
|
-
writeFileSync5(
|
|
44598
|
+
writeFileSync5(join25(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withoutHash) }, null, 2)}
|
|
44417
44599
|
`);
|
|
44418
44600
|
const hash2 = computeContentHash(skillPath);
|
|
44419
44601
|
const withHash = {
|
|
@@ -44423,13 +44605,13 @@ function writeSkillJsonWithHash(skillPath, manifest) {
|
|
|
44423
44605
|
content_hash: hash2
|
|
44424
44606
|
}
|
|
44425
44607
|
};
|
|
44426
|
-
writeFileSync5(
|
|
44608
|
+
writeFileSync5(join25(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withHash) }, null, 2)}
|
|
44427
44609
|
`);
|
|
44428
44610
|
return withHash;
|
|
44429
44611
|
}
|
|
44430
44612
|
function readExistingSkillJson(skillPath) {
|
|
44431
|
-
const path =
|
|
44432
|
-
if (!
|
|
44613
|
+
const path = join25(skillPath, "skill.json");
|
|
44614
|
+
if (!existsSync9(path))
|
|
44433
44615
|
return {};
|
|
44434
44616
|
try {
|
|
44435
44617
|
const parsed = JSON.parse(readFileSync16(path, "utf-8"));
|
|
@@ -44462,28 +44644,28 @@ function ensurePortableSkillFiles(skillPath, manifest) {
|
|
|
44462
44644
|
tags: next.tags?.length ? next.tags : ["custom", next.name]
|
|
44463
44645
|
};
|
|
44464
44646
|
const entry = next.commands[0]?.entry ?? "src/index.ts";
|
|
44465
|
-
if (entry && !
|
|
44466
|
-
mkdirSync6(dirname16(
|
|
44467
|
-
writeFileSync5(
|
|
44647
|
+
if (entry && !existsSync9(join25(skillPath, entry))) {
|
|
44648
|
+
mkdirSync6(dirname16(join25(skillPath, entry)), { recursive: true });
|
|
44649
|
+
writeFileSync5(join25(skillPath, entry), renderEntrypoint(next));
|
|
44468
44650
|
}
|
|
44469
|
-
if (!
|
|
44470
|
-
writeFileSync5(
|
|
44651
|
+
if (!existsSync9(join25(skillPath, "SKILL.md")))
|
|
44652
|
+
writeFileSync5(join25(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
44471
44653
|
else
|
|
44472
|
-
writeFileSync5(
|
|
44473
|
-
if (!
|
|
44474
|
-
writeFileSync5(
|
|
44654
|
+
writeFileSync5(join25(skillPath, "SKILL.md"), ensureSkillMdFrontmatter(readFileSync16(join25(skillPath, "SKILL.md"), "utf-8"), next));
|
|
44655
|
+
if (!existsSync9(join25(skillPath, "AGENTS.md")))
|
|
44656
|
+
writeFileSync5(join25(skillPath, "AGENTS.md"), renderAgentsMd(next));
|
|
44475
44657
|
ensurePackageJson(skillPath, next);
|
|
44476
|
-
if (!
|
|
44477
|
-
writeFileSync5(
|
|
44658
|
+
if (!existsSync9(join25(skillPath, "tsconfig.json")))
|
|
44659
|
+
writeFileSync5(join25(skillPath, "tsconfig.json"), renderTsconfig());
|
|
44478
44660
|
writeSkillJsonWithHash(skillPath, next);
|
|
44479
44661
|
return readPortableSkillManifest(skillPath, next.name);
|
|
44480
44662
|
}
|
|
44481
44663
|
function ensurePackageJson(skillPath, manifest) {
|
|
44482
|
-
const pkgPath =
|
|
44664
|
+
const pkgPath = join25(skillPath, "package.json");
|
|
44483
44665
|
const first = manifest.commands[0] ?? { name: manifest.name, entry: "src/index.ts" };
|
|
44484
44666
|
const commandName = normalizePortableSkillName(first.name || manifest.name);
|
|
44485
44667
|
const entry = (first.entry ?? "src/index.ts").replace(/^\.\//, "");
|
|
44486
|
-
if (!
|
|
44668
|
+
if (!existsSync9(pkgPath)) {
|
|
44487
44669
|
writeFileSync5(pkgPath, renderPackageJson(manifest));
|
|
44488
44670
|
return;
|
|
44489
44671
|
}
|
|
@@ -44526,18 +44708,18 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
|
|
|
44526
44708
|
inputs: [],
|
|
44527
44709
|
commands: []
|
|
44528
44710
|
};
|
|
44529
|
-
if (!
|
|
44530
|
-
writeFileSync5(
|
|
44711
|
+
if (!existsSync9(join25(skillPath, "SKILL.md"))) {
|
|
44712
|
+
writeFileSync5(join25(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
44531
44713
|
} else {
|
|
44532
|
-
const path =
|
|
44714
|
+
const path = join25(skillPath, "SKILL.md");
|
|
44533
44715
|
const content = readFileSync16(path, "utf8");
|
|
44534
44716
|
const declaredName = parseSkillFrontmatter(content)?.name;
|
|
44535
44717
|
if (declaredName && declaredName !== next.name) {
|
|
44536
44718
|
writeFileSync5(path, renameInstructionFrontmatter(content, next.name));
|
|
44537
44719
|
}
|
|
44538
44720
|
}
|
|
44539
|
-
const packagePath =
|
|
44540
|
-
if (
|
|
44721
|
+
const packagePath = join25(skillPath, "package.json");
|
|
44722
|
+
if (existsSync9(packagePath)) {
|
|
44541
44723
|
const pkg = readJsonObject(packagePath);
|
|
44542
44724
|
if (typeof pkg.name === "string" && pkg.name !== next.name) {
|
|
44543
44725
|
writeFileSync5(packagePath, `${JSON.stringify({ ...pkg, name: next.name }, null, 2)}
|
|
@@ -44568,7 +44750,7 @@ function renameInstructionFrontmatter(content, name) {
|
|
|
44568
44750
|
return renamed + content.slice(frontmatter[0].length);
|
|
44569
44751
|
}
|
|
44570
44752
|
function copySkillDirectory(source, destination) {
|
|
44571
|
-
const resolvedSource =
|
|
44753
|
+
const resolvedSource = lstatSync21(source).isSymbolicLink() ? realpathSync9(source) : source;
|
|
44572
44754
|
mkdirSync6(destination, { recursive: true });
|
|
44573
44755
|
cpSync(resolvedSource, destination, {
|
|
44574
44756
|
recursive: true,
|
|
@@ -44581,7 +44763,7 @@ function copySkillDirectory(source, destination) {
|
|
|
44581
44763
|
if (isExcludedCopyEntry(segments[i], i === 0))
|
|
44582
44764
|
return false;
|
|
44583
44765
|
}
|
|
44584
|
-
if (
|
|
44766
|
+
if (lstatSync21(src).isSymbolicLink())
|
|
44585
44767
|
return false;
|
|
44586
44768
|
return true;
|
|
44587
44769
|
}
|
|
@@ -44860,12 +45042,12 @@ function escapeJsString(value) {
|
|
|
44860
45042
|
}
|
|
44861
45043
|
|
|
44862
45044
|
// src/lib/authoring-path.ts
|
|
44863
|
-
import { lstatSync as
|
|
45045
|
+
import { lstatSync as lstatSync22, realpathSync as realpathSync10 } from "fs";
|
|
44864
45046
|
import { homedir as homedir4 } from "os";
|
|
44865
|
-
import { basename as basename9, dirname as dirname17, join as
|
|
45047
|
+
import { basename as basename9, dirname as dirname17, join as join26, resolve as resolve17, sep as sep7 } from "path";
|
|
44866
45048
|
var NATIVE_DISCOVERY_ROOTS = NATIVE_SKILL_ROOTS.map(([, path]) => path.split("/"));
|
|
44867
45049
|
function pathParts(path) {
|
|
44868
|
-
return
|
|
45050
|
+
return resolve17(path).split(sep7).filter(Boolean);
|
|
44869
45051
|
}
|
|
44870
45052
|
function nativeRootMatch(path) {
|
|
44871
45053
|
const parts = pathParts(path);
|
|
@@ -44879,24 +45061,24 @@ function nativeRootMatch(path) {
|
|
|
44879
45061
|
return;
|
|
44880
45062
|
}
|
|
44881
45063
|
function physicalPath(path) {
|
|
44882
|
-
let cursor =
|
|
45064
|
+
let cursor = resolve17(path);
|
|
44883
45065
|
const suffix = [];
|
|
44884
|
-
while (!
|
|
45066
|
+
while (!lstatSync22(cursor, { throwIfNoEntry: false })) {
|
|
44885
45067
|
suffix.unshift(basename9(cursor));
|
|
44886
45068
|
const parent = dirname17(cursor);
|
|
44887
45069
|
if (parent === cursor)
|
|
44888
|
-
return
|
|
45070
|
+
return resolve17(path);
|
|
44889
45071
|
cursor = parent;
|
|
44890
45072
|
}
|
|
44891
|
-
return
|
|
45073
|
+
return resolve17(realpathSync10(cursor), ...suffix);
|
|
44892
45074
|
}
|
|
44893
45075
|
function contains(root2, path) {
|
|
44894
45076
|
return path === root2 || path.startsWith(root2 + sep7);
|
|
44895
45077
|
}
|
|
44896
45078
|
function configuredRoots(home, explicitHome) {
|
|
44897
|
-
const homeData =
|
|
45079
|
+
const homeData = join26(home, ".hasna", "skills");
|
|
44898
45080
|
const homePolicy = readManagedSkillPolicySnapshot(homeData);
|
|
44899
|
-
const configuredPolicy = !explicitHome &&
|
|
45081
|
+
const configuredPolicy = !explicitHome && resolve17(getDataDirReadOnly()) !== resolve17(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
|
|
44900
45082
|
const policy = homePolicy ?? configuredPolicy;
|
|
44901
45083
|
const bridge = policy?.value.bridge;
|
|
44902
45084
|
if (bridge && (typeof bridge.home !== "string" || physicalPath(bridge.home) !== physicalPath(home))) {
|
|
@@ -44925,8 +45107,8 @@ function configuredRoots(home, explicitHome) {
|
|
|
44925
45107
|
admitBinding(binding);
|
|
44926
45108
|
continue;
|
|
44927
45109
|
}
|
|
44928
|
-
const nativeHome =
|
|
44929
|
-
if (!
|
|
45110
|
+
const nativeHome = join26(home, agent === "opencode" ? ".config/opencode" : `.${agent}`);
|
|
45111
|
+
if (!lstatSync22(nativeHome, { throwIfNoEntry: false }))
|
|
44930
45112
|
continue;
|
|
44931
45113
|
roots.push(...resolveAgentDiscovery({ home, agent, canonical: physicalPath }).roots);
|
|
44932
45114
|
}
|
|
@@ -44935,22 +45117,22 @@ function configuredRoots(home, explicitHome) {
|
|
|
44935
45117
|
function assertPortableAuthoringPath(path, options = {}) {
|
|
44936
45118
|
if (typeof path !== "string" || !path || path.includes("\x00"))
|
|
44937
45119
|
throw new Error("Invalid portable authoring path");
|
|
44938
|
-
const lexical =
|
|
45120
|
+
const lexical = resolve17(path);
|
|
44939
45121
|
const physical = physicalPath(lexical);
|
|
44940
45122
|
const candidates = [lexical, physical];
|
|
44941
45123
|
let native = candidates.map(nativeRootMatch).find(Boolean);
|
|
44942
|
-
const home =
|
|
44943
|
-
const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) =>
|
|
44944
|
-
defaults.push(
|
|
45124
|
+
const home = resolve17(options.homeDir ?? homedir4());
|
|
45125
|
+
const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join26(home, ...parts));
|
|
45126
|
+
defaults.push(join26(home, ".codex/plugins/cache"), join26(home, ".claude/plugins/cache"), join26(home, ".claude/plugins/synced"), join26(home, ".gemini/extensions"));
|
|
44945
45127
|
for (const root2 of defaults) {
|
|
44946
|
-
if (candidates.some((candidate2) => contains(
|
|
45128
|
+
if (candidates.some((candidate2) => contains(resolve17(root2), candidate2) || contains(physicalPath(root2), candidate2)))
|
|
44947
45129
|
native ??= root2;
|
|
44948
45130
|
}
|
|
44949
45131
|
if (native) {
|
|
44950
45132
|
throw new Error(`Refusing portable Skills authoring below native discovery root '${native}': ${lexical}. Set HASNA_SKILLS_DIR to a private authoring corpus outside native roots.`);
|
|
44951
45133
|
}
|
|
44952
45134
|
for (const root2 of configuredRoots(home, options.homeDir !== undefined)) {
|
|
44953
|
-
if (candidates.some((candidate2) => contains(
|
|
45135
|
+
if (candidates.some((candidate2) => contains(resolve17(root2), candidate2) || contains(physicalPath(root2), candidate2))) {
|
|
44954
45136
|
throw new Error(`Refusing portable Skills authoring below configured native discovery root: ${lexical}`);
|
|
44955
45137
|
}
|
|
44956
45138
|
}
|
|
@@ -44962,15 +45144,15 @@ function getPortableSkillsRoot(options = {}) {
|
|
|
44962
45144
|
function resolvePortableSkillsRoot(options, migrate) {
|
|
44963
45145
|
if (options.rootDir)
|
|
44964
45146
|
return options.rootDir;
|
|
44965
|
-
const appDir = options.homeDir ?
|
|
44966
|
-
const cache =
|
|
45147
|
+
const appDir = options.homeDir ? join27(options.homeDir, ".hasna", "skills") : migrate ? getDataDir() : getDataDirReadOnly();
|
|
45148
|
+
const cache = join27(appDir, SKILLS_CACHE_DIRNAME);
|
|
44967
45149
|
if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache))
|
|
44968
45150
|
return cache;
|
|
44969
|
-
const installed =
|
|
45151
|
+
const installed = join27(appDir, INSTALLED_SKILLS_DIRNAME);
|
|
44970
45152
|
return installed;
|
|
44971
45153
|
}
|
|
44972
45154
|
function getPortableSkillPath(name, options = {}) {
|
|
44973
|
-
return
|
|
45155
|
+
return join27(getPortableSkillsRoot(options), normalizePortableSkillName(name));
|
|
44974
45156
|
}
|
|
44975
45157
|
function findPortableSkill(name, options = {}) {
|
|
44976
45158
|
let normalized;
|
|
@@ -44980,7 +45162,7 @@ function findPortableSkill(name, options = {}) {
|
|
|
44980
45162
|
return null;
|
|
44981
45163
|
}
|
|
44982
45164
|
const path = getPortableSkillPath(normalized, options);
|
|
44983
|
-
if (!
|
|
45165
|
+
if (!existsSync10(path) || !statSync9(path).isDirectory())
|
|
44984
45166
|
return null;
|
|
44985
45167
|
try {
|
|
44986
45168
|
return summarizePortableSkill(path, normalized);
|
|
@@ -44996,7 +45178,7 @@ function listPortableSkills(options = {}) {
|
|
|
44996
45178
|
for (const entry of readdirSync10(root2).sort()) {
|
|
44997
45179
|
if (entry.startsWith("."))
|
|
44998
45180
|
continue;
|
|
44999
|
-
const path =
|
|
45181
|
+
const path = join27(root2, entry);
|
|
45000
45182
|
if (!safeIsDirectory(path))
|
|
45001
45183
|
continue;
|
|
45002
45184
|
try {
|
|
@@ -45030,11 +45212,11 @@ function isOfficialSkillName(name) {
|
|
|
45030
45212
|
function scaffoldPortableSkill(name, options = {}) {
|
|
45031
45213
|
const skillName = normalizeNewPortableSkillName(name);
|
|
45032
45214
|
const readOnlyRoot = resolvePortableSkillsRoot(options, false);
|
|
45033
|
-
assertPortableAuthoringPath(
|
|
45215
|
+
assertPortableAuthoringPath(join27(readOnlyRoot, skillName), options);
|
|
45034
45216
|
const root2 = getPortableSkillsRoot(options);
|
|
45035
|
-
const skillPath =
|
|
45217
|
+
const skillPath = join27(root2, skillName);
|
|
45036
45218
|
assertPortableAuthoringPath(skillPath, options);
|
|
45037
|
-
if (
|
|
45219
|
+
if (existsSync10(skillPath)) {
|
|
45038
45220
|
if (!options.overwrite)
|
|
45039
45221
|
throw new Error(`Skill '${skillName}' already exists at ${skillPath}`);
|
|
45040
45222
|
rmSync2(skillPath, { recursive: true, force: true });
|
|
@@ -45052,7 +45234,7 @@ function scaffoldPortableSkill(name, options = {}) {
|
|
|
45052
45234
|
}
|
|
45053
45235
|
function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
45054
45236
|
const absoluteSource = normalize4(sourceDir);
|
|
45055
|
-
if (!
|
|
45237
|
+
if (!existsSync10(absoluteSource) || !statSync9(absoluteSource).isDirectory()) {
|
|
45056
45238
|
throw new Error(`Import directory not found: ${sourceDir}`);
|
|
45057
45239
|
}
|
|
45058
45240
|
const continueOnError = options.continueOnError ?? true;
|
|
@@ -45066,7 +45248,7 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
|
45066
45248
|
const skipped = [];
|
|
45067
45249
|
const entries = readdirSync10(absoluteSource, { withFileTypes: true }).map((entry) => entry.name).filter((entryName) => !entryName.startsWith(".")).sort();
|
|
45068
45250
|
for (const entryName of entries) {
|
|
45069
|
-
const childPath =
|
|
45251
|
+
const childPath = join27(absoluteSource, entryName);
|
|
45070
45252
|
if (!safeIsDirectory(childPath))
|
|
45071
45253
|
continue;
|
|
45072
45254
|
if (!isSkillCandidate(childPath)) {
|
|
@@ -45095,19 +45277,19 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
|
45095
45277
|
};
|
|
45096
45278
|
}
|
|
45097
45279
|
function isSkillCandidate(dir) {
|
|
45098
|
-
return
|
|
45280
|
+
return existsSync10(join27(dir, "SKILL.md")) || existsSync10(join27(dir, "skill.json")) || existsSync10(join27(dir, "package.json"));
|
|
45099
45281
|
}
|
|
45100
45282
|
function physicalImportPath(path) {
|
|
45101
|
-
let ancestor =
|
|
45283
|
+
let ancestor = resolve18(path);
|
|
45102
45284
|
const suffix = [];
|
|
45103
|
-
while (!
|
|
45285
|
+
while (!existsSync10(ancestor)) {
|
|
45104
45286
|
suffix.unshift(basename10(ancestor));
|
|
45105
45287
|
ancestor = dirname18(ancestor);
|
|
45106
45288
|
}
|
|
45107
|
-
return
|
|
45289
|
+
return join27(realpathSync11(ancestor), ...suffix);
|
|
45108
45290
|
}
|
|
45109
45291
|
function assertDisjointImportPaths(source, destination) {
|
|
45110
|
-
const sourcePath =
|
|
45292
|
+
const sourcePath = realpathSync11(source);
|
|
45111
45293
|
const destinationPath = physicalImportPath(destination);
|
|
45112
45294
|
const contains2 = (parent, child) => {
|
|
45113
45295
|
const path = relative6(parent, child);
|
|
@@ -45119,7 +45301,7 @@ function assertDisjointImportPaths(source, destination) {
|
|
|
45119
45301
|
}
|
|
45120
45302
|
function portPortableSkill(sourcePath, options = {}) {
|
|
45121
45303
|
const absoluteSource = normalize4(sourcePath);
|
|
45122
|
-
if (!
|
|
45304
|
+
if (!existsSync10(absoluteSource) || !statSync9(absoluteSource).isDirectory()) {
|
|
45123
45305
|
throw new Error(`Skill source directory not found: ${sourcePath}`);
|
|
45124
45306
|
}
|
|
45125
45307
|
const inferred = readPortableSkillManifestForImport(absoluteSource);
|
|
@@ -45130,14 +45312,14 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
45130
45312
|
const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename10(absoluteSource)}')` : ""} matches a bundled official skill.`;
|
|
45131
45313
|
throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
|
|
45132
45314
|
}
|
|
45133
|
-
const readOnlyDestination =
|
|
45315
|
+
const readOnlyDestination = join27(resolvePortableSkillsRoot(options, false), skillName);
|
|
45134
45316
|
assertPortableAuthoringPath(readOnlyDestination, options);
|
|
45135
45317
|
assertDisjointImportPaths(absoluteSource, readOnlyDestination);
|
|
45136
45318
|
const root2 = getPortableSkillsRoot(options);
|
|
45137
|
-
const destination =
|
|
45319
|
+
const destination = join27(root2, skillName);
|
|
45138
45320
|
assertPortableAuthoringPath(destination, options);
|
|
45139
45321
|
assertDisjointImportPaths(absoluteSource, destination);
|
|
45140
|
-
if (
|
|
45322
|
+
if (existsSync10(destination)) {
|
|
45141
45323
|
if (!options.overwrite)
|
|
45142
45324
|
throw new Error(`Skill '${skillName}' already exists at ${destination}`);
|
|
45143
45325
|
rmSync2(destination, { recursive: true, force: true });
|
|
@@ -45179,13 +45361,13 @@ function buildCorpusManifest(input, name) {
|
|
|
45179
45361
|
function writeCorpusSkill(input, options = {}) {
|
|
45180
45362
|
const name = normalizePortableSkillName(input.name);
|
|
45181
45363
|
const readOnlyRoot = resolvePortableSkillsRoot(options, false);
|
|
45182
|
-
assertPortableAuthoringPath(
|
|
45364
|
+
assertPortableAuthoringPath(join27(readOnlyRoot, name), options);
|
|
45183
45365
|
const root2 = getPortableSkillsRoot(options);
|
|
45184
|
-
const skillPath =
|
|
45366
|
+
const skillPath = join27(root2, name);
|
|
45185
45367
|
assertPortableAuthoringPath(skillPath, options);
|
|
45186
|
-
const created = !
|
|
45368
|
+
const created = !existsSync10(skillPath);
|
|
45187
45369
|
mkdirSync7(skillPath, { recursive: true });
|
|
45188
|
-
writeFileSync6(
|
|
45370
|
+
writeFileSync6(join27(skillPath, "SKILL.md"), input.skillMd);
|
|
45189
45371
|
const manifest = buildCorpusManifest(input, name);
|
|
45190
45372
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
45191
45373
|
return { name, path: skillPath, manifest, created };
|
|
@@ -45193,22 +45375,22 @@ function writeCorpusSkill(input, options = {}) {
|
|
|
45193
45375
|
function installCorpusSkillAtomically(input, options = {}) {
|
|
45194
45376
|
const name = normalizePortableSkillName(input.name);
|
|
45195
45377
|
const readOnlyRoot = resolvePortableSkillsRoot(options, false);
|
|
45196
|
-
assertPortableAuthoringPath(
|
|
45378
|
+
assertPortableAuthoringPath(join27(readOnlyRoot, name), options);
|
|
45197
45379
|
const root2 = getPortableSkillsRoot(options);
|
|
45198
|
-
const target =
|
|
45380
|
+
const target = join27(root2, name);
|
|
45199
45381
|
assertPortableAuthoringPath(target, options);
|
|
45200
|
-
const created = !
|
|
45382
|
+
const created = !existsSync10(target);
|
|
45201
45383
|
mkdirSync7(root2, { recursive: true });
|
|
45202
|
-
const staging = mkdtempSync(
|
|
45384
|
+
const staging = mkdtempSync(join27(root2, `.pull-${name}-`));
|
|
45203
45385
|
let moved = false;
|
|
45204
45386
|
let backup = null;
|
|
45205
45387
|
try {
|
|
45206
|
-
writeFileSync6(
|
|
45388
|
+
writeFileSync6(join27(staging, "SKILL.md"), input.skillMd);
|
|
45207
45389
|
const manifest = buildCorpusManifest(input, name);
|
|
45208
45390
|
writeSkillJsonWithHash(staging, manifest);
|
|
45209
|
-
if (
|
|
45210
|
-
backup = mkdtempSync(
|
|
45211
|
-
renameSync4(target,
|
|
45391
|
+
if (existsSync10(target)) {
|
|
45392
|
+
backup = mkdtempSync(join27(root2, `.pull-backup-${name}-`));
|
|
45393
|
+
renameSync4(target, join27(backup, name));
|
|
45212
45394
|
moved = true;
|
|
45213
45395
|
}
|
|
45214
45396
|
renameSync4(staging, target);
|
|
@@ -45217,9 +45399,9 @@ function installCorpusSkillAtomically(input, options = {}) {
|
|
|
45217
45399
|
return { name, path: target, manifest, created };
|
|
45218
45400
|
} catch (error) {
|
|
45219
45401
|
rmSync2(staging, { recursive: true, force: true });
|
|
45220
|
-
if (moved && backup &&
|
|
45402
|
+
if (moved && backup && existsSync10(join27(backup, name))) {
|
|
45221
45403
|
try {
|
|
45222
|
-
renameSync4(
|
|
45404
|
+
renameSync4(join27(backup, name), target);
|
|
45223
45405
|
} catch {}
|
|
45224
45406
|
}
|
|
45225
45407
|
throw error;
|
|
@@ -45231,10 +45413,10 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
45231
45413
|
const issues = [...base.issues];
|
|
45232
45414
|
const warnings = [...base.warnings];
|
|
45233
45415
|
let manifest;
|
|
45234
|
-
if (
|
|
45235
|
-
const skillJsonPath =
|
|
45236
|
-
const skillMdPath =
|
|
45237
|
-
if (!
|
|
45416
|
+
if (existsSync10(skillPath)) {
|
|
45417
|
+
const skillJsonPath = join27(skillPath, "skill.json");
|
|
45418
|
+
const skillMdPath = join27(skillPath, "SKILL.md");
|
|
45419
|
+
if (!existsSync10(skillJsonPath) && !existsSync10(skillMdPath)) {
|
|
45238
45420
|
add3(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
|
|
45239
45421
|
}
|
|
45240
45422
|
try {
|
|
@@ -45253,7 +45435,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
45253
45435
|
add3(issues, "portable.version_missing", "Portable manifest missing version");
|
|
45254
45436
|
}
|
|
45255
45437
|
const contractIssues = validatePortableManifestContract(manifest, {
|
|
45256
|
-
strict:
|
|
45438
|
+
strict: existsSync10(join27(skillPath, "skill.json")),
|
|
45257
45439
|
skillPath
|
|
45258
45440
|
});
|
|
45259
45441
|
for (const issue of contractIssues)
|
|
@@ -45293,8 +45475,8 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
45293
45475
|
add3(issues, "portable.command_entry_unsafe", `Command '${command.name}' entry '${command.entry}' must stay inside the skill directory`);
|
|
45294
45476
|
continue;
|
|
45295
45477
|
}
|
|
45296
|
-
const entryPath =
|
|
45297
|
-
if (!
|
|
45478
|
+
const entryPath = join27(skillPath, command.entry);
|
|
45479
|
+
if (!existsSync10(entryPath))
|
|
45298
45480
|
add3(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
|
|
45299
45481
|
else if (statSync9(entryPath).isDirectory())
|
|
45300
45482
|
add3(issues, "portable.command_entry_directory", `Command '${command.name}' entry '${command.entry}' must be a file`);
|
|
@@ -45304,7 +45486,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
45304
45486
|
} catch (error) {
|
|
45305
45487
|
add3(issues, "portable.manifest_invalid", error.message);
|
|
45306
45488
|
}
|
|
45307
|
-
if (manifest?.kind !== "instruction" && !
|
|
45489
|
+
if (manifest?.kind !== "instruction" && !existsSync10(join27(skillPath, "AGENTS.md"))) {
|
|
45308
45490
|
add3(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
|
|
45309
45491
|
}
|
|
45310
45492
|
}
|
|
@@ -45340,13 +45522,13 @@ async function runPortableSkill(name, args, options = {}) {
|
|
|
45340
45522
|
if (!isSafeRelativePath2(command.entry)) {
|
|
45341
45523
|
return { exitCode: 1, error: `Portable skill '${name}' command entry is unsafe` };
|
|
45342
45524
|
}
|
|
45343
|
-
const entryPath =
|
|
45344
|
-
if (!
|
|
45525
|
+
const entryPath = join27(skill.path, command.entry);
|
|
45526
|
+
if (!existsSync10(entryPath)) {
|
|
45345
45527
|
return { exitCode: 1, error: `Entry point '${command.entry}' not found in portable skill '${name}'` };
|
|
45346
45528
|
}
|
|
45347
|
-
const pkgPath =
|
|
45348
|
-
const nodeModules =
|
|
45349
|
-
if (
|
|
45529
|
+
const pkgPath = join27(skill.path, "package.json");
|
|
45530
|
+
const nodeModules = join27(skill.path, "node_modules");
|
|
45531
|
+
if (existsSync10(pkgPath) && !existsSync10(nodeModules) && hasPackageDependencies(pkgPath)) {
|
|
45350
45532
|
const install = Bun.spawn(["bun", "install", "--no-save"], {
|
|
45351
45533
|
cwd: skill.path,
|
|
45352
45534
|
stdout: "pipe",
|
|
@@ -45417,9 +45599,9 @@ function isSkillsOwnershipMarker(marker) {
|
|
|
45417
45599
|
return typeof marker === "object" && marker !== null && Object.hasOwn(marker, "managedBy") && marker.managedBy === SYNC_MARKER_MANAGED_BY;
|
|
45418
45600
|
}
|
|
45419
45601
|
function hasSkillsOwnershipMarker(dir) {
|
|
45420
|
-
const path =
|
|
45602
|
+
const path = join28(dir, SYNC_MARKER_FILE);
|
|
45421
45603
|
try {
|
|
45422
|
-
if (!
|
|
45604
|
+
if (!lstatSync23(path).isFile())
|
|
45423
45605
|
return false;
|
|
45424
45606
|
const marker = JSON.parse(readFileSync17(path, "utf8"));
|
|
45425
45607
|
return isSkillsOwnershipMarker(marker);
|
|
@@ -45441,9 +45623,9 @@ function resolveSyncAgents(arg) {
|
|
|
45441
45623
|
function agentGlobalSkillsDir(agent, homeDir3 = homedir5()) {
|
|
45442
45624
|
switch (agent) {
|
|
45443
45625
|
case "opencode":
|
|
45444
|
-
return
|
|
45626
|
+
return join28(homeDir3, ".config", "opencode", "skills");
|
|
45445
45627
|
default:
|
|
45446
|
-
return
|
|
45628
|
+
return join28(homeDir3, `.${agent}`, "skills");
|
|
45447
45629
|
}
|
|
45448
45630
|
}
|
|
45449
45631
|
function adaptSkillMdForAgent(skillMd, agent) {
|
|
@@ -45502,8 +45684,8 @@ function resolveSyncCorpus(options = {}) {
|
|
|
45502
45684
|
function packageSourceRoots(source) {
|
|
45503
45685
|
const roots = [];
|
|
45504
45686
|
for (const sub of ["skills"]) {
|
|
45505
|
-
const candidate2 =
|
|
45506
|
-
if (
|
|
45687
|
+
const candidate2 = join28(source, sub);
|
|
45688
|
+
if (existsSync11(candidate2) && isDirectory(candidate2))
|
|
45507
45689
|
roots.push(candidate2);
|
|
45508
45690
|
}
|
|
45509
45691
|
if (roots.length > 0)
|
|
@@ -45518,10 +45700,10 @@ function containsSkillDirectories(path) {
|
|
|
45518
45700
|
return false;
|
|
45519
45701
|
}
|
|
45520
45702
|
return entries.some((entry) => {
|
|
45521
|
-
const candidate2 =
|
|
45703
|
+
const candidate2 = join28(path, entry);
|
|
45522
45704
|
if (!isDirectory(candidate2))
|
|
45523
45705
|
return false;
|
|
45524
|
-
return
|
|
45706
|
+
return existsSync11(join28(candidate2, "SKILL.md")) || existsSync11(join28(candidate2, "skill.json")) || existsSync11(join28(candidate2, "package.json"));
|
|
45525
45707
|
});
|
|
45526
45708
|
}
|
|
45527
45709
|
function isDirectory(path) {
|
|
@@ -45561,7 +45743,7 @@ function syncSkillsToAgents(options = {}) {
|
|
|
45561
45743
|
actions.push({
|
|
45562
45744
|
skill: name,
|
|
45563
45745
|
agent,
|
|
45564
|
-
path:
|
|
45746
|
+
path: join28(agentGlobalSkillsDir(agent, homeDir3), name, "SKILL.md"),
|
|
45565
45747
|
action: "skip",
|
|
45566
45748
|
reason: "not found in this machine's corpus"
|
|
45567
45749
|
});
|
|
@@ -45591,7 +45773,7 @@ function syncSkillsToAgents(options = {}) {
|
|
|
45591
45773
|
}
|
|
45592
45774
|
function writeManagedAgentSkill(params) {
|
|
45593
45775
|
const homeDir3 = params.homeDir ?? homedir5();
|
|
45594
|
-
const dir =
|
|
45776
|
+
const dir = join28(agentGlobalSkillsDir(params.agent, homeDir3), params.skill);
|
|
45595
45777
|
const result = writeManagedSkillDir(dir, params.skillMd, {
|
|
45596
45778
|
skill: params.skill,
|
|
45597
45779
|
source: params.source,
|
|
@@ -45608,11 +45790,11 @@ function writeManagedAgentSkill(params) {
|
|
|
45608
45790
|
};
|
|
45609
45791
|
}
|
|
45610
45792
|
function writeManagedSkillDir(dir, skillMd, options) {
|
|
45611
|
-
const skillMdPath =
|
|
45612
|
-
const markerPath =
|
|
45613
|
-
const dirExists =
|
|
45793
|
+
const skillMdPath = join28(dir, "SKILL.md");
|
|
45794
|
+
const markerPath = join28(dir, SYNC_MARKER_FILE);
|
|
45795
|
+
const dirExists = existsSync11(dir);
|
|
45614
45796
|
const managed = hasSkillsOwnershipMarker(dir);
|
|
45615
|
-
const hasSkillMd =
|
|
45797
|
+
const hasSkillMd = existsSync11(skillMdPath);
|
|
45616
45798
|
if (dirExists && !managed && !hasSkillMd) {
|
|
45617
45799
|
return {
|
|
45618
45800
|
action: "skip",
|
|
@@ -45624,7 +45806,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
45624
45806
|
return {
|
|
45625
45807
|
action: "skip",
|
|
45626
45808
|
path: skillMdPath,
|
|
45627
|
-
reason:
|
|
45809
|
+
reason: existsSync11(markerPath) ? "an unmanaged SKILL.md already exists here (invalid or foreign ownership marker); pass --force to overwrite" : "an unmanaged SKILL.md already exists here (hand-authored); pass --force to overwrite"
|
|
45628
45810
|
};
|
|
45629
45811
|
}
|
|
45630
45812
|
if (dirExists && managed && hasSkillMd && !options.force && isPointerSkillMd(skillMd)) {
|
|
@@ -45647,11 +45829,11 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
45647
45829
|
return { action, path: skillMdPath };
|
|
45648
45830
|
const parentDir = dirname19(dir);
|
|
45649
45831
|
mkdirSync8(parentDir, { recursive: true });
|
|
45650
|
-
const transactionDir = mkdtempSync2(
|
|
45651
|
-
const candidateDir =
|
|
45652
|
-
const backupDir =
|
|
45653
|
-
const candidateSkillMdPath =
|
|
45654
|
-
const candidateMarkerPath =
|
|
45832
|
+
const transactionDir = mkdtempSync2(join28(parentDir, `.hasna-skills-write-${basename11(dir)}-`));
|
|
45833
|
+
const candidateDir = join28(transactionDir, "candidate");
|
|
45834
|
+
const backupDir = join28(transactionDir, "backup");
|
|
45835
|
+
const candidateSkillMdPath = join28(candidateDir, "SKILL.md");
|
|
45836
|
+
const candidateMarkerPath = join28(candidateDir, SYNC_MARKER_FILE);
|
|
45655
45837
|
const marker = {
|
|
45656
45838
|
managedBy: SYNC_MARKER_MANAGED_BY,
|
|
45657
45839
|
skill: options.skill,
|
|
@@ -45678,9 +45860,9 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
45678
45860
|
}
|
|
45679
45861
|
renameDirectory(candidateDir, dir);
|
|
45680
45862
|
} catch (error) {
|
|
45681
|
-
if (originalMoved &&
|
|
45863
|
+
if (originalMoved && existsSync11(backupDir)) {
|
|
45682
45864
|
try {
|
|
45683
|
-
if (
|
|
45865
|
+
if (existsSync11(dir))
|
|
45684
45866
|
rmSync3(dir, { recursive: true, force: true });
|
|
45685
45867
|
renameDirectory(backupDir, dir);
|
|
45686
45868
|
originalMoved = false;
|
|
@@ -45700,7 +45882,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
45700
45882
|
return { action, path: skillMdPath };
|
|
45701
45883
|
}
|
|
45702
45884
|
function removeManagedAgentSkill(skill, agent, homeDir3 = homedir5()) {
|
|
45703
|
-
const dir =
|
|
45885
|
+
const dir = join28(agentGlobalSkillsDir(agent, homeDir3), skill);
|
|
45704
45886
|
if (!hasSkillsOwnershipMarker(dir))
|
|
45705
45887
|
return false;
|
|
45706
45888
|
rmSync3(dir, { recursive: true, force: true });
|
|
@@ -45708,8 +45890,8 @@ function removeManagedAgentSkill(skill, agent, homeDir3 = homedir5()) {
|
|
|
45708
45890
|
}
|
|
45709
45891
|
function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = false) {
|
|
45710
45892
|
if (kind === undefined || kind === "instruction" || preferBundledDocs) {
|
|
45711
|
-
const skillMdPath =
|
|
45712
|
-
if (
|
|
45893
|
+
const skillMdPath = join28(skillPath, "SKILL.md");
|
|
45894
|
+
if (existsSync11(skillMdPath))
|
|
45713
45895
|
return readFileSync17(skillMdPath, "utf-8");
|
|
45714
45896
|
}
|
|
45715
45897
|
return pointerSkillMd(name, description);
|
|
@@ -45740,8 +45922,8 @@ function normalizeSkillName(name) {
|
|
|
45740
45922
|
}
|
|
45741
45923
|
|
|
45742
45924
|
// src/lib/registry.ts
|
|
45743
|
-
import { existsSync as
|
|
45744
|
-
import { join as
|
|
45925
|
+
import { existsSync as existsSync12, readFileSync as readFileSync18, readdirSync as readdirSync12 } from "fs";
|
|
45926
|
+
import { join as join29 } from "path";
|
|
45745
45927
|
|
|
45746
45928
|
// src/lib/registry-merge.ts
|
|
45747
45929
|
var SKILL_SOURCE_PRECEDENCE = {
|
|
@@ -45943,7 +46125,7 @@ function parseSkillMdFrontmatter(content) {
|
|
|
45943
46125
|
return Object.keys(result).length > 0 ? result : null;
|
|
45944
46126
|
}
|
|
45945
46127
|
function discoverSkillsInDir(dir, source = "custom") {
|
|
45946
|
-
if (!
|
|
46128
|
+
if (!existsSync12(dir))
|
|
45947
46129
|
return [];
|
|
45948
46130
|
const result = [];
|
|
45949
46131
|
try {
|
|
@@ -45951,8 +46133,8 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
45951
46133
|
for (const entry of entries) {
|
|
45952
46134
|
if (!entry.isDirectory())
|
|
45953
46135
|
continue;
|
|
45954
|
-
const skillMdPath =
|
|
45955
|
-
if (!
|
|
46136
|
+
const skillMdPath = join29(dir, entry.name, "SKILL.md");
|
|
46137
|
+
if (!existsSync12(skillMdPath))
|
|
45956
46138
|
continue;
|
|
45957
46139
|
let content;
|
|
45958
46140
|
try {
|
|
@@ -45971,7 +46153,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
45971
46153
|
category: fm.category || "Development Tools",
|
|
45972
46154
|
tags: fm.tags || [],
|
|
45973
46155
|
...fm.kind ? { kind: fm.kind } : {},
|
|
45974
|
-
...isHostedMetadataSkillDir(
|
|
46156
|
+
...isHostedMetadataSkillDir(join29(dir, entry.name)) ? { serverOwned: true } : {},
|
|
45975
46157
|
source
|
|
45976
46158
|
});
|
|
45977
46159
|
}
|
|
@@ -45980,16 +46162,16 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
45980
46162
|
}
|
|
45981
46163
|
function findExtensionSkillPath(name) {
|
|
45982
46164
|
const config = loadConfig();
|
|
45983
|
-
if (!config.extensionsDir || !
|
|
46165
|
+
if (!config.extensionsDir || !existsSync12(config.extensionsDir))
|
|
45984
46166
|
return null;
|
|
45985
46167
|
try {
|
|
45986
46168
|
const entries = readdirSync12(config.extensionsDir, { withFileTypes: true });
|
|
45987
46169
|
for (const entry of entries) {
|
|
45988
46170
|
if (!entry.isDirectory())
|
|
45989
46171
|
continue;
|
|
45990
|
-
const skillDir =
|
|
45991
|
-
const skillMdPath =
|
|
45992
|
-
if (!
|
|
46172
|
+
const skillDir = join29(config.extensionsDir, entry.name);
|
|
46173
|
+
const skillMdPath = join29(skillDir, "SKILL.md");
|
|
46174
|
+
if (!existsSync12(skillMdPath))
|
|
45993
46175
|
continue;
|
|
45994
46176
|
let content;
|
|
45995
46177
|
try {
|
|
@@ -46036,21 +46218,21 @@ function clearRegistryCache() {
|
|
|
46036
46218
|
registryCacheTime = 0;
|
|
46037
46219
|
registryCacheKey = null;
|
|
46038
46220
|
}
|
|
46039
|
-
function getSkillsByCategory(category) {
|
|
46040
|
-
return loadRegistry().filter((s) => s.category === category);
|
|
46221
|
+
function getSkillsByCategory(category, catalog) {
|
|
46222
|
+
return (catalog ?? loadRegistry()).filter((s) => s.category === category);
|
|
46041
46223
|
}
|
|
46042
46224
|
function getSkill(name) {
|
|
46043
46225
|
const registry = loadRegistry();
|
|
46044
46226
|
const slug = normalizeSkillSlug(name);
|
|
46045
46227
|
return registry.find((s) => s.name === slug) ?? registry.find((s) => s.name === resolveSkillAlias(slug));
|
|
46046
46228
|
}
|
|
46047
|
-
function getSkillsByTag(tag) {
|
|
46229
|
+
function getSkillsByTag(tag, catalog) {
|
|
46048
46230
|
const needle = tag.toLowerCase();
|
|
46049
|
-
return loadRegistry().filter((s) => s.tags.some((t2) => t2.toLowerCase().includes(needle)));
|
|
46231
|
+
return (catalog ?? loadRegistry()).filter((s) => s.tags.some((t2) => t2.toLowerCase().includes(needle)));
|
|
46050
46232
|
}
|
|
46051
|
-
function getAllTags() {
|
|
46233
|
+
function getAllTags(catalog) {
|
|
46052
46234
|
const tagSet = new Set;
|
|
46053
|
-
for (const skill of loadRegistry()) {
|
|
46235
|
+
for (const skill of catalog ?? loadRegistry()) {
|
|
46054
46236
|
for (const tag of skill.tags)
|
|
46055
46237
|
tagSet.add(tag.toLowerCase());
|
|
46056
46238
|
}
|
|
@@ -46058,8 +46240,8 @@ function getAllTags() {
|
|
|
46058
46240
|
}
|
|
46059
46241
|
|
|
46060
46242
|
// src/lib/project-state.ts
|
|
46061
|
-
import { existsSync as
|
|
46062
|
-
import { join as
|
|
46243
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync19, writeFileSync as writeFileSync8 } from "fs";
|
|
46244
|
+
import { join as join30 } from "path";
|
|
46063
46245
|
var VALID_PIN_SOURCES = [
|
|
46064
46246
|
"official",
|
|
46065
46247
|
"custom",
|
|
@@ -46074,14 +46256,14 @@ var SKILLS_PROJECT_DIR = ".skills";
|
|
|
46074
46256
|
var PROJECT_CONFIG_FILE = "project.json";
|
|
46075
46257
|
var DEFAULT_EXPORT_DIR = ".skills/exports";
|
|
46076
46258
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
46077
|
-
return
|
|
46259
|
+
return join30(targetDir, SKILLS_PROJECT_DIR);
|
|
46078
46260
|
}
|
|
46079
46261
|
function getProjectConfigPath(targetDir = process.cwd()) {
|
|
46080
|
-
return
|
|
46262
|
+
return join30(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
|
|
46081
46263
|
}
|
|
46082
46264
|
function loadProjectConfig(targetDir = process.cwd()) {
|
|
46083
46265
|
const path = getProjectConfigPath(targetDir);
|
|
46084
|
-
if (!
|
|
46266
|
+
if (!existsSync13(path))
|
|
46085
46267
|
return null;
|
|
46086
46268
|
try {
|
|
46087
46269
|
return normalizeProjectConfig(JSON.parse(readFileSync19(path, "utf-8")));
|
|
@@ -46206,19 +46388,19 @@ function getSkillPath(name) {
|
|
|
46206
46388
|
const extensionPath = findExtensionSkillPath(skillName);
|
|
46207
46389
|
if (extensionPath)
|
|
46208
46390
|
return extensionPath;
|
|
46209
|
-
return
|
|
46391
|
+
return join31(getPortableSkillsRoot(), skillName);
|
|
46210
46392
|
}
|
|
46211
46393
|
function getCanonicalSkillName(name) {
|
|
46212
46394
|
return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
|
|
46213
46395
|
}
|
|
46214
46396
|
function skillExists(name) {
|
|
46215
|
-
return
|
|
46397
|
+
return existsSync14(getSkillPath(name));
|
|
46216
46398
|
}
|
|
46217
46399
|
function installSkill(name, options = {}) {
|
|
46218
46400
|
const { targetDir = process.cwd(), overwrite = false } = options;
|
|
46219
46401
|
const canonicalName = getCanonicalSkillName(name);
|
|
46220
46402
|
const skillName = normalizeSkillName(canonicalName);
|
|
46221
|
-
if (!
|
|
46403
|
+
if (!existsSync14(getSkillPath(name))) {
|
|
46222
46404
|
const knownOfficial = Boolean(getSkill(name));
|
|
46223
46405
|
return {
|
|
46224
46406
|
skill: canonicalName,
|
|
@@ -46245,7 +46427,7 @@ function installSkill(name, options = {}) {
|
|
|
46245
46427
|
}
|
|
46246
46428
|
function installSkillSource(name, _options = {}) {
|
|
46247
46429
|
const canonicalName = getCanonicalSkillName(name);
|
|
46248
|
-
if (!
|
|
46430
|
+
if (!existsSync14(getSkillPath(name))) {
|
|
46249
46431
|
return { skill: canonicalName, success: false, error: `Skill '${name}' not found`, mode: "source" };
|
|
46250
46432
|
}
|
|
46251
46433
|
return {
|
|
@@ -46266,11 +46448,11 @@ function installSkillManifest(manifest, _options = {}) {
|
|
|
46266
46448
|
}
|
|
46267
46449
|
function createLocalSkillManifest(name, generateSkillMd) {
|
|
46268
46450
|
const sourcePath = getSkillPath(name);
|
|
46269
|
-
if (!
|
|
46451
|
+
if (!existsSync14(sourcePath))
|
|
46270
46452
|
return null;
|
|
46271
46453
|
let skillMd = "";
|
|
46272
|
-
const skillMdPath =
|
|
46273
|
-
if (
|
|
46454
|
+
const skillMdPath = join31(sourcePath, "SKILL.md");
|
|
46455
|
+
if (existsSync14(skillMdPath)) {
|
|
46274
46456
|
skillMd = readFileSync20(skillMdPath, "utf-8");
|
|
46275
46457
|
} else if (generateSkillMd) {
|
|
46276
46458
|
skillMd = generateSkillMd(name) ?? "";
|
|
@@ -46343,21 +46525,21 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
|
46343
46525
|
const base = projectDir || process.cwd();
|
|
46344
46526
|
switch (agent) {
|
|
46345
46527
|
case "pi":
|
|
46346
|
-
return scope === "project" ?
|
|
46528
|
+
return scope === "project" ? join31(base, ".pi", "skills") : join31(homedir6(), ".pi", "agent", "skills");
|
|
46347
46529
|
case "opencode":
|
|
46348
|
-
return scope === "project" ?
|
|
46530
|
+
return scope === "project" ? join31(base, ".opencode", "skills") : join31(homedir6(), ".config", "opencode", "skills");
|
|
46349
46531
|
default:
|
|
46350
|
-
return scope === "project" ?
|
|
46532
|
+
return scope === "project" ? join31(base, `.${agent}`, "skills") : join31(homedir6(), `.${agent}`, "skills");
|
|
46351
46533
|
}
|
|
46352
46534
|
}
|
|
46353
46535
|
function getAgentSkillPath(name, agent, scope = "global", projectDir) {
|
|
46354
46536
|
const skillName = normalizeSkillName(getCanonicalSkillName(name));
|
|
46355
|
-
return
|
|
46537
|
+
return join31(getAgentSkillsDir(agent, scope, projectDir), skillName);
|
|
46356
46538
|
}
|
|
46357
46539
|
function installSkillForAgent(name, options, generateSkillMd) {
|
|
46358
46540
|
assertNativeExportAllowed();
|
|
46359
46541
|
const canonicalName = getCanonicalSkillName(name);
|
|
46360
|
-
if (!
|
|
46542
|
+
if (!existsSync14(getSkillPath(name))) {
|
|
46361
46543
|
return { skill: canonicalName, success: false, error: `Skill '${name}' not found` };
|
|
46362
46544
|
}
|
|
46363
46545
|
const scope = options.scope ?? "global";
|
|
@@ -46388,8 +46570,8 @@ function removeSkillForAgent(name, options) {
|
|
|
46388
46570
|
}
|
|
46389
46571
|
function resolveAgentSkillMd(name, generateSkillMd) {
|
|
46390
46572
|
const sourcePath = getSkillPath(name);
|
|
46391
|
-
const skillMdPath =
|
|
46392
|
-
if (
|
|
46573
|
+
const skillMdPath = join31(sourcePath, "SKILL.md");
|
|
46574
|
+
if (existsSync14(skillMdPath))
|
|
46393
46575
|
return readFileSync20(skillMdPath, "utf-8");
|
|
46394
46576
|
if (generateSkillMd)
|
|
46395
46577
|
return generateSkillMd(name);
|
|
@@ -46408,7 +46590,7 @@ function warnMissingDependencies(name, targetDir) {
|
|
|
46408
46590
|
}
|
|
46409
46591
|
function generateMinimalSkillMd(name) {
|
|
46410
46592
|
const sourcePath = getSkillPath(name);
|
|
46411
|
-
if (!
|
|
46593
|
+
if (!existsSync14(sourcePath))
|
|
46412
46594
|
return null;
|
|
46413
46595
|
const canonicalName = getCanonicalSkillName(name);
|
|
46414
46596
|
const meta = getSkill(canonicalName);
|
|
@@ -46423,7 +46605,7 @@ function generateMinimalSkillMd(name) {
|
|
|
46423
46605
|
"---",
|
|
46424
46606
|
""
|
|
46425
46607
|
].filter(Boolean);
|
|
46426
|
-
const fallbackDoc = readFileIfExists(
|
|
46608
|
+
const fallbackDoc = readFileIfExists(join31(sourcePath, "README.md")) || readFileIfExists(join31(sourcePath, "CLAUDE.md"));
|
|
46427
46609
|
if (fallbackDoc)
|
|
46428
46610
|
return `${frontmatter.join(`
|
|
46429
46611
|
`)}${fallbackDoc.trim()}
|
|
@@ -46442,8 +46624,8 @@ skills run ${canonicalName}
|
|
|
46442
46624
|
`;
|
|
46443
46625
|
}
|
|
46444
46626
|
function readBundledSkillVersion(name) {
|
|
46445
|
-
const pkgPath =
|
|
46446
|
-
if (!
|
|
46627
|
+
const pkgPath = join31(getSkillPath(name), "package.json");
|
|
46628
|
+
if (!existsSync14(pkgPath))
|
|
46447
46629
|
return "unknown";
|
|
46448
46630
|
try {
|
|
46449
46631
|
const pkg = JSON.parse(readFileSync20(pkgPath, "utf-8"));
|
|
@@ -46453,7 +46635,7 @@ function readBundledSkillVersion(name) {
|
|
|
46453
46635
|
}
|
|
46454
46636
|
}
|
|
46455
46637
|
function readFileIfExists(path) {
|
|
46456
|
-
return
|
|
46638
|
+
return existsSync14(path) ? readFileSync20(path, "utf-8") : null;
|
|
46457
46639
|
}
|
|
46458
46640
|
function loadProjectConfigCompat(targetDir) {
|
|
46459
46641
|
return loadProjectConfig(targetDir);
|
|
@@ -46497,8 +46679,8 @@ function selectSkillDoc(docs, file) {
|
|
|
46497
46679
|
function isInstructionSkillDir(skillPath, meta) {
|
|
46498
46680
|
if (meta?.kind === "instruction")
|
|
46499
46681
|
return true;
|
|
46500
|
-
const skillMdPath =
|
|
46501
|
-
if (!
|
|
46682
|
+
const skillMdPath = join32(skillPath, "SKILL.md");
|
|
46683
|
+
if (!existsSync15(skillMdPath))
|
|
46502
46684
|
return false;
|
|
46503
46685
|
try {
|
|
46504
46686
|
return parseSkillFrontmatter(readFileSync21(skillMdPath, "utf-8"))?.kind === "instruction";
|
|
@@ -46524,12 +46706,12 @@ var HOSTED_PROVIDER_ENV_PREFIXES = [
|
|
|
46524
46706
|
];
|
|
46525
46707
|
function getSkillDocs(name) {
|
|
46526
46708
|
const skillPath = getSkillPath(name);
|
|
46527
|
-
if (!
|
|
46709
|
+
if (!existsSync15(skillPath))
|
|
46528
46710
|
return null;
|
|
46529
46711
|
return {
|
|
46530
|
-
skillMd: readIfExists(
|
|
46531
|
-
readme: readIfExists(
|
|
46532
|
-
claudeMd: readIfExists(
|
|
46712
|
+
skillMd: readIfExists(join32(skillPath, "SKILL.md")),
|
|
46713
|
+
readme: readIfExists(join32(skillPath, "README.md")),
|
|
46714
|
+
claudeMd: readIfExists(join32(skillPath, "CLAUDE.md"))
|
|
46533
46715
|
};
|
|
46534
46716
|
}
|
|
46535
46717
|
function getSkillBestDoc(name, file) {
|
|
@@ -46541,18 +46723,18 @@ function getSkillBestDoc(name, file) {
|
|
|
46541
46723
|
}
|
|
46542
46724
|
function getSkillRequirements(name) {
|
|
46543
46725
|
const skillPath = getSkillPath(name);
|
|
46544
|
-
if (!
|
|
46726
|
+
if (!existsSync15(skillPath))
|
|
46545
46727
|
return null;
|
|
46546
46728
|
const texts = [];
|
|
46547
46729
|
for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
46548
|
-
const content = readIfExists(
|
|
46730
|
+
const content = readIfExists(join32(skillPath, file));
|
|
46549
46731
|
if (content)
|
|
46550
46732
|
texts.push(content);
|
|
46551
46733
|
}
|
|
46552
46734
|
const meta = getSkill(name);
|
|
46553
46735
|
let dependencies = {};
|
|
46554
46736
|
try {
|
|
46555
|
-
dependencies = JSON.parse(readFileSync21(
|
|
46737
|
+
dependencies = JSON.parse(readFileSync21(join32(skillPath, "package.json"), "utf8")).dependencies || {};
|
|
46556
46738
|
} catch {}
|
|
46557
46739
|
return getSkillRequirementsFromContent(name, texts, dependencies, meta);
|
|
46558
46740
|
}
|
|
@@ -46605,7 +46787,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
46605
46787
|
const meta = getSkill(name);
|
|
46606
46788
|
const canonicalName = meta?.name ?? name;
|
|
46607
46789
|
const skillPath = getSkillPath(canonicalName);
|
|
46608
|
-
if (!
|
|
46790
|
+
if (!existsSync15(skillPath)) {
|
|
46609
46791
|
return { exitCode: 1, error: `Skill '${name}' not found` };
|
|
46610
46792
|
}
|
|
46611
46793
|
if (isInstructionSkillDir(skillPath, meta)) {
|
|
@@ -46614,8 +46796,8 @@ async function runSkill(name, args, options = {}) {
|
|
|
46614
46796
|
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'.`
|
|
46615
46797
|
};
|
|
46616
46798
|
}
|
|
46617
|
-
const pkgPath =
|
|
46618
|
-
if (!
|
|
46799
|
+
const pkgPath = join32(skillPath, "package.json");
|
|
46800
|
+
if (!existsSync15(pkgPath)) {
|
|
46619
46801
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
46620
46802
|
}
|
|
46621
46803
|
let entryPoint2;
|
|
@@ -46634,12 +46816,12 @@ async function runSkill(name, args, options = {}) {
|
|
|
46634
46816
|
} catch {
|
|
46635
46817
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
46636
46818
|
}
|
|
46637
|
-
const entryPath =
|
|
46638
|
-
if (!
|
|
46819
|
+
const entryPath = join32(skillPath, entryPoint2);
|
|
46820
|
+
if (!existsSync15(entryPath)) {
|
|
46639
46821
|
return { exitCode: 1, error: `Entry point '${entryPoint2}' not found in skill '${name}'` };
|
|
46640
46822
|
}
|
|
46641
46823
|
const env = { ...process.env, ...options.env };
|
|
46642
|
-
if (!
|
|
46824
|
+
if (!existsSync15(join32(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
|
|
46643
46825
|
const failure = await prepareSkillDependencies(skillPath, env, options.preparationTimeoutMs);
|
|
46644
46826
|
if (failure)
|
|
46645
46827
|
return failure;
|
|
@@ -46708,7 +46890,7 @@ function generateSkillMd(name) {
|
|
|
46708
46890
|
if (!meta)
|
|
46709
46891
|
return null;
|
|
46710
46892
|
const skillPath = getSkillPath(name);
|
|
46711
|
-
if (!
|
|
46893
|
+
if (!existsSync15(skillPath))
|
|
46712
46894
|
return null;
|
|
46713
46895
|
const frontmatter = [
|
|
46714
46896
|
"---",
|
|
@@ -46717,11 +46899,11 @@ function generateSkillMd(name) {
|
|
|
46717
46899
|
"---"
|
|
46718
46900
|
].join(`
|
|
46719
46901
|
`);
|
|
46720
|
-
const readme = readIfExists(
|
|
46721
|
-
const claudeMd = readIfExists(
|
|
46902
|
+
const readme = readIfExists(join32(skillPath, "README.md"));
|
|
46903
|
+
const claudeMd = readIfExists(join32(skillPath, "CLAUDE.md"));
|
|
46722
46904
|
let cliCommand = null;
|
|
46723
|
-
const pkgPath =
|
|
46724
|
-
if (
|
|
46905
|
+
const pkgPath = join32(skillPath, "package.json");
|
|
46906
|
+
if (existsSync15(pkgPath)) {
|
|
46725
46907
|
try {
|
|
46726
46908
|
const pkg = JSON.parse(readFileSync21(pkgPath, "utf-8"));
|
|
46727
46909
|
if (pkg.bin) {
|
|
@@ -46798,7 +46980,7 @@ function extractEnvVars(text2) {
|
|
|
46798
46980
|
}
|
|
46799
46981
|
function readIfExists(path) {
|
|
46800
46982
|
try {
|
|
46801
|
-
if (
|
|
46983
|
+
if (existsSync15(path)) {
|
|
46802
46984
|
return readFileSync21(path, "utf-8");
|
|
46803
46985
|
}
|
|
46804
46986
|
} catch {}
|
|
@@ -46865,6 +47047,8 @@ async function syncSelectionProfile(profileId, options = {}) {
|
|
|
46865
47047
|
async function resolveSelectionContext(profileId, options = {}) {
|
|
46866
47048
|
const sessionState = options.sessionId ? readSkillSessionSnapshotIfExists(options.sessionId, options) : null;
|
|
46867
47049
|
const session = sessionState?.receipt ?? null;
|
|
47050
|
+
if (options.nativeParentSessionId !== undefined && session && (session.parent?.sessionId ?? null) !== options.nativeParentSessionId)
|
|
47051
|
+
throw new SkillSelectionError("SESSION_PARENT_MISMATCH", "The native session id is already bound to different parent custody.");
|
|
46868
47052
|
const parentState = !session && options.parentSessionId ? readSkillSessionSnapshotIfExists(options.parentSessionId, options) : null;
|
|
46869
47053
|
if (!session && options.parentSessionId && !parentState) {
|
|
46870
47054
|
throw new SkillSelectionError("SESSION_PARENT_NOT_FOUND", "A child Skills session requires an existing parent receipt so its inherited pin can be bound exactly.");
|
|
@@ -47002,7 +47186,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
47002
47186
|
throw new SkillSelectionError("INVALID_SECRET_BINDINGS", "Explicit environment values must be bounded strings without NUL bytes.");
|
|
47003
47187
|
secretEnv[name] = value;
|
|
47004
47188
|
}
|
|
47005
|
-
const runDirectory = mkdtempSync3(
|
|
47189
|
+
const runDirectory = mkdtempSync3(join33(tmpdir(), "skills-execution-"));
|
|
47006
47190
|
const paths = new SkillEntryPaths;
|
|
47007
47191
|
for (const entry of entries) {
|
|
47008
47192
|
paths.add(entry.path, 100, () => {
|
|
@@ -47010,12 +47194,12 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
47010
47194
|
}, () => {
|
|
47011
47195
|
throw new SkillSelectionError("INVALID_BUNDLE_PATH", "The execution bundle path exceeds its limit.");
|
|
47012
47196
|
});
|
|
47013
|
-
const destination =
|
|
47197
|
+
const destination = join33(runDirectory, entry.path);
|
|
47014
47198
|
mkdirSync10(dirname20(destination), { recursive: true, mode: 448 });
|
|
47015
47199
|
writeFileSync9(destination, entry.bytes, { flag: "wx", mode: entry.mode & 511 });
|
|
47016
47200
|
}
|
|
47017
47201
|
env.SKILLS_INPUT_JSON = input;
|
|
47018
|
-
env.SKILLS_WORKSPACE_DIR =
|
|
47202
|
+
env.SKILLS_WORKSPACE_DIR = resolve19(options.cwd ?? process.cwd());
|
|
47019
47203
|
env.SKILLS_RUN_DIR = runDirectory;
|
|
47020
47204
|
if (runtime.version) {
|
|
47021
47205
|
const version = await subprocess([executable, ...runtimeName === "bun" ? ["--no-env-file"] : [], "--version"], runDirectory, env, "", Math.min(timeoutMs, 5000));
|
|
@@ -47028,7 +47212,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
47028
47212
|
const { authority: authority2, workspaceId, profileRevision, slug, version, bundleDigest } = selection2;
|
|
47029
47213
|
const request2 = {
|
|
47030
47214
|
selection: { authority: authority2, workspaceId, profileId, profileRevision, slug, version, bundleDigest },
|
|
47031
|
-
consumer: { stationId: process.env.HASNA_STATION || stationHostname2(), workspaceDirectory:
|
|
47215
|
+
consumer: { stationId: process.env.HASNA_STATION || stationHostname2(), workspaceDirectory: realpathSync12(options.cwd ?? process.cwd()) }
|
|
47032
47216
|
};
|
|
47033
47217
|
const grant = await (options.resolveExecutionGrant ?? resolveExecutionGrant)(request2);
|
|
47034
47218
|
if (!grant || !grantIdentifier(grant.policyRevision) || !grantIdentifier(grant.grantId))
|
|
@@ -47038,7 +47222,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
47038
47222
|
}
|
|
47039
47223
|
if (bindings)
|
|
47040
47224
|
secretEnv = await resolveSelectedSecrets(bindings, options.createSecretsClient);
|
|
47041
|
-
const result = await subprocess([executable, ...runtimeName === "bun" ? ["--no-env-file"] : [],
|
|
47225
|
+
const result = await subprocess([executable, ...runtimeName === "bun" ? ["--no-env-file"] : [], join33(runDirectory, declaredEntry), ...args], runDirectory, { ...env, ...secretEnv }, input, timeoutMs);
|
|
47042
47226
|
result.stdout = redactExecutionSecrets(result.stdout, secretEnv);
|
|
47043
47227
|
result.stderr = redactExecutionSecrets(result.stderr, secretEnv);
|
|
47044
47228
|
const receipt = {
|
|
@@ -47051,7 +47235,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
47051
47235
|
...bindings ? { secretBinding: { schema: bindings.schema, selection: bindings.selection, consumer: bindings.consumer, secretsAuthority: bindings.secretsAuthority, bindings: bindings.bindings } } : {},
|
|
47052
47236
|
...result
|
|
47053
47237
|
};
|
|
47054
|
-
writeFileSync9(
|
|
47238
|
+
writeFileSync9(join33(runDirectory, ".execution-receipt.json"), JSON.stringify({ ...receipt, stdout: undefined, stderr: undefined }), { mode: 384 });
|
|
47055
47239
|
return receipt;
|
|
47056
47240
|
}
|
|
47057
47241
|
async function subprocess(command, cwd, env, input, timeoutMs) {
|
|
@@ -57116,7 +57300,7 @@ import { mkdirSync as mkdirSync12 } from "fs";
|
|
|
57116
57300
|
import { dirname as dirname24 } from "path";
|
|
57117
57301
|
|
|
57118
57302
|
// src/server/database-url.ts
|
|
57119
|
-
import { isAbsolute as isAbsolute19, join as
|
|
57303
|
+
import { isAbsolute as isAbsolute19, join as join35 } from "path";
|
|
57120
57304
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
57121
57305
|
var DEFAULT_SQLITE_FILENAME2 = "server.db";
|
|
57122
57306
|
var SQLITE_MEMORY_PATH = ":memory:";
|
|
@@ -57125,7 +57309,7 @@ var SQLITE_SCHEMES = new Set(["sqlite", "sqlite3", "file"]);
|
|
|
57125
57309
|
var MEMORY_SCHEMES = new Set(["memory"]);
|
|
57126
57310
|
var SQLITE_EXTENSIONS = [".db", ".sqlite", ".sqlite3", ".db3"];
|
|
57127
57311
|
function defaultSqlitePath() {
|
|
57128
|
-
return
|
|
57312
|
+
return join35(getDataDir(), DEFAULT_SQLITE_FILENAME2);
|
|
57129
57313
|
}
|
|
57130
57314
|
function resolveDatabaseTarget(raw) {
|
|
57131
57315
|
const value = raw?.trim();
|
|
@@ -57152,7 +57336,7 @@ function resolveDatabaseTarget(raw) {
|
|
|
57152
57336
|
throw new Error(`unsupported database scheme "${scheme}:". Supported: postgres://, postgresql://, sqlite:, file:, ` + `an absolute or relative path to a .db/.sqlite file, ":memory:", or "memory:" (non-durable, tests only). ` + `Leave the setting empty to use the default SQLite database at ${defaultSqlitePath()}.`);
|
|
57153
57337
|
}
|
|
57154
57338
|
if (looksLikeSqlitePath(value)) {
|
|
57155
|
-
const path = isAbsolute19(value) ? value :
|
|
57339
|
+
const path = isAbsolute19(value) ? value : join35(process.cwd(), value);
|
|
57156
57340
|
return { kind: "sqlite", path, durable: true, label: `sqlite (${path})` };
|
|
57157
57341
|
}
|
|
57158
57342
|
throw new Error(`could not resolve a database backend from "${value}". Use postgres://\u2026, sqlite:\u2026, an absolute or ` + `relative path ending in ${SQLITE_EXTENSIONS.join("/")}, ":memory:", or leave it empty for the ` + `default SQLite database at ${defaultSqlitePath()}.`);
|
|
@@ -57176,7 +57360,7 @@ function sqlitePathFromUrl(value, scheme) {
|
|
|
57176
57360
|
}
|
|
57177
57361
|
return rest.slice(2).replace(/^\/\/+/, "/");
|
|
57178
57362
|
}
|
|
57179
|
-
return isAbsolute19(rest) ? rest :
|
|
57363
|
+
return isAbsolute19(rest) ? rest : join35(process.cwd(), rest);
|
|
57180
57364
|
}
|
|
57181
57365
|
function looksLikeSqlitePath(value) {
|
|
57182
57366
|
if (value.includes("/"))
|
|
@@ -57409,7 +57593,7 @@ class PostgresSkillSelectionStore {
|
|
|
57409
57593
|
// src/server/sqlite-store.ts
|
|
57410
57594
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
57411
57595
|
import { mkdirSync as mkdirSync11, readFileSync as readFileSync23, readdirSync as readdirSync13 } from "fs";
|
|
57412
|
-
import { dirname as dirname23, join as
|
|
57596
|
+
import { dirname as dirname23, join as join37 } from "path";
|
|
57413
57597
|
|
|
57414
57598
|
// src/server/auth.ts
|
|
57415
57599
|
import { createHash as createHash23 } from "crypto";
|
|
@@ -57467,16 +57651,16 @@ function permitsSkillsRoute(principal, method, resource) {
|
|
|
57467
57651
|
}
|
|
57468
57652
|
|
|
57469
57653
|
// src/server/migrations-dir.ts
|
|
57470
|
-
import { existsSync as
|
|
57471
|
-
import { dirname as dirname22, join as
|
|
57654
|
+
import { existsSync as existsSync16 } from "fs";
|
|
57655
|
+
import { dirname as dirname22, join as join36 } from "path";
|
|
57472
57656
|
var MIGRATION_DIALECTS = ["postgres", "sqlite"];
|
|
57473
57657
|
var MAX_WALK_UP = 6;
|
|
57474
57658
|
function findMigrationsRoot(startDirs = defaultStartDirs()) {
|
|
57475
57659
|
for (const start of startDirs) {
|
|
57476
57660
|
let dir = start;
|
|
57477
57661
|
for (let level = 0;level < MAX_WALK_UP; level += 1) {
|
|
57478
|
-
const candidate2 =
|
|
57479
|
-
if (MIGRATION_DIALECTS.some((dialect) =>
|
|
57662
|
+
const candidate2 = join36(dir, "migrations");
|
|
57663
|
+
if (MIGRATION_DIALECTS.some((dialect) => existsSync16(join36(candidate2, dialect))))
|
|
57480
57664
|
return candidate2;
|
|
57481
57665
|
const parent = dirname22(dir);
|
|
57482
57666
|
if (parent === dir)
|
|
@@ -57490,8 +57674,8 @@ function resolveMigrationsDir(dialect, root4 = findMigrationsRoot()) {
|
|
|
57490
57674
|
if (!root4) {
|
|
57491
57675
|
throw new Error(`could not locate the migrations directory for dialect "${dialect}". ` + `Expected a migrations/${dialect}/ folder alongside the package root.`);
|
|
57492
57676
|
}
|
|
57493
|
-
const dir =
|
|
57494
|
-
if (!
|
|
57677
|
+
const dir = join36(root4, dialect);
|
|
57678
|
+
if (!existsSync16(dir)) {
|
|
57495
57679
|
throw new Error(`migrations directory not found: ${dir}`);
|
|
57496
57680
|
}
|
|
57497
57681
|
return dir;
|
|
@@ -58462,7 +58646,7 @@ function applySqliteMigrations(db, migrationsDir = resolveMigrationsDir("sqlite"
|
|
|
58462
58646
|
const appliedNow = [];
|
|
58463
58647
|
for (const file of files) {
|
|
58464
58648
|
const version2 = file.replace(/\.sql$/, "");
|
|
58465
|
-
const text2 = readFileSync23(
|
|
58649
|
+
const text2 = readFileSync23(join37(migrationsDir, file), "utf8");
|
|
58466
58650
|
const apply = db.transaction(() => {
|
|
58467
58651
|
const already = db.query("SELECT 1 AS present FROM schema_migrations WHERE version = ? LIMIT 1").get(version2);
|
|
58468
58652
|
if (already)
|
|
@@ -90719,10 +90903,10 @@ async function readQuoteUnavailableCode(response) {
|
|
|
90719
90903
|
let timer;
|
|
90720
90904
|
let deadlineExceeded = false;
|
|
90721
90905
|
const expired = Symbol("quote body deadline");
|
|
90722
|
-
const deadline = new Promise((
|
|
90906
|
+
const deadline = new Promise((resolve21) => {
|
|
90723
90907
|
timer = setTimeout(() => {
|
|
90724
90908
|
deadlineExceeded = true;
|
|
90725
|
-
|
|
90909
|
+
resolve21(expired);
|
|
90726
90910
|
reader.cancel().catch(() => {});
|
|
90727
90911
|
}, 1500);
|
|
90728
90912
|
});
|
|
@@ -92208,12 +92392,12 @@ class RemotePrivatePublicationsClient {
|
|
|
92208
92392
|
previous = view;
|
|
92209
92393
|
if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
|
|
92210
92394
|
return view;
|
|
92211
|
-
await new Promise((
|
|
92395
|
+
await new Promise((resolve21) => {
|
|
92212
92396
|
const timer = setTimeout(done, Math.min(1000, until - Date.now()));
|
|
92213
92397
|
function done() {
|
|
92214
92398
|
clearTimeout(timer);
|
|
92215
92399
|
options.signal?.removeEventListener("abort", done);
|
|
92216
|
-
|
|
92400
|
+
resolve21();
|
|
92217
92401
|
}
|
|
92218
92402
|
options.signal?.addEventListener("abort", done, { once: true });
|
|
92219
92403
|
});
|
|
@@ -92445,23 +92629,23 @@ class RemoteSkillsAuthClient {
|
|
|
92445
92629
|
}
|
|
92446
92630
|
}
|
|
92447
92631
|
// src/lib/private-publication-recovery.ts
|
|
92448
|
-
import { constants as constants13, closeSync as closeSync17, fsyncSync as fsyncSync5, fstatSync as fstatSync17, lstatSync as
|
|
92449
|
-
import { dirname as dirname26, isAbsolute as isAbsolute20, join as
|
|
92632
|
+
import { constants as constants13, closeSync as closeSync17, fsyncSync as fsyncSync5, fstatSync as fstatSync17, lstatSync as lstatSync24, mkdirSync as mkdirSync14, openSync as openSync17, readSync as readSync12, realpathSync as realpathSync13, renameSync as renameSync6, unlinkSync as unlinkSync5, writeFileSync as writeFileSync10 } from "fs";
|
|
92633
|
+
import { dirname as dirname26, isAbsolute as isAbsolute20, join as join39, resolve as resolve21 } from "path";
|
|
92450
92634
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
92451
92635
|
var fail = () => {
|
|
92452
92636
|
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.");
|
|
92453
92637
|
};
|
|
92454
92638
|
function safeDirectory(directory) {
|
|
92455
|
-
if (!isAbsolute20(directory) || directory !==
|
|
92639
|
+
if (!isAbsolute20(directory) || directory !== resolve21(directory) || realpathSync13(directory) !== directory)
|
|
92456
92640
|
return fail();
|
|
92457
92641
|
for (let path2 = directory;; path2 = dirname26(path2)) {
|
|
92458
|
-
const stat2 =
|
|
92642
|
+
const stat2 = lstatSync24(path2);
|
|
92459
92643
|
if (!stat2.isDirectory() || stat2.isSymbolicLink())
|
|
92460
92644
|
return fail();
|
|
92461
92645
|
if (path2 === dirname26(path2))
|
|
92462
92646
|
break;
|
|
92463
92647
|
}
|
|
92464
|
-
const own2 =
|
|
92648
|
+
const own2 = lstatSync24(directory);
|
|
92465
92649
|
if ((own2.mode & 63) !== 0 || process.getuid && own2.uid !== process.getuid())
|
|
92466
92650
|
return fail();
|
|
92467
92651
|
return { dev: own2.dev, ino: own2.ino };
|
|
@@ -92472,7 +92656,7 @@ function unchangedDirectory(directory, identity4) {
|
|
|
92472
92656
|
return fail();
|
|
92473
92657
|
}
|
|
92474
92658
|
function readOwned(directory, name, max) {
|
|
92475
|
-
const identity4 = safeDirectory(directory), file2 =
|
|
92659
|
+
const identity4 = safeDirectory(directory), file2 = join39(directory, name);
|
|
92476
92660
|
const fd = openSync17(file2, constants13.O_RDONLY | constants13.O_NOFOLLOW);
|
|
92477
92661
|
try {
|
|
92478
92662
|
const stat2 = fstatSync17(fd);
|
|
@@ -92496,7 +92680,7 @@ function readOwned(directory, name, max) {
|
|
|
92496
92680
|
}
|
|
92497
92681
|
}
|
|
92498
92682
|
function writeOwned(directory, name, bytes) {
|
|
92499
|
-
const fd = openSync17(
|
|
92683
|
+
const fd = openSync17(join39(directory, name), constants13.O_WRONLY | constants13.O_CREAT | constants13.O_EXCL | constants13.O_NOFOLLOW, 384);
|
|
92500
92684
|
try {
|
|
92501
92685
|
writeFileSync10(fd, bytes);
|
|
92502
92686
|
fsyncSync5(fd);
|
|
@@ -92509,7 +92693,7 @@ function save(directory, value) {
|
|
|
92509
92693
|
writeOwned(directory, temporary, JSON.stringify(value) + `
|
|
92510
92694
|
`);
|
|
92511
92695
|
unchangedDirectory(directory, identity4);
|
|
92512
|
-
renameSync6(
|
|
92696
|
+
renameSync6(join39(directory, temporary), join39(directory, "receipt.json"));
|
|
92513
92697
|
const fd = openSync17(directory, constants13.O_RDONLY | constants13.O_NOFOLLOW);
|
|
92514
92698
|
try {
|
|
92515
92699
|
fsyncSync5(fd);
|
|
@@ -92522,7 +92706,7 @@ function bind(client2, receipt) {
|
|
|
92522
92706
|
throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
|
|
92523
92707
|
}
|
|
92524
92708
|
async function locked(directory, action) {
|
|
92525
|
-
const identity4 = safeDirectory(directory), file2 =
|
|
92709
|
+
const identity4 = safeDirectory(directory), file2 = join39(directory, "operation.lock");
|
|
92526
92710
|
let fd;
|
|
92527
92711
|
try {
|
|
92528
92712
|
fd = openSync17(file2, constants13.O_WRONLY | constants13.O_CREAT | constants13.O_EXCL | constants13.O_NOFOLLOW, 384);
|
|
@@ -92538,7 +92722,7 @@ async function locked(directory, action) {
|
|
|
92538
92722
|
} finally {
|
|
92539
92723
|
closeSync17(fd);
|
|
92540
92724
|
unchangedDirectory(directory, identity4);
|
|
92541
|
-
const now2 =
|
|
92725
|
+
const now2 = lstatSync24(file2);
|
|
92542
92726
|
if (now2.dev !== lock.dev || now2.ino !== lock.ino || !now2.isFile() || now2.isSymbolicLink())
|
|
92543
92727
|
fail();
|
|
92544
92728
|
unlinkSync5(file2);
|
|
@@ -92590,7 +92774,7 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
|
|
|
92590
92774
|
phase: "prepared",
|
|
92591
92775
|
intent: null
|
|
92592
92776
|
};
|
|
92593
|
-
if (!isAbsolute20(recoveryDirectory) ||
|
|
92777
|
+
if (!isAbsolute20(recoveryDirectory) || resolve21(recoveryDirectory) !== recoveryDirectory || realpathSync13(dirname26(recoveryDirectory)) !== dirname26(recoveryDirectory))
|
|
92594
92778
|
return fail();
|
|
92595
92779
|
mkdirSync14(recoveryDirectory, { mode: 448 });
|
|
92596
92780
|
safeDirectory(recoveryDirectory);
|