@comet/cli 8.28.2 → 8.30.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.
|
@@ -12,4 +12,5 @@ export interface InstallOptions {
|
|
|
12
12
|
}
|
|
13
13
|
export declare function isInternalSkill(skillFolderPath: string): boolean;
|
|
14
14
|
export declare function installSkills(sources: SkillSource[], targetDirs: string[], { dryRun }: InstallOptions): void;
|
|
15
|
+
export declare function installAgentSkills(cwd: string, repos: string[], options: InstallOptions): void;
|
|
15
16
|
export declare const installAgentSkillsCommand: Command;
|
|
@@ -72,6 +72,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
72
72
|
exports.installAgentSkillsCommand = void 0;
|
|
73
73
|
exports.isInternalSkill = isInternalSkill;
|
|
74
74
|
exports.installSkills = installSkills;
|
|
75
|
+
exports.installAgentSkills = installAgentSkills;
|
|
75
76
|
/* eslint-disable no-console */
|
|
76
77
|
var child_process_1 = require("child_process");
|
|
77
78
|
var commander_1 = require("commander");
|
|
@@ -87,6 +88,63 @@ function parseRepoUrl(rawUrl) {
|
|
|
87
88
|
return { repoUrl: rawUrl.slice(0, hashIndex), ref: rawUrl.slice(hashIndex + 1) || undefined };
|
|
88
89
|
}
|
|
89
90
|
var SKILL_SOURCE_PATHS = ["skills", "agentic-plugin/skills"];
|
|
91
|
+
function discoverNodeModulesPackages(cwd) {
|
|
92
|
+
var nodeModulesDir = path.join(cwd, "node_modules");
|
|
93
|
+
if (!fs.existsSync(nodeModulesDir)) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
var packageDirs = [];
|
|
97
|
+
var entries;
|
|
98
|
+
try {
|
|
99
|
+
entries = fs.readdirSync(nodeModulesDir);
|
|
100
|
+
}
|
|
101
|
+
catch (_a) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
|
|
105
|
+
var entry = entries_1[_i];
|
|
106
|
+
if (entry.startsWith(".")) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
var fullPath = path.join(nodeModulesDir, entry);
|
|
110
|
+
if (entry.startsWith("@")) {
|
|
111
|
+
// Scoped package: read one level deeper
|
|
112
|
+
var scopedEntries = void 0;
|
|
113
|
+
try {
|
|
114
|
+
scopedEntries = fs.readdirSync(fullPath);
|
|
115
|
+
}
|
|
116
|
+
catch (_b) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
for (var _c = 0, scopedEntries_1 = scopedEntries; _c < scopedEntries_1.length; _c++) {
|
|
120
|
+
var scopedEntry = scopedEntries_1[_c];
|
|
121
|
+
if (scopedEntry.startsWith(".")) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
var scopedPath = path.join(fullPath, scopedEntry);
|
|
125
|
+
try {
|
|
126
|
+
if (fs.statSync(scopedPath).isDirectory()) {
|
|
127
|
+
packageDirs.push(scopedPath);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (_d) {
|
|
131
|
+
// skip unreadable entries
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
try {
|
|
137
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
138
|
+
packageDirs.push(fullPath);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (_e) {
|
|
142
|
+
// skip unreadable entries
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return packageDirs;
|
|
147
|
+
}
|
|
90
148
|
function cloneRepo(rawUrl) {
|
|
91
149
|
var _a = parseRepoUrl(rawUrl), repoUrl = _a.repoUrl, ref = _a.ref;
|
|
92
150
|
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "comet-agent-skills-"));
|
|
@@ -198,54 +256,75 @@ function loadConfig(configPath) {
|
|
|
198
256
|
var raw = fs.readFileSync(resolved, "utf-8");
|
|
199
257
|
return JSON.parse(raw);
|
|
200
258
|
}
|
|
259
|
+
function installAgentSkills(cwd, repos, options) {
|
|
260
|
+
var targetDirs = [path.join(cwd, ".agents", "skills"), path.join(cwd, ".claude", "skills")];
|
|
261
|
+
// Ensure target directories exist (without clearing existing contents)
|
|
262
|
+
for (var _i = 0, targetDirs_2 = targetDirs; _i < targetDirs_2.length; _i++) {
|
|
263
|
+
var dir = targetDirs_2[_i];
|
|
264
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
265
|
+
}
|
|
266
|
+
// Priority order: local skill folders (in declared order) > node_modules packages > external repos (in arg order)
|
|
267
|
+
var sources = SKILL_SOURCE_PATHS.map(function (p) { return ({
|
|
268
|
+
label: "local ".concat(p, "/"),
|
|
269
|
+
directory: path.join(cwd, p),
|
|
270
|
+
symlink: true,
|
|
271
|
+
}); });
|
|
272
|
+
// Discover skills from node_modules (direct dependencies only, including @scoped packages)
|
|
273
|
+
var nodeModulesPackages = discoverNodeModulesPackages(cwd);
|
|
274
|
+
for (var _a = 0, nodeModulesPackages_1 = nodeModulesPackages; _a < nodeModulesPackages_1.length; _a++) {
|
|
275
|
+
var pkgDir = nodeModulesPackages_1[_a];
|
|
276
|
+
var pkgName = pkgDir.includes("".concat(path.sep, "@")) ? pkgDir.split(path.sep).slice(-2).join("/") : path.basename(pkgDir);
|
|
277
|
+
for (var _b = 0, SKILL_SOURCE_PATHS_1 = SKILL_SOURCE_PATHS; _b < SKILL_SOURCE_PATHS_1.length; _b++) {
|
|
278
|
+
var p = SKILL_SOURCE_PATHS_1[_b];
|
|
279
|
+
var dir = path.join(pkgDir, p);
|
|
280
|
+
if (fs.existsSync(dir)) {
|
|
281
|
+
sources.push({
|
|
282
|
+
label: "node_modules ".concat(pkgName, " (").concat(p, "/)"),
|
|
283
|
+
directory: dir,
|
|
284
|
+
symlink: true,
|
|
285
|
+
filterInternal: true,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
var tempDirs = [];
|
|
291
|
+
try {
|
|
292
|
+
for (var _c = 0, repos_1 = repos; _c < repos_1.length; _c++) {
|
|
293
|
+
var rawUrl = repos_1[_c];
|
|
294
|
+
var cloneDir = cloneRepo(rawUrl);
|
|
295
|
+
tempDirs.push(cloneDir);
|
|
296
|
+
for (var _d = 0, SKILL_SOURCE_PATHS_2 = SKILL_SOURCE_PATHS; _d < SKILL_SOURCE_PATHS_2.length; _d++) {
|
|
297
|
+
var p = SKILL_SOURCE_PATHS_2[_d];
|
|
298
|
+
sources.push({
|
|
299
|
+
label: "external ".concat(rawUrl, " (").concat(p, "/)"),
|
|
300
|
+
directory: path.join(cloneDir, p),
|
|
301
|
+
symlink: false,
|
|
302
|
+
filterInternal: true,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
installSkills(sources, targetDirs, options);
|
|
307
|
+
}
|
|
308
|
+
finally {
|
|
309
|
+
for (var _e = 0, tempDirs_1 = tempDirs; _e < tempDirs_1.length; _e++) {
|
|
310
|
+
var tmpDir = tempDirs_1[_e];
|
|
311
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
201
315
|
exports.installAgentSkillsCommand = new commander_1.Command("install-agent-skills")
|
|
202
316
|
.description("Install agent skills from local directories and optional external git repos")
|
|
203
317
|
.option("--config <path>", "Path to a JSON config file specifying repos to install skills from", "agent-skills.json")
|
|
204
318
|
.option("--dry-run", "Show which symlinks/copies would be created without making changes", false)
|
|
205
319
|
.action(function (options) { return __awaiter(void 0, void 0, void 0, function () {
|
|
206
|
-
var configPath, dryRun, resolvedConfig, repos
|
|
207
|
-
var
|
|
208
|
-
return __generator(this, function (
|
|
320
|
+
var configPath, dryRun, resolvedConfig, repos;
|
|
321
|
+
var _a;
|
|
322
|
+
return __generator(this, function (_b) {
|
|
209
323
|
configPath = options.config, dryRun = options.dryRun;
|
|
210
324
|
resolvedConfig = path.resolve(configPath);
|
|
211
|
-
repos = fs.existsSync(resolvedConfig) ? ((
|
|
325
|
+
repos = fs.existsSync(resolvedConfig) ? ((_a = loadConfig(configPath).repos) !== null && _a !== void 0 ? _a : []) : [];
|
|
212
326
|
console.log("=== Installing agent skills".concat(dryRun ? " (dry run)" : "", " ==="));
|
|
213
|
-
|
|
214
|
-
targetDirs = [path.join(cwd, ".agents", "skills"), path.join(cwd, ".claude", "skills")];
|
|
215
|
-
// Ensure target directories exist (without clearing existing contents)
|
|
216
|
-
for (_i = 0, targetDirs_2 = targetDirs; _i < targetDirs_2.length; _i++) {
|
|
217
|
-
dir = targetDirs_2[_i];
|
|
218
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
219
|
-
}
|
|
220
|
-
sources = SKILL_SOURCE_PATHS.map(function (p) { return ({
|
|
221
|
-
label: "local ".concat(p, "/"),
|
|
222
|
-
directory: path.join(cwd, p),
|
|
223
|
-
symlink: true,
|
|
224
|
-
}); });
|
|
225
|
-
tempDirs = [];
|
|
226
|
-
try {
|
|
227
|
-
for (_a = 0, repos_1 = repos; _a < repos_1.length; _a++) {
|
|
228
|
-
rawUrl = repos_1[_a];
|
|
229
|
-
cloneDir = cloneRepo(rawUrl);
|
|
230
|
-
tempDirs.push(cloneDir);
|
|
231
|
-
for (_b = 0, SKILL_SOURCE_PATHS_1 = SKILL_SOURCE_PATHS; _b < SKILL_SOURCE_PATHS_1.length; _b++) {
|
|
232
|
-
p = SKILL_SOURCE_PATHS_1[_b];
|
|
233
|
-
sources.push({
|
|
234
|
-
label: "external ".concat(rawUrl, " (").concat(p, "/)"),
|
|
235
|
-
directory: path.join(cloneDir, p),
|
|
236
|
-
symlink: false,
|
|
237
|
-
filterInternal: true,
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
installSkills(sources, targetDirs, { dryRun: dryRun });
|
|
242
|
-
}
|
|
243
|
-
finally {
|
|
244
|
-
for (_c = 0, tempDirs_1 = tempDirs; _c < tempDirs_1.length; _c++) {
|
|
245
|
-
tmpDir = tempDirs_1[_c];
|
|
246
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
247
|
-
}
|
|
248
|
-
}
|
|
327
|
+
installAgentSkills(process.cwd(), repos, { dryRun: dryRun });
|
|
249
328
|
console.log("=== Finished ===");
|
|
250
329
|
return [2 /*return*/];
|
|
251
330
|
});
|
|
@@ -276,3 +276,69 @@ function mockFilesystem(skillMap, existingDests, skillMdContents) {
|
|
|
276
276
|
(0, vitest_1.expect)(console.log).not.toHaveBeenCalledWith(vitest_1.expect.stringContaining("internal-skill"));
|
|
277
277
|
});
|
|
278
278
|
});
|
|
279
|
+
(0, vitest_1.describe)("installAgentSkills – node_modules skills", function () {
|
|
280
|
+
(0, vitest_1.it)("symlinks skills from node_modules packages", function () {
|
|
281
|
+
mockFilesystem({
|
|
282
|
+
"/proj/node_modules": ["some-tool"],
|
|
283
|
+
"/proj/node_modules/some-tool": ["skills", "package.json"],
|
|
284
|
+
"/proj/node_modules/some-tool/skills": ["tool-docs"],
|
|
285
|
+
});
|
|
286
|
+
(0, install_agent_skills_1.installAgentSkills)("/proj", [], { dryRun: false });
|
|
287
|
+
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/skills/tool-docs"), path.join("/proj", ".agents", "skills", "tool-docs"));
|
|
288
|
+
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/skills/tool-docs"), path.join("/proj", ".claude", "skills", "tool-docs"));
|
|
289
|
+
});
|
|
290
|
+
(0, vitest_1.it)("discovers skills from @scoped packages", function () {
|
|
291
|
+
mockFilesystem({
|
|
292
|
+
"/proj/node_modules": ["@my-scope"],
|
|
293
|
+
"/proj/node_modules/@my-scope": ["my-tool"],
|
|
294
|
+
"/proj/node_modules/@my-scope/my-tool": ["skills", "package.json"],
|
|
295
|
+
"/proj/node_modules/@my-scope/my-tool/skills": ["scoped-skill"],
|
|
296
|
+
});
|
|
297
|
+
(0, install_agent_skills_1.installAgentSkills)("/proj", [], { dryRun: false });
|
|
298
|
+
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/node_modules/@my-scope/my-tool/skills/scoped-skill"), path.join("/proj", ".agents", "skills", "scoped-skill"));
|
|
299
|
+
});
|
|
300
|
+
(0, vitest_1.it)("local skills take priority over node_modules skills", function () {
|
|
301
|
+
mockFilesystem({
|
|
302
|
+
"/proj/skills": ["shared"],
|
|
303
|
+
"/proj/node_modules": ["some-tool"],
|
|
304
|
+
"/proj/node_modules/some-tool": ["skills", "package.json"],
|
|
305
|
+
"/proj/node_modules/some-tool/skills": ["shared"],
|
|
306
|
+
});
|
|
307
|
+
(0, install_agent_skills_1.installAgentSkills)("/proj", [], { dryRun: false });
|
|
308
|
+
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/skills/shared"), vitest_1.expect.any(String));
|
|
309
|
+
(0, vitest_1.expect)(fs.symlinkSync).not.toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/skills/shared"), vitest_1.expect.any(String));
|
|
310
|
+
(0, vitest_1.expect)(console.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('CONFLICT: "shared"'));
|
|
311
|
+
});
|
|
312
|
+
(0, vitest_1.it)("filters internal skills from node_modules", function () {
|
|
313
|
+
mockFilesystem({
|
|
314
|
+
"/proj/node_modules": ["some-tool"],
|
|
315
|
+
"/proj/node_modules/some-tool": ["skills", "package.json"],
|
|
316
|
+
"/proj/node_modules/some-tool/skills": ["public-skill", "internal-skill"],
|
|
317
|
+
}, [], {
|
|
318
|
+
"/proj/node_modules/some-tool/skills/internal-skill/SKILL.md": "---\nmetadata:\n internal: true\n---\n# Internal",
|
|
319
|
+
});
|
|
320
|
+
(0, install_agent_skills_1.installAgentSkills)("/proj", [], { dryRun: false });
|
|
321
|
+
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/skills/public-skill"), vitest_1.expect.any(String));
|
|
322
|
+
(0, vitest_1.expect)(fs.symlinkSync).not.toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/skills/internal-skill"), vitest_1.expect.any(String));
|
|
323
|
+
});
|
|
324
|
+
(0, vitest_1.it)("skips dotfiles and dotfolders in node_modules", function () {
|
|
325
|
+
mockFilesystem({
|
|
326
|
+
"/proj/node_modules": [".bin", ".package-lock.json", "some-tool"],
|
|
327
|
+
"/proj/node_modules/some-tool": ["skills", "package.json"],
|
|
328
|
+
"/proj/node_modules/some-tool/skills": ["my-skill"],
|
|
329
|
+
});
|
|
330
|
+
(0, install_agent_skills_1.installAgentSkills)("/proj", [], { dryRun: false });
|
|
331
|
+
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/skills/my-skill"), vitest_1.expect.any(String));
|
|
332
|
+
});
|
|
333
|
+
(0, vitest_1.it)("does not add a source entry for packages without a skills/ directory", function () {
|
|
334
|
+
mockFilesystem({
|
|
335
|
+
"/proj/node_modules": ["no-skills-tool"],
|
|
336
|
+
"/proj/node_modules/no-skills-tool": ["dist", "package.json"],
|
|
337
|
+
// no skills/ entry
|
|
338
|
+
});
|
|
339
|
+
(0, install_agent_skills_1.installAgentSkills)("/proj", [], { dryRun: false });
|
|
340
|
+
// No symlinks and no "No skills found in node_modules" log noise
|
|
341
|
+
(0, vitest_1.expect)(fs.symlinkSync).not.toHaveBeenCalled();
|
|
342
|
+
(0, vitest_1.expect)(console.log).not.toHaveBeenCalledWith(vitest_1.expect.stringContaining("node_modules no-skills-tool"));
|
|
343
|
+
});
|
|
344
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comet/cli",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.30.0",
|
|
4
4
|
"description": "A collection of CLI tools for Comet projects",
|
|
5
5
|
"repository": {
|
|
6
6
|
"directory": "packages/cli",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"rimraf": "^6.1.2",
|
|
33
33
|
"typescript": "^5.9.3",
|
|
34
34
|
"vitest": "^4.0.16",
|
|
35
|
-
"@comet/eslint-config": "8.
|
|
35
|
+
"@comet/eslint-config": "8.30.0"
|
|
36
36
|
},
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": ">=22.0.0"
|