@delorenj/skillex 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +49 -12
- package/dist/core/profile-policy.d.ts +7 -1
- package/dist/index.js +49 -12
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -18236,7 +18236,7 @@ import { isDeepStrictEqual as isDeepStrictEqual10 } from "node:util";
|
|
|
18236
18236
|
// src/core/profile-policy.ts
|
|
18237
18237
|
import { constants as constants12 } from "node:fs";
|
|
18238
18238
|
import { open as open16, readdir as readdir29, readFile as readFile18, stat as stat8 } from "node:fs/promises";
|
|
18239
|
-
import { join as join43 } from "node:path";
|
|
18239
|
+
import { basename as basename13, join as join43 } from "node:path";
|
|
18240
18240
|
import { parseDocument as parseDocument4 } from "yaml";
|
|
18241
18241
|
var metadataFiles = /* @__PURE__ */ new Set([
|
|
18242
18242
|
".usage.json",
|
|
@@ -18324,11 +18324,38 @@ async function profileInheritsGlobal(profile) {
|
|
|
18324
18324
|
refuse5(path, "Profile skills.inherit_global must be boolean.");
|
|
18325
18325
|
return inherit !== false;
|
|
18326
18326
|
}
|
|
18327
|
-
|
|
18327
|
+
var learnedScanLimit = 2e4;
|
|
18328
|
+
async function learnedSkills(item2) {
|
|
18329
|
+
if (item2.kind !== "directory" || item2.name.startsWith(".")) return null;
|
|
18330
|
+
const skills = [];
|
|
18331
|
+
const pending = [item2.path];
|
|
18332
|
+
let seen = 0;
|
|
18333
|
+
while (pending.length) {
|
|
18334
|
+
const directory2 = pending.pop();
|
|
18335
|
+
const entries = await children(directory2);
|
|
18336
|
+
if (!entries) return null;
|
|
18337
|
+
for (const child of entries) {
|
|
18338
|
+
if (++seen > learnedScanLimit) return null;
|
|
18339
|
+
if (child.isDirectory()) pending.push(join43(directory2, child.name));
|
|
18340
|
+
else if (!child.isFile()) return null;
|
|
18341
|
+
else if (child.name === "SKILL.md") skills.push(basename13(directory2));
|
|
18342
|
+
}
|
|
18343
|
+
}
|
|
18344
|
+
return skills;
|
|
18345
|
+
}
|
|
18346
|
+
async function assertStrictProfile(profile, options, local, desired = /* @__PURE__ */ new Set()) {
|
|
18328
18347
|
if (!await strictProfile(profile, options)) return false;
|
|
18329
|
-
for (const item2 of local)
|
|
18330
|
-
if (
|
|
18331
|
-
|
|
18348
|
+
for (const item2 of local) {
|
|
18349
|
+
if (await hermesMetadata(item2)) continue;
|
|
18350
|
+
const learned = await learnedSkills(item2);
|
|
18351
|
+
if (!learned) refuse5(item2.path, `Skillex-only profile contains unowned entry ${item2.name}.`);
|
|
18352
|
+
const clash = [item2.name, ...learned].find((name) => desired.has(name));
|
|
18353
|
+
if (clash !== void 0)
|
|
18354
|
+
refuse5(
|
|
18355
|
+
item2.path,
|
|
18356
|
+
`Agent-written skill ${clash} in ${item2.name} shares its name with a selected skill.`
|
|
18357
|
+
);
|
|
18358
|
+
}
|
|
18332
18359
|
const path = join43(profile.root, "config.yaml");
|
|
18333
18360
|
const config = await entry2(path);
|
|
18334
18361
|
if (config) {
|
|
@@ -18573,7 +18600,12 @@ async function buildWork(profile, state, options) {
|
|
|
18573
18600
|
);
|
|
18574
18601
|
state = await readProfileState(profile, receiptOptions(profile, options, projection.scopes));
|
|
18575
18602
|
const observed = await observations(profile, state);
|
|
18576
|
-
await assertStrictProfile(
|
|
18603
|
+
await assertStrictProfile(
|
|
18604
|
+
profile,
|
|
18605
|
+
options,
|
|
18606
|
+
observed.local,
|
|
18607
|
+
new Set(projection.desired.map((desired) => desired.name))
|
|
18608
|
+
);
|
|
18577
18609
|
const links = { ...state.data.links };
|
|
18578
18610
|
const changes = [];
|
|
18579
18611
|
if (!observed.root) changes.push({ action: "mkdir", path: profile.skillsRoot });
|
|
@@ -19049,7 +19081,12 @@ async function runProfileSync(name, options) {
|
|
|
19049
19081
|
});
|
|
19050
19082
|
}
|
|
19051
19083
|
await assertProfileIdentity(selected);
|
|
19052
|
-
if (await assertStrictProfile(
|
|
19084
|
+
if (await assertStrictProfile(
|
|
19085
|
+
selected,
|
|
19086
|
+
options,
|
|
19087
|
+
(await observations(selected, state)).local,
|
|
19088
|
+
new Set(work.projection.desired.map((desired) => desired.name))
|
|
19089
|
+
))
|
|
19053
19090
|
await publishStrictPolicy(selected);
|
|
19054
19091
|
const currentProfile = await discoverProfile(name, options);
|
|
19055
19092
|
if (currentProfile.root !== selected.root || currentProfile.skillsRoot !== selected.skillsRoot)
|
|
@@ -19691,7 +19728,7 @@ import { randomUUID as randomUUID13 } from "node:crypto";
|
|
|
19691
19728
|
import { constants as constants14 } from "node:fs";
|
|
19692
19729
|
import { mkdir as mkdir13, open as open19, realpath as realpath34, rename as rename13, symlink as symlink8 } from "node:fs/promises";
|
|
19693
19730
|
import { homedir as homedir16 } from "node:os";
|
|
19694
|
-
import { basename as
|
|
19731
|
+
import { basename as basename14, dirname as dirname29, join as join46, parse as parse6, resolve as resolve27 } from "node:path";
|
|
19695
19732
|
import { isDeepStrictEqual as isDeepStrictEqual12 } from "node:util";
|
|
19696
19733
|
import { valid as valid3 } from "semver";
|
|
19697
19734
|
var binding5 = { namespace: "migrations", targetParts: [".agents", "root-adoption"] };
|
|
@@ -19891,7 +19928,7 @@ async function guard2(context2, journal) {
|
|
|
19891
19928
|
function paths2(snapshot3, id) {
|
|
19892
19929
|
const prefix2 = join46(
|
|
19893
19930
|
dirname29(snapshot3.path),
|
|
19894
|
-
`${
|
|
19931
|
+
`${basename14(snapshot3.path, ".json")}.root-cutover-${id}`
|
|
19895
19932
|
);
|
|
19896
19933
|
return { archive: `${prefix2}-old`, stage: `${prefix2}-new` };
|
|
19897
19934
|
}
|
|
@@ -20003,8 +20040,8 @@ function journalData(snapshot3, context2) {
|
|
|
20003
20040
|
)))
|
|
20004
20041
|
refuse7(snapshot3.path, "The staged root does not match its canonical inventory.");
|
|
20005
20042
|
for (const stage2 of raw.preservedStages) {
|
|
20006
|
-
const prefix2 = `${
|
|
20007
|
-
if (typeof stage2 !== "string" || dirname29(stage2) !== dirname29(snapshot3.path) || !
|
|
20043
|
+
const prefix2 = `${basename14(snapshot3.path, ".json")}.root-cutover-`;
|
|
20044
|
+
if (typeof stage2 !== "string" || dirname29(stage2) !== dirname29(snapshot3.path) || !basename14(stage2).startsWith(prefix2) || !uuid.test(basename14(stage2).slice(prefix2.length, -4)) || !stage2.endsWith("-new"))
|
|
20008
20045
|
refuse7(snapshot3.path, "A preserved stage has an invalid quarantine path.");
|
|
20009
20046
|
}
|
|
20010
20047
|
const data2 = raw;
|
|
@@ -20045,7 +20082,7 @@ async function ownerFor2(context2, allowed) {
|
|
|
20045
20082
|
]) {
|
|
20046
20083
|
if (allowed && (path === context2.root || dirname29(path) === context2.root)) {
|
|
20047
20084
|
const candidates = allowed.map(
|
|
20048
|
-
(node) => path === context2.root ? node : node.children[
|
|
20085
|
+
(node) => path === context2.root ? node : node.children[basename14(path)]
|
|
20049
20086
|
);
|
|
20050
20087
|
if (!candidates.some(
|
|
20051
20088
|
(node) => node && ["link", "directory"].includes(node.identity.kind) && isDeepStrictEqual12(nodeIdentity(node), expected)
|
|
@@ -2,5 +2,11 @@ import type { ProfileLocalEntry, ProfileLocation, ProfileOptions } from "./profi
|
|
|
2
2
|
export declare function strictProfile(profile: ProfileLocation, options: ProfileOptions): Promise<boolean>;
|
|
3
3
|
/** Persist a profile's selection boundary independently of the caller's HOME. */
|
|
4
4
|
export declare function profileInheritsGlobal(profile: ProfileLocation): Promise<boolean>;
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Strict (Skillex-only) policy: Skillex owns every link it projects. Hermes bookkeeping and the
|
|
7
|
+
* skills a desk's own agent wrote may sit beside those links (operator decision 2026-11-06,
|
|
8
|
+
* SKRILL-35); a learned skill named like a selected one, a foreign link, a stray file, an
|
|
9
|
+
* archive or a hub install is still refused. `desired` names the skills Skillex will project.
|
|
10
|
+
*/
|
|
11
|
+
export declare function assertStrictProfile(profile: ProfileLocation, options: ProfileOptions, local: readonly ProfileLocalEntry[], desired?: ReadonlySet<string>): Promise<boolean>;
|
|
6
12
|
export declare function publishStrictPolicy(profile: ProfileLocation): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -18234,7 +18234,7 @@ import { isDeepStrictEqual as isDeepStrictEqual10 } from "node:util";
|
|
|
18234
18234
|
// src/core/profile-policy.ts
|
|
18235
18235
|
import { constants as constants12 } from "node:fs";
|
|
18236
18236
|
import { open as open16, readdir as readdir29, readFile as readFile18, stat as stat8 } from "node:fs/promises";
|
|
18237
|
-
import { join as join43 } from "node:path";
|
|
18237
|
+
import { basename as basename13, join as join43 } from "node:path";
|
|
18238
18238
|
import { parseDocument as parseDocument4 } from "yaml";
|
|
18239
18239
|
var metadataFiles = /* @__PURE__ */ new Set([
|
|
18240
18240
|
".usage.json",
|
|
@@ -18322,11 +18322,38 @@ async function profileInheritsGlobal(profile) {
|
|
|
18322
18322
|
refuse5(path, "Profile skills.inherit_global must be boolean.");
|
|
18323
18323
|
return inherit !== false;
|
|
18324
18324
|
}
|
|
18325
|
-
|
|
18325
|
+
var learnedScanLimit = 2e4;
|
|
18326
|
+
async function learnedSkills(item2) {
|
|
18327
|
+
if (item2.kind !== "directory" || item2.name.startsWith(".")) return null;
|
|
18328
|
+
const skills = [];
|
|
18329
|
+
const pending = [item2.path];
|
|
18330
|
+
let seen = 0;
|
|
18331
|
+
while (pending.length) {
|
|
18332
|
+
const directory2 = pending.pop();
|
|
18333
|
+
const entries = await children(directory2);
|
|
18334
|
+
if (!entries) return null;
|
|
18335
|
+
for (const child of entries) {
|
|
18336
|
+
if (++seen > learnedScanLimit) return null;
|
|
18337
|
+
if (child.isDirectory()) pending.push(join43(directory2, child.name));
|
|
18338
|
+
else if (!child.isFile()) return null;
|
|
18339
|
+
else if (child.name === "SKILL.md") skills.push(basename13(directory2));
|
|
18340
|
+
}
|
|
18341
|
+
}
|
|
18342
|
+
return skills;
|
|
18343
|
+
}
|
|
18344
|
+
async function assertStrictProfile(profile, options, local, desired = /* @__PURE__ */ new Set()) {
|
|
18326
18345
|
if (!await strictProfile(profile, options)) return false;
|
|
18327
|
-
for (const item2 of local)
|
|
18328
|
-
if (
|
|
18329
|
-
|
|
18346
|
+
for (const item2 of local) {
|
|
18347
|
+
if (await hermesMetadata(item2)) continue;
|
|
18348
|
+
const learned = await learnedSkills(item2);
|
|
18349
|
+
if (!learned) refuse5(item2.path, `Skillex-only profile contains unowned entry ${item2.name}.`);
|
|
18350
|
+
const clash = [item2.name, ...learned].find((name) => desired.has(name));
|
|
18351
|
+
if (clash !== void 0)
|
|
18352
|
+
refuse5(
|
|
18353
|
+
item2.path,
|
|
18354
|
+
`Agent-written skill ${clash} in ${item2.name} shares its name with a selected skill.`
|
|
18355
|
+
);
|
|
18356
|
+
}
|
|
18330
18357
|
const path = join43(profile.root, "config.yaml");
|
|
18331
18358
|
const config = await entry2(path);
|
|
18332
18359
|
if (config) {
|
|
@@ -18571,7 +18598,12 @@ async function buildWork(profile, state, options) {
|
|
|
18571
18598
|
);
|
|
18572
18599
|
state = await readProfileState(profile, receiptOptions(profile, options, projection.scopes));
|
|
18573
18600
|
const observed = await observations(profile, state);
|
|
18574
|
-
await assertStrictProfile(
|
|
18601
|
+
await assertStrictProfile(
|
|
18602
|
+
profile,
|
|
18603
|
+
options,
|
|
18604
|
+
observed.local,
|
|
18605
|
+
new Set(projection.desired.map((desired) => desired.name))
|
|
18606
|
+
);
|
|
18575
18607
|
const links = { ...state.data.links };
|
|
18576
18608
|
const changes = [];
|
|
18577
18609
|
if (!observed.root) changes.push({ action: "mkdir", path: profile.skillsRoot });
|
|
@@ -19047,7 +19079,12 @@ async function runProfileSync(name, options) {
|
|
|
19047
19079
|
});
|
|
19048
19080
|
}
|
|
19049
19081
|
await assertProfileIdentity(selected);
|
|
19050
|
-
if (await assertStrictProfile(
|
|
19082
|
+
if (await assertStrictProfile(
|
|
19083
|
+
selected,
|
|
19084
|
+
options,
|
|
19085
|
+
(await observations(selected, state)).local,
|
|
19086
|
+
new Set(work.projection.desired.map((desired) => desired.name))
|
|
19087
|
+
))
|
|
19051
19088
|
await publishStrictPolicy(selected);
|
|
19052
19089
|
const currentProfile = await discoverProfile(name, options);
|
|
19053
19090
|
if (currentProfile.root !== selected.root || currentProfile.skillsRoot !== selected.skillsRoot)
|
|
@@ -19689,7 +19726,7 @@ import { randomUUID as randomUUID13 } from "node:crypto";
|
|
|
19689
19726
|
import { constants as constants14 } from "node:fs";
|
|
19690
19727
|
import { mkdir as mkdir13, open as open19, realpath as realpath34, rename as rename13, symlink as symlink8 } from "node:fs/promises";
|
|
19691
19728
|
import { homedir as homedir16 } from "node:os";
|
|
19692
|
-
import { basename as
|
|
19729
|
+
import { basename as basename14, dirname as dirname29, join as join46, parse as parse6, resolve as resolve27 } from "node:path";
|
|
19693
19730
|
import { isDeepStrictEqual as isDeepStrictEqual12 } from "node:util";
|
|
19694
19731
|
import { valid as valid3 } from "semver";
|
|
19695
19732
|
var binding5 = { namespace: "migrations", targetParts: [".agents", "root-adoption"] };
|
|
@@ -19889,7 +19926,7 @@ async function guard2(context2, journal) {
|
|
|
19889
19926
|
function paths2(snapshot3, id) {
|
|
19890
19927
|
const prefix2 = join46(
|
|
19891
19928
|
dirname29(snapshot3.path),
|
|
19892
|
-
`${
|
|
19929
|
+
`${basename14(snapshot3.path, ".json")}.root-cutover-${id}`
|
|
19893
19930
|
);
|
|
19894
19931
|
return { archive: `${prefix2}-old`, stage: `${prefix2}-new` };
|
|
19895
19932
|
}
|
|
@@ -20001,8 +20038,8 @@ function journalData(snapshot3, context2) {
|
|
|
20001
20038
|
)))
|
|
20002
20039
|
refuse7(snapshot3.path, "The staged root does not match its canonical inventory.");
|
|
20003
20040
|
for (const stage2 of raw.preservedStages) {
|
|
20004
|
-
const prefix2 = `${
|
|
20005
|
-
if (typeof stage2 !== "string" || dirname29(stage2) !== dirname29(snapshot3.path) || !
|
|
20041
|
+
const prefix2 = `${basename14(snapshot3.path, ".json")}.root-cutover-`;
|
|
20042
|
+
if (typeof stage2 !== "string" || dirname29(stage2) !== dirname29(snapshot3.path) || !basename14(stage2).startsWith(prefix2) || !uuid.test(basename14(stage2).slice(prefix2.length, -4)) || !stage2.endsWith("-new"))
|
|
20006
20043
|
refuse7(snapshot3.path, "A preserved stage has an invalid quarantine path.");
|
|
20007
20044
|
}
|
|
20008
20045
|
const data2 = raw;
|
|
@@ -20043,7 +20080,7 @@ async function ownerFor2(context2, allowed) {
|
|
|
20043
20080
|
]) {
|
|
20044
20081
|
if (allowed && (path === context2.root || dirname29(path) === context2.root)) {
|
|
20045
20082
|
const candidates = allowed.map(
|
|
20046
|
-
(node) => path === context2.root ? node : node.children[
|
|
20083
|
+
(node) => path === context2.root ? node : node.children[basename14(path)]
|
|
20047
20084
|
);
|
|
20048
20085
|
if (!candidates.some(
|
|
20049
20086
|
(node) => node && ["link", "directory"].includes(node.identity.kind) && isDeepStrictEqual12(nodeIdentity(node), expected)
|