@sporesec/arcana 2.3.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +140 -10
- package/dist/command-registry.d.ts +10 -0
- package/dist/command-registry.js +65 -0
- package/dist/commands/audit.d.ts +0 -1
- package/dist/commands/audit.js +16 -6
- package/dist/commands/benchmark.d.ts +4 -0
- package/dist/commands/benchmark.js +178 -0
- package/dist/commands/clean.d.ts +2 -1
- package/dist/commands/clean.js +198 -47
- package/dist/commands/compact.d.ts +6 -0
- package/dist/commands/compact.js +239 -0
- 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 +153 -24
- 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 +156 -117
- 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 +3 -0
- package/dist/commands/optimize.js +356 -0
- 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 +83 -16
- 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 +0 -1
- package/dist/commands/validate.js +14 -6
- 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 +37 -10
- package/dist/utils/fs.d.ts +19 -1
- package/dist/utils/fs.js +105 -8
- 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/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 +19 -7
- 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/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/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/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -5,10 +5,7 @@ const require = createRequire(import.meta.url);
|
|
|
5
5
|
const pkg = require("../package.json");
|
|
6
6
|
export function createCli() {
|
|
7
7
|
const program = new Command();
|
|
8
|
-
program
|
|
9
|
-
.name("arcana")
|
|
10
|
-
.description("Universal AI development CLI")
|
|
11
|
-
.version(pkg.version);
|
|
8
|
+
program.name("arcana").description("Universal AI development CLI").version(pkg.version);
|
|
12
9
|
// Store default help formatter before overriding
|
|
13
10
|
const defaultFormatHelp = program.createHelp().formatHelp.bind(program.createHelp());
|
|
14
11
|
program.configureHelp({
|
|
@@ -36,8 +33,8 @@ export function createCli() {
|
|
|
36
33
|
if (cmd) {
|
|
37
34
|
console.error();
|
|
38
35
|
console.error(ui.error(" Error: ") + `unknown command '${cmd}'`);
|
|
39
|
-
const
|
|
40
|
-
const match =
|
|
36
|
+
const { findClosestCommand } = await import("./command-registry.js");
|
|
37
|
+
const match = findClosestCommand(cmd);
|
|
41
38
|
if (match) {
|
|
42
39
|
console.error(ui.dim(` Did you mean '${match}'?`));
|
|
43
40
|
}
|
|
@@ -55,7 +52,7 @@ export function createCli() {
|
|
|
55
52
|
showWelcome(pkg.version);
|
|
56
53
|
markInitialized();
|
|
57
54
|
}
|
|
58
|
-
const { showInteractiveMenu } = await import("./interactive.js");
|
|
55
|
+
const { showInteractiveMenu } = await import("./interactive/index.js");
|
|
59
56
|
await showInteractiveMenu(pkg.version);
|
|
60
57
|
});
|
|
61
58
|
program
|
|
@@ -79,6 +76,7 @@ export function createCli() {
|
|
|
79
76
|
.option("-f, --force", "Reinstall even if already installed")
|
|
80
77
|
.option("--dry-run", "Show what would be installed without installing")
|
|
81
78
|
.option("-j, --json", "Output as JSON")
|
|
79
|
+
.option("--no-check", "Skip conflict detection")
|
|
82
80
|
.addHelpText("after", "\nExamples:\n arcana install code-reviewer\n arcana install skill1 skill2 skill3\n arcana install --all --force")
|
|
83
81
|
.action(async (skills, opts) => {
|
|
84
82
|
const { installCommand } = await import("./commands/install.js");
|
|
@@ -98,8 +96,10 @@ export function createCli() {
|
|
|
98
96
|
.description("Search for skills across providers")
|
|
99
97
|
.option("-p, --provider <name>", "Limit search to provider")
|
|
100
98
|
.option("--no-cache", "Bypass skill cache")
|
|
99
|
+
.option("-t, --tag <tag>", "Filter by tech stack tag")
|
|
100
|
+
.option("-s, --smart", "Context-aware ranking (uses project detection)")
|
|
101
101
|
.option("-j, --json", "Output as JSON")
|
|
102
|
-
.addHelpText("after",
|
|
102
|
+
.addHelpText("after", '\nExamples:\n arcana search testing\n arcana search "code review"\n arcana search react --tag typescript\n arcana search api --smart')
|
|
103
103
|
.action(async (query, opts) => {
|
|
104
104
|
const { searchCommand } = await import("./commands/search.js");
|
|
105
105
|
return searchCommand(query, opts);
|
|
@@ -171,13 +171,26 @@ export function createCli() {
|
|
|
171
171
|
});
|
|
172
172
|
program
|
|
173
173
|
.command("clean")
|
|
174
|
-
.description("Remove orphaned data and temp files")
|
|
174
|
+
.description("Remove orphaned data, old session logs, and temp files")
|
|
175
175
|
.option("-n, --dry-run", "Show what would be removed without deleting")
|
|
176
|
+
.option("--aggressive", "Delete all session logs regardless of age")
|
|
177
|
+
.option("--keep-days <days>", "Keep main session logs newer than N days (default: 30)", parseInt)
|
|
176
178
|
.option("--json", "Output as JSON")
|
|
177
179
|
.action(async (opts) => {
|
|
178
180
|
const { cleanCommand } = await import("./commands/clean.js");
|
|
179
181
|
return cleanCommand(opts);
|
|
180
182
|
});
|
|
183
|
+
program
|
|
184
|
+
.command("compact")
|
|
185
|
+
.description("Remove agent logs while preserving main session history")
|
|
186
|
+
.option("-n, --dry-run", "Show what would be removed without deleting")
|
|
187
|
+
.option("--prune", "Also prune oversized main sessions (>14d old AND >10 MB)")
|
|
188
|
+
.option("--prune-days <days>", "Override prune age threshold (default: 14)", parseInt)
|
|
189
|
+
.option("-j, --json", "Output as JSON")
|
|
190
|
+
.action(async (opts) => {
|
|
191
|
+
const { compactCommand } = await import("./commands/compact.js");
|
|
192
|
+
return compactCommand(opts);
|
|
193
|
+
});
|
|
181
194
|
program
|
|
182
195
|
.command("stats")
|
|
183
196
|
.description("Show session analytics and token usage")
|
|
@@ -214,6 +227,123 @@ export function createCli() {
|
|
|
214
227
|
const { scanCommand } = await import("./commands/scan.js");
|
|
215
228
|
return scanCommand(skill, opts);
|
|
216
229
|
});
|
|
230
|
+
program
|
|
231
|
+
.command("optimize")
|
|
232
|
+
.description("Analyze Claude Code setup and suggest token/performance improvements")
|
|
233
|
+
.option("-j, --json", "Output as JSON")
|
|
234
|
+
.action(async (opts) => {
|
|
235
|
+
const { optimizeCommand } = await import("./commands/optimize.js");
|
|
236
|
+
return optimizeCommand(opts);
|
|
237
|
+
});
|
|
238
|
+
// === Security ===
|
|
239
|
+
program
|
|
240
|
+
.command("verify [skill]")
|
|
241
|
+
.description("Verify installed skill integrity against lockfile")
|
|
242
|
+
.option("-a, --all", "Verify all installed skills")
|
|
243
|
+
.option("-j, --json", "Output as JSON")
|
|
244
|
+
.addHelpText("after", "\nExamples:\n arcana verify code-reviewer\n arcana verify --all\n arcana verify --all --json")
|
|
245
|
+
.action(async (skill, opts) => {
|
|
246
|
+
const { verifyCommand } = await import("./commands/verify.js");
|
|
247
|
+
return verifyCommand(skill, opts);
|
|
248
|
+
});
|
|
249
|
+
program
|
|
250
|
+
.command("lock")
|
|
251
|
+
.description("Generate or validate lockfile from installed skills")
|
|
252
|
+
.option("--ci", "Validate lockfile matches installed skills (CI mode)")
|
|
253
|
+
.option("-j, --json", "Output as JSON")
|
|
254
|
+
.addHelpText("after", "\nExamples:\n arcana lock\n arcana lock --ci\n arcana lock --json")
|
|
255
|
+
.action(async (opts) => {
|
|
256
|
+
const { lockCommand } = await import("./commands/lock.js");
|
|
257
|
+
return lockCommand(opts);
|
|
258
|
+
});
|
|
259
|
+
// === Performance & Inspection ===
|
|
260
|
+
program
|
|
261
|
+
.command("benchmark [skill]")
|
|
262
|
+
.description("Measure token cost of installed skills")
|
|
263
|
+
.option("-a, --all", "Benchmark all installed skills")
|
|
264
|
+
.option("-j, --json", "Output as JSON")
|
|
265
|
+
.addHelpText("after", "\nExamples:\n arcana benchmark code-reviewer\n arcana benchmark --all")
|
|
266
|
+
.action(async (skill, opts) => {
|
|
267
|
+
const { benchmarkCommand } = await import("./commands/benchmark.js");
|
|
268
|
+
return benchmarkCommand(skill, opts);
|
|
269
|
+
});
|
|
270
|
+
program
|
|
271
|
+
.command("diff <skill>")
|
|
272
|
+
.description("Show changes between installed and remote skill version")
|
|
273
|
+
.option("-p, --provider <name>", "Provider to compare against")
|
|
274
|
+
.option("-j, --json", "Output as JSON")
|
|
275
|
+
.addHelpText("after", "\nExamples:\n arcana diff code-reviewer\n arcana diff code-reviewer --json")
|
|
276
|
+
.action(async (skill, opts) => {
|
|
277
|
+
const { diffCommand } = await import("./commands/diff.js");
|
|
278
|
+
return diffCommand(skill, opts);
|
|
279
|
+
});
|
|
280
|
+
program
|
|
281
|
+
.command("outdated")
|
|
282
|
+
.description("List skills with newer versions available")
|
|
283
|
+
.option("-p, --provider <name>", "Check against specific provider")
|
|
284
|
+
.option("-j, --json", "Output as JSON")
|
|
285
|
+
.action(async (opts) => {
|
|
286
|
+
const { outdatedCommand } = await import("./commands/outdated.js");
|
|
287
|
+
return outdatedCommand(opts);
|
|
288
|
+
});
|
|
289
|
+
// === Workflow & Team ===
|
|
290
|
+
program
|
|
291
|
+
.command("profile [action] [name] [skills...]")
|
|
292
|
+
.description("Manage skill profiles (named sets of skills)")
|
|
293
|
+
.option("-j, --json", "Output as JSON")
|
|
294
|
+
.addHelpText("after", "\nActions:\n list List all profiles (default)\n create <name> Create a profile\n delete <name> Delete a profile\n show <name> Show skills in a profile\n apply <name> Install all skills from a profile\n\nExamples:\n arcana profile create security scan verify audit\n arcana profile apply security")
|
|
295
|
+
.action(async (action, name, skills, opts) => {
|
|
296
|
+
const { profileCommand } = await import("./commands/profile.js");
|
|
297
|
+
return profileCommand(action, name, skills, opts);
|
|
298
|
+
});
|
|
299
|
+
program
|
|
300
|
+
.command("team [action] [skill]")
|
|
301
|
+
.description("Manage shared team skill configuration")
|
|
302
|
+
.option("-j, --json", "Output as JSON")
|
|
303
|
+
.addHelpText("after", "\nActions:\n init Create .arcana/team.json\n sync Install skills from team config\n add <skill> Add a skill to team config\n remove <skill> Remove a skill from team config\n\nExamples:\n arcana team init\n arcana team add code-reviewer\n arcana team sync")
|
|
304
|
+
.action(async (action, skill, opts) => {
|
|
305
|
+
const { teamCommand } = await import("./commands/team.js");
|
|
306
|
+
return teamCommand(action, skill, opts);
|
|
307
|
+
});
|
|
308
|
+
program
|
|
309
|
+
.command("export")
|
|
310
|
+
.description("Export installed skills as a manifest")
|
|
311
|
+
.option("--sbom", "Export as SPDX-lite software bill of materials")
|
|
312
|
+
.option("-j, --json", "Output as JSON (default)")
|
|
313
|
+
.action(async (opts) => {
|
|
314
|
+
const { exportCommand } = await import("./commands/export-cmd.js");
|
|
315
|
+
return exportCommand(opts);
|
|
316
|
+
});
|
|
317
|
+
program
|
|
318
|
+
.command("import <file>")
|
|
319
|
+
.description("Import and install skills from a manifest file")
|
|
320
|
+
.option("-f, --force", "Reinstall even if already installed")
|
|
321
|
+
.option("-j, --json", "Output as JSON")
|
|
322
|
+
.addHelpText("after", "\nExamples:\n arcana import manifest.json\n arcana import manifest.json --force")
|
|
323
|
+
.action(async (file, opts) => {
|
|
324
|
+
const { importCommand } = await import("./commands/import-cmd.js");
|
|
325
|
+
return importCommand(file, opts);
|
|
326
|
+
});
|
|
327
|
+
program
|
|
328
|
+
.command("completions <shell>")
|
|
329
|
+
.description("Generate shell completion scripts")
|
|
330
|
+
.option("-j, --json", "Output as JSON")
|
|
331
|
+
.addHelpText("after", "\nSupported shells: bash, zsh, fish\n\nExamples:\n arcana completions bash >> ~/.bashrc\n arcana completions zsh >> ~/.zshrc\n arcana completions fish > ~/.config/fish/completions/arcana.fish")
|
|
332
|
+
.action(async (shell, opts) => {
|
|
333
|
+
const { completionsCommand } = await import("./commands/completions.js");
|
|
334
|
+
return completionsCommand(shell, opts);
|
|
335
|
+
});
|
|
336
|
+
// ── Smart Recommendations ──────────────────────────────────────
|
|
337
|
+
program
|
|
338
|
+
.command("recommend")
|
|
339
|
+
.description("Get smart skill recommendations for current project")
|
|
340
|
+
.option("-j, --json", "Output as JSON")
|
|
341
|
+
.option("-l, --limit <n>", "Max results per category", parseInt)
|
|
342
|
+
.option("-p, --provider <name>", "Limit to provider")
|
|
343
|
+
.addHelpText("after", "\nExamples:\n arcana recommend\n arcana recommend --json\n arcana recommend --limit 5")
|
|
344
|
+
.action(async (opts) => {
|
|
345
|
+
const { recommendCommand } = await import("./commands/recommend.js");
|
|
346
|
+
return recommendCommand(opts);
|
|
347
|
+
});
|
|
217
348
|
return program;
|
|
218
349
|
}
|
|
219
|
-
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface CommandEntry {
|
|
2
|
+
name: string;
|
|
3
|
+
usage: string;
|
|
4
|
+
description: string;
|
|
5
|
+
group: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function getCommandNames(): string[];
|
|
8
|
+
export declare function getGroupedCommands(): Record<string, CommandEntry[]>;
|
|
9
|
+
export declare function findClosestCommand(input: string): string | undefined;
|
|
10
|
+
export declare function getCliReference(): string;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const COMMANDS = [
|
|
2
|
+
// Getting Started
|
|
3
|
+
{ name: "init", usage: "init", description: "Initialize arcana in current project", group: "GETTING STARTED" },
|
|
4
|
+
{ name: "doctor", usage: "doctor", description: "Check environment and diagnose issues", group: "GETTING STARTED" },
|
|
5
|
+
// Skills
|
|
6
|
+
{ name: "list", usage: "list", description: "List available skills", group: "SKILLS" },
|
|
7
|
+
{ name: "search", usage: "search <query>", description: "Search across providers", group: "SKILLS" },
|
|
8
|
+
{ name: "info", usage: "info <skill>", description: "Show skill details", group: "SKILLS" },
|
|
9
|
+
{ name: "install", usage: "install [skills...]", description: "Install one or more skills", group: "SKILLS" },
|
|
10
|
+
{ name: "update", usage: "update [skills...]", description: "Update installed skills", group: "SKILLS" },
|
|
11
|
+
{ name: "uninstall", usage: "uninstall [skills...]", description: "Remove one or more skills", group: "SKILLS" },
|
|
12
|
+
{ name: "recommend", usage: "recommend", description: "Smart skill recommendations", group: "SKILLS" },
|
|
13
|
+
// Development
|
|
14
|
+
{ name: "create", usage: "create <name>", description: "Create a new skill from template", group: "DEVELOPMENT" },
|
|
15
|
+
{ name: "validate", usage: "validate [skill]", description: "Validate skill structure", group: "DEVELOPMENT" },
|
|
16
|
+
{ name: "audit", usage: "audit [skill]", description: "Audit skill quality", group: "DEVELOPMENT" },
|
|
17
|
+
// Security
|
|
18
|
+
{ name: "scan", usage: "scan [skill]", description: "Scan skills for security threats", group: "SECURITY" },
|
|
19
|
+
{ name: "verify", usage: "verify [skill]", description: "Verify skill integrity", group: "SECURITY" },
|
|
20
|
+
{ name: "lock", usage: "lock", description: "Generate or validate lockfile", group: "SECURITY" },
|
|
21
|
+
// Inspection
|
|
22
|
+
{ name: "benchmark", usage: "benchmark [skill]", description: "Measure token cost", group: "INSPECTION" },
|
|
23
|
+
{ name: "diff", usage: "diff <skill>", description: "Show installed vs remote changes", group: "INSPECTION" },
|
|
24
|
+
{ name: "outdated", usage: "outdated", description: "List skills with newer versions", group: "INSPECTION" },
|
|
25
|
+
// Configuration
|
|
26
|
+
{
|
|
27
|
+
name: "config",
|
|
28
|
+
usage: "config [key] [val]",
|
|
29
|
+
description: "View or modify configuration",
|
|
30
|
+
group: "CONFIGURATION",
|
|
31
|
+
},
|
|
32
|
+
{ name: "providers", usage: "providers", description: "Manage skill providers", group: "CONFIGURATION" },
|
|
33
|
+
{ name: "clean", usage: "clean", description: "Remove orphaned data", group: "CONFIGURATION" },
|
|
34
|
+
{ name: "compact", usage: "compact", description: "Remove agent logs", group: "CONFIGURATION" },
|
|
35
|
+
{ name: "stats", usage: "stats", description: "Show session analytics", group: "CONFIGURATION" },
|
|
36
|
+
{
|
|
37
|
+
name: "optimize",
|
|
38
|
+
usage: "optimize",
|
|
39
|
+
description: "Suggest token/performance improvements",
|
|
40
|
+
group: "CONFIGURATION",
|
|
41
|
+
},
|
|
42
|
+
// Team & Workflow
|
|
43
|
+
{ name: "profile", usage: "profile [action]", description: "Manage skill profiles", group: "WORKFLOW" },
|
|
44
|
+
{ name: "team", usage: "team [action]", description: "Shared team skill config", group: "WORKFLOW" },
|
|
45
|
+
{ name: "export", usage: "export", description: "Export installed skills manifest", group: "WORKFLOW" },
|
|
46
|
+
{ name: "import", usage: "import <file>", description: "Import skills from manifest", group: "WORKFLOW" },
|
|
47
|
+
{ name: "completions", usage: "completions <shell>", description: "Generate shell completions", group: "WORKFLOW" },
|
|
48
|
+
];
|
|
49
|
+
export function getCommandNames() {
|
|
50
|
+
return COMMANDS.map((c) => c.name);
|
|
51
|
+
}
|
|
52
|
+
export function getGroupedCommands() {
|
|
53
|
+
const groups = {};
|
|
54
|
+
for (const cmd of COMMANDS) {
|
|
55
|
+
(groups[cmd.group] ??= []).push(cmd);
|
|
56
|
+
}
|
|
57
|
+
return groups;
|
|
58
|
+
}
|
|
59
|
+
export function findClosestCommand(input) {
|
|
60
|
+
const prefix = input.slice(0, 3).toLowerCase();
|
|
61
|
+
return COMMANDS.find((c) => c.name.startsWith(prefix))?.name;
|
|
62
|
+
}
|
|
63
|
+
export function getCliReference() {
|
|
64
|
+
return COMMANDS.map((c) => `arcana ${c.usage}`).join("\n");
|
|
65
|
+
}
|
package/dist/commands/audit.d.ts
CHANGED
package/dist/commands/audit.js
CHANGED
|
@@ -53,7 +53,11 @@ export function auditSkill(skillDir, skillName) {
|
|
|
53
53
|
// 6. Not a capabilities list (detects pattern of many "- " lines with no code between them)
|
|
54
54
|
const bulletLines = (body.match(/^- /gm) || []).length;
|
|
55
55
|
const isCapabilityList = bulletLines > 20 && codeBlockCount < 3;
|
|
56
|
-
checks.push({
|
|
56
|
+
checks.push({
|
|
57
|
+
name: "Not a capabilities list",
|
|
58
|
+
passed: !isCapabilityList,
|
|
59
|
+
detail: isCapabilityList ? `${bulletLines} bullets, ${Math.floor(codeBlockCount)} code blocks` : undefined,
|
|
60
|
+
});
|
|
57
61
|
if (!isCapabilityList)
|
|
58
62
|
score += 15;
|
|
59
63
|
// 7. Reasonable length (50-500 lines)
|
|
@@ -65,7 +69,11 @@ export function auditSkill(skillDir, skillName) {
|
|
|
65
69
|
const hasScripts = existsSync(join(skillDir, "scripts"));
|
|
66
70
|
const hasRefs = existsSync(join(skillDir, "references")) || existsSync(join(skillDir, "rules"));
|
|
67
71
|
const hasExtras = hasScripts || hasRefs;
|
|
68
|
-
checks.push({
|
|
72
|
+
checks.push({
|
|
73
|
+
name: "Has scripts/ or references/",
|
|
74
|
+
passed: hasExtras,
|
|
75
|
+
detail: hasExtras ? [hasScripts && "scripts", hasRefs && "references"].filter(Boolean).join(", ") : "none",
|
|
76
|
+
});
|
|
69
77
|
if (hasExtras)
|
|
70
78
|
score += 10;
|
|
71
79
|
// Rating
|
|
@@ -129,9 +137,12 @@ export async function auditCommand(skill, opts) {
|
|
|
129
137
|
const counts = { PERFECT: 0, STRONG: 0, ADEQUATE: 0, WEAK: 0 };
|
|
130
138
|
for (const r of results) {
|
|
131
139
|
counts[r.rating]++;
|
|
132
|
-
const ratingColor = r.rating === "PERFECT"
|
|
133
|
-
|
|
134
|
-
|
|
140
|
+
const ratingColor = r.rating === "PERFECT"
|
|
141
|
+
? ui.success
|
|
142
|
+
: r.rating === "STRONG"
|
|
143
|
+
? ui.cyan
|
|
144
|
+
: r.rating === "ADEQUATE"
|
|
145
|
+
? ui.warn
|
|
135
146
|
: ui.error;
|
|
136
147
|
console.log(` ${ratingColor(`[${r.rating}]`)} ${ui.bold(r.skill)} ${ui.dim(`(${r.score}/100)`)}`);
|
|
137
148
|
for (const check of r.checks) {
|
|
@@ -154,4 +165,3 @@ export async function auditCommand(skill, opts) {
|
|
|
154
165
|
console.log(` ${parts.join(ui.dim(" | "))}`);
|
|
155
166
|
console.log();
|
|
156
167
|
}
|
|
157
|
-
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getInstallDir, readSkillMeta, getDirSize } from "../utils/fs.js";
|
|
4
|
+
import { CONTEXT_WINDOW_TOKENS } from "../constants.js";
|
|
5
|
+
function collectFiles(dir, prefix) {
|
|
6
|
+
const entries = [];
|
|
7
|
+
for (const entry of readdirSync(dir)) {
|
|
8
|
+
const fullPath = join(dir, entry);
|
|
9
|
+
const stat = statSync(fullPath);
|
|
10
|
+
if (stat.isDirectory()) {
|
|
11
|
+
entries.push(...collectFiles(fullPath, prefix ? `${prefix}/${entry}` : entry));
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
entries.push({ path: prefix ? `${prefix}/${entry}` : entry, sizeBytes: stat.size });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return entries;
|
|
18
|
+
}
|
|
19
|
+
function benchmarkSkill(skillName) {
|
|
20
|
+
const installDir = getInstallDir();
|
|
21
|
+
const skillDir = join(installDir, skillName);
|
|
22
|
+
try {
|
|
23
|
+
statSync(skillDir);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const files = collectFiles(skillDir, "");
|
|
29
|
+
const totalBytes = getDirSize(skillDir);
|
|
30
|
+
const estimatedTokens = Math.round(totalBytes / 4);
|
|
31
|
+
const contextPercent = (estimatedTokens / CONTEXT_WINDOW_TOKENS) * 100;
|
|
32
|
+
return {
|
|
33
|
+
name: skillName,
|
|
34
|
+
fileCount: files.length,
|
|
35
|
+
totalBytes,
|
|
36
|
+
estimatedTokens,
|
|
37
|
+
contextPercent,
|
|
38
|
+
files,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function formatKB(bytes) {
|
|
42
|
+
return (bytes / 1024).toFixed(1) + " KB";
|
|
43
|
+
}
|
|
44
|
+
function formatTokens(tokens) {
|
|
45
|
+
if (tokens >= 1_000_000)
|
|
46
|
+
return (tokens / 1_000_000).toFixed(1) + "M";
|
|
47
|
+
if (tokens >= 1_000)
|
|
48
|
+
return (tokens / 1_000).toFixed(1) + "k";
|
|
49
|
+
return String(tokens);
|
|
50
|
+
}
|
|
51
|
+
export async function benchmarkCommand(skill, opts) {
|
|
52
|
+
const installDir = getInstallDir();
|
|
53
|
+
if (skill) {
|
|
54
|
+
return benchmarkSingle(skill, opts.json);
|
|
55
|
+
}
|
|
56
|
+
if (opts.all) {
|
|
57
|
+
return benchmarkAll(installDir, opts.json);
|
|
58
|
+
}
|
|
59
|
+
console.error("Specify a skill name or use --all to benchmark all installed skills.");
|
|
60
|
+
console.error("Usage: arcana benchmark <skill-name>");
|
|
61
|
+
console.error(" arcana benchmark --all");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
function benchmarkSingle(skillName, json) {
|
|
65
|
+
const result = benchmarkSkill(skillName);
|
|
66
|
+
if (!result) {
|
|
67
|
+
if (json) {
|
|
68
|
+
console.log(JSON.stringify({ error: `Skill "${skillName}" not found` }));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.error(`Skill "${skillName}" is not installed.`);
|
|
72
|
+
}
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
const meta = readSkillMeta(skillName);
|
|
76
|
+
if (json) {
|
|
77
|
+
console.log(JSON.stringify({
|
|
78
|
+
name: result.name,
|
|
79
|
+
version: meta?.version ?? "unknown",
|
|
80
|
+
fileCount: result.fileCount,
|
|
81
|
+
totalBytes: result.totalBytes,
|
|
82
|
+
estimatedTokens: result.estimatedTokens,
|
|
83
|
+
contextPercent: Math.round(result.contextPercent * 100) / 100,
|
|
84
|
+
files: result.files.map((f) => ({
|
|
85
|
+
path: f.path,
|
|
86
|
+
sizeBytes: f.sizeBytes,
|
|
87
|
+
estimatedTokens: Math.round(f.sizeBytes / 4),
|
|
88
|
+
})),
|
|
89
|
+
}));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
console.log();
|
|
93
|
+
console.log(` Benchmark: ${skillName}${meta?.version ? ` v${meta.version}` : ""}`);
|
|
94
|
+
console.log();
|
|
95
|
+
console.log(` Files: ${result.fileCount}`);
|
|
96
|
+
console.log(` Total size: ${formatKB(result.totalBytes)}`);
|
|
97
|
+
console.log(` Est. tokens: ${formatTokens(result.estimatedTokens)}`);
|
|
98
|
+
console.log(` Context usage: ${result.contextPercent.toFixed(2)}% of ${(CONTEXT_WINDOW_TOKENS / 1000).toFixed(0)}k window`);
|
|
99
|
+
console.log();
|
|
100
|
+
console.log(" File breakdown:");
|
|
101
|
+
console.log();
|
|
102
|
+
const sorted = [...result.files].sort((a, b) => b.sizeBytes - a.sizeBytes);
|
|
103
|
+
const maxPathLen = Math.min(Math.max(...sorted.map((f) => f.path.length)), 50);
|
|
104
|
+
for (const file of sorted) {
|
|
105
|
+
const displayPath = file.path.length > 50 ? file.path.slice(0, 47) + "..." : file.path;
|
|
106
|
+
const tokens = Math.round(file.sizeBytes / 4);
|
|
107
|
+
console.log(` ${displayPath.padEnd(maxPathLen + 2)} ${formatKB(file.sizeBytes).padStart(10)} ~${formatTokens(tokens).padStart(6)} tokens`);
|
|
108
|
+
}
|
|
109
|
+
console.log();
|
|
110
|
+
}
|
|
111
|
+
function benchmarkAll(installDir, json) {
|
|
112
|
+
let dirs;
|
|
113
|
+
try {
|
|
114
|
+
dirs = readdirSync(installDir).filter((d) => {
|
|
115
|
+
try {
|
|
116
|
+
return statSync(join(installDir, d)).isDirectory();
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
dirs = [];
|
|
125
|
+
}
|
|
126
|
+
if (dirs.length === 0) {
|
|
127
|
+
if (json) {
|
|
128
|
+
console.log(JSON.stringify({ skills: [], totalTokens: 0, totalBytes: 0 }));
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
console.log("No skills installed.");
|
|
132
|
+
}
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const results = [];
|
|
136
|
+
for (const dir of dirs) {
|
|
137
|
+
const result = benchmarkSkill(dir);
|
|
138
|
+
if (result)
|
|
139
|
+
results.push(result);
|
|
140
|
+
}
|
|
141
|
+
results.sort((a, b) => b.estimatedTokens - a.estimatedTokens);
|
|
142
|
+
const totalBytes = results.reduce((sum, r) => sum + r.totalBytes, 0);
|
|
143
|
+
const totalTokens = results.reduce((sum, r) => sum + r.estimatedTokens, 0);
|
|
144
|
+
const totalContextPercent = (totalTokens / CONTEXT_WINDOW_TOKENS) * 100;
|
|
145
|
+
if (json) {
|
|
146
|
+
console.log(JSON.stringify({
|
|
147
|
+
skills: results.map((r) => ({
|
|
148
|
+
name: r.name,
|
|
149
|
+
fileCount: r.fileCount,
|
|
150
|
+
totalBytes: r.totalBytes,
|
|
151
|
+
estimatedTokens: r.estimatedTokens,
|
|
152
|
+
contextPercent: Math.round(r.contextPercent * 100) / 100,
|
|
153
|
+
})),
|
|
154
|
+
totalBytes,
|
|
155
|
+
totalTokens,
|
|
156
|
+
totalContextPercent: Math.round(totalContextPercent * 100) / 100,
|
|
157
|
+
}));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
console.log();
|
|
161
|
+
console.log(` Benchmark: ${results.length} installed skill(s)`);
|
|
162
|
+
console.log();
|
|
163
|
+
const maxNameLen = Math.min(Math.max(...results.map((r) => r.name.length)), 30);
|
|
164
|
+
console.log(` ${"Skill".padEnd(maxNameLen + 2)} ${"Files".padStart(5)} ${"Size".padStart(10)} ${"Tokens".padStart(8)} ${"Context %".padStart(9)}`);
|
|
165
|
+
console.log(` ${"-".repeat(maxNameLen + 2)} ${"-".repeat(5)} ${"-".repeat(10)} ${"-".repeat(8)} ${"-".repeat(9)}`);
|
|
166
|
+
for (const r of results) {
|
|
167
|
+
const displayName = r.name.length > 30 ? r.name.slice(0, 27) + "..." : r.name;
|
|
168
|
+
console.log(` ${displayName.padEnd(maxNameLen + 2)} ${String(r.fileCount).padStart(5)} ${formatKB(r.totalBytes).padStart(10)} ${formatTokens(r.estimatedTokens).padStart(8)} ${r.contextPercent.toFixed(2).padStart(8)}%`);
|
|
169
|
+
}
|
|
170
|
+
console.log(` ${"-".repeat(maxNameLen + 2)} ${"-".repeat(5)} ${"-".repeat(10)} ${"-".repeat(8)} ${"-".repeat(9)}`);
|
|
171
|
+
console.log(` ${"TOTAL".padEnd(maxNameLen + 2)} ${String(results.reduce((s, r) => s + r.fileCount, 0)).padStart(5)} ${formatKB(totalBytes).padStart(10)} ${formatTokens(totalTokens).padStart(8)} ${totalContextPercent.toFixed(2).padStart(8)}%`);
|
|
172
|
+
console.log();
|
|
173
|
+
if (totalContextPercent > 50) {
|
|
174
|
+
console.log(` Warning: installed skills consume ${totalContextPercent.toFixed(1)}% of context window.`);
|
|
175
|
+
console.log(" Consider removing unused skills with: arcana uninstall <skill>");
|
|
176
|
+
console.log();
|
|
177
|
+
}
|
|
178
|
+
}
|