@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
@@ -1,47 +1,12 @@
1
1
  import * as p from "@clack/prompts";
2
2
  import chalk from "chalk";
3
3
  import { printErrorWithHint } from "../utils/ui.js";
4
- import { installSkill, isSkillInstalled, writeSkillMeta, readSkillMeta } from "../utils/fs.js";
4
+ import { isSkillInstalled } from "../utils/fs.js";
5
5
  import { getProvider, getProviders } from "../registry.js";
6
6
  import { loadConfig } from "../utils/config.js";
7
7
  import { renderBanner } from "../utils/help.js";
8
8
  import { validateSlug } from "../utils/validate.js";
9
- import { scanSkillContent } from "../utils/scanner.js";
10
- /**
11
- * Scan fetched skill files for security threats before installing.
12
- * Returns true if install should proceed, false to block.
13
- */
14
- function preInstallScan(skillName, files, force) {
15
- const skillMd = files.find(f => f.path.endsWith("SKILL.md"));
16
- if (!skillMd)
17
- return true;
18
- const issues = scanSkillContent(skillMd.content);
19
- if (issues.length === 0)
20
- return true;
21
- const critical = issues.filter(i => i.level === "critical");
22
- const high = issues.filter(i => i.level === "high");
23
- if (critical.length > 0) {
24
- p.log.error(`Security scan blocked ${chalk.bold(skillName)}:`);
25
- for (const issue of critical) {
26
- p.log.error(` [CRIT] ${issue.category}: ${issue.detail} (line ${issue.line})`);
27
- }
28
- for (const issue of high) {
29
- p.log.warn(` [HIGH] ${issue.category}: ${issue.detail} (line ${issue.line})`);
30
- }
31
- if (!force) {
32
- p.log.info(chalk.dim("Use --force to install anyway (not recommended)."));
33
- return false;
34
- }
35
- p.log.warn("Installing despite security issues (--force).");
36
- }
37
- else if (high.length > 0) {
38
- p.log.warn(`Security warnings for ${chalk.bold(skillName)}:`);
39
- for (const issue of high) {
40
- p.log.warn(` [HIGH] ${issue.category}: ${issue.detail} (line ${issue.line})`);
41
- }
42
- }
43
- return true;
44
- }
9
+ import { installOneCore, sizeWarning, canInstall, detectProviderChange } from "../utils/install-core.js";
45
10
  export async function installCommand(skillNames, opts) {
46
11
  if (opts.json) {
47
12
  return installJson(skillNames, opts);
@@ -62,16 +27,16 @@ export async function installCommand(skillNames, opts) {
62
27
  process.exit(1);
63
28
  }
64
29
  if (opts.all) {
65
- await installAllInteractive(providers, opts.dryRun, opts.force);
30
+ await installAllInteractive(providers, opts.dryRun, opts.force, opts.noCheck);
66
31
  }
67
32
  else if (skillNames.length === 1) {
68
- await installOneInteractive(skillNames[0], providers[0], opts.dryRun, opts.force);
33
+ await installOneInteractive(skillNames[0], providers[0], opts.dryRun, opts.force, opts.noCheck);
69
34
  }
70
35
  else {
71
- await installMultipleInteractive(skillNames, providers[0], opts.dryRun, opts.force);
36
+ await installBatchInteractive(skillNames, providers[0], opts.dryRun, opts.force, opts.noCheck);
72
37
  }
73
38
  }
74
- async function installOneInteractive(skillName, provider, dryRun, force) {
39
+ async function installOneInteractive(skillName, provider, dryRun, force, noCheck) {
75
40
  p.intro(chalk.bold("Install skill"));
76
41
  try {
77
42
  validateSlug(skillName, "skill name");
@@ -80,20 +45,20 @@ async function installOneInteractive(skillName, provider, dryRun, force) {
80
45
  p.cancel(err instanceof Error ? err.message : "Invalid skill name");
81
46
  process.exit(1);
82
47
  }
83
- if (isSkillInstalled(skillName)) {
48
+ const check = canInstall(skillName, force);
49
+ if (!check.proceed) {
84
50
  if (dryRun) {
85
51
  p.log.info(`${skillName} is already installed.`);
86
52
  p.outro("Dry run complete.");
87
53
  return;
88
54
  }
89
- if (!force) {
90
- p.cancel(`${skillName} is already installed. Use --force to reinstall.`);
91
- process.exit(0);
92
- }
93
- const existingMeta = readSkillMeta(skillName);
94
- if (existingMeta?.source && existingMeta.source !== provider.name) {
95
- p.log.warn(`Overwriting ${skillName} (was from ${existingMeta.source}, now from ${provider.name})`);
96
- }
55
+ p.cancel(check.reason);
56
+ process.exit(0);
57
+ }
58
+ if (isSkillInstalled(skillName) && force) {
59
+ const change = detectProviderChange(skillName, provider.name);
60
+ if (change)
61
+ p.log.warn(change);
97
62
  p.log.warn(`${skillName} is already installed. Reinstalling...`);
98
63
  }
99
64
  if (dryRun) {
@@ -102,40 +67,38 @@ async function installOneInteractive(skillName, provider, dryRun, force) {
102
67
  return;
103
68
  }
104
69
  const spin = p.spinner();
105
- spin.start(`Fetching ${chalk.bold(skillName)} from ${provider.name}...`);
70
+ spin.start(`Installing ${chalk.bold(skillName)} from ${provider.name}...`);
106
71
  try {
107
- const files = await provider.fetch(skillName);
108
- spin.stop("Fetched.");
109
- if (!preInstallScan(skillName, files, force)) {
72
+ const result = await installOneCore(skillName, provider, { force, noCheck });
73
+ if (!result.success) {
74
+ spin.stop("Blocked.");
75
+ if (result.scanBlocked) {
76
+ p.log.error(`Security scan blocked ${chalk.bold(skillName)}`);
77
+ p.log.info(chalk.dim("Use --force to install anyway (not recommended)."));
78
+ }
79
+ else if (result.conflictBlocked) {
80
+ p.log.error(`Conflict detected for ${chalk.bold(skillName)}`);
81
+ p.log.info("Use --force to install anyway or --no-check to skip conflict detection.");
82
+ }
110
83
  process.exit(1);
111
84
  }
112
- const spin2 = p.spinner();
113
- spin2.start(`Installing ${chalk.bold(skillName)}...`);
114
- const dir = installSkill(skillName, files);
115
- const remote = await provider.info(skillName);
116
- writeSkillMeta(skillName, {
117
- version: remote?.version ?? "0.0.0",
118
- installedAt: new Date().toISOString(),
119
- source: provider.name,
120
- description: remote?.description,
121
- fileCount: files.length,
122
- sizeBytes: files.reduce((s, f) => s + f.content.length, 0),
123
- });
124
- const sizeKB = files.reduce((s, f) => s + f.content.length, 0) / 1024;
125
- spin2.stop(`Installed ${chalk.bold(skillName)} (${files.length} files, ${sizeKB.toFixed(1)} KB)`);
126
- if (sizeKB > 50) {
127
- p.log.warn(`Large skill (${sizeKB.toFixed(0)} KB, ~${Math.round(sizeKB * 256)} tokens). May use significant context.`);
85
+ spin.stop(`Installed ${chalk.bold(skillName)} (${result.files?.length ?? 0} files, ${result.sizeKB?.toFixed(1) ?? "0"} KB)`);
86
+ if (result.conflictWarnings?.length) {
87
+ for (const w of result.conflictWarnings)
88
+ p.log.warn(` [WARN] ${w}`);
128
89
  }
129
- p.log.info(`Location: ${dir}`);
90
+ const warn = sizeWarning(result.sizeKB ?? 0);
91
+ if (warn)
92
+ p.log.warn(warn);
130
93
  p.outro(`Next: ${chalk.cyan("arcana validate " + skillName)}`);
131
94
  }
132
95
  catch (err) {
133
- p.log.error(`Failed to install ${skillName}`);
96
+ spin.stop(`Failed to install ${skillName}`);
134
97
  printErrorWithHint(err, true);
135
98
  process.exit(1);
136
99
  }
137
100
  }
138
- async function installMultipleInteractive(skillNames, provider, dryRun, force) {
101
+ async function installBatchInteractive(skillNames, provider, dryRun, force, noCheck) {
139
102
  p.intro(chalk.bold(`Install ${skillNames.length} skills`));
140
103
  for (const name of skillNames) {
141
104
  try {
@@ -149,136 +112,112 @@ async function installMultipleInteractive(skillNames, provider, dryRun, force) {
149
112
  if (dryRun) {
150
113
  const wouldInstall = [];
151
114
  const alreadyInstalled = [];
152
- for (const skillName of skillNames) {
153
- if (isSkillInstalled(skillName) && !force) {
154
- alreadyInstalled.push(skillName);
155
- }
156
- else {
157
- wouldInstall.push(skillName);
158
- }
115
+ for (const name of skillNames) {
116
+ if (isSkillInstalled(name) && !force)
117
+ alreadyInstalled.push(name);
118
+ else
119
+ wouldInstall.push(name);
159
120
  }
160
- if (wouldInstall.length > 0) {
121
+ if (wouldInstall.length > 0)
161
122
  p.log.info(`Would install: ${wouldInstall.join(", ")}`);
162
- }
163
- if (alreadyInstalled.length > 0) {
123
+ if (alreadyInstalled.length > 0)
164
124
  p.log.info(`Already installed: ${alreadyInstalled.join(", ")}`);
165
- }
166
125
  p.outro("Dry run complete.");
167
126
  return;
168
127
  }
169
128
  const spin = p.spinner();
170
129
  spin.start(`Processing ${skillNames.length} skills...`);
171
- const installedList = [];
172
- const skippedList = [];
173
- const failedList = [];
130
+ const installed = [];
131
+ const skipped = [];
132
+ const failed = [];
174
133
  for (let i = 0; i < skillNames.length; i++) {
175
- const skillName = skillNames[i];
176
- if (isSkillInstalled(skillName) && !force) {
177
- skippedList.push(skillName);
134
+ const name = skillNames[i];
135
+ if (isSkillInstalled(name) && !force) {
136
+ skipped.push(name);
178
137
  continue;
179
138
  }
180
- spin.message(`Installing ${chalk.bold(skillName)} (${i + 1}/${skillNames.length}) from ${provider.name}...`);
139
+ spin.message(`Installing ${chalk.bold(name)} (${i + 1}/${skillNames.length}) from ${provider.name}...`);
181
140
  try {
182
- const files = await provider.fetch(skillName);
183
- if (!preInstallScan(skillName, files, force)) {
184
- failedList.push(skillName);
185
- continue;
141
+ const result = await installOneCore(name, provider, { force, noCheck });
142
+ if (result.success) {
143
+ installed.push(name);
144
+ }
145
+ else {
146
+ failed.push(name);
186
147
  }
187
- installSkill(skillName, files);
188
- const remote = await provider.info(skillName);
189
- writeSkillMeta(skillName, {
190
- version: remote?.version ?? "0.0.0",
191
- installedAt: new Date().toISOString(),
192
- source: provider.name,
193
- description: remote?.description,
194
- fileCount: files.length,
195
- sizeBytes: files.reduce((s, f) => s + f.content.length, 0),
196
- });
197
- installedList.push(skillName);
198
148
  }
199
149
  catch (err) {
200
- failedList.push(skillName);
150
+ failed.push(name);
201
151
  if (err instanceof Error)
202
- p.log.warn(`Failed to install ${skillName}: ${err.message}`);
152
+ p.log.warn(`Failed to install ${name}: ${err.message}`);
203
153
  }
204
154
  }
205
- spin.stop(`Done`);
206
- p.log.info(`${installedList.length} installed${skippedList.length > 0 ? `, ${skippedList.length} skipped (already installed)` : ""}${failedList.length > 0 ? `, ${failedList.length} failed` : ""}`);
155
+ spin.stop("Done");
156
+ p.log.info(`${installed.length} installed${skipped.length > 0 ? `, ${skipped.length} skipped (already installed)` : ""}${failed.length > 0 ? `, ${failed.length} failed` : ""}`);
207
157
  p.outro(`Next: ${chalk.cyan("arcana doctor")}`);
208
- if (failedList.length > 0)
158
+ if (failed.length > 0)
209
159
  process.exit(1);
210
160
  }
211
- async function installAllInteractive(providers, dryRun, force) {
161
+ async function installAllInteractive(providers, dryRun, force, noCheck) {
212
162
  p.intro(chalk.bold("Install all skills"));
213
163
  const spin = p.spinner();
214
164
  spin.start("Fetching skill list...");
215
165
  if (dryRun) {
216
166
  let total = 0;
217
- for (const provider of providers) {
167
+ for (const prov of providers) {
218
168
  try {
219
- const skills = await provider.list();
169
+ const skills = await prov.list();
220
170
  total += skills.length;
221
171
  }
222
172
  catch (err) {
223
173
  if (err instanceof Error)
224
- p.log.warn(`Failed to list ${provider.name}: ${err.message}`);
174
+ p.log.warn(`Failed to list ${prov.name}: ${err.message}`);
225
175
  }
226
176
  }
227
177
  spin.stop(`Would install ${total} skills`);
228
178
  p.outro("Dry run complete.");
229
179
  return;
230
180
  }
231
- const installedList = [];
232
- const skippedList = [];
233
- const failedList = [];
234
- for (const provider of providers) {
181
+ const installed = [];
182
+ const skipped = [];
183
+ const failed = [];
184
+ for (const prov of providers) {
235
185
  let skills;
236
186
  try {
237
- skills = await provider.list();
187
+ skills = await prov.list();
238
188
  }
239
189
  catch (err) {
240
190
  if (err instanceof Error)
241
- p.log.warn(`Failed to list ${provider.name}: ${err.message}`);
191
+ p.log.warn(`Failed to list ${prov.name}: ${err.message}`);
242
192
  continue;
243
193
  }
244
- const total = skills.length;
245
- for (let i = 0; i < total; i++) {
194
+ for (let i = 0; i < skills.length; i++) {
246
195
  const skill = skills[i];
247
196
  if (isSkillInstalled(skill.name) && !force) {
248
- skippedList.push(skill.name);
197
+ skipped.push(skill.name);
249
198
  continue;
250
199
  }
200
+ spin.message(`Installing ${chalk.bold(skill.name)} (${i + 1}/${skills.length}) from ${prov.name}...`);
251
201
  try {
252
- spin.message(`Installing ${chalk.bold(skill.name)} (${i + 1}/${total}) from ${provider.name}...`);
253
- const files = await provider.fetch(skill.name);
254
- if (!preInstallScan(skill.name, files, force)) {
255
- failedList.push(skill.name);
256
- continue;
257
- }
258
- installSkill(skill.name, files);
259
- writeSkillMeta(skill.name, {
260
- version: skill.version,
261
- installedAt: new Date().toISOString(),
262
- source: provider.name,
263
- description: skill.description,
264
- fileCount: files.length,
265
- sizeBytes: files.reduce((s, f) => s + f.content.length, 0),
266
- });
267
- installedList.push(skill.name);
202
+ const result = await installOneCore(skill.name, prov, { force, noCheck });
203
+ if (result.success)
204
+ installed.push(skill.name);
205
+ else
206
+ failed.push(skill.name);
268
207
  }
269
208
  catch (err) {
270
- failedList.push(skill.name);
209
+ failed.push(skill.name);
271
210
  if (err instanceof Error)
272
211
  p.log.warn(`Failed to install ${skill.name}: ${err.message}`);
273
212
  }
274
213
  }
275
214
  }
276
- spin.stop(`Installed ${installedList.length} skills${failedList.length > 0 ? `, ${failedList.length} failed` : ""}`);
277
- if (skippedList.length > 0) {
278
- p.log.info(`Skipped ${skippedList.length} already installed${force ? "" : " (use --force to reinstall)"}`);
215
+ spin.stop(`Installed ${installed.length} skills${failed.length > 0 ? `, ${failed.length} failed` : ""}`);
216
+ if (skipped.length > 0) {
217
+ p.log.info(`Skipped ${skipped.length} already installed${force ? "" : " (use --force to reinstall)"}`);
279
218
  }
280
219
  p.outro(`Next: ${chalk.cyan("arcana doctor")}`);
281
- if (failedList.length > 0)
220
+ if (failed.length > 0)
282
221
  process.exit(1);
283
222
  }
284
223
  async function installJson(skillNames, opts) {
@@ -288,17 +227,21 @@ async function installJson(skillNames, opts) {
288
227
  }
289
228
  const providerName = opts.provider ?? loadConfig().defaultProvider;
290
229
  const providers = opts.all ? getProviders() : [getProvider(providerName)];
230
+ const installed = [];
231
+ const skipped = [];
232
+ const failed = [];
233
+ const failedErrors = {};
291
234
  if (opts.all) {
292
235
  if (opts.dryRun) {
293
236
  const wouldInstall = [];
294
237
  const errors = [];
295
- for (const provider of providers) {
238
+ for (const prov of providers) {
296
239
  try {
297
- const skills = await provider.list();
298
- wouldInstall.push(...skills.map(s => s.name));
240
+ const skills = await prov.list();
241
+ wouldInstall.push(...skills.map((s) => s.name));
299
242
  }
300
243
  catch (err) {
301
- errors.push(`Failed to list ${provider.name}: ${err instanceof Error ? err.message : "unknown error"}`);
244
+ errors.push(`Failed to list ${prov.name}: ${err instanceof Error ? err.message : "unknown error"}`);
302
245
  }
303
246
  }
304
247
  const result = { dryRun: true, wouldInstall };
@@ -307,57 +250,42 @@ async function installJson(skillNames, opts) {
307
250
  console.log(JSON.stringify(result));
308
251
  return;
309
252
  }
310
- const installedList = [];
311
- const skippedList = [];
312
- const failedList = [];
313
- const failedErrors = {};
314
253
  const errors = [];
315
- for (const provider of providers) {
254
+ for (const prov of providers) {
316
255
  let skills;
317
256
  try {
318
- skills = await provider.list();
257
+ skills = await prov.list();
319
258
  }
320
259
  catch (err) {
321
- errors.push(`Failed to list ${provider.name}: ${err instanceof Error ? err.message : "unknown error"}`);
260
+ errors.push(`Failed to list ${prov.name}: ${err instanceof Error ? err.message : "unknown error"}`);
322
261
  continue;
323
262
  }
324
263
  for (const skill of skills) {
325
264
  if (isSkillInstalled(skill.name) && !opts.force) {
326
- skippedList.push(skill.name);
265
+ skipped.push(skill.name);
327
266
  continue;
328
267
  }
329
268
  try {
330
- const files = await provider.fetch(skill.name);
331
- if (!preInstallScan(skill.name, files, opts.force)) {
332
- failedList.push(skill.name);
333
- failedErrors[skill.name] = "Blocked by security scan";
334
- continue;
269
+ const result = await installOneCore(skill.name, prov, { force: opts.force, noCheck: opts.noCheck });
270
+ if (result.success)
271
+ installed.push(skill.name);
272
+ else {
273
+ failed.push(skill.name);
274
+ failedErrors[skill.name] = result.error ?? "Install failed";
335
275
  }
336
- installSkill(skill.name, files);
337
- writeSkillMeta(skill.name, {
338
- version: skill.version,
339
- installedAt: new Date().toISOString(),
340
- source: provider.name,
341
- description: skill.description,
342
- fileCount: files.length,
343
- sizeBytes: files.reduce((s, f) => s + f.content.length, 0),
344
- });
345
- installedList.push(skill.name);
346
276
  }
347
277
  catch (err) {
348
- failedList.push(skill.name);
278
+ failed.push(skill.name);
349
279
  failedErrors[skill.name] = err instanceof Error ? err.message : "unknown";
350
280
  }
351
281
  }
352
282
  }
353
- const result = { installed: installedList, skipped: skippedList, failed: failedList };
283
+ const result = { installed, skipped, failed };
354
284
  if (errors.length > 0)
355
285
  result.errors = errors;
356
286
  if (Object.keys(failedErrors).length > 0)
357
287
  result.failedErrors = failedErrors;
358
288
  console.log(JSON.stringify(result));
359
- if (failedList.length > 0)
360
- process.exit(1);
361
289
  }
362
290
  else {
363
291
  const provider = providers[0];
@@ -365,40 +293,25 @@ async function installJson(skillNames, opts) {
365
293
  console.log(JSON.stringify({ dryRun: true, wouldInstall: skillNames }));
366
294
  return;
367
295
  }
368
- const installedList = [];
369
- const skippedList = [];
370
- const failedList = [];
371
- for (const skillName of skillNames) {
296
+ for (const name of skillNames) {
372
297
  try {
373
- validateSlug(skillName, "skill name");
374
- if (isSkillInstalled(skillName) && !opts.force) {
375
- skippedList.push(skillName);
376
- continue;
377
- }
378
- const files = await provider.fetch(skillName);
379
- if (!preInstallScan(skillName, files, opts.force)) {
380
- failedList.push(skillName);
298
+ validateSlug(name, "skill name");
299
+ if (isSkillInstalled(name) && !opts.force) {
300
+ skipped.push(name);
381
301
  continue;
382
302
  }
383
- installSkill(skillName, files);
384
- const remote = await provider.info(skillName);
385
- writeSkillMeta(skillName, {
386
- version: remote?.version ?? "0.0.0",
387
- installedAt: new Date().toISOString(),
388
- source: provider.name,
389
- description: remote?.description,
390
- fileCount: files.length,
391
- sizeBytes: files.reduce((s, f) => s + f.content.length, 0),
392
- });
393
- installedList.push(skillName);
303
+ const result = await installOneCore(name, provider, { force: opts.force, noCheck: opts.noCheck });
304
+ if (result.success)
305
+ installed.push(name);
306
+ else
307
+ failed.push(name);
394
308
  }
395
- catch (err) {
396
- failedList.push(skillName);
309
+ catch {
310
+ failed.push(name);
397
311
  }
398
312
  }
399
- console.log(JSON.stringify({ installed: installedList, skipped: skippedList, failed: failedList }));
400
- if (failedList.length > 0)
401
- process.exit(1);
313
+ console.log(JSON.stringify({ installed, skipped, failed }));
402
314
  }
315
+ if (failed.length > 0)
316
+ process.exit(1);
403
317
  }
404
- //# sourceMappingURL=install.js.map
@@ -5,4 +5,3 @@ export declare function listCommand(opts: {
5
5
  installed?: boolean;
6
6
  json?: boolean;
7
7
  }): Promise<void>;
8
- //# sourceMappingURL=list.d.ts.map
@@ -29,6 +29,8 @@ export async function listCommand(opts) {
29
29
  description: skill.description,
30
30
  source: skill.source,
31
31
  installed: isSkillInstalled(skill.name),
32
+ verified: skill.verified,
33
+ tags: skill.tags,
32
34
  });
33
35
  }
34
36
  }
@@ -44,9 +46,11 @@ export async function listCommand(opts) {
44
46
  console.log(ui.bold(` ${skills.length} skills available:`));
45
47
  console.log();
46
48
  const rows = skills.map((skill) => [
47
- ui.bold(skill.name),
49
+ ui.bold(skill.name) + (skill.verified ? " " + ui.success("[V]") : ""),
48
50
  ui.dim(`v${skill.version}`),
49
- skill.description.slice(0, DESC_TRUNCATE_LENGTH) + (skill.description.length > DESC_TRUNCATE_LENGTH ? "..." : ""),
51
+ skill.description.slice(0, DESC_TRUNCATE_LENGTH) +
52
+ (skill.description.length > DESC_TRUNCATE_LENGTH ? "..." : ""),
53
+ skill.tags?.slice(0, 3).join(", ") ?? "",
50
54
  providers.length > 1 ? ui.dim(skill.source) : "",
51
55
  skill.installed ? ui.success("[installed]") : "",
52
56
  ]);
@@ -72,7 +76,12 @@ function listInstalled(json) {
72
76
  if (json) {
73
77
  const skills = dirs.map((name) => {
74
78
  const meta = readSkillMeta(name);
75
- return { name, version: meta?.version ?? "unknown", source: meta?.source ?? "local", installedAt: meta?.installedAt ?? null };
79
+ return {
80
+ name,
81
+ version: meta?.version ?? "unknown",
82
+ source: meta?.source ?? "local",
83
+ installedAt: meta?.installedAt ?? null,
84
+ };
76
85
  });
77
86
  console.log(JSON.stringify({ skills }, null, 2));
78
87
  return;
@@ -97,4 +106,3 @@ function listInstalled(json) {
97
106
  table(rows);
98
107
  console.log();
99
108
  }
100
- //# sourceMappingURL=list.js.map
@@ -0,0 +1,4 @@
1
+ export declare function lockCommand(opts: {
2
+ ci?: boolean;
3
+ json?: boolean;
4
+ }): Promise<void>;