@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
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { getInstallDir, isSkillInstalled } from "../utils/fs.js";
|
|
6
|
+
import { verifySkillIntegrity } from "../utils/integrity.js";
|
|
7
|
+
import { validateSlug } from "../utils/validate.js";
|
|
8
|
+
import { renderBanner } from "../utils/help.js";
|
|
9
|
+
export async function verifyCommand(skillNames, opts) {
|
|
10
|
+
if (opts.json) {
|
|
11
|
+
return verifyJson(skillNames, opts);
|
|
12
|
+
}
|
|
13
|
+
console.log(renderBanner());
|
|
14
|
+
console.log();
|
|
15
|
+
const installDir = getInstallDir();
|
|
16
|
+
if (skillNames.length === 0 && !opts.all) {
|
|
17
|
+
p.intro(chalk.bold("Verify skill integrity"));
|
|
18
|
+
p.cancel("Specify a skill name or use --all");
|
|
19
|
+
p.log.info("Usage: arcana verify <skill-name> [skill2 ...]");
|
|
20
|
+
p.log.info(" arcana verify --all");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
let skills;
|
|
24
|
+
if (opts.all) {
|
|
25
|
+
try {
|
|
26
|
+
skills = readdirSync(installDir).filter((d) => statSync(join(installDir, d)).isDirectory());
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
skills = [];
|
|
30
|
+
}
|
|
31
|
+
if (skills.length === 0) {
|
|
32
|
+
p.intro(chalk.bold("Verify skill integrity"));
|
|
33
|
+
p.log.info("No installed skills found.");
|
|
34
|
+
p.outro("Nothing to verify.");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
for (const name of skillNames) {
|
|
40
|
+
try {
|
|
41
|
+
validateSlug(name, "skill name");
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
p.log.error(err instanceof Error ? err.message : `Invalid skill name: ${name}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for (const name of skillNames) {
|
|
49
|
+
if (!isSkillInstalled(name)) {
|
|
50
|
+
p.log.error(`Skill ${chalk.bold(name)} is not installed.`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
skills = skillNames;
|
|
55
|
+
}
|
|
56
|
+
p.intro(chalk.bold("Verify skill integrity"));
|
|
57
|
+
const results = [];
|
|
58
|
+
for (const skill of skills) {
|
|
59
|
+
const status = verifySkillIntegrity(skill, installDir);
|
|
60
|
+
results.push({ skill, status });
|
|
61
|
+
if (status === "ok") {
|
|
62
|
+
p.log.info(`${chalk.green("[OK]")} ${skill}`);
|
|
63
|
+
}
|
|
64
|
+
else if (status === "modified") {
|
|
65
|
+
p.log.warn(`${chalk.yellow("[MODIFIED]")} ${skill}`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
p.log.info(`${chalk.dim("[MISSING]")} ${skill}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const total = results.length;
|
|
72
|
+
const okCount = results.filter((r) => r.status === "ok").length;
|
|
73
|
+
const modifiedCount = results.filter((r) => r.status === "modified").length;
|
|
74
|
+
const missingCount = results.filter((r) => r.status === "missing").length;
|
|
75
|
+
console.log();
|
|
76
|
+
p.outro(`${total} skills verified, ${okCount} OK, ${modifiedCount} modified, ${missingCount} not tracked`);
|
|
77
|
+
if (modifiedCount > 0) {
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function verifyJson(skillNames, opts) {
|
|
82
|
+
const installDir = getInstallDir();
|
|
83
|
+
let skills;
|
|
84
|
+
if (opts.all) {
|
|
85
|
+
try {
|
|
86
|
+
skills = readdirSync(installDir).filter((d) => statSync(join(installDir, d)).isDirectory());
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
skills = [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (skillNames.length === 0) {
|
|
93
|
+
console.log(JSON.stringify({ error: "Specify a skill name or use --all" }));
|
|
94
|
+
process.exit(1);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
skills = skillNames;
|
|
99
|
+
}
|
|
100
|
+
const results = [];
|
|
101
|
+
for (const skill of skills) {
|
|
102
|
+
const status = verifySkillIntegrity(skill, installDir);
|
|
103
|
+
results.push({ skill, status });
|
|
104
|
+
}
|
|
105
|
+
const total = results.length;
|
|
106
|
+
const ok = results.filter((r) => r.status === "ok").length;
|
|
107
|
+
const modified = results.filter((r) => r.status === "modified").length;
|
|
108
|
+
const missing = results.filter((r) => r.status === "missing").length;
|
|
109
|
+
console.log(JSON.stringify({
|
|
110
|
+
results: results.map((r) => ({ skill: r.skill, status: r.status })),
|
|
111
|
+
summary: { total, ok, modified, missing },
|
|
112
|
+
}));
|
|
113
|
+
if (modified > 0) {
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const AGENT_LOG_MAX_AGE_DAYS = 7;
|
|
2
|
+
export declare const MAIN_LOG_MAX_AGE_DAYS = 30;
|
|
3
|
+
export declare const STALE_PROJECT_DAYS = 90;
|
|
4
|
+
export declare const PRUNE_DEFAULT_DAYS = 14;
|
|
5
|
+
export declare const PRUNE_SIZE_THRESHOLD_BYTES: number;
|
|
6
|
+
export declare const PRUNE_KEEP_NEWEST = 3;
|
|
7
|
+
export declare const LARGE_SKILL_KB_THRESHOLD = 50;
|
|
8
|
+
export declare const TOKENS_PER_KB = 256;
|
|
9
|
+
export declare const CONTEXT_WINDOW_TOKENS = 200000;
|
|
10
|
+
export declare const DESCRIPTION_TRUNCATION = 50;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Retention & pruning
|
|
2
|
+
export const AGENT_LOG_MAX_AGE_DAYS = 7;
|
|
3
|
+
export const MAIN_LOG_MAX_AGE_DAYS = 30;
|
|
4
|
+
export const STALE_PROJECT_DAYS = 90;
|
|
5
|
+
export const PRUNE_DEFAULT_DAYS = 14;
|
|
6
|
+
export const PRUNE_SIZE_THRESHOLD_BYTES = 10 * 1024 * 1024; // 10 MB
|
|
7
|
+
export const PRUNE_KEEP_NEWEST = 3;
|
|
8
|
+
// Skill size warnings
|
|
9
|
+
export const LARGE_SKILL_KB_THRESHOLD = 50;
|
|
10
|
+
export const TOKENS_PER_KB = 256;
|
|
11
|
+
export const CONTEXT_WINDOW_TOKENS = 200_000;
|
|
12
|
+
// Display
|
|
13
|
+
export const DESCRIPTION_TRUNCATION = 50;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { isSkillInstalled } from "../utils/fs.js";
|
|
4
|
+
import { getProvider } from "../registry.js";
|
|
5
|
+
import { installOneCore } from "../utils/install-core.js";
|
|
6
|
+
import { appendHistory } from "../utils/history.js";
|
|
7
|
+
import { ui } from "../utils/ui.js";
|
|
8
|
+
import { handleCancel, truncate } from "./helpers.js";
|
|
9
|
+
import { SKILL_CATEGORIES } from "./categories.js";
|
|
10
|
+
import { skillDetailFlow } from "./skill-detail.js";
|
|
11
|
+
async function doBatchInstall(names, providerName) {
|
|
12
|
+
if (names.length === 0)
|
|
13
|
+
return 0;
|
|
14
|
+
const provider = getProvider(providerName);
|
|
15
|
+
const s = p.spinner();
|
|
16
|
+
s.start(`Installing ${names.length} skill${names.length > 1 ? "s" : ""}...`);
|
|
17
|
+
let installed = 0;
|
|
18
|
+
for (const name of names) {
|
|
19
|
+
try {
|
|
20
|
+
s.message(`Installing ${chalk.bold(name)} (${installed + 1}/${names.length})...`);
|
|
21
|
+
const result = await installOneCore(name, provider, {});
|
|
22
|
+
if (result.success) {
|
|
23
|
+
installed++;
|
|
24
|
+
appendHistory("install", name);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
s.stop(`Failed: ${name}`);
|
|
29
|
+
if (err instanceof Error)
|
|
30
|
+
p.log.error(ui.dim(err.message));
|
|
31
|
+
if (installed + 1 < names.length)
|
|
32
|
+
s.start(`Installing next...`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
s.stop(`Installed ${installed} skill${installed !== 1 ? "s" : ""}`);
|
|
36
|
+
return installed;
|
|
37
|
+
}
|
|
38
|
+
export async function browseByCategory(allSkills, providerName) {
|
|
39
|
+
const availableNames = new Set(allSkills.map((s) => s.name));
|
|
40
|
+
while (true) {
|
|
41
|
+
const categoryOptions = Object.entries(SKILL_CATEGORIES).map(([name, skills]) => {
|
|
42
|
+
const valid = skills.filter((s) => availableNames.has(s));
|
|
43
|
+
const installedCount = valid.filter((s) => isSkillInstalled(s)).length;
|
|
44
|
+
return {
|
|
45
|
+
value: name,
|
|
46
|
+
label: name,
|
|
47
|
+
hint: `${valid.length} skills, ${installedCount} installed`,
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
const category = await p.select({
|
|
51
|
+
message: "Browse by category",
|
|
52
|
+
options: [...categoryOptions, { value: "__back", label: "Back" }],
|
|
53
|
+
});
|
|
54
|
+
handleCancel(category);
|
|
55
|
+
if (category === "__back")
|
|
56
|
+
return;
|
|
57
|
+
await categorySkillList(category, SKILL_CATEGORIES[category] ?? [], allSkills, providerName);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function categorySkillList(categoryName, skillNames, allSkills, providerName) {
|
|
61
|
+
const availableNames = new Set(allSkills.map((s) => s.name));
|
|
62
|
+
const validSkills = skillNames.filter((s) => availableNames.has(s));
|
|
63
|
+
if (validSkills.length === 0) {
|
|
64
|
+
p.log.warn("No skills found in this category.");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
while (true) {
|
|
68
|
+
const options = validSkills.map((name) => {
|
|
69
|
+
const info = allSkills.find((s) => s.name === name);
|
|
70
|
+
const installed = isSkillInstalled(name);
|
|
71
|
+
return {
|
|
72
|
+
value: name,
|
|
73
|
+
label: `${name}${installed ? chalk.green(" \u2713") : ""}`,
|
|
74
|
+
hint: truncate(info?.description ?? "", 50),
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
const notInstalled = validSkills.filter((s) => !isSkillInstalled(s));
|
|
78
|
+
const extraOptions = [];
|
|
79
|
+
if (notInstalled.length > 0) {
|
|
80
|
+
extraOptions.push({
|
|
81
|
+
value: "__install_all",
|
|
82
|
+
label: `Install all uninstalled`,
|
|
83
|
+
hint: `${notInstalled.length} skill${notInstalled.length > 1 ? "s" : ""}`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
extraOptions.push({ value: "__back", label: "Back to categories" });
|
|
87
|
+
const picked = await p.select({
|
|
88
|
+
message: `${categoryName} (${validSkills.length} skills)`,
|
|
89
|
+
options: [...options, ...extraOptions],
|
|
90
|
+
});
|
|
91
|
+
handleCancel(picked);
|
|
92
|
+
if (picked === "__back")
|
|
93
|
+
return;
|
|
94
|
+
if (picked === "__install_all") {
|
|
95
|
+
await doBatchInstall(notInstalled, providerName);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const result = await skillDetailFlow(picked, allSkills, providerName);
|
|
99
|
+
if (result === "menu")
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export { doBatchInstall };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { isSkillInstalled } from "../utils/fs.js";
|
|
2
|
+
// Category map (7 categories, 4-12 skills each, no orphans)
|
|
3
|
+
const SKILL_CATEGORIES = {
|
|
4
|
+
"Code Quality & Review": [
|
|
5
|
+
"code-reviewer",
|
|
6
|
+
"codebase-dissection",
|
|
7
|
+
"testing-strategy",
|
|
8
|
+
"refactoring-patterns",
|
|
9
|
+
"git-workflow",
|
|
10
|
+
"pre-production-review",
|
|
11
|
+
"frontend-code-review",
|
|
12
|
+
"dependency-audit",
|
|
13
|
+
"performance-optimization",
|
|
14
|
+
],
|
|
15
|
+
"Security & Infrastructure": [
|
|
16
|
+
"security-review",
|
|
17
|
+
"local-security",
|
|
18
|
+
"container-security",
|
|
19
|
+
"docker-kubernetes",
|
|
20
|
+
"ci-cd-pipelines",
|
|
21
|
+
"ci-cd-automation",
|
|
22
|
+
"monitoring-observability",
|
|
23
|
+
"incident-response",
|
|
24
|
+
],
|
|
25
|
+
"Languages & Frameworks": [
|
|
26
|
+
"golang-pro",
|
|
27
|
+
"go-linter-configuration",
|
|
28
|
+
"typescript",
|
|
29
|
+
"typescript-advanced",
|
|
30
|
+
"python-best-practices",
|
|
31
|
+
"rust-best-practices",
|
|
32
|
+
"frontend-design",
|
|
33
|
+
"fullstack-developer",
|
|
34
|
+
"remotion-best-practices",
|
|
35
|
+
"npm-package",
|
|
36
|
+
],
|
|
37
|
+
"API, Data & Docs": [
|
|
38
|
+
"api-design",
|
|
39
|
+
"api-testing",
|
|
40
|
+
"programming-architecture",
|
|
41
|
+
"database-design",
|
|
42
|
+
"env-config",
|
|
43
|
+
"cost-optimization",
|
|
44
|
+
"docx",
|
|
45
|
+
"xlsx",
|
|
46
|
+
"doc-generation",
|
|
47
|
+
"update-docs",
|
|
48
|
+
],
|
|
49
|
+
"Game Design & Production": [
|
|
50
|
+
"game-design-theory",
|
|
51
|
+
"game-engines",
|
|
52
|
+
"game-programming-languages",
|
|
53
|
+
"gameplay-mechanics",
|
|
54
|
+
"level-design",
|
|
55
|
+
"game-tools-workflows",
|
|
56
|
+
"game-servers",
|
|
57
|
+
"networking-servers",
|
|
58
|
+
"synchronization-algorithms",
|
|
59
|
+
"monetization-systems",
|
|
60
|
+
"publishing-platforms",
|
|
61
|
+
"daw-music",
|
|
62
|
+
],
|
|
63
|
+
"Graphics, Audio & Performance": [
|
|
64
|
+
"graphics-rendering",
|
|
65
|
+
"shader-techniques",
|
|
66
|
+
"particle-systems",
|
|
67
|
+
"audio-systems",
|
|
68
|
+
"asset-optimization",
|
|
69
|
+
"optimization-performance",
|
|
70
|
+
"memory-management",
|
|
71
|
+
],
|
|
72
|
+
"Skill Development": ["skill-creation-guide", "skill-creator", "find-skills", "project-migration"],
|
|
73
|
+
};
|
|
74
|
+
export { SKILL_CATEGORIES };
|
|
75
|
+
export function getCategoryFor(skillName) {
|
|
76
|
+
for (const [cat, skills] of Object.entries(SKILL_CATEGORIES)) {
|
|
77
|
+
if (skills.includes(skillName))
|
|
78
|
+
return cat;
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
export function getRelatedSkills(skillName, limit = 3) {
|
|
83
|
+
const cat = getCategoryFor(skillName);
|
|
84
|
+
if (!cat)
|
|
85
|
+
return [];
|
|
86
|
+
return (SKILL_CATEGORIES[cat] ?? []).filter((s) => s !== skillName && !isSkillInstalled(s)).slice(0, limit);
|
|
87
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function checkHealth(): Promise<void>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { runDoctorChecks } from "../commands/doctor.js";
|
|
4
|
+
import { handleCancel } from "./helpers.js";
|
|
5
|
+
export async function checkHealth() {
|
|
6
|
+
const checks = runDoctorChecks();
|
|
7
|
+
p.log.step(chalk.bold("Environment Health Check"));
|
|
8
|
+
for (const check of checks) {
|
|
9
|
+
const icon = check.status === "pass" ? chalk.green("OK") : check.status === "warn" ? chalk.yellow("!!") : chalk.red("XX");
|
|
10
|
+
p.log.info(`${icon} ${chalk.bold(check.name)}: ${check.message}`);
|
|
11
|
+
if (check.fix)
|
|
12
|
+
p.log.info(chalk.dim(` Fix: ${check.fix}`));
|
|
13
|
+
}
|
|
14
|
+
const fails = checks.filter((c) => c.status === "fail").length;
|
|
15
|
+
const warns = checks.filter((c) => c.status === "warn").length;
|
|
16
|
+
if (fails > 0) {
|
|
17
|
+
p.log.error(`${fails} issue${fails > 1 ? "s" : ""} found`);
|
|
18
|
+
}
|
|
19
|
+
else if (warns > 0) {
|
|
20
|
+
p.log.warn(`${warns} warning${warns > 1 ? "s" : ""}`);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
p.log.success("All checks passed");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Offer fixes once
|
|
27
|
+
const fixChecks = checks.filter((c) => c.fix && c.status !== "pass");
|
|
28
|
+
if (fixChecks.length === 0)
|
|
29
|
+
return;
|
|
30
|
+
const fixOptions = fixChecks.map((c) => {
|
|
31
|
+
const cmd = c.fix.replace(/^Run:\s*/, "");
|
|
32
|
+
return { value: cmd, label: `Run: ${cmd}`, hint: c.name };
|
|
33
|
+
});
|
|
34
|
+
const fixAction = await p.select({
|
|
35
|
+
message: "Run a fix?",
|
|
36
|
+
options: [...fixOptions, { value: "__skip", label: "Skip" }],
|
|
37
|
+
});
|
|
38
|
+
handleCancel(fixAction);
|
|
39
|
+
if (fixAction !== "__skip") {
|
|
40
|
+
const cmd = fixAction;
|
|
41
|
+
const SAFE_PREFIXES = ["arcana ", "git config "];
|
|
42
|
+
if (!SAFE_PREFIXES.some((pre) => cmd.startsWith(pre))) {
|
|
43
|
+
p.log.warn(`Skipped unsafe command: ${cmd}`);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
p.log.info(chalk.dim(`Running: ${cmd}`));
|
|
47
|
+
try {
|
|
48
|
+
const { execSync } = await import("node:child_process");
|
|
49
|
+
execSync(cmd, { stdio: "inherit" });
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Non-zero exit expected for some commands
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
p.log.info(chalk.dim("Run health check again to verify."));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const AMBER: import("chalk").ChalkInstance;
|
|
2
|
+
export declare function cancelAndExit(): never;
|
|
3
|
+
export declare function handleCancel(value: unknown): void;
|
|
4
|
+
export declare function countInstalled(): number;
|
|
5
|
+
export declare function truncate(str: string, max: number): string;
|
|
6
|
+
export declare function getInstalledNames(): string[];
|
|
7
|
+
export declare function buildMenuOptions(installedCount: number, _availableCount: number): {
|
|
8
|
+
value: string;
|
|
9
|
+
label: string;
|
|
10
|
+
hint?: string;
|
|
11
|
+
}[];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { getInstallDir } from "../utils/fs.js";
|
|
6
|
+
import { clearProviderCache } from "../registry.js";
|
|
7
|
+
export const AMBER = chalk.hex("#d4943a");
|
|
8
|
+
export function cancelAndExit() {
|
|
9
|
+
clearProviderCache();
|
|
10
|
+
p.cancel("Goodbye.");
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
13
|
+
export function handleCancel(value) {
|
|
14
|
+
if (p.isCancel(value))
|
|
15
|
+
cancelAndExit();
|
|
16
|
+
}
|
|
17
|
+
export function countInstalled() {
|
|
18
|
+
const dir = getInstallDir();
|
|
19
|
+
if (!existsSync(dir))
|
|
20
|
+
return 0;
|
|
21
|
+
return readdirSync(dir).filter((d) => {
|
|
22
|
+
try {
|
|
23
|
+
return statSync(join(dir, d)).isDirectory();
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}).length;
|
|
29
|
+
}
|
|
30
|
+
export function truncate(str, max) {
|
|
31
|
+
return str.length > max ? str.slice(0, max) + "..." : str;
|
|
32
|
+
}
|
|
33
|
+
export function getInstalledNames() {
|
|
34
|
+
const dir = getInstallDir();
|
|
35
|
+
if (!existsSync(dir))
|
|
36
|
+
return [];
|
|
37
|
+
return readdirSync(dir)
|
|
38
|
+
.filter((d) => {
|
|
39
|
+
try {
|
|
40
|
+
return statSync(join(dir, d)).isDirectory();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
.sort();
|
|
47
|
+
}
|
|
48
|
+
export function buildMenuOptions(installedCount, _availableCount) {
|
|
49
|
+
const isNew = installedCount === 0;
|
|
50
|
+
const options = [];
|
|
51
|
+
if (isNew) {
|
|
52
|
+
options.push({ value: "setup", label: AMBER("Get Started"), hint: "detect project, install recommended skills" });
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
options.push({ value: "installed", label: "Manage installed skills", hint: `${installedCount} installed` });
|
|
56
|
+
}
|
|
57
|
+
options.push({ value: "browse", label: "Browse skills by category" });
|
|
58
|
+
options.push({ value: "search", label: "Search for a skill" });
|
|
59
|
+
if (!isNew) {
|
|
60
|
+
options.push({ value: "setup", label: "Get Started", hint: "detect project, add more skills" });
|
|
61
|
+
}
|
|
62
|
+
options.push({ value: "health", label: "Check environment health" });
|
|
63
|
+
options.push({ value: "ref", label: "CLI reference" });
|
|
64
|
+
options.push({ value: "exit", label: "Exit" });
|
|
65
|
+
return options;
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { showInteractiveMenu } from "./menu.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { showInteractiveMenu } from "./menu.js";
|