@mhmdreza-rafiei/agentry 0.2.0 → 0.2.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/dist/cli.mjs CHANGED
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
- import { parseSource } from "./_chunks/source_parser2.mjs";
3
- import { animateLogo, badge, delay, printHelp, theme } from "./_chunks/theme2.mjs";
2
+ import { animateLogo, badge, delay, printHelp, sky, theme } from "./_chunks/theme2.mjs";
4
3
  import { execFileSync, execSync } from "node:child_process";
5
4
  import https from "node:https";
6
- import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
5
+ import { createHash } from "node:crypto";
6
+ import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
7
7
  import { homedir, tmpdir } from "node:os";
8
- import { join } from "node:path";
9
- import { xdgConfig } from "xdg-basedir";
10
- import { parse } from "yaml";
8
+ import { dirname, join, resolve, sep } from "node:path";
9
+ import { xdgCache, xdgConfig } from "xdg-basedir";
10
+ import { parse, stringify } from "yaml";
11
11
  import { z } from "zod";
12
12
  import * as clack from "@clack/prompts";
13
+ import * as readline from "node:readline";
13
14
  import c from "picocolors";
14
15
  import { determineAgent } from "@vercel/detect-agent";
15
- import * as readline from "node:readline";
16
16
  import { stripVTControlCharacters } from "node:util";
17
17
  import { Writable } from "node:stream";
18
18
  var name = "@mhmdreza-rafiei/agentry";
@@ -24,7 +24,165 @@ const ARTIFACT_KINDS = [
24
24
  "profile",
25
25
  "script"
26
26
  ];
27
- function resolveSource(source) {
27
+ function isLocalPath(source) {
28
+ if (!source) return false;
29
+ if (source === "." || source === "..") return true;
30
+ if (source.startsWith("./") || source.startsWith("../") || source.startsWith("/") || source.startsWith("~")) return true;
31
+ try {
32
+ return existsSync(source) && statSync(source).isDirectory();
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+ function parseGithubTreeUrl(url) {
38
+ const m = url.match(/^https?:\/\/github\.com\/([\w.-]+\/[\w.-]+?)\/tree\/([^/]+)\/(.+)$/);
39
+ if (!m) return null;
40
+ return {
41
+ ownerRepo: m[1],
42
+ ref: m[2],
43
+ subpath: m[3].replace(/\/+$/, "")
44
+ };
45
+ }
46
+ function parseSource(source) {
47
+ if (!source || typeof source !== "string") throw new Error("Empty source.");
48
+ const raw = source;
49
+ if (isLocalPath(source)) return {
50
+ kind: "local",
51
+ raw,
52
+ url: null,
53
+ subpath: "",
54
+ local: resolve(source.replace(/^~/, homedir()))
55
+ };
56
+ const tree = parseGithubTreeUrl(source);
57
+ if (tree) return {
58
+ kind: "github-url",
59
+ raw,
60
+ url: `https://github.com/${tree.ownerRepo}.git`,
61
+ subpath: tree.subpath,
62
+ ref: tree.ref,
63
+ local: null
64
+ };
65
+ let m = source.match(/^https?:\/\/github\.com\/([\w.-]+\/[\w.-]+?)(?:\.git)?\/?$/);
66
+ if (m) return {
67
+ kind: "github-url",
68
+ raw,
69
+ url: `https://github.com/${m[1]}.git`,
70
+ subpath: "",
71
+ local: null
72
+ };
73
+ m = source.match(/^https?:\/\/gitlab\.com\/([\w./-]+?)(?:\.git)?\/?$/);
74
+ if (m) return {
75
+ kind: "gitlab-url",
76
+ raw,
77
+ url: `https://gitlab.com/${m[1]}.git`,
78
+ subpath: "",
79
+ local: null
80
+ };
81
+ if (source.startsWith("git@") || /^https?:\/\/.*\.git$/.test(source)) return {
82
+ kind: "git-url",
83
+ raw,
84
+ url: source,
85
+ subpath: "",
86
+ local: null
87
+ };
88
+ if (/^[\w.-]+\/[\w.-]+$/.test(source)) return {
89
+ kind: "github-shorthand",
90
+ raw,
91
+ url: `https://github.com/${source}.git`,
92
+ subpath: "",
93
+ local: null
94
+ };
95
+ throw new Error(`Cannot parse source "${source}". Use author/repo, a GitHub/GitLab URL, a git URL, or a local path.`);
96
+ }
97
+ function sourceIdentity(source) {
98
+ try {
99
+ const p = parseSource(source);
100
+ if (p.kind === "local" && p.local) return `local:${resolve(p.local).replace(/\\/g, "/").toLowerCase()}`;
101
+ return `git:${(p.url || "").replace(/\.git$/i, "").replace(/\/+$/, "").toLowerCase()}${p.subpath ? `/${p.subpath.replace(/^\/+|\/+$/g, "")}` : ""}${p.ref ? `@${p.ref}` : ""}`;
102
+ } catch {
103
+ return `raw:${source.trim().toLowerCase()}`;
104
+ }
105
+ }
106
+ function sourcesEqual(a, b) {
107
+ if (!a || !b) return false;
108
+ if (a === b) return true;
109
+ return sourceIdentity(a) === sourceIdentity(b);
110
+ }
111
+ function isExplicitSource(arg) {
112
+ if (!arg) return false;
113
+ if (isLocalPath(arg)) return true;
114
+ if (/^https?:\/\//i.test(arg)) return true;
115
+ if (arg.startsWith("git@")) return true;
116
+ if (/^https?:\/\/.*\.git$/i.test(arg)) return true;
117
+ return false;
118
+ }
119
+ function isOwnerRepoShape(arg) {
120
+ return /^[\w.-]+\/[\w.-]+$/.test(arg);
121
+ }
122
+ function cacheRoot() {
123
+ return join(xdgCache ?? join(homedir(), ".cache"), "agentry", "repos");
124
+ }
125
+ function cacheKey(parsed) {
126
+ const id = sourceIdentity(parsed.raw);
127
+ return createHash("sha256").update(id).digest("hex").slice(0, 20);
128
+ }
129
+ function cacheDirFor(parsed) {
130
+ return join(cacheRoot(), cacheKey(parsed));
131
+ }
132
+ function saveToCache(parsed, clonedRoot) {
133
+ const dest = cacheDirFor(parsed);
134
+ try {
135
+ rmSync(dest, {
136
+ recursive: true,
137
+ force: true
138
+ });
139
+ mkdirSync(join(dest, ".."), { recursive: true });
140
+ cpSync(clonedRoot, dest, { recursive: true });
141
+ } catch {}
142
+ }
143
+ function cloneTo(url, dest, ref) {
144
+ execFileSync("git", [
145
+ "clone",
146
+ "--quiet",
147
+ ...ref ? [
148
+ "--branch",
149
+ ref,
150
+ "--depth",
151
+ "1"
152
+ ] : ["--depth", "1"],
153
+ url,
154
+ dest
155
+ ], { stdio: [
156
+ "ignore",
157
+ "ignore",
158
+ "pipe"
159
+ ] });
160
+ }
161
+ function withSubpath(root, subpath, cleanupRoot) {
162
+ if (!subpath) return {
163
+ root,
164
+ cleanup: () => rmSync(cleanupRoot, {
165
+ recursive: true,
166
+ force: true
167
+ })
168
+ };
169
+ const sub = join(root, subpath);
170
+ if (!existsSync(sub)) {
171
+ rmSync(cleanupRoot, {
172
+ recursive: true,
173
+ force: true
174
+ });
175
+ throw new Error(`Subpath "${subpath}" not found.`);
176
+ }
177
+ return {
178
+ root: sub,
179
+ cleanup: () => rmSync(cleanupRoot, {
180
+ recursive: true,
181
+ force: true
182
+ })
183
+ };
184
+ }
185
+ async function resolveSource(source, opts = {}) {
28
186
  const parsed = typeof source === "string" ? parseSource(source) : source;
29
187
  if (parsed.kind === "local" && parsed.local) {
30
188
  if (!existsSync(parsed.local)) throw new Error(`Path not found: ${parsed.raw}`);
@@ -37,54 +195,33 @@ function resolveSource(source) {
37
195
  if (!url) throw new Error(`Cannot resolve source "${parsed.raw}".`);
38
196
  const tmp = mkdtempSync(join(tmpdir(), "agentry-src-"));
39
197
  try {
40
- execFileSync("git", [
41
- "clone",
42
- "--quiet",
43
- ...parsed.ref ? [
44
- "--branch",
45
- parsed.ref,
46
- "--depth",
47
- "1"
48
- ] : ["--depth", "1"],
49
- url,
50
- tmp
51
- ], { stdio: [
52
- "ignore",
53
- "ignore",
54
- "pipe"
55
- ] });
198
+ cloneTo(url, tmp, parsed.ref);
56
199
  } catch (e) {
57
200
  rmSync(tmp, {
58
201
  recursive: true,
59
202
  force: true
60
203
  });
61
204
  const detail = e.stderr ? e.stderr.toString().trim() : e.message;
62
- throw new Error(`git clone failed for ${url}\n${detail}`);
63
- }
64
- if (parsed.subpath) {
65
- const sub = join(tmp, parsed.subpath);
66
- if (!existsSync(sub)) {
67
- rmSync(tmp, {
68
- recursive: true,
69
- force: true
70
- });
71
- throw new Error(`Subpath "${parsed.subpath}" not found in ${url}.`);
205
+ const cached = cacheDirFor(parsed);
206
+ const cacheOk = existsSync(cached) && existsSync(join(cached, ".git"));
207
+ if (cacheOk && opts.confirmCache) {
208
+ if (await opts.confirmCache({
209
+ url,
210
+ cacheDir: cached,
211
+ error: detail
212
+ })) {
213
+ const work = mkdtempSync(join(tmpdir(), "agentry-src-"));
214
+ cpSync(cached, work, { recursive: true });
215
+ return {
216
+ ...withSubpath(work, parsed.subpath, work),
217
+ fromCache: true
218
+ };
219
+ }
72
220
  }
73
- return {
74
- root: sub,
75
- cleanup: () => rmSync(tmp, {
76
- recursive: true,
77
- force: true
78
- })
79
- };
221
+ throw new Error(`git clone failed for ${url}\n${detail}${cacheOk ? "\n(A local cache exists — re-run interactively to load it, or pass -y to auto-use cache.)" : ""}`);
80
222
  }
81
- return {
82
- root: tmp,
83
- cleanup: () => rmSync(tmp, {
84
- recursive: true,
85
- force: true
86
- })
87
- };
223
+ saveToCache(parsed, tmp);
224
+ return withSubpath(tmp, parsed.subpath, tmp);
88
225
  }
89
226
  const SKILL_MARKER = "SKILL.md";
90
227
  const FILE_EXT = ".mdc";
@@ -458,10 +595,74 @@ function removeLegacyLockIfPresent(opts) {
458
595
  }
459
596
  function writeLock(opts, lock) {
460
597
  const file = lockPath(opts);
598
+ if (!Object.keys(lock.items).length) {
599
+ const dir = join(lockBase(opts), ".agentry");
600
+ if (existsSync(dir)) rmSync(dir, {
601
+ recursive: true,
602
+ force: true
603
+ });
604
+ removeLegacyLockIfPresent(opts);
605
+ return;
606
+ }
461
607
  mkdirSync(join(file, ".."), { recursive: true });
462
608
  writeFileSync(file, JSON.stringify(lock, null, 2) + "\n");
463
609
  removeLegacyLockIfPresent(opts);
464
610
  }
611
+ function isDirTreeEmpty(dir) {
612
+ if (!existsSync(dir)) return true;
613
+ let entries;
614
+ try {
615
+ entries = readdirSync(dir);
616
+ } catch {
617
+ return true;
618
+ }
619
+ for (const name of entries) {
620
+ const p = join(dir, name);
621
+ let st;
622
+ try {
623
+ st = statSync(p);
624
+ } catch {
625
+ continue;
626
+ }
627
+ if (st.isDirectory()) {
628
+ if (!isDirTreeEmpty(p)) return false;
629
+ continue;
630
+ }
631
+ if (st.isFile()) {
632
+ if (name === "lock.json") {
633
+ try {
634
+ const data = JSON.parse(readFileSync(p, "utf8"));
635
+ if (data && typeof data === "object" && Object.keys(data.items || {}).length === 0) continue;
636
+ } catch {
637
+ return false;
638
+ }
639
+ return false;
640
+ }
641
+ return false;
642
+ }
643
+ }
644
+ return true;
645
+ }
646
+ function pruneEmptyAncestors(dir, stopAt, removedPaths = []) {
647
+ const stop = resolve(stopAt);
648
+ let cur = resolve(dir);
649
+ while (cur.length > stop.length) {
650
+ const prefix = stop.endsWith(sep) ? stop : stop + sep;
651
+ if (!cur.toLowerCase().startsWith(prefix.toLowerCase()) && cur.toLowerCase() !== stop.toLowerCase()) break;
652
+ if (!existsSync(cur)) {
653
+ cur = dirname(cur);
654
+ continue;
655
+ }
656
+ if (!isDirTreeEmpty(cur)) break;
657
+ rmSync(cur, {
658
+ recursive: true,
659
+ force: true
660
+ });
661
+ removedPaths.push(cur);
662
+ cur = dirname(cur);
663
+ }
664
+ return removedPaths;
665
+ }
465
666
  function listInstalled(opts) {
466
667
  return Object.entries(readLock(opts).items).map(([id, v]) => ({
467
668
  id,
@@ -489,12 +690,7 @@ function installOne(artifact, opts, agentList, source) {
489
690
  force: true
490
691
  });
491
692
  mkdirSync(join(canonical, ".."), { recursive: true });
492
- if (opts.copy) cpSync(artifact.dir, canonical, { recursive: true });
493
- else try {
494
- symlinkSync(artifact.dir, canonical, "junction");
495
- } catch {
496
- cpSync(artifact.dir, canonical, { recursive: true });
497
- }
693
+ cpSync(artifact.dir, canonical, { recursive: true });
498
694
  for (const a of list) if (a.skillsDir === ".agents/skills") dests.push({
499
695
  agent: a.name,
500
696
  dir: canonical
@@ -534,12 +730,7 @@ function installOne(artifact, opts, agentList, source) {
534
730
  force: true
535
731
  });
536
732
  mkdirSync(join(dir, ".."), { recursive: true });
537
- if (opts.copy) cpSync(artifact.dir, dir, { recursive: true });
538
- else try {
539
- symlinkSync(artifact.dir, dir, "junction");
540
- } catch {
541
- cpSync(artifact.dir, dir, { recursive: true });
542
- }
733
+ cpSync(artifact.dir, dir, { recursive: true });
543
734
  dests.push({
544
735
  agent: a.name,
545
736
  dir
@@ -563,37 +754,115 @@ function agentDir(artifact, a, base) {
563
754
  }
564
755
  return join(base, a.configDir || a.skillsDir, pluralKind(artifact.kind), artifact.id);
565
756
  }
566
- function removeSelection(kind, selector, opts, agentList) {
757
+ function removeSelection(kind, selector, opts, agentList, sourceFilter) {
567
758
  const base = lockBase(opts);
568
759
  const list = agentList.length ? agentList : [getAgent("cursor")];
569
760
  const rel = [kind, selector].filter(Boolean).join("/");
570
761
  const removedKeys = [];
762
+ const removedPaths = [];
571
763
  const lock = readLock(opts);
572
- const pkind = pluralKind(kind);
573
- for (const a of list) {
574
- let dir;
575
- if (kind === "skill") dir = join(base, a.skillsDir, selector || "");
576
- else if (kind === "agent" || kind === "rule" || kind === "profile") {
577
- const ext = kind === "profile" ? ".yaml" : ".mdc";
578
- if (!selector) dir = join(base, a.configDir || a.skillsDir, pkind);
579
- else if (selector.includes("/")) {
580
- const [cat, name] = selector.split("/");
581
- dir = join(base, a.configDir || a.skillsDir, pkind, cat, name + ext);
582
- } else dir = join(base, a.configDir || a.skillsDir, pkind, selector + ext);
583
- } else dir = join(base, a.configDir || a.skillsDir, pkind, selector || "");
584
- if (existsSync(dir)) rmSync(dir, {
764
+ const matchingKeys = Object.keys(lock.items).filter((key) => {
765
+ const entry = lock.items[key];
766
+ if (sourceFilter && !sourcesEqual(entry.source, sourceFilter)) return false;
767
+ if (kind === "all") return true;
768
+ if (key === rel || key.startsWith(rel + "/")) return true;
769
+ if (!selector && key.startsWith(kind + "/")) return true;
770
+ return false;
771
+ });
772
+ const rmPath = (p) => {
773
+ if (!p || !existsSync(p)) return;
774
+ const parent = dirname(p);
775
+ rmSync(p, {
585
776
  recursive: true,
586
777
  force: true
587
778
  });
588
- }
589
- for (const key of Object.keys(lock.items)) if (key === rel || key.startsWith(rel + "/")) {
779
+ removedPaths.push(p);
780
+ pruneEmptyAncestors(parent, base, removedPaths);
781
+ };
782
+ const agentsFor = (entry) => {
783
+ const fromEntry = entry.agents.map((n) => getAgent(n)).filter(Boolean);
784
+ const byName = /* @__PURE__ */ new Map();
785
+ for (const a of [...fromEntry, ...list]) byName.set(a.name, a);
786
+ return [...byName.values()];
787
+ };
788
+ const removeFilesForKey = (key, entry) => {
789
+ const parts = key.split("/");
790
+ const k = parts[0];
791
+ const sel = parts.slice(1).join("/") || void 0;
792
+ if (!sel && k === "skill") return;
793
+ const agents = agentsFor(entry);
794
+ if (k === "skill" && sel) {
795
+ rmPath(join(base, ".agents", "skills", sel));
796
+ for (const a of agents) {
797
+ if (a.skillsDir === ".agents/skills") continue;
798
+ rmPath(join(base, a.skillsDir, sel));
799
+ }
800
+ return;
801
+ }
802
+ for (const a of agents) if (k === "agent" || k === "rule" || k === "profile") {
803
+ const ext = k === "profile" ? ".yaml" : ".mdc";
804
+ if (!sel) rmPath(join(base, a.configDir || a.skillsDir, pluralKind(k)));
805
+ else if (sel.includes("/")) {
806
+ const [cat, name] = sel.split("/");
807
+ rmPath(join(base, a.configDir || a.skillsDir, pluralKind(k), cat, name + ext));
808
+ } else rmPath(join(base, a.configDir || a.skillsDir, pluralKind(k), sel + ext));
809
+ } else if (sel) rmPath(join(base, a.configDir || a.skillsDir, pluralKind(k), sel));
810
+ };
811
+ for (const key of matchingKeys) {
812
+ const entry = lock.items[key];
813
+ removeFilesForKey(key, entry);
590
814
  delete lock.items[key];
591
815
  removedKeys.push(key);
592
816
  }
817
+ if (!removedKeys.length && !sourceFilter) {
818
+ if (kind === "skill") rmPath(join(base, ".agents", "skills", selector || ""));
819
+ for (const a of list) {
820
+ let dir;
821
+ if (kind === "skill") {
822
+ if (a.skillsDir === ".agents/skills") continue;
823
+ dir = join(base, a.skillsDir, selector || "");
824
+ } else if (kind === "agent" || kind === "rule" || kind === "profile") {
825
+ const ext = kind === "profile" ? ".yaml" : ".mdc";
826
+ if (!selector) dir = join(base, a.configDir || a.skillsDir, pluralKind(kind));
827
+ else if (selector.includes("/")) {
828
+ const [cat, name] = selector.split("/");
829
+ dir = join(base, a.configDir || a.skillsDir, pluralKind(kind), cat, name + ext);
830
+ } else dir = join(base, a.configDir || a.skillsDir, pluralKind(kind), selector + ext);
831
+ } else dir = join(base, a.configDir || a.skillsDir, pluralKind(kind), selector || "");
832
+ if (existsSync(dir)) {
833
+ rmPath(dir);
834
+ if (!removedKeys.includes(rel)) removedKeys.push(rel);
835
+ }
836
+ }
837
+ for (const key of Object.keys(lock.items)) if (key === rel || key.startsWith(rel + "/")) {
838
+ delete lock.items[key];
839
+ if (!removedKeys.includes(key)) removedKeys.push(key);
840
+ }
841
+ }
593
842
  writeLock(opts, lock);
843
+ const sweepRoots = /* @__PURE__ */ new Set([
844
+ join(base, ".agents", "skills"),
845
+ join(base, ".agents"),
846
+ join(base, ".agentry")
847
+ ]);
848
+ for (const a of list) {
849
+ sweepRoots.add(join(base, a.skillsDir));
850
+ if (a.configDir) {
851
+ sweepRoots.add(join(base, a.configDir));
852
+ for (const pk of [
853
+ "skills",
854
+ "agents",
855
+ "rules",
856
+ "profiles",
857
+ "scripts"
858
+ ]) sweepRoots.add(join(base, a.configDir, pk));
859
+ }
860
+ }
861
+ for (const root of sweepRoots) if (existsSync(root) && isDirTreeEmpty(root)) pruneEmptyAncestors(root, base, removedPaths);
594
862
  return {
595
- existed: removedKeys.length > 0,
596
- removedKeys
863
+ existed: removedKeys.length > 0 || removedPaths.length > 0,
864
+ removedKeys,
865
+ removedPaths
597
866
  };
598
867
  }
599
868
  const ProfileArtifactRefSchema = z.object({
@@ -636,6 +905,98 @@ function loadProfile(name, baseDir) {
636
905
  profile: loadProfileFile(file)
637
906
  };
638
907
  }
908
+ function ensureDir(file) {
909
+ mkdirSync(join(file, ".."), { recursive: true });
910
+ }
911
+ function writeNew(file, content, force = false) {
912
+ if (existsSync(file) && !force) throw new Error(`Already exists: ${file}`);
913
+ ensureDir(file);
914
+ writeFileSync(file, content);
915
+ return file;
916
+ }
917
+ function scaffoldSkill(opts) {
918
+ const dir = join(opts.baseDir || process.cwd(), opts.category ? join("skills", opts.category, opts.name) : join("skills", opts.name));
919
+ const id = opts.category ? `${opts.category}/${opts.name}` : opts.name;
920
+ const desc = opts.description || `${opts.name} skill`;
921
+ const skillMd = `---
922
+ name: ${opts.name}
923
+ description: ${desc}
924
+ ---
925
+
926
+ # ${opts.name}
927
+
928
+ ${desc}
929
+
930
+ ## When to use
931
+
932
+ Describe when this skill should run.
933
+
934
+ ## Instructions
935
+
936
+ 1. …
937
+ `;
938
+ const paths = [writeNew(join(dir, "SKILL.md"), skillMd)];
939
+ if (opts.reference !== false) paths.push(writeNew(join(dir, "references", "TEMPLATE.md"), `# ${opts.name} — reference
940
+
941
+ Fill in details here. Keep SKILL.md lean; put depth in references/.
942
+ `));
943
+ return {
944
+ paths,
945
+ id
946
+ };
947
+ }
948
+ function scaffoldMdc(opts) {
949
+ const base = opts.baseDir || process.cwd();
950
+ const folder = opts.kind === "agent" ? "agents" : "rules";
951
+ const rel = opts.category ? join(folder, opts.category, `${opts.name}.mdc`) : join(folder, `${opts.name}.mdc`);
952
+ const id = opts.category ? `${opts.category}/${opts.name}` : opts.name;
953
+ const desc = opts.description || `${opts.name} ${opts.kind}`;
954
+ const always = opts.kind === "rule" && opts.alwaysApply ? "alwaysApply: true\n" : opts.kind === "rule" ? "alwaysApply: false\n" : "";
955
+ const body = `---
956
+ name: ${opts.name}
957
+ description: ${desc}
958
+ ${always}---
959
+
960
+ # ${opts.name}
961
+
962
+ ${desc}
963
+ `;
964
+ return {
965
+ paths: [writeNew(join(base, rel), body)],
966
+ id
967
+ };
968
+ }
969
+ function scaffoldScript(opts) {
970
+ const dir = join(opts.baseDir || process.cwd(), opts.category ? join("scripts", opts.category, opts.name) : join("scripts", opts.name));
971
+ const id = opts.category ? `${opts.category}/${opts.name}` : opts.name;
972
+ const desc = opts.description || `${opts.name} script`;
973
+ return {
974
+ paths: [writeNew(join(dir, "README.md"), `# ${opts.name}
975
+
976
+ ${desc}
977
+
978
+ Place runnable scripts for this use case in this folder.
979
+ `)],
980
+ id
981
+ };
982
+ }
983
+ function scaffoldProfile(opts) {
984
+ return {
985
+ paths: [writeNew(join(opts.baseDir || process.cwd(), opts.localConfig ? "profile" : "profiles", `${opts.name}.yaml`), stringify({
986
+ name: opts.name,
987
+ description: opts.description || `${opts.name} profile`,
988
+ scope: "project",
989
+ targets: { agents: opts.agents?.length ? opts.agents : ["cursor"] },
990
+ artifacts: {
991
+ skills: opts.skills ?? [],
992
+ rules: opts.rules ?? [],
993
+ agents: opts.agentsArtifacts ?? [],
994
+ scripts: opts.scripts ?? []
995
+ }
996
+ }))],
997
+ id: opts.name
998
+ };
999
+ }
639
1000
  let runningInsideAgent = false;
640
1001
  try {
641
1002
  const result = await determineAgent();
@@ -651,17 +1012,17 @@ const silentOutput = new Writable({ write(_chunk, _encoding, callback) {
651
1012
  } });
652
1013
  const cancelSymbol = Symbol("cancel");
653
1014
  const ALL_VALUE = "__all__";
654
- const BLUE = c.blue;
655
- const BLUE_BOLD = (s) => c.bold(c.blue(s));
656
- const S_STEP_ACTIVE = BLUE("◆");
1015
+ const BLUE = sky;
1016
+ const BLUE_BOLD = (s) => c.bold(sky(s));
1017
+ const S_STEP_ACTIVE = sky("◆");
657
1018
  const S_STEP_CANCEL = c.red("■");
658
- const S_STEP_SUBMIT = BLUE("◇");
659
- const S_RADIO_ACTIVE = BLUE("●");
1019
+ const S_STEP_SUBMIT = sky("◇");
1020
+ const S_RADIO_ACTIVE = sky("●");
660
1021
  const S_RADIO_INACTIVE = c.dim("○");
661
1022
  const S_RADIO_DISABLED = c.dim("◌");
662
- const S_BULLET = BLUE("•");
1023
+ const S_BULLET = sky("•");
663
1024
  const S_BAR = c.dim("│");
664
- const S_BAR_H = c.dim("─");
1025
+ c.dim("─");
665
1026
  const S_CORNER = c.dim("└");
666
1027
  const S_BOX_TL = c.dim("┌");
667
1028
  const S_BOX_TR = c.dim("┐");
@@ -764,7 +1125,7 @@ function renderInfoBox(fields, innerWidth, rows) {
764
1125
  });
765
1126
  const body = expanded.map((r) => {
766
1127
  if (!r.label && !r.value) return `${S_BOX_V} ${" ".repeat(contentWidth)} ${S_BOX_V}`;
767
- const content = `${r.label ? `${c.blue(padPlain(r.label, labelW))} ` : `${" ".repeat(labelW)} `}${r.cont ? c.dim(r.value) : c.bold(r.value)}`;
1128
+ const content = `${r.label ? `${sky(padPlain(r.label, labelW))} ` : `${" ".repeat(labelW)} `}${r.cont ? c.dim(r.value) : c.bold(r.value)}`;
768
1129
  const plainW = approxStringWidth(stripVTControlCharacters(content));
769
1130
  const pad = Math.max(0, contentWidth - plainW);
770
1131
  return `${S_BOX_V} ${content}${" ".repeat(pad)} ${S_BOX_V}`;
@@ -859,6 +1220,7 @@ async function searchMultiselect(options) {
859
1220
  });
860
1221
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
861
1222
  readline.emitKeypressEvents(process.stdin, rl);
1223
+ if (process.stdout.isTTY) process.stdout.write("\x1B[?25l");
862
1224
  let query = "";
863
1225
  let cursor = 0;
864
1226
  const selected = new Set(initialSelected.filter((v) => String(v) !== ALL_VALUE));
@@ -873,6 +1235,32 @@ async function searchMultiselect(options) {
873
1235
  const matched = rest.filter((item) => item.label.toLowerCase().includes(lowerQ) || String(item.value).toLowerCase().includes(lowerQ) || item.hint && item.hint.toLowerCase().includes(lowerQ));
874
1236
  return allItem && (allItem.label.toLowerCase().includes(lowerQ) || "all".includes(lowerQ)) && allItem ? [allItem, ...matched] : matched;
875
1237
  };
1238
+ const lockedTotal = lockedSection ? lockedSection.items.length + (lockedSection.hiddenCount ?? 0) : 0;
1239
+ const selectedSummaryLine = (allOn) => {
1240
+ if (allOn) {
1241
+ const extra = lockedTotal ? `Universal (${lockedTotal}) + All additional (${uniqueItems.length})` : `All ${uniqueItems.length}`;
1242
+ return `${S_BAR} ${BLUE("Selected:")} ${c.bold(extra)}`;
1243
+ }
1244
+ const labels = uniqueItems.filter((item) => selected.has(item.value)).map((item) => item.label);
1245
+ if (labels.length === 0) {
1246
+ if (requiredFlash) return `${S_BAR} ${c.red("Selected: pick at least one")}`;
1247
+ if (lockedTotal > 0) return `${S_BAR} ${BLUE("Selected:")} ${c.bold(`Universal (${lockedTotal})`)}`;
1248
+ return `${S_BAR} ${c.dim("Selected: (none)")}`;
1249
+ }
1250
+ const prefix = lockedTotal > 0 ? `Universal (${lockedTotal}) + ` : "";
1251
+ const summary = labels.length <= 3 ? labels.join(", ") : `${labels.slice(0, 3).join(", ")} +${labels.length - 3} more`;
1252
+ return `${S_BAR} ${BLUE("Selected:")} ${prefix}${summary}`;
1253
+ };
1254
+ const submitSummary = (allOn) => {
1255
+ if (allOn) return lockedTotal ? `Universal (${lockedTotal}) + All additional (${uniqueItems.length})` : `All (${uniqueItems.length})`;
1256
+ const labels = uniqueItems.filter((item) => selected.has(item.value)).map((item) => item.label);
1257
+ if (labels.length === 0) return lockedTotal > 0 ? `Universal (${lockedTotal})` : "none";
1258
+ return (lockedTotal > 0 ? `Universal (${lockedTotal}) + ` : "") + (labels.length <= 5 ? labels.join(", ") : `${labels.slice(0, 5).join(", ")} +${labels.length - 5}`);
1259
+ };
1260
+ const clearPrevious = (rows) => {
1261
+ if (rows <= 0) return;
1262
+ for (let i = 0; i < rows; i++) process.stdout.write("\x1B[1A\x1B[2K");
1263
+ };
876
1264
  const render = (state = "active") => {
877
1265
  const lines = [];
878
1266
  const filtered = getFiltered();
@@ -881,14 +1269,9 @@ async function searchMultiselect(options) {
881
1269
  lines.push(`${icon} ${c.bold(message)}`);
882
1270
  lines.push(`${S_BAR}`);
883
1271
  if (state === "active") {
884
- if (lockedSection && lockedSection.items.length > 0) {
885
- const lockedTitle = `${c.bold(lockedSection.title)} ${c.dim("── always included")}`;
886
- lines.push(`${S_BAR} ${S_BAR_H}${S_BAR_H} ${lockedTitle} ${S_BAR_H.repeat(8)}`);
887
- for (const item of lockedSection.items.slice(0, 3)) lines.push(`${S_BAR} ${S_BULLET} ${c.bold(item.label)}`);
888
- const more = (lockedSection.hiddenCount ?? 0) + Math.max(0, lockedSection.items.length - 3);
889
- if (more > 0) lines.push(`${S_BAR} ${c.dim(`…and ${more} more`)}`);
890
- lines.push(`${S_BAR}`);
891
- lines.push(`${S_BAR} ${S_BAR_H}${S_BAR_H} ${c.bold("Additional")} ${S_BAR_H.repeat(26)}`);
1272
+ if (lockedSection && lockedTotal > 0) {
1273
+ lines.push(`${S_BAR} ${S_BULLET} ${c.bold(lockedSection.title)} ${c.dim(`· ${lockedTotal} always included`)}`);
1274
+ lines.push(`${S_BAR} ${c.bold("Additional")}`);
892
1275
  lines.push(`${S_BAR}`);
893
1276
  }
894
1277
  if (searchable) {
@@ -938,28 +1321,20 @@ async function searchMultiselect(options) {
938
1321
  }
939
1322
  if (showSelectedSummary) {
940
1323
  lines.push(`${S_BAR}`);
941
- if (allOn) lines.push(`${S_BAR} ${BLUE("Selected:")} ${c.bold(`All ${uniqueItems.length}`)}`);
942
- else {
943
- const labels = uniqueItems.filter((item) => selected.has(item.value)).map((item) => item.label);
944
- if (labels.length === 0) lines.push(`${S_BAR} ${requiredFlash ? c.red("Selected: pick at least one") : c.dim("Selected: (none)")}`);
945
- else {
946
- const summary = labels.length <= 3 ? labels.join(", ") : `${labels.slice(0, 3).join(", ")} +${labels.length - 3} more`;
947
- lines.push(`${S_BAR} ${BLUE("Selected:")} ${summary}`);
948
- }
949
- }
1324
+ lines.push(selectedSummaryLine(allOn));
950
1325
  }
951
1326
  lines.push(`${S_CORNER}`);
952
- } else if (state === "submit") {
953
- const labels = allOn ? [`All (${uniqueItems.length})`] : uniqueItems.filter((item) => selected.has(item.value)).map((item) => item.label);
954
- lines.push(`${S_BAR} ${c.dim(labels.join(", ") || "none")}`);
955
- } else lines.push(`${S_BAR} ${c.strikethrough(c.dim("Cancelled"))}`);
956
- const clearPreviousFrame = lastRenderHeight > 0 ? `\x1b[${lastRenderHeight}A\x1b[J` : "";
957
- process.stdout.write(clearPreviousFrame + lines.join("\n") + "\n");
1327
+ } else if (state === "submit") lines.push(`${S_BAR} ${c.dim(submitSummary(allOn))}`);
1328
+ else lines.push(`${S_BAR} ${c.strikethrough(c.dim("Cancelled"))}`);
1329
+ clearPrevious(lastRenderHeight);
1330
+ const out = lines.join("\n") + "\n";
1331
+ process.stdout.write(out);
958
1332
  lastRenderHeight = countVisualRowsForLines(lines, process.stdout.columns);
959
1333
  };
960
1334
  const cleanup = () => {
961
1335
  process.stdin.removeListener("keypress", keypressHandler);
962
1336
  if (process.stdin.isTTY) process.stdin.setRawMode(false);
1337
+ if (process.stdout.isTTY) process.stdout.write("\x1B[?25h");
963
1338
  rl.close();
964
1339
  };
965
1340
  const submit = () => {
@@ -1037,6 +1412,11 @@ async function searchMultiselect(options) {
1037
1412
  render();
1038
1413
  });
1039
1414
  }
1415
+ function agentsPrompt(action) {
1416
+ if (action === "remove") return "Which agents do you want to remove from?";
1417
+ if (action === "update") return "Which agents do you want to update for?";
1418
+ return "Which agents do you want to install to?";
1419
+ }
1040
1420
  function isQuiet() {
1041
1421
  return isCI() || !process.stdout.isTTY;
1042
1422
  }
@@ -1054,6 +1434,79 @@ function outro(message) {
1054
1434
  }
1055
1435
  clack.outro(message);
1056
1436
  }
1437
+ async function confirm(message) {
1438
+ if (isQuiet()) return true;
1439
+ return new Promise((resolve) => {
1440
+ if (!process.stdin.isTTY) {
1441
+ resolve(true);
1442
+ return;
1443
+ }
1444
+ const rl = readline.createInterface({
1445
+ input: process.stdin,
1446
+ output: process.stdout
1447
+ });
1448
+ process.stdin.setRawMode(true);
1449
+ readline.emitKeypressEvents(process.stdin, rl);
1450
+ let value = true;
1451
+ let lastH = 0;
1452
+ const paint = (done) => {
1453
+ const lines = [];
1454
+ if (done === "cancel") {
1455
+ lines.push(`${sky("■")} ${c.bold(message)}`);
1456
+ lines.push(`${c.dim("│")} ${c.strikethrough(c.dim("Cancelled"))}`);
1457
+ } else if (done === "yes" || done === "no") {
1458
+ lines.push(`${sky("◇")} ${c.bold(message)}`);
1459
+ lines.push(`${c.dim("│")} ${c.dim(done === "yes" ? "Yes" : "No")}`);
1460
+ } else {
1461
+ lines.push(`${sky("◆")} ${c.bold(message)}`);
1462
+ lines.push(`${c.dim("│")}`);
1463
+ const yes = value ? sky(c.bold("● Yes")) : c.dim("○ Yes");
1464
+ const no = !value ? sky(c.bold("● No")) : c.dim("○ No");
1465
+ lines.push(`${c.dim("│")} ${yes} / ${no}`);
1466
+ lines.push(`${c.dim("└")} ${c.dim("←/→ toggle · enter confirm")}`);
1467
+ }
1468
+ if (lastH > 0) for (let i = 0; i < lastH; i++) process.stdout.write("\x1B[1A\x1B[2K");
1469
+ process.stdout.write(lines.join("\n") + "\n");
1470
+ lastH = lines.length;
1471
+ };
1472
+ const cleanup = () => {
1473
+ process.stdin.removeListener("keypress", onKey);
1474
+ process.stdin.setRawMode(false);
1475
+ rl.close();
1476
+ };
1477
+ const onKey = (_s, key) => {
1478
+ if (!key) return;
1479
+ if (key.name === "left" || key.name === "right" || key.name === "tab") {
1480
+ value = !value;
1481
+ paint();
1482
+ return;
1483
+ }
1484
+ if (key.name === "y") {
1485
+ value = true;
1486
+ paint();
1487
+ return;
1488
+ }
1489
+ if (key.name === "n") {
1490
+ value = false;
1491
+ paint();
1492
+ return;
1493
+ }
1494
+ if (key.name === "return") {
1495
+ paint(value ? "yes" : "no");
1496
+ cleanup();
1497
+ resolve(value);
1498
+ return;
1499
+ }
1500
+ if (key.name === "escape" || key.ctrl && key.name === "c") {
1501
+ paint("cancel");
1502
+ cleanup();
1503
+ resolve(false);
1504
+ }
1505
+ };
1506
+ process.stdin.on("keypress", onKey);
1507
+ paint();
1508
+ });
1509
+ }
1057
1510
  function log(message) {
1058
1511
  process.stdout.write(message + "\n");
1059
1512
  }
@@ -1063,7 +1516,7 @@ function blank() {
1063
1516
  async function sectionTransition(doneLabel, nextLabel) {
1064
1517
  if (isQuiet()) return;
1065
1518
  blank();
1066
- log(`${c.blue("◇")} ${c.dim("Done:")} ${c.bold(doneLabel)}`);
1519
+ log(`${sky("◇")} ${c.dim("Done:")} ${c.bold(doneLabel)}`);
1067
1520
  await delay(320);
1068
1521
  for (const ch of [
1069
1522
  "│",
@@ -1074,15 +1527,15 @@ async function sectionTransition(doneLabel, nextLabel) {
1074
1527
  await delay(140);
1075
1528
  }
1076
1529
  await delay(200);
1077
- log(`${c.blue("◆")} ${c.bold("Next:")} ${nextLabel}`);
1530
+ log(`${sky("◆")} ${c.bold("Next:")} ${nextLabel}`);
1078
1531
  await delay(420);
1079
1532
  blank();
1080
1533
  }
1081
1534
  function step(message) {
1082
- process.stdout.write(` ${c.blue("→")} ${message}\n`);
1535
+ process.stdout.write(` ${sky("→")} ${message}\n`);
1083
1536
  }
1084
1537
  function ok(message) {
1085
- process.stdout.write(`${c.blue("✓")} ${message}\n`);
1538
+ process.stdout.write(`${sky("✓")} ${message}\n`);
1086
1539
  }
1087
1540
  function err(message) {
1088
1541
  process.stdout.write(`${c.red("✗")} ${message}\n`);
@@ -1091,13 +1544,13 @@ function warn(message) {
1091
1544
  process.stdout.write(`${c.yellow("⚠")} ${message}\n`);
1092
1545
  }
1093
1546
  function info(message) {
1094
- process.stdout.write(`${c.blue("ℹ")} ${message}\n`);
1547
+ process.stdout.write(`${sky("ℹ")} ${message}\n`);
1095
1548
  }
1096
1549
  async function spinner(startMsg, fn, stopWith, minMs = 480) {
1097
1550
  if (isQuiet()) {
1098
1551
  process.stdout.write(c.dim(`• ${startMsg}...`) + "\n");
1099
1552
  const r = await fn();
1100
- if (stopWith) process.stdout.write(`${c.blue("◇")} ${stopWith(r)}\n`);
1553
+ if (stopWith) process.stdout.write(`${sky("◇")} ${stopWith(r)}\n`);
1101
1554
  return r;
1102
1555
  }
1103
1556
  const frames = [
@@ -1114,11 +1567,11 @@ async function spinner(startMsg, fn, stopWith, minMs = 480) {
1114
1567
  ];
1115
1568
  let i = 0;
1116
1569
  let alive = true;
1117
- process.stdout.write(`${c.blue(frames[0])} ${startMsg}`);
1570
+ process.stdout.write(`${sky(frames[0])} ${startMsg}`);
1118
1571
  const tick = setInterval(() => {
1119
1572
  if (!alive) return;
1120
1573
  i = (i + 1) % frames.length;
1121
- process.stdout.write(`\r${c.blue(frames[i])} ${startMsg} `);
1574
+ process.stdout.write(`\r${sky(frames[i])} ${startMsg} `);
1122
1575
  }, 80);
1123
1576
  const started = Date.now();
1124
1577
  try {
@@ -1128,7 +1581,7 @@ async function spinner(startMsg, fn, stopWith, minMs = 480) {
1128
1581
  alive = false;
1129
1582
  clearInterval(tick);
1130
1583
  const msg = stopWith ? stopWith(result) : startMsg;
1131
- process.stdout.write(`\r\x1b[K${c.blue("◇")} ${msg}\n`);
1584
+ process.stdout.write(`\r\x1b[K${sky("◇")} ${msg}\n`);
1132
1585
  return result;
1133
1586
  } catch (e) {
1134
1587
  alive = false;
@@ -1235,23 +1688,23 @@ async function selectAgents(message, universal, others, detectedNames = []) {
1235
1688
  }));
1236
1689
  const locked = universal.length > 0 ? {
1237
1690
  title: "Universal",
1238
- items: universal.slice(0, 6).map((a) => ({
1691
+ items: universal.map((a) => ({
1239
1692
  value: a.name,
1240
1693
  label: a.displayName,
1241
1694
  fields: agentFields(a, true)
1242
1695
  })),
1243
- hiddenCount: Math.max(0, universal.length - 6)
1696
+ compact: true
1244
1697
  } : void 0;
1245
1698
  const res = await searchMultiselect({
1246
1699
  message,
1247
1700
  items,
1248
- maxVisible: 8,
1701
+ maxVisible: 5,
1249
1702
  required: false,
1250
1703
  initialSelected: detected.map((d) => d.name),
1251
1704
  lockedSection: locked,
1252
1705
  searchable: true,
1253
1706
  showDetail: true,
1254
- detailLines: 8,
1707
+ detailLines: 4,
1255
1708
  showSelectedSummary: true,
1256
1709
  includeAllOption: true,
1257
1710
  allLabel: `All additional (${uniqueOthers.length})`,
@@ -1273,12 +1726,12 @@ function installSummary(artifacts, agents, scopeLabel) {
1273
1726
  blank();
1274
1727
  const bar = c.dim("│");
1275
1728
  const corner = c.dim("└");
1276
- log(`${c.blue("◆")} ${c.bold("Install summary")}`);
1729
+ log(`${sky("◆")} ${c.bold("Install summary")}`);
1277
1730
  log(bar);
1278
- log(`${bar} ${c.blue("Scope")} ${scopeLabel}`);
1731
+ log(`${bar} ${sky("Scope")} ${scopeLabel}`);
1279
1732
  const universalCount = agents.filter((a) => a.skillsDir === ".agents/skills").length;
1280
1733
  const otherCount = agents.length - universalCount;
1281
- log(`${bar} ${c.blue("Targets")} ${agents.length} agent${agents.length === 1 ? "" : "s"}` + c.dim(` · ${universalCount} universal` + (otherCount ? ` · ${otherCount} additional` : "")));
1734
+ log(`${bar} ${sky("Targets")} ${agents.length} agent${agents.length === 1 ? "" : "s"}` + c.dim(` · ${universalCount} universal` + (otherCount ? ` · ${otherCount} additional` : "")));
1282
1735
  const names = agents.map((a) => a.displayName);
1283
1736
  const chunks = [];
1284
1737
  let line = "";
@@ -1297,7 +1750,7 @@ function installSummary(artifacts, agents, scopeLabel) {
1297
1750
  log(bar);
1298
1751
  for (const a of artifacts) {
1299
1752
  const desc = (a.description || "").replace(/\s+/g, " ").trim().slice(0, 52);
1300
- log(`${bar} ${c.blue("•")} ${c.bold(`${a.kind}/${a.id}`)}`);
1753
+ log(`${bar} ${sky("•")} ${c.bold(`${a.kind}/${a.id}`)}`);
1301
1754
  if (desc) log(`${bar} ${c.dim(desc)}`);
1302
1755
  }
1303
1756
  log(bar);
@@ -1307,16 +1760,29 @@ function installSummary(artifacts, agents, scopeLabel) {
1307
1760
  function preview(artifacts, agents) {
1308
1761
  installSummary(artifacts, agents, "preview (dry-run)");
1309
1762
  }
1763
+ async function resolveSourceWithUi(source, opts) {
1764
+ const resolved = await resolveSource(source, { confirmCache: async ({ url, error }) => {
1765
+ warn(`git clone failed for ${url}`);
1766
+ if (error) step(c.dim(error.split("\n")[0] || error));
1767
+ if (opts.yes || isQuiet()) {
1768
+ info("Using cached copy.");
1769
+ return true;
1770
+ }
1771
+ return confirm("Load from cache instead?");
1772
+ } });
1773
+ if (resolved.fromCache) info("Loaded source from cache.");
1774
+ return resolved;
1775
+ }
1310
1776
  async function cmdAdd(kind, source, selector, opts) {
1311
1777
  if (!source) throw new Error(`Missing source. e.g. agentry add ${kind} author/repo`);
1312
1778
  intro();
1313
1779
  await delayQuiet(180);
1314
1780
  await spinner("Parsing source…", () => parseSourceForUi(source), (p) => {
1315
1781
  const where = p.url || p.local || source;
1316
- return `${c.blue("Source:")} ${where}${p.ref ? c.dim(` @ ${p.ref}`) : ""}${p.subpath ? c.dim(` (${p.subpath})`) : ""}`;
1782
+ return `${theme.primary("Source:")} ${where}${p.ref ? c.dim(` @ ${p.ref}`) : ""}${p.subpath ? c.dim(` (${p.subpath})`) : ""}`;
1317
1783
  }, 520);
1318
1784
  await delayQuiet(280);
1319
- const { root, cleanup } = resolveSource(source);
1785
+ const { root, cleanup } = await resolveSourceWithUi(source, opts);
1320
1786
  try {
1321
1787
  const artifacts = await spinner("Discovering…", () => kind === "all" ? listAll(root) : select(root, kind, selector), (a) => {
1322
1788
  const noun = kind === "all" ? "artifact" : kind;
@@ -1329,7 +1795,7 @@ async function cmdAdd(kind, source, selector, opts) {
1329
1795
  await delayQuiet(320);
1330
1796
  blank();
1331
1797
  if (opts.dryRun) {
1332
- preview(artifacts, await resolveAgentList(opts));
1798
+ preview(artifacts, await resolveAgentList(opts, "install"));
1333
1799
  return;
1334
1800
  }
1335
1801
  let chosen = artifacts;
@@ -1347,7 +1813,7 @@ async function cmdAdd(kind, source, selector, opts) {
1347
1813
  await delayQuiet(200);
1348
1814
  blank();
1349
1815
  }
1350
- const agentList = await resolveAgentList(opts);
1816
+ const agentList = await resolveAgentList(opts, "install");
1351
1817
  if (!isQuiet() && !opts.agents.length && !opts.all && !opts.yes) await sectionTransition(`${agentList.length} agent${agentList.length === 1 ? "" : "s"} selected`, "install summary");
1352
1818
  installAll(chosen, opts, agentList, source);
1353
1819
  } finally {
@@ -1360,14 +1826,14 @@ async function delayQuiet(ms) {
1360
1826
  await delay(ms);
1361
1827
  }
1362
1828
  function parseSourceForUi(source) {
1363
- return import("./_chunks/source_parser.mjs").then((m) => m.parseSource(source));
1829
+ return parseSource(source);
1364
1830
  }
1365
1831
  async function cmdAddProfile(name, source, opts) {
1366
1832
  const { file, profile } = loadProfile(name);
1367
1833
  const repo = source || (profile.artifacts.skills[0]?.source ?? profile.artifacts.rules[0]?.source);
1368
1834
  if (!repo) throw new Error(`No source given and ${file} lists none. Try: agentry add profile ${name} author/repo`);
1369
- const agentList = await resolveAgentList(opts);
1370
- const { root, cleanup } = resolveSource(repo);
1835
+ const agentList = await resolveAgentList(opts, "install");
1836
+ const { root, cleanup } = await resolveSourceWithUi(repo, opts);
1371
1837
  try {
1372
1838
  const artifacts = [];
1373
1839
  const plural = (k) => k === "skill" ? "skills" : k === "rule" ? "rules" : k === "script" ? "scripts" : "agents";
@@ -1375,7 +1841,7 @@ async function cmdAddProfile(name, source, opts) {
1375
1841
  if (k === "profile") continue;
1376
1842
  const refs = profile.artifacts[plural(k)] ?? [];
1377
1843
  for (const ref of refs) {
1378
- const src = ref.source ? resolveSource(ref.source) : {
1844
+ const src = ref.source ? await resolveSourceWithUi(ref.source, opts) : {
1379
1845
  root,
1380
1846
  cleanup: () => {}
1381
1847
  };
@@ -1392,7 +1858,7 @@ async function cmdAddProfile(name, source, opts) {
1392
1858
  cleanup();
1393
1859
  }
1394
1860
  }
1395
- async function resolveAgentList(opts) {
1861
+ async function resolveAgentList(opts, action = "install") {
1396
1862
  if (opts.all) return listAgents();
1397
1863
  if (opts.agents.length) {
1398
1864
  const { agents, unknown } = resolveAgents(opts.agents);
@@ -1408,7 +1874,7 @@ async function resolveAgentList(opts) {
1408
1874
  const picked = detectedOthers.length ? detectedOthers : ["cursor"];
1409
1875
  return [...universal, ...others.filter((o) => picked.includes(o.name))];
1410
1876
  }
1411
- const selected = await selectAgents("Which agents do you want to install to?", visibleUniversal, others, detectedOthers);
1877
+ const selected = await selectAgents(agentsPrompt(action), visibleUniversal, others, detectedOthers);
1412
1878
  if (isCancelLike(selected)) throw new Error("Cancelled.");
1413
1879
  return selected;
1414
1880
  }
@@ -1430,74 +1896,204 @@ function toInstallOpts(opts) {
1430
1896
  dryRun: opts.dryRun
1431
1897
  };
1432
1898
  }
1433
- async function cmdRemove(kind, selector, opts) {
1434
- const agentList = await resolveAgentList(opts);
1899
+ function resolveSourceOrSelector(arg1, arg2, opts) {
1900
+ if (arg1 && arg2) return {
1901
+ source: arg1,
1902
+ selector: arg2
1903
+ };
1904
+ if (!arg1) return {};
1905
+ if (isExplicitSource(arg1)) return { source: arg1 };
1906
+ if (isOwnerRepoShape(arg1)) {
1907
+ const lock = readLock(toInstallOpts(opts));
1908
+ if (Object.values(lock.items).some((e) => e.source && sourcesEqual(e.source, arg1))) return { source: arg1 };
1909
+ }
1910
+ return { selector: arg1 };
1911
+ }
1912
+ async function cmdRemove(kind, arg1, arg2, opts) {
1913
+ intro();
1914
+ const { source, selector } = resolveSourceOrSelector(arg1, arg2, opts);
1915
+ if (source) await spinner("Resolving source…", () => parseSource(source), (p) => {
1916
+ const where = p.url || p.local || source;
1917
+ return `${theme.primary("Source:")} ${where}${p.ref ? c.dim(` @ ${p.ref}`) : ""}`;
1918
+ }, 420);
1919
+ if (!isQuiet() && !opts.agents.length && !opts.all && !opts.yes) await sectionTransition("prepare remove", "choose agents to remove from");
1920
+ const agentList = await resolveAgentList(opts, "remove");
1435
1921
  const installOpts = toInstallOpts(opts);
1436
- const scope = [kind, selector].filter(Boolean).join("/");
1922
+ const scope = [
1923
+ kind,
1924
+ source,
1925
+ selector
1926
+ ].filter(Boolean).join(" / ");
1437
1927
  if (kind === "all") {
1438
1928
  let count = 0;
1439
- for (const t of ARTIFACT_KINDS) count += removeSelection(t, void 0, installOpts, agentList).removedKeys.length;
1440
- ok(`Removed ${count} item(s).`);
1929
+ let paths = 0;
1930
+ for (const t of ARTIFACT_KINDS) {
1931
+ const r = removeSelection(t, void 0, installOpts, agentList, source);
1932
+ count += r.removedKeys.length;
1933
+ paths += r.removedPaths.length;
1934
+ }
1935
+ ok(`Removed ${count} item(s)${paths ? c.dim(` · ${paths} path(s)`) : ""}${source ? c.dim(` from ${source}`) : ""}.`);
1441
1936
  return;
1442
1937
  }
1443
- const r = removeSelection(kind, selector, installOpts, agentList);
1938
+ const r = removeSelection(kind, selector, installOpts, agentList, source);
1444
1939
  if (!r.existed) {
1445
- warn(`Nothing installed at "${scope}".`);
1940
+ warn(`Nothing installed matching "${scope}".`);
1446
1941
  return;
1447
1942
  }
1448
1943
  for (const k of r.removedKeys) step(`removed ${k}`);
1449
- ok(`Removed ${r.removedKeys.length} item(s).`);
1944
+ for (const p of r.removedPaths.slice(0, 12)) step(c.dim(`deleted ${p}`));
1945
+ if (r.removedPaths.length > 12) step(c.dim(`…and ${r.removedPaths.length - 12} more paths`));
1946
+ ok(`Removed ${r.removedKeys.length} item(s)${r.removedPaths.length ? c.dim(` · ${r.removedPaths.length} path(s)`) : ""}${source ? c.dim(` from ${source}`) : ""}.`);
1450
1947
  }
1451
- function cmdList(source, kind) {
1452
- if (!source) throw new Error("Missing source. e.g. agentry list author/repo");
1453
- const { root, cleanup } = resolveSource(source);
1948
+ async function cmdList(source, kind, selector, opts) {
1949
+ if (!source) {
1950
+ cmdListInstalled(opts, kind, selector);
1951
+ return;
1952
+ }
1953
+ intro();
1954
+ await spinner("Parsing source…", () => parseSource(source), (p) => {
1955
+ const where = p.url || p.local || source;
1956
+ return `${theme.primary("Source:")} ${where}${p.ref ? c.dim(` @ ${p.ref}`) : ""}${p.subpath ? c.dim(` (${p.subpath})`) : ""}`;
1957
+ }, 420);
1958
+ const { root, cleanup } = await resolveSourceWithUi(source, opts);
1454
1959
  try {
1455
- for (const k of kind ? [kind] : ARTIFACT_KINDS) {
1456
- log(theme.info(`${k}s:`));
1457
- const items = listKind(root, k);
1458
- if (!items.length) {
1459
- step("(none)");
1460
- continue;
1461
- }
1462
- for (const it of items) {
1463
- const desc = (it.description || "").replace(/\s+/g, " ").slice(0, 80);
1464
- step(`${it.id}${desc ? " - " + desc : ""}`);
1465
- }
1466
- }
1960
+ printArtifactList(await spinner("Discovering…", () => {
1961
+ if (kind) return select(root, kind, selector);
1962
+ return listAll(root);
1963
+ }, (a) => `Found ${a.length} artifact${a.length === 1 ? "" : "s"}`, 520), {
1964
+ title: "Available in source",
1965
+ subtitle: source
1966
+ });
1467
1967
  } finally {
1468
1968
  cleanup();
1469
1969
  }
1470
1970
  }
1471
- function cmdListInstalled(opts) {
1472
- const installed = listInstalled(toInstallOpts(opts));
1971
+ function cmdListInstalled(opts, kind, selector) {
1972
+ intro();
1973
+ let installed = listInstalled(toInstallOpts(opts));
1974
+ if (kind) installed = installed.filter((it) => it.kind === kind || it.id.startsWith(kind + "/"));
1975
+ if (selector && kind) {
1976
+ const rel = `${kind}/${selector}`;
1977
+ installed = installed.filter((it) => it.id === rel || it.id.startsWith(rel + "/") || it.id.endsWith("/" + selector) || it.id === `${kind}/${selector}`);
1978
+ } else if (selector) installed = installed.filter((it) => it.id.includes(selector));
1473
1979
  if (!installed.length) {
1474
- info("Nothing installed.");
1980
+ info(kind || selector ? `Nothing installed matching that filter.` : "Nothing installed.");
1981
+ step(c.dim("Try: agentry add skills mhmdreza-rafiei/agent-tools"));
1475
1982
  return;
1476
1983
  }
1477
- for (const it of installed) step(`${it.id} -> ${it.agents.join(", ")}`);
1984
+ const bar = c.dim("│");
1985
+ const corner = c.dim("└");
1986
+ blank();
1987
+ log(`${sky("◆")} ${c.bold("Installed")}`);
1988
+ log(bar);
1989
+ log(`${bar} ${sky("Scope")} ${opts.scope === "global" ? "global (~)" : `project (${opts.dir || process.cwd()})`}`);
1990
+ log(`${bar} ${sky("Total")} ${installed.length} artifact${installed.length === 1 ? "" : "s"}`);
1991
+ log(bar);
1992
+ const byKind = /* @__PURE__ */ new Map();
1993
+ for (const it of installed) {
1994
+ const k = it.kind || it.id.split("/")[0];
1995
+ const arr = byKind.get(k) || [];
1996
+ arr.push(it);
1997
+ byKind.set(k, arr);
1998
+ }
1999
+ for (const k of ARTIFACT_KINDS) {
2000
+ const items = byKind.get(k);
2001
+ if (!items?.length) continue;
2002
+ log(`${bar} ${c.bold(k + "s")} ${c.dim(`(${items.length})`)}`);
2003
+ for (const it of items) {
2004
+ const id = it.id.includes("/") ? it.id.slice(it.id.indexOf("/") + 1) : it.id;
2005
+ log(`${bar} ${sky("•")} ${c.bold(id)}`);
2006
+ const agents = it.agents?.length ? it.agents.join(", ") : "—";
2007
+ log(`${bar} ${c.dim(`agents: ${agents}`)}`);
2008
+ if (it.source) log(`${bar} ${c.dim(`source: ${it.source}`)}`);
2009
+ }
2010
+ log(bar);
2011
+ }
2012
+ log(`${corner} ${c.dim("agentry remove <kind> [source] [selector] · agentry update <kind> [source]")}`);
2013
+ blank();
1478
2014
  }
1479
- async function cmdUpdateAssets(kind, source, selector, opts) {
2015
+ function printArtifactList(artifacts, meta) {
2016
+ const bar = c.dim("│");
2017
+ const corner = c.dim("└");
2018
+ blank();
2019
+ log(`${sky("◆")} ${c.bold(meta.title)}`);
2020
+ log(bar);
2021
+ if (meta.subtitle) log(`${bar} ${sky("From")} ${meta.subtitle}`);
2022
+ log(`${bar} ${sky("Total")} ${artifacts.length}`);
2023
+ log(bar);
2024
+ if (!artifacts.length) {
2025
+ log(`${bar} ${c.dim("(none)")}`);
2026
+ log(bar);
2027
+ log(`${corner}`);
2028
+ blank();
2029
+ return;
2030
+ }
2031
+ const byKind = /* @__PURE__ */ new Map();
2032
+ for (const a of artifacts) {
2033
+ const arr = byKind.get(a.kind) || [];
2034
+ arr.push(a);
2035
+ byKind.set(a.kind, arr);
2036
+ }
2037
+ for (const k of ARTIFACT_KINDS) {
2038
+ const items = byKind.get(k);
2039
+ if (!items?.length) continue;
2040
+ log(`${bar} ${c.bold(k + "s")} ${c.dim(`(${items.length})`)}`);
2041
+ for (const it of items) {
2042
+ const desc = (it.description || "").replace(/\s+/g, " ").trim().slice(0, 56);
2043
+ log(`${bar} ${sky("•")} ${c.bold(it.id)}`);
2044
+ if (desc) log(`${bar} ${c.dim(desc)}`);
2045
+ }
2046
+ log(bar);
2047
+ }
2048
+ log(`${corner} ${c.dim("agentry add <kind> <source> [selector]")}`);
2049
+ blank();
2050
+ }
2051
+ async function cmdUpdateAssets(kind, arg1, arg2, opts) {
2052
+ intro();
2053
+ const { source, selector } = resolveSourceOrSelector(arg1, arg2, opts);
1480
2054
  const installOpts = toInstallOpts(opts);
1481
- const agentList = await resolveAgentList(opts);
1482
2055
  if (source) {
1483
- const { root, cleanup } = resolveSource(source);
2056
+ await spinner("Parsing source…", () => parseSource(source), (p) => {
2057
+ const where = p.url || p.local || source;
2058
+ return `${theme.primary("Source:")} ${where}${p.ref ? c.dim(` @ ${p.ref}`) : ""}`;
2059
+ }, 520);
2060
+ const { root, cleanup } = await resolveSourceWithUi(source, opts);
1484
2061
  try {
1485
- const artifacts = kind === "all" ? listAll(root) : select(root, kind, selector);
2062
+ const artifacts = await spinner("Discovering…", () => kind === "all" ? listAll(root) : select(root, kind, selector), (a) => `Found ${a.length} to update`, 620);
1486
2063
  if (!artifacts.length) {
1487
2064
  warn(`Nothing found in ${source}.`);
1488
2065
  return;
1489
2066
  }
1490
- installAll(artifacts, opts, agentList, source);
2067
+ let chosen = artifacts;
2068
+ if (!selector && kind !== "all" && artifacts.length > 1 && !opts.all && !opts.yes && !isQuiet()) {
2069
+ await sectionTransition("source discovery", `select ${kind}s to update`);
2070
+ const picked = await selectArtifacts(`Select ${kind}s to update`, artifacts);
2071
+ if (isCancelLike(picked)) {
2072
+ outro("Cancelled.");
2073
+ return;
2074
+ }
2075
+ chosen = picked;
2076
+ await sectionTransition(`${chosen.length} ${kind}${chosen.length === 1 ? "" : "s"} selected`, "choose target agents");
2077
+ } else if (!isQuiet() && !opts.agents.length && !opts.all && !opts.yes) await sectionTransition(`${chosen.length} ${kind}${chosen.length === 1 ? "" : "s"} ready`, "choose target agents");
2078
+ const agentList = await resolveAgentList(opts, "update");
2079
+ if (!isQuiet() && !opts.agents.length && !opts.all && !opts.yes) await sectionTransition(`${agentList.length} agent${agentList.length === 1 ? "" : "s"} selected`, "update summary");
2080
+ installAll(chosen, opts, agentList, source);
1491
2081
  } finally {
1492
2082
  cleanup();
1493
2083
  }
1494
2084
  return;
1495
2085
  }
1496
- const installed = listInstalled(installOpts).filter((it) => kind === "all" || it.id.startsWith(kind + "/"));
2086
+ let installed = listInstalled(installOpts).filter((it) => kind === "all" || it.id.startsWith(kind + "/"));
2087
+ if (selector) {
2088
+ const rel = `${kind}/${selector}`;
2089
+ installed = installed.filter((it) => it.id === rel || it.id.startsWith(rel + "/") || it.id.endsWith("/" + selector));
2090
+ }
1497
2091
  if (!installed.length) {
1498
2092
  warn(`No installed ${kind} recorded in lockfile. Provide a source.`);
1499
2093
  return;
1500
2094
  }
2095
+ if (!isQuiet() && !opts.agents.length && !opts.all && !opts.yes) await sectionTransition("lockfile sources", "choose agents to update for");
2096
+ const agentList = await resolveAgentList(opts, "update");
1501
2097
  const bySource = /* @__PURE__ */ new Map();
1502
2098
  for (const it of installed) if (it.source) {
1503
2099
  const arr = bySource.get(it.source) || [];
@@ -1509,14 +2105,206 @@ async function cmdUpdateAssets(kind, source, selector, opts) {
1509
2105
  return;
1510
2106
  }
1511
2107
  for (const [s, ids] of bySource) {
1512
- const { root, cleanup } = resolveSource(s);
2108
+ info(`Updating from ${s}…`);
2109
+ const { root, cleanup } = await resolveSourceWithUi(s, opts);
1513
2110
  try {
1514
- installAll(ids.map((id) => select(root, id.split("/")[0], id.split("/").slice(1).join("/") || void 0)).flat(), opts, agentList, s);
2111
+ installAll(ids.map((id) => {
2112
+ const [k, ...rest] = id.split("/");
2113
+ return select(root, k, rest.join("/") || void 0);
2114
+ }).flat(), opts, agentList, s);
1515
2115
  } finally {
1516
2116
  cleanup();
1517
2117
  }
1518
2118
  }
1519
2119
  }
2120
+ async function promptText(message, initial) {
2121
+ if (isQuiet()) return initial || null;
2122
+ const res = await clack.text({
2123
+ message,
2124
+ initialValue: initial || "",
2125
+ placeholder: initial
2126
+ });
2127
+ if (clack.isCancel(res)) return null;
2128
+ return String(res).trim() || null;
2129
+ }
2130
+ async function pickFromSource(kind, source, message, opts) {
2131
+ const { root, cleanup } = await resolveSourceWithUi(source, opts);
2132
+ try {
2133
+ const artifacts = listKind(root, kind);
2134
+ if (!artifacts.length) {
2135
+ warn(`No ${kind}s in ${source}.`);
2136
+ return [];
2137
+ }
2138
+ if (isQuiet() || artifacts.length === 1) return artifacts.map((a) => ({
2139
+ id: a.id,
2140
+ source
2141
+ }));
2142
+ const picked = await selectArtifacts(message, artifacts);
2143
+ if (isCancelLike(picked) || !picked) return [];
2144
+ return picked.map((a) => ({
2145
+ id: a.id,
2146
+ source
2147
+ }));
2148
+ } finally {
2149
+ cleanup();
2150
+ }
2151
+ }
2152
+ async function cmdInit(kind, nameArg, categoryArg, opts) {
2153
+ intro();
2154
+ const baseDir = opts.dir || process.cwd();
2155
+ let name = nameArg;
2156
+ let category = categoryArg || null;
2157
+ if (!name && !isQuiet()) name = await promptText(`Name for the new ${kind}`) || void 0;
2158
+ if (!name) throw new Error(`Missing name. e.g. agentry init ${kind} my-${kind}`);
2159
+ let description = opts.description;
2160
+ if (!description && !opts.yes && !isQuiet()) description = await promptText("Description (optional)", `${name} ${kind}`) || `${name} ${kind}`;
2161
+ description = description || `${name} ${kind}`;
2162
+ if (kind === "skill") {
2163
+ const reference = opts.reference !== false;
2164
+ if (opts.dryRun) {
2165
+ info(`Would create skills/${category ? category + "/" : ""}${name}/SKILL.md${reference ? " + references/TEMPLATE.md" : ""}`);
2166
+ return;
2167
+ }
2168
+ const r = scaffoldSkill({
2169
+ name,
2170
+ category,
2171
+ description,
2172
+ reference,
2173
+ baseDir
2174
+ });
2175
+ for (const p of r.paths) step(p);
2176
+ ok(`Initialized skill ${r.id}`);
2177
+ return;
2178
+ }
2179
+ if (kind === "agent" || kind === "rule") {
2180
+ if (opts.dryRun) {
2181
+ info(`Would create ${kind}s/${category ? category + "/" : ""}${name}.mdc`);
2182
+ return;
2183
+ }
2184
+ const r = scaffoldMdc({
2185
+ kind,
2186
+ name,
2187
+ category,
2188
+ description,
2189
+ alwaysApply: opts.alwaysApply,
2190
+ baseDir
2191
+ });
2192
+ for (const p of r.paths) step(p);
2193
+ ok(`Initialized ${kind} ${r.id}`);
2194
+ return;
2195
+ }
2196
+ if (kind === "script") {
2197
+ if (opts.dryRun) {
2198
+ info(`Would create scripts/${category ? category + "/" : ""}${name}/`);
2199
+ return;
2200
+ }
2201
+ const r = scaffoldScript({
2202
+ name,
2203
+ category,
2204
+ description,
2205
+ baseDir
2206
+ });
2207
+ for (const p of r.paths) step(p);
2208
+ ok(`Initialized script ${r.id}`);
2209
+ return;
2210
+ }
2211
+ if (kind === "profile") {
2212
+ await initProfile(name, description, opts, baseDir);
2213
+ return;
2214
+ }
2215
+ throw new Error(`Cannot init kind: ${kind}`);
2216
+ }
2217
+ async function initProfile(name, description, opts, baseDir) {
2218
+ const initKinds = opts.initKinds || {};
2219
+ const wantAll = opts.all;
2220
+ let wantSkills = wantAll || initKinds.skills !== void 0;
2221
+ let wantAgents = wantAll || initKinds.agents !== void 0;
2222
+ let wantRules = wantAll || initKinds.rules !== void 0;
2223
+ let wantScripts = wantAll || initKinds.scripts !== void 0;
2224
+ if (!wantSkills && !wantAgents && !wantRules && !wantScripts && !opts.yes && !isQuiet()) {
2225
+ const res = await clack.multiselect({
2226
+ message: "What should this profile include?",
2227
+ options: [
2228
+ {
2229
+ value: "skills",
2230
+ label: "Skills"
2231
+ },
2232
+ {
2233
+ value: "agents",
2234
+ label: "Agents"
2235
+ },
2236
+ {
2237
+ value: "rules",
2238
+ label: "Rules"
2239
+ },
2240
+ {
2241
+ value: "scripts",
2242
+ label: "Scripts"
2243
+ }
2244
+ ],
2245
+ required: false
2246
+ });
2247
+ if (clack.isCancel(res)) {
2248
+ outro("Cancelled.");
2249
+ return;
2250
+ }
2251
+ const set = new Set(res);
2252
+ wantSkills = set.has("skills");
2253
+ wantAgents = set.has("agents");
2254
+ wantRules = set.has("rules");
2255
+ wantScripts = set.has("scripts");
2256
+ }
2257
+ const agentList = opts.agents.length ? (await resolveAgentList(opts, "install")).map((a) => a.name) : ["cursor"];
2258
+ const skills = [];
2259
+ const rules = [];
2260
+ const agentsArtifacts = [];
2261
+ const scripts = [];
2262
+ async function fill(flag, enabled, k, into) {
2263
+ if (!enabled) return;
2264
+ const src = typeof flag === "string" ? flag : void 0;
2265
+ if (src) {
2266
+ const picked = await pickFromSource(k, src, `Select ${k}s for profile`, opts);
2267
+ into.push(...picked);
2268
+ return;
2269
+ }
2270
+ if (isQuiet() || opts.yes) return;
2271
+ const ask = await promptText(`Source for ${k}s (author/repo, URL, or local path — empty to skip)`);
2272
+ if (!ask) return;
2273
+ const picked = await pickFromSource(k, ask, `Select ${k}s for profile`, opts);
2274
+ into.push(...picked);
2275
+ }
2276
+ await fill(initKinds.skills, wantSkills, "skill", skills);
2277
+ await fill(initKinds.agents, wantAgents, "agent", agentsArtifacts);
2278
+ await fill(initKinds.rules, wantRules, "rule", rules);
2279
+ await fill(initKinds.scripts, wantScripts, "script", scripts);
2280
+ if (opts.dryRun) {
2281
+ info(`Would write profiles/${name}.yaml with ${skills.length} skills, ${agentsArtifacts.length} agents, ${rules.length} rules, ${scripts.length} scripts`);
2282
+ return;
2283
+ }
2284
+ const r = scaffoldProfile({
2285
+ name,
2286
+ description,
2287
+ agents: agentList,
2288
+ skills,
2289
+ rules,
2290
+ agentsArtifacts,
2291
+ scripts,
2292
+ baseDir
2293
+ });
2294
+ for (const p of r.paths) step(p);
2295
+ ok(`Initialized profile ${r.id}`);
2296
+ }
2297
+ function takeOptValue(argv, i) {
2298
+ const next = argv[i + 1];
2299
+ if (next && !next.startsWith("-")) return {
2300
+ value: next,
2301
+ next: i + 1
2302
+ };
2303
+ return {
2304
+ value: true,
2305
+ next: i
2306
+ };
2307
+ }
1520
2308
  function parseArgs(argv) {
1521
2309
  const opts = {
1522
2310
  scope: "project",
@@ -1524,7 +2312,8 @@ function parseArgs(argv) {
1524
2312
  copy: false,
1525
2313
  dryRun: false,
1526
2314
  yes: false,
1527
- all: false
2315
+ all: false,
2316
+ initKinds: {}
1528
2317
  };
1529
2318
  const positional = [];
1530
2319
  const flags = {};
@@ -1554,6 +2343,38 @@ function parseArgs(argv) {
1554
2343
  } else if (a === "--all") {
1555
2344
  opts.all = true;
1556
2345
  flags.all = true;
2346
+ } else if (a === "--description") {
2347
+ opts.description = argv[++i];
2348
+ flags.description = opts.description;
2349
+ } else if (a === "--alwaysApply" || a === "--always-apply") {
2350
+ opts.alwaysApply = true;
2351
+ flags.alwaysApply = true;
2352
+ } else if (a === "--reference") {
2353
+ opts.reference = true;
2354
+ flags.reference = true;
2355
+ } else if (a === "--no-reference") {
2356
+ opts.reference = false;
2357
+ flags.reference = false;
2358
+ } else if (a === "--skills") {
2359
+ const { value, next } = takeOptValue(argv, i);
2360
+ opts.initKinds.skills = value;
2361
+ i = next;
2362
+ flags.skills = true;
2363
+ } else if (a === "--agents") {
2364
+ const { value, next } = takeOptValue(argv, i);
2365
+ opts.initKinds.agents = value;
2366
+ i = next;
2367
+ flags.agents = true;
2368
+ } else if (a === "--rules") {
2369
+ const { value, next } = takeOptValue(argv, i);
2370
+ opts.initKinds.rules = value;
2371
+ i = next;
2372
+ flags.rules = true;
2373
+ } else if (a === "--scripts") {
2374
+ const { value, next } = takeOptValue(argv, i);
2375
+ opts.initKinds.scripts = value;
2376
+ i = next;
2377
+ flags.scripts = true;
1557
2378
  } else if (a === "-v" || a === "--version") flags.version = true;
1558
2379
  else if (a === "-h" || a === "--help") flags.help = true;
1559
2380
  else positional.push(a);
@@ -1650,12 +2471,21 @@ async function main() {
1650
2471
  return;
1651
2472
  }
1652
2473
  if (action === "list") {
1653
- if (positional.length === 1) {
1654
- cmdListInstalled(opts);
2474
+ const a1 = positional[1];
2475
+ const a2 = positional[2];
2476
+ const a3 = positional[3];
2477
+ if (!a1) {
2478
+ await cmdList(void 0, void 0, void 0, opts);
2479
+ return;
2480
+ }
2481
+ const k1 = normalizeKind(a1);
2482
+ if (isKind(k1) && !isExplicitSource(a1) && !/^[\w.-]+\/[\w.-]+$/.test(a1)) {
2483
+ await cmdList(void 0, k1, a2, opts);
1655
2484
  return;
1656
2485
  }
1657
- const k = positional[2] ? normalizeKind(positional[2]) : void 0;
1658
- cmdList(positional[1], k && isKind(k) ? k : void 0);
2486
+ const kind = a2 ? normalizeKind(a2) : void 0;
2487
+ if (kind && kind !== "all" && !isKind(kind)) throw new Error(`Unknown kind: ${kind}`);
2488
+ await cmdList(a1, kind && isKind(kind) ? kind : void 0, a3, opts);
1659
2489
  return;
1660
2490
  }
1661
2491
  if (action === "add") {
@@ -1673,7 +2503,13 @@ async function main() {
1673
2503
  const kind = positional[1] ? normalizeKind(positional[1]) : void 0;
1674
2504
  if (!kind) throw new Error("Missing kind. e.g. agentry remove skills enhance-prompt");
1675
2505
  if (kind !== "all" && !isKind(kind)) throw new Error(`Unknown kind: ${kind}`);
1676
- await cmdRemove(kind, positional[2], opts);
2506
+ await cmdRemove(kind, positional[2], positional[3], opts);
2507
+ return;
2508
+ }
2509
+ if (action === "init") {
2510
+ const kind = positional[1] ? normalizeKind(positional[1]) : void 0;
2511
+ if (!kind || !isKind(kind)) throw new Error("Missing kind. e.g. agentry init skill my-skill [category]");
2512
+ await cmdInit(kind, positional[2], positional[3], opts);
1677
2513
  return;
1678
2514
  }
1679
2515
  err(`Unknown action: ${action}`);