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