@hasna/skills 0.10.28 → 0.10.31
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 +1149 -921
- 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 +684 -471
- 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 +676 -467
- package/docs/plugin-admission.md +21 -0
- 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.31",
|
|
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) {
|
|
@@ -38451,6 +38451,29 @@ function mcpControls(path, read) {
|
|
|
38451
38451
|
refuse3();
|
|
38452
38452
|
return sha2562;
|
|
38453
38453
|
}
|
|
38454
|
+
function isBundledCleanupHooks(root, namespace, value) {
|
|
38455
|
+
const object3 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
|
|
38456
|
+
const exactKeys2 = (v, keys2) => Object.keys(v).length === keys2.length && keys2.every((key) => Object.hasOwn(v, key));
|
|
38457
|
+
if (basename3(dirname5(root)) !== namespace || basename3(dirname5(dirname5(root))) !== "openai-bundled")
|
|
38458
|
+
return false;
|
|
38459
|
+
const server = namespace === "unified-computer-use" ? "cua_repl" : ["browser", "chrome", "chrome-dev", "chrome-internal", "computer-use"].includes(namespace) ? "node_repl" : undefined;
|
|
38460
|
+
if (!server || !object3(value) || !exactKeys2(value, ["hooks"]) || !object3(value.hooks))
|
|
38461
|
+
return false;
|
|
38462
|
+
const events = Object.entries(value.hooks);
|
|
38463
|
+
if (!events.length || events.length > 3)
|
|
38464
|
+
return false;
|
|
38465
|
+
for (const [event, groups] of events) {
|
|
38466
|
+
if (!["Interrupt", "SubagentStop", "Stop"].includes(event) || !Array.isArray(groups) || groups.length !== 1)
|
|
38467
|
+
return false;
|
|
38468
|
+
const group = groups[0];
|
|
38469
|
+
if (!object3(group) || !exactKeys2(group, ["hooks"]) || !Array.isArray(group.hooks) || group.hooks.length !== 1)
|
|
38470
|
+
return false;
|
|
38471
|
+
const handler = group.hooks[0];
|
|
38472
|
+
if (!object3(handler) || !exactKeys2(handler, ["type", "server", "tool", "input"]) || handler.type !== "mcp_tool" || handler.server !== server || handler.tool !== "turn_ended" || !object3(handler.input) || !exactKeys2(handler.input, ["hook_event_name", "session_id", "turn_id"]) || handler.input.hook_event_name !== "${hook_event_name}" || handler.input.turn_id !== "${turn_id}" || handler.input.session_id !== (event === "SubagentStop" ? "${agent_id}" : "${session_id}"))
|
|
38473
|
+
return false;
|
|
38474
|
+
}
|
|
38475
|
+
return true;
|
|
38476
|
+
}
|
|
38454
38477
|
function rootControls(root, read) {
|
|
38455
38478
|
const manifestPath = join10(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync6(manifestPath, { throwIfNoEntry: false });
|
|
38456
38479
|
if (!lstatSync6(join10(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
|
|
@@ -38468,9 +38491,11 @@ function rootControls(root, read) {
|
|
|
38468
38491
|
const cacheVersion = basename3(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
38469
38492
|
if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
38470
38493
|
refuse3();
|
|
38471
|
-
if (manifest.hooks !== undefined || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync6(join10(root, "hooks"), { throwIfNoEntry: false }))
|
|
38494
|
+
if (manifest.hooks !== undefined && !isBundledCleanupHooks(root, manifest.name, manifest.hooks) || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync6(join10(root, "hooks"), { throwIfNoEntry: false }))
|
|
38472
38495
|
refuse3();
|
|
38473
38496
|
const mcp = join10(root, ".mcp.json"), mcpPresent = lstatSync6(mcp, { throwIfNoEntry: false });
|
|
38497
|
+
if (manifest.hooks !== undefined && (manifest.mcpServers !== undefined || mcpPresent))
|
|
38498
|
+
refuse3();
|
|
38474
38499
|
if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync5(mcp) !== mcp))
|
|
38475
38500
|
refuse3();
|
|
38476
38501
|
if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
|
|
@@ -39072,6 +39097,8 @@ var NATIVE_SKILL_ROOTS = [
|
|
|
39072
39097
|
["codex", ".codex/skills"],
|
|
39073
39098
|
["codex", ".agents/skills"],
|
|
39074
39099
|
["gemini", ".gemini/skills"],
|
|
39100
|
+
["sumi", ".sumi/skills"],
|
|
39101
|
+
["sumi", ".sumi/skill"],
|
|
39075
39102
|
["codewith", ".codewith/skills"],
|
|
39076
39103
|
["opencode", ".config/opencode/skills"],
|
|
39077
39104
|
["opencode", ".opencode/skills"],
|
|
@@ -39088,8 +39115,8 @@ var NATIVE_SKILL_ROOTS = [
|
|
|
39088
39115
|
// src/lib/agent-integration.ts
|
|
39089
39116
|
import { createHash as createHash18, randomUUID as randomUUID3 } from "crypto";
|
|
39090
39117
|
import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
|
|
39091
|
-
import { existsSync as
|
|
39092
|
-
import { dirname as dirname15, join as
|
|
39118
|
+
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";
|
|
39119
|
+
import { dirname as dirname15, join as join23, resolve as resolve16, relative as relative4, isAbsolute as isAbsolute17, sep as sep5 } from "path";
|
|
39093
39120
|
import { homedir as homedir3 } from "os";
|
|
39094
39121
|
|
|
39095
39122
|
// src/lib/managed-policy.ts
|
|
@@ -39368,8 +39395,8 @@ function isOwnedCliBridge(path, expectedPaths) {
|
|
|
39368
39395
|
|
|
39369
39396
|
// src/lib/agent-discovery.ts
|
|
39370
39397
|
import { createHash as createHash17 } from "crypto";
|
|
39371
|
-
import { lstatSync as
|
|
39372
|
-
import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute16, join as
|
|
39398
|
+
import { lstatSync as lstatSync19, readFileSync as readFileSync13, readdirSync as readdirSync7, realpathSync as realpathSync7 } from "fs";
|
|
39399
|
+
import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute16, join as join22, resolve as resolve15, sep as sep4 } from "path";
|
|
39373
39400
|
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
39374
39401
|
|
|
39375
39402
|
// node_modules/yaml/dist/index.js
|
|
@@ -41680,6 +41707,114 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
41680
41707
|
return true;
|
|
41681
41708
|
}
|
|
41682
41709
|
|
|
41710
|
+
// src/lib/agent-sumi.ts
|
|
41711
|
+
import { existsSync as existsSync6, lstatSync as lstatSync18, realpathSync as realpathSync6 } from "fs";
|
|
41712
|
+
import { join as join21, resolve as resolve14 } from "path";
|
|
41713
|
+
function sumiConfigDirectory(home, env = process.env) {
|
|
41714
|
+
for (const key of ["SUMI_CONFIG", "SUMI_CONFIG_CONTENT"])
|
|
41715
|
+
if (env[key] !== undefined)
|
|
41716
|
+
throw new Error(`Sumi ${key} requires a dedicated discovery adapter; unset it before Skills integration`);
|
|
41717
|
+
const selected = (key) => env[key]?.trim() ? env[key] : undefined;
|
|
41718
|
+
const expand = (value) => resolve14(value === "~" ? home : value.startsWith("~/") ? join21(home, value.slice(2)) : value);
|
|
41719
|
+
const explicit = selected("SUMI_CONFIG_DIR");
|
|
41720
|
+
const xdg = selected("XDG_CONFIG_HOME");
|
|
41721
|
+
const custom = selected("SUMI_HOME");
|
|
41722
|
+
const canonical6 = explicit ? expand(explicit) : xdg ? join21(expand(xdg), "sumi") : join21(custom ? expand(custom) : join21(home, ".hasna-internal/sumi"), "config");
|
|
41723
|
+
if (explicit || xdg || custom)
|
|
41724
|
+
return canonical6;
|
|
41725
|
+
const legacy = join21(home, ".config/sumi");
|
|
41726
|
+
const a = lstatSync18(canonical6, { throwIfNoEntry: false }), b = lstatSync18(legacy, { throwIfNoEntry: false });
|
|
41727
|
+
if (a && b && realpathSync6(canonical6) !== realpathSync6(legacy))
|
|
41728
|
+
throw new Error("Sumi config roots conflict; complete native home adoption before Skills integration");
|
|
41729
|
+
const path = a ? canonical6 : b ? legacy : canonical6;
|
|
41730
|
+
if (!existsSync6(path))
|
|
41731
|
+
return path;
|
|
41732
|
+
const physical = realpathSync6(path);
|
|
41733
|
+
if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![canonical6, legacy].includes(physical))
|
|
41734
|
+
throw new Error("Sumi config alias must resolve to its canonical or legacy root");
|
|
41735
|
+
return physical;
|
|
41736
|
+
}
|
|
41737
|
+
function sumiBridgeRoot(home) {
|
|
41738
|
+
return join21(sumiConfigDirectory(home), "skills");
|
|
41739
|
+
}
|
|
41740
|
+
function sumiConfigPath(home) {
|
|
41741
|
+
return join21(sumiConfigDirectory(home), "sumi.json");
|
|
41742
|
+
}
|
|
41743
|
+
function renderSumiPlugin(command, profile) {
|
|
41744
|
+
return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
|
|
41745
|
+
import { spawn } from "node:child_process";
|
|
41746
|
+
const command = ${JSON.stringify(command)};
|
|
41747
|
+
const profile = ${JSON.stringify(profile)};
|
|
41748
|
+
function context(input) {
|
|
41749
|
+
return new Promise((resolve, reject) => {
|
|
41750
|
+
const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
41751
|
+
const chunks = []; let bytes = 0, failure;
|
|
41752
|
+
const timer = setTimeout(() => { failure = new Error("Skills prompt hook timed out"); child.kill("SIGKILL"); }, 15000);
|
|
41753
|
+
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); });
|
|
41754
|
+
child.stderr.on("data", () => {});
|
|
41755
|
+
child.stdin.on("error", () => {});
|
|
41756
|
+
child.on("error", error => { clearTimeout(timer); reject(error); });
|
|
41757
|
+
child.on("close", code => {
|
|
41758
|
+
clearTimeout(timer);
|
|
41759
|
+
if (failure || code !== 0) return reject(failure ?? new Error("Skills prompt hook failed"));
|
|
41760
|
+
try {
|
|
41761
|
+
const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
41762
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) throw new Error("Invalid Skills hook response");
|
|
41763
|
+
if (result.decision === "block" || result.continue === false) throw new Error("Skills prompt hook refused");
|
|
41764
|
+
if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw new Error("Invalid Skills hook response");
|
|
41765
|
+
const text = result.hookSpecificOutput?.additionalContext ?? "";
|
|
41766
|
+
if (typeof text !== "string") throw new Error("Invalid Skills prompt context");
|
|
41767
|
+
resolve(text);
|
|
41768
|
+
} catch (error) { reject(error); }
|
|
41769
|
+
});
|
|
41770
|
+
child.stdin.end(JSON.stringify(input));
|
|
41771
|
+
});
|
|
41772
|
+
}
|
|
41773
|
+
export default {
|
|
41774
|
+
id: "local.hasna-skills-cli",
|
|
41775
|
+
async setup(ctx) {
|
|
41776
|
+
if (!ctx?.session?.hook || !ctx?.session?.get || !ctx?.skill?.transform || !ctx?.tool?.hook) throw new Error("Unsupported Sumi native Skills contract");
|
|
41777
|
+
const registrations = [];
|
|
41778
|
+
const sessions = new Set();
|
|
41779
|
+
async function input(sessionID, prompt, first) {
|
|
41780
|
+
// The Promise adapter unwraps single-data endpoint responses.
|
|
41781
|
+
const session = await ctx.session.get({ sessionID });
|
|
41782
|
+
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");
|
|
41783
|
+
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 };
|
|
41784
|
+
}
|
|
41785
|
+
registrations.push(await ctx.skill.transform(editor => {
|
|
41786
|
+
for (const skill of editor.list()) if (skill.id !== "skills-cli" || skill.name !== "skills-cli") editor.remove(skill.id);
|
|
41787
|
+
}));
|
|
41788
|
+
// Native config transforms may add skills after this transform. Guard
|
|
41789
|
+
// both direct tool calls and prompt attachments at the consumption edge.
|
|
41790
|
+
registrations.push(await ctx.tool.hook("execute.before", event => {
|
|
41791
|
+
if (event.tool === "skill" && event.input?.id !== "skills-cli") throw new Error("Native Sumi skill payload refused; use Skills CLI");
|
|
41792
|
+
}));
|
|
41793
|
+
registrations.push(await ctx.session.hook("prompt", async event => {
|
|
41794
|
+
if (typeof event.prompt?.text !== "string") throw new Error("Invalid Sumi prompt");
|
|
41795
|
+
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");
|
|
41796
|
+
await context(await input(event.sessionID, event.prompt.text, !sessions.has(event.sessionID)));
|
|
41797
|
+
sessions.add(event.sessionID);
|
|
41798
|
+
}));
|
|
41799
|
+
registrations.push(await ctx.session.hook("context", async event => {
|
|
41800
|
+
const last = event.messages.findLast(message => message.role === "user");
|
|
41801
|
+
const prompt = (last?.content ?? []).filter(part => part.type === "text").map(part => part.text).join("\\n");
|
|
41802
|
+
// Recheck on every primary request, including resumed and synthetic
|
|
41803
|
+
// sessions. The CLI owns optional failures versus explicit refusals.
|
|
41804
|
+
const text = await context(await input(event.sessionID, prompt, !sessions.has(event.sessionID)));
|
|
41805
|
+
if (text) event.system.push({ type: "text", text });
|
|
41806
|
+
sessions.add(event.sessionID);
|
|
41807
|
+
}));
|
|
41808
|
+
return async () => { for (const registration of registrations) await registration.dispose(); };
|
|
41809
|
+
},
|
|
41810
|
+
};
|
|
41811
|
+
`;
|
|
41812
|
+
}
|
|
41813
|
+
var SUMI_SKILL_PERMISSIONS = [
|
|
41814
|
+
{ action: "skill", resource: "*", effect: "deny" },
|
|
41815
|
+
{ action: "skill", resource: "skills-cli", effect: "allow" }
|
|
41816
|
+
];
|
|
41817
|
+
|
|
41683
41818
|
// src/lib/agent-discovery.ts
|
|
41684
41819
|
var digest2 = (text2) => createHash17("sha256").update(text2).digest("hex");
|
|
41685
41820
|
function parseConfig(text2, path, toml = false) {
|
|
@@ -41696,7 +41831,7 @@ function safe4(path) {
|
|
|
41696
41831
|
if (!isAbsolute16(path) || path.includes("\x00"))
|
|
41697
41832
|
throw new Error("Expected an absolute native discovery path");
|
|
41698
41833
|
for (let cursor = path;; cursor = dirname14(cursor)) {
|
|
41699
|
-
if (
|
|
41834
|
+
if (lstatSync19(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
41700
41835
|
throw new Error(`Refusing symlink native discovery input: ${cursor}`);
|
|
41701
41836
|
if (dirname14(cursor) === cursor)
|
|
41702
41837
|
break;
|
|
@@ -41706,7 +41841,7 @@ function read2(path, changes) {
|
|
|
41706
41841
|
safe4(path);
|
|
41707
41842
|
if (changes?.has(path))
|
|
41708
41843
|
return changes.get(path);
|
|
41709
|
-
const stat2 =
|
|
41844
|
+
const stat2 = lstatSync19(path, { throwIfNoEntry: false });
|
|
41710
41845
|
if (!stat2)
|
|
41711
41846
|
return null;
|
|
41712
41847
|
if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
|
|
@@ -41817,12 +41952,12 @@ function codexInstallationRoots(binding) {
|
|
|
41817
41952
|
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
41953
|
throw new Error("Invalid native installation input proof");
|
|
41819
41954
|
const cache = dirname14(dirname14(proof.plugins[0].pluginParent));
|
|
41820
|
-
const config =
|
|
41955
|
+
const config = join22(dirname14(dirname14(cache)), "config.toml"), home = dirname14(dirname14(dirname14(cache)));
|
|
41821
41956
|
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]) =>
|
|
41957
|
+
const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join22(home, path));
|
|
41823
41958
|
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
41959
|
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 ===
|
|
41960
|
+
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
41961
|
throw new Error("Missing native installation input source witness");
|
|
41827
41962
|
return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
|
|
41828
41963
|
const text2 = read2(path);
|
|
@@ -41864,19 +41999,19 @@ function captureDiscoveryPathSources(paths) {
|
|
|
41864
41999
|
const budget = discoveryByteBudget();
|
|
41865
42000
|
return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget) }));
|
|
41866
42001
|
}
|
|
41867
|
-
function assertProjectDiscovery(agent, directories, home, canonical6 =
|
|
42002
|
+
function assertProjectDiscovery(agent, directories, home, canonical6 = resolve15, reviewed) {
|
|
41868
42003
|
const homeConfigPath = canonical6(agentDiscoveryConfigPath(home, agent));
|
|
41869
42004
|
for (const directory of directories) {
|
|
41870
|
-
const isHome =
|
|
42005
|
+
const isHome = resolve15(directory) === resolve15(home);
|
|
41871
42006
|
if (agent === "hermes") {
|
|
41872
42007
|
assertHermesEnvironment(home);
|
|
41873
42008
|
continue;
|
|
41874
42009
|
}
|
|
41875
|
-
if (isHome && agent !== "claude")
|
|
42010
|
+
if (isHome && agent !== "claude" && agent !== "sumi")
|
|
41876
42011
|
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"];
|
|
42012
|
+
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
42013
|
for (const suffix of paths) {
|
|
41879
|
-
const path = canonical6(
|
|
42014
|
+
const path = canonical6(join22(directory, suffix)), raw = read2(path);
|
|
41880
42015
|
if (raw === null)
|
|
41881
42016
|
continue;
|
|
41882
42017
|
const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
|
|
@@ -41885,7 +42020,7 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve14
|
|
|
41885
42020
|
if (suffix.endsWith(".jsonc"))
|
|
41886
42021
|
throw new Error(`NATIVE_SKILL_DRIFT: project JSONC discovery configuration requires review: ${path}`);
|
|
41887
42022
|
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"];
|
|
42023
|
+
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
42024
|
let admittedPlugins = false;
|
|
41890
42025
|
if (agent === "claude" && reviewed?.agent === agent && reviewed.method === "reviewed" && config.enabledPlugins && typeof config.enabledPlugins === "object" && !Array.isArray(config.enabledPlugins)) {
|
|
41891
42026
|
const exact = reviewed.sources.find((source) => source.path === path && source.format === undefined && source.fields === undefined && (source.hashMode === undefined || source.hashMode === "bytes"));
|
|
@@ -41898,19 +42033,28 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve14
|
|
|
41898
42033
|
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
42034
|
throw new Error(`NATIVE_SKILL_DRIFT: higher-precedence project skill or hook configuration requires review: ${path}`);
|
|
41900
42035
|
}
|
|
42036
|
+
if (agent === "sumi")
|
|
42037
|
+
for (const name of ["plugin", "plugins"]) {
|
|
42038
|
+
const path = canonical6(join22(directory, ".sumi", name));
|
|
42039
|
+
safe4(path);
|
|
42040
|
+
if (lstatSync19(path, { throwIfNoEntry: false })?.isDirectory() && readdirSync7(path).length)
|
|
42041
|
+
throw new Error(`NATIVE_SKILL_DRIFT: project Sumi plugins require a dedicated discovery review: ${path}`);
|
|
42042
|
+
}
|
|
41901
42043
|
if (agent === "claude") {
|
|
41902
|
-
const commands = canonical6(
|
|
42044
|
+
const commands = canonical6(join22(directory, ".claude/commands"));
|
|
41903
42045
|
safe4(commands);
|
|
41904
|
-
if (
|
|
42046
|
+
if (lstatSync19(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync7(commands).length)
|
|
41905
42047
|
throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
|
|
41906
42048
|
}
|
|
41907
42049
|
}
|
|
41908
42050
|
}
|
|
41909
42051
|
function agentDiscoveryConfigPath(home, agent) {
|
|
41910
|
-
|
|
42052
|
+
if (agent === "sumi")
|
|
42053
|
+
return sumiConfigPath(home);
|
|
42054
|
+
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
42055
|
}
|
|
41912
42056
|
function resolveAgentDiscovery(options) {
|
|
41913
|
-
const canonical6 = options.canonical ??
|
|
42057
|
+
const canonical6 = options.canonical ?? resolve15, home = resolve15(options.home), agent = options.agent;
|
|
41914
42058
|
const sources = [], roots = new Set, builtinNames = [];
|
|
41915
42059
|
const witness = (path, format, fields2) => {
|
|
41916
42060
|
const source = { path: canonical6(path), sha256: null, ...format ? { format, fields: fields2 } : {} };
|
|
@@ -41921,13 +42065,49 @@ function resolveAgentDiscovery(options) {
|
|
|
41921
42065
|
if (agent === "hermes")
|
|
41922
42066
|
assertHermesEnvironment(home);
|
|
41923
42067
|
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"]);
|
|
42068
|
+
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
42069
|
const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
|
|
41926
42070
|
const unresolved = (detail) => {
|
|
41927
42071
|
throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
|
|
41928
42072
|
};
|
|
42073
|
+
if (agent === "sumi") {
|
|
42074
|
+
const directory = sumiConfigDirectory(home);
|
|
42075
|
+
if (witness(join22(directory, "sumi.jsonc")) !== null)
|
|
42076
|
+
unresolved("JSONC configuration requires a supported format adapter");
|
|
42077
|
+
for (const root2 of [directory, join22(home, ".claude"), join22(home, ".agents")])
|
|
42078
|
+
for (const name of ["skill", "skills"])
|
|
42079
|
+
roots.add(join22(root2, name));
|
|
42080
|
+
if (config.skills !== undefined && (!Array.isArray(config.skills) || config.skills.some((path) => typeof path !== "string")))
|
|
42081
|
+
unresolved("skills must be a string array");
|
|
42082
|
+
for (const value of config.skills ?? []) {
|
|
42083
|
+
if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(isAbsolute16(value) || value.startsWith("~/")))
|
|
42084
|
+
unresolved("remote or relative skill sources need a dedicated discovery adapter");
|
|
42085
|
+
roots.add(value.startsWith("~/") ? join22(home, value.slice(2)) : value);
|
|
42086
|
+
}
|
|
42087
|
+
for (const key of ["plugin", "plugins"])
|
|
42088
|
+
if (config[key] !== undefined && !Array.isArray(config[key]))
|
|
42089
|
+
unresolved(`${key} must be an array`);
|
|
42090
|
+
const plugins = [join22(directory, "plugin"), join22(directory, "plugins")];
|
|
42091
|
+
for (const path of plugins) {
|
|
42092
|
+
safe4(path);
|
|
42093
|
+
if (lstatSync19(path, { throwIfNoEntry: false })?.isDirectory())
|
|
42094
|
+
for (const name of readdirSync7(path).sort()) {
|
|
42095
|
+
if (name === "skills-cli.js" && path === plugins[1])
|
|
42096
|
+
continue;
|
|
42097
|
+
const target = join22(path, name);
|
|
42098
|
+
if (!lstatSync19(target).isFile())
|
|
42099
|
+
unresolved("local plugin packages need a dedicated discovery adapter");
|
|
42100
|
+
witness(target);
|
|
42101
|
+
}
|
|
42102
|
+
}
|
|
42103
|
+
for (const root2 of roots) {
|
|
42104
|
+
safe4(root2);
|
|
42105
|
+
if (lstatSync19(root2, { throwIfNoEntry: false })?.isDirectory() && readdirSync7(root2).some((name) => name.endsWith(".md")))
|
|
42106
|
+
unresolved("flat markdown skills require a dedicated native migration adapter");
|
|
42107
|
+
}
|
|
42108
|
+
}
|
|
41929
42109
|
if (agent === "hermes") {
|
|
41930
|
-
const profile = witness(
|
|
42110
|
+
const profile = witness(join22(home, ".hermes/active_profile"));
|
|
41931
42111
|
if (profile?.trim() && profile.trim() !== "default")
|
|
41932
42112
|
unresolved("a non-default profile is active");
|
|
41933
42113
|
const declared = config.skills?.external_dirs ?? [], paths = typeof declared === "string" ? [declared] : declared;
|
|
@@ -41941,7 +42121,7 @@ function resolveAgentDiscovery(options) {
|
|
|
41941
42121
|
continue;
|
|
41942
42122
|
if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
|
|
41943
42123
|
unresolved("user-specific tilde expansion requires a separate discovery adapter");
|
|
41944
|
-
const path = normalized === "~" ? home : normalized.startsWith("~/") ?
|
|
42124
|
+
const path = normalized === "~" ? home : normalized.startsWith("~/") ? join22(home, normalized.slice(2)) : resolve15(home, ".hermes", normalized);
|
|
41945
42125
|
safe4(path);
|
|
41946
42126
|
roots.add(path);
|
|
41947
42127
|
}
|
|
@@ -41950,8 +42130,8 @@ function resolveAgentDiscovery(options) {
|
|
|
41950
42130
|
const executable = Bun.which("gemini");
|
|
41951
42131
|
if (executable) {
|
|
41952
42132
|
let packageRoot;
|
|
41953
|
-
for (let directory = dirname14(
|
|
41954
|
-
const path =
|
|
42133
|
+
for (let directory = dirname14(realpathSync7(executable)), depth = 0;depth < 8; directory = dirname14(directory), depth++) {
|
|
42134
|
+
const path = join22(directory, "package.json"), raw = read2(path);
|
|
41955
42135
|
if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
|
|
41956
42136
|
packageRoot = directory;
|
|
41957
42137
|
witness(path);
|
|
@@ -41962,12 +42142,12 @@ function resolveAgentDiscovery(options) {
|
|
|
41962
42142
|
}
|
|
41963
42143
|
if (!packageRoot)
|
|
41964
42144
|
unresolved("installed Gemini package layout is not recognized");
|
|
41965
|
-
const builtinRoot =
|
|
42145
|
+
const builtinRoot = join22(packageRoot, "bundle/builtin");
|
|
41966
42146
|
safe4(builtinRoot);
|
|
41967
|
-
if (!
|
|
42147
|
+
if (!lstatSync19(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
|
|
41968
42148
|
unresolved("installed Gemini builtin root is missing");
|
|
41969
42149
|
for (const name of readdirSync7(builtinRoot).sort()) {
|
|
41970
|
-
const text2 = witness(
|
|
42150
|
+
const text2 = witness(join22(builtinRoot, name, "SKILL.md"));
|
|
41971
42151
|
const match = text2?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
|
|
41972
42152
|
if (!match?.[1] || builtinNames.includes(match[1]))
|
|
41973
42153
|
unresolved("installed Gemini builtin name is missing or ambiguous");
|
|
@@ -42009,39 +42189,39 @@ function resolveAgentDiscovery(options) {
|
|
|
42009
42189
|
function plugin(root2) {
|
|
42010
42190
|
root2 = canonical6(root2);
|
|
42011
42191
|
safe4(root2);
|
|
42012
|
-
const manifestPath =
|
|
42192
|
+
const manifestPath = join22(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
42013
42193
|
const raw = witness(manifestPath);
|
|
42014
42194
|
if (raw === null)
|
|
42015
42195
|
unresolved("plugin manifest missing");
|
|
42016
42196
|
const manifest = parseConfig(raw, manifestPath);
|
|
42017
|
-
const hooks = witness(
|
|
42197
|
+
const hooks = witness(join22(root2, "hooks/hooks.json"));
|
|
42018
42198
|
if (manifest.hooks !== undefined || hooks !== null)
|
|
42019
42199
|
unresolved("plugin hooks require a separate no-skill-injection review");
|
|
42020
|
-
const commands =
|
|
42200
|
+
const commands = join22(root2, "commands");
|
|
42021
42201
|
safe4(commands);
|
|
42022
|
-
if (agent === "claude" && (manifest.commands !== undefined ||
|
|
42202
|
+
if (agent === "claude" && (manifest.commands !== undefined || lstatSync19(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync7(commands).length))
|
|
42023
42203
|
unresolved("legacy command discovery requires retirement or a dedicated format adapter");
|
|
42024
|
-
const rootSkill = witness(
|
|
42204
|
+
const rootSkill = witness(join22(root2, "SKILL.md"));
|
|
42025
42205
|
if (rootSkill !== null)
|
|
42026
42206
|
roots.add(root2);
|
|
42027
42207
|
const declared = manifest.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
|
|
42028
42208
|
if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
|
|
42029
42209
|
unresolved("unsupported plugin skill paths");
|
|
42030
42210
|
if (agent === "claude")
|
|
42031
|
-
roots.add(
|
|
42211
|
+
roots.add(join22(root2, "skills"));
|
|
42032
42212
|
for (const value of declared) {
|
|
42033
|
-
const path =
|
|
42213
|
+
const path = resolve15(root2, value);
|
|
42034
42214
|
if (path !== root2 && !path.startsWith(root2 + sep4))
|
|
42035
42215
|
unresolved("plugin skill path escapes its root");
|
|
42036
42216
|
roots.add(path.endsWith(`${sep4}SKILL.md`) ? dirname14(path) : path);
|
|
42037
42217
|
}
|
|
42038
42218
|
}
|
|
42039
42219
|
if (agent === "claude") {
|
|
42040
|
-
const registrations = witness(
|
|
42041
|
-
const installed = registrations === null ? {} : parseConfig(registrations,
|
|
42042
|
-
const commands = canonical6(
|
|
42220
|
+
const registrations = witness(join22(home, ".claude/plugins/installed_plugins.json"));
|
|
42221
|
+
const installed = registrations === null ? {} : parseConfig(registrations, join22(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
42222
|
+
const commands = canonical6(join22(home, ".claude/commands"));
|
|
42043
42223
|
safe4(commands);
|
|
42044
|
-
if (
|
|
42224
|
+
if (lstatSync19(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync7(commands).length)
|
|
42045
42225
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
42046
42226
|
for (const [id2, enabled] of Object.entries(config.enabledPlugins ?? {})) {
|
|
42047
42227
|
if (enabled === false)
|
|
@@ -42063,16 +42243,16 @@ function resolveAgentDiscovery(options) {
|
|
|
42063
42243
|
const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
|
|
42064
42244
|
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute16(market.source))
|
|
42065
42245
|
unresolved("enabled plugin marketplace is not an explicit local source");
|
|
42066
|
-
const catalogText = witness(
|
|
42246
|
+
const catalogText = witness(join22(market.source, ".agents/plugins/marketplace.json"));
|
|
42067
42247
|
if (catalogText === null)
|
|
42068
42248
|
unresolved("marketplace catalog missing");
|
|
42069
|
-
const catalog = parseConfig(catalogText,
|
|
42249
|
+
const catalog = parseConfig(catalogText, join22(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
|
|
42070
42250
|
if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
|
|
42071
42251
|
unresolved("plugin catalog source is missing or ambiguous");
|
|
42072
|
-
plugin(
|
|
42252
|
+
plugin(resolve15(market.source, matches[0].source.path));
|
|
42073
42253
|
}
|
|
42074
42254
|
} else if (agent === "opencode") {
|
|
42075
|
-
if (witness(
|
|
42255
|
+
if (witness(join22(home, ".config/opencode/opencode.jsonc")) !== null || witness(join22(home, ".config/opencode/config.json")) !== null)
|
|
42076
42256
|
unresolved("additional config layers require review");
|
|
42077
42257
|
if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
|
|
42078
42258
|
unresolved("external plugin or remote skill source");
|
|
@@ -42081,14 +42261,17 @@ function resolveAgentDiscovery(options) {
|
|
|
42081
42261
|
unresolved("relative or malformed added skill root");
|
|
42082
42262
|
roots.add(path);
|
|
42083
42263
|
}
|
|
42084
|
-
const localPlugins =
|
|
42264
|
+
const localPlugins = join22(home, ".config/opencode/plugins");
|
|
42085
42265
|
safe4(localPlugins);
|
|
42086
|
-
if (
|
|
42266
|
+
if (lstatSync19(localPlugins, { throwIfNoEntry: false }) && readdirSync7(localPlugins).some((name) => name !== "skills-cli.js"))
|
|
42087
42267
|
unresolved("additional local plugin hooks require review");
|
|
42268
|
+
} else if (agent === "sumi") {
|
|
42269
|
+
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))
|
|
42270
|
+
unresolved("external or foreign local plugins require review");
|
|
42088
42271
|
} else if (agent === "hermes") {
|
|
42089
|
-
for (const directory of [
|
|
42272
|
+
for (const directory of [join22(home, ".hermes/plugins"), join22(home, ".hermes/hermes-agent")]) {
|
|
42090
42273
|
safe4(directory);
|
|
42091
|
-
if (
|
|
42274
|
+
if (lstatSync19(directory, { throwIfNoEntry: false }))
|
|
42092
42275
|
unresolved("installed runtime/plugin discovery requires reviewed source bindings");
|
|
42093
42276
|
}
|
|
42094
42277
|
if (Bun.which("hermes") || Object.keys(config.plugins ?? {}).length)
|
|
@@ -42096,28 +42279,28 @@ function resolveAgentDiscovery(options) {
|
|
|
42096
42279
|
} else if (agent === "gemini") {
|
|
42097
42280
|
if (Object.keys(config.extensions ?? {}).length || config.skills?.paths?.length)
|
|
42098
42281
|
unresolved("custom extension or skill paths");
|
|
42099
|
-
const directory =
|
|
42282
|
+
const directory = join22(home, ".gemini/extensions");
|
|
42100
42283
|
safe4(directory);
|
|
42101
|
-
if (
|
|
42284
|
+
if (lstatSync19(directory, { throwIfNoEntry: false }))
|
|
42102
42285
|
for (const name of readdirSync7(directory)) {
|
|
42103
|
-
const root2 =
|
|
42286
|
+
const root2 = join22(directory, name);
|
|
42104
42287
|
safe4(root2);
|
|
42105
|
-
if (!
|
|
42288
|
+
if (!lstatSync19(root2)?.isDirectory())
|
|
42106
42289
|
continue;
|
|
42107
|
-
const raw = witness(
|
|
42290
|
+
const raw = witness(join22(root2, "gemini-extension.json"));
|
|
42108
42291
|
if (raw === null)
|
|
42109
42292
|
unresolved("extension registration missing");
|
|
42110
|
-
const manifest = parseConfig(raw,
|
|
42111
|
-
if (manifest.hooks !== undefined || witness(
|
|
42293
|
+
const manifest = parseConfig(raw, join22(root2, "gemini-extension.json"));
|
|
42294
|
+
if (manifest.hooks !== undefined || witness(join22(root2, "hooks/hooks.json")) !== null || witness(join22(root2, ".gemini-extension-install.json")) !== null)
|
|
42112
42295
|
unresolved("extension hooks or linked registration require review");
|
|
42113
|
-
roots.add(
|
|
42296
|
+
roots.add(join22(root2, "skills"));
|
|
42114
42297
|
}
|
|
42115
42298
|
}
|
|
42116
|
-
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([
|
|
42299
|
+
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
42300
|
}
|
|
42118
42301
|
|
|
42119
42302
|
// src/lib/agent-adapters.ts
|
|
42120
|
-
var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes"];
|
|
42303
|
+
var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes", "sumi"];
|
|
42121
42304
|
function renderAgentHookCommand(command, agent, profileId, event) {
|
|
42122
42305
|
if (typeof profileId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(profileId) || profileId.includes(".."))
|
|
42123
42306
|
throw new Error("Invalid selection profile id");
|
|
@@ -42128,6 +42311,7 @@ var AGENT_ADAPTERS = {
|
|
|
42128
42311
|
codex: { root: ".codex/skills", config: ".codex/hooks.json", events: ["UserPromptSubmit", "SessionStart", "SubagentStart"], promptContext: true },
|
|
42129
42312
|
gemini: { root: ".gemini/skills", config: ".gemini/settings.json", events: ["BeforeAgent", "SessionStart"], promptContext: true },
|
|
42130
42313
|
opencode: { root: ".config/opencode/skills", config: ".config/opencode/opencode.json", events: ["chat.message"], promptContext: true },
|
|
42314
|
+
sumi: { root: ".hasna-internal/sumi/config/skills", config: ".hasna-internal/sumi/config/sumi.json", events: ["prompt", "context"], promptContext: true },
|
|
42131
42315
|
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
42316
|
cursor: { root: ".cursor/skills", config: ".cursor/hooks.json", events: ["beforeSubmitPrompt", "sessionStart"], promptContext: false }
|
|
42133
42317
|
};
|
|
@@ -42484,11 +42668,11 @@ function isSystemRootAlias(path) {
|
|
|
42484
42668
|
const target = `/private${path}`, link = `private${path}`;
|
|
42485
42669
|
const identity4 = (stat2) => `${stat2.dev}:${stat2.ino}:${stat2.ctimeNs}:${stat2.uid}:${stat2.mode}`;
|
|
42486
42670
|
try {
|
|
42487
|
-
const before =
|
|
42671
|
+
const before = lstatSync20(path, { bigint: true });
|
|
42488
42672
|
if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync4(path) !== link)
|
|
42489
42673
|
return false;
|
|
42490
42674
|
const directories = ["/", "/private", target].map((directory) => {
|
|
42491
|
-
const stat2 =
|
|
42675
|
+
const stat2 = lstatSync20(directory, { bigint: true });
|
|
42492
42676
|
if (!stat2.isDirectory() || stat2.uid !== 0n)
|
|
42493
42677
|
throw new Error("Untrusted system alias directory");
|
|
42494
42678
|
if (directory !== "/private/tmp") {
|
|
@@ -42505,23 +42689,23 @@ function isSystemRootAlias(path) {
|
|
|
42505
42689
|
}
|
|
42506
42690
|
return { directory, identity: identity4(stat2) };
|
|
42507
42691
|
});
|
|
42508
|
-
return
|
|
42692
|
+
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
42693
|
} catch {
|
|
42510
42694
|
return false;
|
|
42511
42695
|
}
|
|
42512
42696
|
}
|
|
42513
42697
|
function canonicalSystemPath(path) {
|
|
42514
|
-
const absolute =
|
|
42515
|
-
if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root2) || !
|
|
42698
|
+
const absolute = resolve16(path), root2 = `/${absolute.split(sep5)[1]}`;
|
|
42699
|
+
if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root2) || !lstatSync20(root2, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
42516
42700
|
return absolute;
|
|
42517
42701
|
if (!isSystemRootAlias(root2))
|
|
42518
42702
|
throw new Error(`Refusing symlink path: ${root2}`);
|
|
42519
42703
|
return `/private${absolute}`;
|
|
42520
42704
|
}
|
|
42521
42705
|
function assertSafePath(path) {
|
|
42522
|
-
let cursor =
|
|
42706
|
+
let cursor = resolve16(path);
|
|
42523
42707
|
while (true) {
|
|
42524
|
-
if (
|
|
42708
|
+
if (lstatSync20(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
|
|
42525
42709
|
throw new Error(`Refusing symlink path: ${cursor}`);
|
|
42526
42710
|
const parent = dirname15(cursor);
|
|
42527
42711
|
if (parent === cursor)
|
|
@@ -42531,16 +42715,16 @@ function assertSafePath(path) {
|
|
|
42531
42715
|
}
|
|
42532
42716
|
function readRootAlias(home, agent) {
|
|
42533
42717
|
assertSafePath(home);
|
|
42534
|
-
const realHome =
|
|
42535
|
-
const stat2 =
|
|
42718
|
+
const realHome = realpathSync8(home), alias = join23(realHome, `.${agent}`);
|
|
42719
|
+
const stat2 = lstatSync20(alias, { throwIfNoEntry: false, bigint: true });
|
|
42536
42720
|
if (!stat2?.isSymbolicLink())
|
|
42537
42721
|
throw new Error(`Agent root alias changed after planning: ${alias}`);
|
|
42538
|
-
const link = readlinkSync4(alias), targetPath =
|
|
42722
|
+
const link = readlinkSync4(alias), targetPath = resolve16(dirname15(alias), link);
|
|
42539
42723
|
assertSafePath(targetPath);
|
|
42540
|
-
const targetStat =
|
|
42724
|
+
const targetStat = lstatSync20(targetPath, { throwIfNoEntry: false });
|
|
42541
42725
|
if (!targetStat?.isDirectory())
|
|
42542
42726
|
throw new Error(`Agent root alias must target an existing directory: ${alias}`);
|
|
42543
|
-
const target =
|
|
42727
|
+
const target = realpathSync8(targetPath), inside = relative4(realHome, target);
|
|
42544
42728
|
if (!inside || inside === ".." || inside.startsWith(`..${sep5}`) || isAbsolute17(inside))
|
|
42545
42729
|
throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
|
|
42546
42730
|
return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat2.dev}:${stat2.ino}:${stat2.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
|
|
@@ -42550,7 +42734,7 @@ function rootAliases(home, allowed = false) {
|
|
|
42550
42734
|
return [];
|
|
42551
42735
|
const result = [];
|
|
42552
42736
|
for (const agent of ["claude", "codex"])
|
|
42553
|
-
if (
|
|
42737
|
+
if (lstatSync20(join23(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
|
|
42554
42738
|
result.push(readRootAlias(home, agent));
|
|
42555
42739
|
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
42740
|
throw new Error("Agent root alias targets must not overlap");
|
|
@@ -42558,7 +42742,7 @@ function rootAliases(home, allowed = false) {
|
|
|
42558
42742
|
}
|
|
42559
42743
|
function canonicalAgentPath(path, aliases) {
|
|
42560
42744
|
const absolute = canonicalSystemPath(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep5));
|
|
42561
|
-
return binding ?
|
|
42745
|
+
return binding ? join23(binding.target, relative4(binding.alias, absolute)) : absolute;
|
|
42562
42746
|
}
|
|
42563
42747
|
function disabledCodexSkillPaths(config, aliases) {
|
|
42564
42748
|
const skills = config.skills;
|
|
@@ -42577,7 +42761,7 @@ function disabledCodexSkillPaths(config, aliases) {
|
|
|
42577
42761
|
continue;
|
|
42578
42762
|
let document2;
|
|
42579
42763
|
try {
|
|
42580
|
-
document2 =
|
|
42764
|
+
document2 = realpathSync8(canonicalAgentPath(entry.path, aliases));
|
|
42581
42765
|
} catch {
|
|
42582
42766
|
continue;
|
|
42583
42767
|
}
|
|
@@ -42589,7 +42773,7 @@ function disabledCodexSkillPaths(config, aliases) {
|
|
|
42589
42773
|
}
|
|
42590
42774
|
function recheckRootAliases(aliases) {
|
|
42591
42775
|
for (const binding of aliases) {
|
|
42592
|
-
if (!["claude", "codex"].includes(binding.agent) || binding.alias !==
|
|
42776
|
+
if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join23(binding.home, `.${binding.agent}`))
|
|
42593
42777
|
throw new Error("Invalid agent root alias binding");
|
|
42594
42778
|
const current = readRootAlias(binding.home, binding.agent);
|
|
42595
42779
|
if (JSON.stringify(current) !== JSON.stringify(binding))
|
|
@@ -42598,7 +42782,7 @@ function recheckRootAliases(aliases) {
|
|
|
42598
42782
|
}
|
|
42599
42783
|
function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
42600
42784
|
assertSafePath(path);
|
|
42601
|
-
const before =
|
|
42785
|
+
const before = lstatSync20(path);
|
|
42602
42786
|
if (!before.isFile() || before.size > maximum)
|
|
42603
42787
|
throw new Error("Unsupported or oversized native skill file");
|
|
42604
42788
|
const descriptor = openSync16(path, constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK);
|
|
@@ -42614,7 +42798,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
|
42614
42798
|
break;
|
|
42615
42799
|
length += count;
|
|
42616
42800
|
}
|
|
42617
|
-
const after = fstatSync16(descriptor), current =
|
|
42801
|
+
const after = fstatSync16(descriptor), current = lstatSync20(path);
|
|
42618
42802
|
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
42803
|
throw new Error("Native skill file changed while reading");
|
|
42620
42804
|
return bytes.subarray(0, length);
|
|
@@ -42626,14 +42810,14 @@ function treeHash(root2) {
|
|
|
42626
42810
|
const hash2 = createHash18("sha256");
|
|
42627
42811
|
let bytes = 0, files = 0;
|
|
42628
42812
|
function visit2(path) {
|
|
42629
|
-
const stat2 =
|
|
42813
|
+
const stat2 = lstatSync20(path);
|
|
42630
42814
|
if (stat2.isSymbolicLink())
|
|
42631
42815
|
throw new Error(`Refusing symlink in skill: ${path}`);
|
|
42632
42816
|
const rel = relative4(root2, path);
|
|
42633
42817
|
if (stat2.isDirectory()) {
|
|
42634
42818
|
hash2.update(`d\x00${rel}\x00`);
|
|
42635
42819
|
for (const name of readdirSync8(path).sort())
|
|
42636
|
-
visit2(
|
|
42820
|
+
visit2(join23(path, name));
|
|
42637
42821
|
return;
|
|
42638
42822
|
}
|
|
42639
42823
|
if (!stat2.isFile())
|
|
@@ -42655,10 +42839,10 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
42655
42839
|
const identities = new Set;
|
|
42656
42840
|
const selected = [];
|
|
42657
42841
|
for (const target of manifest.targets) {
|
|
42658
|
-
if (!isAbsolute17(target.projectRoot) ||
|
|
42842
|
+
if (!isAbsolute17(target.projectRoot) || resolve16(target.projectRoot) !== target.projectRoot)
|
|
42659
42843
|
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 (!
|
|
42844
|
+
const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative4(projectRoot, resolve16(projectRoot, target.path));
|
|
42845
|
+
if (!existsSync7(projectRoot) || !lstatSync20(projectRoot).isDirectory())
|
|
42662
42846
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
42663
42847
|
assertSafePath(projectRoot);
|
|
42664
42848
|
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 +42851,8 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
42667
42851
|
if (identities.has(identity4))
|
|
42668
42852
|
throw new Error(`Duplicate native migration target: ${target.path}`);
|
|
42669
42853
|
identities.add(identity4);
|
|
42670
|
-
const expected = canonicalSystemPath(
|
|
42671
|
-
const matches = inventory.filter((entry2) => entry2.agent === target.agent &&
|
|
42854
|
+
const expected = canonicalSystemPath(resolve16(target.projectRoot, target.path));
|
|
42855
|
+
const matches = inventory.filter((entry2) => entry2.agent === target.agent && resolve16(entry2.path) === expected);
|
|
42672
42856
|
if (matches.length !== 1)
|
|
42673
42857
|
throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
|
|
42674
42858
|
const entry = matches[0];
|
|
@@ -42697,9 +42881,9 @@ function projectAncestorDirectories(projects) {
|
|
|
42697
42881
|
}
|
|
42698
42882
|
function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
42699
42883
|
home = canonicalSystemPath(home);
|
|
42700
|
-
if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute17(options.reviewedCacheAlias) ||
|
|
42884
|
+
if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute17(options.reviewedCacheAlias) || resolve16(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
|
|
42701
42885
|
throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
|
|
42702
|
-
if (options.disabledVendorPaths?.some((path) => !isAbsolute17(path) ||
|
|
42886
|
+
if (options.disabledVendorPaths?.some((path) => !isAbsolute17(path) || resolve16(path) !== path))
|
|
42703
42887
|
throw new Error("Disabled vendor paths must be exact absolute paths");
|
|
42704
42888
|
const reviewedCacheAlias = options.reviewedCacheAlias === undefined ? undefined : canonicalSystemPath(options.reviewedCacheAlias);
|
|
42705
42889
|
const disabledVendorPaths = new Set((options.disabledVendorPaths ?? []).map(canonicalSystemPath));
|
|
@@ -42707,14 +42891,18 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
42707
42891
|
const selectedAgents = options.agents ? new Set(options.agents) : undefined;
|
|
42708
42892
|
const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
|
|
42709
42893
|
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.
|
|
42894
|
+
const roots = rootDefinitions.map(([agent, path]) => [agent, canonicalAgentPath(join23(home, path), aliases)]);
|
|
42895
|
+
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
42896
|
for (const project of projectAncestorDirectories([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []])) {
|
|
42713
42897
|
for (const [agent, path] of rootDefinitions)
|
|
42714
|
-
roots.push([agent, canonicalAgentPath(
|
|
42898
|
+
roots.push([agent, canonicalAgentPath(join23(project, path), aliases)]);
|
|
42899
|
+
if (selectedAgents?.has("sumi"))
|
|
42900
|
+
for (const directory of [".claude", ".agents"])
|
|
42901
|
+
for (const name of ["skill", "skills"])
|
|
42902
|
+
roots.push(["sumi", canonicalAgentPath(join23(project, directory, name), aliases)]);
|
|
42715
42903
|
}
|
|
42716
42904
|
const installationInputs = options.codexInstallationInputRoots ?? [];
|
|
42717
|
-
if (installationInputs.some((input) =>
|
|
42905
|
+
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
42906
|
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
42719
42907
|
const entries = [], seen = new Set;
|
|
42720
42908
|
const cacheScans = new Map, maxAliasProofEntries = 1e4;
|
|
@@ -42728,13 +42916,13 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
42728
42916
|
}
|
|
42729
42917
|
let reviewedCacheAliasFound = false;
|
|
42730
42918
|
function verifiedSiblingCacheAlias(agent, path, parent) {
|
|
42731
|
-
const link = readlinkSync4(path), lexicalTarget =
|
|
42919
|
+
const link = readlinkSync4(path), lexicalTarget = resolve16(parent, link);
|
|
42732
42920
|
if (isAbsolute17(link) ? link !== lexicalTarget : dirname15(link) !== ".")
|
|
42733
42921
|
return false;
|
|
42734
42922
|
const target = canonicalSystemPath(lexicalTarget);
|
|
42735
42923
|
if (dirname15(target) !== parent)
|
|
42736
42924
|
return false;
|
|
42737
|
-
if (!
|
|
42925
|
+
if (!lstatSync20(target, { throwIfNoEntry: false })?.isDirectory())
|
|
42738
42926
|
return false;
|
|
42739
42927
|
const scan = cacheScans.get(target);
|
|
42740
42928
|
if (!scan?.complete || scan.entries > maxAliasProofEntries)
|
|
@@ -42757,37 +42945,40 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
42757
42945
|
assertSafePath(path);
|
|
42758
42946
|
if (configuredRoot && agent === "codex" && installationInputs.some((root2) => path === root2 || path.startsWith(`${root2}${sep5}`)))
|
|
42759
42947
|
return scan;
|
|
42760
|
-
if (!
|
|
42948
|
+
if (!existsSync7(path))
|
|
42761
42949
|
return scan;
|
|
42762
|
-
const stat2 =
|
|
42763
|
-
if (stat2.isFile())
|
|
42950
|
+
const stat2 = lstatSync20(path);
|
|
42951
|
+
if (stat2.isFile()) {
|
|
42952
|
+
if (agent === "sumi" && depth === 1 && path.endsWith(".md"))
|
|
42953
|
+
throw new Error("NATIVE_SKILL_DRIFT: flat Sumi skill documents require a dedicated migration adapter");
|
|
42764
42954
|
return scan;
|
|
42955
|
+
}
|
|
42765
42956
|
if (!stat2.isDirectory())
|
|
42766
42957
|
throw new Error(`Unsupported native discovery entry: ${path}`);
|
|
42767
|
-
if (
|
|
42958
|
+
if (existsSync7(join23(path, "SKILL.md"))) {
|
|
42768
42959
|
scan.hasSkills = true;
|
|
42769
42960
|
if (seen.has(path))
|
|
42770
42961
|
return scan;
|
|
42771
42962
|
seen.add(path);
|
|
42772
42963
|
let managed = false;
|
|
42773
|
-
const marker =
|
|
42774
|
-
if (
|
|
42964
|
+
const marker = join23(path, ".hasna-skills.json");
|
|
42965
|
+
if (existsSync7(marker)) {
|
|
42775
42966
|
try {
|
|
42776
42967
|
managed = JSON.parse(readFileSync14(marker, "utf8")).managedBy === "@hasna/skills";
|
|
42777
42968
|
} catch {}
|
|
42778
42969
|
}
|
|
42779
|
-
const bridge = !vendor && isOwnedCliBridge(path, bridgePaths);
|
|
42970
|
+
const bridge = (!vendor || agent === "sumi") && isOwnedCliBridge(path, bridgePaths);
|
|
42780
42971
|
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(
|
|
42972
|
+
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
42973
|
return scan;
|
|
42783
42974
|
}
|
|
42784
|
-
if (depth > (vendor ? 32 : 3))
|
|
42975
|
+
if (depth > (agent === "sumi" ? 32 : vendor ? 32 : 3))
|
|
42785
42976
|
return { ...scan, complete: false };
|
|
42786
42977
|
const children = [], directory = opendirSync5(path);
|
|
42787
42978
|
try {
|
|
42788
42979
|
let child;
|
|
42789
42980
|
while ((child = directory.readSync()) !== null) {
|
|
42790
|
-
admit(
|
|
42981
|
+
admit(join23(path, child.name));
|
|
42791
42982
|
children.push(child);
|
|
42792
42983
|
}
|
|
42793
42984
|
} finally {
|
|
@@ -42799,13 +42990,13 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
42799
42990
|
for (const { name } of children) {
|
|
42800
42991
|
if (name === "node_modules")
|
|
42801
42992
|
continue;
|
|
42802
|
-
if (name.startsWith(".") && name !== ".system" && agent !== "hermes" && !(options.guardHermes && `${path}${sep5}`.includes(`${sep5}.agents${sep5}skills${sep5}`)))
|
|
42993
|
+
if (name.startsWith(".") && name !== ".system" && agent !== "hermes" && agent !== "sumi" && !(options.guardHermes && `${path}${sep5}`.includes(`${sep5}.agents${sep5}skills${sep5}`)))
|
|
42803
42994
|
continue;
|
|
42804
42995
|
const isVendor = vendor || name === ".system";
|
|
42805
42996
|
if (isVendor && !options.includeVendor)
|
|
42806
42997
|
continue;
|
|
42807
|
-
const child =
|
|
42808
|
-
if (isVendor &&
|
|
42998
|
+
const child = join23(path, name);
|
|
42999
|
+
if (isVendor && lstatSync20(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
42809
43000
|
const target = statSync6(child, { throwIfNoEntry: false });
|
|
42810
43001
|
if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child, path)) {
|
|
42811
43002
|
scan.entries++;
|
|
@@ -42831,11 +43022,11 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
42831
43022
|
scanRoot(agent, path);
|
|
42832
43023
|
if (options.includeVendor) {
|
|
42833
43024
|
for (const agent of ["codex", "claude"].filter(includesAgent))
|
|
42834
|
-
scanRoot(agent, canonicalAgentPath(
|
|
43025
|
+
scanRoot(agent, canonicalAgentPath(join23(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
|
|
42835
43026
|
if (includesAgent("claude"))
|
|
42836
|
-
scanRoot("claude", canonicalAgentPath(
|
|
43027
|
+
scanRoot("claude", canonicalAgentPath(join23(home, ".claude", "plugins", "synced"), aliases), true);
|
|
42837
43028
|
if (includesAgent("gemini"))
|
|
42838
|
-
scanRoot("gemini",
|
|
43029
|
+
scanRoot("gemini", join23(home, ".gemini", "extensions"), true);
|
|
42839
43030
|
}
|
|
42840
43031
|
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
43032
|
for (const root2 of [...options.agentRoots ?? [], ...configured])
|
|
@@ -42848,7 +43039,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
42848
43039
|
}
|
|
42849
43040
|
function readOptional(path) {
|
|
42850
43041
|
assertSafePath(path);
|
|
42851
|
-
return
|
|
43042
|
+
return existsSync7(path) ? readFileSync14(path, "utf8") : null;
|
|
42852
43043
|
}
|
|
42853
43044
|
function jsonObject(text2, path) {
|
|
42854
43045
|
if (text2 === null)
|
|
@@ -42948,19 +43139,19 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
42948
43139
|
function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
42949
43140
|
const bundled = disableCodexBundledSkills(text2);
|
|
42950
43141
|
let result = normalizeCodexInlinePathConfig(bundled);
|
|
42951
|
-
const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [
|
|
43142
|
+
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
43143
|
const previousConfig = Bun.TOML.parse(bundled).skills?.config;
|
|
42953
43144
|
if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
|
|
42954
43145
|
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
43146
|
const bridgeNeedsRepair = previousConfig?.some((entry) => entry.enabled === false && bridgeSelector(entry));
|
|
42956
43147
|
const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
|
|
42957
43148
|
for (const skill of selections) {
|
|
42958
|
-
const path =
|
|
43149
|
+
const path = join23(skill.path, "SKILL.md");
|
|
42959
43150
|
let found = false;
|
|
42960
43151
|
result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
|
|
42961
43152
|
const parsed = Bun.TOML.parse(section);
|
|
42962
43153
|
const declared = parsed.skills?.config?.[0];
|
|
42963
|
-
if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [
|
|
43154
|
+
if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve16(path), resolve16(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
|
|
42964
43155
|
return section;
|
|
42965
43156
|
found = true;
|
|
42966
43157
|
return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
|
|
@@ -42981,14 +43172,14 @@ enabled = false
|
|
|
42981
43172
|
return result;
|
|
42982
43173
|
}
|
|
42983
43174
|
function assertCodexInstallationInputs(home, projectDirs, inputs) {
|
|
42984
|
-
const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) =>
|
|
43175
|
+
const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve16(directory, path)));
|
|
42985
43176
|
if (inputs.some((input) => nativeRoots.some((root2) => root2 === input || root2.startsWith(input + sep5) || input.startsWith(root2 + sep5))))
|
|
42986
43177
|
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
42987
43178
|
}
|
|
42988
43179
|
function planAgentIntegration(options) {
|
|
42989
43180
|
const home = options.home ?? homedir3(), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
42990
43181
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
42991
|
-
const policyPath =
|
|
43182
|
+
const policyPath = join23(dataDir2, "agent-policy.json");
|
|
42992
43183
|
assertSafePath(policyPath);
|
|
42993
43184
|
const priorSnapshot = readManagedSkillPolicySnapshot(dataDir2), previousPolicy = priorSnapshot?.text ?? null, policy = priorSnapshot?.value ?? {};
|
|
42994
43185
|
if (policy.bridge !== undefined && (!policy.bridge || typeof policy.bridge !== "object" || Array.isArray(policy.bridge)))
|
|
@@ -43020,10 +43211,10 @@ function planAgentIntegration(options) {
|
|
|
43020
43211
|
}
|
|
43021
43212
|
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
43022
43213
|
});
|
|
43023
|
-
const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(
|
|
43214
|
+
const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases)) ?? "") : {};
|
|
43024
43215
|
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
43216
|
const observedNativeSources = [];
|
|
43026
|
-
const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) =>
|
|
43217
|
+
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
43218
|
const bytes = readNativeBytes(path, 1024 * 1024);
|
|
43028
43219
|
observedNativeSources.push({ path, sha256: sha4(bytes) });
|
|
43029
43220
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
@@ -43031,7 +43222,7 @@ function planAgentIntegration(options) {
|
|
|
43031
43222
|
const codexPluginSkills = codexPluginControls.skills;
|
|
43032
43223
|
const codexInactivePlugins = codexPluginControls.inactivePlugins;
|
|
43033
43224
|
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 ===
|
|
43225
|
+
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
43226
|
assertCodexInstallationInputs(home, [options.projectDir ?? home], codexPluginSourceInputs.map((input) => input.sourceRoot));
|
|
43036
43227
|
const changes = [];
|
|
43037
43228
|
for (const agent of [...new Set(options.agents)]) {
|
|
@@ -43039,19 +43230,19 @@ function planAgentIntegration(options) {
|
|
|
43039
43230
|
throw new Error(`Unsupported agent: ${agent}`);
|
|
43040
43231
|
const { command, profileId: profileId2 } = bindings.get(agent);
|
|
43041
43232
|
const adapter = AGENT_ADAPTERS[agent];
|
|
43042
|
-
const bridgePath = canonicalAgentPath(
|
|
43233
|
+
const bridgePath = canonicalAgentPath(join23(agent === "sumi" ? sumiBridgeRoot(home) : join23(home, adapter.root), CLI_BRIDGE_NAME), aliases);
|
|
43043
43234
|
assertSafePath(bridgePath);
|
|
43044
|
-
if (
|
|
43235
|
+
if (existsSync7(bridgePath) && !isOwnedCliBridge(bridgePath, [bridgePath]))
|
|
43045
43236
|
throw new Error(`Refusing to overwrite an unrecognized or modified Skills bridge: ${bridgePath}`);
|
|
43046
43237
|
for (const [name, after2] of Object.entries(CLI_BRIDGE_FILES)) {
|
|
43047
|
-
const path2 =
|
|
43238
|
+
const path2 = join23(bridgePath, name), before2 = readOptional(path2);
|
|
43048
43239
|
if (before2 !== after2)
|
|
43049
43240
|
changes.push({ path: path2, before: before2, after: after2 });
|
|
43050
43241
|
}
|
|
43051
|
-
const path = canonicalAgentPath(
|
|
43242
|
+
const path = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) : join23(home, adapter.config), aliases);
|
|
43052
43243
|
const before = readOptional(path);
|
|
43053
43244
|
if (agent === "hermes") {
|
|
43054
|
-
const supervisorPath =
|
|
43245
|
+
const supervisorPath = join23(dataDir2, "agent-hooks", "hermes.js"), supervisorBefore = readOptional(supervisorPath), supervisorAfter = renderHermesSupervisor(command, profileId2);
|
|
43055
43246
|
const priorCommand = policy.bridge?.commands?.hermes, priorProfile = policy.bridge?.profiles?.hermes ?? policy.profileId;
|
|
43056
43247
|
const previous = policy.bridge?.supervisors?.hermes;
|
|
43057
43248
|
if (supervisorBefore !== null && supervisorBefore !== supervisorAfter && !(previous?.path === supervisorPath && typeof priorCommand === "string" && typeof priorProfile === "string" && supervisorBefore === renderHermesSupervisor(priorCommand, priorProfile)))
|
|
@@ -43062,19 +43253,30 @@ function planAgentIntegration(options) {
|
|
|
43062
43253
|
const after2 = configureHermesHooks(before, supervisor, previous);
|
|
43063
43254
|
if (before !== after2)
|
|
43064
43255
|
changes.push({ path, before, after: after2 });
|
|
43065
|
-
const optOut =
|
|
43256
|
+
const optOut = join23(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
|
|
43066
43257
|
if (optOutBefore === null)
|
|
43067
43258
|
changes.push({ path: optOut, before: null, after: `Managed by @hasna/skills: bundled native skill reseeding is disabled.
|
|
43068
43259
|
` });
|
|
43069
43260
|
continue;
|
|
43070
43261
|
}
|
|
43071
43262
|
const config = jsonObject(before, path);
|
|
43072
|
-
if (agent === "
|
|
43263
|
+
if (agent === "sumi") {
|
|
43264
|
+
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))))
|
|
43265
|
+
throw new Error("Expected Sumi native permission rules");
|
|
43266
|
+
config.permissions = [...(config.permissions ?? []).filter((rule) => rule.action !== "skill"), ...SUMI_SKILL_PERMISSIONS];
|
|
43267
|
+
const pluginPath = join23(sumiConfigDirectory(home), "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
|
|
43268
|
+
const pluginAfter = renderSumiPlugin(command, profileId2);
|
|
43269
|
+
const priorCommand = policy.bridge?.commands?.sumi, priorProfile = policy.bridge?.profiles?.sumi ?? policy.profileId;
|
|
43270
|
+
if (pluginBefore !== null && pluginBefore !== pluginAfter && !(typeof priorCommand === "string" && typeof priorProfile === "string" && pluginBefore === renderSumiPlugin(priorCommand, priorProfile)))
|
|
43271
|
+
throw new Error("Refusing to overwrite a modified Sumi Skills plugin; preserve and review it first");
|
|
43272
|
+
if (pluginBefore !== pluginAfter)
|
|
43273
|
+
changes.push({ path: pluginPath, before: pluginBefore, after: pluginAfter });
|
|
43274
|
+
} else if (agent === "opencode") {
|
|
43073
43275
|
const permission = config.permission ?? {};
|
|
43074
43276
|
if (!permission || typeof permission !== "object" || Array.isArray(permission))
|
|
43075
43277
|
throw new Error("Expected OpenCode permission object");
|
|
43076
43278
|
config.permission = { ...permission, skill: { "*": "deny", [CLI_BRIDGE_NAME]: "allow" } };
|
|
43077
|
-
const pluginPath =
|
|
43279
|
+
const pluginPath = join23(home, ".config", "opencode", "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
|
|
43078
43280
|
const pluginAfter = renderOpenCodePlugin(command, profileId2);
|
|
43079
43281
|
const priorCommand = policy.bridge?.commands?.opencode;
|
|
43080
43282
|
const ownedPrevious = typeof priorCommand === "string" && typeof policy.profileId === "string" && pluginBefore === renderOpenCodePlugin(priorCommand, policy.bridge?.profiles?.opencode ?? policy.profileId);
|
|
@@ -43091,13 +43293,13 @@ function planAgentIntegration(options) {
|
|
|
43091
43293
|
if (before !== after)
|
|
43092
43294
|
changes.push({ path, before, after });
|
|
43093
43295
|
if (agent === "codex") {
|
|
43094
|
-
const configPath = canonicalAgentPath(
|
|
43296
|
+
const configPath = canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
|
|
43095
43297
|
const next = disableReviewedCodexPluginNames(disableCodexSkills(previous ?? "", nativeSkills, aliases, bridgePath), codexPluginSkills);
|
|
43096
43298
|
if (next !== (previous ?? ""))
|
|
43097
43299
|
changes.push({ path: configPath, before: previous, after: next });
|
|
43098
43300
|
}
|
|
43099
43301
|
}
|
|
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(
|
|
43302
|
+
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
43303
|
const discoveryAfter = discoveries.map((binding) => {
|
|
43102
43304
|
const rebound = rebindAgentDiscovery(binding, new Map(changes.map((change) => [change.path, change.after])));
|
|
43103
43305
|
if (binding.agent !== "codex")
|
|
@@ -43112,12 +43314,12 @@ function planAgentIntegration(options) {
|
|
|
43112
43314
|
version: CLI_BRIDGE_VERSION,
|
|
43113
43315
|
digest: CLI_BRIDGE_DIGEST,
|
|
43114
43316
|
agents: [...new Set([...priorAgents, ...options.agents])].sort(),
|
|
43115
|
-
home:
|
|
43317
|
+
home: resolve16(home),
|
|
43116
43318
|
includeVendor: true,
|
|
43117
|
-
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path:
|
|
43319
|
+
...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
43320
|
commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
|
|
43119
43321
|
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(
|
|
43322
|
+
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
43323
|
rootAliases: aliases,
|
|
43122
43324
|
...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
|
|
43123
43325
|
discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
|
|
@@ -43140,7 +43342,7 @@ function claudeSettingsConsumers(home, path, discoveries, aliases) {
|
|
|
43140
43342
|
const distance = relative4(canonical6(root2), path);
|
|
43141
43343
|
return distance === "" || distance !== ".." && !distance.startsWith(`..${sep5}`) && !isAbsolute17(distance);
|
|
43142
43344
|
};
|
|
43143
|
-
return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical6(
|
|
43345
|
+
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
43346
|
}
|
|
43145
43347
|
function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
43146
43348
|
try {
|
|
@@ -43157,8 +43359,8 @@ function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
|
43157
43359
|
}
|
|
43158
43360
|
}
|
|
43159
43361
|
function planClaudeHookEventsUpdate(options) {
|
|
43160
|
-
const home =
|
|
43161
|
-
const policyPath =
|
|
43362
|
+
const home = resolve16(options.home ?? homedir3()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
43363
|
+
const policyPath = join23(dataDir2, "agent-policy.json");
|
|
43162
43364
|
assertSafePath(policyPath);
|
|
43163
43365
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
43164
43366
|
if (!snapshot2)
|
|
@@ -43169,9 +43371,9 @@ function planClaudeHookEventsUpdate(options) {
|
|
|
43169
43371
|
}
|
|
43170
43372
|
const aliases = bridge.rootAliases ?? [];
|
|
43171
43373
|
recheckRootAliases(aliases);
|
|
43172
|
-
const path = canonicalAgentPath(
|
|
43374
|
+
const path = canonicalAgentPath(join23(home, ".claude", "settings.json"), aliases);
|
|
43173
43375
|
assertSafePath(path);
|
|
43174
|
-
const before =
|
|
43376
|
+
const before = lstatSync20(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
|
|
43175
43377
|
if ((before === null ? "absent" : sha4(before)) !== options.expectedSettingsSha256)
|
|
43176
43378
|
throw new Error("Claude settings preimage changed");
|
|
43177
43379
|
assertClaudeHookEventsReplacement(before, options.replacement, options.events);
|
|
@@ -43231,14 +43433,14 @@ function atomicWrite2(path, content) {
|
|
|
43231
43433
|
writeFileSync4(temporary, content, { mode: 384, flag: "wx" });
|
|
43232
43434
|
renameSync3(temporary, path);
|
|
43233
43435
|
} finally {
|
|
43234
|
-
if (
|
|
43436
|
+
if (existsSync7(temporary))
|
|
43235
43437
|
unlinkSync4(temporary);
|
|
43236
43438
|
}
|
|
43237
43439
|
}
|
|
43238
43440
|
function planAgentSettingsWitnessUpgrade(options) {
|
|
43239
43441
|
if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3].includes(options.targetCodexVersion)))
|
|
43240
43442
|
throw new Error("Invalid Codex witness target version");
|
|
43241
|
-
const home =
|
|
43443
|
+
const home = resolve16(options.home ?? homedir3()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
43242
43444
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
43243
43445
|
if (!snapshot2 || sha4(snapshot2.text) !== options.expectedPolicySha256)
|
|
43244
43446
|
throw new Error("Managed policy preimage changed");
|
|
@@ -43247,7 +43449,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
43247
43449
|
const binding = policy.bridge?.discovery?.[options.agent];
|
|
43248
43450
|
if (!binding || binding.agent !== options.agent || binding.method !== "reviewed")
|
|
43249
43451
|
throw new Error("Settings witness upgrade requires an existing reviewed native binding");
|
|
43250
|
-
const configPath = canonicalAgentPath(
|
|
43452
|
+
const configPath = canonicalAgentPath(join23(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
|
|
43251
43453
|
const settings = readOptional(configPath);
|
|
43252
43454
|
if (settings === null || sha4(settings) !== options.expectedSettingsSha256)
|
|
43253
43455
|
throw new Error("Native settings preimage changed");
|
|
@@ -43286,7 +43488,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
43286
43488
|
const replacement = { ...binding, sources: rebound };
|
|
43287
43489
|
verifyAgentDiscovery(replacement);
|
|
43288
43490
|
const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
|
|
43289
|
-
return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path:
|
|
43491
|
+
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
43492
|
}
|
|
43291
43493
|
function applyAgentIntegration(plan) {
|
|
43292
43494
|
for (const source of plan.observedNativeSources ?? [])
|
|
@@ -43294,12 +43496,12 @@ function applyAgentIntegration(plan) {
|
|
|
43294
43496
|
throw new Error("Native identity source changed after planning");
|
|
43295
43497
|
if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
|
|
43296
43498
|
throw new Error("Native settings changed after witness planning");
|
|
43297
|
-
const policyPath =
|
|
43499
|
+
const policyPath = join23(plan.dataDir, "agent-policy.json");
|
|
43298
43500
|
assertSafePath(policyPath);
|
|
43299
|
-
const currentText = (path) =>
|
|
43501
|
+
const currentText = (path) => resolve16(path) === resolve16(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
|
|
43300
43502
|
readManagedSkillPolicySnapshot(plan.dataDir);
|
|
43301
43503
|
for (const change of plan.changes)
|
|
43302
|
-
if (
|
|
43504
|
+
if (resolve16(change.path) === resolve16(policyPath))
|
|
43303
43505
|
parseManagedSkillPolicy(change.after);
|
|
43304
43506
|
const aliases = plan.rootAliases ?? [];
|
|
43305
43507
|
recheckRootAliases(aliases);
|
|
@@ -43328,7 +43530,7 @@ function applyAgentIntegration(plan) {
|
|
|
43328
43530
|
for (const change of plan.changes)
|
|
43329
43531
|
if (currentText(change.path) !== change.before)
|
|
43330
43532
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
43331
|
-
const backupRoot =
|
|
43533
|
+
const backupRoot = join23(plan.dataDir, "migration", randomUUID3()), backups = [], written = [], createdDirectories = new Set;
|
|
43332
43534
|
if (!plan.changes.length) {
|
|
43333
43535
|
recheckRootAliases(aliases);
|
|
43334
43536
|
return { changed: [], backups, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
@@ -43338,7 +43540,7 @@ function applyAgentIntegration(plan) {
|
|
|
43338
43540
|
chmodSync(backupRoot, 448);
|
|
43339
43541
|
for (const [index, change] of plan.changes.entries())
|
|
43340
43542
|
if (change.before !== null) {
|
|
43341
|
-
const backup =
|
|
43543
|
+
const backup = join23(backupRoot, `${index}-${sha4(change.path).slice(0, 12)}.backup`);
|
|
43342
43544
|
writeFileSync4(backup, change.before, { mode: 384, flag: "wx" });
|
|
43343
43545
|
if (readFileSync14(backup, "utf8") !== change.before)
|
|
43344
43546
|
throw new Error("Native configuration preservation readback failed");
|
|
@@ -43349,7 +43551,7 @@ function applyAgentIntegration(plan) {
|
|
|
43349
43551
|
recheckRootAliases(aliases);
|
|
43350
43552
|
if (currentText(change.path) !== change.before)
|
|
43351
43553
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
43352
|
-
for (let directory = dirname15(change.path);!
|
|
43554
|
+
for (let directory = dirname15(change.path);!existsSync7(directory); directory = dirname15(directory))
|
|
43353
43555
|
createdDirectories.add(directory);
|
|
43354
43556
|
const after = change.after;
|
|
43355
43557
|
atomicWrite2(change.path, after);
|
|
@@ -43369,7 +43571,7 @@ function applyAgentIntegration(plan) {
|
|
|
43369
43571
|
}
|
|
43370
43572
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
43371
43573
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
43372
|
-
atomicWrite2(
|
|
43574
|
+
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
43575
|
`);
|
|
43374
43576
|
} catch (error) {
|
|
43375
43577
|
for (const change of written.reverse()) {
|
|
@@ -43423,7 +43625,7 @@ function writeArchiveJournal(path, value) {
|
|
|
43423
43625
|
} finally {
|
|
43424
43626
|
if (descriptor !== undefined)
|
|
43425
43627
|
closeSync16(descriptor);
|
|
43426
|
-
if (
|
|
43628
|
+
if (lstatSync20(temporary, { throwIfNoEntry: false }))
|
|
43427
43629
|
unlinkSync4(temporary);
|
|
43428
43630
|
}
|
|
43429
43631
|
}
|
|
@@ -43434,7 +43636,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
43434
43636
|
recheckRootAliases(aliases);
|
|
43435
43637
|
for (const entry of inventory.filter((entry2) => entry2.bridge)) {
|
|
43436
43638
|
const adapter = AGENT_ADAPTERS[entry.agent];
|
|
43437
|
-
const expected = adapter && entry.bridgeHome ? canonicalAgentPath(
|
|
43639
|
+
const expected = adapter && entry.bridgeHome ? canonicalAgentPath(join23(entry.agent === "sumi" ? sumiBridgeRoot(entry.bridgeHome) : join23(entry.bridgeHome, adapter.root), CLI_BRIDGE_NAME), aliases) : undefined;
|
|
43438
43640
|
if (!expected || treeHash(entry.path) !== entry.hash || !isOwnedCliBridge(entry.path, [expected]))
|
|
43439
43641
|
throw new Error(`Skills bridge changed after planning: ${entry.path}`);
|
|
43440
43642
|
}
|
|
@@ -43445,11 +43647,11 @@ function archiveNativeSkills(inventory, options) {
|
|
|
43445
43647
|
const targetManifest = options.targetManifest ? { schema: options.targetManifest.schema, digest: options.targetManifest.digest, targetCount: options.targetManifest.targets.length } : undefined;
|
|
43446
43648
|
if (!selected.length)
|
|
43447
43649
|
return { entries: [], ...targetManifest ? { targetManifest } : {}, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
43448
|
-
const operationId = randomUUID3(), archiveRoot =
|
|
43650
|
+
const operationId = randomUUID3(), archiveRoot = join23(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join23(archiveRoot, "receipt.json");
|
|
43449
43651
|
const entries = selected.map((entry, index) => ({
|
|
43450
|
-
source: entry.vendor ?
|
|
43451
|
-
archive:
|
|
43452
|
-
hash: entry.vendor ? sha4(readNativeBytes(
|
|
43652
|
+
source: entry.vendor ? join23(entry.path, "SKILL.md") : entry.path,
|
|
43653
|
+
archive: join23(archiveRoot, `${index}-${sha4(entry.path).slice(0, 12)}`),
|
|
43654
|
+
hash: entry.vendor ? sha4(readNativeBytes(join23(entry.path, "SKILL.md"))) : entry.hash,
|
|
43453
43655
|
...entry.vendor ? { discoveryOnly: true } : {},
|
|
43454
43656
|
status: "planned"
|
|
43455
43657
|
}));
|
|
@@ -43457,7 +43659,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
43457
43659
|
const moved = [];
|
|
43458
43660
|
const verifyArchive = (entry) => {
|
|
43459
43661
|
assertSafePath(entry.archive);
|
|
43460
|
-
const stat2 =
|
|
43662
|
+
const stat2 = lstatSync20(entry.archive);
|
|
43461
43663
|
if (entry.discoveryOnly ? !stat2.isFile() : !stat2.isDirectory())
|
|
43462
43664
|
throw new Error("Archive type changed");
|
|
43463
43665
|
if ((entry.discoveryOnly ? sha4(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
|
|
@@ -43465,7 +43667,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
43465
43667
|
};
|
|
43466
43668
|
assertSafePath(archiveRoot);
|
|
43467
43669
|
const created = [];
|
|
43468
|
-
for (let path = archiveRoot;!
|
|
43670
|
+
for (let path = archiveRoot;!existsSync7(path); path = dirname15(path))
|
|
43469
43671
|
created.push(path);
|
|
43470
43672
|
mkdirSync5(archiveRoot, { recursive: true, mode: 448 });
|
|
43471
43673
|
try {
|
|
@@ -43505,7 +43707,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
43505
43707
|
recheckRootAliases(aliases);
|
|
43506
43708
|
assertSafePath(entry.source);
|
|
43507
43709
|
assertSafePath(entry.archive);
|
|
43508
|
-
if (
|
|
43710
|
+
if (lstatSync20(entry.source, { throwIfNoEntry: false })) {
|
|
43509
43711
|
entry.status = "recovery-required";
|
|
43510
43712
|
entry.conflict = "source-occupied";
|
|
43511
43713
|
continue;
|
|
@@ -43522,7 +43724,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
43522
43724
|
syncArchiveDirectory(dirname15(entry.source));
|
|
43523
43725
|
unlinkSync4(entry.archive);
|
|
43524
43726
|
} else {
|
|
43525
|
-
if (
|
|
43727
|
+
if (lstatSync20(entry.source, { throwIfNoEntry: false })) {
|
|
43526
43728
|
entry.status = "recovery-required";
|
|
43527
43729
|
entry.conflict = "source-occupied";
|
|
43528
43730
|
continue;
|
|
@@ -43553,8 +43755,8 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
43553
43755
|
assertManagedAgentBridgeWithDiscovery(agent, options);
|
|
43554
43756
|
}
|
|
43555
43757
|
function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
|
|
43556
|
-
const home =
|
|
43557
|
-
assertSafePath(
|
|
43758
|
+
const home = resolve16(options.home ?? homedir3()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
43759
|
+
assertSafePath(join23(dataDir2, "agent-policy.json"));
|
|
43558
43760
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
43559
43761
|
if (!snapshot2)
|
|
43560
43762
|
throw new Error("NATIVE_SKILL_DRIFT: install the Skills bridge with skills hook install");
|
|
@@ -43565,14 +43767,14 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
43565
43767
|
if (!Array.isArray(aliases))
|
|
43566
43768
|
throw new Error("NATIVE_SKILL_DRIFT: invalid root alias binding");
|
|
43567
43769
|
recheckRootAliases(aliases);
|
|
43568
|
-
const expected = canonicalAgentPath(
|
|
43770
|
+
const expected = canonicalAgentPath(join23(agent === "sumi" ? sumiBridgeRoot(home) : join23(home, AGENT_ADAPTERS[agent].root), CLI_BRIDGE_NAME), aliases);
|
|
43569
43771
|
if (!isOwnedCliBridge(expected, [expected]))
|
|
43570
43772
|
throw new Error("NATIVE_SKILL_DRIFT: the native Skills bridge is missing or modified; repair it before continuing");
|
|
43571
43773
|
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}`) ||
|
|
43774
|
+
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
43775
|
const discovery = provenDiscovery ?? binding.discovery?.[agent];
|
|
43574
43776
|
assertProjectDiscovery(agent, [...roots], home, (path) => canonicalAgentPath(path, aliases), discovery);
|
|
43575
|
-
const configPath = canonicalAgentPath(
|
|
43777
|
+
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
43778
|
const command = binding.commands?.[agent], profile = binding.profiles?.[agent];
|
|
43577
43779
|
if (typeof command !== "string" || typeof profile !== "string")
|
|
43578
43780
|
throw new Error("NATIVE_SKILL_DRIFT: the native hook command/profile binding is missing");
|
|
@@ -43580,11 +43782,14 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
43580
43782
|
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
43783
|
if (agent === "hermes") {
|
|
43582
43784
|
const supervisor = binding.supervisors?.hermes;
|
|
43583
|
-
if (supervisor?.path !==
|
|
43785
|
+
if (supervisor?.path !== join23(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha4(renderHermesSupervisor(command, profile)))
|
|
43584
43786
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
|
|
43585
43787
|
assertHermesProtection(home, config, command, profile, supervisor);
|
|
43788
|
+
} else if (agent === "sumi") {
|
|
43789
|
+
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))
|
|
43790
|
+
throw new Error("NATIVE_SKILL_DRIFT: Sumi bridge protection changed; run skills hook install");
|
|
43586
43791
|
} else if (agent === "opencode") {
|
|
43587
|
-
if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(
|
|
43792
|
+
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
43793
|
throw new Error("NATIVE_SKILL_DRIFT: OpenCode bridge protection changed; run skills hook install");
|
|
43589
43794
|
} else {
|
|
43590
43795
|
const required = {};
|
|
@@ -43601,21 +43806,21 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
43601
43806
|
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
43807
|
throw new Error("NATIVE_SKILL_DRIFT: Gemini bridge or bundled-skill protection changed; run skills hook install");
|
|
43603
43808
|
}
|
|
43604
|
-
const codexPath = canonicalAgentPath(
|
|
43809
|
+
const codexPath = canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases);
|
|
43605
43810
|
if (options.codexDiscoveryRecovery && (agent !== "codex" || options.codexDiscoveryRecovery.configPath !== codexPath))
|
|
43606
43811
|
throw new Error("NATIVE_SKILL_DRIFT: Codex trust recovery names a different configuration root");
|
|
43607
43812
|
const codexConfig = agent === "codex" ? Bun.TOML.parse(readOptional(codexPath) ?? "") : {};
|
|
43608
43813
|
if (agent === "codex" && codexConfig.skills?.bundled?.enabled !== false)
|
|
43609
43814
|
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,
|
|
43815
|
+
const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join23(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
|
|
43611
43816
|
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
43817
|
throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
|
|
43613
|
-
if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(
|
|
43818
|
+
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
43819
|
throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
|
|
43615
43820
|
const disabledPlugin = (entry) => {
|
|
43616
43821
|
if (agent !== "codex" || !entry.vendor)
|
|
43617
43822
|
return false;
|
|
43618
|
-
const document2 =
|
|
43823
|
+
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
43824
|
return isReviewedCodexPluginInactive(document2, cache, binding.codexInactivePlugins ?? [], codexConfig.plugins, codexConfig.skills?.config ?? [], read3) || isReviewedCodexPluginSkillDisabled(document2, cache, binding.codexPluginSkills ?? [], codexConfig.skills?.config, read3);
|
|
43620
43825
|
};
|
|
43621
43826
|
const disabledPaths = new Set(disabledCodexSkillPaths(codexConfig, aliases));
|
|
@@ -43628,15 +43833,15 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
43628
43833
|
return true;
|
|
43629
43834
|
if (!disabledPaths.has(entry.path))
|
|
43630
43835
|
return false;
|
|
43631
|
-
if (entry.path.startsWith(canonicalAgentPath(
|
|
43836
|
+
if (entry.path.startsWith(canonicalAgentPath(join23(home, ".codex", "plugins", "cache"), aliases) + sep5))
|
|
43632
43837
|
return true;
|
|
43633
|
-
const runtimeCache = canonicalAgentPath(
|
|
43838
|
+
const runtimeCache = canonicalAgentPath(join23(home, ".cache", "codex-runtimes"), aliases);
|
|
43634
43839
|
if (entry.path.startsWith(runtimeCache + sep5) && discovery?.method === "automatic" && discovery.roots.some((root2) => entry.path.startsWith(root2 + sep5)))
|
|
43635
43840
|
return true;
|
|
43636
|
-
const bundledMarketplaces = canonicalAgentPath(
|
|
43841
|
+
const bundledMarketplaces = canonicalAgentPath(join23(home, ".codex", ".tmp", "bundled-marketplaces"), aliases);
|
|
43637
43842
|
if (entry.path.startsWith(bundledMarketplaces + sep5) && discovery?.method === "automatic" && discovery.roots.some((root2) => entry.path.startsWith(root2 + sep5)))
|
|
43638
43843
|
return true;
|
|
43639
|
-
if (!entry.path.startsWith(canonicalAgentPath(
|
|
43844
|
+
if (!entry.path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep5))
|
|
43640
43845
|
return false;
|
|
43641
43846
|
return Array.isArray(binding.disabledBuiltins) && binding.disabledBuiltins.some((item) => item.path === entry.path && item.hash === entry.hash);
|
|
43642
43847
|
};
|
|
@@ -43646,6 +43851,8 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
43646
43851
|
throw new Error("NATIVE_SKILL_DRIFT: an installed Gemini builtin is not disabled");
|
|
43647
43852
|
try {
|
|
43648
43853
|
verifyAgentDiscovery(discovery, options.codexDiscoveryRecovery);
|
|
43854
|
+
if (agent === "sumi" && discovery.method === "reviewed")
|
|
43855
|
+
resolveAgentDiscovery({ home, agent, retainedReview: discovery, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
43649
43856
|
if (discovery.method === "automatic") {
|
|
43650
43857
|
const current = resolveAgentDiscovery({ home, agent, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
43651
43858
|
if (JSON.stringify(current) !== JSON.stringify(discovery))
|
|
@@ -43657,7 +43864,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
43657
43864
|
let installationInputRoots = [];
|
|
43658
43865
|
if (agent === "codex") {
|
|
43659
43866
|
try {
|
|
43660
|
-
installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(
|
|
43867
|
+
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
43868
|
} catch {
|
|
43662
43869
|
throw new Error("NATIVE_SKILL_DRIFT: native installation input identity changed; review discovery and catalog");
|
|
43663
43870
|
}
|
|
@@ -43681,25 +43888,25 @@ function assertNativeExportAllowed(dataDir2 = getDataDirReadOnly()) {
|
|
|
43681
43888
|
|
|
43682
43889
|
// src/lib/portable-skills.ts
|
|
43683
43890
|
import {
|
|
43684
|
-
existsSync as
|
|
43891
|
+
existsSync as existsSync10,
|
|
43685
43892
|
mkdirSync as mkdirSync7,
|
|
43686
43893
|
mkdtempSync,
|
|
43687
43894
|
readdirSync as readdirSync10,
|
|
43688
|
-
realpathSync as
|
|
43895
|
+
realpathSync as realpathSync11,
|
|
43689
43896
|
renameSync as renameSync4,
|
|
43690
43897
|
rmSync as rmSync2,
|
|
43691
43898
|
statSync as statSync9,
|
|
43692
43899
|
writeFileSync as writeFileSync6
|
|
43693
43900
|
} from "fs";
|
|
43694
|
-
import { basename as basename10, dirname as dirname18, isAbsolute as isAbsolute18, join as
|
|
43901
|
+
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
43902
|
|
|
43696
43903
|
// src/lib/registry-data/index.ts
|
|
43697
43904
|
var SKILLS = [];
|
|
43698
43905
|
|
|
43699
43906
|
// src/lib/skill-hash.ts
|
|
43700
43907
|
import { createHash as createHash19 } from "crypto";
|
|
43701
|
-
import { existsSync as
|
|
43702
|
-
import { join as
|
|
43908
|
+
import { existsSync as existsSync8, readdirSync as readdirSync9, readFileSync as readFileSync15, statSync as statSync7 } from "fs";
|
|
43909
|
+
import { join as join24, sep as sep6 } from "path";
|
|
43703
43910
|
var CONTENT_HASH_ALGORITHM = "sha256";
|
|
43704
43911
|
var HASH_EXCLUDE_DIRS = new Set([".git", "node_modules", "dist", "build", ".turbo"]);
|
|
43705
43912
|
function excludedHashEntry(name, directory) {
|
|
@@ -43761,8 +43968,8 @@ function collectBundleFiles(skillPath) {
|
|
|
43761
43968
|
if (seen.has(entry))
|
|
43762
43969
|
continue;
|
|
43763
43970
|
seen.add(entry);
|
|
43764
|
-
const absolute =
|
|
43765
|
-
if (!
|
|
43971
|
+
const absolute = join24(skillPath, entry);
|
|
43972
|
+
if (!existsSync8(absolute))
|
|
43766
43973
|
continue;
|
|
43767
43974
|
if (statSync7(absolute).isDirectory())
|
|
43768
43975
|
collectDirectory(files, absolute, entry);
|
|
@@ -43775,7 +43982,7 @@ function collectDirectory(files, dir, rel) {
|
|
|
43775
43982
|
for (const entry of readdirSync9(dir).sort()) {
|
|
43776
43983
|
if (entry.startsWith("."))
|
|
43777
43984
|
continue;
|
|
43778
|
-
const absolute =
|
|
43985
|
+
const absolute = join24(dir, entry);
|
|
43779
43986
|
const childRel = `${rel}/${entry}`;
|
|
43780
43987
|
let stats;
|
|
43781
43988
|
try {
|
|
@@ -43836,7 +44043,7 @@ async function hashBundleFilesCooperatively(files, check) {
|
|
|
43836
44043
|
hash2.update(chunk);
|
|
43837
44044
|
bytesSinceYield += chunk.byteLength;
|
|
43838
44045
|
if (bytesSinceYield >= 256 * 1024) {
|
|
43839
|
-
await new Promise((
|
|
44046
|
+
await new Promise((resolve17) => setImmediate(resolve17));
|
|
43840
44047
|
bytesSinceYield = 0;
|
|
43841
44048
|
}
|
|
43842
44049
|
}
|
|
@@ -43989,7 +44196,7 @@ async function hashContentEntries(entries, options) {
|
|
|
43989
44196
|
const normalized = [];
|
|
43990
44197
|
let normalizedBytes = 0;
|
|
43991
44198
|
let manifest;
|
|
43992
|
-
await new Promise((
|
|
44199
|
+
await new Promise((resolve17) => setImmediate(resolve17));
|
|
43993
44200
|
for (const entry of snapshot2) {
|
|
43994
44201
|
check();
|
|
43995
44202
|
if (!coveredContentPath(entry.path))
|
|
@@ -44002,7 +44209,7 @@ async function hashContentEntries(entries, options) {
|
|
|
44002
44209
|
contentLimit("Content hash normalized byte limit exceeded");
|
|
44003
44210
|
normalizedBytes += file.content.byteLength;
|
|
44004
44211
|
normalized.push(file);
|
|
44005
|
-
await new Promise((
|
|
44212
|
+
await new Promise((resolve17) => setImmediate(resolve17));
|
|
44006
44213
|
}
|
|
44007
44214
|
normalized.sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0);
|
|
44008
44215
|
check();
|
|
@@ -44209,14 +44416,14 @@ function validateRuntimeContract(manifest, issues, strict) {
|
|
|
44209
44416
|
// src/lib/portable-skills-files.ts
|
|
44210
44417
|
import {
|
|
44211
44418
|
cpSync,
|
|
44212
|
-
existsSync as
|
|
44213
|
-
lstatSync as
|
|
44419
|
+
existsSync as existsSync9,
|
|
44420
|
+
lstatSync as lstatSync21,
|
|
44214
44421
|
mkdirSync as mkdirSync6,
|
|
44215
44422
|
readFileSync as readFileSync16,
|
|
44216
|
-
realpathSync as
|
|
44423
|
+
realpathSync as realpathSync9,
|
|
44217
44424
|
writeFileSync as writeFileSync5
|
|
44218
44425
|
} from "fs";
|
|
44219
|
-
import { basename as basename8, dirname as dirname16, join as
|
|
44426
|
+
import { basename as basename8, dirname as dirname16, join as join25, relative as relative5 } from "path";
|
|
44220
44427
|
var ANY_SEGMENT_COPY_EXCLUDES = new Set([
|
|
44221
44428
|
".git",
|
|
44222
44429
|
".DS_Store",
|
|
@@ -44273,12 +44480,12 @@ function readPortableSkillManifestForImport(skillPath) {
|
|
|
44273
44480
|
return readManifest(skillPath, basename8(skillPath), normalizeNewPortableSkillName);
|
|
44274
44481
|
}
|
|
44275
44482
|
function readManifest(skillPath, fallbackName, normalizeName) {
|
|
44276
|
-
const skillJsonPath =
|
|
44277
|
-
const skillMdPath =
|
|
44278
|
-
const pkgPath =
|
|
44279
|
-
const jsonManifest =
|
|
44280
|
-
const frontmatter =
|
|
44281
|
-
const pkg =
|
|
44483
|
+
const skillJsonPath = join25(skillPath, "skill.json");
|
|
44484
|
+
const skillMdPath = join25(skillPath, "SKILL.md");
|
|
44485
|
+
const pkgPath = join25(skillPath, "package.json");
|
|
44486
|
+
const jsonManifest = existsSync9(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
|
|
44487
|
+
const frontmatter = existsSync9(skillMdPath) ? parseSkillFrontmatter(readFileSync16(skillMdPath, "utf-8")) ?? undefined : undefined;
|
|
44488
|
+
const pkg = existsSync9(pkgPath) ? readJsonObject(pkgPath) : undefined;
|
|
44282
44489
|
const name = normalizeName(stringField(jsonManifest, "name") ?? frontmatter?.name ?? stringValue(pkg?.name) ?? fallbackName);
|
|
44283
44490
|
const description = stringField(jsonManifest, "description") ?? frontmatter?.description ?? stringValue(pkg?.description) ?? `${name} skill`;
|
|
44284
44491
|
const version = readDeclaredSkillVersion(skillPath) ?? PORTABLE_SKILL_DEFAULT_VERSION;
|
|
@@ -44307,12 +44514,12 @@ function parseSkillKind(value) {
|
|
|
44307
44514
|
return;
|
|
44308
44515
|
}
|
|
44309
44516
|
function readDeclaredSkillVersion(skillPath) {
|
|
44310
|
-
const skillJsonPath =
|
|
44311
|
-
const skillMdPath =
|
|
44312
|
-
const pkgPath =
|
|
44313
|
-
const jsonManifest =
|
|
44314
|
-
const frontmatter =
|
|
44315
|
-
const pkg =
|
|
44517
|
+
const skillJsonPath = join25(skillPath, "skill.json");
|
|
44518
|
+
const skillMdPath = join25(skillPath, "SKILL.md");
|
|
44519
|
+
const pkgPath = join25(skillPath, "package.json");
|
|
44520
|
+
const jsonManifest = existsSync9(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
|
|
44521
|
+
const frontmatter = existsSync9(skillMdPath) ? parseSkillFrontmatter(readFileSync16(skillMdPath, "utf-8")) ?? undefined : undefined;
|
|
44522
|
+
const pkg = existsSync9(pkgPath) ? readJsonObject(pkgPath) : undefined;
|
|
44316
44523
|
return stringField(jsonManifest, "version") ?? frontmatter?.version ?? stringValue(pkg?.version);
|
|
44317
44524
|
}
|
|
44318
44525
|
function createInstructionManifest(name, options) {
|
|
@@ -44333,7 +44540,7 @@ function createInstructionManifest(name, options) {
|
|
|
44333
44540
|
}
|
|
44334
44541
|
function writeInstructionSkillTemplate(skillPath, manifest) {
|
|
44335
44542
|
mkdirSync6(skillPath, { recursive: true });
|
|
44336
|
-
writeFileSync5(
|
|
44543
|
+
writeFileSync5(join25(skillPath, "SKILL.md"), renderInstructionSkillMd(manifest));
|
|
44337
44544
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
44338
44545
|
}
|
|
44339
44546
|
function renderInstructionSkillMd(manifest) {
|
|
@@ -44383,12 +44590,12 @@ function createPortableManifest(name, options) {
|
|
|
44383
44590
|
};
|
|
44384
44591
|
}
|
|
44385
44592
|
function writePortableSkillTemplate(skillPath, manifest) {
|
|
44386
|
-
mkdirSync6(
|
|
44387
|
-
writeFileSync5(
|
|
44388
|
-
writeFileSync5(
|
|
44389
|
-
writeFileSync5(
|
|
44390
|
-
writeFileSync5(
|
|
44391
|
-
writeFileSync5(
|
|
44593
|
+
mkdirSync6(join25(skillPath, "src"), { recursive: true });
|
|
44594
|
+
writeFileSync5(join25(skillPath, "SKILL.md"), renderSkillMd(manifest));
|
|
44595
|
+
writeFileSync5(join25(skillPath, "AGENTS.md"), renderAgentsMd(manifest));
|
|
44596
|
+
writeFileSync5(join25(skillPath, "package.json"), renderPackageJson(manifest));
|
|
44597
|
+
writeFileSync5(join25(skillPath, "tsconfig.json"), renderTsconfig());
|
|
44598
|
+
writeFileSync5(join25(skillPath, "src", "index.ts"), renderEntrypoint(manifest));
|
|
44392
44599
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
44393
44600
|
}
|
|
44394
44601
|
function fillContractDefaults(manifest, entrypoint) {
|
|
@@ -44413,7 +44620,7 @@ function writeSkillJsonWithHash(skillPath, manifest) {
|
|
|
44413
44620
|
content_hash: undefined
|
|
44414
44621
|
}
|
|
44415
44622
|
};
|
|
44416
|
-
writeFileSync5(
|
|
44623
|
+
writeFileSync5(join25(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withoutHash) }, null, 2)}
|
|
44417
44624
|
`);
|
|
44418
44625
|
const hash2 = computeContentHash(skillPath);
|
|
44419
44626
|
const withHash = {
|
|
@@ -44423,13 +44630,13 @@ function writeSkillJsonWithHash(skillPath, manifest) {
|
|
|
44423
44630
|
content_hash: hash2
|
|
44424
44631
|
}
|
|
44425
44632
|
};
|
|
44426
|
-
writeFileSync5(
|
|
44633
|
+
writeFileSync5(join25(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withHash) }, null, 2)}
|
|
44427
44634
|
`);
|
|
44428
44635
|
return withHash;
|
|
44429
44636
|
}
|
|
44430
44637
|
function readExistingSkillJson(skillPath) {
|
|
44431
|
-
const path =
|
|
44432
|
-
if (!
|
|
44638
|
+
const path = join25(skillPath, "skill.json");
|
|
44639
|
+
if (!existsSync9(path))
|
|
44433
44640
|
return {};
|
|
44434
44641
|
try {
|
|
44435
44642
|
const parsed = JSON.parse(readFileSync16(path, "utf-8"));
|
|
@@ -44462,28 +44669,28 @@ function ensurePortableSkillFiles(skillPath, manifest) {
|
|
|
44462
44669
|
tags: next.tags?.length ? next.tags : ["custom", next.name]
|
|
44463
44670
|
};
|
|
44464
44671
|
const entry = next.commands[0]?.entry ?? "src/index.ts";
|
|
44465
|
-
if (entry && !
|
|
44466
|
-
mkdirSync6(dirname16(
|
|
44467
|
-
writeFileSync5(
|
|
44672
|
+
if (entry && !existsSync9(join25(skillPath, entry))) {
|
|
44673
|
+
mkdirSync6(dirname16(join25(skillPath, entry)), { recursive: true });
|
|
44674
|
+
writeFileSync5(join25(skillPath, entry), renderEntrypoint(next));
|
|
44468
44675
|
}
|
|
44469
|
-
if (!
|
|
44470
|
-
writeFileSync5(
|
|
44676
|
+
if (!existsSync9(join25(skillPath, "SKILL.md")))
|
|
44677
|
+
writeFileSync5(join25(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
44471
44678
|
else
|
|
44472
|
-
writeFileSync5(
|
|
44473
|
-
if (!
|
|
44474
|
-
writeFileSync5(
|
|
44679
|
+
writeFileSync5(join25(skillPath, "SKILL.md"), ensureSkillMdFrontmatter(readFileSync16(join25(skillPath, "SKILL.md"), "utf-8"), next));
|
|
44680
|
+
if (!existsSync9(join25(skillPath, "AGENTS.md")))
|
|
44681
|
+
writeFileSync5(join25(skillPath, "AGENTS.md"), renderAgentsMd(next));
|
|
44475
44682
|
ensurePackageJson(skillPath, next);
|
|
44476
|
-
if (!
|
|
44477
|
-
writeFileSync5(
|
|
44683
|
+
if (!existsSync9(join25(skillPath, "tsconfig.json")))
|
|
44684
|
+
writeFileSync5(join25(skillPath, "tsconfig.json"), renderTsconfig());
|
|
44478
44685
|
writeSkillJsonWithHash(skillPath, next);
|
|
44479
44686
|
return readPortableSkillManifest(skillPath, next.name);
|
|
44480
44687
|
}
|
|
44481
44688
|
function ensurePackageJson(skillPath, manifest) {
|
|
44482
|
-
const pkgPath =
|
|
44689
|
+
const pkgPath = join25(skillPath, "package.json");
|
|
44483
44690
|
const first = manifest.commands[0] ?? { name: manifest.name, entry: "src/index.ts" };
|
|
44484
44691
|
const commandName = normalizePortableSkillName(first.name || manifest.name);
|
|
44485
44692
|
const entry = (first.entry ?? "src/index.ts").replace(/^\.\//, "");
|
|
44486
|
-
if (!
|
|
44693
|
+
if (!existsSync9(pkgPath)) {
|
|
44487
44694
|
writeFileSync5(pkgPath, renderPackageJson(manifest));
|
|
44488
44695
|
return;
|
|
44489
44696
|
}
|
|
@@ -44526,18 +44733,18 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
|
|
|
44526
44733
|
inputs: [],
|
|
44527
44734
|
commands: []
|
|
44528
44735
|
};
|
|
44529
|
-
if (!
|
|
44530
|
-
writeFileSync5(
|
|
44736
|
+
if (!existsSync9(join25(skillPath, "SKILL.md"))) {
|
|
44737
|
+
writeFileSync5(join25(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
44531
44738
|
} else {
|
|
44532
|
-
const path =
|
|
44739
|
+
const path = join25(skillPath, "SKILL.md");
|
|
44533
44740
|
const content = readFileSync16(path, "utf8");
|
|
44534
44741
|
const declaredName = parseSkillFrontmatter(content)?.name;
|
|
44535
44742
|
if (declaredName && declaredName !== next.name) {
|
|
44536
44743
|
writeFileSync5(path, renameInstructionFrontmatter(content, next.name));
|
|
44537
44744
|
}
|
|
44538
44745
|
}
|
|
44539
|
-
const packagePath =
|
|
44540
|
-
if (
|
|
44746
|
+
const packagePath = join25(skillPath, "package.json");
|
|
44747
|
+
if (existsSync9(packagePath)) {
|
|
44541
44748
|
const pkg = readJsonObject(packagePath);
|
|
44542
44749
|
if (typeof pkg.name === "string" && pkg.name !== next.name) {
|
|
44543
44750
|
writeFileSync5(packagePath, `${JSON.stringify({ ...pkg, name: next.name }, null, 2)}
|
|
@@ -44568,7 +44775,7 @@ function renameInstructionFrontmatter(content, name) {
|
|
|
44568
44775
|
return renamed + content.slice(frontmatter[0].length);
|
|
44569
44776
|
}
|
|
44570
44777
|
function copySkillDirectory(source, destination) {
|
|
44571
|
-
const resolvedSource =
|
|
44778
|
+
const resolvedSource = lstatSync21(source).isSymbolicLink() ? realpathSync9(source) : source;
|
|
44572
44779
|
mkdirSync6(destination, { recursive: true });
|
|
44573
44780
|
cpSync(resolvedSource, destination, {
|
|
44574
44781
|
recursive: true,
|
|
@@ -44581,7 +44788,7 @@ function copySkillDirectory(source, destination) {
|
|
|
44581
44788
|
if (isExcludedCopyEntry(segments[i], i === 0))
|
|
44582
44789
|
return false;
|
|
44583
44790
|
}
|
|
44584
|
-
if (
|
|
44791
|
+
if (lstatSync21(src).isSymbolicLink())
|
|
44585
44792
|
return false;
|
|
44586
44793
|
return true;
|
|
44587
44794
|
}
|
|
@@ -44860,12 +45067,12 @@ function escapeJsString(value) {
|
|
|
44860
45067
|
}
|
|
44861
45068
|
|
|
44862
45069
|
// src/lib/authoring-path.ts
|
|
44863
|
-
import { lstatSync as
|
|
45070
|
+
import { lstatSync as lstatSync22, realpathSync as realpathSync10 } from "fs";
|
|
44864
45071
|
import { homedir as homedir4 } from "os";
|
|
44865
|
-
import { basename as basename9, dirname as dirname17, join as
|
|
45072
|
+
import { basename as basename9, dirname as dirname17, join as join26, resolve as resolve17, sep as sep7 } from "path";
|
|
44866
45073
|
var NATIVE_DISCOVERY_ROOTS = NATIVE_SKILL_ROOTS.map(([, path]) => path.split("/"));
|
|
44867
45074
|
function pathParts(path) {
|
|
44868
|
-
return
|
|
45075
|
+
return resolve17(path).split(sep7).filter(Boolean);
|
|
44869
45076
|
}
|
|
44870
45077
|
function nativeRootMatch(path) {
|
|
44871
45078
|
const parts = pathParts(path);
|
|
@@ -44879,24 +45086,24 @@ function nativeRootMatch(path) {
|
|
|
44879
45086
|
return;
|
|
44880
45087
|
}
|
|
44881
45088
|
function physicalPath(path) {
|
|
44882
|
-
let cursor =
|
|
45089
|
+
let cursor = resolve17(path);
|
|
44883
45090
|
const suffix = [];
|
|
44884
|
-
while (!
|
|
45091
|
+
while (!lstatSync22(cursor, { throwIfNoEntry: false })) {
|
|
44885
45092
|
suffix.unshift(basename9(cursor));
|
|
44886
45093
|
const parent = dirname17(cursor);
|
|
44887
45094
|
if (parent === cursor)
|
|
44888
|
-
return
|
|
45095
|
+
return resolve17(path);
|
|
44889
45096
|
cursor = parent;
|
|
44890
45097
|
}
|
|
44891
|
-
return
|
|
45098
|
+
return resolve17(realpathSync10(cursor), ...suffix);
|
|
44892
45099
|
}
|
|
44893
45100
|
function contains(root2, path) {
|
|
44894
45101
|
return path === root2 || path.startsWith(root2 + sep7);
|
|
44895
45102
|
}
|
|
44896
45103
|
function configuredRoots(home, explicitHome) {
|
|
44897
|
-
const homeData =
|
|
45104
|
+
const homeData = join26(home, ".hasna", "skills");
|
|
44898
45105
|
const homePolicy = readManagedSkillPolicySnapshot(homeData);
|
|
44899
|
-
const configuredPolicy = !explicitHome &&
|
|
45106
|
+
const configuredPolicy = !explicitHome && resolve17(getDataDirReadOnly()) !== resolve17(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
|
|
44900
45107
|
const policy = homePolicy ?? configuredPolicy;
|
|
44901
45108
|
const bridge = policy?.value.bridge;
|
|
44902
45109
|
if (bridge && (typeof bridge.home !== "string" || physicalPath(bridge.home) !== physicalPath(home))) {
|
|
@@ -44925,8 +45132,8 @@ function configuredRoots(home, explicitHome) {
|
|
|
44925
45132
|
admitBinding(binding);
|
|
44926
45133
|
continue;
|
|
44927
45134
|
}
|
|
44928
|
-
const nativeHome =
|
|
44929
|
-
if (!
|
|
45135
|
+
const nativeHome = join26(home, agent === "opencode" ? ".config/opencode" : `.${agent}`);
|
|
45136
|
+
if (!lstatSync22(nativeHome, { throwIfNoEntry: false }))
|
|
44930
45137
|
continue;
|
|
44931
45138
|
roots.push(...resolveAgentDiscovery({ home, agent, canonical: physicalPath }).roots);
|
|
44932
45139
|
}
|
|
@@ -44935,22 +45142,22 @@ function configuredRoots(home, explicitHome) {
|
|
|
44935
45142
|
function assertPortableAuthoringPath(path, options = {}) {
|
|
44936
45143
|
if (typeof path !== "string" || !path || path.includes("\x00"))
|
|
44937
45144
|
throw new Error("Invalid portable authoring path");
|
|
44938
|
-
const lexical =
|
|
45145
|
+
const lexical = resolve17(path);
|
|
44939
45146
|
const physical = physicalPath(lexical);
|
|
44940
45147
|
const candidates = [lexical, physical];
|
|
44941
45148
|
let native = candidates.map(nativeRootMatch).find(Boolean);
|
|
44942
|
-
const home =
|
|
44943
|
-
const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) =>
|
|
44944
|
-
defaults.push(
|
|
45149
|
+
const home = resolve17(options.homeDir ?? homedir4());
|
|
45150
|
+
const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join26(home, ...parts));
|
|
45151
|
+
defaults.push(join26(home, ".codex/plugins/cache"), join26(home, ".claude/plugins/cache"), join26(home, ".claude/plugins/synced"), join26(home, ".gemini/extensions"));
|
|
44945
45152
|
for (const root2 of defaults) {
|
|
44946
|
-
if (candidates.some((candidate2) => contains(
|
|
45153
|
+
if (candidates.some((candidate2) => contains(resolve17(root2), candidate2) || contains(physicalPath(root2), candidate2)))
|
|
44947
45154
|
native ??= root2;
|
|
44948
45155
|
}
|
|
44949
45156
|
if (native) {
|
|
44950
45157
|
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
45158
|
}
|
|
44952
45159
|
for (const root2 of configuredRoots(home, options.homeDir !== undefined)) {
|
|
44953
|
-
if (candidates.some((candidate2) => contains(
|
|
45160
|
+
if (candidates.some((candidate2) => contains(resolve17(root2), candidate2) || contains(physicalPath(root2), candidate2))) {
|
|
44954
45161
|
throw new Error(`Refusing portable Skills authoring below configured native discovery root: ${lexical}`);
|
|
44955
45162
|
}
|
|
44956
45163
|
}
|
|
@@ -44962,15 +45169,15 @@ function getPortableSkillsRoot(options = {}) {
|
|
|
44962
45169
|
function resolvePortableSkillsRoot(options, migrate) {
|
|
44963
45170
|
if (options.rootDir)
|
|
44964
45171
|
return options.rootDir;
|
|
44965
|
-
const appDir = options.homeDir ?
|
|
44966
|
-
const cache =
|
|
45172
|
+
const appDir = options.homeDir ? join27(options.homeDir, ".hasna", "skills") : migrate ? getDataDir() : getDataDirReadOnly();
|
|
45173
|
+
const cache = join27(appDir, SKILLS_CACHE_DIRNAME);
|
|
44967
45174
|
if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache))
|
|
44968
45175
|
return cache;
|
|
44969
|
-
const installed =
|
|
45176
|
+
const installed = join27(appDir, INSTALLED_SKILLS_DIRNAME);
|
|
44970
45177
|
return installed;
|
|
44971
45178
|
}
|
|
44972
45179
|
function getPortableSkillPath(name, options = {}) {
|
|
44973
|
-
return
|
|
45180
|
+
return join27(getPortableSkillsRoot(options), normalizePortableSkillName(name));
|
|
44974
45181
|
}
|
|
44975
45182
|
function findPortableSkill(name, options = {}) {
|
|
44976
45183
|
let normalized;
|
|
@@ -44980,7 +45187,7 @@ function findPortableSkill(name, options = {}) {
|
|
|
44980
45187
|
return null;
|
|
44981
45188
|
}
|
|
44982
45189
|
const path = getPortableSkillPath(normalized, options);
|
|
44983
|
-
if (!
|
|
45190
|
+
if (!existsSync10(path) || !statSync9(path).isDirectory())
|
|
44984
45191
|
return null;
|
|
44985
45192
|
try {
|
|
44986
45193
|
return summarizePortableSkill(path, normalized);
|
|
@@ -44996,7 +45203,7 @@ function listPortableSkills(options = {}) {
|
|
|
44996
45203
|
for (const entry of readdirSync10(root2).sort()) {
|
|
44997
45204
|
if (entry.startsWith("."))
|
|
44998
45205
|
continue;
|
|
44999
|
-
const path =
|
|
45206
|
+
const path = join27(root2, entry);
|
|
45000
45207
|
if (!safeIsDirectory(path))
|
|
45001
45208
|
continue;
|
|
45002
45209
|
try {
|
|
@@ -45030,11 +45237,11 @@ function isOfficialSkillName(name) {
|
|
|
45030
45237
|
function scaffoldPortableSkill(name, options = {}) {
|
|
45031
45238
|
const skillName = normalizeNewPortableSkillName(name);
|
|
45032
45239
|
const readOnlyRoot = resolvePortableSkillsRoot(options, false);
|
|
45033
|
-
assertPortableAuthoringPath(
|
|
45240
|
+
assertPortableAuthoringPath(join27(readOnlyRoot, skillName), options);
|
|
45034
45241
|
const root2 = getPortableSkillsRoot(options);
|
|
45035
|
-
const skillPath =
|
|
45242
|
+
const skillPath = join27(root2, skillName);
|
|
45036
45243
|
assertPortableAuthoringPath(skillPath, options);
|
|
45037
|
-
if (
|
|
45244
|
+
if (existsSync10(skillPath)) {
|
|
45038
45245
|
if (!options.overwrite)
|
|
45039
45246
|
throw new Error(`Skill '${skillName}' already exists at ${skillPath}`);
|
|
45040
45247
|
rmSync2(skillPath, { recursive: true, force: true });
|
|
@@ -45052,7 +45259,7 @@ function scaffoldPortableSkill(name, options = {}) {
|
|
|
45052
45259
|
}
|
|
45053
45260
|
function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
45054
45261
|
const absoluteSource = normalize4(sourceDir);
|
|
45055
|
-
if (!
|
|
45262
|
+
if (!existsSync10(absoluteSource) || !statSync9(absoluteSource).isDirectory()) {
|
|
45056
45263
|
throw new Error(`Import directory not found: ${sourceDir}`);
|
|
45057
45264
|
}
|
|
45058
45265
|
const continueOnError = options.continueOnError ?? true;
|
|
@@ -45066,7 +45273,7 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
|
45066
45273
|
const skipped = [];
|
|
45067
45274
|
const entries = readdirSync10(absoluteSource, { withFileTypes: true }).map((entry) => entry.name).filter((entryName) => !entryName.startsWith(".")).sort();
|
|
45068
45275
|
for (const entryName of entries) {
|
|
45069
|
-
const childPath =
|
|
45276
|
+
const childPath = join27(absoluteSource, entryName);
|
|
45070
45277
|
if (!safeIsDirectory(childPath))
|
|
45071
45278
|
continue;
|
|
45072
45279
|
if (!isSkillCandidate(childPath)) {
|
|
@@ -45095,19 +45302,19 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
|
45095
45302
|
};
|
|
45096
45303
|
}
|
|
45097
45304
|
function isSkillCandidate(dir) {
|
|
45098
|
-
return
|
|
45305
|
+
return existsSync10(join27(dir, "SKILL.md")) || existsSync10(join27(dir, "skill.json")) || existsSync10(join27(dir, "package.json"));
|
|
45099
45306
|
}
|
|
45100
45307
|
function physicalImportPath(path) {
|
|
45101
|
-
let ancestor =
|
|
45308
|
+
let ancestor = resolve18(path);
|
|
45102
45309
|
const suffix = [];
|
|
45103
|
-
while (!
|
|
45310
|
+
while (!existsSync10(ancestor)) {
|
|
45104
45311
|
suffix.unshift(basename10(ancestor));
|
|
45105
45312
|
ancestor = dirname18(ancestor);
|
|
45106
45313
|
}
|
|
45107
|
-
return
|
|
45314
|
+
return join27(realpathSync11(ancestor), ...suffix);
|
|
45108
45315
|
}
|
|
45109
45316
|
function assertDisjointImportPaths(source, destination) {
|
|
45110
|
-
const sourcePath =
|
|
45317
|
+
const sourcePath = realpathSync11(source);
|
|
45111
45318
|
const destinationPath = physicalImportPath(destination);
|
|
45112
45319
|
const contains2 = (parent, child) => {
|
|
45113
45320
|
const path = relative6(parent, child);
|
|
@@ -45119,7 +45326,7 @@ function assertDisjointImportPaths(source, destination) {
|
|
|
45119
45326
|
}
|
|
45120
45327
|
function portPortableSkill(sourcePath, options = {}) {
|
|
45121
45328
|
const absoluteSource = normalize4(sourcePath);
|
|
45122
|
-
if (!
|
|
45329
|
+
if (!existsSync10(absoluteSource) || !statSync9(absoluteSource).isDirectory()) {
|
|
45123
45330
|
throw new Error(`Skill source directory not found: ${sourcePath}`);
|
|
45124
45331
|
}
|
|
45125
45332
|
const inferred = readPortableSkillManifestForImport(absoluteSource);
|
|
@@ -45130,14 +45337,14 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
45130
45337
|
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
45338
|
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
45339
|
}
|
|
45133
|
-
const readOnlyDestination =
|
|
45340
|
+
const readOnlyDestination = join27(resolvePortableSkillsRoot(options, false), skillName);
|
|
45134
45341
|
assertPortableAuthoringPath(readOnlyDestination, options);
|
|
45135
45342
|
assertDisjointImportPaths(absoluteSource, readOnlyDestination);
|
|
45136
45343
|
const root2 = getPortableSkillsRoot(options);
|
|
45137
|
-
const destination =
|
|
45344
|
+
const destination = join27(root2, skillName);
|
|
45138
45345
|
assertPortableAuthoringPath(destination, options);
|
|
45139
45346
|
assertDisjointImportPaths(absoluteSource, destination);
|
|
45140
|
-
if (
|
|
45347
|
+
if (existsSync10(destination)) {
|
|
45141
45348
|
if (!options.overwrite)
|
|
45142
45349
|
throw new Error(`Skill '${skillName}' already exists at ${destination}`);
|
|
45143
45350
|
rmSync2(destination, { recursive: true, force: true });
|
|
@@ -45179,13 +45386,13 @@ function buildCorpusManifest(input, name) {
|
|
|
45179
45386
|
function writeCorpusSkill(input, options = {}) {
|
|
45180
45387
|
const name = normalizePortableSkillName(input.name);
|
|
45181
45388
|
const readOnlyRoot = resolvePortableSkillsRoot(options, false);
|
|
45182
|
-
assertPortableAuthoringPath(
|
|
45389
|
+
assertPortableAuthoringPath(join27(readOnlyRoot, name), options);
|
|
45183
45390
|
const root2 = getPortableSkillsRoot(options);
|
|
45184
|
-
const skillPath =
|
|
45391
|
+
const skillPath = join27(root2, name);
|
|
45185
45392
|
assertPortableAuthoringPath(skillPath, options);
|
|
45186
|
-
const created = !
|
|
45393
|
+
const created = !existsSync10(skillPath);
|
|
45187
45394
|
mkdirSync7(skillPath, { recursive: true });
|
|
45188
|
-
writeFileSync6(
|
|
45395
|
+
writeFileSync6(join27(skillPath, "SKILL.md"), input.skillMd);
|
|
45189
45396
|
const manifest = buildCorpusManifest(input, name);
|
|
45190
45397
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
45191
45398
|
return { name, path: skillPath, manifest, created };
|
|
@@ -45193,22 +45400,22 @@ function writeCorpusSkill(input, options = {}) {
|
|
|
45193
45400
|
function installCorpusSkillAtomically(input, options = {}) {
|
|
45194
45401
|
const name = normalizePortableSkillName(input.name);
|
|
45195
45402
|
const readOnlyRoot = resolvePortableSkillsRoot(options, false);
|
|
45196
|
-
assertPortableAuthoringPath(
|
|
45403
|
+
assertPortableAuthoringPath(join27(readOnlyRoot, name), options);
|
|
45197
45404
|
const root2 = getPortableSkillsRoot(options);
|
|
45198
|
-
const target =
|
|
45405
|
+
const target = join27(root2, name);
|
|
45199
45406
|
assertPortableAuthoringPath(target, options);
|
|
45200
|
-
const created = !
|
|
45407
|
+
const created = !existsSync10(target);
|
|
45201
45408
|
mkdirSync7(root2, { recursive: true });
|
|
45202
|
-
const staging = mkdtempSync(
|
|
45409
|
+
const staging = mkdtempSync(join27(root2, `.pull-${name}-`));
|
|
45203
45410
|
let moved = false;
|
|
45204
45411
|
let backup = null;
|
|
45205
45412
|
try {
|
|
45206
|
-
writeFileSync6(
|
|
45413
|
+
writeFileSync6(join27(staging, "SKILL.md"), input.skillMd);
|
|
45207
45414
|
const manifest = buildCorpusManifest(input, name);
|
|
45208
45415
|
writeSkillJsonWithHash(staging, manifest);
|
|
45209
|
-
if (
|
|
45210
|
-
backup = mkdtempSync(
|
|
45211
|
-
renameSync4(target,
|
|
45416
|
+
if (existsSync10(target)) {
|
|
45417
|
+
backup = mkdtempSync(join27(root2, `.pull-backup-${name}-`));
|
|
45418
|
+
renameSync4(target, join27(backup, name));
|
|
45212
45419
|
moved = true;
|
|
45213
45420
|
}
|
|
45214
45421
|
renameSync4(staging, target);
|
|
@@ -45217,9 +45424,9 @@ function installCorpusSkillAtomically(input, options = {}) {
|
|
|
45217
45424
|
return { name, path: target, manifest, created };
|
|
45218
45425
|
} catch (error) {
|
|
45219
45426
|
rmSync2(staging, { recursive: true, force: true });
|
|
45220
|
-
if (moved && backup &&
|
|
45427
|
+
if (moved && backup && existsSync10(join27(backup, name))) {
|
|
45221
45428
|
try {
|
|
45222
|
-
renameSync4(
|
|
45429
|
+
renameSync4(join27(backup, name), target);
|
|
45223
45430
|
} catch {}
|
|
45224
45431
|
}
|
|
45225
45432
|
throw error;
|
|
@@ -45231,10 +45438,10 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
45231
45438
|
const issues = [...base.issues];
|
|
45232
45439
|
const warnings = [...base.warnings];
|
|
45233
45440
|
let manifest;
|
|
45234
|
-
if (
|
|
45235
|
-
const skillJsonPath =
|
|
45236
|
-
const skillMdPath =
|
|
45237
|
-
if (!
|
|
45441
|
+
if (existsSync10(skillPath)) {
|
|
45442
|
+
const skillJsonPath = join27(skillPath, "skill.json");
|
|
45443
|
+
const skillMdPath = join27(skillPath, "SKILL.md");
|
|
45444
|
+
if (!existsSync10(skillJsonPath) && !existsSync10(skillMdPath)) {
|
|
45238
45445
|
add3(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
|
|
45239
45446
|
}
|
|
45240
45447
|
try {
|
|
@@ -45253,7 +45460,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
45253
45460
|
add3(issues, "portable.version_missing", "Portable manifest missing version");
|
|
45254
45461
|
}
|
|
45255
45462
|
const contractIssues = validatePortableManifestContract(manifest, {
|
|
45256
|
-
strict:
|
|
45463
|
+
strict: existsSync10(join27(skillPath, "skill.json")),
|
|
45257
45464
|
skillPath
|
|
45258
45465
|
});
|
|
45259
45466
|
for (const issue of contractIssues)
|
|
@@ -45293,8 +45500,8 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
45293
45500
|
add3(issues, "portable.command_entry_unsafe", `Command '${command.name}' entry '${command.entry}' must stay inside the skill directory`);
|
|
45294
45501
|
continue;
|
|
45295
45502
|
}
|
|
45296
|
-
const entryPath =
|
|
45297
|
-
if (!
|
|
45503
|
+
const entryPath = join27(skillPath, command.entry);
|
|
45504
|
+
if (!existsSync10(entryPath))
|
|
45298
45505
|
add3(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
|
|
45299
45506
|
else if (statSync9(entryPath).isDirectory())
|
|
45300
45507
|
add3(issues, "portable.command_entry_directory", `Command '${command.name}' entry '${command.entry}' must be a file`);
|
|
@@ -45304,7 +45511,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
45304
45511
|
} catch (error) {
|
|
45305
45512
|
add3(issues, "portable.manifest_invalid", error.message);
|
|
45306
45513
|
}
|
|
45307
|
-
if (manifest?.kind !== "instruction" && !
|
|
45514
|
+
if (manifest?.kind !== "instruction" && !existsSync10(join27(skillPath, "AGENTS.md"))) {
|
|
45308
45515
|
add3(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
|
|
45309
45516
|
}
|
|
45310
45517
|
}
|
|
@@ -45340,13 +45547,13 @@ async function runPortableSkill(name, args, options = {}) {
|
|
|
45340
45547
|
if (!isSafeRelativePath2(command.entry)) {
|
|
45341
45548
|
return { exitCode: 1, error: `Portable skill '${name}' command entry is unsafe` };
|
|
45342
45549
|
}
|
|
45343
|
-
const entryPath =
|
|
45344
|
-
if (!
|
|
45550
|
+
const entryPath = join27(skill.path, command.entry);
|
|
45551
|
+
if (!existsSync10(entryPath)) {
|
|
45345
45552
|
return { exitCode: 1, error: `Entry point '${command.entry}' not found in portable skill '${name}'` };
|
|
45346
45553
|
}
|
|
45347
|
-
const pkgPath =
|
|
45348
|
-
const nodeModules =
|
|
45349
|
-
if (
|
|
45554
|
+
const pkgPath = join27(skill.path, "package.json");
|
|
45555
|
+
const nodeModules = join27(skill.path, "node_modules");
|
|
45556
|
+
if (existsSync10(pkgPath) && !existsSync10(nodeModules) && hasPackageDependencies(pkgPath)) {
|
|
45350
45557
|
const install = Bun.spawn(["bun", "install", "--no-save"], {
|
|
45351
45558
|
cwd: skill.path,
|
|
45352
45559
|
stdout: "pipe",
|
|
@@ -45417,9 +45624,9 @@ function isSkillsOwnershipMarker(marker) {
|
|
|
45417
45624
|
return typeof marker === "object" && marker !== null && Object.hasOwn(marker, "managedBy") && marker.managedBy === SYNC_MARKER_MANAGED_BY;
|
|
45418
45625
|
}
|
|
45419
45626
|
function hasSkillsOwnershipMarker(dir) {
|
|
45420
|
-
const path =
|
|
45627
|
+
const path = join28(dir, SYNC_MARKER_FILE);
|
|
45421
45628
|
try {
|
|
45422
|
-
if (!
|
|
45629
|
+
if (!lstatSync23(path).isFile())
|
|
45423
45630
|
return false;
|
|
45424
45631
|
const marker = JSON.parse(readFileSync17(path, "utf8"));
|
|
45425
45632
|
return isSkillsOwnershipMarker(marker);
|
|
@@ -45441,9 +45648,9 @@ function resolveSyncAgents(arg) {
|
|
|
45441
45648
|
function agentGlobalSkillsDir(agent, homeDir3 = homedir5()) {
|
|
45442
45649
|
switch (agent) {
|
|
45443
45650
|
case "opencode":
|
|
45444
|
-
return
|
|
45651
|
+
return join28(homeDir3, ".config", "opencode", "skills");
|
|
45445
45652
|
default:
|
|
45446
|
-
return
|
|
45653
|
+
return join28(homeDir3, `.${agent}`, "skills");
|
|
45447
45654
|
}
|
|
45448
45655
|
}
|
|
45449
45656
|
function adaptSkillMdForAgent(skillMd, agent) {
|
|
@@ -45502,8 +45709,8 @@ function resolveSyncCorpus(options = {}) {
|
|
|
45502
45709
|
function packageSourceRoots(source) {
|
|
45503
45710
|
const roots = [];
|
|
45504
45711
|
for (const sub of ["skills"]) {
|
|
45505
|
-
const candidate2 =
|
|
45506
|
-
if (
|
|
45712
|
+
const candidate2 = join28(source, sub);
|
|
45713
|
+
if (existsSync11(candidate2) && isDirectory(candidate2))
|
|
45507
45714
|
roots.push(candidate2);
|
|
45508
45715
|
}
|
|
45509
45716
|
if (roots.length > 0)
|
|
@@ -45518,10 +45725,10 @@ function containsSkillDirectories(path) {
|
|
|
45518
45725
|
return false;
|
|
45519
45726
|
}
|
|
45520
45727
|
return entries.some((entry) => {
|
|
45521
|
-
const candidate2 =
|
|
45728
|
+
const candidate2 = join28(path, entry);
|
|
45522
45729
|
if (!isDirectory(candidate2))
|
|
45523
45730
|
return false;
|
|
45524
|
-
return
|
|
45731
|
+
return existsSync11(join28(candidate2, "SKILL.md")) || existsSync11(join28(candidate2, "skill.json")) || existsSync11(join28(candidate2, "package.json"));
|
|
45525
45732
|
});
|
|
45526
45733
|
}
|
|
45527
45734
|
function isDirectory(path) {
|
|
@@ -45561,7 +45768,7 @@ function syncSkillsToAgents(options = {}) {
|
|
|
45561
45768
|
actions.push({
|
|
45562
45769
|
skill: name,
|
|
45563
45770
|
agent,
|
|
45564
|
-
path:
|
|
45771
|
+
path: join28(agentGlobalSkillsDir(agent, homeDir3), name, "SKILL.md"),
|
|
45565
45772
|
action: "skip",
|
|
45566
45773
|
reason: "not found in this machine's corpus"
|
|
45567
45774
|
});
|
|
@@ -45591,7 +45798,7 @@ function syncSkillsToAgents(options = {}) {
|
|
|
45591
45798
|
}
|
|
45592
45799
|
function writeManagedAgentSkill(params) {
|
|
45593
45800
|
const homeDir3 = params.homeDir ?? homedir5();
|
|
45594
|
-
const dir =
|
|
45801
|
+
const dir = join28(agentGlobalSkillsDir(params.agent, homeDir3), params.skill);
|
|
45595
45802
|
const result = writeManagedSkillDir(dir, params.skillMd, {
|
|
45596
45803
|
skill: params.skill,
|
|
45597
45804
|
source: params.source,
|
|
@@ -45608,11 +45815,11 @@ function writeManagedAgentSkill(params) {
|
|
|
45608
45815
|
};
|
|
45609
45816
|
}
|
|
45610
45817
|
function writeManagedSkillDir(dir, skillMd, options) {
|
|
45611
|
-
const skillMdPath =
|
|
45612
|
-
const markerPath =
|
|
45613
|
-
const dirExists =
|
|
45818
|
+
const skillMdPath = join28(dir, "SKILL.md");
|
|
45819
|
+
const markerPath = join28(dir, SYNC_MARKER_FILE);
|
|
45820
|
+
const dirExists = existsSync11(dir);
|
|
45614
45821
|
const managed = hasSkillsOwnershipMarker(dir);
|
|
45615
|
-
const hasSkillMd =
|
|
45822
|
+
const hasSkillMd = existsSync11(skillMdPath);
|
|
45616
45823
|
if (dirExists && !managed && !hasSkillMd) {
|
|
45617
45824
|
return {
|
|
45618
45825
|
action: "skip",
|
|
@@ -45624,7 +45831,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
45624
45831
|
return {
|
|
45625
45832
|
action: "skip",
|
|
45626
45833
|
path: skillMdPath,
|
|
45627
|
-
reason:
|
|
45834
|
+
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
45835
|
};
|
|
45629
45836
|
}
|
|
45630
45837
|
if (dirExists && managed && hasSkillMd && !options.force && isPointerSkillMd(skillMd)) {
|
|
@@ -45647,11 +45854,11 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
45647
45854
|
return { action, path: skillMdPath };
|
|
45648
45855
|
const parentDir = dirname19(dir);
|
|
45649
45856
|
mkdirSync8(parentDir, { recursive: true });
|
|
45650
|
-
const transactionDir = mkdtempSync2(
|
|
45651
|
-
const candidateDir =
|
|
45652
|
-
const backupDir =
|
|
45653
|
-
const candidateSkillMdPath =
|
|
45654
|
-
const candidateMarkerPath =
|
|
45857
|
+
const transactionDir = mkdtempSync2(join28(parentDir, `.hasna-skills-write-${basename11(dir)}-`));
|
|
45858
|
+
const candidateDir = join28(transactionDir, "candidate");
|
|
45859
|
+
const backupDir = join28(transactionDir, "backup");
|
|
45860
|
+
const candidateSkillMdPath = join28(candidateDir, "SKILL.md");
|
|
45861
|
+
const candidateMarkerPath = join28(candidateDir, SYNC_MARKER_FILE);
|
|
45655
45862
|
const marker = {
|
|
45656
45863
|
managedBy: SYNC_MARKER_MANAGED_BY,
|
|
45657
45864
|
skill: options.skill,
|
|
@@ -45678,9 +45885,9 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
45678
45885
|
}
|
|
45679
45886
|
renameDirectory(candidateDir, dir);
|
|
45680
45887
|
} catch (error) {
|
|
45681
|
-
if (originalMoved &&
|
|
45888
|
+
if (originalMoved && existsSync11(backupDir)) {
|
|
45682
45889
|
try {
|
|
45683
|
-
if (
|
|
45890
|
+
if (existsSync11(dir))
|
|
45684
45891
|
rmSync3(dir, { recursive: true, force: true });
|
|
45685
45892
|
renameDirectory(backupDir, dir);
|
|
45686
45893
|
originalMoved = false;
|
|
@@ -45700,7 +45907,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
45700
45907
|
return { action, path: skillMdPath };
|
|
45701
45908
|
}
|
|
45702
45909
|
function removeManagedAgentSkill(skill, agent, homeDir3 = homedir5()) {
|
|
45703
|
-
const dir =
|
|
45910
|
+
const dir = join28(agentGlobalSkillsDir(agent, homeDir3), skill);
|
|
45704
45911
|
if (!hasSkillsOwnershipMarker(dir))
|
|
45705
45912
|
return false;
|
|
45706
45913
|
rmSync3(dir, { recursive: true, force: true });
|
|
@@ -45708,8 +45915,8 @@ function removeManagedAgentSkill(skill, agent, homeDir3 = homedir5()) {
|
|
|
45708
45915
|
}
|
|
45709
45916
|
function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = false) {
|
|
45710
45917
|
if (kind === undefined || kind === "instruction" || preferBundledDocs) {
|
|
45711
|
-
const skillMdPath =
|
|
45712
|
-
if (
|
|
45918
|
+
const skillMdPath = join28(skillPath, "SKILL.md");
|
|
45919
|
+
if (existsSync11(skillMdPath))
|
|
45713
45920
|
return readFileSync17(skillMdPath, "utf-8");
|
|
45714
45921
|
}
|
|
45715
45922
|
return pointerSkillMd(name, description);
|
|
@@ -45740,8 +45947,8 @@ function normalizeSkillName(name) {
|
|
|
45740
45947
|
}
|
|
45741
45948
|
|
|
45742
45949
|
// src/lib/registry.ts
|
|
45743
|
-
import { existsSync as
|
|
45744
|
-
import { join as
|
|
45950
|
+
import { existsSync as existsSync12, readFileSync as readFileSync18, readdirSync as readdirSync12 } from "fs";
|
|
45951
|
+
import { join as join29 } from "path";
|
|
45745
45952
|
|
|
45746
45953
|
// src/lib/registry-merge.ts
|
|
45747
45954
|
var SKILL_SOURCE_PRECEDENCE = {
|
|
@@ -45943,7 +46150,7 @@ function parseSkillMdFrontmatter(content) {
|
|
|
45943
46150
|
return Object.keys(result).length > 0 ? result : null;
|
|
45944
46151
|
}
|
|
45945
46152
|
function discoverSkillsInDir(dir, source = "custom") {
|
|
45946
|
-
if (!
|
|
46153
|
+
if (!existsSync12(dir))
|
|
45947
46154
|
return [];
|
|
45948
46155
|
const result = [];
|
|
45949
46156
|
try {
|
|
@@ -45951,8 +46158,8 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
45951
46158
|
for (const entry of entries) {
|
|
45952
46159
|
if (!entry.isDirectory())
|
|
45953
46160
|
continue;
|
|
45954
|
-
const skillMdPath =
|
|
45955
|
-
if (!
|
|
46161
|
+
const skillMdPath = join29(dir, entry.name, "SKILL.md");
|
|
46162
|
+
if (!existsSync12(skillMdPath))
|
|
45956
46163
|
continue;
|
|
45957
46164
|
let content;
|
|
45958
46165
|
try {
|
|
@@ -45971,7 +46178,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
45971
46178
|
category: fm.category || "Development Tools",
|
|
45972
46179
|
tags: fm.tags || [],
|
|
45973
46180
|
...fm.kind ? { kind: fm.kind } : {},
|
|
45974
|
-
...isHostedMetadataSkillDir(
|
|
46181
|
+
...isHostedMetadataSkillDir(join29(dir, entry.name)) ? { serverOwned: true } : {},
|
|
45975
46182
|
source
|
|
45976
46183
|
});
|
|
45977
46184
|
}
|
|
@@ -45980,16 +46187,16 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
45980
46187
|
}
|
|
45981
46188
|
function findExtensionSkillPath(name) {
|
|
45982
46189
|
const config = loadConfig();
|
|
45983
|
-
if (!config.extensionsDir || !
|
|
46190
|
+
if (!config.extensionsDir || !existsSync12(config.extensionsDir))
|
|
45984
46191
|
return null;
|
|
45985
46192
|
try {
|
|
45986
46193
|
const entries = readdirSync12(config.extensionsDir, { withFileTypes: true });
|
|
45987
46194
|
for (const entry of entries) {
|
|
45988
46195
|
if (!entry.isDirectory())
|
|
45989
46196
|
continue;
|
|
45990
|
-
const skillDir =
|
|
45991
|
-
const skillMdPath =
|
|
45992
|
-
if (!
|
|
46197
|
+
const skillDir = join29(config.extensionsDir, entry.name);
|
|
46198
|
+
const skillMdPath = join29(skillDir, "SKILL.md");
|
|
46199
|
+
if (!existsSync12(skillMdPath))
|
|
45993
46200
|
continue;
|
|
45994
46201
|
let content;
|
|
45995
46202
|
try {
|
|
@@ -46036,21 +46243,21 @@ function clearRegistryCache() {
|
|
|
46036
46243
|
registryCacheTime = 0;
|
|
46037
46244
|
registryCacheKey = null;
|
|
46038
46245
|
}
|
|
46039
|
-
function getSkillsByCategory(category) {
|
|
46040
|
-
return loadRegistry().filter((s) => s.category === category);
|
|
46246
|
+
function getSkillsByCategory(category, catalog) {
|
|
46247
|
+
return (catalog ?? loadRegistry()).filter((s) => s.category === category);
|
|
46041
46248
|
}
|
|
46042
46249
|
function getSkill(name) {
|
|
46043
46250
|
const registry = loadRegistry();
|
|
46044
46251
|
const slug = normalizeSkillSlug(name);
|
|
46045
46252
|
return registry.find((s) => s.name === slug) ?? registry.find((s) => s.name === resolveSkillAlias(slug));
|
|
46046
46253
|
}
|
|
46047
|
-
function getSkillsByTag(tag) {
|
|
46254
|
+
function getSkillsByTag(tag, catalog) {
|
|
46048
46255
|
const needle = tag.toLowerCase();
|
|
46049
|
-
return loadRegistry().filter((s) => s.tags.some((t2) => t2.toLowerCase().includes(needle)));
|
|
46256
|
+
return (catalog ?? loadRegistry()).filter((s) => s.tags.some((t2) => t2.toLowerCase().includes(needle)));
|
|
46050
46257
|
}
|
|
46051
|
-
function getAllTags() {
|
|
46258
|
+
function getAllTags(catalog) {
|
|
46052
46259
|
const tagSet = new Set;
|
|
46053
|
-
for (const skill of loadRegistry()) {
|
|
46260
|
+
for (const skill of catalog ?? loadRegistry()) {
|
|
46054
46261
|
for (const tag of skill.tags)
|
|
46055
46262
|
tagSet.add(tag.toLowerCase());
|
|
46056
46263
|
}
|
|
@@ -46058,8 +46265,8 @@ function getAllTags() {
|
|
|
46058
46265
|
}
|
|
46059
46266
|
|
|
46060
46267
|
// src/lib/project-state.ts
|
|
46061
|
-
import { existsSync as
|
|
46062
|
-
import { join as
|
|
46268
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync19, writeFileSync as writeFileSync8 } from "fs";
|
|
46269
|
+
import { join as join30 } from "path";
|
|
46063
46270
|
var VALID_PIN_SOURCES = [
|
|
46064
46271
|
"official",
|
|
46065
46272
|
"custom",
|
|
@@ -46074,14 +46281,14 @@ var SKILLS_PROJECT_DIR = ".skills";
|
|
|
46074
46281
|
var PROJECT_CONFIG_FILE = "project.json";
|
|
46075
46282
|
var DEFAULT_EXPORT_DIR = ".skills/exports";
|
|
46076
46283
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
46077
|
-
return
|
|
46284
|
+
return join30(targetDir, SKILLS_PROJECT_DIR);
|
|
46078
46285
|
}
|
|
46079
46286
|
function getProjectConfigPath(targetDir = process.cwd()) {
|
|
46080
|
-
return
|
|
46287
|
+
return join30(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
|
|
46081
46288
|
}
|
|
46082
46289
|
function loadProjectConfig(targetDir = process.cwd()) {
|
|
46083
46290
|
const path = getProjectConfigPath(targetDir);
|
|
46084
|
-
if (!
|
|
46291
|
+
if (!existsSync13(path))
|
|
46085
46292
|
return null;
|
|
46086
46293
|
try {
|
|
46087
46294
|
return normalizeProjectConfig(JSON.parse(readFileSync19(path, "utf-8")));
|
|
@@ -46206,19 +46413,19 @@ function getSkillPath(name) {
|
|
|
46206
46413
|
const extensionPath = findExtensionSkillPath(skillName);
|
|
46207
46414
|
if (extensionPath)
|
|
46208
46415
|
return extensionPath;
|
|
46209
|
-
return
|
|
46416
|
+
return join31(getPortableSkillsRoot(), skillName);
|
|
46210
46417
|
}
|
|
46211
46418
|
function getCanonicalSkillName(name) {
|
|
46212
46419
|
return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
|
|
46213
46420
|
}
|
|
46214
46421
|
function skillExists(name) {
|
|
46215
|
-
return
|
|
46422
|
+
return existsSync14(getSkillPath(name));
|
|
46216
46423
|
}
|
|
46217
46424
|
function installSkill(name, options = {}) {
|
|
46218
46425
|
const { targetDir = process.cwd(), overwrite = false } = options;
|
|
46219
46426
|
const canonicalName = getCanonicalSkillName(name);
|
|
46220
46427
|
const skillName = normalizeSkillName(canonicalName);
|
|
46221
|
-
if (!
|
|
46428
|
+
if (!existsSync14(getSkillPath(name))) {
|
|
46222
46429
|
const knownOfficial = Boolean(getSkill(name));
|
|
46223
46430
|
return {
|
|
46224
46431
|
skill: canonicalName,
|
|
@@ -46245,7 +46452,7 @@ function installSkill(name, options = {}) {
|
|
|
46245
46452
|
}
|
|
46246
46453
|
function installSkillSource(name, _options = {}) {
|
|
46247
46454
|
const canonicalName = getCanonicalSkillName(name);
|
|
46248
|
-
if (!
|
|
46455
|
+
if (!existsSync14(getSkillPath(name))) {
|
|
46249
46456
|
return { skill: canonicalName, success: false, error: `Skill '${name}' not found`, mode: "source" };
|
|
46250
46457
|
}
|
|
46251
46458
|
return {
|
|
@@ -46266,11 +46473,11 @@ function installSkillManifest(manifest, _options = {}) {
|
|
|
46266
46473
|
}
|
|
46267
46474
|
function createLocalSkillManifest(name, generateSkillMd) {
|
|
46268
46475
|
const sourcePath = getSkillPath(name);
|
|
46269
|
-
if (!
|
|
46476
|
+
if (!existsSync14(sourcePath))
|
|
46270
46477
|
return null;
|
|
46271
46478
|
let skillMd = "";
|
|
46272
|
-
const skillMdPath =
|
|
46273
|
-
if (
|
|
46479
|
+
const skillMdPath = join31(sourcePath, "SKILL.md");
|
|
46480
|
+
if (existsSync14(skillMdPath)) {
|
|
46274
46481
|
skillMd = readFileSync20(skillMdPath, "utf-8");
|
|
46275
46482
|
} else if (generateSkillMd) {
|
|
46276
46483
|
skillMd = generateSkillMd(name) ?? "";
|
|
@@ -46343,21 +46550,21 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
|
46343
46550
|
const base = projectDir || process.cwd();
|
|
46344
46551
|
switch (agent) {
|
|
46345
46552
|
case "pi":
|
|
46346
|
-
return scope === "project" ?
|
|
46553
|
+
return scope === "project" ? join31(base, ".pi", "skills") : join31(homedir6(), ".pi", "agent", "skills");
|
|
46347
46554
|
case "opencode":
|
|
46348
|
-
return scope === "project" ?
|
|
46555
|
+
return scope === "project" ? join31(base, ".opencode", "skills") : join31(homedir6(), ".config", "opencode", "skills");
|
|
46349
46556
|
default:
|
|
46350
|
-
return scope === "project" ?
|
|
46557
|
+
return scope === "project" ? join31(base, `.${agent}`, "skills") : join31(homedir6(), `.${agent}`, "skills");
|
|
46351
46558
|
}
|
|
46352
46559
|
}
|
|
46353
46560
|
function getAgentSkillPath(name, agent, scope = "global", projectDir) {
|
|
46354
46561
|
const skillName = normalizeSkillName(getCanonicalSkillName(name));
|
|
46355
|
-
return
|
|
46562
|
+
return join31(getAgentSkillsDir(agent, scope, projectDir), skillName);
|
|
46356
46563
|
}
|
|
46357
46564
|
function installSkillForAgent(name, options, generateSkillMd) {
|
|
46358
46565
|
assertNativeExportAllowed();
|
|
46359
46566
|
const canonicalName = getCanonicalSkillName(name);
|
|
46360
|
-
if (!
|
|
46567
|
+
if (!existsSync14(getSkillPath(name))) {
|
|
46361
46568
|
return { skill: canonicalName, success: false, error: `Skill '${name}' not found` };
|
|
46362
46569
|
}
|
|
46363
46570
|
const scope = options.scope ?? "global";
|
|
@@ -46388,8 +46595,8 @@ function removeSkillForAgent(name, options) {
|
|
|
46388
46595
|
}
|
|
46389
46596
|
function resolveAgentSkillMd(name, generateSkillMd) {
|
|
46390
46597
|
const sourcePath = getSkillPath(name);
|
|
46391
|
-
const skillMdPath =
|
|
46392
|
-
if (
|
|
46598
|
+
const skillMdPath = join31(sourcePath, "SKILL.md");
|
|
46599
|
+
if (existsSync14(skillMdPath))
|
|
46393
46600
|
return readFileSync20(skillMdPath, "utf-8");
|
|
46394
46601
|
if (generateSkillMd)
|
|
46395
46602
|
return generateSkillMd(name);
|
|
@@ -46408,7 +46615,7 @@ function warnMissingDependencies(name, targetDir) {
|
|
|
46408
46615
|
}
|
|
46409
46616
|
function generateMinimalSkillMd(name) {
|
|
46410
46617
|
const sourcePath = getSkillPath(name);
|
|
46411
|
-
if (!
|
|
46618
|
+
if (!existsSync14(sourcePath))
|
|
46412
46619
|
return null;
|
|
46413
46620
|
const canonicalName = getCanonicalSkillName(name);
|
|
46414
46621
|
const meta = getSkill(canonicalName);
|
|
@@ -46423,7 +46630,7 @@ function generateMinimalSkillMd(name) {
|
|
|
46423
46630
|
"---",
|
|
46424
46631
|
""
|
|
46425
46632
|
].filter(Boolean);
|
|
46426
|
-
const fallbackDoc = readFileIfExists(
|
|
46633
|
+
const fallbackDoc = readFileIfExists(join31(sourcePath, "README.md")) || readFileIfExists(join31(sourcePath, "CLAUDE.md"));
|
|
46427
46634
|
if (fallbackDoc)
|
|
46428
46635
|
return `${frontmatter.join(`
|
|
46429
46636
|
`)}${fallbackDoc.trim()}
|
|
@@ -46442,8 +46649,8 @@ skills run ${canonicalName}
|
|
|
46442
46649
|
`;
|
|
46443
46650
|
}
|
|
46444
46651
|
function readBundledSkillVersion(name) {
|
|
46445
|
-
const pkgPath =
|
|
46446
|
-
if (!
|
|
46652
|
+
const pkgPath = join31(getSkillPath(name), "package.json");
|
|
46653
|
+
if (!existsSync14(pkgPath))
|
|
46447
46654
|
return "unknown";
|
|
46448
46655
|
try {
|
|
46449
46656
|
const pkg = JSON.parse(readFileSync20(pkgPath, "utf-8"));
|
|
@@ -46453,7 +46660,7 @@ function readBundledSkillVersion(name) {
|
|
|
46453
46660
|
}
|
|
46454
46661
|
}
|
|
46455
46662
|
function readFileIfExists(path) {
|
|
46456
|
-
return
|
|
46663
|
+
return existsSync14(path) ? readFileSync20(path, "utf-8") : null;
|
|
46457
46664
|
}
|
|
46458
46665
|
function loadProjectConfigCompat(targetDir) {
|
|
46459
46666
|
return loadProjectConfig(targetDir);
|
|
@@ -46497,8 +46704,8 @@ function selectSkillDoc(docs, file) {
|
|
|
46497
46704
|
function isInstructionSkillDir(skillPath, meta) {
|
|
46498
46705
|
if (meta?.kind === "instruction")
|
|
46499
46706
|
return true;
|
|
46500
|
-
const skillMdPath =
|
|
46501
|
-
if (!
|
|
46707
|
+
const skillMdPath = join32(skillPath, "SKILL.md");
|
|
46708
|
+
if (!existsSync15(skillMdPath))
|
|
46502
46709
|
return false;
|
|
46503
46710
|
try {
|
|
46504
46711
|
return parseSkillFrontmatter(readFileSync21(skillMdPath, "utf-8"))?.kind === "instruction";
|
|
@@ -46524,12 +46731,12 @@ var HOSTED_PROVIDER_ENV_PREFIXES = [
|
|
|
46524
46731
|
];
|
|
46525
46732
|
function getSkillDocs(name) {
|
|
46526
46733
|
const skillPath = getSkillPath(name);
|
|
46527
|
-
if (!
|
|
46734
|
+
if (!existsSync15(skillPath))
|
|
46528
46735
|
return null;
|
|
46529
46736
|
return {
|
|
46530
|
-
skillMd: readIfExists(
|
|
46531
|
-
readme: readIfExists(
|
|
46532
|
-
claudeMd: readIfExists(
|
|
46737
|
+
skillMd: readIfExists(join32(skillPath, "SKILL.md")),
|
|
46738
|
+
readme: readIfExists(join32(skillPath, "README.md")),
|
|
46739
|
+
claudeMd: readIfExists(join32(skillPath, "CLAUDE.md"))
|
|
46533
46740
|
};
|
|
46534
46741
|
}
|
|
46535
46742
|
function getSkillBestDoc(name, file) {
|
|
@@ -46541,18 +46748,18 @@ function getSkillBestDoc(name, file) {
|
|
|
46541
46748
|
}
|
|
46542
46749
|
function getSkillRequirements(name) {
|
|
46543
46750
|
const skillPath = getSkillPath(name);
|
|
46544
|
-
if (!
|
|
46751
|
+
if (!existsSync15(skillPath))
|
|
46545
46752
|
return null;
|
|
46546
46753
|
const texts = [];
|
|
46547
46754
|
for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
46548
|
-
const content = readIfExists(
|
|
46755
|
+
const content = readIfExists(join32(skillPath, file));
|
|
46549
46756
|
if (content)
|
|
46550
46757
|
texts.push(content);
|
|
46551
46758
|
}
|
|
46552
46759
|
const meta = getSkill(name);
|
|
46553
46760
|
let dependencies = {};
|
|
46554
46761
|
try {
|
|
46555
|
-
dependencies = JSON.parse(readFileSync21(
|
|
46762
|
+
dependencies = JSON.parse(readFileSync21(join32(skillPath, "package.json"), "utf8")).dependencies || {};
|
|
46556
46763
|
} catch {}
|
|
46557
46764
|
return getSkillRequirementsFromContent(name, texts, dependencies, meta);
|
|
46558
46765
|
}
|
|
@@ -46605,7 +46812,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
46605
46812
|
const meta = getSkill(name);
|
|
46606
46813
|
const canonicalName = meta?.name ?? name;
|
|
46607
46814
|
const skillPath = getSkillPath(canonicalName);
|
|
46608
|
-
if (!
|
|
46815
|
+
if (!existsSync15(skillPath)) {
|
|
46609
46816
|
return { exitCode: 1, error: `Skill '${name}' not found` };
|
|
46610
46817
|
}
|
|
46611
46818
|
if (isInstructionSkillDir(skillPath, meta)) {
|
|
@@ -46614,8 +46821,8 @@ async function runSkill(name, args, options = {}) {
|
|
|
46614
46821
|
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
46822
|
};
|
|
46616
46823
|
}
|
|
46617
|
-
const pkgPath =
|
|
46618
|
-
if (!
|
|
46824
|
+
const pkgPath = join32(skillPath, "package.json");
|
|
46825
|
+
if (!existsSync15(pkgPath)) {
|
|
46619
46826
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
46620
46827
|
}
|
|
46621
46828
|
let entryPoint2;
|
|
@@ -46634,12 +46841,12 @@ async function runSkill(name, args, options = {}) {
|
|
|
46634
46841
|
} catch {
|
|
46635
46842
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
46636
46843
|
}
|
|
46637
|
-
const entryPath =
|
|
46638
|
-
if (!
|
|
46844
|
+
const entryPath = join32(skillPath, entryPoint2);
|
|
46845
|
+
if (!existsSync15(entryPath)) {
|
|
46639
46846
|
return { exitCode: 1, error: `Entry point '${entryPoint2}' not found in skill '${name}'` };
|
|
46640
46847
|
}
|
|
46641
46848
|
const env = { ...process.env, ...options.env };
|
|
46642
|
-
if (!
|
|
46849
|
+
if (!existsSync15(join32(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
|
|
46643
46850
|
const failure = await prepareSkillDependencies(skillPath, env, options.preparationTimeoutMs);
|
|
46644
46851
|
if (failure)
|
|
46645
46852
|
return failure;
|
|
@@ -46708,7 +46915,7 @@ function generateSkillMd(name) {
|
|
|
46708
46915
|
if (!meta)
|
|
46709
46916
|
return null;
|
|
46710
46917
|
const skillPath = getSkillPath(name);
|
|
46711
|
-
if (!
|
|
46918
|
+
if (!existsSync15(skillPath))
|
|
46712
46919
|
return null;
|
|
46713
46920
|
const frontmatter = [
|
|
46714
46921
|
"---",
|
|
@@ -46717,11 +46924,11 @@ function generateSkillMd(name) {
|
|
|
46717
46924
|
"---"
|
|
46718
46925
|
].join(`
|
|
46719
46926
|
`);
|
|
46720
|
-
const readme = readIfExists(
|
|
46721
|
-
const claudeMd = readIfExists(
|
|
46927
|
+
const readme = readIfExists(join32(skillPath, "README.md"));
|
|
46928
|
+
const claudeMd = readIfExists(join32(skillPath, "CLAUDE.md"));
|
|
46722
46929
|
let cliCommand = null;
|
|
46723
|
-
const pkgPath =
|
|
46724
|
-
if (
|
|
46930
|
+
const pkgPath = join32(skillPath, "package.json");
|
|
46931
|
+
if (existsSync15(pkgPath)) {
|
|
46725
46932
|
try {
|
|
46726
46933
|
const pkg = JSON.parse(readFileSync21(pkgPath, "utf-8"));
|
|
46727
46934
|
if (pkg.bin) {
|
|
@@ -46798,7 +47005,7 @@ function extractEnvVars(text2) {
|
|
|
46798
47005
|
}
|
|
46799
47006
|
function readIfExists(path) {
|
|
46800
47007
|
try {
|
|
46801
|
-
if (
|
|
47008
|
+
if (existsSync15(path)) {
|
|
46802
47009
|
return readFileSync21(path, "utf-8");
|
|
46803
47010
|
}
|
|
46804
47011
|
} catch {}
|
|
@@ -46865,6 +47072,8 @@ async function syncSelectionProfile(profileId, options = {}) {
|
|
|
46865
47072
|
async function resolveSelectionContext(profileId, options = {}) {
|
|
46866
47073
|
const sessionState = options.sessionId ? readSkillSessionSnapshotIfExists(options.sessionId, options) : null;
|
|
46867
47074
|
const session = sessionState?.receipt ?? null;
|
|
47075
|
+
if (options.nativeParentSessionId !== undefined && session && (session.parent?.sessionId ?? null) !== options.nativeParentSessionId)
|
|
47076
|
+
throw new SkillSelectionError("SESSION_PARENT_MISMATCH", "The native session id is already bound to different parent custody.");
|
|
46868
47077
|
const parentState = !session && options.parentSessionId ? readSkillSessionSnapshotIfExists(options.parentSessionId, options) : null;
|
|
46869
47078
|
if (!session && options.parentSessionId && !parentState) {
|
|
46870
47079
|
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 +47211,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
47002
47211
|
throw new SkillSelectionError("INVALID_SECRET_BINDINGS", "Explicit environment values must be bounded strings without NUL bytes.");
|
|
47003
47212
|
secretEnv[name] = value;
|
|
47004
47213
|
}
|
|
47005
|
-
const runDirectory = mkdtempSync3(
|
|
47214
|
+
const runDirectory = mkdtempSync3(join33(tmpdir(), "skills-execution-"));
|
|
47006
47215
|
const paths = new SkillEntryPaths;
|
|
47007
47216
|
for (const entry of entries) {
|
|
47008
47217
|
paths.add(entry.path, 100, () => {
|
|
@@ -47010,12 +47219,12 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
47010
47219
|
}, () => {
|
|
47011
47220
|
throw new SkillSelectionError("INVALID_BUNDLE_PATH", "The execution bundle path exceeds its limit.");
|
|
47012
47221
|
});
|
|
47013
|
-
const destination =
|
|
47222
|
+
const destination = join33(runDirectory, entry.path);
|
|
47014
47223
|
mkdirSync10(dirname20(destination), { recursive: true, mode: 448 });
|
|
47015
47224
|
writeFileSync9(destination, entry.bytes, { flag: "wx", mode: entry.mode & 511 });
|
|
47016
47225
|
}
|
|
47017
47226
|
env.SKILLS_INPUT_JSON = input;
|
|
47018
|
-
env.SKILLS_WORKSPACE_DIR =
|
|
47227
|
+
env.SKILLS_WORKSPACE_DIR = resolve19(options.cwd ?? process.cwd());
|
|
47019
47228
|
env.SKILLS_RUN_DIR = runDirectory;
|
|
47020
47229
|
if (runtime.version) {
|
|
47021
47230
|
const version = await subprocess([executable, ...runtimeName === "bun" ? ["--no-env-file"] : [], "--version"], runDirectory, env, "", Math.min(timeoutMs, 5000));
|
|
@@ -47028,7 +47237,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
47028
47237
|
const { authority: authority2, workspaceId, profileRevision, slug, version, bundleDigest } = selection2;
|
|
47029
47238
|
const request2 = {
|
|
47030
47239
|
selection: { authority: authority2, workspaceId, profileId, profileRevision, slug, version, bundleDigest },
|
|
47031
|
-
consumer: { stationId: process.env.HASNA_STATION || stationHostname2(), workspaceDirectory:
|
|
47240
|
+
consumer: { stationId: process.env.HASNA_STATION || stationHostname2(), workspaceDirectory: realpathSync12(options.cwd ?? process.cwd()) }
|
|
47032
47241
|
};
|
|
47033
47242
|
const grant = await (options.resolveExecutionGrant ?? resolveExecutionGrant)(request2);
|
|
47034
47243
|
if (!grant || !grantIdentifier(grant.policyRevision) || !grantIdentifier(grant.grantId))
|
|
@@ -47038,7 +47247,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
47038
47247
|
}
|
|
47039
47248
|
if (bindings)
|
|
47040
47249
|
secretEnv = await resolveSelectedSecrets(bindings, options.createSecretsClient);
|
|
47041
|
-
const result = await subprocess([executable, ...runtimeName === "bun" ? ["--no-env-file"] : [],
|
|
47250
|
+
const result = await subprocess([executable, ...runtimeName === "bun" ? ["--no-env-file"] : [], join33(runDirectory, declaredEntry), ...args], runDirectory, { ...env, ...secretEnv }, input, timeoutMs);
|
|
47042
47251
|
result.stdout = redactExecutionSecrets(result.stdout, secretEnv);
|
|
47043
47252
|
result.stderr = redactExecutionSecrets(result.stderr, secretEnv);
|
|
47044
47253
|
const receipt = {
|
|
@@ -47051,7 +47260,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
47051
47260
|
...bindings ? { secretBinding: { schema: bindings.schema, selection: bindings.selection, consumer: bindings.consumer, secretsAuthority: bindings.secretsAuthority, bindings: bindings.bindings } } : {},
|
|
47052
47261
|
...result
|
|
47053
47262
|
};
|
|
47054
|
-
writeFileSync9(
|
|
47263
|
+
writeFileSync9(join33(runDirectory, ".execution-receipt.json"), JSON.stringify({ ...receipt, stdout: undefined, stderr: undefined }), { mode: 384 });
|
|
47055
47264
|
return receipt;
|
|
47056
47265
|
}
|
|
47057
47266
|
async function subprocess(command, cwd, env, input, timeoutMs) {
|
|
@@ -57116,7 +57325,7 @@ import { mkdirSync as mkdirSync12 } from "fs";
|
|
|
57116
57325
|
import { dirname as dirname24 } from "path";
|
|
57117
57326
|
|
|
57118
57327
|
// src/server/database-url.ts
|
|
57119
|
-
import { isAbsolute as isAbsolute19, join as
|
|
57328
|
+
import { isAbsolute as isAbsolute19, join as join35 } from "path";
|
|
57120
57329
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
57121
57330
|
var DEFAULT_SQLITE_FILENAME2 = "server.db";
|
|
57122
57331
|
var SQLITE_MEMORY_PATH = ":memory:";
|
|
@@ -57125,7 +57334,7 @@ var SQLITE_SCHEMES = new Set(["sqlite", "sqlite3", "file"]);
|
|
|
57125
57334
|
var MEMORY_SCHEMES = new Set(["memory"]);
|
|
57126
57335
|
var SQLITE_EXTENSIONS = [".db", ".sqlite", ".sqlite3", ".db3"];
|
|
57127
57336
|
function defaultSqlitePath() {
|
|
57128
|
-
return
|
|
57337
|
+
return join35(getDataDir(), DEFAULT_SQLITE_FILENAME2);
|
|
57129
57338
|
}
|
|
57130
57339
|
function resolveDatabaseTarget(raw) {
|
|
57131
57340
|
const value = raw?.trim();
|
|
@@ -57152,7 +57361,7 @@ function resolveDatabaseTarget(raw) {
|
|
|
57152
57361
|
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
57362
|
}
|
|
57154
57363
|
if (looksLikeSqlitePath(value)) {
|
|
57155
|
-
const path = isAbsolute19(value) ? value :
|
|
57364
|
+
const path = isAbsolute19(value) ? value : join35(process.cwd(), value);
|
|
57156
57365
|
return { kind: "sqlite", path, durable: true, label: `sqlite (${path})` };
|
|
57157
57366
|
}
|
|
57158
57367
|
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 +57385,7 @@ function sqlitePathFromUrl(value, scheme) {
|
|
|
57176
57385
|
}
|
|
57177
57386
|
return rest.slice(2).replace(/^\/\/+/, "/");
|
|
57178
57387
|
}
|
|
57179
|
-
return isAbsolute19(rest) ? rest :
|
|
57388
|
+
return isAbsolute19(rest) ? rest : join35(process.cwd(), rest);
|
|
57180
57389
|
}
|
|
57181
57390
|
function looksLikeSqlitePath(value) {
|
|
57182
57391
|
if (value.includes("/"))
|
|
@@ -57409,7 +57618,7 @@ class PostgresSkillSelectionStore {
|
|
|
57409
57618
|
// src/server/sqlite-store.ts
|
|
57410
57619
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
57411
57620
|
import { mkdirSync as mkdirSync11, readFileSync as readFileSync23, readdirSync as readdirSync13 } from "fs";
|
|
57412
|
-
import { dirname as dirname23, join as
|
|
57621
|
+
import { dirname as dirname23, join as join37 } from "path";
|
|
57413
57622
|
|
|
57414
57623
|
// src/server/auth.ts
|
|
57415
57624
|
import { createHash as createHash23 } from "crypto";
|
|
@@ -57467,16 +57676,16 @@ function permitsSkillsRoute(principal, method, resource) {
|
|
|
57467
57676
|
}
|
|
57468
57677
|
|
|
57469
57678
|
// src/server/migrations-dir.ts
|
|
57470
|
-
import { existsSync as
|
|
57471
|
-
import { dirname as dirname22, join as
|
|
57679
|
+
import { existsSync as existsSync16 } from "fs";
|
|
57680
|
+
import { dirname as dirname22, join as join36 } from "path";
|
|
57472
57681
|
var MIGRATION_DIALECTS = ["postgres", "sqlite"];
|
|
57473
57682
|
var MAX_WALK_UP = 6;
|
|
57474
57683
|
function findMigrationsRoot(startDirs = defaultStartDirs()) {
|
|
57475
57684
|
for (const start of startDirs) {
|
|
57476
57685
|
let dir = start;
|
|
57477
57686
|
for (let level = 0;level < MAX_WALK_UP; level += 1) {
|
|
57478
|
-
const candidate2 =
|
|
57479
|
-
if (MIGRATION_DIALECTS.some((dialect) =>
|
|
57687
|
+
const candidate2 = join36(dir, "migrations");
|
|
57688
|
+
if (MIGRATION_DIALECTS.some((dialect) => existsSync16(join36(candidate2, dialect))))
|
|
57480
57689
|
return candidate2;
|
|
57481
57690
|
const parent = dirname22(dir);
|
|
57482
57691
|
if (parent === dir)
|
|
@@ -57490,8 +57699,8 @@ function resolveMigrationsDir(dialect, root4 = findMigrationsRoot()) {
|
|
|
57490
57699
|
if (!root4) {
|
|
57491
57700
|
throw new Error(`could not locate the migrations directory for dialect "${dialect}". ` + `Expected a migrations/${dialect}/ folder alongside the package root.`);
|
|
57492
57701
|
}
|
|
57493
|
-
const dir =
|
|
57494
|
-
if (!
|
|
57702
|
+
const dir = join36(root4, dialect);
|
|
57703
|
+
if (!existsSync16(dir)) {
|
|
57495
57704
|
throw new Error(`migrations directory not found: ${dir}`);
|
|
57496
57705
|
}
|
|
57497
57706
|
return dir;
|
|
@@ -58462,7 +58671,7 @@ function applySqliteMigrations(db, migrationsDir = resolveMigrationsDir("sqlite"
|
|
|
58462
58671
|
const appliedNow = [];
|
|
58463
58672
|
for (const file of files) {
|
|
58464
58673
|
const version2 = file.replace(/\.sql$/, "");
|
|
58465
|
-
const text2 = readFileSync23(
|
|
58674
|
+
const text2 = readFileSync23(join37(migrationsDir, file), "utf8");
|
|
58466
58675
|
const apply = db.transaction(() => {
|
|
58467
58676
|
const already = db.query("SELECT 1 AS present FROM schema_migrations WHERE version = ? LIMIT 1").get(version2);
|
|
58468
58677
|
if (already)
|
|
@@ -90719,10 +90928,10 @@ async function readQuoteUnavailableCode(response) {
|
|
|
90719
90928
|
let timer;
|
|
90720
90929
|
let deadlineExceeded = false;
|
|
90721
90930
|
const expired = Symbol("quote body deadline");
|
|
90722
|
-
const deadline = new Promise((
|
|
90931
|
+
const deadline = new Promise((resolve21) => {
|
|
90723
90932
|
timer = setTimeout(() => {
|
|
90724
90933
|
deadlineExceeded = true;
|
|
90725
|
-
|
|
90934
|
+
resolve21(expired);
|
|
90726
90935
|
reader.cancel().catch(() => {});
|
|
90727
90936
|
}, 1500);
|
|
90728
90937
|
});
|
|
@@ -92208,12 +92417,12 @@ class RemotePrivatePublicationsClient {
|
|
|
92208
92417
|
previous = view;
|
|
92209
92418
|
if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
|
|
92210
92419
|
return view;
|
|
92211
|
-
await new Promise((
|
|
92420
|
+
await new Promise((resolve21) => {
|
|
92212
92421
|
const timer = setTimeout(done, Math.min(1000, until - Date.now()));
|
|
92213
92422
|
function done() {
|
|
92214
92423
|
clearTimeout(timer);
|
|
92215
92424
|
options.signal?.removeEventListener("abort", done);
|
|
92216
|
-
|
|
92425
|
+
resolve21();
|
|
92217
92426
|
}
|
|
92218
92427
|
options.signal?.addEventListener("abort", done, { once: true });
|
|
92219
92428
|
});
|
|
@@ -92445,23 +92654,23 @@ class RemoteSkillsAuthClient {
|
|
|
92445
92654
|
}
|
|
92446
92655
|
}
|
|
92447
92656
|
// 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
|
|
92657
|
+
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";
|
|
92658
|
+
import { dirname as dirname26, isAbsolute as isAbsolute20, join as join39, resolve as resolve21 } from "path";
|
|
92450
92659
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
92451
92660
|
var fail = () => {
|
|
92452
92661
|
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
92662
|
};
|
|
92454
92663
|
function safeDirectory(directory) {
|
|
92455
|
-
if (!isAbsolute20(directory) || directory !==
|
|
92664
|
+
if (!isAbsolute20(directory) || directory !== resolve21(directory) || realpathSync13(directory) !== directory)
|
|
92456
92665
|
return fail();
|
|
92457
92666
|
for (let path2 = directory;; path2 = dirname26(path2)) {
|
|
92458
|
-
const stat2 =
|
|
92667
|
+
const stat2 = lstatSync24(path2);
|
|
92459
92668
|
if (!stat2.isDirectory() || stat2.isSymbolicLink())
|
|
92460
92669
|
return fail();
|
|
92461
92670
|
if (path2 === dirname26(path2))
|
|
92462
92671
|
break;
|
|
92463
92672
|
}
|
|
92464
|
-
const own2 =
|
|
92673
|
+
const own2 = lstatSync24(directory);
|
|
92465
92674
|
if ((own2.mode & 63) !== 0 || process.getuid && own2.uid !== process.getuid())
|
|
92466
92675
|
return fail();
|
|
92467
92676
|
return { dev: own2.dev, ino: own2.ino };
|
|
@@ -92472,7 +92681,7 @@ function unchangedDirectory(directory, identity4) {
|
|
|
92472
92681
|
return fail();
|
|
92473
92682
|
}
|
|
92474
92683
|
function readOwned(directory, name, max) {
|
|
92475
|
-
const identity4 = safeDirectory(directory), file2 =
|
|
92684
|
+
const identity4 = safeDirectory(directory), file2 = join39(directory, name);
|
|
92476
92685
|
const fd = openSync17(file2, constants13.O_RDONLY | constants13.O_NOFOLLOW);
|
|
92477
92686
|
try {
|
|
92478
92687
|
const stat2 = fstatSync17(fd);
|
|
@@ -92496,7 +92705,7 @@ function readOwned(directory, name, max) {
|
|
|
92496
92705
|
}
|
|
92497
92706
|
}
|
|
92498
92707
|
function writeOwned(directory, name, bytes) {
|
|
92499
|
-
const fd = openSync17(
|
|
92708
|
+
const fd = openSync17(join39(directory, name), constants13.O_WRONLY | constants13.O_CREAT | constants13.O_EXCL | constants13.O_NOFOLLOW, 384);
|
|
92500
92709
|
try {
|
|
92501
92710
|
writeFileSync10(fd, bytes);
|
|
92502
92711
|
fsyncSync5(fd);
|
|
@@ -92509,7 +92718,7 @@ function save(directory, value) {
|
|
|
92509
92718
|
writeOwned(directory, temporary, JSON.stringify(value) + `
|
|
92510
92719
|
`);
|
|
92511
92720
|
unchangedDirectory(directory, identity4);
|
|
92512
|
-
renameSync6(
|
|
92721
|
+
renameSync6(join39(directory, temporary), join39(directory, "receipt.json"));
|
|
92513
92722
|
const fd = openSync17(directory, constants13.O_RDONLY | constants13.O_NOFOLLOW);
|
|
92514
92723
|
try {
|
|
92515
92724
|
fsyncSync5(fd);
|
|
@@ -92522,7 +92731,7 @@ function bind(client2, receipt) {
|
|
|
92522
92731
|
throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
|
|
92523
92732
|
}
|
|
92524
92733
|
async function locked(directory, action) {
|
|
92525
|
-
const identity4 = safeDirectory(directory), file2 =
|
|
92734
|
+
const identity4 = safeDirectory(directory), file2 = join39(directory, "operation.lock");
|
|
92526
92735
|
let fd;
|
|
92527
92736
|
try {
|
|
92528
92737
|
fd = openSync17(file2, constants13.O_WRONLY | constants13.O_CREAT | constants13.O_EXCL | constants13.O_NOFOLLOW, 384);
|
|
@@ -92538,7 +92747,7 @@ async function locked(directory, action) {
|
|
|
92538
92747
|
} finally {
|
|
92539
92748
|
closeSync17(fd);
|
|
92540
92749
|
unchangedDirectory(directory, identity4);
|
|
92541
|
-
const now2 =
|
|
92750
|
+
const now2 = lstatSync24(file2);
|
|
92542
92751
|
if (now2.dev !== lock.dev || now2.ino !== lock.ino || !now2.isFile() || now2.isSymbolicLink())
|
|
92543
92752
|
fail();
|
|
92544
92753
|
unlinkSync5(file2);
|
|
@@ -92590,7 +92799,7 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
|
|
|
92590
92799
|
phase: "prepared",
|
|
92591
92800
|
intent: null
|
|
92592
92801
|
};
|
|
92593
|
-
if (!isAbsolute20(recoveryDirectory) ||
|
|
92802
|
+
if (!isAbsolute20(recoveryDirectory) || resolve21(recoveryDirectory) !== recoveryDirectory || realpathSync13(dirname26(recoveryDirectory)) !== dirname26(recoveryDirectory))
|
|
92594
92803
|
return fail();
|
|
92595
92804
|
mkdirSync14(recoveryDirectory, { mode: 448 });
|
|
92596
92805
|
safeDirectory(recoveryDirectory);
|