@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/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);
|
|
@@ -127,6 +127,9 @@ export function createCli() {
|
|
|
127
127
|
.option("-a, --all", "Validate all installed skills")
|
|
128
128
|
.option("-f, --fix", "Auto-fix common issues")
|
|
129
129
|
.option("-j, --json", "Output as JSON")
|
|
130
|
+
.option("--source <dir>", "Validate from source directory instead of install dir")
|
|
131
|
+
.option("--cross", "Run cross-validation (marketplace sync, companions, orphans)")
|
|
132
|
+
.option("--min-score <n>", "Minimum quality score (0-100), fail if any skill scores below", parseInt)
|
|
130
133
|
.action(async (skill, opts) => {
|
|
131
134
|
const { validateCommand } = await import("./commands/validate.js");
|
|
132
135
|
return validateCommand(skill, opts);
|
|
@@ -184,6 +187,8 @@ export function createCli() {
|
|
|
184
187
|
.command("compact")
|
|
185
188
|
.description("Remove agent logs while preserving main session history")
|
|
186
189
|
.option("-n, --dry-run", "Show what would be removed without deleting")
|
|
190
|
+
.option("--prune", "Also prune oversized main sessions (>14d old AND >10 MB)")
|
|
191
|
+
.option("--prune-days <days>", "Override prune age threshold (default: 14)", parseInt)
|
|
187
192
|
.option("-j, --json", "Output as JSON")
|
|
188
193
|
.action(async (opts) => {
|
|
189
194
|
const { compactCommand } = await import("./commands/compact.js");
|
|
@@ -211,6 +216,7 @@ export function createCli() {
|
|
|
211
216
|
.description("Audit skill quality (code examples, BAD/GOOD pairs, structure)")
|
|
212
217
|
.option("-a, --all", "Audit all installed skills")
|
|
213
218
|
.option("-j, --json", "Output as JSON")
|
|
219
|
+
.option("--source <dir>", "Audit from source directory instead of install dir")
|
|
214
220
|
.action(async (skill, opts) => {
|
|
215
221
|
const { auditCommand } = await import("./commands/audit.js");
|
|
216
222
|
return auditCommand(skill, opts);
|
|
@@ -233,6 +239,115 @@ export function createCli() {
|
|
|
233
239
|
const { optimizeCommand } = await import("./commands/optimize.js");
|
|
234
240
|
return optimizeCommand(opts);
|
|
235
241
|
});
|
|
242
|
+
// === Security ===
|
|
243
|
+
program
|
|
244
|
+
.command("verify [skill]")
|
|
245
|
+
.description("Verify installed skill integrity against lockfile")
|
|
246
|
+
.option("-a, --all", "Verify all installed skills")
|
|
247
|
+
.option("-j, --json", "Output as JSON")
|
|
248
|
+
.addHelpText("after", "\nExamples:\n arcana verify code-reviewer\n arcana verify --all\n arcana verify --all --json")
|
|
249
|
+
.action(async (skill, opts) => {
|
|
250
|
+
const { verifyCommand } = await import("./commands/verify.js");
|
|
251
|
+
return verifyCommand(skill, opts);
|
|
252
|
+
});
|
|
253
|
+
program
|
|
254
|
+
.command("lock")
|
|
255
|
+
.description("Generate or validate lockfile from installed skills")
|
|
256
|
+
.option("--ci", "Validate lockfile matches installed skills (CI mode)")
|
|
257
|
+
.option("-j, --json", "Output as JSON")
|
|
258
|
+
.addHelpText("after", "\nExamples:\n arcana lock\n arcana lock --ci\n arcana lock --json")
|
|
259
|
+
.action(async (opts) => {
|
|
260
|
+
const { lockCommand } = await import("./commands/lock.js");
|
|
261
|
+
return lockCommand(opts);
|
|
262
|
+
});
|
|
263
|
+
// === Performance & Inspection ===
|
|
264
|
+
program
|
|
265
|
+
.command("benchmark [skill]")
|
|
266
|
+
.description("Measure token cost of installed skills")
|
|
267
|
+
.option("-a, --all", "Benchmark all installed skills")
|
|
268
|
+
.option("-j, --json", "Output as JSON")
|
|
269
|
+
.addHelpText("after", "\nExamples:\n arcana benchmark code-reviewer\n arcana benchmark --all")
|
|
270
|
+
.action(async (skill, opts) => {
|
|
271
|
+
const { benchmarkCommand } = await import("./commands/benchmark.js");
|
|
272
|
+
return benchmarkCommand(skill, opts);
|
|
273
|
+
});
|
|
274
|
+
program
|
|
275
|
+
.command("diff <skill>")
|
|
276
|
+
.description("Show changes between installed and remote skill version")
|
|
277
|
+
.option("-p, --provider <name>", "Provider to compare against")
|
|
278
|
+
.option("-j, --json", "Output as JSON")
|
|
279
|
+
.addHelpText("after", "\nExamples:\n arcana diff code-reviewer\n arcana diff code-reviewer --json")
|
|
280
|
+
.action(async (skill, opts) => {
|
|
281
|
+
const { diffCommand } = await import("./commands/diff.js");
|
|
282
|
+
return diffCommand(skill, opts);
|
|
283
|
+
});
|
|
284
|
+
program
|
|
285
|
+
.command("outdated")
|
|
286
|
+
.description("List skills with newer versions available")
|
|
287
|
+
.option("-p, --provider <name>", "Check against specific provider")
|
|
288
|
+
.option("-j, --json", "Output as JSON")
|
|
289
|
+
.action(async (opts) => {
|
|
290
|
+
const { outdatedCommand } = await import("./commands/outdated.js");
|
|
291
|
+
return outdatedCommand(opts);
|
|
292
|
+
});
|
|
293
|
+
// === Workflow & Team ===
|
|
294
|
+
program
|
|
295
|
+
.command("profile [action] [name] [skills...]")
|
|
296
|
+
.description("Manage skill profiles (named sets of skills)")
|
|
297
|
+
.option("-j, --json", "Output as JSON")
|
|
298
|
+
.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")
|
|
299
|
+
.action(async (action, name, skills, opts) => {
|
|
300
|
+
const { profileCommand } = await import("./commands/profile.js");
|
|
301
|
+
return profileCommand(action, name, skills, opts);
|
|
302
|
+
});
|
|
303
|
+
program
|
|
304
|
+
.command("team [action] [skill]")
|
|
305
|
+
.description("Manage shared team skill configuration")
|
|
306
|
+
.option("-j, --json", "Output as JSON")
|
|
307
|
+
.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")
|
|
308
|
+
.action(async (action, skill, opts) => {
|
|
309
|
+
const { teamCommand } = await import("./commands/team.js");
|
|
310
|
+
return teamCommand(action, skill, opts);
|
|
311
|
+
});
|
|
312
|
+
program
|
|
313
|
+
.command("export")
|
|
314
|
+
.description("Export installed skills as a manifest")
|
|
315
|
+
.option("--sbom", "Export as SPDX-lite software bill of materials")
|
|
316
|
+
.option("-j, --json", "Output as JSON (default)")
|
|
317
|
+
.action(async (opts) => {
|
|
318
|
+
const { exportCommand } = await import("./commands/export-cmd.js");
|
|
319
|
+
return exportCommand(opts);
|
|
320
|
+
});
|
|
321
|
+
program
|
|
322
|
+
.command("import <file>")
|
|
323
|
+
.description("Import and install skills from a manifest file")
|
|
324
|
+
.option("-f, --force", "Reinstall even if already installed")
|
|
325
|
+
.option("-j, --json", "Output as JSON")
|
|
326
|
+
.addHelpText("after", "\nExamples:\n arcana import manifest.json\n arcana import manifest.json --force")
|
|
327
|
+
.action(async (file, opts) => {
|
|
328
|
+
const { importCommand } = await import("./commands/import-cmd.js");
|
|
329
|
+
return importCommand(file, opts);
|
|
330
|
+
});
|
|
331
|
+
program
|
|
332
|
+
.command("completions <shell>")
|
|
333
|
+
.description("Generate shell completion scripts")
|
|
334
|
+
.option("-j, --json", "Output as JSON")
|
|
335
|
+
.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")
|
|
336
|
+
.action(async (shell, opts) => {
|
|
337
|
+
const { completionsCommand } = await import("./commands/completions.js");
|
|
338
|
+
return completionsCommand(shell, opts);
|
|
339
|
+
});
|
|
340
|
+
// ── Smart Recommendations ──────────────────────────────────────
|
|
341
|
+
program
|
|
342
|
+
.command("recommend")
|
|
343
|
+
.description("Get smart skill recommendations for current project")
|
|
344
|
+
.option("-j, --json", "Output as JSON")
|
|
345
|
+
.option("-l, --limit <n>", "Max results per category", parseInt)
|
|
346
|
+
.option("-p, --provider <name>", "Limit to provider")
|
|
347
|
+
.addHelpText("after", "\nExamples:\n arcana recommend\n arcana recommend --json\n arcana recommend --limit 5")
|
|
348
|
+
.action(async (opts) => {
|
|
349
|
+
const { recommendCommand } = await import("./commands/recommend.js");
|
|
350
|
+
return recommendCommand(opts);
|
|
351
|
+
});
|
|
236
352
|
return program;
|
|
237
353
|
}
|
|
238
|
-
//# 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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
interface AuditResult {
|
|
1
|
+
export interface AuditResult {
|
|
2
2
|
skill: string;
|
|
3
3
|
rating: "PERFECT" | "STRONG" | "ADEQUATE" | "WEAK";
|
|
4
4
|
score: number;
|
|
@@ -12,6 +12,5 @@ export declare function auditSkill(skillDir: string, skillName: string): AuditRe
|
|
|
12
12
|
export declare function auditCommand(skill: string | undefined, opts: {
|
|
13
13
|
all?: boolean;
|
|
14
14
|
json?: boolean;
|
|
15
|
+
source?: string;
|
|
15
16
|
}): Promise<void>;
|
|
16
|
-
export {};
|
|
17
|
-
//# sourceMappingURL=audit.d.ts.map
|
package/dist/commands/audit.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
3
|
import { getInstallDir } from "../utils/fs.js";
|
|
4
4
|
import { extractFrontmatter, parseFrontmatter } from "../utils/frontmatter.js";
|
|
5
5
|
import { ui, banner } from "../utils/ui.js";
|
|
@@ -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,12 +69,32 @@ 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
|
+
// 9. Section diversity (3+ unique ## headings)
|
|
80
|
+
const uniqueHeadings = new Set((body.match(/^## .+$/gm) || []).map((h) => h.toLowerCase()));
|
|
81
|
+
const goodDiversity = uniqueHeadings.size >= 3;
|
|
82
|
+
checks.push({
|
|
83
|
+
name: "Section diversity (3+ unique headings)",
|
|
84
|
+
passed: goodDiversity,
|
|
85
|
+
detail: `${uniqueHeadings.size} unique sections`,
|
|
86
|
+
});
|
|
87
|
+
if (goodDiversity)
|
|
88
|
+
score += 5;
|
|
89
|
+
// 10. Numbered steps (task decomposition signal)
|
|
90
|
+
const numberedSteps = (body.match(/^\d+\.\s/gm) || []).length;
|
|
91
|
+
const hasSteps = numberedSteps >= 3;
|
|
92
|
+
checks.push({ name: "Has numbered steps (3+)", passed: hasSteps, detail: `${numberedSteps} steps` });
|
|
93
|
+
if (hasSteps)
|
|
94
|
+
score += 5;
|
|
95
|
+
// Rating (max possible: 110)
|
|
72
96
|
let rating;
|
|
73
|
-
if (score >=
|
|
97
|
+
if (score >= 90)
|
|
74
98
|
rating = "PERFECT";
|
|
75
99
|
else if (score >= 65)
|
|
76
100
|
rating = "STRONG";
|
|
@@ -83,8 +107,8 @@ export function auditSkill(skillDir, skillName) {
|
|
|
83
107
|
export async function auditCommand(skill, opts) {
|
|
84
108
|
if (!opts.json)
|
|
85
109
|
banner();
|
|
86
|
-
const
|
|
87
|
-
if (!existsSync(
|
|
110
|
+
const baseDir = opts.source ? resolve(opts.source) : getInstallDir();
|
|
111
|
+
if (!existsSync(baseDir)) {
|
|
88
112
|
if (opts.json) {
|
|
89
113
|
console.log(JSON.stringify({ results: [] }));
|
|
90
114
|
}
|
|
@@ -96,7 +120,14 @@ export async function auditCommand(skill, opts) {
|
|
|
96
120
|
}
|
|
97
121
|
let skills;
|
|
98
122
|
if (opts.all) {
|
|
99
|
-
skills = readdirSync(
|
|
123
|
+
skills = readdirSync(baseDir).filter((d) => {
|
|
124
|
+
try {
|
|
125
|
+
return statSync(join(baseDir, d)).isDirectory();
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
100
131
|
}
|
|
101
132
|
else if (skill) {
|
|
102
133
|
skills = [skill];
|
|
@@ -115,7 +146,7 @@ export async function auditCommand(skill, opts) {
|
|
|
115
146
|
}
|
|
116
147
|
const results = [];
|
|
117
148
|
for (const name of skills.sort()) {
|
|
118
|
-
const skillDir = join(
|
|
149
|
+
const skillDir = join(baseDir, name);
|
|
119
150
|
if (!existsSync(skillDir)) {
|
|
120
151
|
results.push({ skill: name, rating: "WEAK", score: 0, checks: [{ name: "Exists", passed: false }] });
|
|
121
152
|
continue;
|
|
@@ -129,11 +160,14 @@ export async function auditCommand(skill, opts) {
|
|
|
129
160
|
const counts = { PERFECT: 0, STRONG: 0, ADEQUATE: 0, WEAK: 0 };
|
|
130
161
|
for (const r of results) {
|
|
131
162
|
counts[r.rating]++;
|
|
132
|
-
const ratingColor = r.rating === "PERFECT"
|
|
133
|
-
|
|
134
|
-
|
|
163
|
+
const ratingColor = r.rating === "PERFECT"
|
|
164
|
+
? ui.success
|
|
165
|
+
: r.rating === "STRONG"
|
|
166
|
+
? ui.cyan
|
|
167
|
+
: r.rating === "ADEQUATE"
|
|
168
|
+
? ui.warn
|
|
135
169
|
: ui.error;
|
|
136
|
-
console.log(` ${ratingColor(`[${r.rating}]`)} ${ui.bold(r.skill)} ${ui.dim(`(${r.score}/
|
|
170
|
+
console.log(` ${ratingColor(`[${r.rating}]`)} ${ui.bold(r.skill)} ${ui.dim(`(${r.score}/110)`)}`);
|
|
137
171
|
for (const check of r.checks) {
|
|
138
172
|
if (!check.passed) {
|
|
139
173
|
const detail = check.detail ? ` ${ui.dim(`(${check.detail})`)}` : "";
|
|
@@ -154,4 +188,3 @@ export async function auditCommand(skill, opts) {
|
|
|
154
188
|
console.log(` ${parts.join(ui.dim(" | "))}`);
|
|
155
189
|
console.log();
|
|
156
190
|
}
|
|
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
|
+
}
|
package/dist/commands/clean.d.ts
CHANGED
package/dist/commands/clean.js
CHANGED
|
@@ -4,10 +4,8 @@ import { homedir } from "node:os";
|
|
|
4
4
|
import { ui, banner } from "../utils/ui.js";
|
|
5
5
|
import { getDirSize, listSymlinks, isOrphanedProject } from "../utils/fs.js";
|
|
6
6
|
import { clearHistory } from "../utils/history.js";
|
|
7
|
+
import { STALE_PROJECT_DAYS, AGENT_LOG_MAX_AGE_DAYS, MAIN_LOG_MAX_AGE_DAYS } from "../constants.js";
|
|
7
8
|
const AUXILIARY_DIRS = ["file-history", "debug", "shell-snapshots", "todos", "plans"];
|
|
8
|
-
const STALE_PROJECT_DAYS = 90;
|
|
9
|
-
const AGENT_LOG_MAX_AGE_DAYS = 7;
|
|
10
|
-
const MAIN_LOG_MAX_AGE_DAYS = 30;
|
|
11
9
|
function purgeDir(dir, dryRun) {
|
|
12
10
|
if (!existsSync(dir))
|
|
13
11
|
return 0;
|
|
@@ -20,7 +18,9 @@ function purgeDir(dir, dryRun) {
|
|
|
20
18
|
if (!dryRun)
|
|
21
19
|
rmSync(full, { recursive: true, force: true });
|
|
22
20
|
}
|
|
23
|
-
catch {
|
|
21
|
+
catch {
|
|
22
|
+
/* skip locked files */
|
|
23
|
+
}
|
|
24
24
|
}
|
|
25
25
|
return reclaimed;
|
|
26
26
|
}
|
|
@@ -53,7 +53,7 @@ export async function cleanCommand(opts) {
|
|
|
53
53
|
const failedSymlinks = [];
|
|
54
54
|
if (!opts.json)
|
|
55
55
|
console.log(ui.bold(" Symlinks"));
|
|
56
|
-
for (const link of listSymlinks().filter(s => s.broken)) {
|
|
56
|
+
for (const link of listSymlinks().filter((s) => s.broken)) {
|
|
57
57
|
if (!dryRun) {
|
|
58
58
|
try {
|
|
59
59
|
rmSync(link.fullPath);
|
|
@@ -161,7 +161,13 @@ export async function cleanCommand(opts) {
|
|
|
161
161
|
const reason = isAgent ? "agent log" : "main session";
|
|
162
162
|
if (!opts.json)
|
|
163
163
|
console.log(` ${ui.dim("Remove:")} ${entry}/${file} ${ui.dim(`(${sizeMB.toFixed(1)} MB, ${Math.floor(daysOld)}d, ${reason})`)}`);
|
|
164
|
-
result.removedSessionLogs.push({
|
|
164
|
+
result.removedSessionLogs.push({
|
|
165
|
+
project: entry,
|
|
166
|
+
file,
|
|
167
|
+
sizeMB: sizeMB.toFixed(1),
|
|
168
|
+
daysOld: Math.floor(daysOld),
|
|
169
|
+
reason,
|
|
170
|
+
});
|
|
165
171
|
logCount++;
|
|
166
172
|
result.actions++;
|
|
167
173
|
}
|
|
@@ -187,7 +193,13 @@ export async function cleanCommand(opts) {
|
|
|
187
193
|
const mb = (size / (1024 * 1024)).toFixed(1);
|
|
188
194
|
if (!opts.json)
|
|
189
195
|
console.log(` ${ui.dim("Remove:")} ${entry}/${sub}/ ${ui.dim(`(${mb} MB, ${Math.floor(daysOld)}d, session dir)`)}`);
|
|
190
|
-
result.removedSessionLogs.push({
|
|
196
|
+
result.removedSessionLogs.push({
|
|
197
|
+
project: entry,
|
|
198
|
+
file: sub + "/",
|
|
199
|
+
sizeMB: mb,
|
|
200
|
+
daysOld: Math.floor(daysOld),
|
|
201
|
+
reason: "session dir",
|
|
202
|
+
});
|
|
191
203
|
logCount++;
|
|
192
204
|
result.actions++;
|
|
193
205
|
}
|
|
@@ -262,4 +274,3 @@ export async function cleanCommand(opts) {
|
|
|
262
274
|
}
|
|
263
275
|
console.log();
|
|
264
276
|
}
|
|
265
|
-
//# sourceMappingURL=clean.js.map
|