@sporesec/arcana 3.0.3 → 4.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.js +25 -298
- package/dist/command-defs.d.ts +28 -0
- package/dist/command-defs.js +414 -0
- package/dist/commands/audit.js +18 -4
- package/dist/commands/clean.d.ts +1 -0
- package/dist/commands/clean.js +80 -0
- package/dist/commands/compress.d.ts +5 -0
- package/dist/commands/compress.js +38 -0
- package/dist/commands/config.js +40 -26
- package/dist/commands/create.js +2 -0
- package/dist/commands/curate.d.ts +39 -0
- package/dist/commands/curate.js +222 -0
- package/dist/commands/diff.js +2 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +61 -2
- package/dist/commands/import-cmd.js +5 -0
- package/dist/commands/index.d.ts +5 -0
- package/dist/commands/index.js +107 -0
- package/dist/commands/info.js +19 -8
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.js +71 -0
- package/dist/commands/install.js +2 -0
- package/dist/commands/list.js +8 -0
- package/dist/commands/load.d.ts +10 -0
- package/dist/commands/load.js +130 -0
- package/dist/commands/lock.js +35 -24
- package/dist/commands/mcp.d.ts +4 -0
- package/dist/commands/mcp.js +87 -0
- package/dist/commands/outdated.js +8 -6
- package/dist/commands/providers.js +29 -21
- package/dist/commands/recommend.js +11 -3
- package/dist/commands/remember.d.ts +12 -0
- package/dist/commands/remember.js +111 -0
- package/dist/commands/scan.d.ts +2 -0
- package/dist/commands/scan.js +46 -8
- package/dist/commands/search.js +6 -0
- package/dist/commands/uninstall.js +36 -0
- package/dist/commands/update.js +27 -0
- package/dist/commands/validate.js +8 -0
- package/dist/commands/verify.js +2 -0
- package/dist/compress/engine.d.ts +21 -0
- package/dist/compress/engine.js +106 -0
- package/dist/compress/index.d.ts +7 -0
- package/dist/compress/index.js +10 -0
- package/dist/compress/rules/generic.d.ts +1 -0
- package/dist/compress/rules/generic.js +9 -0
- package/dist/compress/rules/git.d.ts +1 -0
- package/dist/compress/rules/git.js +113 -0
- package/dist/compress/rules/npm.d.ts +1 -0
- package/dist/compress/rules/npm.js +99 -0
- package/dist/compress/rules/test-runner.d.ts +1 -0
- package/dist/compress/rules/test-runner.js +103 -0
- package/dist/compress/rules/tsc.d.ts +1 -0
- package/dist/compress/rules/tsc.js +39 -0
- package/dist/compress/tracker.d.ts +16 -0
- package/dist/compress/tracker.js +45 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.js +29 -0
- package/dist/interactive/helpers.js +1 -0
- package/dist/interactive/menu.js +6 -1
- package/dist/interactive/optimize-flow.js +4 -4
- package/dist/mcp/install.d.ts +10 -0
- package/dist/mcp/install.js +109 -0
- package/dist/mcp/registry.d.ts +11 -0
- package/dist/mcp/registry.js +27 -0
- package/dist/providers/anthropics.d.ts +4 -0
- package/dist/providers/anthropics.js +10 -0
- package/dist/registry.js +4 -0
- package/dist/session/trim.d.ts +23 -0
- package/dist/session/trim.js +132 -0
- package/dist/utils/cache.js +2 -2
- package/dist/utils/config.d.ts +2 -0
- package/dist/utils/config.js +33 -14
- package/dist/utils/help.js +16 -8
- package/dist/utils/install-core.js +23 -1
- package/dist/utils/memory.d.ts +25 -0
- package/dist/utils/memory.js +103 -0
- package/dist/utils/project-context.js +4 -0
- package/dist/utils/scanner.d.ts +22 -1
- package/dist/utils/scanner.js +81 -9
- package/dist/utils/sessions.d.ts +2 -0
- package/dist/utils/sessions.js +36 -0
- package/dist/utils/ui.js +5 -0
- package/dist/utils/usage.d.ts +17 -0
- package/dist/utils/usage.js +83 -0
- package/package.json +42 -7
- package/dist/command-registry.d.ts +0 -10
- package/dist/command-registry.js +0 -65
- package/dist/commands/benchmark.d.ts +0 -4
- package/dist/commands/benchmark.js +0 -178
- package/dist/commands/compact.d.ts +0 -6
- package/dist/commands/compact.js +0 -239
- package/dist/commands/optimize.d.ts +0 -3
- package/dist/commands/optimize.js +0 -356
- package/dist/commands/profile.d.ts +0 -3
- package/dist/commands/profile.js +0 -274
- package/dist/commands/stats.d.ts +0 -3
- package/dist/commands/stats.js +0 -210
- package/dist/commands/team.d.ts +0 -3
- package/dist/commands/team.js +0 -291
- package/dist/interactive.d.ts +0 -1
- package/dist/interactive.js +0 -841
package/dist/commands/lock.js
CHANGED
|
@@ -31,21 +31,25 @@ export async function lockCommand(opts) {
|
|
|
31
31
|
return generateMode(opts.json);
|
|
32
32
|
}
|
|
33
33
|
async function generateMode(json) {
|
|
34
|
+
/* v8 ignore start */
|
|
34
35
|
if (!json) {
|
|
35
36
|
console.log(renderBanner());
|
|
36
37
|
console.log();
|
|
37
38
|
p.intro(chalk.bold("Generate lockfile"));
|
|
38
39
|
}
|
|
40
|
+
/* v8 ignore stop */
|
|
39
41
|
const installDir = getInstallDir();
|
|
40
42
|
if (!existsSync(installDir)) {
|
|
41
43
|
if (json) {
|
|
42
44
|
console.log(JSON.stringify({ action: "generate", entries: 0, path: "~/.arcana/arcana-lock.json" }));
|
|
45
|
+
writeLockfile([]);
|
|
46
|
+
return;
|
|
43
47
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
48
|
+
/* v8 ignore start */
|
|
49
|
+
p.log.info("No skills installed. Lockfile written with 0 entries.");
|
|
47
50
|
writeLockfile([]);
|
|
48
51
|
return;
|
|
52
|
+
/* v8 ignore stop */
|
|
49
53
|
}
|
|
50
54
|
const dirs = readdirSync(installDir).filter((d) => {
|
|
51
55
|
try {
|
|
@@ -74,18 +78,21 @@ async function generateMode(json) {
|
|
|
74
78
|
writeLockfile(entries);
|
|
75
79
|
if (json) {
|
|
76
80
|
console.log(JSON.stringify({ action: "generate", entries: entries.length, path: "~/.arcana/arcana-lock.json" }));
|
|
81
|
+
return;
|
|
77
82
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
/* v8 ignore start */
|
|
84
|
+
p.log.success(`Lockfile written with ${entries.length} entries.`);
|
|
85
|
+
p.outro(chalk.dim("~/.arcana/arcana-lock.json"));
|
|
86
|
+
/* v8 ignore stop */
|
|
82
87
|
}
|
|
83
88
|
async function ciMode(json) {
|
|
89
|
+
/* v8 ignore start */
|
|
84
90
|
if (!json) {
|
|
85
91
|
console.log(renderBanner());
|
|
86
92
|
console.log();
|
|
87
93
|
p.intro(chalk.bold("Validate lockfile"));
|
|
88
94
|
}
|
|
95
|
+
/* v8 ignore stop */
|
|
89
96
|
const existing = readLockfile();
|
|
90
97
|
if (existing.length === 0) {
|
|
91
98
|
const lockPath = join(getInstallDir(), "..", "arcana-lock.json");
|
|
@@ -99,11 +106,12 @@ async function ciMode(json) {
|
|
|
99
106
|
extra: [],
|
|
100
107
|
error: "No lockfile found",
|
|
101
108
|
}));
|
|
109
|
+
process.exit(1);
|
|
102
110
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
111
|
+
/* v8 ignore start */
|
|
112
|
+
printErrorWithHint(new Error("No lockfile found. Run `arcana lock` first to generate one."), true);
|
|
106
113
|
process.exit(1);
|
|
114
|
+
/* v8 ignore stop */
|
|
107
115
|
}
|
|
108
116
|
}
|
|
109
117
|
const installDir = getInstallDir();
|
|
@@ -146,26 +154,29 @@ async function ciMode(json) {
|
|
|
146
154
|
const valid = mismatches.length === 0 && missing.length === 0 && extra.length === 0;
|
|
147
155
|
if (json) {
|
|
148
156
|
console.log(JSON.stringify({ action: "ci", valid, mismatches, missing, extra }));
|
|
157
|
+
if (!valid)
|
|
158
|
+
process.exit(1);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
/* v8 ignore start */
|
|
162
|
+
if (valid) {
|
|
163
|
+
p.log.success("Lockfile matches installed state.");
|
|
164
|
+
p.outro(chalk.dim("All entries verified."));
|
|
149
165
|
}
|
|
150
166
|
else {
|
|
151
|
-
if (
|
|
152
|
-
p.log.
|
|
153
|
-
p.outro(chalk.dim("All entries verified."));
|
|
167
|
+
if (mismatches.length > 0) {
|
|
168
|
+
p.log.error(`Hash mismatch: ${mismatches.join(", ")}`);
|
|
154
169
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
p.log.error(`Missing from disk: ${missing.join(", ")}`);
|
|
161
|
-
}
|
|
162
|
-
if (extra.length > 0) {
|
|
163
|
-
p.log.warn(`Extra (not in lockfile): ${extra.join(", ")}`);
|
|
164
|
-
}
|
|
165
|
-
p.outro(chalk.dim("Lockfile validation failed."));
|
|
170
|
+
if (missing.length > 0) {
|
|
171
|
+
p.log.error(`Missing from disk: ${missing.join(", ")}`);
|
|
172
|
+
}
|
|
173
|
+
if (extra.length > 0) {
|
|
174
|
+
p.log.warn(`Extra (not in lockfile): ${extra.join(", ")}`);
|
|
166
175
|
}
|
|
176
|
+
p.outro(chalk.dim("Lockfile validation failed."));
|
|
167
177
|
}
|
|
168
178
|
if (!valid) {
|
|
169
179
|
process.exit(1);
|
|
170
180
|
}
|
|
181
|
+
/* v8 ignore stop */
|
|
171
182
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { ui, banner } from "../utils/ui.js";
|
|
2
|
+
export async function mcpCommand(action, name, opts) {
|
|
3
|
+
if (action === "list") {
|
|
4
|
+
const { listRegistry } = await import("../mcp/registry.js");
|
|
5
|
+
const servers = listRegistry();
|
|
6
|
+
if (opts.json) {
|
|
7
|
+
console.log(JSON.stringify(servers));
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
/* v8 ignore start */
|
|
11
|
+
banner();
|
|
12
|
+
console.log(ui.bold(" Available MCP Servers\n"));
|
|
13
|
+
for (const s of servers) {
|
|
14
|
+
console.log(` ${ui.success(s.name)} ${s.description}`);
|
|
15
|
+
console.log(ui.dim(` ${s.command} ${s.args.join(" ")}`));
|
|
16
|
+
}
|
|
17
|
+
console.log();
|
|
18
|
+
return;
|
|
19
|
+
/* v8 ignore stop */
|
|
20
|
+
}
|
|
21
|
+
if (action === "install") {
|
|
22
|
+
if (!name) {
|
|
23
|
+
console.error("Usage: arcana mcp install <name>");
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const { installMcpServer } = await import("../mcp/install.js");
|
|
27
|
+
const tool = (opts.tool ?? "claude");
|
|
28
|
+
const result = installMcpServer(name, tool, process.cwd());
|
|
29
|
+
if (opts.json) {
|
|
30
|
+
console.log(JSON.stringify(result));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
/* v8 ignore start */
|
|
34
|
+
if (result.installed) {
|
|
35
|
+
console.log(`${ui.success("[OK]")} ${name} configured in ${result.path}`);
|
|
36
|
+
if (result.error)
|
|
37
|
+
console.log(ui.dim(` Note: ${result.error}`));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
console.log(`${ui.warn("[!!]")} ${result.error}`);
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
/* v8 ignore stop */
|
|
44
|
+
}
|
|
45
|
+
if (action === "remove") {
|
|
46
|
+
if (!name) {
|
|
47
|
+
console.error("Usage: arcana mcp remove <name>");
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
const { removeMcpServer } = await import("../mcp/install.js");
|
|
51
|
+
const tool = (opts.tool ?? "claude");
|
|
52
|
+
const ok = removeMcpServer(name, tool, process.cwd());
|
|
53
|
+
if (opts.json) {
|
|
54
|
+
console.log(JSON.stringify({ removed: ok, name }));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
/* v8 ignore start */
|
|
58
|
+
console.log(ok ? `${ui.success("[OK]")} Removed ${name}` : `${ui.warn("[!!]")} ${name} not found`);
|
|
59
|
+
return;
|
|
60
|
+
/* v8 ignore stop */
|
|
61
|
+
}
|
|
62
|
+
if (action === "status") {
|
|
63
|
+
const { listConfiguredServers } = await import("../mcp/install.js");
|
|
64
|
+
const tool = (opts.tool ?? "claude");
|
|
65
|
+
const servers = listConfiguredServers(tool, process.cwd());
|
|
66
|
+
if (opts.json) {
|
|
67
|
+
console.log(JSON.stringify({ tool, servers }));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
/* v8 ignore start */
|
|
71
|
+
banner();
|
|
72
|
+
console.log(ui.bold(` MCP Status (${tool})\n`));
|
|
73
|
+
if (servers.length === 0) {
|
|
74
|
+
console.log(ui.dim(" No MCP servers configured."));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
for (const s of servers) {
|
|
78
|
+
console.log(` ${ui.success("[OK]")} ${s}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
console.log();
|
|
82
|
+
return;
|
|
83
|
+
/* v8 ignore stop */
|
|
84
|
+
}
|
|
85
|
+
console.error("Usage: arcana mcp <list|install|remove|status> [name]");
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
@@ -35,10 +35,10 @@ export async function outdatedCommand(opts) {
|
|
|
35
35
|
if (skills.length === 0) {
|
|
36
36
|
if (opts.json) {
|
|
37
37
|
console.log(JSON.stringify({ outdated: [], upToDate: 0, total: 0 }));
|
|
38
|
+
process.exit(0);
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
40
|
+
/* v8 ignore next 2 */
|
|
41
|
+
console.log("No skills installed.");
|
|
42
42
|
process.exit(0);
|
|
43
43
|
}
|
|
44
44
|
const providerName = opts.provider ?? loadConfig().defaultProvider;
|
|
@@ -51,10 +51,10 @@ export async function outdatedCommand(opts) {
|
|
|
51
51
|
upToDate: 0,
|
|
52
52
|
total: 0,
|
|
53
53
|
}));
|
|
54
|
+
process.exit(0);
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
56
|
+
/* v8 ignore next 2 */
|
|
57
|
+
console.error("No providers configured. Run: arcana providers --add owner/repo");
|
|
58
58
|
process.exit(0);
|
|
59
59
|
}
|
|
60
60
|
const outdated = [];
|
|
@@ -116,6 +116,7 @@ export async function outdatedCommand(opts) {
|
|
|
116
116
|
console.log(JSON.stringify(result, null, 2));
|
|
117
117
|
process.exit(0);
|
|
118
118
|
}
|
|
119
|
+
/* v8 ignore start -- display-only console table output */
|
|
119
120
|
// Console output: aligned table
|
|
120
121
|
console.log(`Checked ${result.total} installed skills.`);
|
|
121
122
|
console.log();
|
|
@@ -156,4 +157,5 @@ export async function outdatedCommand(opts) {
|
|
|
156
157
|
console.log();
|
|
157
158
|
console.log(`${outdated.length} outdated, ${upToDate} up to date, ${result.total} total`);
|
|
158
159
|
process.exit(0);
|
|
160
|
+
/* v8 ignore stop */
|
|
159
161
|
}
|
|
@@ -3,11 +3,13 @@ import { loadConfig, addProvider, removeProvider } from "../utils/config.js";
|
|
|
3
3
|
import { httpGet } from "../utils/http.js";
|
|
4
4
|
import { parseProviderSlug, clearProviderCache } from "../registry.js";
|
|
5
5
|
export async function providersCommand(opts) {
|
|
6
|
+
/* v8 ignore next */
|
|
6
7
|
if (!opts.json)
|
|
7
8
|
banner();
|
|
8
9
|
if (opts.add) {
|
|
9
10
|
const { owner, repo } = parseProviderSlug(opts.add);
|
|
10
11
|
const name = `${owner}/${repo}`;
|
|
12
|
+
/* v8 ignore next */
|
|
11
13
|
const s = opts.json ? noopSpinner() : spinner(`Validating ${opts.add}...`);
|
|
12
14
|
s.start();
|
|
13
15
|
try {
|
|
@@ -22,54 +24,58 @@ export async function providersCommand(opts) {
|
|
|
22
24
|
catch {
|
|
23
25
|
if (opts.json) {
|
|
24
26
|
console.log(JSON.stringify({ error: `Could not find marketplace.json at ${opts.add}` }));
|
|
27
|
+
process.exit(1);
|
|
25
28
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
29
|
+
/* v8 ignore start */
|
|
30
|
+
s.fail(`Could not find marketplace.json at ${opts.add}`);
|
|
31
|
+
console.log(ui.dim(" Ensure the repo has .claude-plugin/marketplace.json"));
|
|
32
|
+
console.log();
|
|
31
33
|
process.exit(1);
|
|
34
|
+
/* v8 ignore stop */
|
|
32
35
|
}
|
|
33
36
|
}
|
|
34
37
|
addProvider({ name, type: "github", url: opts.add, enabled: true });
|
|
35
38
|
clearProviderCache();
|
|
36
39
|
if (opts.json) {
|
|
37
40
|
console.log(JSON.stringify({ action: "add", provider: name, success: true }));
|
|
41
|
+
return;
|
|
38
42
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
43
|
+
/* v8 ignore start */
|
|
44
|
+
console.log(ui.success(` Added provider: ${name}`));
|
|
45
|
+
console.log(ui.dim(` Use: arcana list --provider ${name}`));
|
|
46
|
+
console.log();
|
|
44
47
|
return;
|
|
48
|
+
/* v8 ignore stop */
|
|
45
49
|
}
|
|
46
50
|
if (opts.remove) {
|
|
47
51
|
if (opts.remove === "arcana") {
|
|
48
52
|
if (opts.json) {
|
|
49
53
|
console.log(JSON.stringify({ error: "Cannot remove the default provider" }));
|
|
54
|
+
process.exit(1);
|
|
50
55
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
56
|
+
/* v8 ignore start */
|
|
57
|
+
console.log(ui.error(" Cannot remove the default provider."));
|
|
58
|
+
console.log();
|
|
55
59
|
process.exit(1);
|
|
60
|
+
/* v8 ignore stop */
|
|
56
61
|
}
|
|
57
62
|
const removed = removeProvider(opts.remove);
|
|
58
63
|
if (removed)
|
|
59
64
|
clearProviderCache();
|
|
60
65
|
if (opts.json) {
|
|
61
66
|
console.log(JSON.stringify({ action: "remove", provider: opts.remove, success: removed }));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
/* v8 ignore start */
|
|
70
|
+
if (removed) {
|
|
71
|
+
console.log(ui.success(` Removed provider: ${opts.remove}`));
|
|
62
72
|
}
|
|
63
73
|
else {
|
|
64
|
-
|
|
65
|
-
console.log(ui.success(` Removed provider: ${opts.remove}`));
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
console.log(ui.error(` Provider "${opts.remove}" not found.`));
|
|
69
|
-
}
|
|
70
|
-
console.log();
|
|
74
|
+
console.log(ui.error(` Provider "${opts.remove}" not found.`));
|
|
71
75
|
}
|
|
76
|
+
console.log();
|
|
72
77
|
return;
|
|
78
|
+
/* v8 ignore stop */
|
|
73
79
|
}
|
|
74
80
|
const config = loadConfig();
|
|
75
81
|
if (opts.json) {
|
|
@@ -84,6 +90,7 @@ export async function providersCommand(opts) {
|
|
|
84
90
|
}));
|
|
85
91
|
return;
|
|
86
92
|
}
|
|
93
|
+
/* v8 ignore start */
|
|
87
94
|
console.log(ui.bold(" Configured providers:"));
|
|
88
95
|
console.log();
|
|
89
96
|
const rows = config.providers.map((p) => [
|
|
@@ -97,4 +104,5 @@ export async function providersCommand(opts) {
|
|
|
97
104
|
console.log(ui.dim(" Add: arcana providers --add owner/repo"));
|
|
98
105
|
console.log(ui.dim(" Remove: arcana providers --remove name"));
|
|
99
106
|
console.log();
|
|
107
|
+
/* v8 ignore stop */
|
|
100
108
|
}
|
|
@@ -6,6 +6,7 @@ import { getProviders } from "../registry.js";
|
|
|
6
6
|
export async function recommendCommand(opts) {
|
|
7
7
|
const cwd = process.cwd();
|
|
8
8
|
const context = detectProjectContext(cwd);
|
|
9
|
+
/* v8 ignore start */
|
|
9
10
|
if (!opts.json) {
|
|
10
11
|
p.intro(chalk.bold("Smart Recommendations"));
|
|
11
12
|
p.log.step(`Project: ${chalk.cyan(context.name)} (${context.type} / ${context.lang})`);
|
|
@@ -16,6 +17,7 @@ export async function recommendCommand(opts) {
|
|
|
16
17
|
p.log.info(`Rules found: ${context.ruleFiles.join(", ")}`);
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
/* v8 ignore stop */
|
|
19
21
|
// Fetch all skills from providers
|
|
20
22
|
const providers = getProviders(opts.provider);
|
|
21
23
|
const allSkills = [];
|
|
@@ -25,18 +27,20 @@ export async function recommendCommand(opts) {
|
|
|
25
27
|
allSkills.push(...skills);
|
|
26
28
|
}
|
|
27
29
|
catch (err) {
|
|
30
|
+
/* v8 ignore start */
|
|
28
31
|
if (!opts.json) {
|
|
29
32
|
p.log.warn(`Could not fetch from ${prov.displayName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
30
33
|
}
|
|
34
|
+
/* v8 ignore stop */
|
|
31
35
|
}
|
|
32
36
|
}
|
|
33
37
|
if (allSkills.length === 0) {
|
|
34
38
|
if (opts.json) {
|
|
35
39
|
console.log(JSON.stringify({ error: "No skills available" }));
|
|
40
|
+
process.exit(1);
|
|
36
41
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
42
|
+
/* v8 ignore next 2 */
|
|
43
|
+
p.log.error("No skills available from any provider.");
|
|
40
44
|
process.exit(1);
|
|
41
45
|
}
|
|
42
46
|
// Score and rank
|
|
@@ -57,6 +61,7 @@ export async function recommendCommand(opts) {
|
|
|
57
61
|
console.log(JSON.stringify(output, null, 2));
|
|
58
62
|
return;
|
|
59
63
|
}
|
|
64
|
+
/* v8 ignore start */
|
|
60
65
|
// Display results
|
|
61
66
|
if (recommended.length > 0) {
|
|
62
67
|
console.log();
|
|
@@ -86,11 +91,14 @@ export async function recommendCommand(opts) {
|
|
|
86
91
|
}
|
|
87
92
|
console.log();
|
|
88
93
|
p.outro(`Install: ${chalk.cyan("arcana install <skill>")}`);
|
|
94
|
+
/* v8 ignore stop */
|
|
89
95
|
}
|
|
96
|
+
/* v8 ignore start */
|
|
90
97
|
function printVerdict(v) {
|
|
91
98
|
const scoreStr = v.score > 0 ? `+${v.score}` : String(v.score);
|
|
92
99
|
console.log(` ${chalk.bold(v.skill.padEnd(28))} ${chalk.cyan(scoreStr.padStart(4))} ${chalk.dim(v.reasons.join(" | "))}`);
|
|
93
100
|
}
|
|
101
|
+
/* v8 ignore stop */
|
|
94
102
|
function formatVerdict(v) {
|
|
95
103
|
return { skill: v.skill, score: v.score, reasons: v.reasons };
|
|
96
104
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function rememberCommand(content: string[], opts: {
|
|
2
|
+
tag?: string[];
|
|
3
|
+
json?: boolean;
|
|
4
|
+
}): Promise<void>;
|
|
5
|
+
export declare function recallCommand(query: string[], opts: {
|
|
6
|
+
all?: boolean;
|
|
7
|
+
project?: string;
|
|
8
|
+
json?: boolean;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
export declare function forgetCommand(id: string, opts: {
|
|
11
|
+
json?: boolean;
|
|
12
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
import { addMemory, searchMemories, listMemories, removeMemory } from "../utils/memory.js";
|
|
3
|
+
import { ui, banner } from "../utils/ui.js";
|
|
4
|
+
export async function rememberCommand(content, opts) {
|
|
5
|
+
const text = content.join(" ").trim();
|
|
6
|
+
if (!text) {
|
|
7
|
+
if (opts.json) {
|
|
8
|
+
console.log(JSON.stringify({ error: "Provide content to remember" }));
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
/* v8 ignore next 2 */
|
|
12
|
+
console.error('Usage: arcana remember "your fact or preference"');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const memory = addMemory(text, { tags: opts.tag, project: basename(process.cwd()) });
|
|
16
|
+
if (opts.json) {
|
|
17
|
+
console.log(JSON.stringify(memory));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
/* v8 ignore start */
|
|
21
|
+
banner();
|
|
22
|
+
console.log(ui.bold(" Remember\n"));
|
|
23
|
+
console.log(` ${ui.success("[OK]")} Saved: "${text}"`);
|
|
24
|
+
console.log(ui.dim(` ID: ${memory.id} | Tags: ${memory.tags.join(", ")} | Project: ${memory.project}`));
|
|
25
|
+
console.log();
|
|
26
|
+
/* v8 ignore stop */
|
|
27
|
+
}
|
|
28
|
+
export async function recallCommand(query, opts) {
|
|
29
|
+
if (opts.all) {
|
|
30
|
+
const memories = listMemories({ project: opts.project });
|
|
31
|
+
if (opts.json) {
|
|
32
|
+
console.log(JSON.stringify(memories));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
/* v8 ignore start */
|
|
36
|
+
banner();
|
|
37
|
+
console.log(ui.bold(" Recall\n"));
|
|
38
|
+
if (memories.length === 0) {
|
|
39
|
+
console.log(ui.dim(' No memories stored. Use: arcana remember "your fact"'));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
for (const m of memories) {
|
|
43
|
+
console.log(` ${ui.success(m.id)} ${m.content}`);
|
|
44
|
+
console.log(ui.dim(` Tags: ${m.tags.join(", ")} | Project: ${m.project ?? "global"} | ${m.created.slice(0, 10)}`));
|
|
45
|
+
}
|
|
46
|
+
console.log();
|
|
47
|
+
console.log(ui.dim(` ${memories.length} memories total`));
|
|
48
|
+
}
|
|
49
|
+
console.log();
|
|
50
|
+
return;
|
|
51
|
+
/* v8 ignore stop */
|
|
52
|
+
}
|
|
53
|
+
const q = query.join(" ").trim();
|
|
54
|
+
if (!q) {
|
|
55
|
+
if (opts.json) {
|
|
56
|
+
console.log(JSON.stringify({ error: "Provide a search query or use --all" }));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
/* v8 ignore next 2 */
|
|
60
|
+
console.error('Usage: arcana recall "search query" or arcana recall --all');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const results = searchMemories(q, { project: opts.project });
|
|
64
|
+
if (opts.json) {
|
|
65
|
+
console.log(JSON.stringify(results));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
/* v8 ignore start */
|
|
69
|
+
banner();
|
|
70
|
+
console.log(ui.bold(" Recall\n"));
|
|
71
|
+
if (results.length === 0) {
|
|
72
|
+
console.log(ui.dim(` No memories matching "${q}"`));
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
for (const m of results) {
|
|
76
|
+
console.log(` ${ui.success(m.id)} ${m.content}`);
|
|
77
|
+
console.log(ui.dim(` Tags: ${m.tags.join(", ")} | Project: ${m.project ?? "global"} | ${m.created.slice(0, 10)}`));
|
|
78
|
+
}
|
|
79
|
+
console.log();
|
|
80
|
+
console.log(ui.dim(` ${results.length} result${results.length > 1 ? "s" : ""}`));
|
|
81
|
+
}
|
|
82
|
+
console.log();
|
|
83
|
+
/* v8 ignore stop */
|
|
84
|
+
}
|
|
85
|
+
export async function forgetCommand(id, opts) {
|
|
86
|
+
if (!id) {
|
|
87
|
+
if (opts.json) {
|
|
88
|
+
console.log(JSON.stringify({ error: "Provide a memory ID to forget" }));
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
/* v8 ignore next 2 */
|
|
92
|
+
console.error("Usage: arcana forget <id>");
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
const removed = removeMemory(id);
|
|
96
|
+
if (opts.json) {
|
|
97
|
+
console.log(JSON.stringify({ removed, id }));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
/* v8 ignore start */
|
|
101
|
+
banner();
|
|
102
|
+
console.log(ui.bold(" Forget\n"));
|
|
103
|
+
if (removed) {
|
|
104
|
+
console.log(` ${ui.success("[OK]")} Memory ${id} removed`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
console.log(` ${ui.warn("[!!]")} Memory ${id} not found`);
|
|
108
|
+
}
|
|
109
|
+
console.log();
|
|
110
|
+
/* v8 ignore stop */
|
|
111
|
+
}
|