@comet/cli 9.0.0-beta.4 → 9.0.0-beta.5

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.
@@ -89,6 +89,63 @@ var os = __importStar(require("os"));
89
89
  var path = __importStar(require("path"));
90
90
  var SKILL_SOURCE_PATHS = ["skills", "agentic-plugin/skills"];
91
91
  var RULE_SOURCE_PATHS = ["rules"];
92
+ function discoverNodeModulesPackages(cwd) {
93
+ var nodeModulesDir = path.join(cwd, "node_modules");
94
+ if (!fs.existsSync(nodeModulesDir)) {
95
+ return [];
96
+ }
97
+ var packageDirs = [];
98
+ var entries;
99
+ try {
100
+ entries = fs.readdirSync(nodeModulesDir);
101
+ }
102
+ catch (_a) {
103
+ return [];
104
+ }
105
+ for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
106
+ var entry = entries_1[_i];
107
+ if (entry.startsWith(".")) {
108
+ continue;
109
+ }
110
+ var fullPath = path.join(nodeModulesDir, entry);
111
+ if (entry.startsWith("@")) {
112
+ // Scoped package: read one level deeper
113
+ var scopedEntries = void 0;
114
+ try {
115
+ scopedEntries = fs.readdirSync(fullPath);
116
+ }
117
+ catch (_b) {
118
+ continue;
119
+ }
120
+ for (var _c = 0, scopedEntries_1 = scopedEntries; _c < scopedEntries_1.length; _c++) {
121
+ var scopedEntry = scopedEntries_1[_c];
122
+ if (scopedEntry.startsWith(".")) {
123
+ continue;
124
+ }
125
+ var scopedPath = path.join(fullPath, scopedEntry);
126
+ try {
127
+ if (fs.statSync(scopedPath).isDirectory()) {
128
+ packageDirs.push(scopedPath);
129
+ }
130
+ }
131
+ catch (_d) {
132
+ // skip unreadable entries
133
+ }
134
+ }
135
+ }
136
+ else {
137
+ try {
138
+ if (fs.statSync(fullPath).isDirectory()) {
139
+ packageDirs.push(fullPath);
140
+ }
141
+ }
142
+ catch (_e) {
143
+ // skip unreadable entries
144
+ }
145
+ }
146
+ }
147
+ return packageDirs;
148
+ }
92
149
  function parseRepoUrl(rawUrl) {
93
150
  var hashIndex = rawUrl.lastIndexOf("#");
94
151
  if (hashIndex === -1) {
@@ -274,15 +331,53 @@ function installFeatures(cwd, repos, options) {
274
331
  itemLabelSingular: "rule",
275
332
  itemLabelPlural: "rules",
276
333
  }); });
334
+ // Discover skills/rules from node_modules (direct dependencies only, including @scoped packages)
335
+ var nodeModulesPackages = discoverNodeModulesPackages(cwd);
336
+ for (var _b = 0, nodeModulesPackages_1 = nodeModulesPackages; _b < nodeModulesPackages_1.length; _b++) {
337
+ var pkgDir = nodeModulesPackages_1[_b];
338
+ var pkgName = pkgDir.includes("".concat(path.sep, "@")) ? pkgDir.split(path.sep).slice(-2).join("/") : path.basename(pkgDir);
339
+ for (var _c = 0, SKILL_SOURCE_PATHS_1 = SKILL_SOURCE_PATHS; _c < SKILL_SOURCE_PATHS_1.length; _c++) {
340
+ var p = SKILL_SOURCE_PATHS_1[_c];
341
+ var dir = path.join(pkgDir, p);
342
+ if (fs.existsSync(dir)) {
343
+ skillSources.push({
344
+ label: "node_modules ".concat(pkgName, " (").concat(p, "/)"),
345
+ directory: dir,
346
+ discover: "folders",
347
+ symlink: true,
348
+ filterInternal: true,
349
+ installed: installedSkills,
350
+ itemLabelSingular: "skill",
351
+ itemLabelPlural: "skills",
352
+ });
353
+ }
354
+ }
355
+ for (var _d = 0, RULE_SOURCE_PATHS_1 = RULE_SOURCE_PATHS; _d < RULE_SOURCE_PATHS_1.length; _d++) {
356
+ var p = RULE_SOURCE_PATHS_1[_d];
357
+ var dir = path.join(pkgDir, p);
358
+ if (fs.existsSync(dir)) {
359
+ ruleSources.push({
360
+ label: "node_modules ".concat(pkgName, " (").concat(p, "/)"),
361
+ directory: dir,
362
+ discover: "files",
363
+ symlink: true,
364
+ filterInternal: true,
365
+ installed: installedRules,
366
+ itemLabelSingular: "rule",
367
+ itemLabelPlural: "rules",
368
+ });
369
+ }
370
+ }
371
+ }
277
372
  var sparsePatterns = __spreadArray(__spreadArray([], SKILL_SOURCE_PATHS, true), RULE_SOURCE_PATHS, true).map(function (p) { return "".concat(p, "/"); });
278
373
  var tempDirs = [];
279
374
  try {
280
- for (var _b = 0, repos_1 = repos; _b < repos_1.length; _b++) {
281
- var rawUrl = repos_1[_b];
375
+ for (var _e = 0, repos_1 = repos; _e < repos_1.length; _e++) {
376
+ var rawUrl = repos_1[_e];
282
377
  var cloneDir = cloneRepo(rawUrl, sparsePatterns);
283
378
  tempDirs.push(cloneDir);
284
- for (var _c = 0, SKILL_SOURCE_PATHS_1 = SKILL_SOURCE_PATHS; _c < SKILL_SOURCE_PATHS_1.length; _c++) {
285
- var p = SKILL_SOURCE_PATHS_1[_c];
379
+ for (var _f = 0, SKILL_SOURCE_PATHS_2 = SKILL_SOURCE_PATHS; _f < SKILL_SOURCE_PATHS_2.length; _f++) {
380
+ var p = SKILL_SOURCE_PATHS_2[_f];
286
381
  skillSources.push({
287
382
  label: "external ".concat(rawUrl, " (").concat(p, "/)"),
288
383
  directory: path.join(cloneDir, p),
@@ -294,8 +389,8 @@ function installFeatures(cwd, repos, options) {
294
389
  itemLabelPlural: "skills",
295
390
  });
296
391
  }
297
- for (var _d = 0, RULE_SOURCE_PATHS_1 = RULE_SOURCE_PATHS; _d < RULE_SOURCE_PATHS_1.length; _d++) {
298
- var p = RULE_SOURCE_PATHS_1[_d];
392
+ for (var _g = 0, RULE_SOURCE_PATHS_2 = RULE_SOURCE_PATHS; _g < RULE_SOURCE_PATHS_2.length; _g++) {
393
+ var p = RULE_SOURCE_PATHS_2[_g];
299
394
  ruleSources.push({
300
395
  label: "external ".concat(rawUrl, " (").concat(p, "/)"),
301
396
  directory: path.join(cloneDir, p),
@@ -314,8 +409,8 @@ function installFeatures(cwd, repos, options) {
314
409
  console.log("Total rules installed: ".concat(ruleCount));
315
410
  }
316
411
  finally {
317
- for (var _e = 0, tempDirs_1 = tempDirs; _e < tempDirs_1.length; _e++) {
318
- var tmpDir = tempDirs_1[_e];
412
+ for (var _h = 0, tempDirs_1 = tempDirs; _h < tempDirs_1.length; _h++) {
413
+ var tmpDir = tempDirs_1[_h];
319
414
  fs.rmSync(tmpDir, { recursive: true, force: true });
320
415
  }
321
416
  }
@@ -210,6 +210,97 @@ function mockFilesystem(_a) {
210
210
  (0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/skills/internal"), vitest_1.expect.any(String));
211
211
  });
212
212
  });
213
+ (0, vitest_1.describe)("installFeatures – node_modules skills and rules", function () {
214
+ (0, vitest_1.it)("symlinks skills from node_modules packages", function () {
215
+ mockFilesystem({
216
+ listings: {
217
+ "/proj/node_modules": ["some-tool"],
218
+ "/proj/node_modules/some-tool": ["skills", "package.json"],
219
+ "/proj/node_modules/some-tool/skills": ["tool-docs"],
220
+ },
221
+ });
222
+ (0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
223
+ (0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/skills/tool-docs"), path.join("/proj", ".agents", "skills", "tool-docs"));
224
+ (0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/skills/tool-docs"), path.join("/proj", ".claude", "skills", "tool-docs"));
225
+ });
226
+ (0, vitest_1.it)("symlinks rules from node_modules packages", function () {
227
+ mockFilesystem({
228
+ listings: {
229
+ "/proj/node_modules": ["some-tool"],
230
+ "/proj/node_modules/some-tool": ["rules", "package.json"],
231
+ "/proj/node_modules/some-tool/rules": ["best-practices.md"],
232
+ },
233
+ });
234
+ (0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
235
+ (0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/rules/best-practices.md"), path.join("/proj", ".agents", "rules", "best-practices.md"));
236
+ (0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/rules/best-practices.md"), path.join("/proj", ".claude", "rules", "best-practices.md"));
237
+ });
238
+ (0, vitest_1.it)("discovers skills from @scoped packages", function () {
239
+ mockFilesystem({
240
+ listings: {
241
+ "/proj/node_modules": ["@my-scope"],
242
+ "/proj/node_modules/@my-scope": ["my-tool"],
243
+ "/proj/node_modules/@my-scope/my-tool": ["skills", "package.json"],
244
+ "/proj/node_modules/@my-scope/my-tool/skills": ["scoped-skill"],
245
+ },
246
+ });
247
+ (0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
248
+ (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"));
249
+ });
250
+ (0, vitest_1.it)("local skills take priority over node_modules skills", function () {
251
+ mockFilesystem({
252
+ listings: {
253
+ "/proj/skills": ["shared"],
254
+ "/proj/node_modules": ["some-tool"],
255
+ "/proj/node_modules/some-tool": ["skills", "package.json"],
256
+ "/proj/node_modules/some-tool/skills": ["shared"],
257
+ },
258
+ });
259
+ (0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
260
+ (0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/skills/shared"), vitest_1.expect.any(String));
261
+ (0, vitest_1.expect)(fs.symlinkSync).not.toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/skills/shared"), vitest_1.expect.any(String));
262
+ (0, vitest_1.expect)(console.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('CONFLICT: "shared"'));
263
+ });
264
+ (0, vitest_1.it)("filters internal skills from node_modules", function () {
265
+ mockFilesystem({
266
+ listings: {
267
+ "/proj/node_modules": ["some-tool"],
268
+ "/proj/node_modules/some-tool": ["skills", "package.json"],
269
+ "/proj/node_modules/some-tool/skills": ["public-skill", "internal-skill"],
270
+ },
271
+ fileContents: {
272
+ "/proj/node_modules/some-tool/skills/internal-skill/SKILL.md": "---\nmetadata:\n internal: true\n---\n# Internal",
273
+ },
274
+ });
275
+ (0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
276
+ (0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/skills/public-skill"), vitest_1.expect.any(String));
277
+ (0, vitest_1.expect)(fs.symlinkSync).not.toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/skills/internal-skill"), vitest_1.expect.any(String));
278
+ });
279
+ (0, vitest_1.it)("skips dotfiles and dotfolders in node_modules", function () {
280
+ mockFilesystem({
281
+ listings: {
282
+ "/proj/node_modules": [".bin", ".package-lock.json", "some-tool"],
283
+ "/proj/node_modules/some-tool": ["skills", "package.json"],
284
+ "/proj/node_modules/some-tool/skills": ["my-skill"],
285
+ },
286
+ });
287
+ (0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
288
+ (0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/node_modules/some-tool/skills/my-skill"), vitest_1.expect.any(String));
289
+ });
290
+ (0, vitest_1.it)("does not add a source entry for packages without a skills/ or rules/ directory", function () {
291
+ mockFilesystem({
292
+ listings: {
293
+ "/proj/node_modules": ["no-skills-tool"],
294
+ "/proj/node_modules/no-skills-tool": ["dist", "package.json"],
295
+ // no skills/ or rules/ entry
296
+ },
297
+ });
298
+ (0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
299
+ // No symlinks and no "No skills found in node_modules" log noise
300
+ (0, vitest_1.expect)(fs.symlinkSync).not.toHaveBeenCalled();
301
+ (0, vitest_1.expect)(console.log).not.toHaveBeenCalledWith(vitest_1.expect.stringContaining("node_modules no-skills-tool"));
302
+ });
303
+ });
213
304
  (0, vitest_1.describe)("installFeatures – missing local sources", function () {
214
305
  (0, vitest_1.it)("runs cleanly when there are no local skills or rules", function () {
215
306
  mockFilesystem({});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comet/cli",
3
- "version": "9.0.0-beta.4",
3
+ "version": "9.0.0-beta.5",
4
4
  "description": "A collection of CLI tools for Comet projects",
5
5
  "repository": {
6
6
  "directory": "packages/cli",
@@ -33,7 +33,7 @@
33
33
  "rimraf": "^6.1.2",
34
34
  "typescript": "^5.9.3",
35
35
  "vitest": "^4.0.16",
36
- "@comet/eslint-config": "9.0.0-beta.4"
36
+ "@comet/eslint-config": "9.0.0-beta.5"
37
37
  },
38
38
  "engines": {
39
39
  "node": ">=22.0.0"