@skillctl/core 0.2.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 skillctl contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Adapter registration helpers (minimal for PR6).
3
+ * Core defines the surface; concrete adapters live in @skillctl/adapters.
4
+ * This allows future plugin registration without core depending on concrete pkgs.
5
+ */
6
+ import type { AgentAdapter, SkillctlConfig } from './types.js';
7
+ export declare function registerAdapter(adapter: AgentAdapter): void;
8
+ export declare function getRegisteredAdapters(): AgentAdapter[];
9
+ /**
10
+ * Filter by enabled in config.
11
+ */
12
+ export declare function getEnabledRegisteredAdapters(config: SkillctlConfig): AgentAdapter[];
13
+ //# sourceMappingURL=adapters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAI/D,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAK3D;AAED,wBAAgB,qBAAqB,IAAI,YAAY,EAAE,CAEtD;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,cAAc,GAAG,YAAY,EAAE,CAEnF"}
@@ -0,0 +1,21 @@
1
+ const registeredAdapters = [];
2
+ export function registerAdapter(adapter) {
3
+ // last wins on id conflict (simple)
4
+ const idx = registeredAdapters.findIndex((a) => a.id === adapter.id);
5
+ if (idx >= 0)
6
+ registeredAdapters[idx] = adapter;
7
+ else
8
+ registeredAdapters.push(adapter);
9
+ }
10
+ export function getRegisteredAdapters() {
11
+ return [...registeredAdapters];
12
+ }
13
+ /**
14
+ * Filter by enabled in config.
15
+ */
16
+ export function getEnabledRegisteredAdapters(config) {
17
+ return registeredAdapters.filter((a) => config.agents?.[a.id] !== false);
18
+ }
19
+ // Note: the concrete first adapters (claude etc) auto-register on import of @skillctl/adapters
20
+ // See adapters package for wiring + getEnabledAdapters (which may use this or its own).
21
+ //# sourceMappingURL=adapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.js","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAOA,MAAM,kBAAkB,GAAmB,EAAE,CAAC;AAE9C,MAAM,UAAU,eAAe,CAAC,OAAqB;IACnD,oCAAoC;IACpC,MAAM,GAAG,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,CAAC,CAAC;IACrE,IAAI,GAAG,IAAI,CAAC;QAAE,kBAAkB,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;;QAC3C,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,OAAO,CAAC,GAAG,kBAAkB,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,4BAA4B,CAAC,MAAsB;IACjE,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC;AAC3E,CAAC;AAED,+FAA+F;AAC/F,wFAAwF"}
@@ -0,0 +1,34 @@
1
+ export declare function getCacheDir(): string;
2
+ export declare function getDownloadCacheDir(): string;
3
+ export declare function ensureCacheDir(sub?: string): Promise<string>;
4
+ export declare function cacheKeyFromIntegrity(integrity: string): string;
5
+ /**
6
+ * Return path to cached extracted skill dir for this integrity, or null.
7
+ */
8
+ export declare function getCachedSkill(integrity: string): Promise<string | null>;
9
+ /**
10
+ * Store a materialized skill dir into content-addressable cache.
11
+ * Returns the cache path. Idempotent.
12
+ */
13
+ export declare function putCachedSkill(integrity: string, srcDir: string): Promise<string>;
14
+ /**
15
+ * Cache a raw download (e.g. tarball) by a content or source key (sha1/shasum or hash of url).
16
+ * key should be stable short hex.
17
+ */
18
+ export declare function getCachedDownload(key: string): Promise<string | null>;
19
+ export declare function putCachedDownload(key: string, srcFile: string): Promise<string>;
20
+ /**
21
+ * Optional cleanup (for doctor --gc or manual).
22
+ */
23
+ export declare function clearCache(olderThanMs?: number): Promise<number>;
24
+ /**
25
+ * Fast-path stat signature check against a previous signature.
26
+ * Returns true if mtime/size/count match (no content drift likely).
27
+ * Caller can still do full integrity if needed for security.
28
+ */
29
+ export declare function matchesStatSignature(dir: string, prev: {
30
+ mtime: number;
31
+ size: number;
32
+ count: number;
33
+ }): Promise<boolean>;
34
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAmBA,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAsB,cAAc,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIlE;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAG/D;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAc9E;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAWvF;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQ3E;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKrF;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBtE;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAO9H"}
package/dist/cache.js ADDED
@@ -0,0 +1,134 @@
1
+ import { cp, mkdir, stat, readdir, rm } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { getDirStatSignature } from './fs.js';
5
+ /**
6
+ * Content-addressable cache for performance (PR12).
7
+ * Located at ~/.skillctl/cache/
8
+ * Keyed by integrity (sha256:<hex> -> strip prefix for dir name under cache/).
9
+ * Used to skip re-extract / reuse identical skill trees across adds/installs.
10
+ * Also supports raw download cache for tarballs keyed by their known hash/shasum.
11
+ *
12
+ * Integrates with registry fetch/materialize and fs integrity.
13
+ * Fast-path stat checks for drift before full re-hash.
14
+ */
15
+ const CACHE_ROOT = join(homedir(), '.skillctl', 'cache');
16
+ const DOWNLOAD_CACHE = join(CACHE_ROOT, 'downloads'); // for raw tarballs etc.
17
+ export function getCacheDir() {
18
+ return CACHE_ROOT;
19
+ }
20
+ export function getDownloadCacheDir() {
21
+ return DOWNLOAD_CACHE;
22
+ }
23
+ export async function ensureCacheDir(sub) {
24
+ const d = sub ? join(CACHE_ROOT, sub) : CACHE_ROOT;
25
+ await mkdir(d, { recursive: true });
26
+ return d;
27
+ }
28
+ export function cacheKeyFromIntegrity(integrity) {
29
+ // e.g. 'sha256:abc123...' -> 'abc123...'
30
+ return integrity.replace(/^sha256:/i, '').toLowerCase();
31
+ }
32
+ /**
33
+ * Return path to cached extracted skill dir for this integrity, or null.
34
+ */
35
+ export async function getCachedSkill(integrity) {
36
+ const key = cacheKeyFromIntegrity(integrity);
37
+ const p = join(CACHE_ROOT, key);
38
+ try {
39
+ const st = await stat(p);
40
+ if (st.isDirectory()) {
41
+ // quick sanity: has something
42
+ const ents = await readdir(p);
43
+ if (ents.length > 0)
44
+ return p;
45
+ }
46
+ }
47
+ catch {
48
+ // not present
49
+ }
50
+ return null;
51
+ }
52
+ /**
53
+ * Store a materialized skill dir into content-addressable cache.
54
+ * Returns the cache path. Idempotent.
55
+ */
56
+ export async function putCachedSkill(integrity, srcDir) {
57
+ const key = cacheKeyFromIntegrity(integrity);
58
+ const dest = join(CACHE_ROOT, key);
59
+ await ensureCacheDir();
60
+ // if already there, optionally validate but for perf just return
61
+ try {
62
+ const st = await stat(dest);
63
+ if (st.isDirectory())
64
+ return dest;
65
+ }
66
+ catch { }
67
+ await cp(srcDir, dest, { recursive: true, force: true });
68
+ return dest;
69
+ }
70
+ /**
71
+ * Cache a raw download (e.g. tarball) by a content or source key (sha1/shasum or hash of url).
72
+ * key should be stable short hex.
73
+ */
74
+ export async function getCachedDownload(key) {
75
+ await ensureCacheDir('downloads');
76
+ const p = join(DOWNLOAD_CACHE, key);
77
+ try {
78
+ const st = await stat(p);
79
+ if (st.isFile())
80
+ return p;
81
+ }
82
+ catch { }
83
+ return null;
84
+ }
85
+ export async function putCachedDownload(key, srcFile) {
86
+ await ensureCacheDir('downloads');
87
+ const dest = join(DOWNLOAD_CACHE, key);
88
+ await cp(srcFile, dest, { force: true });
89
+ return dest;
90
+ }
91
+ /**
92
+ * Optional cleanup (for doctor --gc or manual).
93
+ */
94
+ export async function clearCache(olderThanMs) {
95
+ let removed = 0;
96
+ try {
97
+ const root = await readdir(CACHE_ROOT, { withFileTypes: true }).catch(() => []);
98
+ for (const e of root) {
99
+ const p = join(CACHE_ROOT, e.name);
100
+ if (olderThanMs) {
101
+ try {
102
+ const s = await stat(p);
103
+ if (Date.now() - s.mtimeMs > olderThanMs) {
104
+ await rm(p, { recursive: true, force: true });
105
+ removed++;
106
+ }
107
+ }
108
+ catch { }
109
+ }
110
+ else {
111
+ await rm(p, { recursive: true, force: true }).catch(() => { });
112
+ removed++;
113
+ }
114
+ }
115
+ }
116
+ catch { }
117
+ return removed;
118
+ }
119
+ /**
120
+ * Fast-path stat signature check against a previous signature.
121
+ * Returns true if mtime/size/count match (no content drift likely).
122
+ * Caller can still do full integrity if needed for security.
123
+ */
124
+ export async function matchesStatSignature(dir, prev) {
125
+ try {
126
+ const cur = await getDirStatSignature(dir); // reuse from fs, but to avoid cycle import here redef minimal? wait we'll import in use or duplicate helper
127
+ return cur.mtime === prev.mtime && cur.size === prev.size && cur.count === prev.count;
128
+ }
129
+ catch {
130
+ return false;
131
+ }
132
+ }
133
+ // Note: getDirStatSignature is in fs.ts; to avoid circular, we will call from places after or re-export.
134
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { SkillctlConfig } from './types.js';
2
+ export declare function loadConfig(customPath?: string): Promise<SkillctlConfig>;
3
+ export declare function saveConfig(config: SkillctlConfig, customPath?: string): Promise<void>;
4
+ export declare function getDefaultConfig(): SkillctlConfig;
5
+ export declare function getConfigPath(): string;
6
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAqBjD,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAiB7E;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM3F;AAED,wBAAgB,gBAAgB,IAAI,cAAc,CAEjD;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
package/dist/config.js ADDED
@@ -0,0 +1,53 @@
1
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ const DEFAULT_STORE = join(homedir(), '.skillctl', 'skills');
5
+ const CONFIG_DIR = join(homedir(), '.skillctl');
6
+ const CONFIG_PATH = join(CONFIG_DIR, 'config.json');
7
+ const DEFAULT_CONFIG = {
8
+ version: 1,
9
+ store: DEFAULT_STORE,
10
+ defaultMode: 'symlink',
11
+ agents: {
12
+ 'claude-code': true,
13
+ cursor: true,
14
+ opencode: true,
15
+ codex: true,
16
+ 'gemini-cli': true,
17
+ },
18
+ registries: [],
19
+ trustedSources: ['github:vercel-labs/*', 'skills.sh/*'],
20
+ };
21
+ export async function loadConfig(customPath) {
22
+ const path = customPath || CONFIG_PATH;
23
+ try {
24
+ const raw = await readFile(path, 'utf8');
25
+ const parsed = JSON.parse(raw);
26
+ // merge with defaults for forward compat / missing keys
27
+ return {
28
+ ...DEFAULT_CONFIG,
29
+ ...parsed,
30
+ agents: { ...DEFAULT_CONFIG.agents, ...parsed.agents },
31
+ trustedSources: parsed.trustedSources ?? DEFAULT_CONFIG.trustedSources,
32
+ registries: parsed.registries ?? DEFAULT_CONFIG.registries,
33
+ };
34
+ }
35
+ catch {
36
+ // missing or invalid -> return defaults (do not auto-write unless save called)
37
+ return { ...DEFAULT_CONFIG };
38
+ }
39
+ }
40
+ export async function saveConfig(config, customPath) {
41
+ const path = customPath || CONFIG_PATH;
42
+ await mkdir(CONFIG_DIR, { recursive: true });
43
+ // simple write; PR2 would have atomic + proper-lockfile
44
+ const data = JSON.stringify(config, null, 2);
45
+ await writeFile(path, data + '\n', 'utf8');
46
+ }
47
+ export function getDefaultConfig() {
48
+ return { ...DEFAULT_CONFIG };
49
+ }
50
+ export function getConfigPath() {
51
+ return CONFIG_PATH;
52
+ }
53
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;AAC7D,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AAChD,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEpD,MAAM,cAAc,GAAmB;IACrC,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,aAAa;IACpB,WAAW,EAAE,SAAS;IACtB,MAAM,EAAE;QACN,aAAa,EAAE,IAAI;QACnB,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,IAAI;QACX,YAAY,EAAE,IAAI;KACnB;IACD,UAAU,EAAE,EAAE;IACd,cAAc,EAAE,CAAC,sBAAsB,EAAE,aAAa,CAAC;CACxD,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAAmB;IAClD,MAAM,IAAI,GAAG,UAAU,IAAI,WAAW,CAAC;IACvC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QAC1D,wDAAwD;QACxD,OAAO;YACL,GAAG,cAAc;YACjB,GAAG,MAAM;YACT,MAAM,EAAE,EAAE,GAAG,cAAc,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE;YACtD,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,cAAc,CAAC,cAAc;YACtE,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,cAAc,CAAC,UAAU;SACzC,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,+EAA+E;QAC/E,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;IAC/B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAsB,EAAE,UAAmB;IAC1E,MAAM,IAAI,GAAG,UAAU,IAAI,WAAW,CAAC;IACvC,MAAM,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,wDAAwD;IACxD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7C,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,WAAW,CAAC;AACrB,CAAC"}
package/dist/fs.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ import type { LinkMode } from './types.js';
2
+ /**
3
+ * FS utilities (PR2 scope): atomic writes, dir hashing (fast mtime+size path + full sha),
4
+ * cross platform notes. Minimal impl; expanded in later PRs (proper-lockfile etc).
5
+ */
6
+ export declare function ensureDir(dir: string): Promise<void>;
7
+ /**
8
+ * Atomic write: write to temp then rename.
9
+ */
10
+ export declare function writeFileAtomic(filePath: string, data: string | Buffer): Promise<void>;
11
+ /**
12
+ * Compute sha256 of a file (for single files).
13
+ */
14
+ export declare function computeFileHash(filePath: string): Promise<string>;
15
+ /**
16
+ * Recursive dir hash for skill canonical dir.
17
+ * PR12 Performance: callers use getDirStatSignature + stored integrity (from lock/cache)
18
+ * to fast-skip full re-hash when mtime/size/count unchanged (drift detection).
19
+ * Only re-compute SHA on add --force or detected change. Cache uses integrity key.
20
+ * Always full content hash for correctness of integrity value.
21
+ */
22
+ export declare function computeDirIntegrity(dir: string): Promise<string>;
23
+ /**
24
+ * Fast stat based check (used for drift detection in doctor etc).
25
+ */
26
+ export declare function getDirStatSignature(dir: string): Promise<{
27
+ mtime: number;
28
+ size: number;
29
+ count: number;
30
+ }>;
31
+ export declare function getDefaultLinkMode(): LinkMode;
32
+ //# sourceMappingURL=fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;GAGG;AAEH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK5F;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIvE;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBtE;AAeD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAU9G;AAkBD,wBAAgB,kBAAkB,IAAI,QAAQ,CAE7C"}
package/dist/fs.js ADDED
@@ -0,0 +1,104 @@
1
+ import { readFile, writeFile, mkdir, rename, stat, readdir } from 'node:fs/promises';
2
+ import { join, dirname } from 'node:path';
3
+ import { createHash } from 'node:crypto';
4
+ /**
5
+ * FS utilities (PR2 scope): atomic writes, dir hashing (fast mtime+size path + full sha),
6
+ * cross platform notes. Minimal impl; expanded in later PRs (proper-lockfile etc).
7
+ */
8
+ export async function ensureDir(dir) {
9
+ await mkdir(dir, { recursive: true });
10
+ }
11
+ /**
12
+ * Atomic write: write to temp then rename.
13
+ */
14
+ export async function writeFileAtomic(filePath, data) {
15
+ await ensureDir(dirname(filePath));
16
+ const tmp = `${filePath}.tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`;
17
+ await writeFile(tmp, data);
18
+ await rename(tmp, filePath);
19
+ }
20
+ /**
21
+ * Compute sha256 of a file (for single files).
22
+ */
23
+ export async function computeFileHash(filePath) {
24
+ const buf = await readFile(filePath);
25
+ const hash = createHash('sha256').update(buf).digest('hex');
26
+ return `sha256:${hash}`;
27
+ }
28
+ /**
29
+ * Recursive dir hash for skill canonical dir.
30
+ * PR12 Performance: callers use getDirStatSignature + stored integrity (from lock/cache)
31
+ * to fast-skip full re-hash when mtime/size/count unchanged (drift detection).
32
+ * Only re-compute SHA on add --force or detected change. Cache uses integrity key.
33
+ * Always full content hash for correctness of integrity value.
34
+ */
35
+ export async function computeDirIntegrity(dir) {
36
+ const files = [];
37
+ await walk(dir, files);
38
+ files.sort(); // deterministic
39
+ const hash = createHash('sha256');
40
+ for (const f of files) {
41
+ try {
42
+ const buf = await readFile(f);
43
+ hash.update(f.slice(dir.length)); // relative
44
+ hash.update('\0');
45
+ hash.update(buf);
46
+ hash.update('\0');
47
+ }
48
+ catch {
49
+ // ignore unreadable
50
+ }
51
+ }
52
+ return `sha256:${hash.digest('hex')}`;
53
+ }
54
+ async function walk(dir, out) {
55
+ const entries = await readdir(dir, { withFileTypes: true });
56
+ for (const e of entries) {
57
+ if (e.name === '.git' || e.name === 'node_modules')
58
+ continue;
59
+ const p = join(dir, e.name);
60
+ if (e.isDirectory()) {
61
+ await walk(p, out);
62
+ }
63
+ else if (e.isFile()) {
64
+ out.push(p);
65
+ }
66
+ }
67
+ }
68
+ /**
69
+ * Fast stat based check (used for drift detection in doctor etc).
70
+ */
71
+ export async function getDirStatSignature(dir) {
72
+ let size = 0;
73
+ let count = 0;
74
+ let latestMtime = 0;
75
+ await walkStats(dir, (s) => {
76
+ size += s.size;
77
+ count += 1;
78
+ if (s.mtimeMs > latestMtime)
79
+ latestMtime = s.mtimeMs;
80
+ });
81
+ return { mtime: latestMtime, size, count };
82
+ }
83
+ async function walkStats(dir, cb) {
84
+ const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
85
+ for (const e of entries) {
86
+ if (e.name === '.git')
87
+ continue;
88
+ const p = join(dir, e.name);
89
+ try {
90
+ const s = await stat(p);
91
+ if (s.isDirectory()) {
92
+ await walkStats(p, cb);
93
+ }
94
+ else if (s.isFile()) {
95
+ cb({ size: s.size, mtimeMs: s.mtimeMs });
96
+ }
97
+ }
98
+ catch { }
99
+ }
100
+ }
101
+ export function getDefaultLinkMode() {
102
+ return process.platform === 'win32' ? 'junction' : 'symlink';
103
+ }
104
+ //# sourceMappingURL=fs.js.map
package/dist/fs.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export * from './types.js';
2
+ export * from './config.js';
3
+ export * from './fs.js';
4
+ export * from './adapters.js';
5
+ export * from './cache.js';
6
+ export * from './names.js';
7
+ export * from './paths.js';
8
+ export * from './operations.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export * from './types.js';
2
+ export * from './config.js';
3
+ export * from './fs.js';
4
+ export * from './adapters.js';
5
+ export * from './cache.js';
6
+ export * from './names.js';
7
+ export * from './paths.js';
8
+ export * from './operations.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Canonical skill name: lowercase, hyphen normalized (per design). */
2
+ export declare function canonicalizeName(raw: string): string;
3
+ //# sourceMappingURL=names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../src/names.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpD"}
package/dist/names.js ADDED
@@ -0,0 +1,5 @@
1
+ /** Canonical skill name: lowercase, hyphen normalized (per design). */
2
+ export function canonicalizeName(raw) {
3
+ return raw.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
4
+ }
5
+ //# sourceMappingURL=names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.js","sourceRoot":"","sources":["../src/names.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { LockfileEntry, SkillLockfile } from './types.js';
2
+ export interface InstallResult {
3
+ installed: number;
4
+ skipped: number;
5
+ lock: SkillLockfile;
6
+ }
7
+ export declare function needsInstall(entry: LockfileEntry): Promise<boolean>;
8
+ export declare function verifyLockIntegrity(lock: SkillLockfile): Promise<string[]>;
9
+ export declare function lockToSkillTargets(lock: SkillLockfile): Array<{
10
+ name: string;
11
+ canonicalPath: string;
12
+ }>;
13
+ export declare function purgeCanonical(name: string): Promise<void>;
14
+ //# sourceMappingURL=operations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../src/operations.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAI/D,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,aAAa,CAAC;CACrB;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAYzE;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAchF;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CAEtG;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhE"}
@@ -0,0 +1,46 @@
1
+ import { stat } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { rm } from 'node:fs/promises';
5
+ import { computeDirIntegrity } from './fs.js';
6
+ import { canonicalizeName } from './names.js';
7
+ export async function needsInstall(entry) {
8
+ try {
9
+ await stat(entry.canonicalPath);
10
+ }
11
+ catch {
12
+ return true;
13
+ }
14
+ try {
15
+ const integrity = await computeDirIntegrity(entry.canonicalPath);
16
+ return integrity !== entry.integrity;
17
+ }
18
+ catch {
19
+ return true;
20
+ }
21
+ }
22
+ export async function verifyLockIntegrity(lock) {
23
+ const errors = [];
24
+ for (const [name, entry] of Object.entries(lock.skills)) {
25
+ try {
26
+ await stat(entry.canonicalPath);
27
+ const integrity = await computeDirIntegrity(entry.canonicalPath);
28
+ if (integrity !== entry.integrity) {
29
+ errors.push(`${name}: integrity mismatch (expected ${entry.integrity.slice(0, 20)}...)`);
30
+ }
31
+ }
32
+ catch {
33
+ errors.push(`${name}: canonical path missing (${entry.canonicalPath})`);
34
+ }
35
+ }
36
+ return errors;
37
+ }
38
+ export function lockToSkillTargets(lock) {
39
+ return Object.values(lock.skills).map((e) => ({ name: e.name, canonicalPath: e.canonicalPath }));
40
+ }
41
+ export async function purgeCanonical(name) {
42
+ const canonicalName = canonicalizeName(name);
43
+ const p = join(homedir(), '.skillctl', 'skills', canonicalName);
44
+ await rm(p, { recursive: true, force: true }).catch(() => { });
45
+ }
46
+ //# sourceMappingURL=operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.js","sourceRoot":"","sources":["../src/operations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAEtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAQ9C,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAoB;IACrD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACjE,OAAO,SAAS,KAAK,KAAK,CAAC,SAAS,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAmB;IAC3D,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACxD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;YAChC,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;YACjE,IAAI,SAAS,KAAK,KAAK,CAAC,SAAS,EAAE,CAAC;gBAClC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,kCAAkC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;YAC3F,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,6BAA6B,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAmB;IACpD,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;AACnG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY;IAC/C,MAAM,aAAa,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;IAChE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Resolve adapter target base + skill name (project-relative or absolute global). */
2
+ export declare function resolveAdapterTarget(basePath: string, skillName: string, cwd?: string): string;
3
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAEA,sFAAsF;AACtF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,SAAgB,GAAG,MAAM,CAGrG"}
package/dist/paths.js ADDED
@@ -0,0 +1,7 @@
1
+ import { join } from 'node:path';
2
+ /** Resolve adapter target base + skill name (project-relative or absolute global). */
3
+ export function resolveAdapterTarget(basePath, skillName, cwd = process.cwd()) {
4
+ const resolvedBase = basePath.startsWith('.') ? join(cwd, basePath) : basePath;
5
+ return join(resolvedBase, skillName);
6
+ }
7
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,sFAAsF;AACtF,MAAM,UAAU,oBAAoB,CAAC,QAAgB,EAAE,SAAiB,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAC3F,MAAM,YAAY,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC/E,OAAO,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Core types for skillctl.
3
+ * Defines shared interfaces used across manifest, lockfile, registry, etc.
4
+ * Follows patterns from design doc (PR2).
5
+ */
6
+ export interface SkillRef {
7
+ name: string;
8
+ version?: string;
9
+ source: string;
10
+ }
11
+ export interface ResolvedSkill {
12
+ name: string;
13
+ canonicalPath: string;
14
+ integrity: string;
15
+ resolvedSource: string;
16
+ fetchedAt: string;
17
+ provenance?: Provenance;
18
+ }
19
+ export interface Provenance {
20
+ type: 'github' | 'npm' | 'local' | 'skills.sh' | 'other';
21
+ commit?: string;
22
+ tarballHash?: string;
23
+ subpath?: string;
24
+ migratedFrom?: 'npx' | 'python-skillctl';
25
+ originalHash?: string;
26
+ originalSource?: string;
27
+ signature?: string;
28
+ }
29
+ export interface AgentAdapter {
30
+ readonly id: string;
31
+ readonly name: string;
32
+ readonly projectPaths: string[];
33
+ readonly globalPaths: string[];
34
+ detect(): Promise<boolean>;
35
+ ensureTarget(skillName: string, targetPath: string, canonical: string, mode: 'symlink' | 'copy' | 'junction'): Promise<void>;
36
+ removeTarget(skillName: string, targetPath: string): Promise<void>;
37
+ }
38
+ export interface RegistrySource {
39
+ readonly id: string;
40
+ match(spec: string): boolean;
41
+ resolve(spec: string, options?: {
42
+ ref?: string;
43
+ }): Promise<ResolvedSource>;
44
+ fetch(resolved: ResolvedSource, dest: string): Promise<{
45
+ integrity: string;
46
+ }>;
47
+ }
48
+ export interface ResolvedSource {
49
+ name: string;
50
+ resolved: string;
51
+ sourceType: 'github' | 'npm' | 'local' | 'skills.sh';
52
+ sourceId: string;
53
+ originalSpec: string;
54
+ tarballUrl?: string;
55
+ tarballHash?: string;
56
+ gitUrl?: string;
57
+ ref?: string;
58
+ subpath?: string;
59
+ localPath?: string;
60
+ }
61
+ export interface SkillManifest {
62
+ name?: string;
63
+ version?: string;
64
+ agentSkills?: {
65
+ dependencies?: Record<string, string>;
66
+ devDependencies?: Record<string, string>;
67
+ };
68
+ }
69
+ export interface LockfileEntry {
70
+ specifier: string;
71
+ resolved: string;
72
+ integrity: string;
73
+ name: string;
74
+ canonicalPath: string;
75
+ fetchedAt: string;
76
+ provenance: Provenance;
77
+ }
78
+ export interface SkillLockfile {
79
+ lockfileVersion: '1.0';
80
+ agents?: string[];
81
+ skills: Record<string, LockfileEntry>;
82
+ metadata?: {
83
+ migratedAt?: string;
84
+ toolVersion?: string;
85
+ };
86
+ }
87
+ export interface SkillctlConfig {
88
+ version: 1;
89
+ store: string;
90
+ defaultMode: 'symlink' | 'copy' | 'junction';
91
+ agents: Record<string, boolean>;
92
+ registries?: string[];
93
+ trustedSources?: string[];
94
+ experimental?: {
95
+ plugins?: boolean;
96
+ };
97
+ plugins?: Array<{
98
+ name: string;
99
+ path: string;
100
+ enabled: boolean;
101
+ }>;
102
+ }
103
+ export type LinkMode = 'symlink' | 'copy' | 'junction';
104
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Core types for skillctl.
3
+ * Defines shared interfaces used across manifest, lockfile, registry, etc.
4
+ * Follows patterns from design doc (PR2).
5
+ */
6
+ export {};
7
+ // Collision policy note: within a scope (project/global), names are unique.
8
+ // Project manifest takes precedence over global for resolution in project context.
9
+ // On add conflict: warn, use first-seen or scoped name; record in lock.
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;;AA+GH,4EAA4E;AAC5E,mFAAmF;AACnF,wEAAwE"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@skillctl/core",
3
+ "version": "0.2.0",
4
+ "description": "Core types, config, and FS utilities for skillctl",
5
+ "license": "MIT",
6
+ "author": "skillctl contributors",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "devDependencies": {
14
+ "@types/node": "^20.14.0",
15
+ "rimraf": "^5.0.7",
16
+ "typescript": "^5.6.3"
17
+ },
18
+ "engines": {
19
+ "node": ">=22.13"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/xFurti/skillctl.git",
24
+ "directory": "packages/core"
25
+ },
26
+ "keywords": [
27
+ "core",
28
+ "types",
29
+ "config",
30
+ "agent-skills"
31
+ ],
32
+ "scripts": {
33
+ "build": "tsc -b",
34
+ "dev": "tsc -b --watch",
35
+ "clean": "rimraf dist",
36
+ "lint": "tsc --noEmit",
37
+ "test": "echo \"no tests yet for core\" && exit 0"
38
+ }
39
+ }