@rackerlabs/agent-skills-cli 1.5.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.
@@ -0,0 +1,14 @@
1
+ import path from "path";
2
+ import os from "os";
3
+ const homeDirectory = os.homedir();
4
+ const { env } = process;
5
+ const xdgData = env.XDG_DATA_HOME || (homeDirectory ? path.join(homeDirectory, ".local", "share") : void 0);
6
+ const xdgConfig = env.XDG_CONFIG_HOME || (homeDirectory ? path.join(homeDirectory, ".config") : void 0);
7
+ env.XDG_STATE_HOME || homeDirectory && path.join(homeDirectory, ".local", "state");
8
+ env.XDG_CACHE_HOME || homeDirectory && path.join(homeDirectory, ".cache");
9
+ env.XDG_RUNTIME_DIR;
10
+ const xdgDataDirectories = (env.XDG_DATA_DIRS || "/usr/local/share/:/usr/share/").split(":");
11
+ if (xdgData) xdgDataDirectories.unshift(xdgData);
12
+ const xdgConfigDirectories = (env.XDG_CONFIG_DIRS || "/etc/xdg").split(":");
13
+ if (xdgConfig) xdgConfigDirectories.unshift(xdgConfig);
14
+ export { xdgConfig as t };
@@ -0,0 +1,73 @@
1
+ import { r as __toESM } from "./rolldown-runtime.mjs";
2
+ import { u as require_picocolors } from "./libs/@clack/core.mjs";
3
+ import { n as M } from "./libs/@clack/prompts.mjs";
4
+ import "./libs/@kwsites/file-exists.mjs";
5
+ import "./libs/@kwsites/promise-deferred.mjs";
6
+ import "./libs/@simple-git/argv-parser.mjs";
7
+ import "./libs/simple-git.mjs";
8
+ import "./libs/gray-matter.mjs";
9
+ import "./libs/extend-shallow.mjs";
10
+ import "./libs/esprima.mjs";
11
+ import "./libs/xdg-basedir.mjs";
12
+ import { l as CENTRAL_REGISTRY_REPO, r as getLocalLockPath, t as runAdd } from "../cli.mjs";
13
+ import { n as assertConsumerGroupsShape, r as readManifest, t as ManifestSchemaError } from "./manifest.mjs";
14
+ import { n as resolveRegistryManifest, r as resolveSkillPath, t as resolveGroupSkills } from "./registry.mjs";
15
+ import { access } from "fs/promises";
16
+ var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
17
+ async function runInstallFromManifest(options) {
18
+ if (options.strict) try {
19
+ await access(getLocalLockPath());
20
+ M.error(import_picocolors.default.red("skills-lock.json already exists. In --strict mode this is not allowed — it may have been force-committed by a malicious actor."));
21
+ process.exit(1);
22
+ } catch {}
23
+ let manifest;
24
+ try {
25
+ manifest = await readManifest();
26
+ } catch (error) {
27
+ if (error instanceof ManifestSchemaError) {
28
+ M.error(import_picocolors.default.red(`Invalid skills.json: ${error.message}`));
29
+ process.exit(1);
30
+ }
31
+ throw error;
32
+ }
33
+ const toInstall = [];
34
+ if (manifest.groups) assertConsumerGroupsShape(manifest.groups);
35
+ if (manifest.groups && manifest.groups.include.length > 0) {
36
+ M.step(`Resolving groups from ${import_picocolors.default.cyan(CENTRAL_REGISTRY_REPO)} @ ${import_picocolors.default.yellow(manifest.groups.version)}...`);
37
+ const registryManifest = await resolveRegistryManifest(manifest.groups.version);
38
+ for (const groupName of manifest.groups.include) {
39
+ M.message(` Group: ${import_picocolors.default.cyan(groupName)}`);
40
+ const skills = resolveGroupSkills(registryManifest, groupName);
41
+ toInstall.push(...skills);
42
+ }
43
+ }
44
+ if (manifest.skills && Object.keys(manifest.skills).length > 0) for (const [skillName, ref] of Object.entries(manifest.skills)) {
45
+ M.step(`Resolving skill ${import_picocolors.default.cyan(skillName)} @ ${import_picocolors.default.yellow(ref)}...`);
46
+ const fullPath = resolveSkillPath(await resolveRegistryManifest(ref), skillName);
47
+ toInstall.push({
48
+ source: fullPath,
49
+ ref
50
+ });
51
+ }
52
+ if (toInstall.length === 0) {
53
+ M.info("No skills to install from manifest.");
54
+ return;
55
+ }
56
+ const unique = /* @__PURE__ */ new Map();
57
+ for (const item of toInstall) unique.set(item.source, item.ref || unique.get(item.source) || "latest");
58
+ M.info(`Installing ${unique.size} skills from registry...`);
59
+ for (const [source, ref] of unique.entries()) {
60
+ M.step(`Installing ${import_picocolors.default.cyan(source)} @ ${import_picocolors.default.yellow(ref)}`);
61
+ try {
62
+ await runAdd([source], {
63
+ ...options,
64
+ ref
65
+ });
66
+ } catch (err) {
67
+ M.error(`Failed to install ${source}: ${err instanceof Error ? err.message : err}`);
68
+ if (options.strict) process.exit(1);
69
+ }
70
+ }
71
+ M.success("Manifest installation complete");
72
+ }
73
+ export { runInstallFromManifest };
@@ -0,0 +1,49 @@
1
+ import { join } from "path";
2
+ import { readFile } from "fs/promises";
3
+ const SKILLS_MANIFEST_FILE = "skills.json";
4
+ function getManifestPath(cwd) {
5
+ return join(cwd || process.cwd(), SKILLS_MANIFEST_FILE);
6
+ }
7
+ async function readManifest(cwd) {
8
+ const manifestPath = getManifestPath(cwd);
9
+ try {
10
+ const content = await readFile(manifestPath, "utf-8");
11
+ const parsed = JSON.parse(content);
12
+ validateManifestSchema(parsed);
13
+ return parsed;
14
+ } catch (error) {
15
+ if (error instanceof ManifestSchemaError) throw error;
16
+ return { skills: {} };
17
+ }
18
+ }
19
+ var ManifestSchemaError = class extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = "ManifestSchemaError";
23
+ }
24
+ };
25
+ function assertConsumerGroupsShape(groups) {
26
+ const correctShape = "Expected groups to be { \"version\": string, \"include\": string[] }, e.g. { \"version\": \"latest\", \"include\": [\"baseline\"] }. Inline/registry-style group maps (groupName → { source: ref }) are not valid in a local skills.json.";
27
+ if (typeof groups !== "object" || groups === null || Array.isArray(groups)) throw new ManifestSchemaError(`Invalid groups in skills.json. ${correctShape}`);
28
+ const obj = groups;
29
+ for (const key of Object.keys(obj)) if (key !== "version" && key !== "include") throw new ManifestSchemaError(`Invalid key "${key}" in groups — this looks like a registry/inline group map, not a consumer manifest. ${correctShape}`);
30
+ if (typeof obj.version !== "string" || !obj.version) throw new ManifestSchemaError(`groups.version must be a non-empty string. ${correctShape}`);
31
+ if (!Array.isArray(obj.include) || obj.include.some((item) => typeof item !== "string")) throw new ManifestSchemaError(`groups.include must be an array of strings. ${correctShape}`);
32
+ }
33
+ function validateManifestSchema(parsed) {
34
+ if (typeof parsed !== "object" || parsed === null) throw new ManifestSchemaError("skills.json must be a JSON object");
35
+ const obj = parsed;
36
+ if (obj.groups !== void 0) {
37
+ if (typeof obj.groups !== "object" || obj.groups === null) throw new ManifestSchemaError("groups must be an object with {version, include}");
38
+ const groups = obj.groups;
39
+ for (const [key, value] of Object.entries(groups)) if (key !== "version" && key !== "include") throw new ManifestSchemaError(`Invalid key "${key}" in groups. Groups must only contain "version" and "include". Inline group definitions are no longer supported.`);
40
+ if (typeof groups.version !== "string" || !groups.version) throw new ManifestSchemaError("groups.version is required and must be a non-empty string");
41
+ if (!Array.isArray(groups.include) || groups.include.length === 0) throw new ManifestSchemaError("groups.include is required and must be a non-empty array of strings");
42
+ for (const item of groups.include) if (typeof item !== "string") throw new ManifestSchemaError("groups.include must contain only strings");
43
+ }
44
+ if (obj.skills !== void 0) {
45
+ if (typeof obj.skills !== "object" || obj.skills === null || Array.isArray(obj.skills)) throw new ManifestSchemaError("skills must be an object mapping skill names to version refs");
46
+ for (const [key, value] of Object.entries(obj.skills)) if (typeof value !== "string") throw new ManifestSchemaError(`skills.${key} must be a version string (e.g., "v0.1.0" or "latest")`);
47
+ }
48
+ }
49
+ export { assertConsumerGroupsShape as n, readManifest as r, ManifestSchemaError as t };
@@ -0,0 +1,56 @@
1
+ import { r as __toESM } from "./rolldown-runtime.mjs";
2
+ import { u as require_picocolors } from "./libs/@clack/core.mjs";
3
+ import { n as M } from "./libs/@clack/prompts.mjs";
4
+ import { n as computeSkillFolderHash, u as parseSource } from "../cli.mjs";
5
+ import { r as readManifest } from "./manifest.mjs";
6
+ import { join } from "path";
7
+ import { access, writeFile } from "fs/promises";
8
+ import { createHash } from "crypto";
9
+ var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
10
+ const REGISTRY_HASHES_FILE = "registry-hashes.json";
11
+ async function generateRegistryHashesCommand(cwd) {
12
+ const currentDir = cwd || process.cwd();
13
+ M.step(`Generating registry hashes in ${currentDir}`);
14
+ const manifest = await readManifest(currentDir);
15
+ const hashes = {
16
+ version: 1,
17
+ skills: {},
18
+ groups: {}
19
+ };
20
+ let processedSkills = 0;
21
+ let missingSkills = 0;
22
+ if (manifest.skills) for (const [source, ref] of Object.entries(manifest.skills)) {
23
+ const parsed = parseSource(source);
24
+ let localTargetDir = currentDir;
25
+ if (parsed.subpath) localTargetDir = join(currentDir, parsed.subpath);
26
+ try {
27
+ await access(localTargetDir);
28
+ const hash = await computeSkillFolderHash(localTargetDir);
29
+ hashes.skills[source] = hash;
30
+ processedSkills++;
31
+ M.message(`Hashed ${import_picocolors.default.cyan(source)} -> ${hash.substring(0, 16)}...`);
32
+ } catch (err) {
33
+ missingSkills++;
34
+ M.warn(`Could not access local folder for ${source}. Looked in: ${localTargetDir}`);
35
+ }
36
+ }
37
+ if (manifest.groups) for (const [groupName, groupSkills] of Object.entries(manifest.groups)) {
38
+ const memberHashes = [];
39
+ let groupValid = true;
40
+ for (const source of Object.keys(groupSkills)) if (hashes.skills[source]) memberHashes.push(hashes.skills[source]);
41
+ else {
42
+ M.warn(`Group '${groupName}' references skill '${source}' which has no generated hash. Group hash might be invalid.`);
43
+ groupValid = false;
44
+ }
45
+ memberHashes.sort();
46
+ const groupHash = createHash("sha256");
47
+ for (const h of memberHashes) groupHash.update(`${h.length}:${h}`);
48
+ const finalGroupHash = groupHash.digest("hex");
49
+ hashes.groups[groupName] = finalGroupHash;
50
+ if (groupValid) M.message(`Hashed group ${import_picocolors.default.cyan(groupName)} -> ${finalGroupHash.substring(0, 16)}...`);
51
+ }
52
+ await writeFile(join(currentDir, REGISTRY_HASHES_FILE), JSON.stringify(hashes, null, 2) + "\n", "utf-8");
53
+ M.success(import_picocolors.default.green(`Successfully wrote ${REGISTRY_HASHES_FILE} with ${processedSkills} skill hashes and ${Object.keys(hashes.groups).length} group hashes.`));
54
+ if (missingSkills > 0) M.warn(import_picocolors.default.yellow(`Failed to compute hashes for ${missingSkills} skills.`));
55
+ }
56
+ export { generateRegistryHashesCommand };
@@ -0,0 +1,47 @@
1
+ import { l as CENTRAL_REGISTRY_REPO } from "../cli.mjs";
2
+ import { exec } from "child_process";
3
+ import { promisify } from "util";
4
+ const execAsync = promisify(exec);
5
+ function describeType(value) {
6
+ if (value === null) return "null";
7
+ if (Array.isArray(value)) return "an array";
8
+ return `a ${typeof value}`;
9
+ }
10
+ async function resolveVersionRef(ref) {
11
+ if (ref === "latest") {
12
+ const { stdout } = await execAsync(`gh api repos/${CENTRAL_REGISTRY_REPO}/releases/latest --jq .tag_name`);
13
+ const tag = stdout.trim();
14
+ if (!tag) throw new Error(`No releases found in ${CENTRAL_REGISTRY_REPO}`);
15
+ return tag;
16
+ }
17
+ return ref;
18
+ }
19
+ async function resolveRegistryManifest(versionRef) {
20
+ const tag = await resolveVersionRef(versionRef);
21
+ const { stdout } = await execAsync(`gh api repos/${CENTRAL_REGISTRY_REPO}/contents/skills.json?ref=${tag} --jq .content`);
22
+ const content = Buffer.from(stdout.trim(), "base64").toString("utf-8");
23
+ const parsed = JSON.parse(content);
24
+ if (!parsed.groups || typeof parsed.groups !== "object" || Array.isArray(parsed.groups)) throw new Error(`Registry skills.json at ${tag} has an invalid or missing groups definition (expected an object mapping group names to skill maps).`);
25
+ return {
26
+ skills: parsed.skills || {},
27
+ groups: parsed.groups
28
+ };
29
+ }
30
+ function resolveGroupSkills(registryManifest, groupName) {
31
+ if (!(groupName in registryManifest.groups)) throw new Error(`Group '${groupName}' not found in ${CENTRAL_REGISTRY_REPO}`);
32
+ const group = registryManifest.groups[groupName];
33
+ if (group === null || typeof group !== "object" || Array.isArray(group)) throw new Error(`Group '${groupName}' in ${CENTRAL_REGISTRY_REPO} has an invalid shape: expected an object mapping skill sources to version refs (e.g. { "owner/repo/skills/foo": "latest" }), but got ${describeType(group)}.`);
34
+ return Object.entries(group).map(([source, ref]) => {
35
+ if (typeof source !== "string" || typeof ref !== "string") throw new Error(`Group '${groupName}' in ${CENTRAL_REGISTRY_REPO} contains a malformed entry: each member must map a skill source string to a version ref string.`);
36
+ return {
37
+ source,
38
+ ref
39
+ };
40
+ });
41
+ }
42
+ function resolveSkillPath(registryManifest, skillName) {
43
+ const fullPath = Object.keys(registryManifest.skills).find((key) => key.endsWith(`/skills/${skillName}`) || key.endsWith(`/${skillName}`));
44
+ if (!fullPath) throw new Error(`Skill '${skillName}' not found in ${CENTRAL_REGISTRY_REPO}`);
45
+ return fullPath;
46
+ }
47
+ export { resolveRegistryManifest as n, resolveSkillPath as r, resolveGroupSkills as t };
@@ -0,0 +1,24 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
24
+ export { __require as n, __toESM as r, __commonJSMin as t };
@@ -0,0 +1,170 @@
1
+ import { r as __toESM } from "./rolldown-runtime.mjs";
2
+ import { u as require_picocolors } from "./libs/@clack/core.mjs";
3
+ import { n as M } from "./libs/@clack/prompts.mjs";
4
+ import "./libs/gray-matter.mjs";
5
+ import "./libs/extend-shallow.mjs";
6
+ import "./libs/esprima.mjs";
7
+ import "./libs/xdg-basedir.mjs";
8
+ import { a as getAgentBaseDir, c as detectInstalledAgents, i as readLocalLock, n as computeSkillFolderHash, o as getCanonicalSkillsDir, s as agents } from "../cli.mjs";
9
+ import { n as assertConsumerGroupsShape, r as readManifest, t as ManifestSchemaError } from "./manifest.mjs";
10
+ import { n as resolveRegistryManifest, r as resolveSkillPath, t as resolveGroupSkills } from "./registry.mjs";
11
+ import { join } from "path";
12
+ import { homedir } from "os";
13
+ import { access, readdir } from "fs/promises";
14
+ var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
15
+ async function runVerifyCommand(args) {
16
+ const subcommand = args[0];
17
+ if (subcommand === "structure") await runVerifyStructureCommand();
18
+ else if (subcommand === "integrity") await runVerifyIntegrityCommand();
19
+ else if (subcommand === "empty-global-skills") await runVerifyEmptyGlobalSkillsCommand();
20
+ else {
21
+ M.error(`Unknown verify subcommand: ${subcommand}. Use 'structure', 'integrity', or 'empty-global-skills'.`);
22
+ process.exit(1);
23
+ }
24
+ }
25
+ async function runVerifyStructureCommand() {
26
+ let manifest;
27
+ try {
28
+ manifest = await readManifest();
29
+ } catch (error) {
30
+ if (error instanceof ManifestSchemaError) {
31
+ M.error(import_picocolors.default.red(`Invalid skills.json: ${error.message}`));
32
+ process.exit(1);
33
+ }
34
+ throw error;
35
+ }
36
+ let failures = 0;
37
+ let total = 0;
38
+ if (manifest.groups) try {
39
+ assertConsumerGroupsShape(manifest.groups);
40
+ } catch (error) {
41
+ if (error instanceof ManifestSchemaError) {
42
+ M.error(import_picocolors.default.red(`Invalid skills.json: ${error.message}`));
43
+ process.exit(1);
44
+ }
45
+ throw error;
46
+ }
47
+ if (manifest.groups && manifest.groups.include.length > 0) {
48
+ M.step(`Verifying groups from registry @ ${import_picocolors.default.yellow(manifest.groups.version)}...`);
49
+ let registryManifest;
50
+ try {
51
+ registryManifest = await resolveRegistryManifest(manifest.groups.version);
52
+ } catch (error) {
53
+ M.error(import_picocolors.default.red(`Failed to fetch registry at ref "${manifest.groups.version}": ${error instanceof Error ? error.message : error}`));
54
+ process.exit(1);
55
+ }
56
+ for (const groupName of manifest.groups.include) {
57
+ total++;
58
+ try {
59
+ const skills = resolveGroupSkills(registryManifest, groupName);
60
+ M.message(` ${import_picocolors.default.green("✓")} Group ${import_picocolors.default.cyan(groupName)}: ${skills.length} skills`);
61
+ } catch (error) {
62
+ M.error(` ${import_picocolors.default.red("✗")} Group ${import_picocolors.default.cyan(groupName)}: ${error instanceof Error ? error.message : error}`);
63
+ failures++;
64
+ }
65
+ }
66
+ }
67
+ if (manifest.skills) for (const [skillName, ref] of Object.entries(manifest.skills)) {
68
+ total++;
69
+ try {
70
+ resolveSkillPath(await resolveRegistryManifest(ref), skillName);
71
+ M.message(` ${import_picocolors.default.green("✓")} Skill ${import_picocolors.default.cyan(skillName)} @ ${import_picocolors.default.yellow(ref)}`);
72
+ } catch (error) {
73
+ M.error(` ${import_picocolors.default.red("✗")} Skill ${import_picocolors.default.cyan(skillName)} @ ${import_picocolors.default.yellow(ref)}: ${error instanceof Error ? error.message : error}`);
74
+ failures++;
75
+ }
76
+ }
77
+ if (total === 0) {
78
+ M.info("No skills or groups found in skills.json to verify.");
79
+ return;
80
+ }
81
+ if (failures > 0) {
82
+ M.error(import_picocolors.default.red(`Verification failed: ${failures} out of ${total} entries could not be resolved.`));
83
+ process.exit(1);
84
+ } else M.success(import_picocolors.default.green(`Verification complete. All ${total} entries resolved successfully.`));
85
+ }
86
+ async function runVerifyIntegrityCommand() {
87
+ M.step(`Verifying local skills integrity...`);
88
+ const lock = await readLocalLock(process.cwd());
89
+ const allowedSkills = new Set(Object.keys(lock.skills));
90
+ let failures = 0;
91
+ const directoriesToScan = /* @__PURE__ */ new Set();
92
+ directoriesToScan.add(getCanonicalSkillsDir(false));
93
+ const installedAgents = await detectInstalledAgents();
94
+ for (const agent of installedAgents) directoriesToScan.add(getAgentBaseDir(agent, false));
95
+ for (const dir of directoriesToScan) {
96
+ try {
97
+ await access(dir);
98
+ } catch {
99
+ continue;
100
+ }
101
+ const entries = await readdir(dir, { withFileTypes: true });
102
+ for (const entry of entries) {
103
+ if (entry.name.startsWith(".")) continue;
104
+ if (!allowedSkills.has(entry.name)) {
105
+ M.error(import_picocolors.default.red(`Unauthorized skill detected in ${dir}: ${entry.name}`));
106
+ failures++;
107
+ }
108
+ }
109
+ }
110
+ for (const [skillName, entry] of Object.entries(lock.skills)) {
111
+ const canonicalDir = join(getCanonicalSkillsDir(false), skillName);
112
+ try {
113
+ await access(canonicalDir);
114
+ const computed = await computeSkillFolderHash(canonicalDir);
115
+ if (computed !== entry.computedHash) {
116
+ M.error(import_picocolors.default.red(`Integrity verification failed for authorized skill: ${skillName}`));
117
+ M.error(import_picocolors.default.dim(`Expected: ${entry.computedHash}`));
118
+ M.error(import_picocolors.default.dim(`Computed: ${computed}`));
119
+ failures++;
120
+ } else M.message(`Integrity verified for ${import_picocolors.default.cyan(skillName)}`);
121
+ } catch {
122
+ M.error(import_picocolors.default.red(`Skill ${skillName} is in lockfile but not found locally at ${canonicalDir}`));
123
+ failures++;
124
+ }
125
+ }
126
+ if (failures > 0) {
127
+ M.error(import_picocolors.default.red(`Integrity Verification Failed: ${failures} anomalies detected.`));
128
+ process.exit(1);
129
+ } else M.success(import_picocolors.default.green(`Integrity complete. No unauthorized skills or tampered contents detected.`));
130
+ }
131
+ async function checkEmptyGlobalSkills(homeDir) {
132
+ const findings = [];
133
+ const realHome = homedir();
134
+ const globalDirs = /* @__PURE__ */ new Set();
135
+ for (const config of Object.values(agents)) if (config.globalSkillsDir) {
136
+ let dir = config.globalSkillsDir;
137
+ if (homeDir && dir.startsWith(realHome)) dir = join(homeDir, dir.slice(realHome.length));
138
+ globalDirs.add(dir);
139
+ }
140
+ for (const dir of globalDirs) {
141
+ try {
142
+ await access(dir);
143
+ } catch {
144
+ continue;
145
+ }
146
+ const nonHidden = (await readdir(dir, { withFileTypes: true })).filter((e) => !e.name.startsWith("."));
147
+ if (nonHidden.length > 0) {
148
+ const names = nonHidden.map((e) => e.name);
149
+ findings.push(`Global skills found in ${dir}: ${names.join(", ")}`);
150
+ }
151
+ }
152
+ const lockPath = homeDir ? join(homeDir, ".agents", ".skill-lock.json") : join(realHome, ".agents", ".skill-lock.json");
153
+ try {
154
+ await access(lockPath);
155
+ findings.push(`Global skill lock file exists: ${lockPath}`);
156
+ } catch {}
157
+ return {
158
+ clean: findings.length === 0,
159
+ findings
160
+ };
161
+ }
162
+ async function runVerifyEmptyGlobalSkillsCommand() {
163
+ const result = await checkEmptyGlobalSkills();
164
+ if (result.clean) M.success(import_picocolors.default.green("No global skills detected. Global directories are clean."));
165
+ else {
166
+ for (const finding of result.findings) M.error(import_picocolors.default.red(finding));
167
+ process.exit(1);
168
+ }
169
+ }
170
+ export { runVerifyCommand };
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export { };