@mhmdreza-rafiei/agentry 0.2.2 → 0.3.1
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/README.md +62 -21
- package/dist/_chunks/discovery.mjs +614 -0
- package/dist/_chunks/theme2.mjs +16 -20
- package/dist/_chunks/web.mjs +111 -0
- package/dist/cli.mjs +84 -413
- package/package.json +1 -1
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
4
|
+
import { homedir, tmpdir } from "node:os";
|
|
5
|
+
import { basename, join, relative, resolve, sep } from "node:path";
|
|
6
|
+
import { xdgCache } from "xdg-basedir";
|
|
7
|
+
function isLocalPath(source) {
|
|
8
|
+
if (!source) return false;
|
|
9
|
+
if (source === "." || source === "..") return true;
|
|
10
|
+
if (source.startsWith("./") || source.startsWith("../") || source.startsWith("/") || source.startsWith("~")) return true;
|
|
11
|
+
try {
|
|
12
|
+
return existsSync(source) && statSync(source).isDirectory();
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const SAFE_REF$1 = /^[a-zA-Z0-9][\w./-]*$/;
|
|
18
|
+
function parseGithubTreeUrl(url) {
|
|
19
|
+
const m = url.match(/^https?:\/\/github\.com\/([\w.-]+\/[\w.-]+?)\/tree\/([^/]+)\/(.+)$/);
|
|
20
|
+
if (!m) return null;
|
|
21
|
+
const ref = m[2];
|
|
22
|
+
if (!SAFE_REF$1.test(ref)) throw new Error(`Invalid git ref in source URL: "${ref}"`);
|
|
23
|
+
return {
|
|
24
|
+
ownerRepo: m[1],
|
|
25
|
+
ref,
|
|
26
|
+
subpath: m[3].replace(/\/+$/, "")
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function redactUrl(url) {
|
|
30
|
+
return url.replace(/:\/\/([^/@]+)@/g, "://***@");
|
|
31
|
+
}
|
|
32
|
+
function parseSource(source) {
|
|
33
|
+
if (!source || typeof source !== "string") throw new Error("Empty source.");
|
|
34
|
+
const raw = source;
|
|
35
|
+
if (isLocalPath(source)) return {
|
|
36
|
+
kind: "local",
|
|
37
|
+
raw,
|
|
38
|
+
url: null,
|
|
39
|
+
subpath: "",
|
|
40
|
+
local: resolve(source.replace(/^~/, homedir()))
|
|
41
|
+
};
|
|
42
|
+
const tree = parseGithubTreeUrl(source);
|
|
43
|
+
if (tree) return {
|
|
44
|
+
kind: "github-url",
|
|
45
|
+
raw,
|
|
46
|
+
url: `https://github.com/${tree.ownerRepo}.git`,
|
|
47
|
+
subpath: tree.subpath,
|
|
48
|
+
ref: tree.ref,
|
|
49
|
+
local: null
|
|
50
|
+
};
|
|
51
|
+
let m = source.match(/^https?:\/\/github\.com\/([\w.-]+\/[\w.-]+?)(?:\.git)?\/?$/);
|
|
52
|
+
if (m) return {
|
|
53
|
+
kind: "github-url",
|
|
54
|
+
raw,
|
|
55
|
+
url: `https://github.com/${m[1]}.git`,
|
|
56
|
+
subpath: "",
|
|
57
|
+
local: null
|
|
58
|
+
};
|
|
59
|
+
m = source.match(/^https?:\/\/gitlab\.com\/([\w./-]+?)(?:\.git)?\/?$/);
|
|
60
|
+
if (m) return {
|
|
61
|
+
kind: "gitlab-url",
|
|
62
|
+
raw,
|
|
63
|
+
url: `https://gitlab.com/${m[1]}.git`,
|
|
64
|
+
subpath: "",
|
|
65
|
+
local: null
|
|
66
|
+
};
|
|
67
|
+
if (source.startsWith("git@") || /^https?:\/\/.*\.git$/.test(source)) return {
|
|
68
|
+
kind: "git-url",
|
|
69
|
+
raw,
|
|
70
|
+
url: source,
|
|
71
|
+
subpath: "",
|
|
72
|
+
local: null
|
|
73
|
+
};
|
|
74
|
+
if (/^[\w.-]+\/[\w.-]+$/.test(source)) return {
|
|
75
|
+
kind: "github-shorthand",
|
|
76
|
+
raw,
|
|
77
|
+
url: `https://github.com/${source}.git`,
|
|
78
|
+
subpath: "",
|
|
79
|
+
local: null
|
|
80
|
+
};
|
|
81
|
+
if (/^https?:\/\//i.test(source)) return {
|
|
82
|
+
kind: "web-url",
|
|
83
|
+
raw,
|
|
84
|
+
url: source,
|
|
85
|
+
subpath: "",
|
|
86
|
+
local: null
|
|
87
|
+
};
|
|
88
|
+
throw new Error(`Cannot parse source "${source}". Use author/repo, a GitHub/GitLab URL, a git URL, a skill website URL, or a local path.`);
|
|
89
|
+
}
|
|
90
|
+
function sourceIdentity(source) {
|
|
91
|
+
try {
|
|
92
|
+
const p = parseSource(source);
|
|
93
|
+
if (p.kind === "local" && p.local) return `local:${resolve(p.local).replace(/\\/g, "/").toLowerCase()}`;
|
|
94
|
+
if (p.kind === "web-url") return `web:${(p.url || "").toLowerCase()}`;
|
|
95
|
+
return `git:${(p.url || "").replace(/\.git$/i, "").replace(/\/+$/, "").toLowerCase()}${p.subpath ? `/${p.subpath.replace(/^\/+|\/+$/g, "")}` : ""}${p.ref ? `@${p.ref}` : ""}`;
|
|
96
|
+
} catch {
|
|
97
|
+
return `raw:${source.trim().toLowerCase()}`;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function sourcesEqual(a, b) {
|
|
101
|
+
if (!a || !b) return false;
|
|
102
|
+
if (a === b) return true;
|
|
103
|
+
return sourceIdentity(a) === sourceIdentity(b);
|
|
104
|
+
}
|
|
105
|
+
function isExplicitSource(arg) {
|
|
106
|
+
if (!arg) return false;
|
|
107
|
+
if (isLocalPath(arg)) return true;
|
|
108
|
+
if (/^https?:\/\//i.test(arg)) return true;
|
|
109
|
+
if (arg.startsWith("git@")) return true;
|
|
110
|
+
if (/^https?:\/\/.*\.git$/i.test(arg)) return true;
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
function isOwnerRepoShape(arg) {
|
|
114
|
+
return /^[\w.-]+\/[\w.-]+$/.test(arg);
|
|
115
|
+
}
|
|
116
|
+
function cacheRoot() {
|
|
117
|
+
return join(xdgCache ?? join(homedir(), ".cache"), "agentry", "repos");
|
|
118
|
+
}
|
|
119
|
+
function cacheKey(parsed) {
|
|
120
|
+
const id = sourceIdentity(parsed.raw);
|
|
121
|
+
return createHash("sha256").update(id).digest("hex").slice(0, 20);
|
|
122
|
+
}
|
|
123
|
+
function cacheDirFor(parsed) {
|
|
124
|
+
return join(cacheRoot(), cacheKey(parsed));
|
|
125
|
+
}
|
|
126
|
+
function saveToCache(parsed, clonedRoot) {
|
|
127
|
+
const dest = cacheDirFor(parsed);
|
|
128
|
+
try {
|
|
129
|
+
rmSync(dest, {
|
|
130
|
+
recursive: true,
|
|
131
|
+
force: true
|
|
132
|
+
});
|
|
133
|
+
mkdirSync(join(dest, ".."), { recursive: true });
|
|
134
|
+
cpSync(clonedRoot, dest, { recursive: true });
|
|
135
|
+
} catch {}
|
|
136
|
+
}
|
|
137
|
+
const SAFE_REF = /^[a-zA-Z0-9][\w./-]*$/;
|
|
138
|
+
function cloneTo(url, dest, ref) {
|
|
139
|
+
if (ref && !SAFE_REF.test(ref)) throw new Error(`Invalid git ref: "${ref}"`);
|
|
140
|
+
execFileSync("git", [
|
|
141
|
+
"clone",
|
|
142
|
+
"--quiet",
|
|
143
|
+
...ref ? [
|
|
144
|
+
"--branch",
|
|
145
|
+
ref,
|
|
146
|
+
"--depth",
|
|
147
|
+
"1"
|
|
148
|
+
] : ["--depth", "1"],
|
|
149
|
+
url,
|
|
150
|
+
dest
|
|
151
|
+
], { stdio: [
|
|
152
|
+
"ignore",
|
|
153
|
+
"ignore",
|
|
154
|
+
"pipe"
|
|
155
|
+
] });
|
|
156
|
+
}
|
|
157
|
+
function assertInsideRoot(root, candidate) {
|
|
158
|
+
const base = resolve(root);
|
|
159
|
+
const resolved = resolve(candidate);
|
|
160
|
+
const prefix = base.endsWith(sep) ? base : base + sep;
|
|
161
|
+
const a = resolved.toLowerCase();
|
|
162
|
+
const b = base.toLowerCase();
|
|
163
|
+
const p = prefix.toLowerCase();
|
|
164
|
+
if (a !== b && !a.startsWith(p)) throw new Error(`Path escapes source root: "${candidate}"`);
|
|
165
|
+
return resolved;
|
|
166
|
+
}
|
|
167
|
+
function withSubpath(root, subpath, cleanupRoot) {
|
|
168
|
+
const cleanup = () => rmSync(cleanupRoot, {
|
|
169
|
+
recursive: true,
|
|
170
|
+
force: true
|
|
171
|
+
});
|
|
172
|
+
if (!subpath) return {
|
|
173
|
+
root,
|
|
174
|
+
cleanup
|
|
175
|
+
};
|
|
176
|
+
let sub;
|
|
177
|
+
try {
|
|
178
|
+
sub = assertInsideRoot(root, join(root, subpath));
|
|
179
|
+
} catch (e) {
|
|
180
|
+
cleanup();
|
|
181
|
+
throw e;
|
|
182
|
+
}
|
|
183
|
+
if (!existsSync(sub)) {
|
|
184
|
+
cleanup();
|
|
185
|
+
throw new Error(`Subpath "${subpath}" not found.`);
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
root: sub,
|
|
189
|
+
cleanup
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async function resolveSource(source, opts = {}) {
|
|
193
|
+
const parsed = typeof source === "string" ? parseSource(source) : source;
|
|
194
|
+
if (parsed.kind === "local" && parsed.local) {
|
|
195
|
+
if (!existsSync(parsed.local)) throw new Error(`Path not found: ${parsed.raw}`);
|
|
196
|
+
return {
|
|
197
|
+
root: parsed.local,
|
|
198
|
+
cleanup: () => {}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
if (parsed.kind === "web-url" && parsed.url) {
|
|
202
|
+
const { resolveWebSource } = await import("./web.mjs");
|
|
203
|
+
return resolveWebSource(parsed.url, opts);
|
|
204
|
+
}
|
|
205
|
+
const url = parsed.url;
|
|
206
|
+
if (!url) throw new Error(`Cannot resolve source "${parsed.raw}".`);
|
|
207
|
+
const safeUrl = redactUrl(url);
|
|
208
|
+
const tmp = mkdtempSync(join(tmpdir(), "agentry-src-"));
|
|
209
|
+
try {
|
|
210
|
+
cloneTo(url, tmp, parsed.ref);
|
|
211
|
+
} catch (e) {
|
|
212
|
+
rmSync(tmp, {
|
|
213
|
+
recursive: true,
|
|
214
|
+
force: true
|
|
215
|
+
});
|
|
216
|
+
const detail = e.stderr ? e.stderr.toString().trim() : e.message;
|
|
217
|
+
const cached = cacheDirFor(parsed);
|
|
218
|
+
const cacheOk = existsSync(cached) && existsSync(join(cached, ".git"));
|
|
219
|
+
if (cacheOk && opts.confirmCache) {
|
|
220
|
+
if (await opts.confirmCache({
|
|
221
|
+
url: safeUrl,
|
|
222
|
+
cacheDir: cached,
|
|
223
|
+
error: detail
|
|
224
|
+
})) {
|
|
225
|
+
const work = mkdtempSync(join(tmpdir(), "agentry-src-"));
|
|
226
|
+
cpSync(cached, work, { recursive: true });
|
|
227
|
+
return {
|
|
228
|
+
...withSubpath(work, parsed.subpath, work),
|
|
229
|
+
fromCache: true
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
throw new Error(`git clone failed for ${safeUrl}\n${detail}${cacheOk ? "\n(A local cache exists — re-run interactively to load it, or pass -y to auto-use cache.)" : ""}`);
|
|
234
|
+
}
|
|
235
|
+
saveToCache(parsed, tmp);
|
|
236
|
+
return withSubpath(tmp, parsed.subpath, tmp);
|
|
237
|
+
}
|
|
238
|
+
const DEFAULT_IGNORES = [
|
|
239
|
+
".git",
|
|
240
|
+
".github",
|
|
241
|
+
"node_modules",
|
|
242
|
+
".agents",
|
|
243
|
+
".agentry",
|
|
244
|
+
".cursor",
|
|
245
|
+
".claude",
|
|
246
|
+
".vscode",
|
|
247
|
+
"dist",
|
|
248
|
+
"build",
|
|
249
|
+
".DS_Store"
|
|
250
|
+
];
|
|
251
|
+
function parseIgnorePattern(pattern) {
|
|
252
|
+
const trimmed = pattern.trim();
|
|
253
|
+
if (!trimmed || trimmed.startsWith("#")) return null;
|
|
254
|
+
const isDirOnly = trimmed.endsWith("/");
|
|
255
|
+
let p = isDirOnly ? trimmed.slice(0, -1) : trimmed;
|
|
256
|
+
const isRootAnchored = p.startsWith("/");
|
|
257
|
+
if (isRootAnchored) p = p.slice(1);
|
|
258
|
+
let regexStr = "";
|
|
259
|
+
for (let i = 0; i < p.length; i++) {
|
|
260
|
+
const char = p[i];
|
|
261
|
+
if (char === "*") if (p[i + 1] === "*") {
|
|
262
|
+
regexStr += ".*";
|
|
263
|
+
i++;
|
|
264
|
+
if (p[i + 1] === "/") i++;
|
|
265
|
+
} else regexStr += "[^/]*";
|
|
266
|
+
else if (char === "?") regexStr += "[^/]";
|
|
267
|
+
else if ([
|
|
268
|
+
".",
|
|
269
|
+
"+",
|
|
270
|
+
"^",
|
|
271
|
+
"$",
|
|
272
|
+
"{",
|
|
273
|
+
"}",
|
|
274
|
+
"(",
|
|
275
|
+
")",
|
|
276
|
+
"|",
|
|
277
|
+
"[",
|
|
278
|
+
"]",
|
|
279
|
+
"\\"
|
|
280
|
+
].includes(char)) regexStr += "\\" + char;
|
|
281
|
+
else regexStr += char;
|
|
282
|
+
}
|
|
283
|
+
let regex;
|
|
284
|
+
if (isRootAnchored) regex = new RegExp(`^${regexStr}(/.*)?$`, "i");
|
|
285
|
+
else if (p.includes("/")) regex = new RegExp(`(^|/)${regexStr}(/.*)?$`, "i");
|
|
286
|
+
else regex = new RegExp(`(^|/)${regexStr}($|/)`, "i");
|
|
287
|
+
return {
|
|
288
|
+
raw: trimmed,
|
|
289
|
+
isDirOnly,
|
|
290
|
+
regex
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function parseIgnoreFile(content) {
|
|
294
|
+
return content.split(/\r?\n/).map((line) => parseIgnorePattern(line)).filter((r) => r !== null);
|
|
295
|
+
}
|
|
296
|
+
function loadIgnoreRules(root) {
|
|
297
|
+
const candidates = [".agentryignore", ".agentry.ignore"];
|
|
298
|
+
const rules = [];
|
|
299
|
+
for (const name of candidates) {
|
|
300
|
+
const file = join(root, name);
|
|
301
|
+
if (existsSync(file)) try {
|
|
302
|
+
const text = readFileSync(file, "utf8");
|
|
303
|
+
rules.push(...parseIgnoreFile(text));
|
|
304
|
+
} catch {}
|
|
305
|
+
}
|
|
306
|
+
return rules;
|
|
307
|
+
}
|
|
308
|
+
function isDefaultIgnored(relPath) {
|
|
309
|
+
return relPath.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "").split("/").some((s) => DEFAULT_IGNORES.includes(s));
|
|
310
|
+
}
|
|
311
|
+
function createIgnoreMatcher(root) {
|
|
312
|
+
const fileRules = loadIgnoreRules(root);
|
|
313
|
+
return (relPath, isDirectory = false) => {
|
|
314
|
+
if (!relPath || relPath === "." || relPath === "./") return false;
|
|
315
|
+
const normalized = relPath.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
|
|
316
|
+
if (!normalized) return false;
|
|
317
|
+
if (isDefaultIgnored(normalized)) return true;
|
|
318
|
+
for (const rule of fileRules) {
|
|
319
|
+
if (rule.isDirOnly && !isDirectory) {
|
|
320
|
+
if (rule.regex.test(normalized + "/")) return true;
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (rule.regex.test(normalized)) return true;
|
|
324
|
+
}
|
|
325
|
+
return false;
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
const SKILL_MARKER = "SKILL.md";
|
|
329
|
+
const AGENT_MARKER = "AGENT.md";
|
|
330
|
+
const RULE_MARKER = "RULE.md";
|
|
331
|
+
const CURSORRULES_MARKER = ".cursorrules";
|
|
332
|
+
const FILE_EXT = ".mdc";
|
|
333
|
+
const PROFILE_EXTS = [".yaml", ".yml"];
|
|
334
|
+
const KIND_DIRS = {
|
|
335
|
+
skill: "skills",
|
|
336
|
+
rule: "rules",
|
|
337
|
+
agent: "agents",
|
|
338
|
+
profile: "profiles",
|
|
339
|
+
script: "scripts"
|
|
340
|
+
};
|
|
341
|
+
const ROOT_SKIP = /* @__PURE__ */ new Set([
|
|
342
|
+
".",
|
|
343
|
+
"node_modules",
|
|
344
|
+
"skills",
|
|
345
|
+
"rules",
|
|
346
|
+
"agents",
|
|
347
|
+
"profiles",
|
|
348
|
+
"scripts"
|
|
349
|
+
]);
|
|
350
|
+
function entries(dir) {
|
|
351
|
+
try {
|
|
352
|
+
return readdirSync(dir, { withFileTypes: true });
|
|
353
|
+
} catch {
|
|
354
|
+
return [];
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
function firstHeading(md) {
|
|
358
|
+
const m = md.match(/^#\s+(.+)$/m);
|
|
359
|
+
return m ? m[1].trim() : "";
|
|
360
|
+
}
|
|
361
|
+
function frontmatterName(md) {
|
|
362
|
+
const fm = md.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
363
|
+
if (!fm) return "";
|
|
364
|
+
const m = fm[1].match(/^name:\s*(.+)$/m);
|
|
365
|
+
return m ? m[1].trim().replace(/^['"]|['"]$/g, "") : "";
|
|
366
|
+
}
|
|
367
|
+
function frontmatterDescription(md) {
|
|
368
|
+
const fm = md.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
369
|
+
if (!fm) return "";
|
|
370
|
+
const lines = fm[1].split(/\r?\n/);
|
|
371
|
+
for (let i = 0; i < lines.length; i++) {
|
|
372
|
+
const dm = lines[i].match(/^description:\s*(.*)$/);
|
|
373
|
+
if (!dm) continue;
|
|
374
|
+
let val = dm[1].trim();
|
|
375
|
+
if (val === "" || val === ">" || val === "|" || val === ">-" || val === "|-") {
|
|
376
|
+
const collected = [];
|
|
377
|
+
for (let j = i + 1; j < lines.length; j++) if (/^\s+\S/.test(lines[j])) collected.push(lines[j].trim());
|
|
378
|
+
else break;
|
|
379
|
+
val = collected.join(" ");
|
|
380
|
+
}
|
|
381
|
+
return val.trim();
|
|
382
|
+
}
|
|
383
|
+
return "";
|
|
384
|
+
}
|
|
385
|
+
function readDescription(filePath) {
|
|
386
|
+
try {
|
|
387
|
+
const md = readFileSync(filePath, "utf8");
|
|
388
|
+
return frontmatterDescription(md) || firstHeading(md);
|
|
389
|
+
} catch {
|
|
390
|
+
return "";
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function folderDescription(dir) {
|
|
394
|
+
for (const f of [SKILL_MARKER, "README.md"]) {
|
|
395
|
+
const p = join(dir, f);
|
|
396
|
+
if (existsSync(p)) {
|
|
397
|
+
const d = readDescription(p);
|
|
398
|
+
if (d) return d;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return "";
|
|
402
|
+
}
|
|
403
|
+
function makeArtifact(kind, category, name, srcPath, isFile) {
|
|
404
|
+
return {
|
|
405
|
+
kind,
|
|
406
|
+
category,
|
|
407
|
+
name,
|
|
408
|
+
id: category ? `${category}/${name}` : name,
|
|
409
|
+
dir: srcPath,
|
|
410
|
+
isFile,
|
|
411
|
+
description: isFile ? readDescription(srcPath) : folderDescription(srcPath)
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function rel(root, fullPath) {
|
|
415
|
+
return relative(root, fullPath).replace(/\\/g, "/");
|
|
416
|
+
}
|
|
417
|
+
function collectSkills(root, base, out, matcher) {
|
|
418
|
+
for (const child of entries(base)) {
|
|
419
|
+
if (!child.isDirectory()) continue;
|
|
420
|
+
const childDir = join(base, child.name);
|
|
421
|
+
if (matcher(rel(root, childDir), true)) continue;
|
|
422
|
+
if (existsSync(join(childDir, SKILL_MARKER))) out.push(makeArtifact("skill", null, child.name, childDir, false));
|
|
423
|
+
else for (const item of entries(childDir)) {
|
|
424
|
+
if (!item.isDirectory()) continue;
|
|
425
|
+
const itemDir = join(childDir, item.name);
|
|
426
|
+
if (matcher(rel(root, itemDir), true)) continue;
|
|
427
|
+
if (existsSync(join(itemDir, SKILL_MARKER))) out.push(makeArtifact("skill", child.name, item.name, itemDir, false));
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
function collectMdc(root, kind, base, out, matcher) {
|
|
432
|
+
for (const child of entries(base)) {
|
|
433
|
+
const childPath = join(base, child.name);
|
|
434
|
+
if (child.isFile() && child.name.endsWith(FILE_EXT)) {
|
|
435
|
+
if (matcher(rel(root, childPath), false)) continue;
|
|
436
|
+
out.push(makeArtifact(kind, null, child.name.slice(0, -4), childPath, true));
|
|
437
|
+
} else if (child.isDirectory()) {
|
|
438
|
+
if (matcher(rel(root, childPath), true)) continue;
|
|
439
|
+
for (const item of entries(childPath)) {
|
|
440
|
+
const itemPath = join(childPath, item.name);
|
|
441
|
+
if (item.isFile() && item.name.endsWith(FILE_EXT)) {
|
|
442
|
+
if (matcher(rel(root, itemPath), false)) continue;
|
|
443
|
+
out.push(makeArtifact(kind, child.name, item.name.slice(0, -4), itemPath, true));
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function collectProfiles(root, base, out, matcher) {
|
|
450
|
+
for (const child of entries(base)) {
|
|
451
|
+
const childPath = join(base, child.name);
|
|
452
|
+
if (child.isFile() && PROFILE_EXTS.some((e) => child.name.endsWith(e))) {
|
|
453
|
+
if (matcher(rel(root, childPath), false)) continue;
|
|
454
|
+
const stem = child.name.replace(/\.(yaml|yml)$/, "");
|
|
455
|
+
out.push(makeArtifact("profile", null, stem, childPath, true));
|
|
456
|
+
} else if (child.isDirectory()) {
|
|
457
|
+
if (matcher(rel(root, childPath), true)) continue;
|
|
458
|
+
for (const item of entries(childPath)) {
|
|
459
|
+
const itemPath = join(childPath, item.name);
|
|
460
|
+
if (item.isFile() && PROFILE_EXTS.some((e) => item.name.endsWith(e))) {
|
|
461
|
+
if (matcher(rel(root, itemPath), false)) continue;
|
|
462
|
+
const stem = item.name.replace(/\.(yaml|yml)$/, "");
|
|
463
|
+
out.push(makeArtifact("profile", child.name, stem, itemPath, true));
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function collectScripts(root, base, out, matcher) {
|
|
470
|
+
for (const child of entries(base)) {
|
|
471
|
+
if (!child.isDirectory()) continue;
|
|
472
|
+
const childDir = join(base, child.name);
|
|
473
|
+
if (matcher(rel(root, childDir), true)) continue;
|
|
474
|
+
if (entries(childDir).some((e) => e.isFile())) out.push(makeArtifact("script", null, child.name, childDir, false));
|
|
475
|
+
else for (const item of entries(childDir)) {
|
|
476
|
+
const itemDir = join(childDir, item.name);
|
|
477
|
+
if (!item.isDirectory() || matcher(rel(root, itemDir), true)) continue;
|
|
478
|
+
if (entries(itemDir).some((e) => e.isFile())) out.push(makeArtifact("script", child.name, item.name, join(childDir, item.name), false));
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
function collectRootSkills(root, out, matcher) {
|
|
483
|
+
const rootSkillFile = join(root, SKILL_MARKER);
|
|
484
|
+
if (existsSync(rootSkillFile) && !matcher(SKILL_MARKER, false)) {
|
|
485
|
+
let name = "";
|
|
486
|
+
try {
|
|
487
|
+
name = frontmatterName(readFileSync(rootSkillFile, "utf8"));
|
|
488
|
+
} catch {}
|
|
489
|
+
if (!name) name = basename(root) || "skill";
|
|
490
|
+
out.push(makeArtifact("skill", null, name, root, false));
|
|
491
|
+
}
|
|
492
|
+
for (const child of entries(root)) {
|
|
493
|
+
if (!child.isDirectory() || child.name.startsWith(".") || ROOT_SKIP.has(child.name)) continue;
|
|
494
|
+
const childDir = join(root, child.name);
|
|
495
|
+
if (matcher(child.name, true)) continue;
|
|
496
|
+
if (existsSync(join(childDir, SKILL_MARKER))) out.push(makeArtifact("skill", null, child.name, childDir, false));
|
|
497
|
+
else for (const item of entries(childDir)) {
|
|
498
|
+
if (!item.isDirectory()) continue;
|
|
499
|
+
const itemDir = join(childDir, item.name);
|
|
500
|
+
if (matcher(rel(root, itemDir), true)) continue;
|
|
501
|
+
if (existsSync(join(itemDir, SKILL_MARKER))) out.push(makeArtifact("skill", child.name, item.name, itemDir, false));
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
function collectRootMdc(kind, root, out, matcher) {
|
|
506
|
+
if (kind === "agent") {
|
|
507
|
+
const rootAgent = join(root, AGENT_MARKER);
|
|
508
|
+
if (existsSync(rootAgent) && !matcher(AGENT_MARKER, false)) {
|
|
509
|
+
let name = "";
|
|
510
|
+
try {
|
|
511
|
+
name = frontmatterName(readFileSync(rootAgent, "utf8"));
|
|
512
|
+
} catch {}
|
|
513
|
+
out.push(makeArtifact("agent", null, name || "agent", rootAgent, true));
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
if (kind === "rule") {
|
|
517
|
+
const rootRule = join(root, RULE_MARKER);
|
|
518
|
+
if (existsSync(rootRule) && !matcher(RULE_MARKER, false)) {
|
|
519
|
+
let name = "";
|
|
520
|
+
try {
|
|
521
|
+
name = frontmatterName(readFileSync(rootRule, "utf8"));
|
|
522
|
+
} catch {}
|
|
523
|
+
out.push(makeArtifact("rule", null, name || "rule", rootRule, true));
|
|
524
|
+
}
|
|
525
|
+
const cursorRules = join(root, CURSORRULES_MARKER);
|
|
526
|
+
if (existsSync(cursorRules) && !matcher(CURSORRULES_MARKER, false)) out.push(makeArtifact("rule", null, "cursorrules", cursorRules, true));
|
|
527
|
+
}
|
|
528
|
+
for (const child of entries(root)) {
|
|
529
|
+
const childPath = join(root, child.name);
|
|
530
|
+
if (child.isFile() && child.name.endsWith(FILE_EXT) && !child.name.startsWith(".")) {
|
|
531
|
+
if (matcher(child.name, false)) continue;
|
|
532
|
+
out.push(makeArtifact(kind, null, child.name.slice(0, -4), childPath, true));
|
|
533
|
+
} else if (child.isDirectory() && !child.name.startsWith(".") && !ROOT_SKIP.has(child.name)) {
|
|
534
|
+
if (matcher(child.name, true)) continue;
|
|
535
|
+
for (const item of entries(childPath)) {
|
|
536
|
+
const itemPath = join(childPath, item.name);
|
|
537
|
+
if (item.isFile() && item.name.endsWith(FILE_EXT)) {
|
|
538
|
+
if (matcher(rel(root, itemPath), false)) continue;
|
|
539
|
+
out.push(makeArtifact(kind, child.name, item.name.slice(0, -4), itemPath, true));
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function collectRootProfiles(root, out, matcher) {
|
|
546
|
+
const localProfileDir = join(root, "profile");
|
|
547
|
+
if (existsSync(localProfileDir) && !matcher("profile", true)) collectProfiles(root, localProfileDir, out, matcher);
|
|
548
|
+
for (const child of entries(root)) {
|
|
549
|
+
if (!child.isFile()) continue;
|
|
550
|
+
if (matcher(child.name, false)) continue;
|
|
551
|
+
const lower = child.name.toLowerCase();
|
|
552
|
+
if (lower === "profile.yaml" || lower === "profile.yml" || lower.endsWith(".profile.yaml") || lower.endsWith(".profile.yml")) {
|
|
553
|
+
const stem = child.name.replace(/\.(profile\.)?(yaml|yml)$/i, "");
|
|
554
|
+
out.push(makeArtifact("profile", null, stem, join(root, child.name), true));
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
function collectRootScripts(root, out, matcher) {
|
|
559
|
+
const localScriptDir = join(root, "script");
|
|
560
|
+
if (existsSync(localScriptDir) && !matcher("script", true)) collectScripts(root, localScriptDir, out, matcher);
|
|
561
|
+
if (!existsSync(join(root, SKILL_MARKER))) {
|
|
562
|
+
for (const sf of [
|
|
563
|
+
"run.sh",
|
|
564
|
+
"script.sh",
|
|
565
|
+
"run.js",
|
|
566
|
+
"main.py"
|
|
567
|
+
]) if (existsSync(join(root, sf)) && !matcher(sf, false)) {
|
|
568
|
+
const name = basename(root) || "script";
|
|
569
|
+
out.push(makeArtifact("script", null, name, root, false));
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
function listKind(root, kind) {
|
|
575
|
+
const out = [];
|
|
576
|
+
const matcher = createIgnoreMatcher(root);
|
|
577
|
+
const base = join(root, KIND_DIRS[kind]);
|
|
578
|
+
if (existsSync(base) && !matcher(KIND_DIRS[kind], true)) if (kind === "skill") collectSkills(root, base, out, matcher);
|
|
579
|
+
else if (kind === "profile") collectProfiles(root, base, out, matcher);
|
|
580
|
+
else if (kind === "script") collectScripts(root, base, out, matcher);
|
|
581
|
+
else collectMdc(root, kind, base, out, matcher);
|
|
582
|
+
if (kind === "skill") collectRootSkills(root, out, matcher);
|
|
583
|
+
else if (kind === "agent" || kind === "rule") collectRootMdc(kind, root, out, matcher);
|
|
584
|
+
else if (kind === "profile") collectRootProfiles(root, out, matcher);
|
|
585
|
+
else if (kind === "script") collectRootScripts(root, out, matcher);
|
|
586
|
+
const seen = /* @__PURE__ */ new Set();
|
|
587
|
+
return out.filter((a) => seen.has(a.id) ? false : seen.add(a.id)).sort((a, b) => a.id.localeCompare(b.id));
|
|
588
|
+
}
|
|
589
|
+
function listAll(root) {
|
|
590
|
+
return [
|
|
591
|
+
"skill",
|
|
592
|
+
"rule",
|
|
593
|
+
"agent",
|
|
594
|
+
"profile",
|
|
595
|
+
"script"
|
|
596
|
+
].flatMap((k) => listKind(root, k));
|
|
597
|
+
}
|
|
598
|
+
function select(root, kind, selector) {
|
|
599
|
+
const items = listKind(root, kind);
|
|
600
|
+
if (!selector) return items;
|
|
601
|
+
if (selector.includes("/")) return items.filter((a) => a.id === selector);
|
|
602
|
+
const byCategory = items.filter((a) => a.category === selector);
|
|
603
|
+
if (byCategory.length) return byCategory;
|
|
604
|
+
return items.filter((a) => a.name === selector);
|
|
605
|
+
}
|
|
606
|
+
function selectAll(root, selector) {
|
|
607
|
+
const items = listAll(root);
|
|
608
|
+
if (!selector) return items;
|
|
609
|
+
if (selector.includes("/")) return items.filter((a) => a.id === selector);
|
|
610
|
+
const byCategory = items.filter((a) => a.category === selector);
|
|
611
|
+
if (byCategory.length) return byCategory;
|
|
612
|
+
return items.filter((a) => a.name === selector);
|
|
613
|
+
}
|
|
614
|
+
export { DEFAULT_IGNORES, createIgnoreMatcher, frontmatterName, isExplicitSource, isOwnerRepoShape, listAll, listKind, parseSource, resolveSource, select, selectAll, sourcesEqual };
|
package/dist/_chunks/theme2.mjs
CHANGED
|
@@ -85,34 +85,35 @@ function printHelp() {
|
|
|
85
85
|
`${badge()} ${dim("install agents, skills, rules, scripts & profiles")}`,
|
|
86
86
|
"",
|
|
87
87
|
h("Usage"),
|
|
88
|
-
` ${cmd("agentry")} ${flag("add")}
|
|
89
|
-
` ${cmd("agentry")} ${flag("add")} ${dim("
|
|
90
|
-
` ${cmd("agentry")} ${flag("
|
|
91
|
-
` ${cmd("agentry")} ${flag("
|
|
92
|
-
` ${cmd("agentry")} ${flag("
|
|
93
|
-
` ${cmd("agentry")} ${flag("
|
|
88
|
+
` ${cmd("agentry")} ${flag("add")} ${dim("[kind] <source> [selector] [options]")}`,
|
|
89
|
+
` ${cmd("agentry")} ${flag("add from")} ${dim("<url> [selector] [options]")}`,
|
|
90
|
+
` ${cmd("agentry")} ${flag("add")} ${dim("profile <name> [source] [options]")}`,
|
|
91
|
+
` ${cmd("agentry")} ${flag("remove")} ${dim("[kind] [source] [selector] [options]")}`,
|
|
92
|
+
` ${cmd("agentry")} ${flag("list")} ${dim("[source] [kind] [selector]")}`,
|
|
93
|
+
` ${cmd("agentry")} ${flag("update")} ${dim("[kind] [source] [selector]")}`,
|
|
94
|
+
` ${cmd("agentry")} ${flag("init")} ${dim("<kind> [name] [category] [options]")}`,
|
|
94
95
|
` ${cmd("agentry")} ${flag("uninstall")}`,
|
|
95
96
|
"",
|
|
96
97
|
h("Kinds"),
|
|
97
98
|
bullet(`${c.bold("skill")} ${c.bold("rule")} ${c.bold("agent")} ${c.bold("profile")} ${c.bold("script")} ${dim("(or all)")}`),
|
|
98
|
-
dim(" Plurals accepted: skills → skill, agents → agent, …"),
|
|
99
|
+
dim(" Plurals accepted: skills → skill, agents → agent, … (optional on add/remove)"),
|
|
99
100
|
"",
|
|
100
101
|
h("Sources"),
|
|
101
102
|
bullet(`${cmd("author/repo")} ${dim("GitHub shorthand")}`),
|
|
102
103
|
bullet(`${cmd("https://github.com/…")} ${dim("GitHub URL or /tree/<ref>/<dir>")}`),
|
|
104
|
+
bullet(`${cmd("https://awesomeskill.ai/…")} ${dim("Skill directory/hub website")}`),
|
|
103
105
|
bullet(`${cmd("https://gitlab.com/…")} ${dim("GitLab URL")}`),
|
|
104
106
|
bullet(`${cmd("git@host:owner/repo.git")} ${dim("any git URL")}`),
|
|
105
107
|
bullet(`${cmd("./path")} ${dim("|")} ${cmd("/path")} ${dim("|")} ${cmd("~/path")} ${dim("local directory")}`),
|
|
106
108
|
"",
|
|
107
109
|
h("Examples"),
|
|
108
|
-
bullet(`${cmd("agentry add
|
|
110
|
+
bullet(`${cmd("agentry add")} mhmdreza-rafiei/agent-tools`),
|
|
111
|
+
bullet(`${cmd("agentry add from")} https://awesomeskill.ai/skill/vercel-labs-agent-skills-web-design-guidelines`),
|
|
109
112
|
bullet(`${cmd("agentry add skills")} mhmdreza-rafiei/agent-tools enhance-prompt ${flag("-a")} cursor`),
|
|
110
113
|
bullet(`${cmd("agentry list")}`),
|
|
111
114
|
bullet(`${cmd("agentry list skills")}`),
|
|
112
115
|
bullet(`${cmd("agentry list")} mhmdreza-rafiei/agent-tools`),
|
|
113
|
-
bullet(`${cmd("agentry
|
|
114
|
-
bullet(`${cmd("agentry list")} mhmdreza-rafiei/agent-tools skills enhance-prompt`),
|
|
115
|
-
bullet(`${cmd("agentry remove skills")} enhance-prompt`),
|
|
116
|
+
bullet(`${cmd("agentry remove")} enhance-prompt`),
|
|
116
117
|
bullet(`${cmd("agentry remove skills")} mhmdreza-rafiei/agent-tools`),
|
|
117
118
|
bullet(`${cmd("agentry update skills")} mhmdreza-rafiei/agent-tools`),
|
|
118
119
|
bullet(`${cmd("agentry init skill")} enhance-prompt prompt ${flag("--reference")}`),
|
|
@@ -130,20 +131,15 @@ function printHelp() {
|
|
|
130
131
|
` ${flag("--all")} ${dim("Install everything to all agents, no prompts")}`,
|
|
131
132
|
` ${flag("-y, --yes")} ${dim("Don't prompt (scope defaults to project)")}`,
|
|
132
133
|
` ${flag("--description")} ${dim("<t>")} ${dim("init: frontmatter description")}`,
|
|
133
|
-
` ${flag("--alwaysApply")}
|
|
134
|
-
` ${flag("--reference")}
|
|
135
|
-
` ${flag("--no-reference")}
|
|
134
|
+
` ${flag("--alwaysApply")} ${dim("init rule: alwaysApply: true")}`,
|
|
135
|
+
` ${flag("--reference")} ${dim("init skill: include references/TEMPLATE.md")}`,
|
|
136
|
+
` ${flag("--no-reference")} ${dim("init skill: omit references/ folder")}`,
|
|
136
137
|
` ${flag("--skills")} ${dim("[src]")} ${dim("init profile: include skills (optional source)")}`,
|
|
137
138
|
` ${flag("--agents")} ${dim("[src]")} ${dim("init profile: include agents")}`,
|
|
138
139
|
` ${flag("--rules")} ${dim("[src]")} ${dim("init profile: include rules")}`,
|
|
139
140
|
` ${flag("--scripts")} ${dim("[src]")} ${dim("init profile: include scripts")}`,
|
|
140
|
-
` ${flag("-v, --version")} ${dim("Print version")}`,
|
|
141
|
-
` ${flag("-h, --help")} ${dim("Show this help")}`,
|
|
142
|
-
"",
|
|
143
|
-
dim("Tip: omit --agent to pick providers interactively (universal locked + searchable list)."),
|
|
144
|
-
dim("Tip: remove/update accept a GitHub repo or local path to filter by install source."),
|
|
145
141
|
""
|
|
146
142
|
];
|
|
147
|
-
process.stdout.write(lines.join("\n"));
|
|
143
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
148
144
|
}
|
|
149
145
|
export { PALETTE, animateLogo, badge, delay, logo, printHelp, sky, tagline, theme };
|