@mhmdreza-rafiei/agentry 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -20
- package/dist/cli.mjs +309 -62
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,11 +24,15 @@ npx @mhmdreza-rafiei/agentry@latest --help
|
|
|
24
24
|
## Quick start
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
#
|
|
27
|
+
# Install a standalone single-skill repository (root SKILL.md)
|
|
28
|
+
agentry add author/my-skill
|
|
29
|
+
|
|
30
|
+
# Browse + install from a catalog repo (kind is optional or explicit)
|
|
31
|
+
agentry add mhmdreza-rafiei/agent-tools
|
|
28
32
|
agentry add skills mhmdreza-rafiei/agent-tools
|
|
29
33
|
|
|
30
34
|
# Install one skill to Cursor
|
|
31
|
-
agentry add
|
|
35
|
+
agentry add mhmdreza-rafiei/agent-tools enhance-prompt -a cursor
|
|
32
36
|
|
|
33
37
|
# See what is installed
|
|
34
38
|
agentry list
|
|
@@ -90,36 +94,37 @@ agentry list skills
|
|
|
90
94
|
|
|
91
95
|
| Command | Description |
|
|
92
96
|
| --- | --- |
|
|
93
|
-
| `agentry add
|
|
97
|
+
| `agentry add [kind] <source> [selector]` | Discover and install artifacts (kind is optional) |
|
|
94
98
|
| `agentry add profile <name> [source]` | Apply a local `profile/<name>.yaml` bundle |
|
|
95
99
|
| `agentry list` | List installed artifacts |
|
|
96
100
|
| `agentry list <kind>` | List installed artifacts of one kind |
|
|
97
101
|
| `agentry list <source> [kind] [selector]` | List what a repo/path contains |
|
|
98
|
-
| `agentry remove
|
|
102
|
+
| `agentry remove [kind] [source] [selector]` | Remove installs (files + lock) |
|
|
99
103
|
| `agentry update [kind] [source] [selector]` | Re-install from source or lockfile |
|
|
100
104
|
| `agentry init <kind> [name] [category]` | Scaffold a new artifact |
|
|
101
105
|
| `agentry update` | Update the Agentry CLI itself |
|
|
102
106
|
| `agentry uninstall` | Uninstall the Agentry CLI |
|
|
103
107
|
|
|
104
|
-
`<kind>` is `skill | rule | agent | profile | script` (plurals accepted).
|
|
108
|
+
`<kind>` is `skill | rule | agent | profile | script` (plurals accepted; omit to match all).
|
|
105
109
|
|
|
106
110
|
### Source formats
|
|
107
111
|
|
|
108
112
|
```bash
|
|
109
|
-
# GitHub shorthand
|
|
113
|
+
# GitHub shorthand (single-skill repo or catalog)
|
|
114
|
+
agentry add mhmdreza-rafiei/agent-tools
|
|
110
115
|
agentry add skills mhmdreza-rafiei/agent-tools
|
|
111
116
|
|
|
112
117
|
# Full GitHub URL
|
|
113
|
-
agentry add
|
|
118
|
+
agentry add https://github.com/mhmdreza-rafiei/agent-tools
|
|
114
119
|
|
|
115
120
|
# Tree URL (subdir + ref)
|
|
116
|
-
agentry add
|
|
121
|
+
agentry add https://github.com/mhmdreza-rafiei/agent-tools/tree/main/skills
|
|
117
122
|
|
|
118
123
|
# Local path
|
|
119
|
-
agentry add
|
|
124
|
+
agentry add ./my-skills
|
|
120
125
|
|
|
121
126
|
# Any git URL
|
|
122
|
-
agentry add
|
|
127
|
+
agentry add git@github.com:mhmdreza-rafiei/agent-tools.git
|
|
123
128
|
```
|
|
124
129
|
|
|
125
130
|
### Options
|
|
@@ -145,10 +150,10 @@ agentry add skills git@github.com:mhmdreza-rafiei/agent-tools.git
|
|
|
145
150
|
# List everything in a catalog repo
|
|
146
151
|
agentry list mhmdreza-rafiei/agent-tools
|
|
147
152
|
agentry list mhmdreza-rafiei/agent-tools skills
|
|
148
|
-
agentry list mhmdreza-rafiei/agent-tools
|
|
153
|
+
agentry list mhmdreza-rafiei/agent-tools enhance-prompt
|
|
149
154
|
|
|
150
155
|
# Install
|
|
151
|
-
agentry add
|
|
156
|
+
agentry add mhmdreza-rafiei/agent-tools
|
|
152
157
|
agentry add skills mhmdreza-rafiei/agent-tools enhance-prompt -a cursor -a claude-code
|
|
153
158
|
agentry add rules mhmdreza-rafiei/agent-tools ask-dont-guess
|
|
154
159
|
agentry add agents mhmdreza-rafiei/agent-tools frontend-developer
|
|
@@ -159,7 +164,7 @@ agentry list skills
|
|
|
159
164
|
|
|
160
165
|
# Update / remove by selector or by install source
|
|
161
166
|
agentry update skills mhmdreza-rafiei/agent-tools
|
|
162
|
-
agentry remove
|
|
167
|
+
agentry remove enhance-prompt
|
|
163
168
|
agentry remove skills mhmdreza-rafiei/agent-tools
|
|
164
169
|
|
|
165
170
|
# Scaffold
|
|
@@ -173,18 +178,33 @@ Example catalog used throughout this README: [`mhmdreza-rafiei/agent-tools`](htt
|
|
|
173
178
|
## How it works
|
|
174
179
|
|
|
175
180
|
1. Resolve a source (local path or shallow git clone).
|
|
176
|
-
2. Discover artifacts under `<kind>s/` **and** root markers
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
181
|
+
2. Discover artifacts under `<kind>s/` **and** root markers:
|
|
182
|
+
- Root `SKILL.md` (standalone single-skill repo)
|
|
183
|
+
- Root `AGENT.md` / `RULE.md` / `.cursorrules` / `.mdc` files
|
|
184
|
+
- Root `profile/<name>.yaml` / `*.profile.yaml` / `profile.yaml`
|
|
185
|
+
- Root scripts (`run.sh`, `script.sh`, `run.js`)
|
|
186
|
+
3. Filter out directories/files ignored by `.agentryignore` or `.agentry.ignore` (and built-in VCS ignores like `.git`).
|
|
187
|
+
4. Let you pick what to install (searchable list + `All`).
|
|
188
|
+
5. Install once to `.agents/skills/<id>` for universal providers; symlink (or `--copy`) for others.
|
|
189
|
+
6. Track installs in `.agentry/lock.json` for remove/update.
|
|
180
190
|
|
|
181
191
|
If a clone fails and a prior cache exists, Agentry asks whether to load from cache (`~/.cache/agentry/repos`).
|
|
182
192
|
|
|
183
193
|
## Artifact layouts
|
|
184
194
|
|
|
185
|
-
Author installable tools in a source repo
|
|
195
|
+
Author installable tools in a source repo:
|
|
186
196
|
|
|
187
|
-
###
|
|
197
|
+
### Single-skill repo (Root layout)
|
|
198
|
+
|
|
199
|
+
```text
|
|
200
|
+
SKILL.md
|
|
201
|
+
references/
|
|
202
|
+
GUIDE.md
|
|
203
|
+
scripts/
|
|
204
|
+
tool.sh
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Multi-skill catalog
|
|
188
208
|
|
|
189
209
|
```text
|
|
190
210
|
skills/
|
|
@@ -216,7 +236,18 @@ profiles/
|
|
|
216
236
|
frontend.yaml
|
|
217
237
|
```
|
|
218
238
|
|
|
219
|
-
Local install profiles
|
|
239
|
+
Local install profiles live at `profile/<name>.yaml`.
|
|
240
|
+
|
|
241
|
+
### Excluding development assets (`.agentryignore`)
|
|
242
|
+
|
|
243
|
+
Place `.agentryignore` (or `.agentry.ignore`) in the root of your repo to prevent internal tools from being discovered or installed:
|
|
244
|
+
|
|
245
|
+
```gitignore
|
|
246
|
+
# Exclude internal dev skills
|
|
247
|
+
dev-skills/
|
|
248
|
+
skills/internal-*
|
|
249
|
+
*.draft.mdc
|
|
250
|
+
```
|
|
220
251
|
|
|
221
252
|
## Supported agents
|
|
222
253
|
|
package/dist/cli.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import https from "node:https";
|
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
6
|
import { copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { homedir, tmpdir } from "node:os";
|
|
8
|
-
import { dirname, join, resolve, sep } from "node:path";
|
|
8
|
+
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
9
9
|
import { xdgCache, xdgConfig } from "xdg-basedir";
|
|
10
10
|
import { parse, stringify } from "yaml";
|
|
11
11
|
import { z } from "zod";
|
|
@@ -16,7 +16,7 @@ import { determineAgent } from "@vercel/detect-agent";
|
|
|
16
16
|
import { stripVTControlCharacters } from "node:util";
|
|
17
17
|
import { Writable } from "node:stream";
|
|
18
18
|
var name = "@mhmdreza-rafiei/agentry";
|
|
19
|
-
var version = "0.
|
|
19
|
+
var version = "0.3.0";
|
|
20
20
|
const ARTIFACT_KINDS = [
|
|
21
21
|
"skill",
|
|
22
22
|
"rule",
|
|
@@ -243,7 +243,100 @@ async function resolveSource(source, opts = {}) {
|
|
|
243
243
|
saveToCache(parsed, tmp);
|
|
244
244
|
return withSubpath(tmp, parsed.subpath, tmp);
|
|
245
245
|
}
|
|
246
|
+
const DEFAULT_IGNORES = [
|
|
247
|
+
".git",
|
|
248
|
+
".github",
|
|
249
|
+
"node_modules",
|
|
250
|
+
".agents",
|
|
251
|
+
".agentry",
|
|
252
|
+
".cursor",
|
|
253
|
+
".claude",
|
|
254
|
+
".vscode",
|
|
255
|
+
"dist",
|
|
256
|
+
"build",
|
|
257
|
+
".DS_Store"
|
|
258
|
+
];
|
|
259
|
+
function parseIgnorePattern(pattern) {
|
|
260
|
+
const trimmed = pattern.trim();
|
|
261
|
+
if (!trimmed || trimmed.startsWith("#")) return null;
|
|
262
|
+
const isDirOnly = trimmed.endsWith("/");
|
|
263
|
+
let p = isDirOnly ? trimmed.slice(0, -1) : trimmed;
|
|
264
|
+
const isRootAnchored = p.startsWith("/");
|
|
265
|
+
if (isRootAnchored) p = p.slice(1);
|
|
266
|
+
let regexStr = "";
|
|
267
|
+
for (let i = 0; i < p.length; i++) {
|
|
268
|
+
const char = p[i];
|
|
269
|
+
if (char === "*") if (p[i + 1] === "*") {
|
|
270
|
+
regexStr += ".*";
|
|
271
|
+
i++;
|
|
272
|
+
if (p[i + 1] === "/") i++;
|
|
273
|
+
} else regexStr += "[^/]*";
|
|
274
|
+
else if (char === "?") regexStr += "[^/]";
|
|
275
|
+
else if ([
|
|
276
|
+
".",
|
|
277
|
+
"+",
|
|
278
|
+
"^",
|
|
279
|
+
"$",
|
|
280
|
+
"{",
|
|
281
|
+
"}",
|
|
282
|
+
"(",
|
|
283
|
+
")",
|
|
284
|
+
"|",
|
|
285
|
+
"[",
|
|
286
|
+
"]",
|
|
287
|
+
"\\"
|
|
288
|
+
].includes(char)) regexStr += "\\" + char;
|
|
289
|
+
else regexStr += char;
|
|
290
|
+
}
|
|
291
|
+
let regex;
|
|
292
|
+
if (isRootAnchored) regex = new RegExp(`^${regexStr}(/.*)?$`, "i");
|
|
293
|
+
else if (p.includes("/")) regex = new RegExp(`(^|/)${regexStr}(/.*)?$`, "i");
|
|
294
|
+
else regex = new RegExp(`(^|/)${regexStr}($|/)`, "i");
|
|
295
|
+
return {
|
|
296
|
+
raw: trimmed,
|
|
297
|
+
isDirOnly,
|
|
298
|
+
regex
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function parseIgnoreFile(content) {
|
|
302
|
+
return content.split(/\r?\n/).map((line) => parseIgnorePattern(line)).filter((r) => r !== null);
|
|
303
|
+
}
|
|
304
|
+
function loadIgnoreRules(root) {
|
|
305
|
+
const candidates = [".agentryignore", ".agentry.ignore"];
|
|
306
|
+
const rules = [];
|
|
307
|
+
for (const name of candidates) {
|
|
308
|
+
const file = join(root, name);
|
|
309
|
+
if (existsSync(file)) try {
|
|
310
|
+
const text = readFileSync(file, "utf8");
|
|
311
|
+
rules.push(...parseIgnoreFile(text));
|
|
312
|
+
} catch {}
|
|
313
|
+
}
|
|
314
|
+
return rules;
|
|
315
|
+
}
|
|
316
|
+
function isDefaultIgnored(relPath) {
|
|
317
|
+
return relPath.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "").split("/").some((s) => DEFAULT_IGNORES.includes(s));
|
|
318
|
+
}
|
|
319
|
+
function createIgnoreMatcher(root) {
|
|
320
|
+
const fileRules = loadIgnoreRules(root);
|
|
321
|
+
return (relPath, isDirectory = false) => {
|
|
322
|
+
if (!relPath || relPath === "." || relPath === "./") return false;
|
|
323
|
+
const normalized = relPath.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
|
|
324
|
+
if (!normalized) return false;
|
|
325
|
+
if (isDefaultIgnored(normalized)) return true;
|
|
326
|
+
for (const rule of fileRules) {
|
|
327
|
+
if (rule.isDirOnly && !isDirectory) {
|
|
328
|
+
if (rule.regex.test(normalized + "/")) return true;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (rule.regex.test(normalized)) return true;
|
|
332
|
+
}
|
|
333
|
+
return false;
|
|
334
|
+
};
|
|
335
|
+
}
|
|
246
336
|
const SKILL_MARKER = "SKILL.md";
|
|
337
|
+
const AGENT_MARKER = "AGENT.md";
|
|
338
|
+
const RULE_MARKER = "RULE.md";
|
|
339
|
+
const CURSORRULES_MARKER = ".cursorrules";
|
|
247
340
|
const FILE_EXT = ".mdc";
|
|
248
341
|
const PROFILE_EXTS = [".yaml", ".yml"];
|
|
249
342
|
const KIND_DIRS = {
|
|
@@ -273,6 +366,12 @@ function firstHeading(md) {
|
|
|
273
366
|
const m = md.match(/^#\s+(.+)$/m);
|
|
274
367
|
return m ? m[1].trim() : "";
|
|
275
368
|
}
|
|
369
|
+
function frontmatterName(md) {
|
|
370
|
+
const fm = md.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
371
|
+
if (!fm) return "";
|
|
372
|
+
const m = fm[1].match(/^name:\s*(.+)$/m);
|
|
373
|
+
return m ? m[1].trim().replace(/^['"]|['"]$/g, "") : "";
|
|
374
|
+
}
|
|
276
375
|
function frontmatterDescription(md) {
|
|
277
376
|
const fm = md.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
278
377
|
if (!fm) return "";
|
|
@@ -320,69 +419,178 @@ function makeArtifact(kind, category, name, srcPath, isFile) {
|
|
|
320
419
|
description: isFile ? readDescription(srcPath) : folderDescription(srcPath)
|
|
321
420
|
};
|
|
322
421
|
}
|
|
323
|
-
function
|
|
422
|
+
function rel(root, fullPath) {
|
|
423
|
+
return relative(root, fullPath).replace(/\\/g, "/");
|
|
424
|
+
}
|
|
425
|
+
function collectSkills(root, base, out, matcher) {
|
|
324
426
|
for (const child of entries(base)) {
|
|
325
427
|
if (!child.isDirectory()) continue;
|
|
326
428
|
const childDir = join(base, child.name);
|
|
429
|
+
if (matcher(rel(root, childDir), true)) continue;
|
|
327
430
|
if (existsSync(join(childDir, SKILL_MARKER))) out.push(makeArtifact("skill", null, child.name, childDir, false));
|
|
328
431
|
else for (const item of entries(childDir)) {
|
|
329
432
|
if (!item.isDirectory()) continue;
|
|
330
433
|
const itemDir = join(childDir, item.name);
|
|
434
|
+
if (matcher(rel(root, itemDir), true)) continue;
|
|
331
435
|
if (existsSync(join(itemDir, SKILL_MARKER))) out.push(makeArtifact("skill", child.name, item.name, itemDir, false));
|
|
332
436
|
}
|
|
333
437
|
}
|
|
334
438
|
}
|
|
335
|
-
function collectMdc(kind, base, out) {
|
|
336
|
-
for (const child of entries(base))
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
439
|
+
function collectMdc(root, kind, base, out, matcher) {
|
|
440
|
+
for (const child of entries(base)) {
|
|
441
|
+
const childPath = join(base, child.name);
|
|
442
|
+
if (child.isFile() && child.name.endsWith(FILE_EXT)) {
|
|
443
|
+
if (matcher(rel(root, childPath), false)) continue;
|
|
444
|
+
out.push(makeArtifact(kind, null, child.name.slice(0, -4), childPath, true));
|
|
445
|
+
} else if (child.isDirectory()) {
|
|
446
|
+
if (matcher(rel(root, childPath), true)) continue;
|
|
447
|
+
for (const item of entries(childPath)) {
|
|
448
|
+
const itemPath = join(childPath, item.name);
|
|
449
|
+
if (item.isFile() && item.name.endsWith(FILE_EXT)) {
|
|
450
|
+
if (matcher(rel(root, itemPath), false)) continue;
|
|
451
|
+
out.push(makeArtifact(kind, child.name, item.name.slice(0, -4), itemPath, true));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
340
455
|
}
|
|
341
456
|
}
|
|
342
|
-
function collectProfiles(base, out) {
|
|
343
|
-
for (const child of entries(base))
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
457
|
+
function collectProfiles(root, base, out, matcher) {
|
|
458
|
+
for (const child of entries(base)) {
|
|
459
|
+
const childPath = join(base, child.name);
|
|
460
|
+
if (child.isFile() && PROFILE_EXTS.some((e) => child.name.endsWith(e))) {
|
|
461
|
+
if (matcher(rel(root, childPath), false)) continue;
|
|
462
|
+
const stem = child.name.replace(/\.(yaml|yml)$/, "");
|
|
463
|
+
out.push(makeArtifact("profile", null, stem, childPath, true));
|
|
464
|
+
} else if (child.isDirectory()) {
|
|
465
|
+
if (matcher(rel(root, childPath), true)) continue;
|
|
466
|
+
for (const item of entries(childPath)) {
|
|
467
|
+
const itemPath = join(childPath, item.name);
|
|
468
|
+
if (item.isFile() && PROFILE_EXTS.some((e) => item.name.endsWith(e))) {
|
|
469
|
+
if (matcher(rel(root, itemPath), false)) continue;
|
|
470
|
+
const stem = item.name.replace(/\.(yaml|yml)$/, "");
|
|
471
|
+
out.push(makeArtifact("profile", child.name, stem, itemPath, true));
|
|
472
|
+
}
|
|
473
|
+
}
|
|
351
474
|
}
|
|
352
475
|
}
|
|
353
476
|
}
|
|
354
|
-
function collectScripts(base, out) {
|
|
477
|
+
function collectScripts(root, base, out, matcher) {
|
|
355
478
|
for (const child of entries(base)) {
|
|
356
479
|
if (!child.isDirectory()) continue;
|
|
357
480
|
const childDir = join(base, child.name);
|
|
481
|
+
if (matcher(rel(root, childDir), true)) continue;
|
|
358
482
|
if (entries(childDir).some((e) => e.isFile())) out.push(makeArtifact("script", null, child.name, childDir, false));
|
|
359
|
-
else for (const item of entries(childDir))
|
|
483
|
+
else for (const item of entries(childDir)) {
|
|
484
|
+
const itemDir = join(childDir, item.name);
|
|
485
|
+
if (!item.isDirectory() || matcher(rel(root, itemDir), true)) continue;
|
|
486
|
+
if (entries(itemDir).some((e) => e.isFile())) out.push(makeArtifact("script", child.name, item.name, join(childDir, item.name), false));
|
|
487
|
+
}
|
|
360
488
|
}
|
|
361
489
|
}
|
|
362
|
-
function collectRootSkills(root, out) {
|
|
490
|
+
function collectRootSkills(root, out, matcher) {
|
|
491
|
+
const rootSkillFile = join(root, SKILL_MARKER);
|
|
492
|
+
if (existsSync(rootSkillFile) && !matcher(SKILL_MARKER, false)) {
|
|
493
|
+
let name = "";
|
|
494
|
+
try {
|
|
495
|
+
name = frontmatterName(readFileSync(rootSkillFile, "utf8"));
|
|
496
|
+
} catch {}
|
|
497
|
+
if (!name) name = basename(root) || "skill";
|
|
498
|
+
out.push(makeArtifact("skill", null, name, root, false));
|
|
499
|
+
}
|
|
363
500
|
for (const child of entries(root)) {
|
|
364
501
|
if (!child.isDirectory() || child.name.startsWith(".") || ROOT_SKIP.has(child.name)) continue;
|
|
365
502
|
const childDir = join(root, child.name);
|
|
503
|
+
if (matcher(child.name, true)) continue;
|
|
366
504
|
if (existsSync(join(childDir, SKILL_MARKER))) out.push(makeArtifact("skill", null, child.name, childDir, false));
|
|
367
|
-
else for (const item of entries(childDir))
|
|
505
|
+
else for (const item of entries(childDir)) {
|
|
506
|
+
if (!item.isDirectory()) continue;
|
|
507
|
+
const itemDir = join(childDir, item.name);
|
|
508
|
+
if (matcher(rel(root, itemDir), true)) continue;
|
|
509
|
+
if (existsSync(join(itemDir, SKILL_MARKER))) out.push(makeArtifact("skill", child.name, item.name, itemDir, false));
|
|
510
|
+
}
|
|
368
511
|
}
|
|
369
512
|
}
|
|
370
|
-
function collectRootMdc(kind, root, out) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
513
|
+
function collectRootMdc(kind, root, out, matcher) {
|
|
514
|
+
if (kind === "agent") {
|
|
515
|
+
const rootAgent = join(root, AGENT_MARKER);
|
|
516
|
+
if (existsSync(rootAgent) && !matcher(AGENT_MARKER, false)) {
|
|
517
|
+
let name = "";
|
|
518
|
+
try {
|
|
519
|
+
name = frontmatterName(readFileSync(rootAgent, "utf8"));
|
|
520
|
+
} catch {}
|
|
521
|
+
out.push(makeArtifact("agent", null, name || "agent", rootAgent, true));
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (kind === "rule") {
|
|
525
|
+
const rootRule = join(root, RULE_MARKER);
|
|
526
|
+
if (existsSync(rootRule) && !matcher(RULE_MARKER, false)) {
|
|
527
|
+
let name = "";
|
|
528
|
+
try {
|
|
529
|
+
name = frontmatterName(readFileSync(rootRule, "utf8"));
|
|
530
|
+
} catch {}
|
|
531
|
+
out.push(makeArtifact("rule", null, name || "rule", rootRule, true));
|
|
532
|
+
}
|
|
533
|
+
const cursorRules = join(root, CURSORRULES_MARKER);
|
|
534
|
+
if (existsSync(cursorRules) && !matcher(CURSORRULES_MARKER, false)) out.push(makeArtifact("rule", null, "cursorrules", cursorRules, true));
|
|
535
|
+
}
|
|
536
|
+
for (const child of entries(root)) {
|
|
537
|
+
const childPath = join(root, child.name);
|
|
538
|
+
if (child.isFile() && child.name.endsWith(FILE_EXT) && !child.name.startsWith(".")) {
|
|
539
|
+
if (matcher(child.name, false)) continue;
|
|
540
|
+
out.push(makeArtifact(kind, null, child.name.slice(0, -4), childPath, true));
|
|
541
|
+
} else if (child.isDirectory() && !child.name.startsWith(".") && !ROOT_SKIP.has(child.name)) {
|
|
542
|
+
if (matcher(child.name, true)) continue;
|
|
543
|
+
for (const item of entries(childPath)) {
|
|
544
|
+
const itemPath = join(childPath, item.name);
|
|
545
|
+
if (item.isFile() && item.name.endsWith(FILE_EXT)) {
|
|
546
|
+
if (matcher(rel(root, itemPath), false)) continue;
|
|
547
|
+
out.push(makeArtifact(kind, child.name, item.name.slice(0, -4), itemPath, true));
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
function collectRootProfiles(root, out, matcher) {
|
|
554
|
+
const localProfileDir = join(root, "profile");
|
|
555
|
+
if (existsSync(localProfileDir) && !matcher("profile", true)) collectProfiles(root, localProfileDir, out, matcher);
|
|
556
|
+
for (const child of entries(root)) {
|
|
557
|
+
if (!child.isFile()) continue;
|
|
558
|
+
if (matcher(child.name, false)) continue;
|
|
559
|
+
const lower = child.name.toLowerCase();
|
|
560
|
+
if (lower === "profile.yaml" || lower === "profile.yml" || lower.endsWith(".profile.yaml") || lower.endsWith(".profile.yml")) {
|
|
561
|
+
const stem = child.name.replace(/\.(profile\.)?(yaml|yml)$/i, "");
|
|
562
|
+
out.push(makeArtifact("profile", null, stem, join(root, child.name), true));
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
function collectRootScripts(root, out, matcher) {
|
|
567
|
+
const localScriptDir = join(root, "script");
|
|
568
|
+
if (existsSync(localScriptDir) && !matcher("script", true)) collectScripts(root, localScriptDir, out, matcher);
|
|
569
|
+
if (!existsSync(join(root, SKILL_MARKER))) {
|
|
570
|
+
for (const sf of [
|
|
571
|
+
"run.sh",
|
|
572
|
+
"script.sh",
|
|
573
|
+
"run.js",
|
|
574
|
+
"main.py"
|
|
575
|
+
]) if (existsSync(join(root, sf)) && !matcher(sf, false)) {
|
|
576
|
+
const name = basename(root) || "script";
|
|
577
|
+
out.push(makeArtifact("script", null, name, root, false));
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
375
580
|
}
|
|
376
581
|
}
|
|
377
582
|
function listKind(root, kind) {
|
|
378
583
|
const out = [];
|
|
584
|
+
const matcher = createIgnoreMatcher(root);
|
|
379
585
|
const base = join(root, KIND_DIRS[kind]);
|
|
380
|
-
if (existsSync(base)) if (kind === "skill") collectSkills(base, out);
|
|
381
|
-
else if (kind === "profile") collectProfiles(base, out);
|
|
382
|
-
else if (kind === "script") collectScripts(base, out);
|
|
383
|
-
else collectMdc(kind, base, out);
|
|
384
|
-
if (kind === "skill") collectRootSkills(root, out);
|
|
385
|
-
else if (kind === "agent" || kind === "rule") collectRootMdc(kind, root, out);
|
|
586
|
+
if (existsSync(base) && !matcher(KIND_DIRS[kind], true)) if (kind === "skill") collectSkills(root, base, out, matcher);
|
|
587
|
+
else if (kind === "profile") collectProfiles(root, base, out, matcher);
|
|
588
|
+
else if (kind === "script") collectScripts(root, base, out, matcher);
|
|
589
|
+
else collectMdc(root, kind, base, out, matcher);
|
|
590
|
+
if (kind === "skill") collectRootSkills(root, out, matcher);
|
|
591
|
+
else if (kind === "agent" || kind === "rule") collectRootMdc(kind, root, out, matcher);
|
|
592
|
+
else if (kind === "profile") collectRootProfiles(root, out, matcher);
|
|
593
|
+
else if (kind === "script") collectRootScripts(root, out, matcher);
|
|
386
594
|
const seen = /* @__PURE__ */ new Set();
|
|
387
595
|
return out.filter((a) => seen.has(a.id) ? false : seen.add(a.id)).sort((a, b) => a.id.localeCompare(b.id));
|
|
388
596
|
}
|
|
@@ -391,7 +599,8 @@ function listAll(root) {
|
|
|
391
599
|
"skill",
|
|
392
600
|
"rule",
|
|
393
601
|
"agent",
|
|
394
|
-
"profile"
|
|
602
|
+
"profile",
|
|
603
|
+
"script"
|
|
395
604
|
].flatMap((k) => listKind(root, k));
|
|
396
605
|
}
|
|
397
606
|
function select(root, kind, selector) {
|
|
@@ -402,6 +611,14 @@ function select(root, kind, selector) {
|
|
|
402
611
|
if (byCategory.length) return byCategory;
|
|
403
612
|
return items.filter((a) => a.name === selector);
|
|
404
613
|
}
|
|
614
|
+
function selectAll(root, selector) {
|
|
615
|
+
const items = listAll(root);
|
|
616
|
+
if (!selector) return items;
|
|
617
|
+
if (selector.includes("/")) return items.filter((a) => a.id === selector);
|
|
618
|
+
const byCategory = items.filter((a) => a.category === selector);
|
|
619
|
+
if (byCategory.length) return byCategory;
|
|
620
|
+
return items.filter((a) => a.name === selector);
|
|
621
|
+
}
|
|
405
622
|
const home$1 = homedir();
|
|
406
623
|
const configHome = xdgConfig ?? join(home$1, ".config");
|
|
407
624
|
const codexHome = process.env.CODEX_HOME?.trim() || join(home$1, ".codex");
|
|
@@ -583,7 +800,7 @@ function getNonUniversalAgents() {
|
|
|
583
800
|
return listAgents().filter((a) => a.skillsDir !== ".agents/skills");
|
|
584
801
|
}
|
|
585
802
|
const home = homedir();
|
|
586
|
-
function copyTreeDereferenced(src, dest, seen = /* @__PURE__ */ new Set()) {
|
|
803
|
+
function copyTreeDereferenced(src, dest, seen = /* @__PURE__ */ new Set(), filter, baseSrc = src) {
|
|
587
804
|
const st = lstatSync(src);
|
|
588
805
|
if (st.isSymbolicLink()) {
|
|
589
806
|
let real;
|
|
@@ -594,7 +811,7 @@ function copyTreeDereferenced(src, dest, seen = /* @__PURE__ */ new Set()) {
|
|
|
594
811
|
}
|
|
595
812
|
if (seen.has(real)) return;
|
|
596
813
|
seen.add(real);
|
|
597
|
-
copyTreeDereferenced(real, dest, seen);
|
|
814
|
+
copyTreeDereferenced(real, dest, seen, filter, baseSrc);
|
|
598
815
|
return;
|
|
599
816
|
}
|
|
600
817
|
if (st.isDirectory()) {
|
|
@@ -607,7 +824,17 @@ function copyTreeDereferenced(src, dest, seen = /* @__PURE__ */ new Set()) {
|
|
|
607
824
|
if (seen.has(real)) return;
|
|
608
825
|
seen.add(real);
|
|
609
826
|
mkdirSync(dest, { recursive: true });
|
|
610
|
-
for (const name of readdirSync(src))
|
|
827
|
+
for (const name of readdirSync(src)) {
|
|
828
|
+
const childSrc = join(src, name);
|
|
829
|
+
const relPath = relative(baseSrc, childSrc).replace(/\\/g, "/");
|
|
830
|
+
let isDir = false;
|
|
831
|
+
try {
|
|
832
|
+
isDir = statSync(childSrc).isDirectory();
|
|
833
|
+
} catch {}
|
|
834
|
+
if (DEFAULT_IGNORES.includes(name) || name === ".agentryignore" || name === ".agentry.ignore") continue;
|
|
835
|
+
if (filter && filter(relPath, isDir)) continue;
|
|
836
|
+
copyTreeDereferenced(childSrc, join(dest, name), seen, filter, baseSrc);
|
|
837
|
+
}
|
|
611
838
|
return;
|
|
612
839
|
}
|
|
613
840
|
if (st.isFile()) {
|
|
@@ -735,6 +962,7 @@ function installOne(artifact, opts, agentList, source) {
|
|
|
735
962
|
});
|
|
736
963
|
return dests;
|
|
737
964
|
}
|
|
965
|
+
const ignoreFilter = artifact.isFile ? void 0 : createIgnoreMatcher(artifact.dir);
|
|
738
966
|
if (artifact.kind === "skill") {
|
|
739
967
|
const canonical = join(base, ".agents", "skills", artifact.id);
|
|
740
968
|
rmSync(canonical, {
|
|
@@ -742,7 +970,7 @@ function installOne(artifact, opts, agentList, source) {
|
|
|
742
970
|
force: true
|
|
743
971
|
});
|
|
744
972
|
mkdirSync(join(canonical, ".."), { recursive: true });
|
|
745
|
-
copyTreeDereferenced(artifact.dir, canonical);
|
|
973
|
+
copyTreeDereferenced(artifact.dir, canonical, /* @__PURE__ */ new Set(), ignoreFilter);
|
|
746
974
|
for (const a of list) if (a.skillsDir === ".agents/skills") dests.push({
|
|
747
975
|
agent: a.name,
|
|
748
976
|
dir: canonical
|
|
@@ -782,7 +1010,7 @@ function installOne(artifact, opts, agentList, source) {
|
|
|
782
1010
|
force: true
|
|
783
1011
|
});
|
|
784
1012
|
mkdirSync(join(dir, ".."), { recursive: true });
|
|
785
|
-
copyTreeDereferenced(artifact.dir, dir);
|
|
1013
|
+
copyTreeDereferenced(artifact.dir, dir, /* @__PURE__ */ new Set(), ignoreFilter);
|
|
786
1014
|
dests.push({
|
|
787
1015
|
agent: a.name,
|
|
788
1016
|
dir
|
|
@@ -1928,7 +2156,7 @@ async function cmdAdd(kind, source, selector, opts) {
|
|
|
1928
2156
|
await delayQuiet(280);
|
|
1929
2157
|
const { root, cleanup } = await resolveSourceWithUi(source, opts);
|
|
1930
2158
|
try {
|
|
1931
|
-
const artifacts = await spinner("Discovering…", () => kind === "all" ? listAll(root) : select(root, kind, selector), (a) => {
|
|
2159
|
+
const artifacts = await spinner("Discovering…", () => kind === "all" ? selector ? selectAll(root, selector) : listAll(root) : select(root, kind, selector), (a) => {
|
|
1932
2160
|
const noun = kind === "all" ? "artifact" : kind;
|
|
1933
2161
|
return `Found ${a.length} ${noun}${a.length === 1 ? "" : "s"}`;
|
|
1934
2162
|
}, 620);
|
|
@@ -1943,17 +2171,20 @@ async function cmdAdd(kind, source, selector, opts) {
|
|
|
1943
2171
|
return;
|
|
1944
2172
|
}
|
|
1945
2173
|
let chosen = artifacts;
|
|
1946
|
-
if (!selector &&
|
|
1947
|
-
|
|
1948
|
-
|
|
2174
|
+
if (!selector && artifacts.length > 1 && !opts.all && !opts.yes && !isQuiet()) {
|
|
2175
|
+
const noun = kind === "all" ? "artifacts" : `${kind}s`;
|
|
2176
|
+
await sectionTransition("source discovery", `select ${noun} to install`);
|
|
2177
|
+
const picked = await selectArtifacts(`Select ${noun} to install`, artifacts);
|
|
1949
2178
|
if (isCancelLike(picked)) {
|
|
1950
2179
|
outro("Cancelled.");
|
|
1951
2180
|
return;
|
|
1952
2181
|
}
|
|
1953
2182
|
chosen = picked;
|
|
1954
|
-
await sectionTransition(`${chosen.length} ${
|
|
1955
|
-
} else if (!isQuiet() && !opts.agents.length && !opts.all && !opts.yes)
|
|
1956
|
-
|
|
2183
|
+
await sectionTransition(`${chosen.length} ${chosen.length === 1 ? "artifact" : "artifacts"} selected`, "choose target agents");
|
|
2184
|
+
} else if (!isQuiet() && !opts.agents.length && !opts.all && !opts.yes) {
|
|
2185
|
+
const noun = kind === "all" ? chosen.length === 1 ? "artifact" : "artifacts" : `${kind}${chosen.length === 1 ? "" : "s"}`;
|
|
2186
|
+
await sectionTransition(`${chosen.length} ${noun} ready`, "choose target agents");
|
|
2187
|
+
} else {
|
|
1957
2188
|
await delayQuiet(200);
|
|
1958
2189
|
blank();
|
|
1959
2190
|
}
|
|
@@ -2108,7 +2339,7 @@ async function cmdRemove(kind, arg1, arg2, opts) {
|
|
|
2108
2339
|
let count = 0;
|
|
2109
2340
|
let paths = 0;
|
|
2110
2341
|
for (const t of ARTIFACT_KINDS) {
|
|
2111
|
-
const r = removeSelection(t,
|
|
2342
|
+
const r = removeSelection(t, selector, installOpts, agentList, source);
|
|
2112
2343
|
count += r.removedKeys.length;
|
|
2113
2344
|
paths += r.removedPaths.length;
|
|
2114
2345
|
}
|
|
@@ -2139,7 +2370,7 @@ async function cmdList(source, kind, selector, opts) {
|
|
|
2139
2370
|
try {
|
|
2140
2371
|
printArtifactList(await spinner("Discovering…", () => {
|
|
2141
2372
|
if (kind) return select(root, kind, selector);
|
|
2142
|
-
return listAll(root);
|
|
2373
|
+
return selector ? selectAll(root, selector) : listAll(root);
|
|
2143
2374
|
}, (a) => `Found ${a.length} artifact${a.length === 1 ? "" : "s"}`, 520), {
|
|
2144
2375
|
title: "Available in source",
|
|
2145
2376
|
subtitle: source
|
|
@@ -2238,22 +2469,26 @@ async function cmdUpdateAssets(kind, arg1, arg2, opts) {
|
|
|
2238
2469
|
}, 520);
|
|
2239
2470
|
const { root, cleanup } = await resolveSourceWithUi(source, opts);
|
|
2240
2471
|
try {
|
|
2241
|
-
const artifacts = await spinner("Discovering…", () => kind === "all" ? listAll(root) : select(root, kind, selector), (a) => `Found ${a.length} to update`, 620);
|
|
2472
|
+
const artifacts = await spinner("Discovering…", () => kind === "all" ? selector ? selectAll(root, selector) : listAll(root) : select(root, kind, selector), (a) => `Found ${a.length} to update`, 620);
|
|
2242
2473
|
if (!artifacts.length) {
|
|
2243
2474
|
warn(`Nothing found in ${source}.`);
|
|
2244
2475
|
return;
|
|
2245
2476
|
}
|
|
2246
2477
|
let chosen = artifacts;
|
|
2247
|
-
if (!selector &&
|
|
2248
|
-
|
|
2249
|
-
|
|
2478
|
+
if (!selector && artifacts.length > 1 && !opts.all && !opts.yes && !isQuiet()) {
|
|
2479
|
+
const noun = kind === "all" ? "artifacts" : `${kind}s`;
|
|
2480
|
+
await sectionTransition("source discovery", `select ${noun} to update`);
|
|
2481
|
+
const picked = await selectArtifacts(`Select ${noun} to update`, artifacts);
|
|
2250
2482
|
if (isCancelLike(picked)) {
|
|
2251
2483
|
outro("Cancelled.");
|
|
2252
2484
|
return;
|
|
2253
2485
|
}
|
|
2254
2486
|
chosen = picked;
|
|
2255
|
-
await sectionTransition(`${chosen.length} ${
|
|
2256
|
-
} else if (!isQuiet() && !opts.agents.length && !opts.all && !opts.yes)
|
|
2487
|
+
await sectionTransition(`${chosen.length} ${chosen.length === 1 ? "artifact" : "artifacts"} selected`, "choose target agents");
|
|
2488
|
+
} else if (!isQuiet() && !opts.agents.length && !opts.all && !opts.yes) {
|
|
2489
|
+
const noun = kind === "all" ? chosen.length === 1 ? "artifact" : "artifacts" : `${kind}${chosen.length === 1 ? "" : "s"}`;
|
|
2490
|
+
await sectionTransition(`${chosen.length} ${noun} ready`, "choose target agents");
|
|
2491
|
+
}
|
|
2257
2492
|
const agentList = await resolveAgentList(opts, "update");
|
|
2258
2493
|
if (!await ensureInstallScope(opts, "update")) return;
|
|
2259
2494
|
if (!isQuiet() && !opts.agents.length && !opts.all && !opts.yes) await sectionTransition(`${agentList.length} agent${agentList.length === 1 ? "" : "s"} selected`, "update summary");
|
|
@@ -2683,21 +2918,33 @@ async function main() {
|
|
|
2683
2918
|
return;
|
|
2684
2919
|
}
|
|
2685
2920
|
if (action === "add") {
|
|
2686
|
-
const
|
|
2687
|
-
if (!
|
|
2688
|
-
|
|
2921
|
+
const a1 = positional[1];
|
|
2922
|
+
if (!a1) throw new Error("Missing source or kind. e.g. agentry add author/repo or agentry add skills author/repo");
|
|
2923
|
+
const k1 = normalizeKind(a1);
|
|
2924
|
+
if (k1 === "profile") {
|
|
2689
2925
|
await cmdAddProfile(positional[2], positional[3], opts);
|
|
2690
2926
|
return;
|
|
2691
2927
|
}
|
|
2692
|
-
if (
|
|
2693
|
-
|
|
2928
|
+
if (k1 === "all" || isKind(k1)) {
|
|
2929
|
+
const source = positional[2];
|
|
2930
|
+
if (!source) throw new Error(`Missing source. e.g. agentry add ${k1} author/repo`);
|
|
2931
|
+
await cmdAdd(k1, source, positional[3], opts);
|
|
2932
|
+
return;
|
|
2933
|
+
}
|
|
2934
|
+
const source = a1;
|
|
2935
|
+
const selector = positional[2];
|
|
2936
|
+
await cmdAdd("all", source, selector, opts);
|
|
2694
2937
|
return;
|
|
2695
2938
|
}
|
|
2696
2939
|
if (action === "remove" || action === "rm") {
|
|
2697
|
-
const
|
|
2698
|
-
if (!
|
|
2699
|
-
|
|
2700
|
-
|
|
2940
|
+
const a1 = positional[1];
|
|
2941
|
+
if (!a1) throw new Error("Missing kind or selector. e.g. agentry remove skills enhance-prompt");
|
|
2942
|
+
const k1 = normalizeKind(a1);
|
|
2943
|
+
if (k1 === "all" || isKind(k1)) {
|
|
2944
|
+
await cmdRemove(k1, positional[2], positional[3], opts);
|
|
2945
|
+
return;
|
|
2946
|
+
}
|
|
2947
|
+
await cmdRemove("all", positional[1], positional[2], opts);
|
|
2701
2948
|
return;
|
|
2702
2949
|
}
|
|
2703
2950
|
if (action === "init") {
|
package/package.json
CHANGED