@comet/cli 9.0.0-beta.2 → 9.0.0-beta.4
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/comet.js +2 -2
- package/lib/commands/generate-block-types.js +8 -4
- package/lib/commands/install-agent-features.d.ts +7 -0
- package/lib/commands/install-agent-features.js +339 -0
- package/lib/commands/install-agent-features.test.js +234 -0
- package/lib/commands/site-configs.js +28 -5
- package/lib/commands/site-configs.test.js +14 -7
- package/package.json +6 -5
- package/lib/commands/install-agent-skills.d.ts +0 -15
- package/lib/commands/install-agent-skills.js +0 -244
- package/lib/commands/install-agent-skills.test.js +0 -278
- /package/lib/commands/{install-agent-skills.test.d.ts → install-agent-features.test.d.ts} +0 -0
package/lib/comet.js
CHANGED
|
@@ -4,12 +4,12 @@ var commander_1 = require("commander");
|
|
|
4
4
|
var download_mitmproxy_1 = require("./commands/download-mitmproxy");
|
|
5
5
|
var download_oauth2_proxy_1 = require("./commands/download-oauth2-proxy");
|
|
6
6
|
var generate_block_types_1 = require("./commands/generate-block-types");
|
|
7
|
-
var
|
|
7
|
+
var install_agent_features_1 = require("./commands/install-agent-features");
|
|
8
8
|
var site_configs_1 = require("./commands/site-configs");
|
|
9
9
|
var program = new commander_1.Command();
|
|
10
10
|
program.addCommand(generate_block_types_1.generateBlockTypes);
|
|
11
11
|
program.addCommand(site_configs_1.injectSiteConfigsCommand);
|
|
12
12
|
program.addCommand(download_oauth2_proxy_1.downloadOAuth2ProxyCommand);
|
|
13
13
|
program.addCommand(download_mitmproxy_1.downloadMitmproxyCommand);
|
|
14
|
-
program.addCommand(
|
|
14
|
+
program.addCommand(install_agent_features_1.installAgentFeaturesCommand);
|
|
15
15
|
program.parse();
|
|
@@ -100,8 +100,9 @@ function writeFieldType(field, blockNamePostfix) {
|
|
|
100
100
|
content += "{\n";
|
|
101
101
|
field.object.fields.forEach(function (f) {
|
|
102
102
|
content += f.name;
|
|
103
|
-
if (f.nullable)
|
|
103
|
+
if (f.nullable) {
|
|
104
104
|
content += "?";
|
|
105
|
+
}
|
|
105
106
|
content += ": ";
|
|
106
107
|
writeFieldType(f, blockNamePostfix);
|
|
107
108
|
content += ";\n";
|
|
@@ -112,8 +113,9 @@ function writeFieldType(field, blockNamePostfix) {
|
|
|
112
113
|
content += "Array<{\n";
|
|
113
114
|
field.object.fields.forEach(function (f) {
|
|
114
115
|
content += f.name;
|
|
115
|
-
if (f.nullable)
|
|
116
|
+
if (f.nullable) {
|
|
116
117
|
content += "?";
|
|
118
|
+
}
|
|
117
119
|
content += ": ";
|
|
118
120
|
writeFieldType(f, blockNamePostfix);
|
|
119
121
|
content += ";\n";
|
|
@@ -146,8 +148,9 @@ var generateBlockTypes = new commander_1.Command("generate-block-types")
|
|
|
146
148
|
content += "export interface ".concat(block.name, "BlockData {\n");
|
|
147
149
|
block.fields.forEach(function (field) {
|
|
148
150
|
content += field.name;
|
|
149
|
-
if (field.nullable)
|
|
151
|
+
if (field.nullable) {
|
|
150
152
|
content += "?";
|
|
153
|
+
}
|
|
151
154
|
content += ": ";
|
|
152
155
|
writeFieldType(field, "BlockData");
|
|
153
156
|
content += ";\n";
|
|
@@ -159,8 +162,9 @@ var generateBlockTypes = new commander_1.Command("generate-block-types")
|
|
|
159
162
|
content += "export interface ".concat(block.name, "BlockInput {\n");
|
|
160
163
|
block.inputFields.forEach(function (field) {
|
|
161
164
|
content += field.name;
|
|
162
|
-
if (field.nullable)
|
|
165
|
+
if (field.nullable) {
|
|
163
166
|
content += "?";
|
|
167
|
+
}
|
|
164
168
|
content += ": ";
|
|
165
169
|
writeFieldType(field, "BlockInput");
|
|
166
170
|
content += ";\n";
|
|
@@ -0,0 +1,339 @@
|
|
|
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
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
45
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
46
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
47
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
48
|
+
function step(op) {
|
|
49
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
50
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
51
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
52
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
53
|
+
switch (op[0]) {
|
|
54
|
+
case 0: case 1: t = op; break;
|
|
55
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
56
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
57
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
58
|
+
default:
|
|
59
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
60
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
61
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
62
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
63
|
+
if (t[2]) _.ops.pop();
|
|
64
|
+
_.trys.pop(); continue;
|
|
65
|
+
}
|
|
66
|
+
op = body.call(thisArg, _);
|
|
67
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
68
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
72
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
73
|
+
if (ar || !(i in from)) {
|
|
74
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
75
|
+
ar[i] = from[i];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
79
|
+
};
|
|
80
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
81
|
+
exports.installAgentFeaturesCommand = void 0;
|
|
82
|
+
exports.installFeatures = installFeatures;
|
|
83
|
+
/* eslint-disable no-console */
|
|
84
|
+
var child_process_1 = require("child_process");
|
|
85
|
+
var commander_1 = require("commander");
|
|
86
|
+
var fs = __importStar(require("fs"));
|
|
87
|
+
var yaml = __importStar(require("js-yaml"));
|
|
88
|
+
var os = __importStar(require("os"));
|
|
89
|
+
var path = __importStar(require("path"));
|
|
90
|
+
var SKILL_SOURCE_PATHS = ["skills", "agentic-plugin/skills"];
|
|
91
|
+
var RULE_SOURCE_PATHS = ["rules"];
|
|
92
|
+
function parseRepoUrl(rawUrl) {
|
|
93
|
+
var hashIndex = rawUrl.lastIndexOf("#");
|
|
94
|
+
if (hashIndex === -1) {
|
|
95
|
+
return { repoUrl: rawUrl, ref: undefined };
|
|
96
|
+
}
|
|
97
|
+
return { repoUrl: rawUrl.slice(0, hashIndex), ref: rawUrl.slice(hashIndex + 1) || undefined };
|
|
98
|
+
}
|
|
99
|
+
function cloneRepo(rawUrl, sparsePatterns) {
|
|
100
|
+
var _a = parseRepoUrl(rawUrl), repoUrl = _a.repoUrl, ref = _a.ref;
|
|
101
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "comet-agent-install-"));
|
|
102
|
+
(0, child_process_1.execFileSync)("git", ["init", tmpDir], { stdio: "pipe" });
|
|
103
|
+
(0, child_process_1.execFileSync)("git", ["-C", tmpDir, "remote", "add", "origin", "--", repoUrl], { stdio: "pipe" });
|
|
104
|
+
(0, child_process_1.execFileSync)("git", ["-C", tmpDir, "config", "core.sparseCheckout", "true"], { stdio: "pipe" });
|
|
105
|
+
fs.writeFileSync(path.join(tmpDir, ".git", "info", "sparse-checkout"), sparsePatterns.map(function (p) { return "".concat(p, "\n"); }).join(""));
|
|
106
|
+
var fetchRef = ref !== null && ref !== void 0 ? ref : "HEAD";
|
|
107
|
+
try {
|
|
108
|
+
console.log("Fetching ".concat(repoUrl, " ref \"").concat(fetchRef, "\" (sparse: ").concat(sparsePatterns.join(", "), ")..."));
|
|
109
|
+
(0, child_process_1.execFileSync)("git", ["-C", tmpDir, "fetch", "--depth", "1", "origin", fetchRef], { stdio: "pipe" });
|
|
110
|
+
(0, child_process_1.execFileSync)("git", ["-C", tmpDir, "checkout", "FETCH_HEAD"], { stdio: "pipe" });
|
|
111
|
+
}
|
|
112
|
+
catch (_b) {
|
|
113
|
+
console.log("Shallow fetch failed for ref \"".concat(fetchRef, "\"; falling back to full fetch..."));
|
|
114
|
+
(0, child_process_1.execFileSync)("git", ["-C", tmpDir, "fetch", "origin", fetchRef], { stdio: "pipe" });
|
|
115
|
+
(0, child_process_1.execFileSync)("git", ["-C", tmpDir, "checkout", "FETCH_HEAD"], { stdio: "pipe" });
|
|
116
|
+
}
|
|
117
|
+
return tmpDir;
|
|
118
|
+
}
|
|
119
|
+
function pathExists(p) {
|
|
120
|
+
try {
|
|
121
|
+
fs.lstatSync(p);
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
catch (_a) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function isInternalMarkdown(mdPath) {
|
|
129
|
+
var _a;
|
|
130
|
+
var content;
|
|
131
|
+
try {
|
|
132
|
+
content = fs.readFileSync(mdPath, "utf-8");
|
|
133
|
+
}
|
|
134
|
+
catch (_b) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
var match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
138
|
+
if (!match) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
var parsed = yaml.load(match[1]);
|
|
143
|
+
return ((_a = parsed === null || parsed === void 0 ? void 0 : parsed.metadata) === null || _a === void 0 ? void 0 : _a.internal) === true;
|
|
144
|
+
}
|
|
145
|
+
catch (_c) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function listFolders(directory, filterInternal) {
|
|
150
|
+
return fs
|
|
151
|
+
.readdirSync(directory)
|
|
152
|
+
.filter(function (entry) { return fs.statSync(path.join(directory, entry)).isDirectory(); })
|
|
153
|
+
.filter(function (folder) { return !filterInternal || !isInternalMarkdown(path.join(directory, folder, "SKILL.md")); })
|
|
154
|
+
.sort();
|
|
155
|
+
}
|
|
156
|
+
function listMarkdownFiles(directory, filterInternal) {
|
|
157
|
+
var results = [];
|
|
158
|
+
var walk = function (currentDir, relativePath) {
|
|
159
|
+
for (var _i = 0, _a = fs.readdirSync(currentDir); _i < _a.length; _i++) {
|
|
160
|
+
var entry = _a[_i];
|
|
161
|
+
var fullPath = path.join(currentDir, entry);
|
|
162
|
+
var relPath = relativePath ? path.join(relativePath, entry) : entry;
|
|
163
|
+
var stat = fs.statSync(fullPath);
|
|
164
|
+
if (stat.isDirectory()) {
|
|
165
|
+
walk(fullPath, relPath);
|
|
166
|
+
}
|
|
167
|
+
else if (stat.isFile() && entry.endsWith(".md")) {
|
|
168
|
+
if (!filterInternal || !isInternalMarkdown(fullPath)) {
|
|
169
|
+
results.push(relPath);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
walk(directory, "");
|
|
175
|
+
return results.sort();
|
|
176
|
+
}
|
|
177
|
+
function symlinkOrCopy(src, dest, symlink) {
|
|
178
|
+
if (symlink) {
|
|
179
|
+
try {
|
|
180
|
+
fs.symlinkSync(src, dest);
|
|
181
|
+
return "symlinked";
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
if (err.code !== "EPERM") {
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
// Windows without symlink privilege — fall back to copy
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
191
|
+
return "copied";
|
|
192
|
+
}
|
|
193
|
+
function installItems(sources, targetDirs, _a) {
|
|
194
|
+
var dryRun = _a.dryRun;
|
|
195
|
+
var totalInstalled = 0;
|
|
196
|
+
for (var _i = 0, sources_1 = sources; _i < sources_1.length; _i++) {
|
|
197
|
+
var source = sources_1[_i];
|
|
198
|
+
var label = source.label, directory = source.directory, discover = source.discover, symlink = source.symlink, filterInternal = source.filterInternal, installed = source.installed, itemLabelSingular = source.itemLabelSingular, itemLabelPlural = source.itemLabelPlural;
|
|
199
|
+
if (!fs.existsSync(directory)) {
|
|
200
|
+
console.log("No ".concat(itemLabelPlural, " found in ").concat(label));
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
var items = discover === "folders" ? listFolders(directory, filterInternal !== null && filterInternal !== void 0 ? filterInternal : false) : listMarkdownFiles(directory, filterInternal !== null && filterInternal !== void 0 ? filterInternal : false);
|
|
204
|
+
if (items.length === 0) {
|
|
205
|
+
console.log("No ".concat(itemLabelPlural, " found in ").concat(label));
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
console.log("Installing ".concat(items.length, " ").concat(items.length === 1 ? itemLabelSingular : itemLabelPlural, " from ").concat(label, "..."));
|
|
209
|
+
for (var _b = 0, items_1 = items; _b < items_1.length; _b++) {
|
|
210
|
+
var item = items_1[_b];
|
|
211
|
+
if (installed.has(item)) {
|
|
212
|
+
console.warn(" CONFLICT: \"".concat(item, "\" from ").concat(label, " skipped (already installed from a higher-priority source)"));
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
var srcPath = path.resolve(path.join(directory, item));
|
|
216
|
+
for (var _c = 0, targetDirs_1 = targetDirs; _c < targetDirs_1.length; _c++) {
|
|
217
|
+
var targetDir = targetDirs_1[_c];
|
|
218
|
+
var destPath = path.join(targetDir, item);
|
|
219
|
+
if (dryRun) {
|
|
220
|
+
console.log(" [dry-run] Would ".concat(symlink ? "symlink" : "copy", ": ").concat(srcPath, " -> ").concat(destPath));
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
if (pathExists(destPath)) {
|
|
224
|
+
fs.rmSync(destPath, { recursive: true, force: true });
|
|
225
|
+
}
|
|
226
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
227
|
+
var action = symlinkOrCopy(srcPath, destPath, symlink);
|
|
228
|
+
console.log(" ".concat(action === "symlinked" ? "Symlinked" : "Copied", ": ").concat(item));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
installed.add(item);
|
|
232
|
+
totalInstalled++;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return totalInstalled;
|
|
236
|
+
}
|
|
237
|
+
function loadConfig(configPath) {
|
|
238
|
+
var resolved = path.resolve(configPath);
|
|
239
|
+
if (!fs.existsSync(resolved)) {
|
|
240
|
+
throw new Error("Config file not found: ".concat(resolved));
|
|
241
|
+
}
|
|
242
|
+
var raw = fs.readFileSync(resolved, "utf-8");
|
|
243
|
+
return JSON.parse(raw);
|
|
244
|
+
}
|
|
245
|
+
function installFeatures(cwd, repos, options) {
|
|
246
|
+
var skillsTargetDirs = [path.join(cwd, ".agents", "skills"), path.join(cwd, ".claude", "skills")];
|
|
247
|
+
var rulesTargetDirs = [
|
|
248
|
+
path.join(cwd, ".agents", "rules"),
|
|
249
|
+
path.join(cwd, ".claude", "rules"),
|
|
250
|
+
path.join(cwd, ".cursor", "rules"),
|
|
251
|
+
path.join(cwd, ".github", "instructions"),
|
|
252
|
+
];
|
|
253
|
+
for (var _i = 0, _a = __spreadArray(__spreadArray([], skillsTargetDirs, true), rulesTargetDirs, true); _i < _a.length; _i++) {
|
|
254
|
+
var dir = _a[_i];
|
|
255
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
256
|
+
}
|
|
257
|
+
var installedSkills = new Set();
|
|
258
|
+
var installedRules = new Set();
|
|
259
|
+
var skillSources = SKILL_SOURCE_PATHS.map(function (p) { return ({
|
|
260
|
+
label: "local ".concat(p, "/"),
|
|
261
|
+
directory: path.join(cwd, p),
|
|
262
|
+
discover: "folders",
|
|
263
|
+
symlink: true,
|
|
264
|
+
installed: installedSkills,
|
|
265
|
+
itemLabelSingular: "skill",
|
|
266
|
+
itemLabelPlural: "skills",
|
|
267
|
+
}); });
|
|
268
|
+
var ruleSources = RULE_SOURCE_PATHS.map(function (p) { return ({
|
|
269
|
+
label: "local ".concat(p, "/"),
|
|
270
|
+
directory: path.join(cwd, p),
|
|
271
|
+
discover: "files",
|
|
272
|
+
symlink: true,
|
|
273
|
+
installed: installedRules,
|
|
274
|
+
itemLabelSingular: "rule",
|
|
275
|
+
itemLabelPlural: "rules",
|
|
276
|
+
}); });
|
|
277
|
+
var sparsePatterns = __spreadArray(__spreadArray([], SKILL_SOURCE_PATHS, true), RULE_SOURCE_PATHS, true).map(function (p) { return "".concat(p, "/"); });
|
|
278
|
+
var tempDirs = [];
|
|
279
|
+
try {
|
|
280
|
+
for (var _b = 0, repos_1 = repos; _b < repos_1.length; _b++) {
|
|
281
|
+
var rawUrl = repos_1[_b];
|
|
282
|
+
var cloneDir = cloneRepo(rawUrl, sparsePatterns);
|
|
283
|
+
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];
|
|
286
|
+
skillSources.push({
|
|
287
|
+
label: "external ".concat(rawUrl, " (").concat(p, "/)"),
|
|
288
|
+
directory: path.join(cloneDir, p),
|
|
289
|
+
discover: "folders",
|
|
290
|
+
symlink: false,
|
|
291
|
+
filterInternal: true,
|
|
292
|
+
installed: installedSkills,
|
|
293
|
+
itemLabelSingular: "skill",
|
|
294
|
+
itemLabelPlural: "skills",
|
|
295
|
+
});
|
|
296
|
+
}
|
|
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];
|
|
299
|
+
ruleSources.push({
|
|
300
|
+
label: "external ".concat(rawUrl, " (").concat(p, "/)"),
|
|
301
|
+
directory: path.join(cloneDir, p),
|
|
302
|
+
discover: "files",
|
|
303
|
+
symlink: false,
|
|
304
|
+
filterInternal: true,
|
|
305
|
+
installed: installedRules,
|
|
306
|
+
itemLabelSingular: "rule",
|
|
307
|
+
itemLabelPlural: "rules",
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
var skillCount = installItems(skillSources, skillsTargetDirs, options);
|
|
312
|
+
var ruleCount = installItems(ruleSources, rulesTargetDirs, options);
|
|
313
|
+
console.log("\nTotal skills installed: ".concat(skillCount));
|
|
314
|
+
console.log("Total rules installed: ".concat(ruleCount));
|
|
315
|
+
}
|
|
316
|
+
finally {
|
|
317
|
+
for (var _e = 0, tempDirs_1 = tempDirs; _e < tempDirs_1.length; _e++) {
|
|
318
|
+
var tmpDir = tempDirs_1[_e];
|
|
319
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
exports.installAgentFeaturesCommand = new commander_1.Command("install-agent-features")
|
|
324
|
+
.description("Install agent features (skills and rules) from local directories and optional external git repos")
|
|
325
|
+
.option("--config <path>", "Path to a JSON config file specifying repos to install features from", "agent-features.json")
|
|
326
|
+
.option("--dry-run", "Show which symlinks/copies would be created without making changes", false)
|
|
327
|
+
.action(function (options) { return __awaiter(void 0, void 0, void 0, function () {
|
|
328
|
+
var configPath, dryRun, resolvedConfig, repos;
|
|
329
|
+
var _a;
|
|
330
|
+
return __generator(this, function (_b) {
|
|
331
|
+
configPath = options.config, dryRun = options.dryRun;
|
|
332
|
+
resolvedConfig = path.resolve(configPath);
|
|
333
|
+
repos = fs.existsSync(resolvedConfig) ? ((_a = loadConfig(configPath).repos) !== null && _a !== void 0 ? _a : []) : [];
|
|
334
|
+
console.log("=== Installing agent features".concat(dryRun ? " (dry run)" : "", " ==="));
|
|
335
|
+
installFeatures(process.cwd(), repos, { dryRun: dryRun });
|
|
336
|
+
console.log("=== Finished ===");
|
|
337
|
+
return [2 /*return*/];
|
|
338
|
+
});
|
|
339
|
+
}); });
|
|
@@ -0,0 +1,234 @@
|
|
|
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
|
+
});
|