@hasna/skills 0.10.1 → 0.10.3
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 +46 -9
- package/bin/index.js +470 -160
- package/bin/mcp.js +229 -42
- package/bin/migrate.js +2 -6
- package/bin/server.js +5 -6
- package/bin/worker.js +2 -6
- package/dist/cli/commands/context.d.ts +1 -0
- package/dist/index.js +256 -65
- package/dist/lib/agent-integration.d.ts +2 -0
- package/dist/lib/selection-cache.d.ts +12 -0
- package/dist/lib/session-reconciliation.d.ts +7 -1
- package/dist/sdk/index.js +329 -139
- package/package.json +1 -1
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.3",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
|
@@ -46400,7 +46400,9 @@ var init_skill_version = __esm(() => {
|
|
|
46400
46400
|
|
|
46401
46401
|
// src/lib/selection-cache.ts
|
|
46402
46402
|
import { createHash as createHash7, randomUUID } from "crypto";
|
|
46403
|
-
import {
|
|
46403
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
46404
|
+
import { closeSync as closeSync6, constants as constants3, fstatSync as fstatSync6, fsyncSync, lstatSync as lstatSync8, mkdirSync as mkdirSync3, openSync as openSync6, readFileSync as readFileSync10, readlinkSync as readlinkSync2, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
46405
|
+
import { hostname as hostname3 } from "os";
|
|
46404
46406
|
import { dirname as dirname8, join as join14, parse as parse7, resolve as resolve7 } from "path";
|
|
46405
46407
|
import { isDeepStrictEqual } from "util";
|
|
46406
46408
|
function selectionCacheRoot(options = {}) {
|
|
@@ -46670,6 +46672,164 @@ function readSkillSessionSnapshotIfExists(sessionId, options = {}) {
|
|
|
46670
46672
|
function skillSessionSnapshotBinding(snapshot) {
|
|
46671
46673
|
return { sessionId: snapshot.sessionId, generation: snapshot.generation, receiptSha256: snapshot.receiptSha256 };
|
|
46672
46674
|
}
|
|
46675
|
+
function localBootId() {
|
|
46676
|
+
try {
|
|
46677
|
+
if (process.platform === "linux") {
|
|
46678
|
+
const value = readFileSync10("/proc/sys/kernel/random/boot_id", "utf8").trim();
|
|
46679
|
+
return /^[a-f0-9-]{36}$/i.test(value) ? value : null;
|
|
46680
|
+
}
|
|
46681
|
+
if (process.platform === "darwin") {
|
|
46682
|
+
const result = spawnSync2("/usr/sbin/sysctl", ["-n", "kern.boottime"], { encoding: "utf8", timeout: 3000, env: { LC_ALL: "C" } });
|
|
46683
|
+
if (result.status !== 0 || result.error)
|
|
46684
|
+
return null;
|
|
46685
|
+
const seconds = /\bsec\s*=\s*(\d+)\b/.exec(result.stdout)?.[1];
|
|
46686
|
+
return seconds ? `darwin:${seconds}` : null;
|
|
46687
|
+
}
|
|
46688
|
+
} catch {}
|
|
46689
|
+
return null;
|
|
46690
|
+
}
|
|
46691
|
+
function processStart(pid) {
|
|
46692
|
+
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
46693
|
+
return { state: "unknown" };
|
|
46694
|
+
if (process.platform === "linux") {
|
|
46695
|
+
try {
|
|
46696
|
+
const stat = readFileSync10(`/proc/${pid}/stat`, "utf8");
|
|
46697
|
+
const close = stat.lastIndexOf(")");
|
|
46698
|
+
const start = close < 0 ? undefined : stat.slice(close + 2).trim().split(/\s+/)[19];
|
|
46699
|
+
return start && /^\d+$/.test(start) ? { state: "alive", start: `linux:${start}` } : { state: "unknown" };
|
|
46700
|
+
} catch (error61) {
|
|
46701
|
+
return { state: error61.code === "ENOENT" ? "absent" : "unknown" };
|
|
46702
|
+
}
|
|
46703
|
+
}
|
|
46704
|
+
if (process.platform === "darwin") {
|
|
46705
|
+
const result = spawnSync2("/bin/ps", ["-p", String(pid), "-o", "lstart="], { encoding: "utf8", timeout: 3000, env: { LC_ALL: "C" } });
|
|
46706
|
+
if (result.error || result.signal)
|
|
46707
|
+
return { state: "unknown" };
|
|
46708
|
+
const start = result.stdout.trim();
|
|
46709
|
+
if (result.status === 1 && !start && !result.stderr.trim())
|
|
46710
|
+
return { state: "absent" };
|
|
46711
|
+
return result.status === 0 && start ? { state: "alive", start: `darwin:${start}` } : { state: "unknown" };
|
|
46712
|
+
}
|
|
46713
|
+
return { state: "unknown" };
|
|
46714
|
+
}
|
|
46715
|
+
function localLockIdentity() {
|
|
46716
|
+
const bootId = localBootId(), owner = processStart(process.pid);
|
|
46717
|
+
if (!bootId || owner.state !== "alive")
|
|
46718
|
+
return null;
|
|
46719
|
+
let pidNamespace;
|
|
46720
|
+
if (process.platform === "linux") {
|
|
46721
|
+
try {
|
|
46722
|
+
pidNamespace = readlinkSync2("/proc/self/ns/pid");
|
|
46723
|
+
} catch {
|
|
46724
|
+
return null;
|
|
46725
|
+
}
|
|
46726
|
+
if (!/^pid:\[\d+\]$/.test(pidNamespace))
|
|
46727
|
+
return null;
|
|
46728
|
+
}
|
|
46729
|
+
return { host: hostname3(), bootId, pidNamespace, processStart: owner.start };
|
|
46730
|
+
}
|
|
46731
|
+
function readSessionLock(sessionId, options) {
|
|
46732
|
+
const path = `${sessionReceiptPath(sessionId, options)}.write-lock`;
|
|
46733
|
+
const before = lstatSync8(path, { throwIfNoEntry: false });
|
|
46734
|
+
if (!before)
|
|
46735
|
+
return null;
|
|
46736
|
+
if (!before.isFile() || before.nlink !== 1 || process.getuid && before.uid !== process.getuid()) {
|
|
46737
|
+
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock is not a private regular file; recovery requires review.");
|
|
46738
|
+
}
|
|
46739
|
+
const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
|
|
46740
|
+
const after = lstatSync8(path, { throwIfNoEntry: false });
|
|
46741
|
+
if (!bytes || !after?.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs) {
|
|
46742
|
+
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock changed during inspection; recovery requires review.");
|
|
46743
|
+
}
|
|
46744
|
+
let marker;
|
|
46745
|
+
try {
|
|
46746
|
+
marker = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
46747
|
+
} catch {
|
|
46748
|
+
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock is unreadable; recovery requires review.");
|
|
46749
|
+
}
|
|
46750
|
+
if (!marker || ![1, 2].includes(marker.schemaVersion) || marker.sessionId !== sessionId || !/^[a-f0-9-]{36}$/i.test(marker.operationId) || !Number.isSafeInteger(marker.pid) || marker.pid <= 0 || marker.schemaVersion === 2 && (!marker.host || !marker.bootId || !marker.processStart || process.platform === "linux" && !marker.pidNamespace)) {
|
|
46751
|
+
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock has invalid ownership metadata; recovery requires review.");
|
|
46752
|
+
}
|
|
46753
|
+
return { path, bytes, marker, sha256: sha256Hex(bytes), dev: after.dev, ino: after.ino, mtimeMs: after.mtimeMs };
|
|
46754
|
+
}
|
|
46755
|
+
function writerIsGone(lock, explicitLocalHost) {
|
|
46756
|
+
if (Date.now() - lock.mtimeMs < SESSION_LOCK_MIN_AGE_MS)
|
|
46757
|
+
return false;
|
|
46758
|
+
const owner = processStart(lock.marker.pid);
|
|
46759
|
+
if (owner.state === "unknown")
|
|
46760
|
+
return false;
|
|
46761
|
+
if (lock.marker.schemaVersion === 1)
|
|
46762
|
+
return explicitLocalHost && owner.state === "absent";
|
|
46763
|
+
const local = localLockIdentity();
|
|
46764
|
+
if (!local || lock.marker.host !== local.host || lock.marker.bootId !== local.bootId || lock.marker.pidNamespace !== local.pidNamespace)
|
|
46765
|
+
return false;
|
|
46766
|
+
return owner.state === "absent" || owner.state === "alive" && owner.start !== lock.marker.processStart;
|
|
46767
|
+
}
|
|
46768
|
+
function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
46769
|
+
const guardPath = `${lock.path}.recovery-guard`;
|
|
46770
|
+
let guardFd;
|
|
46771
|
+
try {
|
|
46772
|
+
guardFd = openSync6(guardPath, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
|
|
46773
|
+
} catch (error61) {
|
|
46774
|
+
if (error61.code === "EEXIST")
|
|
46775
|
+
return null;
|
|
46776
|
+
throw error61;
|
|
46777
|
+
}
|
|
46778
|
+
const guardStat = fstatSync6(guardFd);
|
|
46779
|
+
try {
|
|
46780
|
+
writeFileSync3(guardFd, `${JSON.stringify({ schemaVersion: 1, pid: process.pid, operationId: randomUUID() })}
|
|
46781
|
+
`);
|
|
46782
|
+
fsyncSync(guardFd);
|
|
46783
|
+
const cacheOptions = { cacheDir: dirname8(dirname8(lock.path)) };
|
|
46784
|
+
const current = readSessionLock(lock.marker.sessionId, cacheOptions);
|
|
46785
|
+
if (!current || current.dev !== lock.dev || current.ino !== lock.ino || current.sha256 !== lock.sha256 || current.mtimeMs !== lock.mtimeMs || !writerIsGone(current, explicitLocalHost))
|
|
46786
|
+
return null;
|
|
46787
|
+
const currentReceipt = readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions);
|
|
46788
|
+
if (currentReceipt?.sha256 !== receipt?.sha256 || currentReceipt?.generation !== receipt?.generation)
|
|
46789
|
+
return null;
|
|
46790
|
+
const directory = join14(dirname8(dirname8(lock.path)), "session-lock-recoveries", randomUUID());
|
|
46791
|
+
const archivePath = join14(directory, "original.write-lock");
|
|
46792
|
+
const recordPath = join14(directory, "recovery.json");
|
|
46793
|
+
assertRegularPath(archivePath, true);
|
|
46794
|
+
archiveBytes(recordPath, Buffer.from(`${JSON.stringify({
|
|
46795
|
+
schemaVersion: 1,
|
|
46796
|
+
sessionId: lock.marker.sessionId,
|
|
46797
|
+
originalPath: lock.path,
|
|
46798
|
+
lockSha256: lock.sha256,
|
|
46799
|
+
lockMtime: new Date(lock.mtimeMs).toISOString(),
|
|
46800
|
+
lockPid: lock.marker.pid,
|
|
46801
|
+
receiptSha256: receipt?.sha256 ?? null,
|
|
46802
|
+
receiptGeneration: receipt?.generation ?? null,
|
|
46803
|
+
status: "prepared"
|
|
46804
|
+
})}
|
|
46805
|
+
`));
|
|
46806
|
+
syncDirectory(directory);
|
|
46807
|
+
syncDirectory(dirname8(directory));
|
|
46808
|
+
const finalLock = readSessionLock(lock.marker.sessionId, cacheOptions);
|
|
46809
|
+
if (!finalLock || finalLock.dev !== lock.dev || finalLock.ino !== lock.ino || finalLock.sha256 !== lock.sha256 || readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions)?.sha256 !== receipt?.sha256)
|
|
46810
|
+
return null;
|
|
46811
|
+
let moved = false;
|
|
46812
|
+
try {
|
|
46813
|
+
renameSync(lock.path, archivePath);
|
|
46814
|
+
moved = true;
|
|
46815
|
+
syncDirectory(directory);
|
|
46816
|
+
syncDirectory(dirname8(lock.path));
|
|
46817
|
+
if (sha256Hex(readRegularFile(archivePath, SESSION_LOCK_MAX_BYTES)) !== lock.sha256) {
|
|
46818
|
+
throw new Error("Archived lock bytes changed");
|
|
46819
|
+
}
|
|
46820
|
+
} catch (error61) {
|
|
46821
|
+
if (moved)
|
|
46822
|
+
throw new SkillSelectionError("SESSION_LOCK_RECOVERY_INCOMPLETE", "The lock moved, but durable recovery could not be confirmed; inspect the preserved recovery directory and session receipt.");
|
|
46823
|
+
throw error61;
|
|
46824
|
+
}
|
|
46825
|
+
return archivePath;
|
|
46826
|
+
} finally {
|
|
46827
|
+
closeSync6(guardFd);
|
|
46828
|
+
const current = lstatSync8(guardPath, { throwIfNoEntry: false });
|
|
46829
|
+
if (current?.isFile() && current.dev === guardStat.dev && current.ino === guardStat.ino)
|
|
46830
|
+
unlinkSync(guardPath);
|
|
46831
|
+
}
|
|
46832
|
+
}
|
|
46673
46833
|
function withSessionWriteLocks(sessionIds, options, action) {
|
|
46674
46834
|
const operationId = randomUUID();
|
|
46675
46835
|
const locks = [...new Set(sessionIds)].map((sessionId) => ({ sessionId, path: `${sessionReceiptPath(sessionId, options)}.write-lock` })).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
@@ -46689,13 +46849,30 @@ function withSessionWriteLocks(sessionIds, options, action) {
|
|
|
46689
46849
|
try {
|
|
46690
46850
|
fd = openSync6(lock.path, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
|
|
46691
46851
|
} catch (error61) {
|
|
46692
|
-
if (error61.code
|
|
46693
|
-
throw
|
|
46694
|
-
|
|
46852
|
+
if (error61.code !== "EEXIST")
|
|
46853
|
+
throw error61;
|
|
46854
|
+
try {
|
|
46855
|
+
const stale = readSessionLock(lock.sessionId, options);
|
|
46856
|
+
const receipt = stale && readSkillSessionSnapshotIfExists(lock.sessionId, options);
|
|
46857
|
+
if (stale?.marker.schemaVersion === 2 && writerIsGone(stale, false)) {
|
|
46858
|
+
archiveSessionLock(stale, receipt, false);
|
|
46859
|
+
}
|
|
46860
|
+
} catch (recoveryError) {
|
|
46861
|
+
if (recoveryError instanceof SkillSelectionError && recoveryError.code === "SESSION_LOCK_RECOVERY_INCOMPLETE")
|
|
46862
|
+
throw recoveryError;
|
|
46863
|
+
}
|
|
46864
|
+
try {
|
|
46865
|
+
fd = openSync6(lock.path, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
|
|
46866
|
+
} catch (retryError) {
|
|
46867
|
+
if (retryError.code === "EEXIST")
|
|
46868
|
+
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "Another Skills process owns a required session write lock; retry after it completes. A surviving legacy lock requires explicit recovery review.");
|
|
46869
|
+
throw retryError;
|
|
46870
|
+
}
|
|
46695
46871
|
}
|
|
46696
46872
|
const stat = fstatSync6(fd);
|
|
46697
46873
|
owned.push({ path: lock.path, fd, dev: stat.dev, ino: stat.ino });
|
|
46698
|
-
|
|
46874
|
+
const identity3 = localLockIdentity();
|
|
46875
|
+
writeFileSync3(fd, `${JSON.stringify({ schemaVersion: identity3 ? 2 : 1, operationId, sessionId: lock.sessionId, pid: process.pid, ...identity3 })}
|
|
46699
46876
|
`);
|
|
46700
46877
|
}
|
|
46701
46878
|
return action(assertOwned);
|
|
@@ -46758,7 +46935,17 @@ function syncDirectory(path) {
|
|
|
46758
46935
|
closeSync6(fd);
|
|
46759
46936
|
}
|
|
46760
46937
|
}
|
|
46761
|
-
|
|
46938
|
+
function archiveBytes(path, bytes) {
|
|
46939
|
+
assertRegularPath(path, true);
|
|
46940
|
+
const fd = openSync6(path, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
|
|
46941
|
+
try {
|
|
46942
|
+
writeFileSync3(fd, bytes);
|
|
46943
|
+
fsyncSync(fd);
|
|
46944
|
+
} finally {
|
|
46945
|
+
closeSync6(fd);
|
|
46946
|
+
}
|
|
46947
|
+
}
|
|
46948
|
+
var SkillSelectionError, MAX_CACHED_PROFILE_AGE_MS, SELECTION_LOCK_FILE = "selection.lock.json", SESSION_LOCK_MAX_BYTES = 4096, SESSION_LOCK_MIN_AGE_MS = 30000;
|
|
46762
46949
|
var init_selection_cache = __esm(() => {
|
|
46763
46950
|
init_config();
|
|
46764
46951
|
init_skill_bundle();
|
|
@@ -46816,7 +47003,7 @@ function capturePreparationProcess(child, platform = process.platform, signalGro
|
|
|
46816
47003
|
}
|
|
46817
47004
|
|
|
46818
47005
|
// src/lib/dependency-preparation.ts
|
|
46819
|
-
import { constants as constants4, closeSync as closeSync7, fstatSync as fstatSync7, lstatSync as lstatSync9, mkdirSync as mkdirSync4, openSync as openSync7, readFileSync as
|
|
47006
|
+
import { constants as constants4, closeSync as closeSync7, fstatSync as fstatSync7, lstatSync as lstatSync9, mkdirSync as mkdirSync4, openSync as openSync7, readFileSync as readFileSync11, readdirSync as readdirSync5, rmdirSync, unlinkSync as unlinkSync2, ftruncateSync, writeSync } from "fs";
|
|
46820
47007
|
import { join as join15 } from "path";
|
|
46821
47008
|
function hasDependencyPreparationMarker(skillPath) {
|
|
46822
47009
|
try {
|
|
@@ -46846,7 +47033,7 @@ function claimPreparation(skillPath) {
|
|
|
46846
47033
|
return;
|
|
46847
47034
|
if (!lstatSync9(state).isFile() || lstatSync9(state).isSymbolicLink() || lstatSync9(state).size > 1024)
|
|
46848
47035
|
return;
|
|
46849
|
-
const previous = JSON.parse(
|
|
47036
|
+
const previous = JSON.parse(readFileSync11(state, "utf8"));
|
|
46850
47037
|
if (previous.version !== 1 || previous.status !== "failed" || Object.keys(previous).length !== 2)
|
|
46851
47038
|
return;
|
|
46852
47039
|
}
|
|
@@ -47398,7 +47585,7 @@ var init_skill_aliases = __esm(() => {
|
|
|
47398
47585
|
});
|
|
47399
47586
|
|
|
47400
47587
|
// src/lib/project-state.ts
|
|
47401
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as
|
|
47588
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
|
|
47402
47589
|
import { join as join17 } from "path";
|
|
47403
47590
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
47404
47591
|
return join17(targetDir, SKILLS_PROJECT_DIR);
|
|
@@ -47411,7 +47598,7 @@ function loadProjectConfig(targetDir = process.cwd()) {
|
|
|
47411
47598
|
if (!existsSync8(path))
|
|
47412
47599
|
return null;
|
|
47413
47600
|
try {
|
|
47414
|
-
return normalizeProjectConfig(JSON.parse(
|
|
47601
|
+
return normalizeProjectConfig(JSON.parse(readFileSync12(path, "utf-8")));
|
|
47415
47602
|
} catch {
|
|
47416
47603
|
return null;
|
|
47417
47604
|
}
|
|
@@ -47519,7 +47706,7 @@ var init_project_state = __esm(() => {
|
|
|
47519
47706
|
});
|
|
47520
47707
|
|
|
47521
47708
|
// src/lib/installer.ts
|
|
47522
|
-
import { existsSync as existsSync9, readFileSync as
|
|
47709
|
+
import { existsSync as existsSync9, readFileSync as readFileSync13, rmSync } from "fs";
|
|
47523
47710
|
import { join as join18 } from "path";
|
|
47524
47711
|
import { homedir as homedir2 } from "os";
|
|
47525
47712
|
function getSkillPath(name) {
|
|
@@ -47607,7 +47794,7 @@ function readBundledSkillVersion(name) {
|
|
|
47607
47794
|
if (!existsSync9(pkgPath))
|
|
47608
47795
|
return "unknown";
|
|
47609
47796
|
try {
|
|
47610
|
-
const pkg = JSON.parse(
|
|
47797
|
+
const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
|
|
47611
47798
|
return pkg.version || "unknown";
|
|
47612
47799
|
} catch {
|
|
47613
47800
|
return "unknown";
|
|
@@ -47671,7 +47858,7 @@ var init_skill_doc_file = __esm(() => {
|
|
|
47671
47858
|
});
|
|
47672
47859
|
|
|
47673
47860
|
// src/lib/skillinfo.ts
|
|
47674
|
-
import { existsSync as existsSync10, readFileSync as
|
|
47861
|
+
import { existsSync as existsSync10, readFileSync as readFileSync14 } from "fs";
|
|
47675
47862
|
import { join as join19 } from "path";
|
|
47676
47863
|
function isInstructionSkillDir(skillPath, meta3) {
|
|
47677
47864
|
if (meta3?.kind === "instruction")
|
|
@@ -47680,7 +47867,7 @@ function isInstructionSkillDir(skillPath, meta3) {
|
|
|
47680
47867
|
if (!existsSync10(skillMdPath))
|
|
47681
47868
|
return false;
|
|
47682
47869
|
try {
|
|
47683
|
-
return parseSkillFrontmatter(
|
|
47870
|
+
return parseSkillFrontmatter(readFileSync14(skillMdPath, "utf-8"))?.kind === "instruction";
|
|
47684
47871
|
} catch {
|
|
47685
47872
|
return false;
|
|
47686
47873
|
}
|
|
@@ -47715,7 +47902,7 @@ function getSkillRequirements(name) {
|
|
|
47715
47902
|
const meta3 = getSkill(name);
|
|
47716
47903
|
let dependencies = {};
|
|
47717
47904
|
try {
|
|
47718
|
-
dependencies = JSON.parse(
|
|
47905
|
+
dependencies = JSON.parse(readFileSync14(join19(skillPath, "package.json"), "utf8")).dependencies || {};
|
|
47719
47906
|
} catch {}
|
|
47720
47907
|
return getSkillRequirementsFromContent(name, texts, dependencies, meta3);
|
|
47721
47908
|
}
|
|
@@ -47783,7 +47970,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
47783
47970
|
}
|
|
47784
47971
|
let entryPoint2;
|
|
47785
47972
|
try {
|
|
47786
|
-
const pkg = JSON.parse(
|
|
47973
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
47787
47974
|
if (pkg.bin) {
|
|
47788
47975
|
const binValues = Object.values(pkg.bin);
|
|
47789
47976
|
entryPoint2 = binValues[0];
|
|
@@ -47828,7 +48015,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
47828
48015
|
function detectProjectSkills(cwd = process.cwd()) {
|
|
47829
48016
|
let pkg;
|
|
47830
48017
|
try {
|
|
47831
|
-
pkg = JSON.parse(
|
|
48018
|
+
pkg = JSON.parse(readFileSync14(join19(cwd, "package.json"), "utf8"));
|
|
47832
48019
|
} catch {
|
|
47833
48020
|
return { detected: [], recommended: [] };
|
|
47834
48021
|
}
|
|
@@ -47867,7 +48054,7 @@ function extractEnvVars(text2) {
|
|
|
47867
48054
|
function readIfExists(path) {
|
|
47868
48055
|
try {
|
|
47869
48056
|
if (existsSync10(path)) {
|
|
47870
|
-
return
|
|
48057
|
+
return readFileSync14(path, "utf-8");
|
|
47871
48058
|
}
|
|
47872
48059
|
} catch {}
|
|
47873
48060
|
return null;
|
|
@@ -48985,7 +49172,7 @@ var init_codex_hook_discovery_recovery = __esm(() => {
|
|
|
48985
49172
|
|
|
48986
49173
|
// src/lib/agent-discovery.ts
|
|
48987
49174
|
import { createHash as createHash14 } from "crypto";
|
|
48988
|
-
import { lstatSync as lstatSync16, readFileSync as
|
|
49175
|
+
import { lstatSync as lstatSync16, readFileSync as readFileSync15, readdirSync as readdirSync6, realpathSync as realpathSync3 } from "fs";
|
|
48989
49176
|
import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute13, join as join23, resolve as resolve13, sep as sep4 } from "path";
|
|
48990
49177
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
48991
49178
|
function parseConfig(text2, path, toml = false) {
|
|
@@ -49017,7 +49204,7 @@ function read(path, changes) {
|
|
|
49017
49204
|
return null;
|
|
49018
49205
|
if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
|
|
49019
49206
|
throw new Error(`Unsupported or oversized native discovery input: ${path}`);
|
|
49020
|
-
return
|
|
49207
|
+
return readFileSync15(path, "utf8");
|
|
49021
49208
|
}
|
|
49022
49209
|
function projected(source, changes, budget = discoveryByteBudget()) {
|
|
49023
49210
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
@@ -49894,7 +50081,7 @@ var init_search = __esm(() => {
|
|
|
49894
50081
|
});
|
|
49895
50082
|
|
|
49896
50083
|
// src/lib/registry.ts
|
|
49897
|
-
import { existsSync as existsSync12, readFileSync as
|
|
50084
|
+
import { existsSync as existsSync12, readFileSync as readFileSync16, readdirSync as readdirSync8 } from "fs";
|
|
49898
50085
|
import { join as join26 } from "path";
|
|
49899
50086
|
function parseSkillMdFrontmatter(content) {
|
|
49900
50087
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -49941,7 +50128,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
49941
50128
|
continue;
|
|
49942
50129
|
let content;
|
|
49943
50130
|
try {
|
|
49944
|
-
content =
|
|
50131
|
+
content = readFileSync16(skillMdPath, "utf-8");
|
|
49945
50132
|
} catch {
|
|
49946
50133
|
continue;
|
|
49947
50134
|
}
|
|
@@ -49978,7 +50165,7 @@ function findExtensionSkillPath(name) {
|
|
|
49978
50165
|
continue;
|
|
49979
50166
|
let content;
|
|
49980
50167
|
try {
|
|
49981
|
-
content =
|
|
50168
|
+
content = readFileSync16(skillMdPath, "utf-8");
|
|
49982
50169
|
} catch {
|
|
49983
50170
|
continue;
|
|
49984
50171
|
}
|
|
@@ -51170,7 +51357,7 @@ var init_remote_workspace_leave = __esm(() => {
|
|
|
51170
51357
|
});
|
|
51171
51358
|
|
|
51172
51359
|
// src/lib/auth-store.ts
|
|
51173
|
-
import { chmodSync, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as
|
|
51360
|
+
import { chmodSync, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync17, renameSync as renameSync4, statSync as statSync8, unlinkSync as unlinkSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
51174
51361
|
import { basename as basename10, dirname as dirname17, join as join27 } from "path";
|
|
51175
51362
|
function getAuthFilePath(env = process.env) {
|
|
51176
51363
|
return skillsCredentialFilePath(env);
|
|
@@ -53184,7 +53371,7 @@ var init_remote_auth = __esm(() => {
|
|
|
53184
53371
|
});
|
|
53185
53372
|
|
|
53186
53373
|
// src/lib/workspace-profile.ts
|
|
53187
|
-
import { constants as constants11, closeSync as closeSync14, fstatSync as fstatSync14, lstatSync as lstatSync18, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync2, openSync as openSync14, readFileSync as
|
|
53374
|
+
import { constants as constants11, closeSync as closeSync14, fstatSync as fstatSync14, lstatSync as lstatSync18, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync2, openSync as openSync14, readFileSync as readFileSync18, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "fs";
|
|
53188
53375
|
function stat2(path) {
|
|
53189
53376
|
try {
|
|
53190
53377
|
return lstatSync18(path);
|
|
@@ -53202,7 +53389,7 @@ function safeText(file2) {
|
|
|
53202
53389
|
const s = fstatSync14(fd);
|
|
53203
53390
|
if (!s.isFile() || s.size > 65536 || ![256, 384].includes(s.mode & 4095) || process.getuid && s.uid !== process.getuid())
|
|
53204
53391
|
return fail("The selected profile must use bounded owner-only regular files.");
|
|
53205
|
-
return
|
|
53392
|
+
return readFileSync18(fd, "utf8");
|
|
53206
53393
|
} finally {
|
|
53207
53394
|
closeSync14(fd);
|
|
53208
53395
|
}
|
|
@@ -55658,7 +55845,7 @@ var init_credential_state = __esm(() => {
|
|
|
55658
55845
|
|
|
55659
55846
|
// src/lib/run-state.ts
|
|
55660
55847
|
import { createHash as createHash17, randomBytes } from "crypto";
|
|
55661
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as
|
|
55848
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as readFileSync19, readdirSync as readdirSync9, statSync as statSync9, writeFileSync as writeFileSync10 } from "fs";
|
|
55662
55849
|
import { extname, join as join29, relative as relative5 } from "path";
|
|
55663
55850
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
55664
55851
|
const now = new Date;
|
|
@@ -55730,7 +55917,7 @@ function appendRunEvent(context2, event, data = {}) {
|
|
|
55730
55917
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
55731
55918
|
`;
|
|
55732
55919
|
const path = join29(context2.runDir, "events.ndjson");
|
|
55733
|
-
const previous = existsSync14(path) ?
|
|
55920
|
+
const previous = existsSync14(path) ? readFileSync19(path, "utf-8") : "";
|
|
55734
55921
|
writeFileSync10(path, previous + line);
|
|
55735
55922
|
}
|
|
55736
55923
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
@@ -55768,7 +55955,7 @@ function collectRunArtifacts(context2) {
|
|
|
55768
55955
|
const artifacts = [];
|
|
55769
55956
|
for (const path of walkFiles(context2.exportDir)) {
|
|
55770
55957
|
const stat3 = statSync9(path);
|
|
55771
|
-
const bytes =
|
|
55958
|
+
const bytes = readFileSync19(path);
|
|
55772
55959
|
artifacts.push({
|
|
55773
55960
|
path: toProjectRelative(context2.targetDir, path),
|
|
55774
55961
|
mime: mimeForPath(path),
|
|
@@ -55783,7 +55970,7 @@ function readRunRecord(runDir) {
|
|
|
55783
55970
|
if (!existsSync14(path))
|
|
55784
55971
|
return null;
|
|
55785
55972
|
try {
|
|
55786
|
-
return JSON.parse(
|
|
55973
|
+
return JSON.parse(readFileSync19(path, "utf-8"));
|
|
55787
55974
|
} catch {
|
|
55788
55975
|
return null;
|
|
55789
55976
|
}
|
|
@@ -56043,8 +56230,8 @@ var init_cloud_executions = __esm(() => {
|
|
|
56043
56230
|
|
|
56044
56231
|
// node_modules/@hasna/secrets/dist/sdk.js
|
|
56045
56232
|
import { isIP as isIP2 } from "net";
|
|
56046
|
-
import { spawnSync as
|
|
56047
|
-
import { closeSync as closeSync16, fstatSync as fstatSync16, openSync as openSync16, readFileSync as
|
|
56233
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
56234
|
+
import { closeSync as closeSync16, fstatSync as fstatSync16, openSync as openSync16, readFileSync as readFileSync20 } from "fs";
|
|
56048
56235
|
import { O_NOFOLLOW as O_NOFOLLOW3, O_NONBLOCK as O_NONBLOCK3, O_RDONLY as O_RDONLY3 } from "constants";
|
|
56049
56236
|
import { createRequire as createRequire2 } from "module";
|
|
56050
56237
|
import { hostname as osHostname2 } from "os";
|
|
@@ -56170,7 +56357,7 @@ function readAppConfigFile2(path) {
|
|
|
56170
56357
|
unsafe("the file is not owned by the current user");
|
|
56171
56358
|
if (before.size > MAX_CREDENTIAL_FILE_BYTES2)
|
|
56172
56359
|
unsafe("the file exceeds the size limit");
|
|
56173
|
-
const bytes =
|
|
56360
|
+
const bytes = readFileSync20(fd);
|
|
56174
56361
|
const after = fstatSync16(fd);
|
|
56175
56362
|
if (!configFileReadsCoherent2(before, after)) {
|
|
56176
56363
|
unsafe("the file changed while being read");
|
|
@@ -56318,7 +56505,7 @@ function isAmbientEnvironment2(env) {
|
|
|
56318
56505
|
return env === process.env || env[AMBIENT_ENVIRONMENT2] === true;
|
|
56319
56506
|
}
|
|
56320
56507
|
function defaultKeychainRunner2(argv) {
|
|
56321
|
-
const result =
|
|
56508
|
+
const result = spawnSync3(KEYCHAIN_SECURITY_BIN2, [...argv], {
|
|
56322
56509
|
encoding: "utf8",
|
|
56323
56510
|
stdio: ["ignore", "pipe", "pipe"],
|
|
56324
56511
|
timeout: KEYCHAIN_SPAWN_TIMEOUT_MS2
|
|
@@ -58652,7 +58839,7 @@ var init_resource_meta_tools = __esm(() => {
|
|
|
58652
58839
|
});
|
|
58653
58840
|
|
|
58654
58841
|
// src/lib/scheduler.ts
|
|
58655
|
-
import { existsSync as existsSync17, readFileSync as
|
|
58842
|
+
import { existsSync as existsSync17, readFileSync as readFileSync21, writeFileSync as writeFileSync12, mkdirSync as mkdirSync14 } from "fs";
|
|
58656
58843
|
import { join as join34 } from "path";
|
|
58657
58844
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
58658
58845
|
return join34(targetDir, ".skills", "schedules.json");
|
|
@@ -58661,7 +58848,7 @@ function loadSchedules(targetDir = process.cwd()) {
|
|
|
58661
58848
|
const path = getSchedulesPath(targetDir);
|
|
58662
58849
|
if (existsSync17(path)) {
|
|
58663
58850
|
try {
|
|
58664
|
-
return JSON.parse(
|
|
58851
|
+
return JSON.parse(readFileSync21(path, "utf-8"));
|
|
58665
58852
|
} catch {}
|
|
58666
58853
|
}
|
|
58667
58854
|
return { version: 1, schedules: [] };
|
|
@@ -58944,7 +59131,7 @@ import { createHash as createHash19, createHmac } from "crypto";
|
|
|
58944
59131
|
import {
|
|
58945
59132
|
existsSync as existsSync18,
|
|
58946
59133
|
mkdirSync as mkdirSync15,
|
|
58947
|
-
readFileSync as
|
|
59134
|
+
readFileSync as readFileSync22,
|
|
58948
59135
|
readdirSync as readdirSync11,
|
|
58949
59136
|
statSync as statSync11,
|
|
58950
59137
|
writeFileSync as writeFileSync13
|
|
@@ -59012,7 +59199,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
59012
59199
|
const files = [];
|
|
59013
59200
|
if (existsSync18(projectStateDir)) {
|
|
59014
59201
|
for (const filePath of walkFiles2(projectStateDir)) {
|
|
59015
|
-
const bytes =
|
|
59202
|
+
const bytes = readFileSync22(filePath);
|
|
59016
59203
|
const relativePath = toPosix(relative6(targetDir, filePath));
|
|
59017
59204
|
files.push({
|
|
59018
59205
|
path: relativePath,
|
|
@@ -62214,7 +62401,7 @@ async function handleMcpHttpNodeRequest(req, res) {
|
|
|
62214
62401
|
return true;
|
|
62215
62402
|
}
|
|
62216
62403
|
async function startSkillsMcpHttpServer(options = {}) {
|
|
62217
|
-
const
|
|
62404
|
+
const hostname4 = options.hostname ?? "127.0.0.1";
|
|
62218
62405
|
const port = options.port ?? parseMcpHttpPort(process.argv.slice(2));
|
|
62219
62406
|
const httpServer = createServer(async (req, res) => {
|
|
62220
62407
|
try {
|
|
@@ -62232,11 +62419,11 @@ async function startSkillsMcpHttpServer(options = {}) {
|
|
|
62232
62419
|
});
|
|
62233
62420
|
await new Promise((resolve19, reject) => {
|
|
62234
62421
|
httpServer.once("error", reject);
|
|
62235
|
-
httpServer.listen(port,
|
|
62422
|
+
httpServer.listen(port, hostname4, () => resolve19());
|
|
62236
62423
|
});
|
|
62237
62424
|
const address = httpServer.address();
|
|
62238
62425
|
const listenPort = typeof address === "object" && address ? address.port : port;
|
|
62239
|
-
console.error(`skills-mcp HTTP listening on http://${
|
|
62426
|
+
console.error(`skills-mcp HTTP listening on http://${hostname4}:${listenPort}/mcp`);
|
|
62240
62427
|
return httpServer;
|
|
62241
62428
|
}
|
|
62242
62429
|
var MCP_HTTP_DEFAULT_PORT = 8836, MCP_HTTP_SERVICE_NAME = "skills";
|
package/bin/migrate.js
CHANGED
|
@@ -7,7 +7,7 @@ import { join as join5 } from "path";
|
|
|
7
7
|
// package.json
|
|
8
8
|
var package_default = {
|
|
9
9
|
name: "@hasna/skills",
|
|
10
|
-
version: "0.10.
|
|
10
|
+
version: "0.10.3",
|
|
11
11
|
description: "Skills library for AI coding agents",
|
|
12
12
|
type: "module",
|
|
13
13
|
bin: {
|
|
@@ -201,15 +201,11 @@ function resolveServerConfig(env = process.env) {
|
|
|
201
201
|
requestBodyLimitBytes: parsePositiveInt(env.HASNA_SKILLS_REQUEST_BODY_LIMIT_BYTES, 1e6),
|
|
202
202
|
skillBundleLimitBytes: parsePositiveInt(env.HASNA_SKILLS_BUNDLE_LIMIT_BYTES, 25000000),
|
|
203
203
|
tombstoneWindowMs: parsePositiveInt(env.HASNA_SKILLS_TOMBSTONE_WINDOW_MS, 7 * 24 * 60 * 60 * 1000),
|
|
204
|
-
publicBaseUrl: (env.SKILLS_PUBLIC_BASE_URL ||
|
|
204
|
+
publicBaseUrl: (env.SKILLS_PUBLIC_BASE_URL || "").replace(/\/+$/, ""),
|
|
205
205
|
nodeEnv,
|
|
206
206
|
allowEphemeralStore: env.HASNA_SKILLS_ALLOW_EPHEMERAL_STORE === "1"
|
|
207
207
|
};
|
|
208
208
|
}
|
|
209
|
-
function localOrigin(host, port) {
|
|
210
|
-
const hostname = host === "0.0.0.0" || host === "::" ? "localhost" : host;
|
|
211
|
-
return `http://${hostname.includes(":") ? `[${hostname}]` : hostname}:${port}`;
|
|
212
|
-
}
|
|
213
209
|
function parsePositiveInt(value, fallback) {
|
|
214
210
|
const parsed = Number.parseInt(value ?? "", 10);
|
|
215
211
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
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.3",
|
|
24126
24126
|
description: "Skills library for AI coding agents",
|
|
24127
24127
|
type: "module",
|
|
24128
24128
|
bin: {
|
|
@@ -42903,15 +42903,11 @@ function resolveServerConfig(env = process.env) {
|
|
|
42903
42903
|
requestBodyLimitBytes: parsePositiveInt(env.HASNA_SKILLS_REQUEST_BODY_LIMIT_BYTES, 1e6),
|
|
42904
42904
|
skillBundleLimitBytes: parsePositiveInt(env.HASNA_SKILLS_BUNDLE_LIMIT_BYTES, 25000000),
|
|
42905
42905
|
tombstoneWindowMs: parsePositiveInt(env.HASNA_SKILLS_TOMBSTONE_WINDOW_MS, 7 * 24 * 60 * 60 * 1000),
|
|
42906
|
-
publicBaseUrl: (env.SKILLS_PUBLIC_BASE_URL ||
|
|
42906
|
+
publicBaseUrl: (env.SKILLS_PUBLIC_BASE_URL || "").replace(/\/+$/, ""),
|
|
42907
42907
|
nodeEnv,
|
|
42908
42908
|
allowEphemeralStore: env.HASNA_SKILLS_ALLOW_EPHEMERAL_STORE === "1"
|
|
42909
42909
|
};
|
|
42910
42910
|
}
|
|
42911
|
-
function localOrigin(host, port) {
|
|
42912
|
-
const hostname = host === "0.0.0.0" || host === "::" ? "localhost" : host;
|
|
42913
|
-
return `http://${hostname.includes(":") ? `[${hostname}]` : hostname}:${port}`;
|
|
42914
|
-
}
|
|
42915
42911
|
function parsePositiveInt(value, fallback) {
|
|
42916
42912
|
const parsed = Number.parseInt(value ?? "", 10);
|
|
42917
42913
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
@@ -44150,6 +44146,9 @@ async function createSkillsFetchHandler(options = {}) {
|
|
|
44150
44146
|
const config = { ...resolveServerConfig(), ...options.config };
|
|
44151
44147
|
if (!options.store)
|
|
44152
44148
|
assertDurableTarget(resolveDatabaseTarget(config.databaseUrl), config);
|
|
44149
|
+
if (!config.publicBaseUrl) {
|
|
44150
|
+
throw new Error("refusing to start: set SKILLS_PUBLIC_BASE_URL to this Skills server's public API origin; a bound socket is not an authority for client receipts.");
|
|
44151
|
+
}
|
|
44153
44152
|
const store = options.store ?? await createStore({
|
|
44154
44153
|
databaseUrl: config.databaseUrl,
|
|
44155
44154
|
bootstrapApiKey: config.bootstrapApiKey
|
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.3",
|
|
24129
24129
|
description: "Skills library for AI coding agents",
|
|
24130
24130
|
type: "module",
|
|
24131
24131
|
bin: {
|
|
@@ -36401,15 +36401,11 @@ function resolveServerConfig(env = process.env) {
|
|
|
36401
36401
|
requestBodyLimitBytes: parsePositiveInt(env.HASNA_SKILLS_REQUEST_BODY_LIMIT_BYTES, 1e6),
|
|
36402
36402
|
skillBundleLimitBytes: parsePositiveInt(env.HASNA_SKILLS_BUNDLE_LIMIT_BYTES, 25000000),
|
|
36403
36403
|
tombstoneWindowMs: parsePositiveInt(env.HASNA_SKILLS_TOMBSTONE_WINDOW_MS, 7 * 24 * 60 * 60 * 1000),
|
|
36404
|
-
publicBaseUrl: (env.SKILLS_PUBLIC_BASE_URL ||
|
|
36404
|
+
publicBaseUrl: (env.SKILLS_PUBLIC_BASE_URL || "").replace(/\/+$/, ""),
|
|
36405
36405
|
nodeEnv,
|
|
36406
36406
|
allowEphemeralStore: env.HASNA_SKILLS_ALLOW_EPHEMERAL_STORE === "1"
|
|
36407
36407
|
};
|
|
36408
36408
|
}
|
|
36409
|
-
function localOrigin(host, port) {
|
|
36410
|
-
const hostname = host === "0.0.0.0" || host === "::" ? "localhost" : host;
|
|
36411
|
-
return `http://${hostname.includes(":") ? `[${hostname}]` : hostname}:${port}`;
|
|
36412
|
-
}
|
|
36413
36409
|
function parsePositiveInt(value, fallback) {
|
|
36414
36410
|
const parsed = Number.parseInt(value ?? "", 10);
|
|
36415
36411
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|