@hasna/skills 0.10.33 → 0.10.34
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 +26 -0
- package/bin/index.js +68 -13
- package/bin/mcp.js +1 -1
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/cli/commands/runtime-copyfile.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/sdk/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1148,6 +1148,32 @@ semantic version. If discovery or verification fails, it exits nonzero and
|
|
|
1148
1148
|
explains that installation may already have completed. It does not automatically
|
|
1149
1149
|
reinstall or change your PATH.
|
|
1150
1150
|
|
|
1151
|
+
### Exact-version runtime updates
|
|
1152
|
+
|
|
1153
|
+
`skills self-update --version 0.10.34` installs a verified package into a retained
|
|
1154
|
+
copyfile runtime, preserves configuration and launcher preimages, and keeps the
|
|
1155
|
+
previous runtime available for receipt-bound rollback.
|
|
1156
|
+
|
|
1157
|
+
To require a minimum dependency age, pass a positive integer number of days and
|
|
1158
|
+
repeat the exclusion option for package names or package globs:
|
|
1159
|
+
|
|
1160
|
+
```bash
|
|
1161
|
+
skills self-update --version 0.10.34 --min-release-age 7 \
|
|
1162
|
+
--min-release-age-exclude '@hasna/*' \
|
|
1163
|
+
--min-release-age-exclude '@hasna-internal/*' \
|
|
1164
|
+
--min-release-age-exclude '@openai/*' \
|
|
1165
|
+
--min-release-age-exclude '@anthropic-ai/*' \
|
|
1166
|
+
--min-release-age-exclude openai
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
The policy reaches both dependency lock resolution and `npm ci` through private
|
|
1170
|
+
npm configuration. Exemptions match each dependency's own package identity;
|
|
1171
|
+
unrelated transitive dependencies retain the minimum age. These options require
|
|
1172
|
+
an exact version and npm 11.19.0 or newer with both age capabilities. They are
|
|
1173
|
+
refused for rollback, alias adoption or the no-argument updater. Existing ambient
|
|
1174
|
+
npm settings and credentials are not forwarded. Omitting the policy preserves
|
|
1175
|
+
existing exact-version update behavior.
|
|
1176
|
+
|
|
1151
1177
|
### Local environment assignments
|
|
1152
1178
|
|
|
1153
1179
|
`skills env-check --set 'KEY=value'` writes one literal value to the current
|
package/bin/index.js
CHANGED
|
@@ -36868,7 +36868,7 @@ var package_default;
|
|
|
36868
36868
|
var init_package = __esm(() => {
|
|
36869
36869
|
package_default = {
|
|
36870
36870
|
name: "@hasna/skills",
|
|
36871
|
-
version: "0.10.
|
|
36871
|
+
version: "0.10.34",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -109753,16 +109753,47 @@ function assertConfigUnchanged(configs) {
|
|
|
109753
109753
|
}
|
|
109754
109754
|
}
|
|
109755
109755
|
}
|
|
109756
|
-
|
|
109756
|
+
function releaseAgePolicy(options) {
|
|
109757
|
+
if (options.minReleaseAgeExclude !== undefined && !Array.isArray(options.minReleaseAgeExclude))
|
|
109758
|
+
throw new Error("MIN_RELEASE_AGE_EXCLUDE_INVALID");
|
|
109759
|
+
if (options.minReleaseAge === undefined) {
|
|
109760
|
+
if (options.minReleaseAgeExclude?.length)
|
|
109761
|
+
throw new Error("MIN_RELEASE_AGE_REQUIRED_FOR_EXCLUSIONS");
|
|
109762
|
+
return;
|
|
109763
|
+
}
|
|
109764
|
+
if (!Number.isSafeInteger(options.minReleaseAge) || options.minReleaseAge < 1)
|
|
109765
|
+
throw new Error("MIN_RELEASE_AGE_INVALID");
|
|
109766
|
+
const exclusions = options.minReleaseAgeExclude ?? [];
|
|
109767
|
+
if (!Array.isArray(exclusions) || exclusions.some((pattern) => typeof pattern !== "string" || pattern.length > 214 || !/^(?:@[a-z0-9*?][a-z0-9._*?-]*\/)?[a-z0-9*?][a-z0-9._*?-]*$/.test(pattern))) {
|
|
109768
|
+
throw new Error("MIN_RELEASE_AGE_EXCLUDE_INVALID");
|
|
109769
|
+
}
|
|
109770
|
+
return { minReleaseAge: options.minReleaseAge, minReleaseAgeExclude: [...new Set(exclusions)] };
|
|
109771
|
+
}
|
|
109772
|
+
async function npmReleaseAgeCapability(cwd2, env3) {
|
|
109773
|
+
try {
|
|
109774
|
+
const versionProbe = Bun.spawn(["npm", "--version"], { cwd: cwd2, env: env3, stdin: "ignore", stdout: "pipe", stderr: "ignore" });
|
|
109775
|
+
const [versionText, versionStatus] = await Promise.all([new Response(versionProbe.stdout).text(), versionProbe.exited]);
|
|
109776
|
+
const version2 = versionText.trim();
|
|
109777
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version2);
|
|
109778
|
+
if (versionStatus !== 0 || !match || Number(match[1]) < 11 || Number(match[1]) === 11 && Number(match[2]) < 19)
|
|
109779
|
+
throw new Error("NPM_RELEASE_AGE_UNSUPPORTED");
|
|
109780
|
+
const configProbe = Bun.spawn(["npm", "config", "list", "--json"], { cwd: cwd2, env: env3, stdin: "ignore", stdout: "pipe", stderr: "ignore" });
|
|
109781
|
+
const [configText, configStatus] = await Promise.all([new Response(configProbe.stdout).text(), configProbe.exited]);
|
|
109782
|
+
const config2 = object9(JSON.parse(configText), "NPM_RELEASE_AGE_UNSUPPORTED");
|
|
109783
|
+
if (configStatus !== 0 || !Object.hasOwn(config2, "min-release-age") || !Object.hasOwn(config2, "min-release-age-exclude") || !Array.isArray(config2["min-release-age-exclude"]))
|
|
109784
|
+
throw new Error("NPM_RELEASE_AGE_UNSUPPORTED");
|
|
109785
|
+
return version2;
|
|
109786
|
+
} catch {
|
|
109787
|
+
throw new Error("NPM_RELEASE_AGE_UNSUPPORTED");
|
|
109788
|
+
}
|
|
109789
|
+
}
|
|
109790
|
+
async function runNpm(args2, cwd2, home, staging, policy) {
|
|
109757
109791
|
const userNpmrc = join56(staging, "npm-user.npmrc"), globalNpmrc = join56(staging, "npm-global.npmrc");
|
|
109758
|
-
|
|
109759
|
-
writeFileSync23(userNpmrc, `registry=${REGISTRY_ORIGIN}/
|
|
109792
|
+
const baseConfig = `registry=${REGISTRY_ORIGIN}/
|
|
109760
109793
|
ignore-scripts=true
|
|
109761
|
-
|
|
109794
|
+
`;
|
|
109762
109795
|
if (!existsSync34(globalNpmrc))
|
|
109763
|
-
writeFileSync23(globalNpmrc,
|
|
109764
|
-
ignore-scripts=true
|
|
109765
|
-
`, { mode: 384, flag: "wx" });
|
|
109796
|
+
writeFileSync23(globalNpmrc, baseConfig, { mode: 384, flag: "wx" });
|
|
109766
109797
|
const env3 = {
|
|
109767
109798
|
PATH: process.env.PATH ?? "",
|
|
109768
109799
|
HOME: home,
|
|
@@ -109776,6 +109807,19 @@ ignore-scripts=true
|
|
|
109776
109807
|
NPM_CONFIG_REGISTRY: `${REGISTRY_ORIGIN}/`,
|
|
109777
109808
|
NPM_CONFIG_IGNORE_SCRIPTS: "true"
|
|
109778
109809
|
};
|
|
109810
|
+
const capabilityNpmrc = join56(staging, "npm-capability.npmrc");
|
|
109811
|
+
if (policy && !existsSync34(capabilityNpmrc))
|
|
109812
|
+
writeFileSync23(capabilityNpmrc, baseConfig, { mode: 384, flag: "wx" });
|
|
109813
|
+
if (policy && readFileSync37(capabilityNpmrc, "utf8") !== baseConfig)
|
|
109814
|
+
throw new Error("NPM_ISOLATED_CONFIG_DRIFT");
|
|
109815
|
+
const npmVersion = policy ? await npmReleaseAgeCapability(cwd2, { ...env3, NPM_CONFIG_USERCONFIG: capabilityNpmrc }) : undefined;
|
|
109816
|
+
const config2 = baseConfig + (policy ? `min-release-age=${policy.minReleaseAge}
|
|
109817
|
+
${policy.minReleaseAgeExclude.map((pattern) => `min-release-age-exclude[]=${pattern}
|
|
109818
|
+
`).join("")}` : "");
|
|
109819
|
+
if (!existsSync34(userNpmrc))
|
|
109820
|
+
writeFileSync23(userNpmrc, config2, { mode: 384, flag: "wx" });
|
|
109821
|
+
if (readFileSync37(userNpmrc, "utf8") !== config2 || readFileSync37(globalNpmrc, "utf8") !== baseConfig)
|
|
109822
|
+
throw new Error("NPM_ISOLATED_CONFIG_DRIFT");
|
|
109779
109823
|
const proc = Bun.spawn(["npm", ...args2, "--registry", `${REGISTRY_ORIGIN}/`, "--ignore-scripts", "--no-audit", "--no-fund"], {
|
|
109780
109824
|
cwd: cwd2,
|
|
109781
109825
|
env: env3,
|
|
@@ -109786,6 +109830,7 @@ ignore-scripts=true
|
|
|
109786
109830
|
const exitCode = await proc.exited;
|
|
109787
109831
|
if (exitCode !== 0)
|
|
109788
109832
|
throw new Error("DEPENDENCY_GRAPH_INSTALL_FAILED");
|
|
109833
|
+
return npmVersion;
|
|
109789
109834
|
}
|
|
109790
109835
|
function comparePackageTrees(expectedRoot, installedRoot) {
|
|
109791
109836
|
const expected = readTree(expectedRoot, false);
|
|
@@ -110164,6 +110209,7 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
110164
110209
|
throw new Error("EXACT_VERSION_REQUIRED");
|
|
110165
110210
|
if (!STABLE_SEMVER_PATTERN.test(version2))
|
|
110166
110211
|
throw new Error("EXACT_STABLE_VERSION_REQUIRED");
|
|
110212
|
+
const policy = releaseAgePolicy(options);
|
|
110167
110213
|
const home = resolve24(options.homeDir ?? process.env.HOME ?? "");
|
|
110168
110214
|
if (!home || !existsSync34(home))
|
|
110169
110215
|
throw new Error("HOME_NOT_FOUND");
|
|
@@ -110201,8 +110247,10 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
110201
110247
|
mkdirSync24(stagePackage, { mode: 448 });
|
|
110202
110248
|
copyFileSync(artifact.tarballPath, join56(stagePackage, "verified.tgz"));
|
|
110203
110249
|
writeFileSync23(join56(stagePackage, "package.json"), JSON.stringify({ private: true, dependencies: { [PACKAGE_NAME]: "file:./verified.tgz" } }, null, 2), { mode: 384, flag: "wx" });
|
|
110204
|
-
await runNpm(["install", "--package-lock-only", "--prefix", stagePackage], stagePackage, home, stagePath);
|
|
110205
|
-
await runNpm(["ci", "--prefix", stagePackage], stagePackage, home, stagePath);
|
|
110250
|
+
const npmVersion = await runNpm(["install", "--package-lock-only", "--prefix", stagePackage], stagePackage, home, stagePath, policy);
|
|
110251
|
+
const ciNpmVersion = await runNpm(["ci", "--prefix", stagePackage], stagePackage, home, stagePath, policy);
|
|
110252
|
+
if (npmVersion !== ciNpmVersion)
|
|
110253
|
+
throw new Error("NPM_VERSION_DRIFT_DURING_UPDATE");
|
|
110206
110254
|
const lockBytes = readFileSync37(join56(stagePackage, "package-lock.json"));
|
|
110207
110255
|
const installedPackage = join56(stagePackage, "node_modules", "@hasna", "skills");
|
|
110208
110256
|
const installed = readPackage(join56(installedPackage, "package.json"));
|
|
@@ -110243,6 +110291,7 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
110243
110291
|
targetPackageRoot,
|
|
110244
110292
|
configs: configPreimages,
|
|
110245
110293
|
preimageSha256,
|
|
110294
|
+
...policy ? { dependencyPolicy: { ...policy, npmVersion } } : {},
|
|
110246
110295
|
launchers,
|
|
110247
110296
|
switchedLaunchers: [],
|
|
110248
110297
|
rollbackCompletedLaunchers: []
|
|
@@ -110281,7 +110330,7 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
110281
110330
|
}
|
|
110282
110331
|
receipt.state = "switched";
|
|
110283
110332
|
persistReceipt(join56(finalPath, "rollout-receipt.json"), receipt);
|
|
110284
|
-
return { updated: true, version: version2, currentVersion: layout.currentVersion, receiptId: id2, runtimeRoot: finalPath, tarballSha256: artifact.sha256, tarballIntegrity: artifact.integrity, runtimeTreeSha256: stagedTree.digest, launcherCount: launchers.length, configCount: configPreimages.length };
|
|
110333
|
+
return { updated: true, version: version2, currentVersion: layout.currentVersion, receiptId: id2, runtimeRoot: finalPath, tarballSha256: artifact.sha256, tarballIntegrity: artifact.integrity, runtimeTreeSha256: stagedTree.digest, launcherCount: launchers.length, configCount: configPreimages.length, ...policy ? { dependencyPolicy: { ...policy, npmVersion } } : {} };
|
|
110285
110334
|
} catch (error61) {
|
|
110286
110335
|
if (switched.length > 0 || moved) {
|
|
110287
110336
|
let rollbackFailed = false;
|
|
@@ -110505,7 +110554,12 @@ function registerRuntime(parent) {
|
|
|
110505
110554
|
});
|
|
110506
110555
|
const setup = parent.command("setup").description("Point this CLI at a Skills API server, or register agent integrations").option("--api-url <url>", "Skills API origin to send remote work to").option("--global", "Accepted for compatibility; the API origin is always per-user", false).option("--json", "Output setup result as JSON", false).action(async (options) => handleSetup(options));
|
|
110507
110556
|
setup.command("agents").option("--json", "Output registration result as JSON", false).description("Register the Skills MCP server with all supported agents").action(async (options) => handleMcp({ register: "all", json: options.json }));
|
|
110508
|
-
parent.command("self-update").description("Update @hasna/skills to the latest version").option("--json", "Output result as JSON", false).option("--version <version>", "Install this exact @hasna/skills version as a staged copyfile runtime").option("--rollback <receipt-id>", "Restore launcher links from an exact copyfile rollout receipt").option("--adopt-aliases", "Switch verified legacy @hasna/skills aliases to the active copyfile runtime").option("--rollback-aliases <receipt-id>", "Restore aliases from an exact adoption receipt").action(async (options) => {
|
|
110557
|
+
parent.command("self-update").description("Update @hasna/skills to the latest version").option("--json", "Output result as JSON", false).option("--version <version>", "Install this exact @hasna/skills version as a staged copyfile runtime").option("--min-release-age <days>", "Require a positive integer dependency release age in days for an exact-version update").option("--min-release-age-exclude <pattern>", "Exclude matching package identities from the release-age policy (repeatable)", (pattern, prior) => [...prior, pattern], []).option("--rollback <receipt-id>", "Restore launcher links from an exact copyfile rollout receipt").option("--adopt-aliases", "Switch verified legacy @hasna/skills aliases to the active copyfile runtime").option("--rollback-aliases <receipt-id>", "Restore aliases from an exact adoption receipt").action(async (options) => {
|
|
110558
|
+
if ((options.minReleaseAge !== undefined || options.minReleaseAgeExclude.length > 0) && (!options.version || options.rollback || options.adoptAliases || options.rollbackAliases)) {
|
|
110559
|
+
console.error(JSON.stringify({ error: "RELEASE_AGE_POLICY_REQUIRES_EXACT_VERSION" }));
|
|
110560
|
+
process.exitCode = 1;
|
|
110561
|
+
return;
|
|
110562
|
+
}
|
|
110509
110563
|
if ([options.version, options.rollback, options.adoptAliases, options.rollbackAliases].filter(Boolean).length > 1) {
|
|
110510
110564
|
console.error(JSON.stringify({ error: "SELF_UPDATE_OPERATIONS_ARE_MUTUALLY_EXCLUSIVE" }));
|
|
110511
110565
|
process.exitCode = 1;
|
|
@@ -110541,7 +110595,8 @@ function registerRuntime(parent) {
|
|
|
110541
110595
|
}
|
|
110542
110596
|
if (options.version) {
|
|
110543
110597
|
try {
|
|
110544
|
-
const
|
|
110598
|
+
const minReleaseAge = options.minReleaseAge === undefined ? undefined : /^\d+$/.test(options.minReleaseAge) ? Number(options.minReleaseAge) : NaN;
|
|
110599
|
+
const result2 = await updateCopyfileRuntime(options.version, { minReleaseAge, minReleaseAgeExclude: options.minReleaseAgeExclude });
|
|
110545
110600
|
if (options.json)
|
|
110546
110601
|
console.log(JSON.stringify(result2));
|
|
110547
110602
|
else
|
package/bin/mcp.js
CHANGED
|
@@ -20019,7 +20019,7 @@ var package_default;
|
|
|
20019
20019
|
var init_package = __esm(() => {
|
|
20020
20020
|
package_default = {
|
|
20021
20021
|
name: "@hasna/skills",
|
|
20022
|
-
version: "0.10.
|
|
20022
|
+
version: "0.10.34",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
package/bin/migrate.js
CHANGED
package/bin/server.js
CHANGED
|
@@ -24122,7 +24122,7 @@ var init_dist_es9 = __esm(() => {
|
|
|
24122
24122
|
// package.json
|
|
24123
24123
|
var package_default = {
|
|
24124
24124
|
name: "@hasna/skills",
|
|
24125
|
-
version: "0.10.
|
|
24125
|
+
version: "0.10.34",
|
|
24126
24126
|
description: "Skills library for AI coding agents",
|
|
24127
24127
|
type: "module",
|
|
24128
24128
|
bin: {
|
package/bin/worker.js
CHANGED
|
@@ -24125,7 +24125,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
24125
24125
|
// package.json
|
|
24126
24126
|
var package_default = {
|
|
24127
24127
|
name: "@hasna/skills",
|
|
24128
|
-
version: "0.10.
|
|
24128
|
+
version: "0.10.34",
|
|
24129
24129
|
description: "Skills library for AI coding agents",
|
|
24130
24130
|
type: "module",
|
|
24131
24131
|
bin: {
|
|
@@ -26,6 +26,8 @@ export declare function updateCopyfileRuntime(version: string, options?: {
|
|
|
26
26
|
pathValue?: string;
|
|
27
27
|
registryOrigin?: string;
|
|
28
28
|
fetcher?: typeof fetch;
|
|
29
|
+
minReleaseAge?: number;
|
|
30
|
+
minReleaseAgeExclude?: string[];
|
|
29
31
|
onLauncherSwitched?: (path: string) => void;
|
|
30
32
|
}): Promise<JsonObject>;
|
|
31
33
|
export declare function rollbackCopyfileRuntime(receiptId: string, options?: {
|
package/dist/index.js
CHANGED
|
@@ -10975,7 +10975,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
10975
10975
|
// package.json
|
|
10976
10976
|
var package_default = {
|
|
10977
10977
|
name: "@hasna/skills",
|
|
10978
|
-
version: "0.10.
|
|
10978
|
+
version: "0.10.34",
|
|
10979
10979
|
description: "Skills library for AI coding agents",
|
|
10980
10980
|
type: "module",
|
|
10981
10981
|
bin: {
|
package/dist/sdk/index.js
CHANGED
|
@@ -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.34",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|