@sporesec/arcana 2.3.1 → 3.0.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.
Files changed (241) hide show
  1. package/dist/cli.d.ts +0 -1
  2. package/dist/cli.js +140 -10
  3. package/dist/command-registry.d.ts +10 -0
  4. package/dist/command-registry.js +65 -0
  5. package/dist/commands/audit.d.ts +0 -1
  6. package/dist/commands/audit.js +16 -6
  7. package/dist/commands/benchmark.d.ts +4 -0
  8. package/dist/commands/benchmark.js +178 -0
  9. package/dist/commands/clean.d.ts +2 -1
  10. package/dist/commands/clean.js +198 -47
  11. package/dist/commands/compact.d.ts +6 -0
  12. package/dist/commands/compact.js +239 -0
  13. package/dist/commands/completions.d.ts +3 -0
  14. package/dist/commands/completions.js +104 -0
  15. package/dist/commands/config.d.ts +0 -1
  16. package/dist/commands/config.js +15 -6
  17. package/dist/commands/create.d.ts +0 -1
  18. package/dist/commands/create.js +1 -1
  19. package/dist/commands/diff.d.ts +4 -0
  20. package/dist/commands/diff.js +166 -0
  21. package/dist/commands/doctor.d.ts +0 -1
  22. package/dist/commands/doctor.js +153 -24
  23. package/dist/commands/export-cmd.d.ts +4 -0
  24. package/dist/commands/export-cmd.js +66 -0
  25. package/dist/commands/import-cmd.d.ts +4 -0
  26. package/dist/commands/import-cmd.js +131 -0
  27. package/dist/commands/info.d.ts +0 -1
  28. package/dist/commands/info.js +29 -4
  29. package/dist/commands/init.d.ts +0 -1
  30. package/dist/commands/init.js +156 -117
  31. package/dist/commands/install.d.ts +1 -1
  32. package/dist/commands/install.js +118 -205
  33. package/dist/commands/list.d.ts +0 -1
  34. package/dist/commands/list.js +12 -4
  35. package/dist/commands/lock.d.ts +4 -0
  36. package/dist/commands/lock.js +171 -0
  37. package/dist/commands/optimize.d.ts +3 -0
  38. package/dist/commands/optimize.js +356 -0
  39. package/dist/commands/outdated.d.ts +4 -0
  40. package/dist/commands/outdated.js +159 -0
  41. package/dist/commands/profile.d.ts +3 -0
  42. package/dist/commands/profile.js +274 -0
  43. package/dist/commands/providers.d.ts +0 -1
  44. package/dist/commands/providers.js +1 -4
  45. package/dist/commands/recommend.d.ts +5 -0
  46. package/dist/commands/recommend.js +96 -0
  47. package/dist/commands/scan.d.ts +0 -1
  48. package/dist/commands/scan.js +13 -7
  49. package/dist/commands/search.d.ts +2 -1
  50. package/dist/commands/search.js +32 -9
  51. package/dist/commands/stats.d.ts +0 -1
  52. package/dist/commands/stats.js +83 -16
  53. package/dist/commands/team.d.ts +3 -0
  54. package/dist/commands/team.js +291 -0
  55. package/dist/commands/uninstall.d.ts +0 -1
  56. package/dist/commands/uninstall.js +18 -4
  57. package/dist/commands/update.d.ts +0 -1
  58. package/dist/commands/update.js +155 -155
  59. package/dist/commands/validate.d.ts +0 -1
  60. package/dist/commands/validate.js +14 -6
  61. package/dist/commands/verify.d.ts +4 -0
  62. package/dist/commands/verify.js +116 -0
  63. package/dist/constants.d.ts +10 -0
  64. package/dist/constants.js +13 -0
  65. package/dist/index.d.ts +0 -1
  66. package/dist/index.js +0 -1
  67. package/dist/interactive/browse.d.ts +4 -0
  68. package/dist/interactive/browse.js +103 -0
  69. package/dist/interactive/categories.d.ts +4 -0
  70. package/dist/interactive/categories.js +87 -0
  71. package/dist/interactive/health.d.ts +1 -0
  72. package/dist/interactive/health.js +57 -0
  73. package/dist/interactive/helpers.d.ts +11 -0
  74. package/dist/interactive/helpers.js +66 -0
  75. package/dist/interactive/index.d.ts +1 -0
  76. package/dist/interactive/index.js +1 -0
  77. package/dist/interactive/manage.d.ts +2 -0
  78. package/dist/interactive/manage.js +187 -0
  79. package/dist/interactive/menu.d.ts +1 -0
  80. package/dist/interactive/menu.js +107 -0
  81. package/dist/interactive/search.d.ts +2 -0
  82. package/dist/interactive/search.js +66 -0
  83. package/dist/interactive/setup.d.ts +2 -0
  84. package/dist/interactive/setup.js +48 -0
  85. package/dist/interactive/skill-detail.d.ts +5 -0
  86. package/dist/interactive/skill-detail.js +126 -0
  87. package/dist/interactive.d.ts +0 -1
  88. package/dist/interactive.js +89 -66
  89. package/dist/providers/arcana.d.ts +0 -1
  90. package/dist/providers/arcana.js +0 -1
  91. package/dist/providers/base.d.ts +0 -1
  92. package/dist/providers/base.js +0 -1
  93. package/dist/providers/github.d.ts +0 -1
  94. package/dist/providers/github.js +8 -3
  95. package/dist/registry.d.ts +0 -1
  96. package/dist/registry.js +1 -4
  97. package/dist/types.d.ts +10 -1
  98. package/dist/types.js +0 -1
  99. package/dist/utils/atomic.d.ts +0 -1
  100. package/dist/utils/atomic.js +3 -2
  101. package/dist/utils/cache.d.ts +0 -1
  102. package/dist/utils/cache.js +3 -2
  103. package/dist/utils/config.d.ts +2 -1
  104. package/dist/utils/config.js +30 -5
  105. package/dist/utils/conflict-check.d.ts +8 -0
  106. package/dist/utils/conflict-check.js +72 -0
  107. package/dist/utils/errors.d.ts +0 -1
  108. package/dist/utils/errors.js +0 -1
  109. package/dist/utils/frontmatter.d.ts +0 -1
  110. package/dist/utils/frontmatter.js +37 -10
  111. package/dist/utils/fs.d.ts +19 -1
  112. package/dist/utils/fs.js +105 -8
  113. package/dist/utils/help.d.ts +0 -1
  114. package/dist/utils/help.js +15 -28
  115. package/dist/utils/history.d.ts +0 -1
  116. package/dist/utils/history.js +0 -1
  117. package/dist/utils/http.d.ts +0 -1
  118. package/dist/utils/http.js +14 -5
  119. package/dist/utils/install-core.d.ts +48 -0
  120. package/dist/utils/install-core.js +108 -0
  121. package/dist/utils/integrity.d.ts +17 -0
  122. package/dist/utils/integrity.js +84 -0
  123. package/dist/utils/parallel.d.ts +0 -1
  124. package/dist/utils/parallel.js +0 -1
  125. package/dist/utils/project-context.d.ts +19 -0
  126. package/dist/utils/project-context.js +283 -0
  127. package/dist/utils/scanner.d.ts +0 -1
  128. package/dist/utils/scanner.js +138 -10
  129. package/dist/utils/scoring.d.ts +10 -0
  130. package/dist/utils/scoring.js +84 -0
  131. package/dist/utils/ui.d.ts +0 -1
  132. package/dist/utils/ui.js +11 -4
  133. package/dist/utils/validate.d.ts +0 -1
  134. package/dist/utils/validate.js +4 -1
  135. package/package.json +19 -7
  136. package/dist/cli.d.ts.map +0 -1
  137. package/dist/cli.js.map +0 -1
  138. package/dist/commands/audit.d.ts.map +0 -1
  139. package/dist/commands/audit.js.map +0 -1
  140. package/dist/commands/audit.test.d.ts +0 -2
  141. package/dist/commands/audit.test.d.ts.map +0 -1
  142. package/dist/commands/audit.test.js +0 -217
  143. package/dist/commands/audit.test.js.map +0 -1
  144. package/dist/commands/clean.d.ts.map +0 -1
  145. package/dist/commands/clean.js.map +0 -1
  146. package/dist/commands/config.d.ts.map +0 -1
  147. package/dist/commands/config.js.map +0 -1
  148. package/dist/commands/create.d.ts.map +0 -1
  149. package/dist/commands/create.js.map +0 -1
  150. package/dist/commands/doctor.d.ts.map +0 -1
  151. package/dist/commands/doctor.js.map +0 -1
  152. package/dist/commands/info.d.ts.map +0 -1
  153. package/dist/commands/info.js.map +0 -1
  154. package/dist/commands/init.d.ts.map +0 -1
  155. package/dist/commands/init.js.map +0 -1
  156. package/dist/commands/install.d.ts.map +0 -1
  157. package/dist/commands/install.js.map +0 -1
  158. package/dist/commands/list.d.ts.map +0 -1
  159. package/dist/commands/list.js.map +0 -1
  160. package/dist/commands/providers.d.ts.map +0 -1
  161. package/dist/commands/providers.js.map +0 -1
  162. package/dist/commands/scan.d.ts.map +0 -1
  163. package/dist/commands/scan.js.map +0 -1
  164. package/dist/commands/search.d.ts.map +0 -1
  165. package/dist/commands/search.js.map +0 -1
  166. package/dist/commands/stats.d.ts.map +0 -1
  167. package/dist/commands/stats.js.map +0 -1
  168. package/dist/commands/uninstall.d.ts.map +0 -1
  169. package/dist/commands/uninstall.js.map +0 -1
  170. package/dist/commands/update.d.ts.map +0 -1
  171. package/dist/commands/update.js.map +0 -1
  172. package/dist/commands/validate.d.ts.map +0 -1
  173. package/dist/commands/validate.js.map +0 -1
  174. package/dist/index.d.ts.map +0 -1
  175. package/dist/index.js.map +0 -1
  176. package/dist/interactive.d.ts.map +0 -1
  177. package/dist/interactive.js.map +0 -1
  178. package/dist/providers/arcana.d.ts.map +0 -1
  179. package/dist/providers/arcana.js.map +0 -1
  180. package/dist/providers/base.d.ts.map +0 -1
  181. package/dist/providers/base.js.map +0 -1
  182. package/dist/providers/github.d.ts.map +0 -1
  183. package/dist/providers/github.js.map +0 -1
  184. package/dist/registry.d.ts.map +0 -1
  185. package/dist/registry.js.map +0 -1
  186. package/dist/types.d.ts.map +0 -1
  187. package/dist/types.js.map +0 -1
  188. package/dist/utils/atomic.d.ts.map +0 -1
  189. package/dist/utils/atomic.js.map +0 -1
  190. package/dist/utils/atomic.test.d.ts +0 -2
  191. package/dist/utils/atomic.test.d.ts.map +0 -1
  192. package/dist/utils/atomic.test.js +0 -31
  193. package/dist/utils/atomic.test.js.map +0 -1
  194. package/dist/utils/cache.d.ts.map +0 -1
  195. package/dist/utils/cache.js.map +0 -1
  196. package/dist/utils/config.d.ts.map +0 -1
  197. package/dist/utils/config.js.map +0 -1
  198. package/dist/utils/config.test.d.ts +0 -2
  199. package/dist/utils/config.test.d.ts.map +0 -1
  200. package/dist/utils/config.test.js +0 -38
  201. package/dist/utils/config.test.js.map +0 -1
  202. package/dist/utils/errors.d.ts.map +0 -1
  203. package/dist/utils/errors.js.map +0 -1
  204. package/dist/utils/frontmatter.d.ts.map +0 -1
  205. package/dist/utils/frontmatter.js.map +0 -1
  206. package/dist/utils/frontmatter.test.d.ts +0 -2
  207. package/dist/utils/frontmatter.test.d.ts.map +0 -1
  208. package/dist/utils/frontmatter.test.js +0 -152
  209. package/dist/utils/frontmatter.test.js.map +0 -1
  210. package/dist/utils/fs.d.ts.map +0 -1
  211. package/dist/utils/fs.js.map +0 -1
  212. package/dist/utils/fs.test.d.ts +0 -2
  213. package/dist/utils/fs.test.d.ts.map +0 -1
  214. package/dist/utils/fs.test.js +0 -145
  215. package/dist/utils/fs.test.js.map +0 -1
  216. package/dist/utils/help.d.ts.map +0 -1
  217. package/dist/utils/help.js.map +0 -1
  218. package/dist/utils/help.test.d.ts +0 -2
  219. package/dist/utils/help.test.d.ts.map +0 -1
  220. package/dist/utils/help.test.js +0 -66
  221. package/dist/utils/help.test.js.map +0 -1
  222. package/dist/utils/history.d.ts.map +0 -1
  223. package/dist/utils/history.js.map +0 -1
  224. package/dist/utils/http.d.ts.map +0 -1
  225. package/dist/utils/http.js.map +0 -1
  226. package/dist/utils/http.test.d.ts +0 -2
  227. package/dist/utils/http.test.d.ts.map +0 -1
  228. package/dist/utils/http.test.js +0 -55
  229. package/dist/utils/http.test.js.map +0 -1
  230. package/dist/utils/parallel.d.ts.map +0 -1
  231. package/dist/utils/parallel.js.map +0 -1
  232. package/dist/utils/scanner.d.ts.map +0 -1
  233. package/dist/utils/scanner.js.map +0 -1
  234. package/dist/utils/ui.d.ts.map +0 -1
  235. package/dist/utils/ui.js.map +0 -1
  236. package/dist/utils/ui.test.d.ts +0 -2
  237. package/dist/utils/ui.test.d.ts.map +0 -1
  238. package/dist/utils/ui.test.js +0 -31
  239. package/dist/utils/ui.test.js.map +0 -1
  240. package/dist/utils/validate.d.ts.map +0 -1
  241. package/dist/utils/validate.js.map +0 -1
@@ -3,6 +3,7 @@ import { join } from "node:path";
3
3
  import { homedir } from "node:os";
4
4
  import { ui, banner, table } from "../utils/ui.js";
5
5
  import { readHistory } from "../utils/history.js";
6
+ import { getDirSize } from "../utils/fs.js";
6
7
  function discoverSessions() {
7
8
  const projectsDir = join(homedir(), ".claude", "projects");
8
9
  if (!existsSync(projectsDir))
@@ -58,6 +59,44 @@ function formatBytes(bytes) {
58
59
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
59
60
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
60
61
  }
62
+ function getDiskBreakdown() {
63
+ const claudeDir = join(homedir(), ".claude");
64
+ const dirs = [
65
+ "projects",
66
+ "file-history",
67
+ "skills",
68
+ "debug",
69
+ "todos",
70
+ "shell-snapshots",
71
+ "plans",
72
+ "cache",
73
+ "usage-data",
74
+ ];
75
+ const breakdown = [];
76
+ for (const dirName of dirs) {
77
+ const dir = join(claudeDir, dirName);
78
+ if (!existsSync(dir))
79
+ continue;
80
+ breakdown.push({ name: dirName, sizeBytes: getDirSize(dir) });
81
+ }
82
+ breakdown.sort((a, b) => b.sizeBytes - a.sizeBytes);
83
+ return breakdown;
84
+ }
85
+ function getProjectBreakdown() {
86
+ const projectsDir = join(homedir(), ".claude", "projects");
87
+ if (!existsSync(projectsDir))
88
+ return [];
89
+ const projects = [];
90
+ for (const entry of readdirSync(projectsDir)) {
91
+ const full = join(projectsDir, entry);
92
+ if (!statSync(full).isDirectory())
93
+ continue;
94
+ const jsonlCount = readdirSync(full).filter((f) => f.endsWith(".jsonl")).length;
95
+ projects.push({ name: entry, sizeBytes: getDirSize(full), sessionCount: jsonlCount });
96
+ }
97
+ projects.sort((a, b) => b.sizeBytes - a.sizeBytes);
98
+ return projects;
99
+ }
61
100
  export async function statsCommand(opts) {
62
101
  if (!opts.json)
63
102
  banner();
@@ -77,9 +116,7 @@ export async function statsCommand(opts) {
77
116
  const avgLines = Math.round(totalLines / sessions.length);
78
117
  // Rough token estimate: ~4 chars per token for LLM tokenizers
79
118
  const estimatedTokens = Math.round(totalSize / 4);
80
- // Find unique projects
81
119
  const projects = new Set(sessions.map((s) => s.project));
82
- // Find most recent and oldest sessions
83
120
  const sorted = [...sessions].sort((a, b) => b.modified.getTime() - a.modified.getTime());
84
121
  const newest = sorted[0];
85
122
  const oldest = sorted[sorted.length - 1];
@@ -88,9 +125,16 @@ export async function statsCommand(opts) {
88
125
  for (const s of sessions) {
89
126
  projectCounts.set(s.project, (projectCounts.get(s.project) ?? 0) + 1);
90
127
  }
91
- const topProjects = [...projectCounts.entries()]
92
- .sort((a, b) => b[1] - a[1])
93
- .slice(0, 5);
128
+ const topProjects = [...projectCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5);
129
+ // Disk breakdown
130
+ const diskBreakdown = getDiskBreakdown();
131
+ const projectBreakdown = getProjectBreakdown();
132
+ const totalDisk = diskBreakdown.reduce((sum, d) => sum + d.sizeBytes, 0);
133
+ // Calculate reclaimable space (auxiliary dirs)
134
+ const reclaimableDirs = ["file-history", "debug", "shell-snapshots", "todos", "plans"];
135
+ const reclaimable = diskBreakdown
136
+ .filter((d) => reclaimableDirs.includes(d.name))
137
+ .reduce((sum, d) => sum + d.sizeBytes, 0);
94
138
  if (opts.json) {
95
139
  const data = {
96
140
  totalSessions: sessions.length,
@@ -98,10 +142,12 @@ export async function statsCommand(opts) {
98
142
  totalSizeBytes: totalSize,
99
143
  estimatedTokens,
100
144
  avgLinesPerSession: avgLines,
101
- topProjects: topProjects.map(([name, count]) => ({
102
- name,
103
- sessions: count,
104
- })),
145
+ topProjects: topProjects.map(([name, count]) => ({ name, sessions: count })),
146
+ diskBreakdown: diskBreakdown.map((d) => ({ name: d.name, sizeBytes: d.sizeBytes })),
147
+ projectBreakdown: projectBreakdown
148
+ .slice(0, 10)
149
+ .map((p) => ({ name: p.name, sizeBytes: p.sizeBytes, sessions: p.sessionCount })),
150
+ reclaimableBytes: reclaimable,
105
151
  };
106
152
  console.log(JSON.stringify(data, null, 2));
107
153
  return;
@@ -110,13 +156,34 @@ export async function statsCommand(opts) {
110
156
  const rows = [
111
157
  [ui.dim("Sessions"), String(sessions.length)],
112
158
  [ui.dim("Projects"), String(projects.size)],
113
- [ui.dim("Total data"), formatBytes(totalSize)],
159
+ [ui.dim("Total session data"), formatBytes(totalSize)],
114
160
  [ui.dim("Est. tokens"), `~${(estimatedTokens / 1_000_000).toFixed(1)}M (rough)`],
115
161
  [ui.dim("Avg lines/session"), String(avgLines)],
116
162
  [ui.dim("Newest session"), newest.modified.toLocaleDateString()],
117
163
  [ui.dim("Oldest session"), oldest.modified.toLocaleDateString()],
118
164
  ];
119
165
  table(rows);
166
+ // Disk breakdown
167
+ console.log();
168
+ console.log(ui.bold(" Disk Breakdown\n"));
169
+ const diskRows = diskBreakdown
170
+ .filter((d) => d.sizeBytes > 0)
171
+ .map((d) => [ui.dim(`~/.claude/${d.name}/`), formatBytes(d.sizeBytes)]);
172
+ diskRows.push([ui.bold("Total"), ui.bold(formatBytes(totalDisk))]);
173
+ table(diskRows);
174
+ // Per-project breakdown (top 5)
175
+ if (projectBreakdown.length > 0) {
176
+ console.log();
177
+ console.log(ui.bold(" Projects by Size\n"));
178
+ const projRows = projectBreakdown
179
+ .slice(0, 5)
180
+ .map((p) => [
181
+ ui.dim(p.name.length > 45 ? p.name.slice(0, 42) + "..." : p.name),
182
+ formatBytes(p.sizeBytes),
183
+ `${p.sessionCount} sessions`,
184
+ ]);
185
+ table(projRows);
186
+ }
120
187
  if (topProjects.length > 0) {
121
188
  console.log();
122
189
  console.log(ui.bold(" Most Active Projects\n"));
@@ -126,18 +193,18 @@ export async function statsCommand(opts) {
126
193
  ]);
127
194
  table(projRows);
128
195
  }
196
+ // Cleanup suggestion
197
+ if (reclaimable > 1024 * 1024) {
198
+ console.log();
199
+ console.log(ui.warn(` ${formatBytes(reclaimable)} reclaimable in temp directories. Run: arcana clean`));
200
+ }
129
201
  const history = readHistory();
130
202
  if (history.length > 0) {
131
203
  console.log();
132
204
  console.log(ui.bold(" Recent Arcana Activity\n"));
133
205
  const recent = history.slice(-5).reverse();
134
- const histRows = recent.map(e => [
135
- ui.dim(new Date(e.timestamp).toLocaleDateString()),
136
- e.action,
137
- e.target ?? "",
138
- ]);
206
+ const histRows = recent.map((e) => [ui.dim(new Date(e.timestamp).toLocaleDateString()), e.action, e.target ?? ""]);
139
207
  table(histRows);
140
208
  }
141
209
  console.log();
142
210
  }
143
- //# sourceMappingURL=stats.js.map
@@ -0,0 +1,3 @@
1
+ export declare function teamCommand(action: string | undefined, skill: string | undefined, opts: {
2
+ json?: boolean;
3
+ }): Promise<void>;
@@ -0,0 +1,291 @@
1
+ import { existsSync, readFileSync, mkdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { atomicWriteSync } from "../utils/atomic.js";
4
+ import { isSkillInstalled, readSkillMeta } from "../utils/fs.js";
5
+ import { validateSlug } from "../utils/validate.js";
6
+ const TEAM_DIR = ".arcana";
7
+ const TEAM_FILE = "team.json";
8
+ function getTeamConfigPath() {
9
+ return join(process.cwd(), TEAM_DIR, TEAM_FILE);
10
+ }
11
+ function readTeamConfig() {
12
+ const configPath = getTeamConfigPath();
13
+ if (!existsSync(configPath))
14
+ return null;
15
+ try {
16
+ const raw = readFileSync(configPath, "utf-8");
17
+ return JSON.parse(raw);
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ function writeTeamConfig(config) {
24
+ const dir = join(process.cwd(), TEAM_DIR);
25
+ if (!existsSync(dir)) {
26
+ mkdirSync(dir, { recursive: true });
27
+ }
28
+ atomicWriteSync(getTeamConfigPath(), JSON.stringify(config, null, 2) + "\n", 0o644);
29
+ }
30
+ function output(json, message) {
31
+ if (!json) {
32
+ console.log(message);
33
+ }
34
+ }
35
+ export async function teamCommand(action, skill, opts) {
36
+ if (action === "init") {
37
+ return teamInit(opts);
38
+ }
39
+ if (action === "sync") {
40
+ return teamSync(opts);
41
+ }
42
+ if (action === "add") {
43
+ if (!skill) {
44
+ if (opts.json) {
45
+ console.log(JSON.stringify({ error: "Skill name required for add" }));
46
+ }
47
+ else {
48
+ console.error("Error: Skill name required. Usage: arcana team add <skill>");
49
+ }
50
+ process.exit(1);
51
+ }
52
+ return teamAdd(skill, opts);
53
+ }
54
+ if (action === "remove") {
55
+ if (!skill) {
56
+ if (opts.json) {
57
+ console.log(JSON.stringify({ error: "Skill name required for remove" }));
58
+ }
59
+ else {
60
+ console.error("Error: Skill name required. Usage: arcana team remove <skill>");
61
+ }
62
+ process.exit(1);
63
+ }
64
+ return teamRemove(skill, opts);
65
+ }
66
+ if (action === undefined) {
67
+ return teamList(opts);
68
+ }
69
+ if (opts.json) {
70
+ console.log(JSON.stringify({ error: `Unknown action: ${action}` }));
71
+ }
72
+ else {
73
+ console.error(`Error: Unknown action "${action}". Valid actions: init, sync, add, remove`);
74
+ }
75
+ process.exit(1);
76
+ }
77
+ function teamInit(opts) {
78
+ const configPath = getTeamConfigPath();
79
+ if (existsSync(configPath)) {
80
+ if (opts.json) {
81
+ console.log(JSON.stringify({ error: "Team config already exists", path: configPath }));
82
+ }
83
+ else {
84
+ console.error(`Error: ${configPath} already exists.`);
85
+ }
86
+ process.exit(1);
87
+ }
88
+ const config = {
89
+ skills: [],
90
+ updatedAt: new Date().toISOString(),
91
+ };
92
+ writeTeamConfig(config);
93
+ if (opts.json) {
94
+ console.log(JSON.stringify({ created: configPath }));
95
+ }
96
+ else {
97
+ console.log(`Created ${configPath}`);
98
+ console.log("Add skills with: arcana team add <skill-name>");
99
+ }
100
+ }
101
+ async function teamSync(opts) {
102
+ const config = readTeamConfig();
103
+ if (!config) {
104
+ if (opts.json) {
105
+ console.log(JSON.stringify({ error: "No team config found. Run: arcana team init" }));
106
+ }
107
+ else {
108
+ console.error("Error: No team config found. Run: arcana team init");
109
+ }
110
+ process.exit(1);
111
+ }
112
+ if (config.skills.length === 0) {
113
+ if (opts.json) {
114
+ console.log(JSON.stringify({ installed: [], skipped: [], message: "No skills in team config" }));
115
+ }
116
+ else {
117
+ console.log("No skills listed in team config.");
118
+ }
119
+ return;
120
+ }
121
+ const { installSkill } = await import("../utils/fs.js");
122
+ const installed = [];
123
+ const skipped = [];
124
+ const failed = [];
125
+ for (const entry of config.skills) {
126
+ if (isSkillInstalled(entry.name)) {
127
+ skipped.push(entry.name);
128
+ continue;
129
+ }
130
+ try {
131
+ validateSlug(entry.name, "skill name");
132
+ }
133
+ catch (_err) {
134
+ output(opts.json, `Invalid skill name: ${entry.name}`);
135
+ failed.push(entry.name);
136
+ continue;
137
+ }
138
+ // installSkill requires files, but for sync we re-fetch from provider
139
+ // Lazy import the registry to avoid circular deps at module load
140
+ try {
141
+ const { getProvider } = await import("../registry.js");
142
+ const { loadConfig } = await import("../utils/config.js");
143
+ const { updateLockEntry } = await import("../utils/integrity.js");
144
+ const { writeSkillMeta } = await import("../utils/fs.js");
145
+ const providerName = entry.source ?? loadConfig().defaultProvider;
146
+ const provider = getProvider(providerName);
147
+ const files = await provider.fetch(entry.name);
148
+ installSkill(entry.name, files);
149
+ const remote = await provider.info(entry.name);
150
+ const version = remote?.version ?? entry.version ?? "0.0.0";
151
+ writeSkillMeta(entry.name, {
152
+ version,
153
+ installedAt: new Date().toISOString(),
154
+ source: provider.name,
155
+ description: remote?.description,
156
+ fileCount: files.length,
157
+ sizeBytes: files.reduce((s, f) => s + f.content.length, 0),
158
+ });
159
+ updateLockEntry(entry.name, version, provider.name, files);
160
+ installed.push(entry.name);
161
+ }
162
+ catch (err) {
163
+ const msg = err instanceof Error ? err.message : "unknown error";
164
+ output(opts.json, `Failed to install ${entry.name}: ${msg}`);
165
+ failed.push(entry.name);
166
+ }
167
+ }
168
+ if (opts.json) {
169
+ console.log(JSON.stringify({ installed, skipped, failed }));
170
+ }
171
+ else {
172
+ console.log(`Sync complete: ${installed.length} installed, ${skipped.length} skipped, ${failed.length} failed`);
173
+ }
174
+ if (failed.length > 0)
175
+ process.exit(1);
176
+ }
177
+ function teamAdd(skill, opts) {
178
+ try {
179
+ validateSlug(skill, "skill name");
180
+ }
181
+ catch (err) {
182
+ if (opts.json) {
183
+ console.log(JSON.stringify({ error: err instanceof Error ? err.message : "Invalid skill name" }));
184
+ }
185
+ else {
186
+ console.error(err instanceof Error ? err.message : "Invalid skill name");
187
+ }
188
+ process.exit(1);
189
+ }
190
+ const config = readTeamConfig();
191
+ if (!config) {
192
+ if (opts.json) {
193
+ console.log(JSON.stringify({ error: "No team config found. Run: arcana team init" }));
194
+ }
195
+ else {
196
+ console.error("Error: No team config found. Run: arcana team init");
197
+ }
198
+ process.exit(1);
199
+ }
200
+ const exists = config.skills.find((s) => s.name === skill);
201
+ if (exists) {
202
+ if (opts.json) {
203
+ console.log(JSON.stringify({ error: `${skill} is already in team config` }));
204
+ }
205
+ else {
206
+ console.error(`Error: ${skill} is already in team config.`);
207
+ }
208
+ process.exit(1);
209
+ }
210
+ const entry = { name: skill };
211
+ // If skill is installed locally, read meta for version/source
212
+ const meta = readSkillMeta(skill);
213
+ if (meta) {
214
+ if (meta.version)
215
+ entry.version = meta.version;
216
+ if (meta.source)
217
+ entry.source = meta.source;
218
+ }
219
+ config.skills.push(entry);
220
+ config.updatedAt = new Date().toISOString();
221
+ writeTeamConfig(config);
222
+ if (opts.json) {
223
+ console.log(JSON.stringify({ added: entry }));
224
+ }
225
+ else {
226
+ console.log(`Added ${skill} to team config.`);
227
+ }
228
+ }
229
+ function teamRemove(skill, opts) {
230
+ const config = readTeamConfig();
231
+ if (!config) {
232
+ if (opts.json) {
233
+ console.log(JSON.stringify({ error: "No team config found. Run: arcana team init" }));
234
+ }
235
+ else {
236
+ console.error("Error: No team config found. Run: arcana team init");
237
+ }
238
+ process.exit(1);
239
+ }
240
+ const idx = config.skills.findIndex((s) => s.name === skill);
241
+ if (idx < 0) {
242
+ if (opts.json) {
243
+ console.log(JSON.stringify({ error: `${skill} is not in team config` }));
244
+ }
245
+ else {
246
+ console.error(`Error: ${skill} is not in team config.`);
247
+ }
248
+ process.exit(1);
249
+ }
250
+ config.skills.splice(idx, 1);
251
+ config.updatedAt = new Date().toISOString();
252
+ writeTeamConfig(config);
253
+ if (opts.json) {
254
+ console.log(JSON.stringify({ removed: skill }));
255
+ }
256
+ else {
257
+ console.log(`Removed ${skill} from team config.`);
258
+ }
259
+ }
260
+ function teamList(opts) {
261
+ const config = readTeamConfig();
262
+ if (!config) {
263
+ if (opts.json) {
264
+ console.log(JSON.stringify({ error: "No team config found. Run: arcana team init" }));
265
+ }
266
+ else {
267
+ console.error("Error: No team config found. Run: arcana team init");
268
+ }
269
+ process.exit(1);
270
+ }
271
+ if (opts.json) {
272
+ console.log(JSON.stringify({ skills: config.skills, updatedAt: config.updatedAt }));
273
+ return;
274
+ }
275
+ if (config.skills.length === 0) {
276
+ console.log("No skills in team config. Add one with: arcana team add <skill>");
277
+ return;
278
+ }
279
+ console.log(`Team skills (${config.skills.length}):`);
280
+ for (const entry of config.skills) {
281
+ const parts = [entry.name];
282
+ if (entry.version)
283
+ parts.push(`v${entry.version}`);
284
+ if (entry.source)
285
+ parts.push(`(${entry.source})`);
286
+ const installed = isSkillInstalled(entry.name);
287
+ if (installed)
288
+ parts.push("[installed]");
289
+ console.log(` ${parts.join(" ")}`);
290
+ }
291
+ }
@@ -3,4 +3,3 @@ export declare function uninstallCommand(skillNames: string[], opts?: {
3
3
  json?: boolean;
4
4
  }): Promise<void>;
5
5
  export declare function removeSymlinksFor(skillName: string): number;
6
- //# sourceMappingURL=uninstall.d.ts.map
@@ -125,7 +125,12 @@ function uninstallJson(skillNames) {
125
125
  validateSlug(skillName, "skill name");
126
126
  }
127
127
  catch (err) {
128
- results.push({ name: skillName, success: false, error: err instanceof Error ? err.message : "Invalid name", symlinksRemoved: 0 });
128
+ results.push({
129
+ name: skillName,
130
+ success: false,
131
+ error: err instanceof Error ? err.message : "Invalid name",
132
+ symlinksRemoved: 0,
133
+ });
129
134
  continue;
130
135
  }
131
136
  const skillDir = getSkillDir(skillName);
@@ -139,7 +144,13 @@ function uninstallJson(skillNames) {
139
144
  rmSync(skillDir, { recursive: true, force: true });
140
145
  }
141
146
  catch (err) {
142
- results.push({ name: skillName, success: false, version: meta?.version, error: err instanceof Error ? err.message : "Failed to remove", symlinksRemoved: 0 });
147
+ results.push({
148
+ name: skillName,
149
+ success: false,
150
+ version: meta?.version,
151
+ error: err instanceof Error ? err.message : "Failed to remove",
152
+ symlinksRemoved: 0,
153
+ });
143
154
  continue;
144
155
  }
145
156
  const symlinksRemoved = removeSymlinksFor(skillName);
@@ -147,7 +158,11 @@ function uninstallJson(skillNames) {
147
158
  }
148
159
  if (results.length === 1) {
149
160
  const r = results[0];
150
- const output = { uninstalled: r.name, success: r.success, symlinksRemoved: r.symlinksRemoved };
161
+ const output = {
162
+ uninstalled: r.name,
163
+ success: r.success,
164
+ symlinksRemoved: r.symlinksRemoved,
165
+ };
151
166
  if (r.version)
152
167
  output.version = r.version;
153
168
  if (r.error)
@@ -160,4 +175,3 @@ function uninstallJson(skillNames) {
160
175
  if (results.some((r) => !r.success))
161
176
  process.exit(1);
162
177
  }
163
- //# sourceMappingURL=uninstall.js.map
@@ -4,4 +4,3 @@ export declare function updateCommand(skills: string[], opts: {
4
4
  dryRun?: boolean;
5
5
  json?: boolean;
6
6
  }): Promise<void>;
7
- //# sourceMappingURL=update.d.ts.map