@sporesec/arcana 2.4.0 → 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 (245) hide show
  1. package/dist/cli.d.ts +0 -1
  2. package/dist/cli.js +120 -9
  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 +0 -1
  10. package/dist/commands/clean.js +19 -8
  11. package/dist/commands/compact.d.ts +2 -1
  12. package/dist/commands/compact.js +74 -14
  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 +64 -23
  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 +26 -33
  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 +0 -1
  38. package/dist/commands/optimize.js +111 -20
  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 +24 -20
  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 +0 -1
  112. package/dist/utils/fs.js +30 -11
  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 +74 -62
  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/compact.d.ts.map +0 -1
  147. package/dist/commands/compact.js.map +0 -1
  148. package/dist/commands/config.d.ts.map +0 -1
  149. package/dist/commands/config.js.map +0 -1
  150. package/dist/commands/create.d.ts.map +0 -1
  151. package/dist/commands/create.js.map +0 -1
  152. package/dist/commands/doctor.d.ts.map +0 -1
  153. package/dist/commands/doctor.js.map +0 -1
  154. package/dist/commands/info.d.ts.map +0 -1
  155. package/dist/commands/info.js.map +0 -1
  156. package/dist/commands/init.d.ts.map +0 -1
  157. package/dist/commands/init.js.map +0 -1
  158. package/dist/commands/install.d.ts.map +0 -1
  159. package/dist/commands/install.js.map +0 -1
  160. package/dist/commands/list.d.ts.map +0 -1
  161. package/dist/commands/list.js.map +0 -1
  162. package/dist/commands/optimize.d.ts.map +0 -1
  163. package/dist/commands/optimize.js.map +0 -1
  164. package/dist/commands/providers.d.ts.map +0 -1
  165. package/dist/commands/providers.js.map +0 -1
  166. package/dist/commands/scan.d.ts.map +0 -1
  167. package/dist/commands/scan.js.map +0 -1
  168. package/dist/commands/search.d.ts.map +0 -1
  169. package/dist/commands/search.js.map +0 -1
  170. package/dist/commands/stats.d.ts.map +0 -1
  171. package/dist/commands/stats.js.map +0 -1
  172. package/dist/commands/uninstall.d.ts.map +0 -1
  173. package/dist/commands/uninstall.js.map +0 -1
  174. package/dist/commands/update.d.ts.map +0 -1
  175. package/dist/commands/update.js.map +0 -1
  176. package/dist/commands/validate.d.ts.map +0 -1
  177. package/dist/commands/validate.js.map +0 -1
  178. package/dist/index.d.ts.map +0 -1
  179. package/dist/index.js.map +0 -1
  180. package/dist/interactive.d.ts.map +0 -1
  181. package/dist/interactive.js.map +0 -1
  182. package/dist/providers/arcana.d.ts.map +0 -1
  183. package/dist/providers/arcana.js.map +0 -1
  184. package/dist/providers/base.d.ts.map +0 -1
  185. package/dist/providers/base.js.map +0 -1
  186. package/dist/providers/github.d.ts.map +0 -1
  187. package/dist/providers/github.js.map +0 -1
  188. package/dist/registry.d.ts.map +0 -1
  189. package/dist/registry.js.map +0 -1
  190. package/dist/types.d.ts.map +0 -1
  191. package/dist/types.js.map +0 -1
  192. package/dist/utils/atomic.d.ts.map +0 -1
  193. package/dist/utils/atomic.js.map +0 -1
  194. package/dist/utils/atomic.test.d.ts +0 -2
  195. package/dist/utils/atomic.test.d.ts.map +0 -1
  196. package/dist/utils/atomic.test.js +0 -31
  197. package/dist/utils/atomic.test.js.map +0 -1
  198. package/dist/utils/cache.d.ts.map +0 -1
  199. package/dist/utils/cache.js.map +0 -1
  200. package/dist/utils/config.d.ts.map +0 -1
  201. package/dist/utils/config.js.map +0 -1
  202. package/dist/utils/config.test.d.ts +0 -2
  203. package/dist/utils/config.test.d.ts.map +0 -1
  204. package/dist/utils/config.test.js +0 -38
  205. package/dist/utils/config.test.js.map +0 -1
  206. package/dist/utils/errors.d.ts.map +0 -1
  207. package/dist/utils/errors.js.map +0 -1
  208. package/dist/utils/frontmatter.d.ts.map +0 -1
  209. package/dist/utils/frontmatter.js.map +0 -1
  210. package/dist/utils/frontmatter.test.d.ts +0 -2
  211. package/dist/utils/frontmatter.test.d.ts.map +0 -1
  212. package/dist/utils/frontmatter.test.js +0 -152
  213. package/dist/utils/frontmatter.test.js.map +0 -1
  214. package/dist/utils/fs.d.ts.map +0 -1
  215. package/dist/utils/fs.js.map +0 -1
  216. package/dist/utils/fs.test.d.ts +0 -2
  217. package/dist/utils/fs.test.d.ts.map +0 -1
  218. package/dist/utils/fs.test.js +0 -145
  219. package/dist/utils/fs.test.js.map +0 -1
  220. package/dist/utils/help.d.ts.map +0 -1
  221. package/dist/utils/help.js.map +0 -1
  222. package/dist/utils/help.test.d.ts +0 -2
  223. package/dist/utils/help.test.d.ts.map +0 -1
  224. package/dist/utils/help.test.js +0 -66
  225. package/dist/utils/help.test.js.map +0 -1
  226. package/dist/utils/history.d.ts.map +0 -1
  227. package/dist/utils/history.js.map +0 -1
  228. package/dist/utils/http.d.ts.map +0 -1
  229. package/dist/utils/http.js.map +0 -1
  230. package/dist/utils/http.test.d.ts +0 -2
  231. package/dist/utils/http.test.d.ts.map +0 -1
  232. package/dist/utils/http.test.js +0 -55
  233. package/dist/utils/http.test.js.map +0 -1
  234. package/dist/utils/parallel.d.ts.map +0 -1
  235. package/dist/utils/parallel.js.map +0 -1
  236. package/dist/utils/scanner.d.ts.map +0 -1
  237. package/dist/utils/scanner.js.map +0 -1
  238. package/dist/utils/ui.d.ts.map +0 -1
  239. package/dist/utils/ui.js.map +0 -1
  240. package/dist/utils/ui.test.d.ts +0 -2
  241. package/dist/utils/ui.test.d.ts.map +0 -1
  242. package/dist/utils/ui.test.js +0 -31
  243. package/dist/utils/ui.test.js.map +0 -1
  244. package/dist/utils/validate.d.ts.map +0 -1
  245. package/dist/utils/validate.js.map +0 -1
@@ -6,15 +6,29 @@ import { getProvider, getProviders } from "../registry.js";
6
6
  import { ui, banner, spinner, noopSpinner } from "../utils/ui.js";
7
7
  import { loadConfig } from "../utils/config.js";
8
8
  import { validateSlug } from "../utils/validate.js";
9
+ import { updateLockEntry } from "../utils/integrity.js";
9
10
  function isNewer(remoteVersion, localVersion) {
10
11
  const local = semver.valid(semver.coerce(localVersion)) ?? "0.0.0";
11
12
  const remote = semver.valid(semver.coerce(remoteVersion)) ?? "0.0.0";
12
13
  return semver.gt(remote, local);
13
14
  }
15
+ /** Fetch, write files, write meta, update lock for a single skill. */
16
+ async function applyUpdate(skillName, remote, provider) {
17
+ const files = await provider.fetch(skillName);
18
+ installSkill(skillName, files);
19
+ writeSkillMeta(skillName, {
20
+ version: remote.version,
21
+ installedAt: new Date().toISOString(),
22
+ source: provider.name,
23
+ description: remote.description,
24
+ fileCount: files.length,
25
+ });
26
+ updateLockEntry(skillName, remote.version, provider.name, files);
27
+ return files;
28
+ }
14
29
  export async function updateCommand(skills, opts) {
15
- if (!opts.json) {
30
+ if (!opts.json)
16
31
  banner();
17
- }
18
32
  if (skills.length === 0 && !opts.all) {
19
33
  if (opts.json) {
20
34
  console.log(JSON.stringify({ error: "Specify a skill name or use --all" }));
@@ -40,13 +54,13 @@ export async function updateCommand(skills, opts) {
40
54
  }
41
55
  const providerName = opts.provider ?? loadConfig().defaultProvider;
42
56
  if (opts.all) {
43
- await updateAll(installDir, providerName, opts.json, opts.dryRun);
57
+ await updateBatch(null, installDir, providerName, opts.json, opts.dryRun);
44
58
  }
45
59
  else if (skills.length === 1) {
46
60
  await updateOne(skills[0], installDir, providerName, opts.json, opts.dryRun);
47
61
  }
48
62
  else {
49
- await updateMultiple(skills, installDir, providerName, opts.json, opts.dryRun);
63
+ await updateBatch(skills, installDir, providerName, opts.json, opts.dryRun);
50
64
  }
51
65
  }
52
66
  async function updateOne(skillName, installDir, providerName, json, dryRun) {
@@ -55,7 +69,12 @@ async function updateOne(skillName, installDir, providerName, json, dryRun) {
55
69
  }
56
70
  catch (err) {
57
71
  if (json) {
58
- console.log(JSON.stringify({ updated: [], upToDate: [], failed: [skillName], error: err instanceof Error ? err.message : "Invalid name" }));
72
+ console.log(JSON.stringify({
73
+ updated: [],
74
+ upToDate: [],
75
+ failed: [skillName],
76
+ error: err instanceof Error ? err.message : "Invalid name",
77
+ }));
59
78
  }
60
79
  else {
61
80
  console.log(ui.error(` ${err instanceof Error ? err.message : "Invalid skill name"}`));
@@ -102,7 +121,10 @@ async function updateOne(skillName, installDir, providerName, json, dryRun) {
102
121
  }
103
122
  if (dryRun) {
104
123
  if (json) {
105
- console.log(JSON.stringify({ dryRun: true, wouldUpdate: [{ name: skillName, from: meta?.version ?? "unknown", to: remote.version }] }));
124
+ console.log(JSON.stringify({
125
+ dryRun: true,
126
+ wouldUpdate: [{ name: skillName, from: meta?.version ?? "unknown", to: remote.version }],
127
+ }));
106
128
  }
107
129
  else {
108
130
  s.info(`${ui.bold(skillName)} would be updated: v${meta?.version ?? "unknown"} -> v${remote.version}`);
@@ -111,15 +133,7 @@ async function updateOne(skillName, installDir, providerName, json, dryRun) {
111
133
  return;
112
134
  }
113
135
  s.text = `Updating ${ui.bold(skillName)}...`;
114
- const files = await provider.fetch(skillName);
115
- installSkill(skillName, files);
116
- writeSkillMeta(skillName, {
117
- version: remote.version,
118
- installedAt: new Date().toISOString(),
119
- source: providerName,
120
- description: remote.description,
121
- fileCount: files.length,
122
- });
136
+ const files = await applyUpdate(skillName, remote, provider);
123
137
  if (json) {
124
138
  console.log(JSON.stringify({ updated: [skillName], upToDate: [], failed: [] }));
125
139
  }
@@ -130,7 +144,12 @@ async function updateOne(skillName, installDir, providerName, json, dryRun) {
130
144
  }
131
145
  catch (err) {
132
146
  if (json) {
133
- console.log(JSON.stringify({ updated: [], upToDate: [], failed: [skillName], error: err instanceof Error ? err.message : "Update failed" }));
147
+ console.log(JSON.stringify({
148
+ updated: [],
149
+ upToDate: [],
150
+ failed: [skillName],
151
+ error: err instanceof Error ? err.message : "Update failed",
152
+ }));
134
153
  }
135
154
  else {
136
155
  s.fail(`Failed to update ${skillName}`);
@@ -141,102 +160,35 @@ async function updateOne(skillName, installDir, providerName, json, dryRun) {
141
160
  process.exit(1);
142
161
  }
143
162
  }
144
- async function updateMultiple(skillNames, installDir, providerName, json, dryRun) {
145
- for (const name of skillNames) {
146
- try {
147
- validateSlug(name, "skill name");
148
- }
149
- catch (err) {
150
- if (json) {
151
- console.log(JSON.stringify({ updated: [], upToDate: [], failed: skillNames, error: err instanceof Error ? err.message : "Invalid name" }));
163
+ /**
164
+ * Batch update. If `skillNames` is null, update all installed skills.
165
+ * If `skillNames` is provided, update only those specific skills.
166
+ */
167
+ async function updateBatch(skillNames, installDir, providerName, json, dryRun) {
168
+ // Validate explicit skill names
169
+ if (skillNames) {
170
+ for (const name of skillNames) {
171
+ try {
172
+ validateSlug(name, "skill name");
152
173
  }
153
- else {
154
- console.log(ui.error(` ${err instanceof Error ? err.message : "Invalid skill name"}`));
155
- console.log();
156
- }
157
- process.exit(1);
158
- }
159
- }
160
- const s = json ? noopSpinner() : spinner(`Checking ${skillNames.length} skills for updates...`);
161
- s.start();
162
- const provider = getProvider(providerName);
163
- const updatedList = [];
164
- const upToDateList = [];
165
- const failedList = [];
166
- const dryRunUpdates = [];
167
- for (let i = 0; i < skillNames.length; i++) {
168
- const skillName = skillNames[i];
169
- const skillDir = join(installDir, skillName);
170
- if (!existsSync(skillDir)) {
171
- failedList.push(skillName);
172
- if (!json)
173
- console.error(ui.dim(` ${skillName} is not installed`));
174
- continue;
175
- }
176
- try {
177
- const remote = await provider.info(skillName);
178
- if (!remote) {
179
- failedList.push(skillName);
180
- continue;
181
- }
182
- const meta = readSkillMeta(skillName);
183
- if (!isNewer(remote.version, meta?.version)) {
184
- upToDateList.push(skillName);
185
- continue;
186
- }
187
- if (dryRun) {
188
- dryRunUpdates.push({ name: skillName, from: meta?.version ?? "unknown", to: remote.version });
189
- continue;
190
- }
191
- s.text = `Updating ${ui.bold(skillName)} (${i + 1}/${skillNames.length})...`;
192
- const files = await provider.fetch(skillName);
193
- installSkill(skillName, files);
194
- writeSkillMeta(skillName, {
195
- version: remote.version,
196
- installedAt: new Date().toISOString(),
197
- source: providerName,
198
- description: remote.description,
199
- fileCount: files.length,
200
- });
201
- updatedList.push(skillName);
202
- }
203
- catch (err) {
204
- failedList.push(skillName);
205
- if (err instanceof Error && !json)
206
- console.error(ui.dim(` Failed to update ${skillName}: ${err.message}`));
207
- }
208
- }
209
- if (dryRun) {
210
- if (json) {
211
- console.log(JSON.stringify({ dryRun: true, wouldUpdate: dryRunUpdates, upToDate: upToDateList, failed: failedList }));
212
- }
213
- else {
214
- s.stop();
215
- if (dryRunUpdates.length === 0) {
216
- console.log(ui.dim(" All skills are up to date."));
217
- }
218
- else {
219
- for (const u of dryRunUpdates) {
220
- console.log(` ${ui.bold(u.name)}: v${u.from} -> v${u.to}`);
174
+ catch (err) {
175
+ if (json) {
176
+ console.log(JSON.stringify({
177
+ updated: [],
178
+ upToDate: [],
179
+ failed: skillNames,
180
+ error: err instanceof Error ? err.message : "Invalid name",
181
+ }));
182
+ }
183
+ else {
184
+ console.log(ui.error(` ${err instanceof Error ? err.message : "Invalid skill name"}`));
185
+ console.log();
221
186
  }
187
+ process.exit(1);
222
188
  }
223
- console.log();
224
189
  }
225
- return;
226
- }
227
- if (json) {
228
- console.log(JSON.stringify({ updated: updatedList, upToDate: upToDateList, failed: failedList }));
229
190
  }
230
- else {
231
- s.succeed(`Update complete`);
232
- console.log(ui.dim(` ${updatedList.length} updated, ${upToDateList.length} up to date${failedList.length > 0 ? `, ${failedList.length} failed` : ""}`));
233
- console.log();
234
- }
235
- if (failedList.length > 0)
236
- process.exit(1);
237
- }
238
- async function updateAll(installDir, providerName, json, dryRun) {
239
- const installed = readdirSync(installDir).filter((d) => statSync(join(installDir, d)).isDirectory());
191
+ const installed = skillNames ?? readdirSync(installDir).filter((d) => statSync(join(installDir, d)).isDirectory());
240
192
  if (installed.length === 0) {
241
193
  if (json) {
242
194
  console.log(JSON.stringify({ updated: [], upToDate: [], failed: [] }));
@@ -254,69 +206,106 @@ async function updateAll(installDir, providerName, json, dryRun) {
254
206
  const failedList = [];
255
207
  const skippedList = [];
256
208
  const dryRunUpdates = [];
257
- const providers = getProviders(providerName === "arcana" ? undefined : providerName);
258
- // Pre-fetch skill lists to avoid N+1 info() calls
209
+ // For --all mode, use all providers and pre-fetch skill lists (avoids N+1 info() calls)
210
+ // For explicit names, use single provider with per-skill info() calls
211
+ const isAllMode = skillNames === null;
212
+ const providers = isAllMode ? getProviders(providerName === "arcana" ? undefined : providerName) : [];
213
+ const singleProvider = isAllMode ? null : getProvider(providerName);
214
+ // Pre-fetch provider skill maps for --all mode
259
215
  const providerSkillMaps = new Map();
260
- for (const provider of providers) {
261
- try {
262
- const skills = await provider.list();
263
- const map = new Map();
264
- for (const skill of skills) {
265
- map.set(skill.name, { version: skill.version, description: skill.description });
216
+ if (isAllMode) {
217
+ for (const provider of providers) {
218
+ try {
219
+ const skills = await provider.list();
220
+ const map = new Map();
221
+ for (const skill of skills) {
222
+ map.set(skill.name, { version: skill.version, description: skill.description });
223
+ }
224
+ providerSkillMaps.set(provider.name, map);
225
+ }
226
+ catch (err) {
227
+ if (err instanceof Error && !json)
228
+ console.error(ui.dim(` Failed to list ${provider.name}: ${err.message}`));
266
229
  }
267
- providerSkillMaps.set(provider.name, map);
268
- }
269
- catch (err) {
270
- if (err instanceof Error && !json)
271
- console.error(ui.dim(` Failed to list ${provider.name}: ${err.message}`));
272
230
  }
273
231
  }
274
232
  const total = installed.length;
275
233
  for (let i = 0; i < total; i++) {
276
- const skillName = installed[i];
277
- let found = false;
234
+ const name = installed[i];
235
+ // Check installation exists (for explicit names)
236
+ if (!isAllMode) {
237
+ const skillDir = join(installDir, name);
238
+ if (!existsSync(skillDir)) {
239
+ failedList.push(name);
240
+ if (!json)
241
+ console.error(ui.dim(` ${name} is not installed`));
242
+ continue;
243
+ }
244
+ }
278
245
  try {
279
- for (const provider of providers) {
280
- const skillMap = providerSkillMaps.get(provider.name);
281
- const remote = skillMap?.get(skillName) ?? null;
282
- if (!remote)
246
+ if (isAllMode) {
247
+ // Find across all providers
248
+ let found = false;
249
+ for (const provider of providers) {
250
+ const skillMap = providerSkillMaps.get(provider.name);
251
+ const remote = skillMap?.get(name) ?? null;
252
+ if (!remote)
253
+ continue;
254
+ found = true;
255
+ const meta = readSkillMeta(name);
256
+ if (!isNewer(remote.version, meta?.version)) {
257
+ upToDateList.push(name);
258
+ break;
259
+ }
260
+ if (dryRun) {
261
+ dryRunUpdates.push({ name, from: meta?.version ?? "unknown", to: remote.version });
262
+ break;
263
+ }
264
+ s.text = `Updating ${ui.bold(name)} (${i + 1}/${total})...`;
265
+ await applyUpdate(name, remote, provider);
266
+ updatedList.push(name);
267
+ break;
268
+ }
269
+ if (!found)
270
+ skippedList.push(name);
271
+ }
272
+ else {
273
+ // Single provider mode
274
+ const remote = await singleProvider.info(name);
275
+ if (!remote) {
276
+ failedList.push(name);
283
277
  continue;
284
- found = true;
285
- const meta = readSkillMeta(skillName);
278
+ }
279
+ const meta = readSkillMeta(name);
286
280
  if (!isNewer(remote.version, meta?.version)) {
287
- upToDateList.push(skillName);
288
- break;
281
+ upToDateList.push(name);
282
+ continue;
289
283
  }
290
284
  if (dryRun) {
291
- dryRunUpdates.push({ name: skillName, from: meta?.version ?? "unknown", to: remote.version });
292
- break;
285
+ dryRunUpdates.push({ name, from: meta?.version ?? "unknown", to: remote.version });
286
+ continue;
293
287
  }
294
- s.text = `Updating ${ui.bold(skillName)} (${i + 1}/${total})...`;
295
- const files = await provider.fetch(skillName);
296
- installSkill(skillName, files);
297
- writeSkillMeta(skillName, {
298
- version: remote.version,
299
- installedAt: new Date().toISOString(),
300
- source: provider.name,
301
- description: remote.description,
302
- fileCount: files.length,
303
- });
304
- updatedList.push(skillName);
305
- break;
288
+ s.text = `Updating ${ui.bold(name)} (${i + 1}/${total})...`;
289
+ await applyUpdate(name, remote, singleProvider);
290
+ updatedList.push(name);
306
291
  }
307
292
  }
308
293
  catch (err) {
309
- failedList.push(skillName);
294
+ failedList.push(name);
310
295
  if (err instanceof Error && !json)
311
- console.error(ui.dim(` Failed to update ${skillName}: ${err.message}`));
312
- continue;
296
+ console.error(ui.dim(` Failed to update ${name}: ${err.message}`));
313
297
  }
314
- if (!found)
315
- skippedList.push(skillName);
316
298
  }
299
+ // Output results
317
300
  if (dryRun) {
318
301
  if (json) {
319
- console.log(JSON.stringify({ dryRun: true, wouldUpdate: dryRunUpdates, upToDate: upToDateList, skipped: skippedList, failed: failedList }));
302
+ console.log(JSON.stringify({
303
+ dryRun: true,
304
+ wouldUpdate: dryRunUpdates,
305
+ upToDate: upToDateList,
306
+ ...(isAllMode ? { skipped: skippedList } : {}),
307
+ failed: failedList,
308
+ }));
320
309
  }
321
310
  else {
322
311
  s.stop();
@@ -324,8 +313,10 @@ async function updateAll(installDir, providerName, json, dryRun) {
324
313
  console.log(ui.dim(" All skills are up to date."));
325
314
  }
326
315
  else {
327
- console.log(ui.bold(` ${dryRunUpdates.length} of ${total} skills have updates available:`));
328
- console.log();
316
+ if (isAllMode) {
317
+ console.log(ui.bold(` ${dryRunUpdates.length} of ${total} skills have updates available:`));
318
+ console.log();
319
+ }
329
320
  for (const u of dryRunUpdates) {
330
321
  console.log(` ${ui.bold(u.name)}: v${u.from} -> v${u.to}`);
331
322
  }
@@ -335,14 +326,23 @@ async function updateAll(installDir, providerName, json, dryRun) {
335
326
  return;
336
327
  }
337
328
  if (json) {
338
- console.log(JSON.stringify({ updated: updatedList, upToDate: upToDateList, skipped: skippedList, failed: failedList }));
329
+ console.log(JSON.stringify({
330
+ updated: updatedList,
331
+ upToDate: upToDateList,
332
+ ...(isAllMode ? { skipped: skippedList } : {}),
333
+ failed: failedList,
334
+ }));
339
335
  }
340
336
  else {
341
337
  s.succeed(`Update complete`);
342
- console.log(ui.dim(` ${updatedList.length} updated, ${upToDateList.length} up to date${skippedList.length > 0 ? `, ${skippedList.length} skipped (not on provider)` : ""}${failedList.length > 0 ? `, ${failedList.length} failed` : ""}`));
338
+ const parts = [`${updatedList.length} updated`, `${upToDateList.length} up to date`];
339
+ if (skippedList.length > 0)
340
+ parts.push(`${skippedList.length} skipped (not on provider)`);
341
+ if (failedList.length > 0)
342
+ parts.push(`${failedList.length} failed`);
343
+ console.log(ui.dim(` ${parts.join(", ")}`));
343
344
  console.log();
344
345
  }
345
346
  if (failedList.length > 0)
346
347
  process.exit(1);
347
348
  }
348
- //# sourceMappingURL=update.js.map
@@ -3,4 +3,3 @@ export declare function validateCommand(skill: string | undefined, opts: {
3
3
  fix?: boolean;
4
4
  json?: boolean;
5
5
  }): Promise<void>;
6
- //# sourceMappingURL=validate.d.ts.map
@@ -81,12 +81,23 @@ export async function validateCommand(skill, opts) {
81
81
  }
82
82
  }
83
83
  }
84
- catch { /* skip if unreadable */ }
84
+ catch {
85
+ /* skip if unreadable */
86
+ }
85
87
  }
86
88
  results.push(result);
87
89
  }
88
90
  if (opts.json) {
89
- console.log(JSON.stringify({ results: results.map((r) => ({ skill: r.skill, valid: r.valid, errors: r.errors, warnings: r.warnings, infos: r.infos, fixed: r.fixed ?? false })) }, null, 2));
91
+ console.log(JSON.stringify({
92
+ results: results.map((r) => ({
93
+ skill: r.skill,
94
+ valid: r.valid,
95
+ errors: r.errors,
96
+ warnings: r.warnings,
97
+ infos: r.infos,
98
+ fixed: r.fixed ?? false,
99
+ })),
100
+ }, null, 2));
90
101
  if (results.some((r) => !r.valid))
91
102
  process.exit(1);
92
103
  return;
@@ -96,9 +107,7 @@ export async function validateCommand(skill, opts) {
96
107
  let failed = 0;
97
108
  let fixed = 0;
98
109
  for (const r of results) {
99
- const icon = r.valid
100
- ? r.warnings.length > 0 ? ui.warn("[!!]") : ui.success("[OK]")
101
- : ui.error("[XX]");
110
+ const icon = r.valid ? (r.warnings.length > 0 ? ui.warn("[!!]") : ui.success("[OK]")) : ui.error("[XX]");
102
111
  const fixTag = r.fixed ? ui.cyan(" [fixed]") : "";
103
112
  console.log(` ${icon} ${ui.bold(r.skill)}${fixTag}`);
104
113
  for (const err of r.errors) {
@@ -137,4 +146,3 @@ export async function validateCommand(skill, opts) {
137
146
  if (failed > 0)
138
147
  process.exit(1);
139
148
  }
140
- //# sourceMappingURL=validate.js.map
@@ -0,0 +1,4 @@
1
+ export declare function verifyCommand(skillNames: string[], opts: {
2
+ all?: boolean;
3
+ json?: boolean;
4
+ }): Promise<void>;
@@ -0,0 +1,116 @@
1
+ import { readdirSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import * as p from "@clack/prompts";
4
+ import chalk from "chalk";
5
+ import { getInstallDir, isSkillInstalled } from "../utils/fs.js";
6
+ import { verifySkillIntegrity } from "../utils/integrity.js";
7
+ import { validateSlug } from "../utils/validate.js";
8
+ import { renderBanner } from "../utils/help.js";
9
+ export async function verifyCommand(skillNames, opts) {
10
+ if (opts.json) {
11
+ return verifyJson(skillNames, opts);
12
+ }
13
+ console.log(renderBanner());
14
+ console.log();
15
+ const installDir = getInstallDir();
16
+ if (skillNames.length === 0 && !opts.all) {
17
+ p.intro(chalk.bold("Verify skill integrity"));
18
+ p.cancel("Specify a skill name or use --all");
19
+ p.log.info("Usage: arcana verify <skill-name> [skill2 ...]");
20
+ p.log.info(" arcana verify --all");
21
+ process.exit(1);
22
+ }
23
+ let skills;
24
+ if (opts.all) {
25
+ try {
26
+ skills = readdirSync(installDir).filter((d) => statSync(join(installDir, d)).isDirectory());
27
+ }
28
+ catch {
29
+ skills = [];
30
+ }
31
+ if (skills.length === 0) {
32
+ p.intro(chalk.bold("Verify skill integrity"));
33
+ p.log.info("No installed skills found.");
34
+ p.outro("Nothing to verify.");
35
+ return;
36
+ }
37
+ }
38
+ else {
39
+ for (const name of skillNames) {
40
+ try {
41
+ validateSlug(name, "skill name");
42
+ }
43
+ catch (err) {
44
+ p.log.error(err instanceof Error ? err.message : `Invalid skill name: ${name}`);
45
+ process.exit(1);
46
+ }
47
+ }
48
+ for (const name of skillNames) {
49
+ if (!isSkillInstalled(name)) {
50
+ p.log.error(`Skill ${chalk.bold(name)} is not installed.`);
51
+ process.exit(1);
52
+ }
53
+ }
54
+ skills = skillNames;
55
+ }
56
+ p.intro(chalk.bold("Verify skill integrity"));
57
+ const results = [];
58
+ for (const skill of skills) {
59
+ const status = verifySkillIntegrity(skill, installDir);
60
+ results.push({ skill, status });
61
+ if (status === "ok") {
62
+ p.log.info(`${chalk.green("[OK]")} ${skill}`);
63
+ }
64
+ else if (status === "modified") {
65
+ p.log.warn(`${chalk.yellow("[MODIFIED]")} ${skill}`);
66
+ }
67
+ else {
68
+ p.log.info(`${chalk.dim("[MISSING]")} ${skill}`);
69
+ }
70
+ }
71
+ const total = results.length;
72
+ const okCount = results.filter((r) => r.status === "ok").length;
73
+ const modifiedCount = results.filter((r) => r.status === "modified").length;
74
+ const missingCount = results.filter((r) => r.status === "missing").length;
75
+ console.log();
76
+ p.outro(`${total} skills verified, ${okCount} OK, ${modifiedCount} modified, ${missingCount} not tracked`);
77
+ if (modifiedCount > 0) {
78
+ process.exit(1);
79
+ }
80
+ }
81
+ async function verifyJson(skillNames, opts) {
82
+ const installDir = getInstallDir();
83
+ let skills;
84
+ if (opts.all) {
85
+ try {
86
+ skills = readdirSync(installDir).filter((d) => statSync(join(installDir, d)).isDirectory());
87
+ }
88
+ catch {
89
+ skills = [];
90
+ }
91
+ }
92
+ else if (skillNames.length === 0) {
93
+ console.log(JSON.stringify({ error: "Specify a skill name or use --all" }));
94
+ process.exit(1);
95
+ return;
96
+ }
97
+ else {
98
+ skills = skillNames;
99
+ }
100
+ const results = [];
101
+ for (const skill of skills) {
102
+ const status = verifySkillIntegrity(skill, installDir);
103
+ results.push({ skill, status });
104
+ }
105
+ const total = results.length;
106
+ const ok = results.filter((r) => r.status === "ok").length;
107
+ const modified = results.filter((r) => r.status === "modified").length;
108
+ const missing = results.filter((r) => r.status === "missing").length;
109
+ console.log(JSON.stringify({
110
+ results: results.map((r) => ({ skill: r.skill, status: r.status })),
111
+ summary: { total, ok, modified, missing },
112
+ }));
113
+ if (modified > 0) {
114
+ process.exit(1);
115
+ }
116
+ }
@@ -0,0 +1,10 @@
1
+ export declare const AGENT_LOG_MAX_AGE_DAYS = 7;
2
+ export declare const MAIN_LOG_MAX_AGE_DAYS = 30;
3
+ export declare const STALE_PROJECT_DAYS = 90;
4
+ export declare const PRUNE_DEFAULT_DAYS = 14;
5
+ export declare const PRUNE_SIZE_THRESHOLD_BYTES: number;
6
+ export declare const PRUNE_KEEP_NEWEST = 3;
7
+ export declare const LARGE_SKILL_KB_THRESHOLD = 50;
8
+ export declare const TOKENS_PER_KB = 256;
9
+ export declare const CONTEXT_WINDOW_TOKENS = 200000;
10
+ export declare const DESCRIPTION_TRUNCATION = 50;
@@ -0,0 +1,13 @@
1
+ // Retention & pruning
2
+ export const AGENT_LOG_MAX_AGE_DAYS = 7;
3
+ export const MAIN_LOG_MAX_AGE_DAYS = 30;
4
+ export const STALE_PROJECT_DAYS = 90;
5
+ export const PRUNE_DEFAULT_DAYS = 14;
6
+ export const PRUNE_SIZE_THRESHOLD_BYTES = 10 * 1024 * 1024; // 10 MB
7
+ export const PRUNE_KEEP_NEWEST = 3;
8
+ // Skill size warnings
9
+ export const LARGE_SKILL_KB_THRESHOLD = 50;
10
+ export const TOKENS_PER_KB = 256;
11
+ export const CONTEXT_WINDOW_TOKENS = 200_000;
12
+ // Display
13
+ export const DESCRIPTION_TRUNCATION = 50;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  #!/usr/bin/env node
2
2
  export {};
3
- //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -36,4 +36,3 @@ catch (err) {
36
36
  }
37
37
  process.exit(1);
38
38
  }
39
- //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ import type { SkillInfo } from "../types.js";
2
+ declare function doBatchInstall(names: string[], providerName: string): Promise<number>;
3
+ export declare function browseByCategory(allSkills: SkillInfo[], providerName: string): Promise<void>;
4
+ export { doBatchInstall };