@sporesec/arcana 2.4.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +124 -9
- package/dist/command-registry.d.ts +10 -0
- package/dist/command-registry.js +65 -0
- package/dist/commands/audit.d.ts +2 -3
- package/dist/commands/audit.js +47 -14
- package/dist/commands/benchmark.d.ts +4 -0
- package/dist/commands/benchmark.js +178 -0
- package/dist/commands/clean.d.ts +0 -1
- package/dist/commands/clean.js +19 -8
- package/dist/commands/compact.d.ts +2 -1
- package/dist/commands/compact.js +74 -14
- package/dist/commands/completions.d.ts +3 -0
- package/dist/commands/completions.js +104 -0
- package/dist/commands/config.d.ts +0 -1
- package/dist/commands/config.js +15 -6
- package/dist/commands/create.d.ts +0 -1
- package/dist/commands/create.js +1 -1
- package/dist/commands/diff.d.ts +4 -0
- package/dist/commands/diff.js +166 -0
- package/dist/commands/doctor.d.ts +0 -1
- package/dist/commands/doctor.js +64 -23
- package/dist/commands/export-cmd.d.ts +4 -0
- package/dist/commands/export-cmd.js +66 -0
- package/dist/commands/import-cmd.d.ts +4 -0
- package/dist/commands/import-cmd.js +131 -0
- package/dist/commands/info.d.ts +0 -1
- package/dist/commands/info.js +29 -4
- package/dist/commands/init.d.ts +0 -1
- package/dist/commands/init.js +26 -33
- package/dist/commands/install.d.ts +1 -1
- package/dist/commands/install.js +118 -205
- package/dist/commands/list.d.ts +0 -1
- package/dist/commands/list.js +12 -4
- package/dist/commands/lock.d.ts +4 -0
- package/dist/commands/lock.js +171 -0
- package/dist/commands/optimize.d.ts +0 -1
- package/dist/commands/optimize.js +111 -20
- package/dist/commands/outdated.d.ts +4 -0
- package/dist/commands/outdated.js +159 -0
- package/dist/commands/profile.d.ts +3 -0
- package/dist/commands/profile.js +274 -0
- package/dist/commands/providers.d.ts +0 -1
- package/dist/commands/providers.js +1 -4
- package/dist/commands/recommend.d.ts +5 -0
- package/dist/commands/recommend.js +96 -0
- package/dist/commands/scan.d.ts +0 -1
- package/dist/commands/scan.js +13 -7
- package/dist/commands/search.d.ts +2 -1
- package/dist/commands/search.js +32 -9
- package/dist/commands/stats.d.ts +0 -1
- package/dist/commands/stats.js +24 -20
- package/dist/commands/team.d.ts +3 -0
- package/dist/commands/team.js +291 -0
- package/dist/commands/uninstall.d.ts +0 -1
- package/dist/commands/uninstall.js +18 -4
- package/dist/commands/update.d.ts +0 -1
- package/dist/commands/update.js +155 -155
- package/dist/commands/validate.d.ts +3 -1
- package/dist/commands/validate.js +90 -15
- package/dist/commands/verify.d.ts +4 -0
- package/dist/commands/verify.js +116 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.js +13 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/interactive/browse.d.ts +4 -0
- package/dist/interactive/browse.js +103 -0
- package/dist/interactive/categories.d.ts +4 -0
- package/dist/interactive/categories.js +87 -0
- package/dist/interactive/health.d.ts +1 -0
- package/dist/interactive/health.js +57 -0
- package/dist/interactive/helpers.d.ts +11 -0
- package/dist/interactive/helpers.js +66 -0
- package/dist/interactive/index.d.ts +1 -0
- package/dist/interactive/index.js +1 -0
- package/dist/interactive/manage.d.ts +2 -0
- package/dist/interactive/manage.js +187 -0
- package/dist/interactive/menu.d.ts +1 -0
- package/dist/interactive/menu.js +107 -0
- package/dist/interactive/search.d.ts +2 -0
- package/dist/interactive/search.js +66 -0
- package/dist/interactive/setup.d.ts +2 -0
- package/dist/interactive/setup.js +48 -0
- package/dist/interactive/skill-detail.d.ts +5 -0
- package/dist/interactive/skill-detail.js +126 -0
- package/dist/interactive.d.ts +0 -1
- package/dist/interactive.js +89 -66
- package/dist/providers/arcana.d.ts +0 -1
- package/dist/providers/arcana.js +0 -1
- package/dist/providers/base.d.ts +0 -1
- package/dist/providers/base.js +0 -1
- package/dist/providers/github.d.ts +0 -1
- package/dist/providers/github.js +8 -3
- package/dist/registry.d.ts +0 -1
- package/dist/registry.js +1 -4
- package/dist/types.d.ts +10 -1
- package/dist/types.js +0 -1
- package/dist/utils/atomic.d.ts +0 -1
- package/dist/utils/atomic.js +3 -2
- package/dist/utils/cache.d.ts +0 -1
- package/dist/utils/cache.js +3 -2
- package/dist/utils/config.d.ts +2 -1
- package/dist/utils/config.js +30 -5
- package/dist/utils/conflict-check.d.ts +8 -0
- package/dist/utils/conflict-check.js +72 -0
- package/dist/utils/errors.d.ts +0 -1
- package/dist/utils/errors.js +0 -1
- package/dist/utils/frontmatter.d.ts +0 -1
- package/dist/utils/frontmatter.js +44 -14
- package/dist/utils/fs.d.ts +0 -1
- package/dist/utils/fs.js +30 -11
- package/dist/utils/help.d.ts +0 -1
- package/dist/utils/help.js +15 -28
- package/dist/utils/history.d.ts +0 -1
- package/dist/utils/history.js +0 -1
- package/dist/utils/http.d.ts +0 -1
- package/dist/utils/http.js +14 -5
- package/dist/utils/install-core.d.ts +48 -0
- package/dist/utils/install-core.js +108 -0
- package/dist/utils/integrity.d.ts +17 -0
- package/dist/utils/integrity.js +84 -0
- package/dist/utils/parallel.d.ts +0 -1
- package/dist/utils/parallel.js +0 -1
- package/dist/utils/project-context.d.ts +19 -0
- package/dist/utils/project-context.js +283 -0
- package/dist/utils/quality.d.ts +27 -0
- package/dist/utils/quality.js +174 -0
- package/dist/utils/scanner.d.ts +0 -1
- package/dist/utils/scanner.js +138 -10
- package/dist/utils/scoring.d.ts +10 -0
- package/dist/utils/scoring.js +84 -0
- package/dist/utils/ui.d.ts +0 -1
- package/dist/utils/ui.js +11 -4
- package/dist/utils/validate.d.ts +0 -1
- package/dist/utils/validate.js +4 -1
- package/package.json +74 -62
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/audit.d.ts.map +0 -1
- package/dist/commands/audit.js.map +0 -1
- package/dist/commands/audit.test.d.ts +0 -2
- package/dist/commands/audit.test.d.ts.map +0 -1
- package/dist/commands/audit.test.js +0 -217
- package/dist/commands/audit.test.js.map +0 -1
- package/dist/commands/clean.d.ts.map +0 -1
- package/dist/commands/clean.js.map +0 -1
- package/dist/commands/compact.d.ts.map +0 -1
- package/dist/commands/compact.js.map +0 -1
- package/dist/commands/config.d.ts.map +0 -1
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/create.d.ts.map +0 -1
- package/dist/commands/create.js.map +0 -1
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/info.d.ts.map +0 -1
- package/dist/commands/info.js.map +0 -1
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/install.d.ts.map +0 -1
- package/dist/commands/install.js.map +0 -1
- package/dist/commands/list.d.ts.map +0 -1
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/optimize.d.ts.map +0 -1
- package/dist/commands/optimize.js.map +0 -1
- package/dist/commands/providers.d.ts.map +0 -1
- package/dist/commands/providers.js.map +0 -1
- package/dist/commands/scan.d.ts.map +0 -1
- package/dist/commands/scan.js.map +0 -1
- package/dist/commands/search.d.ts.map +0 -1
- package/dist/commands/search.js.map +0 -1
- package/dist/commands/stats.d.ts.map +0 -1
- package/dist/commands/stats.js.map +0 -1
- package/dist/commands/uninstall.d.ts.map +0 -1
- package/dist/commands/uninstall.js.map +0 -1
- package/dist/commands/update.d.ts.map +0 -1
- package/dist/commands/update.js.map +0 -1
- package/dist/commands/validate.d.ts.map +0 -1
- package/dist/commands/validate.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/interactive.d.ts.map +0 -1
- package/dist/interactive.js.map +0 -1
- package/dist/providers/arcana.d.ts.map +0 -1
- package/dist/providers/arcana.js.map +0 -1
- package/dist/providers/base.d.ts.map +0 -1
- package/dist/providers/base.js.map +0 -1
- package/dist/providers/github.d.ts.map +0 -1
- package/dist/providers/github.js.map +0 -1
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils/atomic.d.ts.map +0 -1
- package/dist/utils/atomic.js.map +0 -1
- package/dist/utils/atomic.test.d.ts +0 -2
- package/dist/utils/atomic.test.d.ts.map +0 -1
- package/dist/utils/atomic.test.js +0 -31
- package/dist/utils/atomic.test.js.map +0 -1
- package/dist/utils/cache.d.ts.map +0 -1
- package/dist/utils/cache.js.map +0 -1
- package/dist/utils/config.d.ts.map +0 -1
- package/dist/utils/config.js.map +0 -1
- package/dist/utils/config.test.d.ts +0 -2
- package/dist/utils/config.test.d.ts.map +0 -1
- package/dist/utils/config.test.js +0 -38
- package/dist/utils/config.test.js.map +0 -1
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/frontmatter.d.ts.map +0 -1
- package/dist/utils/frontmatter.js.map +0 -1
- package/dist/utils/frontmatter.test.d.ts +0 -2
- package/dist/utils/frontmatter.test.d.ts.map +0 -1
- package/dist/utils/frontmatter.test.js +0 -152
- package/dist/utils/frontmatter.test.js.map +0 -1
- package/dist/utils/fs.d.ts.map +0 -1
- package/dist/utils/fs.js.map +0 -1
- package/dist/utils/fs.test.d.ts +0 -2
- package/dist/utils/fs.test.d.ts.map +0 -1
- package/dist/utils/fs.test.js +0 -145
- package/dist/utils/fs.test.js.map +0 -1
- package/dist/utils/help.d.ts.map +0 -1
- package/dist/utils/help.js.map +0 -1
- package/dist/utils/help.test.d.ts +0 -2
- package/dist/utils/help.test.d.ts.map +0 -1
- package/dist/utils/help.test.js +0 -66
- package/dist/utils/help.test.js.map +0 -1
- package/dist/utils/history.d.ts.map +0 -1
- package/dist/utils/history.js.map +0 -1
- package/dist/utils/http.d.ts.map +0 -1
- package/dist/utils/http.js.map +0 -1
- package/dist/utils/http.test.d.ts +0 -2
- package/dist/utils/http.test.d.ts.map +0 -1
- package/dist/utils/http.test.js +0 -55
- package/dist/utils/http.test.js.map +0 -1
- package/dist/utils/parallel.d.ts.map +0 -1
- package/dist/utils/parallel.js.map +0 -1
- package/dist/utils/scanner.d.ts.map +0 -1
- package/dist/utils/scanner.js.map +0 -1
- package/dist/utils/ui.d.ts.map +0 -1
- package/dist/utils/ui.js.map +0 -1
- package/dist/utils/ui.test.d.ts +0 -2
- package/dist/utils/ui.test.d.ts.map +0 -1
- package/dist/utils/ui.test.js +0 -31
- package/dist/utils/ui.test.js.map +0 -1
- package/dist/utils/validate.d.ts.map +0 -1
- package/dist/utils/validate.js.map +0 -1
package/dist/commands/update.js
CHANGED
|
@@ -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
|
|
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
|
|
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({
|
|
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({
|
|
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
|
|
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({
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
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
|
-
|
|
258
|
-
//
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
|
277
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
-
|
|
285
|
-
const meta = readSkillMeta(
|
|
278
|
+
}
|
|
279
|
+
const meta = readSkillMeta(name);
|
|
286
280
|
if (!isNewer(remote.version, meta?.version)) {
|
|
287
|
-
upToDateList.push(
|
|
288
|
-
|
|
281
|
+
upToDateList.push(name);
|
|
282
|
+
continue;
|
|
289
283
|
}
|
|
290
284
|
if (dryRun) {
|
|
291
|
-
dryRunUpdates.push({ name
|
|
292
|
-
|
|
285
|
+
dryRunUpdates.push({ name, from: meta?.version ?? "unknown", to: remote.version });
|
|
286
|
+
continue;
|
|
293
287
|
}
|
|
294
|
-
s.text = `Updating ${ui.bold(
|
|
295
|
-
|
|
296
|
-
|
|
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(
|
|
294
|
+
failedList.push(name);
|
|
310
295
|
if (err instanceof Error && !json)
|
|
311
|
-
console.error(ui.dim(` Failed to update ${
|
|
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({
|
|
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
|
-
|
|
328
|
-
|
|
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({
|
|
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
|
-
|
|
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
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
3
|
import { getInstallDir } from "../utils/fs.js";
|
|
4
4
|
import { validateSkillDir, fixSkillFrontmatter } from "../utils/frontmatter.js";
|
|
5
5
|
import { atomicWriteSync } from "../utils/atomic.js";
|
|
@@ -8,8 +8,8 @@ import { scanSkillContent } from "../utils/scanner.js";
|
|
|
8
8
|
export async function validateCommand(skill, opts) {
|
|
9
9
|
if (!opts.json)
|
|
10
10
|
banner();
|
|
11
|
-
const
|
|
12
|
-
if (!existsSync(
|
|
11
|
+
const baseDir = opts.source ? resolve(opts.source) : getInstallDir();
|
|
12
|
+
if (!existsSync(baseDir)) {
|
|
13
13
|
if (opts.json) {
|
|
14
14
|
console.log(JSON.stringify({ results: [] }));
|
|
15
15
|
}
|
|
@@ -21,7 +21,14 @@ export async function validateCommand(skill, opts) {
|
|
|
21
21
|
}
|
|
22
22
|
let skills;
|
|
23
23
|
if (opts.all) {
|
|
24
|
-
skills = readdirSync(
|
|
24
|
+
skills = readdirSync(baseDir).filter((d) => {
|
|
25
|
+
try {
|
|
26
|
+
return statSync(join(baseDir, d)).isDirectory();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
25
32
|
}
|
|
26
33
|
else if (skill) {
|
|
27
34
|
skills = [skill];
|
|
@@ -40,7 +47,7 @@ export async function validateCommand(skill, opts) {
|
|
|
40
47
|
}
|
|
41
48
|
const results = [];
|
|
42
49
|
for (const name of skills) {
|
|
43
|
-
const skillDir = join(
|
|
50
|
+
const skillDir = join(baseDir, name);
|
|
44
51
|
if (!existsSync(skillDir)) {
|
|
45
52
|
results.push({ skill: name, valid: false, errors: ["Not installed"], warnings: [], infos: [] });
|
|
46
53
|
continue;
|
|
@@ -81,26 +88,84 @@ export async function validateCommand(skill, opts) {
|
|
|
81
88
|
}
|
|
82
89
|
}
|
|
83
90
|
}
|
|
84
|
-
catch {
|
|
91
|
+
catch {
|
|
92
|
+
/* skip if unreadable */
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Quality scoring (when --min-score is set)
|
|
96
|
+
const entry = { ...result };
|
|
97
|
+
if (opts.minScore !== undefined) {
|
|
98
|
+
const { auditSkill } = await import("./audit.js");
|
|
99
|
+
const audit = auditSkill(skillDir, name);
|
|
100
|
+
entry.qualityScore = audit.score;
|
|
101
|
+
entry.qualityRating = audit.rating;
|
|
102
|
+
if (audit.score < opts.minScore) {
|
|
103
|
+
entry.valid = false;
|
|
104
|
+
entry.errors.push(`Quality score ${audit.score} below minimum ${opts.minScore} (${audit.rating})`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
results.push(entry);
|
|
108
|
+
}
|
|
109
|
+
// Cross-validation (when --cross is set)
|
|
110
|
+
let crossIssues = [];
|
|
111
|
+
if (opts.cross) {
|
|
112
|
+
const { crossValidate } = await import("../utils/quality.js");
|
|
113
|
+
const marketplacePaths = opts.source
|
|
114
|
+
? [
|
|
115
|
+
resolve(opts.source, "..", ".claude-plugin", "marketplace.json"),
|
|
116
|
+
resolve(opts.source, ".claude-plugin", "marketplace.json"),
|
|
117
|
+
]
|
|
118
|
+
: [resolve(baseDir, "..", ".claude-plugin", "marketplace.json")];
|
|
119
|
+
const marketplacePath = marketplacePaths.find((p) => existsSync(p));
|
|
120
|
+
if (marketplacePath) {
|
|
121
|
+
crossIssues = crossValidate(baseDir, marketplacePath);
|
|
122
|
+
}
|
|
123
|
+
else if (!opts.json) {
|
|
124
|
+
console.log(ui.warn(" Could not find marketplace.json for cross-validation"));
|
|
85
125
|
}
|
|
86
|
-
results.push(result);
|
|
87
126
|
}
|
|
127
|
+
const hasErrors = results.some((r) => !r.valid);
|
|
128
|
+
const hasCrossErrors = crossIssues.some((i) => i.level === "error");
|
|
88
129
|
if (opts.json) {
|
|
89
|
-
|
|
90
|
-
|
|
130
|
+
const output = {
|
|
131
|
+
results: results.map((r) => ({
|
|
132
|
+
skill: r.skill,
|
|
133
|
+
valid: r.valid,
|
|
134
|
+
errors: r.errors,
|
|
135
|
+
warnings: r.warnings,
|
|
136
|
+
infos: r.infos,
|
|
137
|
+
fixed: r.fixed ?? false,
|
|
138
|
+
...(r.qualityScore !== undefined && { qualityScore: r.qualityScore, qualityRating: r.qualityRating }),
|
|
139
|
+
})),
|
|
140
|
+
};
|
|
141
|
+
if (opts.cross && crossIssues.length > 0) {
|
|
142
|
+
output.crossValidation = crossIssues;
|
|
143
|
+
}
|
|
144
|
+
const scores = results.filter((r) => r.qualityScore !== undefined).map((r) => r.qualityScore);
|
|
145
|
+
if (scores.length > 0) {
|
|
146
|
+
output.summary = {
|
|
147
|
+
total: results.length,
|
|
148
|
+
passed: results.filter((r) => r.valid).length,
|
|
149
|
+
failed: results.filter((r) => !r.valid).length,
|
|
150
|
+
averageScore: Math.round(scores.reduce((a, b) => a + b, 0) / scores.length),
|
|
151
|
+
belowThreshold: opts.minScore ? results.filter((r) => (r.qualityScore ?? 0) < opts.minScore).length : 0,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
console.log(JSON.stringify(output, null, 2));
|
|
155
|
+
if (hasErrors || hasCrossErrors)
|
|
91
156
|
process.exit(1);
|
|
92
157
|
return;
|
|
93
158
|
}
|
|
159
|
+
// Human-readable output
|
|
94
160
|
let passed = 0;
|
|
95
161
|
let warned = 0;
|
|
96
162
|
let failed = 0;
|
|
97
163
|
let fixed = 0;
|
|
98
164
|
for (const r of results) {
|
|
99
|
-
const icon = r.valid
|
|
100
|
-
? r.warnings.length > 0 ? ui.warn("[!!]") : ui.success("[OK]")
|
|
101
|
-
: ui.error("[XX]");
|
|
165
|
+
const icon = r.valid ? (r.warnings.length > 0 ? ui.warn("[!!]") : ui.success("[OK]")) : ui.error("[XX]");
|
|
102
166
|
const fixTag = r.fixed ? ui.cyan(" [fixed]") : "";
|
|
103
|
-
|
|
167
|
+
const scoreTag = r.qualityScore !== undefined ? ui.dim(` (${r.qualityScore}/100 ${r.qualityRating})`) : "";
|
|
168
|
+
console.log(` ${icon} ${ui.bold(r.skill)}${fixTag}${scoreTag}`);
|
|
104
169
|
for (const err of r.errors) {
|
|
105
170
|
console.log(ui.error(` Error: ${err}`));
|
|
106
171
|
}
|
|
@@ -122,6 +187,15 @@ export async function validateCommand(skill, opts) {
|
|
|
122
187
|
if (r.fixed)
|
|
123
188
|
fixed++;
|
|
124
189
|
}
|
|
190
|
+
// Cross-validation output
|
|
191
|
+
if (opts.cross && crossIssues.length > 0) {
|
|
192
|
+
console.log();
|
|
193
|
+
console.log(ui.bold(" Cross-validation:"));
|
|
194
|
+
for (const issue of crossIssues) {
|
|
195
|
+
const icon = issue.level === "error" ? ui.error("[XX]") : ui.warn("[!!]");
|
|
196
|
+
console.log(` ${icon} ${issue.skill}: ${issue.detail}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
125
199
|
console.log();
|
|
126
200
|
const parts = [];
|
|
127
201
|
if (passed > 0)
|
|
@@ -132,9 +206,10 @@ export async function validateCommand(skill, opts) {
|
|
|
132
206
|
parts.push(ui.error(`${failed} failed`));
|
|
133
207
|
if (fixed > 0)
|
|
134
208
|
parts.push(ui.cyan(`${fixed} fixed`));
|
|
209
|
+
if (hasCrossErrors)
|
|
210
|
+
parts.push(ui.error(`${crossIssues.filter((i) => i.level === "error").length} cross-validation errors`));
|
|
135
211
|
console.log(` ${parts.join(ui.dim(" | "))}`);
|
|
136
212
|
console.log();
|
|
137
|
-
if (failed > 0)
|
|
213
|
+
if (failed > 0 || hasCrossErrors)
|
|
138
214
|
process.exit(1);
|
|
139
215
|
}
|
|
140
|
-
//# sourceMappingURL=validate.js.map
|