@comet/cli 9.0.0-beta.4 → 9.0.0-beta.6
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/lib/commands/install-agent-features.js +103 -8
- package/package.json +6 -5
- package/lib/commands/install-agent-features.test.d.ts +0 -1
- package/lib/commands/install-agent-features.test.js +0 -234
- package/lib/commands/site-configs.test.d.ts +0 -1
- package/lib/commands/site-configs.test.js +0 -70
|
@@ -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
|
|
281
|
-
var rawUrl = repos_1[
|
|
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
|
|
285
|
-
var p =
|
|
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
|
|
298
|
-
var p =
|
|
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
|
|
318
|
-
var tmpDir = tempDirs_1[
|
|
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
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comet/cli",
|
|
3
|
-
"version": "9.0.0-beta.
|
|
3
|
+
"version": "9.0.0-beta.6",
|
|
4
4
|
"description": "A collection of CLI tools for Comet projects",
|
|
5
5
|
"repository": {
|
|
6
6
|
"directory": "packages/cli",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"npm-run-all2": "^8.0.4",
|
|
33
33
|
"rimraf": "^6.1.2",
|
|
34
34
|
"typescript": "^5.9.3",
|
|
35
|
-
"vitest": "^4.
|
|
36
|
-
"@comet/eslint-config": "9.0.0-beta.
|
|
35
|
+
"vitest": "^4.1.8",
|
|
36
|
+
"@comet/eslint-config": "9.0.0-beta.6"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
|
39
39
|
"node": ">=22.0.0"
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"registry": "https://registry.npmjs.org"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
|
-
"build": "pnpm run clean && tsc",
|
|
46
|
+
"build": "pnpm run clean && tsc -p tsconfig.build.json",
|
|
47
47
|
"clean": "rimraf lib",
|
|
48
48
|
"dev": "tsc --watch",
|
|
49
49
|
"lint": "run-p lint:prettier lint:eslint lint:tsc",
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
"lint:fix:prettier": "pnpm run lint:prettier --write",
|
|
55
55
|
"lint:prettier": "pnpm exec prettier --check '*.{ts,js,json,md,yml,yaml}'",
|
|
56
56
|
"lint:tsc": "tsc",
|
|
57
|
-
"test": "
|
|
57
|
+
"test": "pnpm run test:unit",
|
|
58
|
+
"test:unit": "vitest --run",
|
|
58
59
|
"test:watch": "vitest --watch"
|
|
59
60
|
}
|
|
60
61
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
/* eslint-disable no-console */
|
|
37
|
-
var fs = __importStar(require("fs"));
|
|
38
|
-
var path = __importStar(require("path"));
|
|
39
|
-
var vitest_1 = require("vitest");
|
|
40
|
-
var install_agent_features_1 = require("./install-agent-features");
|
|
41
|
-
vitest_1.vi.mock("fs");
|
|
42
|
-
/**
|
|
43
|
-
* fs mock for installFeatures.
|
|
44
|
-
*
|
|
45
|
-
* - `listings` maps a directory path to the names of its direct entries.
|
|
46
|
-
* - Entries are assumed to be directories unless the entry name ends in `.md`
|
|
47
|
-
* or appears in `fileContents`.
|
|
48
|
-
* - `existingDests` is the set of destination paths that pathExists/lstatSync
|
|
49
|
-
* should return truthy for (used to assert overwrite behavior).
|
|
50
|
-
* - `fileContents` provides content for fs.readFileSync; any path not in the
|
|
51
|
-
* map throws ENOENT.
|
|
52
|
-
*/
|
|
53
|
-
function mockFilesystem(_a) {
|
|
54
|
-
var _b = _a === void 0 ? {} : _a, _c = _b.listings, listings = _c === void 0 ? {} : _c, _d = _b.existingDests, existingDests = _d === void 0 ? [] : _d, _e = _b.fileContents, fileContents = _e === void 0 ? {} : _e;
|
|
55
|
-
var isFile = function (p) {
|
|
56
|
-
if (p in fileContents) {
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
return path.basename(p).endsWith(".md");
|
|
60
|
-
};
|
|
61
|
-
vitest_1.vi.mocked(fs.existsSync).mockImplementation(function (p) { return String(p) in listings; });
|
|
62
|
-
vitest_1.vi.mocked(fs.readdirSync).mockImplementation(function (p) { var _a; return (_a = listings[String(p)]) !== null && _a !== void 0 ? _a : []; });
|
|
63
|
-
vitest_1.vi.mocked(fs.statSync).mockImplementation(function (p) {
|
|
64
|
-
var fileLike = isFile(String(p));
|
|
65
|
-
return { isDirectory: function () { return !fileLike; }, isFile: function () { return fileLike; } };
|
|
66
|
-
});
|
|
67
|
-
vitest_1.vi.mocked(fs.lstatSync).mockImplementation(function (p) {
|
|
68
|
-
if (existingDests.includes(String(p))) {
|
|
69
|
-
return {};
|
|
70
|
-
}
|
|
71
|
-
throw Object.assign(new Error("ENOENT"), { code: "ENOENT" });
|
|
72
|
-
});
|
|
73
|
-
vitest_1.vi.mocked(fs.readFileSync).mockImplementation(function (p) {
|
|
74
|
-
var content = fileContents[String(p)];
|
|
75
|
-
if (content !== undefined) {
|
|
76
|
-
return content;
|
|
77
|
-
}
|
|
78
|
-
throw Object.assign(new Error("ENOENT"), { code: "ENOENT" });
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
(0, vitest_1.beforeEach)(function () {
|
|
82
|
-
vitest_1.vi.mocked(fs.mkdirSync).mockImplementation(function () { return undefined; });
|
|
83
|
-
vitest_1.vi.mocked(fs.symlinkSync).mockImplementation(function () { return undefined; });
|
|
84
|
-
vitest_1.vi.mocked(fs.cpSync).mockImplementation(function () { return undefined; });
|
|
85
|
-
vitest_1.vi.mocked(fs.rmSync).mockImplementation(function () { return undefined; });
|
|
86
|
-
vitest_1.vi.spyOn(console, "log").mockImplementation(function () { return undefined; });
|
|
87
|
-
vitest_1.vi.spyOn(console, "warn").mockImplementation(function () { return undefined; });
|
|
88
|
-
});
|
|
89
|
-
(0, vitest_1.afterEach)(function () {
|
|
90
|
-
vitest_1.vi.clearAllMocks();
|
|
91
|
-
vitest_1.vi.restoreAllMocks();
|
|
92
|
-
});
|
|
93
|
-
(0, vitest_1.describe)("installFeatures – local skills", function () {
|
|
94
|
-
(0, vitest_1.it)("symlinks skill folders into .agents/skills and .claude/skills", function () {
|
|
95
|
-
mockFilesystem({ listings: { "/proj/skills": ["my-skill"] } });
|
|
96
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
|
|
97
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/skills/my-skill"), path.join("/proj", ".agents", "skills", "my-skill"));
|
|
98
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/skills/my-skill"), path.join("/proj", ".claude", "skills", "my-skill"));
|
|
99
|
-
});
|
|
100
|
-
(0, vitest_1.it)("also installs from agentic-plugin/skills/", function () {
|
|
101
|
-
mockFilesystem({ listings: { "/proj/agentic-plugin/skills": ["plugin-skill"] } });
|
|
102
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
|
|
103
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/agentic-plugin/skills/plugin-skill"), path.join("/proj", ".claude", "skills", "plugin-skill"));
|
|
104
|
-
});
|
|
105
|
-
(0, vitest_1.it)("skills/ takes priority over agentic-plugin/skills/ for same name", function () {
|
|
106
|
-
mockFilesystem({
|
|
107
|
-
listings: {
|
|
108
|
-
"/proj/skills": ["shared"],
|
|
109
|
-
"/proj/agentic-plugin/skills": ["shared"],
|
|
110
|
-
},
|
|
111
|
-
});
|
|
112
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
|
|
113
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/skills/shared"), vitest_1.expect.any(String));
|
|
114
|
-
(0, vitest_1.expect)(fs.symlinkSync).not.toHaveBeenCalledWith(path.resolve("/proj/agentic-plugin/skills/shared"), vitest_1.expect.any(String));
|
|
115
|
-
(0, vitest_1.expect)(console.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('CONFLICT: "shared"'));
|
|
116
|
-
});
|
|
117
|
-
(0, vitest_1.it)("ignores files in skills/ — only subdirectories are skills", function () {
|
|
118
|
-
mockFilesystem({ listings: { "/proj/skills": ["real-skill", "not-a-skill.md"] } });
|
|
119
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
|
|
120
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/skills/real-skill"), vitest_1.expect.any(String));
|
|
121
|
-
(0, vitest_1.expect)(fs.symlinkSync).not.toHaveBeenCalledWith(path.resolve("/proj/skills/not-a-skill.md"), vitest_1.expect.any(String));
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
(0, vitest_1.describe)("installFeatures – local rules", function () {
|
|
125
|
-
(0, vitest_1.it)("symlinks .md files into .agents/rules, .claude/rules, .cursor/rules, and .github/instructions", function () {
|
|
126
|
-
mockFilesystem({ listings: { "/proj/rules": ["my-rule.md"] } });
|
|
127
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
|
|
128
|
-
(0, vitest_1.expect)(fs.mkdirSync).toHaveBeenCalledWith(path.join("/proj", ".agents", "rules"), { recursive: true });
|
|
129
|
-
(0, vitest_1.expect)(fs.mkdirSync).toHaveBeenCalledWith(path.join("/proj", ".claude", "rules"), { recursive: true });
|
|
130
|
-
(0, vitest_1.expect)(fs.mkdirSync).toHaveBeenCalledWith(path.join("/proj", ".cursor", "rules"), { recursive: true });
|
|
131
|
-
(0, vitest_1.expect)(fs.mkdirSync).toHaveBeenCalledWith(path.join("/proj", ".github", "instructions"), { recursive: true });
|
|
132
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/rules/my-rule.md"), path.join("/proj", ".agents", "rules", "my-rule.md"));
|
|
133
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/rules/my-rule.md"), path.join("/proj", ".claude", "rules", "my-rule.md"));
|
|
134
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/rules/my-rule.md"), path.join("/proj", ".cursor", "rules", "my-rule.md"));
|
|
135
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/rules/my-rule.md"), path.join("/proj", ".github", "instructions", "my-rule.md"));
|
|
136
|
-
});
|
|
137
|
-
(0, vitest_1.it)("walks subdirectories recursively, preserving the layout in each target", function () {
|
|
138
|
-
mockFilesystem({
|
|
139
|
-
listings: {
|
|
140
|
-
"/proj/rules": ["top.md", "backend"],
|
|
141
|
-
"/proj/rules/backend": ["api.md", "deeper"],
|
|
142
|
-
"/proj/rules/backend/deeper": ["nested.md"],
|
|
143
|
-
},
|
|
144
|
-
});
|
|
145
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
|
|
146
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/rules/backend/api.md"), path.join("/proj", ".claude", "rules", "backend", "api.md"));
|
|
147
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/rules/backend/deeper/nested.md"), path.join("/proj", ".claude", "rules", "backend", "deeper", "nested.md"));
|
|
148
|
-
(0, vitest_1.expect)(fs.mkdirSync).toHaveBeenCalledWith(path.join("/proj", ".claude", "rules", "backend"), { recursive: true });
|
|
149
|
-
(0, vitest_1.expect)(fs.mkdirSync).toHaveBeenCalledWith(path.join("/proj", ".claude", "rules", "backend", "deeper"), { recursive: true });
|
|
150
|
-
});
|
|
151
|
-
(0, vitest_1.it)("ignores non-.md entries", function () {
|
|
152
|
-
mockFilesystem({ listings: { "/proj/rules": ["rule.md", "README.txt"] } });
|
|
153
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
|
|
154
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/rules/rule.md"), vitest_1.expect.any(String));
|
|
155
|
-
(0, vitest_1.expect)(fs.symlinkSync).not.toHaveBeenCalledWith(path.resolve("/proj/rules/README.txt"), vitest_1.expect.any(String));
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
(0, vitest_1.describe)("installFeatures – cross-namespace", function () {
|
|
159
|
-
(0, vitest_1.it)("a skill and a rule may share a name without conflicting", function () {
|
|
160
|
-
mockFilesystem({
|
|
161
|
-
listings: {
|
|
162
|
-
"/proj/skills": ["shared"],
|
|
163
|
-
"/proj/rules": ["shared.md"],
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
|
|
167
|
-
(0, vitest_1.expect)(console.warn).not.toHaveBeenCalled();
|
|
168
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/skills/shared"), vitest_1.expect.any(String));
|
|
169
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/rules/shared.md"), vitest_1.expect.any(String));
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
(0, vitest_1.describe)("installFeatures – dry-run", function () {
|
|
173
|
-
(0, vitest_1.it)("writes nothing", function () {
|
|
174
|
-
mockFilesystem({
|
|
175
|
-
listings: {
|
|
176
|
-
"/proj/skills": ["my-skill"],
|
|
177
|
-
"/proj/rules": ["my-rule.md"],
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: true });
|
|
181
|
-
(0, vitest_1.expect)(fs.symlinkSync).not.toHaveBeenCalled();
|
|
182
|
-
(0, vitest_1.expect)(fs.cpSync).not.toHaveBeenCalled();
|
|
183
|
-
(0, vitest_1.expect)(fs.rmSync).not.toHaveBeenCalled();
|
|
184
|
-
});
|
|
185
|
-
(0, vitest_1.it)("logs would-be operations", function () {
|
|
186
|
-
mockFilesystem({ listings: { "/proj/skills": ["my-skill"] } });
|
|
187
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: true });
|
|
188
|
-
(0, vitest_1.expect)(console.log).toHaveBeenCalledWith(vitest_1.expect.stringContaining("[dry-run] Would symlink: ".concat(path.resolve("/proj/skills/my-skill"))));
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
(0, vitest_1.describe)("installFeatures – overwrites existing destinations", function () {
|
|
192
|
-
(0, vitest_1.it)("removes the existing destination before re-creating it", function () {
|
|
193
|
-
mockFilesystem({
|
|
194
|
-
listings: { "/proj/skills": ["my-skill"] },
|
|
195
|
-
existingDests: [path.join("/proj", ".agents", "skills", "my-skill"), path.join("/proj", ".claude", "skills", "my-skill")],
|
|
196
|
-
});
|
|
197
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
|
|
198
|
-
(0, vitest_1.expect)(fs.rmSync).toHaveBeenCalledWith(path.join("/proj", ".agents", "skills", "my-skill"), { recursive: true, force: true });
|
|
199
|
-
(0, vitest_1.expect)(fs.rmSync).toHaveBeenCalledWith(path.join("/proj", ".claude", "skills", "my-skill"), { recursive: true, force: true });
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
(0, vitest_1.describe)("installFeatures – internal filtering", function () {
|
|
203
|
-
(0, vitest_1.it)("local sources install internal skills (filterInternal applies only to external repos)", function () {
|
|
204
|
-
mockFilesystem({
|
|
205
|
-
listings: { "/proj/skills": ["public", "internal"] },
|
|
206
|
-
fileContents: { "/proj/skills/internal/SKILL.md": "---\nmetadata:\n internal: true\n---\n# Internal" },
|
|
207
|
-
});
|
|
208
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
|
|
209
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/skills/public"), vitest_1.expect.any(String));
|
|
210
|
-
(0, vitest_1.expect)(fs.symlinkSync).toHaveBeenCalledWith(path.resolve("/proj/skills/internal"), vitest_1.expect.any(String));
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
(0, vitest_1.describe)("installFeatures – missing local sources", function () {
|
|
214
|
-
(0, vitest_1.it)("runs cleanly when there are no local skills or rules", function () {
|
|
215
|
-
mockFilesystem({});
|
|
216
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
|
|
217
|
-
(0, vitest_1.expect)(fs.symlinkSync).not.toHaveBeenCalled();
|
|
218
|
-
(0, vitest_1.expect)(fs.cpSync).not.toHaveBeenCalled();
|
|
219
|
-
(0, vitest_1.expect)(console.log).toHaveBeenCalledWith(vitest_1.expect.stringContaining("No skills found"));
|
|
220
|
-
(0, vitest_1.expect)(console.log).toHaveBeenCalledWith(vitest_1.expect.stringContaining("No rules found"));
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
(0, vitest_1.describe)("installFeatures – EPERM symlink fallback", function () {
|
|
224
|
-
(0, vitest_1.it)("falls back to copy when symlinkSync throws EPERM (e.g. Windows without privileges)", function () {
|
|
225
|
-
mockFilesystem({ listings: { "/proj/skills": ["my-skill"] } });
|
|
226
|
-
vitest_1.vi.mocked(fs.symlinkSync).mockImplementationOnce(function () {
|
|
227
|
-
throw Object.assign(new Error("permission denied"), { code: "EPERM" });
|
|
228
|
-
});
|
|
229
|
-
(0, install_agent_features_1.installFeatures)("/proj", [], { dryRun: false });
|
|
230
|
-
(0, vitest_1.expect)(fs.cpSync).toHaveBeenCalledWith(path.resolve("/proj/skills/my-skill"), path.join("/proj", ".agents", "skills", "my-skill"), {
|
|
231
|
-
recursive: true,
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
var child_process_1 = require("child_process");
|
|
4
|
-
var vitest_1 = require("vitest");
|
|
5
|
-
var site_configs_1 = require("./site-configs");
|
|
6
|
-
vitest_1.vi.mock("child_process", function () { return ({
|
|
7
|
-
execSync: vitest_1.vi.fn(),
|
|
8
|
-
}); });
|
|
9
|
-
var mockedExecSync = vitest_1.vi.mocked(child_process_1.execSync);
|
|
10
|
-
(0, vitest_1.afterEach)(function () {
|
|
11
|
-
vitest_1.vi.clearAllMocks();
|
|
12
|
-
vitest_1.vi.restoreAllMocks();
|
|
13
|
-
});
|
|
14
|
-
(0, vitest_1.describe)("resolveOpReferences", function () {
|
|
15
|
-
(0, vitest_1.it)("should resolve op:// references", function () {
|
|
16
|
-
mockedExecSync.mockImplementation(function (cmd) {
|
|
17
|
-
if (cmd === "op --version") {
|
|
18
|
-
return Buffer.from("2.0.0");
|
|
19
|
-
}
|
|
20
|
-
if (cmd === 'op read "op://vault/item/password"') {
|
|
21
|
-
return "resolved-secret\n";
|
|
22
|
-
}
|
|
23
|
-
return "";
|
|
24
|
-
});
|
|
25
|
-
var result = (0, site_configs_1.resolveOpReferences)('{"key":"{{ op://vault/item/password }}"}');
|
|
26
|
-
(0, vitest_1.expect)(result).toBe('{"key":"resolved-secret"}');
|
|
27
|
-
});
|
|
28
|
-
(0, vitest_1.it)("should throw an error when op CLI is not installed", function () {
|
|
29
|
-
mockedExecSync.mockImplementation(function () {
|
|
30
|
-
throw new Error("command not found: op");
|
|
31
|
-
});
|
|
32
|
-
(0, vitest_1.expect)(function () { return (0, site_configs_1.resolveOpReferences)('{"key":"{{ op://vault/item/password }}"}'); }).toThrow("inject-site-configs: Config contains 1Password references (op://) but the 1Password CLI (op) is not installed");
|
|
33
|
-
});
|
|
34
|
-
(0, vitest_1.it)("should throw an error when op reference resolution fails", function () {
|
|
35
|
-
mockedExecSync.mockImplementation(function (cmd) {
|
|
36
|
-
if (cmd === "op --version") {
|
|
37
|
-
return Buffer.from("2.0.0");
|
|
38
|
-
}
|
|
39
|
-
if (cmd === 'op read "op://vault/item/password"') {
|
|
40
|
-
throw new Error("Item not found");
|
|
41
|
-
}
|
|
42
|
-
return "";
|
|
43
|
-
});
|
|
44
|
-
(0, vitest_1.expect)(function () { return (0, site_configs_1.resolveOpReferences)('{"key":"{{ op://vault/item/password }}"}'); }).toThrow("inject-site-configs: Failed to resolve 1Password reference {{ op://vault/item/password }}");
|
|
45
|
-
});
|
|
46
|
-
(0, vitest_1.it)("should resolve multiple op:// references", function () {
|
|
47
|
-
mockedExecSync.mockImplementation(function (cmd) {
|
|
48
|
-
if (cmd === "op --version") {
|
|
49
|
-
return Buffer.from("2.0.0");
|
|
50
|
-
}
|
|
51
|
-
if (cmd === 'op read "op://vault/item/api-key"') {
|
|
52
|
-
return "resolved-api-key\n";
|
|
53
|
-
}
|
|
54
|
-
if (cmd === 'op read "op://vault/item/api-secret"') {
|
|
55
|
-
return "resolved-api-secret\n";
|
|
56
|
-
}
|
|
57
|
-
if (cmd === 'op read "op://vault/database/password"') {
|
|
58
|
-
return "resolved-db-password\n";
|
|
59
|
-
}
|
|
60
|
-
return "";
|
|
61
|
-
});
|
|
62
|
-
var result = (0, site_configs_1.resolveOpReferences)('{"apiKey":"{{ op://vault/item/api-key }}","apiSecret":"{{ op://vault/item/api-secret }}","dbPassword":"{{ op://vault/database/password }}"}');
|
|
63
|
-
(0, vitest_1.expect)(result).toBe('{"apiKey":"resolved-api-key","apiSecret":"resolved-api-secret","dbPassword":"resolved-db-password"}');
|
|
64
|
-
});
|
|
65
|
-
(0, vitest_1.it)("should not call op CLI when no op:// references are present", function () {
|
|
66
|
-
var result = (0, site_configs_1.resolveOpReferences)('{"key":"plain-value"}');
|
|
67
|
-
(0, vitest_1.expect)(result).toBe('{"key":"plain-value"}');
|
|
68
|
-
(0, vitest_1.expect)(mockedExecSync).not.toHaveBeenCalled();
|
|
69
|
-
});
|
|
70
|
-
});
|