@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/interactive.js
CHANGED
|
@@ -18,39 +18,74 @@ const AMBER = chalk.hex("#d4943a");
|
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
19
|
const SKILL_CATEGORIES = {
|
|
20
20
|
"Code Quality & Review": [
|
|
21
|
-
"code-reviewer",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
21
|
+
"code-reviewer",
|
|
22
|
+
"codebase-dissection",
|
|
23
|
+
"testing-strategy",
|
|
24
|
+
"refactoring-patterns",
|
|
25
|
+
"git-workflow",
|
|
26
|
+
"pre-production-review",
|
|
27
|
+
"frontend-code-review",
|
|
28
|
+
"dependency-audit",
|
|
29
|
+
"performance-optimization",
|
|
24
30
|
],
|
|
25
31
|
"Security & Infrastructure": [
|
|
26
|
-
"security-review",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
32
|
+
"security-review",
|
|
33
|
+
"local-security",
|
|
34
|
+
"container-security",
|
|
35
|
+
"docker-kubernetes",
|
|
36
|
+
"ci-cd-pipelines",
|
|
37
|
+
"ci-cd-automation",
|
|
38
|
+
"monitoring-observability",
|
|
39
|
+
"incident-response",
|
|
29
40
|
],
|
|
30
41
|
"Languages & Frameworks": [
|
|
31
|
-
"golang-pro",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
42
|
+
"golang-pro",
|
|
43
|
+
"go-linter-configuration",
|
|
44
|
+
"typescript",
|
|
45
|
+
"typescript-advanced",
|
|
46
|
+
"python-best-practices",
|
|
47
|
+
"rust-best-practices",
|
|
48
|
+
"frontend-design",
|
|
49
|
+
"fullstack-developer",
|
|
50
|
+
"remotion-best-practices",
|
|
51
|
+
"npm-package",
|
|
34
52
|
],
|
|
35
53
|
"API, Data & Docs": [
|
|
36
|
-
"api-design",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
54
|
+
"api-design",
|
|
55
|
+
"api-testing",
|
|
56
|
+
"programming-architecture",
|
|
57
|
+
"database-design",
|
|
58
|
+
"env-config",
|
|
59
|
+
"cost-optimization",
|
|
60
|
+
"docx",
|
|
61
|
+
"xlsx",
|
|
62
|
+
"doc-generation",
|
|
63
|
+
"update-docs",
|
|
39
64
|
],
|
|
40
65
|
"Game Design & Production": [
|
|
41
|
-
"game-design-theory",
|
|
42
|
-
"
|
|
43
|
-
"game-
|
|
44
|
-
"
|
|
66
|
+
"game-design-theory",
|
|
67
|
+
"game-engines",
|
|
68
|
+
"game-programming-languages",
|
|
69
|
+
"gameplay-mechanics",
|
|
70
|
+
"level-design",
|
|
71
|
+
"game-tools-workflows",
|
|
72
|
+
"game-servers",
|
|
73
|
+
"networking-servers",
|
|
74
|
+
"synchronization-algorithms",
|
|
75
|
+
"monetization-systems",
|
|
76
|
+
"publishing-platforms",
|
|
77
|
+
"daw-music",
|
|
45
78
|
],
|
|
46
79
|
"Graphics, Audio & Performance": [
|
|
47
|
-
"graphics-rendering",
|
|
48
|
-
"
|
|
80
|
+
"graphics-rendering",
|
|
81
|
+
"shader-techniques",
|
|
82
|
+
"particle-systems",
|
|
83
|
+
"audio-systems",
|
|
84
|
+
"asset-optimization",
|
|
85
|
+
"optimization-performance",
|
|
49
86
|
"memory-management",
|
|
50
87
|
],
|
|
51
|
-
"Skill Development": [
|
|
52
|
-
"skill-creation-guide", "skill-creator", "find-skills", "project-migration",
|
|
53
|
-
],
|
|
88
|
+
"Skill Development": ["skill-creation-guide", "skill-creator", "find-skills", "project-migration"],
|
|
54
89
|
};
|
|
55
90
|
// ---------------------------------------------------------------------------
|
|
56
91
|
// Helpers
|
|
@@ -68,7 +103,7 @@ function countInstalled() {
|
|
|
68
103
|
const dir = getInstallDir();
|
|
69
104
|
if (!existsSync(dir))
|
|
70
105
|
return 0;
|
|
71
|
-
return readdirSync(dir).filter(d => {
|
|
106
|
+
return readdirSync(dir).filter((d) => {
|
|
72
107
|
try {
|
|
73
108
|
return statSync(join(dir, d)).isDirectory();
|
|
74
109
|
}
|
|
@@ -91,16 +126,14 @@ function getRelatedSkills(skillName, limit = 3) {
|
|
|
91
126
|
const cat = getCategoryFor(skillName);
|
|
92
127
|
if (!cat)
|
|
93
128
|
return [];
|
|
94
|
-
return (SKILL_CATEGORIES[cat] ?? [])
|
|
95
|
-
.filter(s => s !== skillName && !isSkillInstalled(s))
|
|
96
|
-
.slice(0, limit);
|
|
129
|
+
return (SKILL_CATEGORIES[cat] ?? []).filter((s) => s !== skillName && !isSkillInstalled(s)).slice(0, limit);
|
|
97
130
|
}
|
|
98
131
|
function getInstalledNames() {
|
|
99
132
|
const dir = getInstallDir();
|
|
100
133
|
if (!existsSync(dir))
|
|
101
134
|
return [];
|
|
102
135
|
return readdirSync(dir)
|
|
103
|
-
.filter(d => {
|
|
136
|
+
.filter((d) => {
|
|
104
137
|
try {
|
|
105
138
|
return statSync(join(dir, d)).isDirectory();
|
|
106
139
|
}
|
|
@@ -110,7 +143,7 @@ function getInstalledNames() {
|
|
|
110
143
|
})
|
|
111
144
|
.sort();
|
|
112
145
|
}
|
|
113
|
-
function buildMenuOptions(installedCount,
|
|
146
|
+
function buildMenuOptions(installedCount, _availableCount) {
|
|
114
147
|
const isNew = installedCount === 0;
|
|
115
148
|
const options = [];
|
|
116
149
|
if (isNew) {
|
|
@@ -211,7 +244,7 @@ function doUninstall(skillName) {
|
|
|
211
244
|
// Skill Detail View (central action point)
|
|
212
245
|
// ---------------------------------------------------------------------------
|
|
213
246
|
async function skillDetailFlow(skillName, allSkills, providerName) {
|
|
214
|
-
const info = allSkills.find(s => s.name === skillName);
|
|
247
|
+
const info = allSkills.find((s) => s.name === skillName);
|
|
215
248
|
const installed = isSkillInstalled(skillName);
|
|
216
249
|
const meta = installed ? readSkillMeta(skillName) : null;
|
|
217
250
|
// Build info block
|
|
@@ -277,11 +310,11 @@ async function skillDetailFlow(skillName, allSkills, providerName) {
|
|
|
277
310
|
// Browse by Category
|
|
278
311
|
// ---------------------------------------------------------------------------
|
|
279
312
|
async function browseByCategory(allSkills, providerName) {
|
|
280
|
-
const availableNames = new Set(allSkills.map(s => s.name));
|
|
313
|
+
const availableNames = new Set(allSkills.map((s) => s.name));
|
|
281
314
|
while (true) {
|
|
282
315
|
const categoryOptions = Object.entries(SKILL_CATEGORIES).map(([name, skills]) => {
|
|
283
|
-
const valid = skills.filter(s => availableNames.has(s));
|
|
284
|
-
const installedCount = valid.filter(s => isSkillInstalled(s)).length;
|
|
316
|
+
const valid = skills.filter((s) => availableNames.has(s));
|
|
317
|
+
const installedCount = valid.filter((s) => isSkillInstalled(s)).length;
|
|
285
318
|
return {
|
|
286
319
|
value: name,
|
|
287
320
|
label: name,
|
|
@@ -290,10 +323,7 @@ async function browseByCategory(allSkills, providerName) {
|
|
|
290
323
|
});
|
|
291
324
|
const category = await p.select({
|
|
292
325
|
message: "Browse by category",
|
|
293
|
-
options: [
|
|
294
|
-
...categoryOptions,
|
|
295
|
-
{ value: "__back", label: "Back" },
|
|
296
|
-
],
|
|
326
|
+
options: [...categoryOptions, { value: "__back", label: "Back" }],
|
|
297
327
|
});
|
|
298
328
|
handleCancel(category);
|
|
299
329
|
if (category === "__back")
|
|
@@ -302,15 +332,15 @@ async function browseByCategory(allSkills, providerName) {
|
|
|
302
332
|
}
|
|
303
333
|
}
|
|
304
334
|
async function categorySkillList(categoryName, skillNames, allSkills, providerName) {
|
|
305
|
-
const availableNames = new Set(allSkills.map(s => s.name));
|
|
306
|
-
const validSkills = skillNames.filter(s => availableNames.has(s));
|
|
335
|
+
const availableNames = new Set(allSkills.map((s) => s.name));
|
|
336
|
+
const validSkills = skillNames.filter((s) => availableNames.has(s));
|
|
307
337
|
if (validSkills.length === 0) {
|
|
308
338
|
p.log.warn("No skills found in this category.");
|
|
309
339
|
return;
|
|
310
340
|
}
|
|
311
341
|
while (true) {
|
|
312
|
-
const options = validSkills.map(name => {
|
|
313
|
-
const info = allSkills.find(s => s.name === name);
|
|
342
|
+
const options = validSkills.map((name) => {
|
|
343
|
+
const info = allSkills.find((s) => s.name === name);
|
|
314
344
|
const installed = isSkillInstalled(name);
|
|
315
345
|
return {
|
|
316
346
|
value: name,
|
|
@@ -318,7 +348,7 @@ async function categorySkillList(categoryName, skillNames, allSkills, providerNa
|
|
|
318
348
|
hint: truncate(info?.description ?? "", 50),
|
|
319
349
|
};
|
|
320
350
|
});
|
|
321
|
-
const notInstalled = validSkills.filter(s => !isSkillInstalled(s));
|
|
351
|
+
const notInstalled = validSkills.filter((s) => !isSkillInstalled(s));
|
|
322
352
|
const extraOptions = [];
|
|
323
353
|
if (notInstalled.length > 0) {
|
|
324
354
|
extraOptions.push({
|
|
@@ -386,18 +416,14 @@ async function searchFlow(allSkills, providerName) {
|
|
|
386
416
|
}
|
|
387
417
|
async function searchResultsPicker(results, allSkills, providerName) {
|
|
388
418
|
while (true) {
|
|
389
|
-
const options = results.map(skill => ({
|
|
419
|
+
const options = results.map((skill) => ({
|
|
390
420
|
value: skill.name,
|
|
391
421
|
label: `${skill.name}${isSkillInstalled(skill.name) ? chalk.green(" \u2713") : ""}`,
|
|
392
422
|
hint: truncate(skill.description, 50),
|
|
393
423
|
}));
|
|
394
424
|
const picked = await p.select({
|
|
395
425
|
message: "Pick a skill for details",
|
|
396
|
-
options: [
|
|
397
|
-
...options,
|
|
398
|
-
{ value: "__search", label: "Search again" },
|
|
399
|
-
{ value: "__back", label: "Back" },
|
|
400
|
-
],
|
|
426
|
+
options: [...options, { value: "__search", label: "Search again" }, { value: "__back", label: "Back" }],
|
|
401
427
|
});
|
|
402
428
|
handleCancel(picked);
|
|
403
429
|
if (picked === "__search")
|
|
@@ -417,7 +443,7 @@ async function quickSetup(allSkills, providerName) {
|
|
|
417
443
|
const proj = detectProject(process.cwd());
|
|
418
444
|
p.log.step(`Detected: ${chalk.cyan(proj.name)} (${proj.type})`);
|
|
419
445
|
const suggestions = SKILL_SUGGESTIONS[proj.type] ?? SKILL_SUGGESTIONS_DEFAULT;
|
|
420
|
-
const availableNames = new Set(allSkills.map(s => s.name));
|
|
446
|
+
const availableNames = new Set(allSkills.map((s) => s.name));
|
|
421
447
|
const validSuggestions = suggestions.filter((s) => availableNames.has(s));
|
|
422
448
|
if (validSuggestions.length === 0) {
|
|
423
449
|
p.log.info("No specific recommendations for this project type.");
|
|
@@ -467,17 +493,17 @@ async function manageInstalled(allSkills, providerName) {
|
|
|
467
493
|
const groups = [];
|
|
468
494
|
const categorized = new Set();
|
|
469
495
|
for (const [cat, catSkills] of Object.entries(SKILL_CATEGORIES)) {
|
|
470
|
-
const installed = catSkills.filter(s => names.includes(s));
|
|
496
|
+
const installed = catSkills.filter((s) => names.includes(s));
|
|
471
497
|
if (installed.length > 0) {
|
|
472
498
|
groups.push({ cat, skills: installed });
|
|
473
|
-
installed.forEach(s => categorized.add(s));
|
|
499
|
+
installed.forEach((s) => categorized.add(s));
|
|
474
500
|
}
|
|
475
501
|
}
|
|
476
|
-
const uncategorized = names.filter(s => !categorized.has(s));
|
|
502
|
+
const uncategorized = names.filter((s) => !categorized.has(s));
|
|
477
503
|
if (uncategorized.length > 0) {
|
|
478
504
|
groups.push({ cat: "Other", skills: uncategorized });
|
|
479
505
|
}
|
|
480
|
-
const options = groups.map(g => ({
|
|
506
|
+
const options = groups.map((g) => ({
|
|
481
507
|
value: g.cat,
|
|
482
508
|
label: g.cat,
|
|
483
509
|
hint: `${g.skills.length} installed`,
|
|
@@ -502,7 +528,7 @@ async function manageInstalled(allSkills, providerName) {
|
|
|
502
528
|
await bulkUninstall(names);
|
|
503
529
|
continue;
|
|
504
530
|
}
|
|
505
|
-
const group = groups.find(g => g.cat === picked);
|
|
531
|
+
const group = groups.find((g) => g.cat === picked);
|
|
506
532
|
if (group) {
|
|
507
533
|
await installedCategoryList(group.cat, group.skills, allSkills, providerName);
|
|
508
534
|
}
|
|
@@ -510,12 +536,12 @@ async function manageInstalled(allSkills, providerName) {
|
|
|
510
536
|
}
|
|
511
537
|
async function installedCategoryList(categoryName, installedNames, allSkills, providerName) {
|
|
512
538
|
while (true) {
|
|
513
|
-
const stillInstalled = installedNames.filter(s => isSkillInstalled(s));
|
|
539
|
+
const stillInstalled = installedNames.filter((s) => isSkillInstalled(s));
|
|
514
540
|
if (stillInstalled.length === 0) {
|
|
515
541
|
p.log.info("No skills remaining in this category.");
|
|
516
542
|
return;
|
|
517
543
|
}
|
|
518
|
-
const options = stillInstalled.map(name => {
|
|
544
|
+
const options = stillInstalled.map((name) => {
|
|
519
545
|
const meta = readSkillMeta(name);
|
|
520
546
|
const ver = meta ? `v${meta.version}` : "";
|
|
521
547
|
const date = meta?.installedAt ? new Date(meta.installedAt).toLocaleDateString() : "";
|
|
@@ -540,7 +566,7 @@ async function installedCategoryList(categoryName, installedNames, allSkills, pr
|
|
|
540
566
|
async function bulkUninstall(installedNames) {
|
|
541
567
|
const selected = await p.multiselect({
|
|
542
568
|
message: "Select skills to uninstall",
|
|
543
|
-
options: installedNames.map(name => ({ value: name, label: name })),
|
|
569
|
+
options: installedNames.map((name) => ({ value: name, label: name })),
|
|
544
570
|
required: false,
|
|
545
571
|
maxItems: 15,
|
|
546
572
|
});
|
|
@@ -583,7 +609,7 @@ async function updateAll(providerName) {
|
|
|
583
609
|
p.log.error(ui.dim(err.message));
|
|
584
610
|
return;
|
|
585
611
|
}
|
|
586
|
-
const remoteMap = new Map(remoteSkills.map(rs => [rs.name, rs]));
|
|
612
|
+
const remoteMap = new Map(remoteSkills.map((rs) => [rs.name, rs]));
|
|
587
613
|
const updates = [];
|
|
588
614
|
for (const name of installed) {
|
|
589
615
|
const remote = remoteMap.get(name);
|
|
@@ -642,15 +668,13 @@ async function checkHealth() {
|
|
|
642
668
|
const checks = runDoctorChecks();
|
|
643
669
|
p.log.step(chalk.bold("Environment Health Check"));
|
|
644
670
|
for (const check of checks) {
|
|
645
|
-
const icon = check.status === "pass" ? chalk.green("OK")
|
|
646
|
-
: check.status === "warn" ? chalk.yellow("!!")
|
|
647
|
-
: chalk.red("XX");
|
|
671
|
+
const icon = check.status === "pass" ? chalk.green("OK") : check.status === "warn" ? chalk.yellow("!!") : chalk.red("XX");
|
|
648
672
|
p.log.info(`${icon} ${chalk.bold(check.name)}: ${check.message}`);
|
|
649
673
|
if (check.fix)
|
|
650
674
|
p.log.info(chalk.dim(` Fix: ${check.fix}`));
|
|
651
675
|
}
|
|
652
|
-
const fails = checks.filter(c => c.status === "fail").length;
|
|
653
|
-
const warns = checks.filter(c => c.status === "warn").length;
|
|
676
|
+
const fails = checks.filter((c) => c.status === "fail").length;
|
|
677
|
+
const warns = checks.filter((c) => c.status === "warn").length;
|
|
654
678
|
if (fails > 0) {
|
|
655
679
|
p.log.error(`${fails} issue${fails > 1 ? "s" : ""} found`);
|
|
656
680
|
}
|
|
@@ -662,10 +686,10 @@ async function checkHealth() {
|
|
|
662
686
|
return;
|
|
663
687
|
}
|
|
664
688
|
// Offer fixes once - no loop. User can re-enter health check to verify.
|
|
665
|
-
const fixChecks = checks.filter(c => c.fix && c.status !== "pass");
|
|
689
|
+
const fixChecks = checks.filter((c) => c.fix && c.status !== "pass");
|
|
666
690
|
if (fixChecks.length === 0)
|
|
667
691
|
return;
|
|
668
|
-
const fixOptions = fixChecks.map(c => {
|
|
692
|
+
const fixOptions = fixChecks.map((c) => {
|
|
669
693
|
const cmd = c.fix.replace(/^Run:\s*/, "");
|
|
670
694
|
return { value: cmd, label: `Run: ${cmd}`, hint: c.name };
|
|
671
695
|
});
|
|
@@ -677,7 +701,7 @@ async function checkHealth() {
|
|
|
677
701
|
if (fixAction !== "__skip") {
|
|
678
702
|
const cmd = fixAction;
|
|
679
703
|
const SAFE_PREFIXES = ["arcana ", "git config "];
|
|
680
|
-
if (!SAFE_PREFIXES.some(pre => cmd.startsWith(pre))) {
|
|
704
|
+
if (!SAFE_PREFIXES.some((pre) => cmd.startsWith(pre))) {
|
|
681
705
|
p.log.warn(`Skipped unsafe command: ${cmd}`);
|
|
682
706
|
}
|
|
683
707
|
else {
|
|
@@ -724,7 +748,7 @@ export async function showInteractiveMenu(version) {
|
|
|
724
748
|
const config = loadConfig();
|
|
725
749
|
const providerName = config.defaultProvider;
|
|
726
750
|
// Fetch skill list once for the session
|
|
727
|
-
|
|
751
|
+
const allSkills = [];
|
|
728
752
|
let availableCount = 0;
|
|
729
753
|
try {
|
|
730
754
|
const providers = getProviders();
|
|
@@ -815,4 +839,3 @@ export async function showInteractiveMenu(version) {
|
|
|
815
839
|
}
|
|
816
840
|
}
|
|
817
841
|
}
|
|
818
|
-
//# sourceMappingURL=interactive.js.map
|
package/dist/providers/arcana.js
CHANGED
package/dist/providers/base.d.ts
CHANGED
package/dist/providers/base.js
CHANGED
package/dist/providers/github.js
CHANGED
|
@@ -81,6 +81,11 @@ export class GitHubProvider extends Provider {
|
|
|
81
81
|
version: p.version,
|
|
82
82
|
source: this.name,
|
|
83
83
|
repo: `https://github.com/${this.owner}/${this.repo}`,
|
|
84
|
+
tags: p.tags,
|
|
85
|
+
conflicts: p.conflicts,
|
|
86
|
+
companions: p.companions,
|
|
87
|
+
verified: p.verified,
|
|
88
|
+
author: p.author,
|
|
84
89
|
}));
|
|
85
90
|
// Warn about malformed entries
|
|
86
91
|
const skipped = data.plugins.length - this.cache.length;
|
|
@@ -119,7 +124,7 @@ export class GitHubProvider extends Provider {
|
|
|
119
124
|
return { path: relativePath, content };
|
|
120
125
|
}
|
|
121
126
|
catch (err) {
|
|
122
|
-
throw new Error(`Failed to fetch file "${relativePath}" for skill "${skillName}": ${err instanceof Error ? err.message : String(err)}
|
|
127
|
+
throw new Error(`Failed to fetch file "${relativePath}" for skill "${skillName}": ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
123
128
|
}
|
|
124
129
|
}, 6);
|
|
125
130
|
return results.filter((r) => r !== null);
|
|
@@ -128,7 +133,8 @@ export class GitHubProvider extends Provider {
|
|
|
128
133
|
const all = await this.list();
|
|
129
134
|
const q = query.toLowerCase();
|
|
130
135
|
const exact = all.filter((s) => s.name.toLowerCase().includes(q) ||
|
|
131
|
-
s.description.toLowerCase().includes(q)
|
|
136
|
+
s.description.toLowerCase().includes(q) ||
|
|
137
|
+
s.tags?.some((t) => t.toLowerCase().includes(q)));
|
|
132
138
|
if (exact.length > 0)
|
|
133
139
|
return exact;
|
|
134
140
|
// Fuzzy fallback: match skills where Levenshtein distance to name <= 3
|
|
@@ -143,4 +149,3 @@ export class GitHubProvider extends Provider {
|
|
|
143
149
|
clearCacheFile(this.cacheKey);
|
|
144
150
|
}
|
|
145
151
|
}
|
|
146
|
-
//# sourceMappingURL=github.js.map
|
package/dist/registry.d.ts
CHANGED
package/dist/registry.js
CHANGED
|
@@ -64,8 +64,5 @@ export function getProviders(name) {
|
|
|
64
64
|
if (name)
|
|
65
65
|
return [getProvider(name)];
|
|
66
66
|
const config = loadConfig();
|
|
67
|
-
return config.providers
|
|
68
|
-
.filter((p) => p.enabled)
|
|
69
|
-
.map((p) => createProvider(p.name, p.type, p.url));
|
|
67
|
+
return config.providers.filter((p) => p.enabled).map((p) => createProvider(p.name, p.type, p.url));
|
|
70
68
|
}
|
|
71
|
-
//# sourceMappingURL=registry.js.map
|
package/dist/types.d.ts
CHANGED
|
@@ -4,6 +4,11 @@ export interface SkillInfo {
|
|
|
4
4
|
version: string;
|
|
5
5
|
source: string;
|
|
6
6
|
repo?: string;
|
|
7
|
+
tags?: string[];
|
|
8
|
+
conflicts?: string[];
|
|
9
|
+
companions?: string[];
|
|
10
|
+
verified?: boolean;
|
|
11
|
+
author?: string;
|
|
7
12
|
}
|
|
8
13
|
export interface SkillFile {
|
|
9
14
|
path: string;
|
|
@@ -26,6 +31,11 @@ export interface MarketplacePlugin {
|
|
|
26
31
|
source: string;
|
|
27
32
|
description: string;
|
|
28
33
|
version: string;
|
|
34
|
+
tags?: string[];
|
|
35
|
+
conflicts?: string[];
|
|
36
|
+
companions?: string[];
|
|
37
|
+
verified?: boolean;
|
|
38
|
+
author?: string;
|
|
29
39
|
}
|
|
30
40
|
export interface ProviderConfig {
|
|
31
41
|
name: string;
|
|
@@ -64,4 +74,3 @@ export interface DoctorCheck {
|
|
|
64
74
|
message: string;
|
|
65
75
|
fix?: string;
|
|
66
76
|
}
|
|
67
|
-
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.js
CHANGED
package/dist/utils/atomic.d.ts
CHANGED
package/dist/utils/atomic.js
CHANGED
package/dist/utils/cache.d.ts
CHANGED
package/dist/utils/cache.js
CHANGED
package/dist/utils/config.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ArcanaConfig, ProviderConfig } from "../types.js";
|
|
2
|
+
/** Validate config and return warnings for invalid fields. */
|
|
3
|
+
export declare function validateConfig(config: ArcanaConfig): string[];
|
|
2
4
|
export declare function loadConfig(): ArcanaConfig;
|
|
3
5
|
export declare function saveConfig(config: ArcanaConfig): void;
|
|
4
6
|
export declare function addProvider(provider: ProviderConfig): void;
|
|
5
7
|
export declare function removeProvider(name: string): boolean;
|
|
6
|
-
//# sourceMappingURL=config.d.ts.map
|
package/dist/utils/config.js
CHANGED
|
@@ -4,6 +4,8 @@ import { homedir } from "node:os";
|
|
|
4
4
|
import { ui } from "./ui.js";
|
|
5
5
|
import { atomicWriteSync } from "./atomic.js";
|
|
6
6
|
const CONFIG_PATH = join(homedir(), ".arcana", "config.json");
|
|
7
|
+
/** Matches owner/repo slug format (e.g. "medy-gribkov/arcana") */
|
|
8
|
+
const SLUG_RE = /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/;
|
|
7
9
|
const DEFAULT_CONFIG = {
|
|
8
10
|
defaultProvider: "arcana",
|
|
9
11
|
installDir: join(homedir(), ".agents", "skills"),
|
|
@@ -17,7 +19,27 @@ const DEFAULT_CONFIG = {
|
|
|
17
19
|
],
|
|
18
20
|
};
|
|
19
21
|
function cloneConfig(config) {
|
|
20
|
-
return { ...config, providers: config.providers.map(p => ({ ...p })) };
|
|
22
|
+
return { ...config, providers: config.providers.map((p) => ({ ...p })) };
|
|
23
|
+
}
|
|
24
|
+
/** Validate config and return warnings for invalid fields. */
|
|
25
|
+
export function validateConfig(config) {
|
|
26
|
+
const warnings = [];
|
|
27
|
+
// Validate providers have valid owner/repo slugs
|
|
28
|
+
for (const p of config.providers) {
|
|
29
|
+
if (p.type === "github" && !SLUG_RE.test(p.url)) {
|
|
30
|
+
warnings.push(`Provider "${p.name}" has invalid URL "${p.url}". Expected owner/repo format.`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Validate installDir is absolute
|
|
34
|
+
if (!isAbsolute(config.installDir)) {
|
|
35
|
+
warnings.push(`installDir "${config.installDir}" is not an absolute path.`);
|
|
36
|
+
}
|
|
37
|
+
// Validate defaultProvider matches a configured provider or valid slug
|
|
38
|
+
const providerNames = config.providers.map((p) => p.name);
|
|
39
|
+
if (!providerNames.includes(config.defaultProvider) && !SLUG_RE.test(config.defaultProvider)) {
|
|
40
|
+
warnings.push(`defaultProvider "${config.defaultProvider}" doesn't match any configured provider and isn't a valid slug.`);
|
|
41
|
+
}
|
|
42
|
+
return warnings;
|
|
21
43
|
}
|
|
22
44
|
export function loadConfig() {
|
|
23
45
|
if (!existsSync(CONFIG_PATH)) {
|
|
@@ -29,7 +51,7 @@ export function loadConfig() {
|
|
|
29
51
|
const config = {
|
|
30
52
|
...DEFAULT_CONFIG,
|
|
31
53
|
...loaded,
|
|
32
|
-
providers: loaded.providers ?? DEFAULT_CONFIG.providers.map(p => ({ ...p })),
|
|
54
|
+
providers: loaded.providers ?? DEFAULT_CONFIG.providers.map((p) => ({ ...p })),
|
|
33
55
|
};
|
|
34
56
|
return applyEnvOverrides(config);
|
|
35
57
|
}
|
|
@@ -51,11 +73,15 @@ function applyEnvOverrides(base) {
|
|
|
51
73
|
}
|
|
52
74
|
const envProvider = process.env.ARCANA_DEFAULT_PROVIDER;
|
|
53
75
|
if (envProvider) {
|
|
54
|
-
|
|
76
|
+
const trimmed = envProvider.trim();
|
|
77
|
+
if (trimmed.length === 0) {
|
|
55
78
|
console.error(ui.warn(" Warning: ARCANA_DEFAULT_PROVIDER is empty. Ignoring."));
|
|
56
79
|
}
|
|
80
|
+
else if (!SLUG_RE.test(trimmed) && !config.providers.some((p) => p.name === trimmed)) {
|
|
81
|
+
console.error(ui.warn(` Warning: ARCANA_DEFAULT_PROVIDER "${trimmed}" is not a valid slug. Ignoring.`));
|
|
82
|
+
}
|
|
57
83
|
else {
|
|
58
|
-
config.defaultProvider =
|
|
84
|
+
config.defaultProvider = trimmed;
|
|
59
85
|
}
|
|
60
86
|
}
|
|
61
87
|
return config;
|
|
@@ -87,4 +113,3 @@ export function removeProvider(name) {
|
|
|
87
113
|
saveConfig(config);
|
|
88
114
|
return true;
|
|
89
115
|
}
|
|
90
|
-
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SkillInfo } from "../types.js";
|
|
2
|
+
import type { ProjectContext } from "./project-context.js";
|
|
3
|
+
export interface ConflictWarning {
|
|
4
|
+
type: "rule-overlap" | "skill-conflict" | "preference-clash";
|
|
5
|
+
message: string;
|
|
6
|
+
severity: "warn" | "block";
|
|
7
|
+
}
|
|
8
|
+
export declare function checkConflicts(skillName: string, skillInfo: SkillInfo | null, skillContent: string | null, context: ProjectContext): ConflictWarning[];
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** Known opposing preference pairs. If a skill promotes one, and CLAUDE.md has the other, warn. */
|
|
2
|
+
const OPPOSING_PAIRS = [
|
|
3
|
+
["callbacks", "async/await"],
|
|
4
|
+
["any", "strict typing"],
|
|
5
|
+
["abbreviations", "meaningful names"],
|
|
6
|
+
["classes", "functional"],
|
|
7
|
+
["oop", "functional programming"],
|
|
8
|
+
["semicolons", "no semicolons"],
|
|
9
|
+
["tabs", "spaces"],
|
|
10
|
+
];
|
|
11
|
+
export function checkConflicts(skillName, skillInfo, skillContent, context) {
|
|
12
|
+
const warnings = [];
|
|
13
|
+
// 1. Explicit skill-level conflicts from marketplace metadata
|
|
14
|
+
if (skillInfo?.conflicts && skillInfo.conflicts.length > 0) {
|
|
15
|
+
const installed = context.installedSkills;
|
|
16
|
+
const conflicting = skillInfo.conflicts.filter((c) => installed.includes(c));
|
|
17
|
+
for (const c of conflicting) {
|
|
18
|
+
warnings.push({
|
|
19
|
+
type: "skill-conflict",
|
|
20
|
+
message: `"${skillName}" conflicts with installed skill "${c}".`,
|
|
21
|
+
severity: "block",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// 2. Rule overlap: skill name matches existing .claude/rules/*.md filename
|
|
26
|
+
const ruleBasenames = context.ruleFiles.map((f) => f.replace(/\.md$/, "").toLowerCase());
|
|
27
|
+
if (ruleBasenames.includes(skillName.toLowerCase())) {
|
|
28
|
+
warnings.push({
|
|
29
|
+
type: "rule-overlap",
|
|
30
|
+
message: `A rule file "${skillName}.md" already exists in .claude/rules/. This skill may duplicate existing instructions.`,
|
|
31
|
+
severity: "warn",
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
// Also check if skill tags overlap heavily with rule file names
|
|
35
|
+
if (skillInfo?.tags) {
|
|
36
|
+
for (const tag of skillInfo.tags) {
|
|
37
|
+
if (ruleBasenames.includes(tag.toLowerCase()) && tag.toLowerCase() !== skillName.toLowerCase()) {
|
|
38
|
+
warnings.push({
|
|
39
|
+
type: "rule-overlap",
|
|
40
|
+
message: `Skill tag "${tag}" matches existing rule "${tag}.md". May overlap.`,
|
|
41
|
+
severity: "warn",
|
|
42
|
+
});
|
|
43
|
+
break; // one warning is enough
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// 3. Preference clash: check skill content against CLAUDE.md preferences
|
|
48
|
+
if (skillContent && context.preferences.length > 0) {
|
|
49
|
+
const contentLower = skillContent.toLowerCase();
|
|
50
|
+
for (const [a, b] of OPPOSING_PAIRS) {
|
|
51
|
+
const skillHasA = contentLower.includes(a);
|
|
52
|
+
const skillHasB = contentLower.includes(b);
|
|
53
|
+
const prefsHaveA = context.preferences.some((p) => p.toLowerCase().includes(a));
|
|
54
|
+
const prefsHaveB = context.preferences.some((p) => p.toLowerCase().includes(b));
|
|
55
|
+
if (skillHasA && prefsHaveB) {
|
|
56
|
+
warnings.push({
|
|
57
|
+
type: "preference-clash",
|
|
58
|
+
message: `Skill mentions "${a}" but your project prefers "${b}".`,
|
|
59
|
+
severity: "warn",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
else if (skillHasB && prefsHaveA) {
|
|
63
|
+
warnings.push({
|
|
64
|
+
type: "preference-clash",
|
|
65
|
+
message: `Skill mentions "${b}" but your project prefers "${a}".`,
|
|
66
|
+
severity: "warn",
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return warnings;
|
|
72
|
+
}
|
package/dist/utils/errors.d.ts
CHANGED