@sporesec/arcana 3.0.2 → 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.
Files changed (102) hide show
  1. package/dist/cli.js +25 -298
  2. package/dist/command-defs.d.ts +28 -0
  3. package/dist/command-defs.js +414 -0
  4. package/dist/commands/audit.js +18 -4
  5. package/dist/commands/clean.d.ts +1 -0
  6. package/dist/commands/clean.js +80 -0
  7. package/dist/commands/compress.d.ts +5 -0
  8. package/dist/commands/compress.js +38 -0
  9. package/dist/commands/config.js +40 -26
  10. package/dist/commands/create.js +2 -0
  11. package/dist/commands/curate.d.ts +39 -0
  12. package/dist/commands/curate.js +222 -0
  13. package/dist/commands/diff.js +2 -0
  14. package/dist/commands/doctor.d.ts +1 -0
  15. package/dist/commands/doctor.js +61 -2
  16. package/dist/commands/import-cmd.js +5 -0
  17. package/dist/commands/index.d.ts +5 -0
  18. package/dist/commands/index.js +107 -0
  19. package/dist/commands/info.js +19 -8
  20. package/dist/commands/init.d.ts +3 -0
  21. package/dist/commands/init.js +71 -0
  22. package/dist/commands/install.js +2 -0
  23. package/dist/commands/list.js +8 -0
  24. package/dist/commands/load.d.ts +10 -0
  25. package/dist/commands/load.js +130 -0
  26. package/dist/commands/lock.js +35 -24
  27. package/dist/commands/mcp.d.ts +4 -0
  28. package/dist/commands/mcp.js +87 -0
  29. package/dist/commands/outdated.js +8 -6
  30. package/dist/commands/providers.js +29 -21
  31. package/dist/commands/recommend.js +11 -3
  32. package/dist/commands/remember.d.ts +12 -0
  33. package/dist/commands/remember.js +111 -0
  34. package/dist/commands/scan.d.ts +2 -0
  35. package/dist/commands/scan.js +46 -8
  36. package/dist/commands/search.js +6 -0
  37. package/dist/commands/uninstall.js +36 -0
  38. package/dist/commands/update.js +27 -0
  39. package/dist/commands/validate.js +8 -0
  40. package/dist/commands/verify.js +2 -0
  41. package/dist/compress/engine.d.ts +21 -0
  42. package/dist/compress/engine.js +106 -0
  43. package/dist/compress/index.d.ts +7 -0
  44. package/dist/compress/index.js +10 -0
  45. package/dist/compress/rules/generic.d.ts +1 -0
  46. package/dist/compress/rules/generic.js +9 -0
  47. package/dist/compress/rules/git.d.ts +1 -0
  48. package/dist/compress/rules/git.js +113 -0
  49. package/dist/compress/rules/npm.d.ts +1 -0
  50. package/dist/compress/rules/npm.js +99 -0
  51. package/dist/compress/rules/test-runner.d.ts +1 -0
  52. package/dist/compress/rules/test-runner.js +103 -0
  53. package/dist/compress/rules/tsc.d.ts +1 -0
  54. package/dist/compress/rules/tsc.js +39 -0
  55. package/dist/compress/tracker.d.ts +16 -0
  56. package/dist/compress/tracker.js +45 -0
  57. package/dist/constants.d.ts +12 -0
  58. package/dist/constants.js +29 -0
  59. package/dist/interactive/helpers.js +1 -0
  60. package/dist/interactive/menu.js +6 -1
  61. package/dist/interactive/optimize-flow.js +4 -4
  62. package/dist/mcp/install.d.ts +10 -0
  63. package/dist/mcp/install.js +109 -0
  64. package/dist/mcp/registry.d.ts +11 -0
  65. package/dist/mcp/registry.js +27 -0
  66. package/dist/providers/anthropics.d.ts +4 -0
  67. package/dist/providers/anthropics.js +10 -0
  68. package/dist/registry.js +4 -0
  69. package/dist/session/trim.d.ts +23 -0
  70. package/dist/session/trim.js +132 -0
  71. package/dist/utils/cache.js +2 -2
  72. package/dist/utils/config.d.ts +2 -0
  73. package/dist/utils/config.js +33 -14
  74. package/dist/utils/help.js +16 -8
  75. package/dist/utils/install-core.js +23 -1
  76. package/dist/utils/memory.d.ts +25 -0
  77. package/dist/utils/memory.js +103 -0
  78. package/dist/utils/project-context.js +4 -0
  79. package/dist/utils/scanner.d.ts +22 -1
  80. package/dist/utils/scanner.js +81 -9
  81. package/dist/utils/sessions.d.ts +2 -0
  82. package/dist/utils/sessions.js +36 -0
  83. package/dist/utils/ui.js +5 -0
  84. package/dist/utils/usage.d.ts +17 -0
  85. package/dist/utils/usage.js +83 -0
  86. package/package.json +42 -7
  87. package/dist/command-registry.d.ts +0 -10
  88. package/dist/command-registry.js +0 -65
  89. package/dist/commands/benchmark.d.ts +0 -4
  90. package/dist/commands/benchmark.js +0 -178
  91. package/dist/commands/compact.d.ts +0 -6
  92. package/dist/commands/compact.js +0 -239
  93. package/dist/commands/optimize.d.ts +0 -3
  94. package/dist/commands/optimize.js +0 -356
  95. package/dist/commands/profile.d.ts +0 -3
  96. package/dist/commands/profile.js +0 -274
  97. package/dist/commands/stats.d.ts +0 -3
  98. package/dist/commands/stats.js +0 -210
  99. package/dist/commands/team.d.ts +0 -3
  100. package/dist/commands/team.js +0 -291
  101. package/dist/interactive.d.ts +0 -1
  102. package/dist/interactive.js +0 -841
@@ -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
- else {
45
- p.log.info("No skills installed. Lockfile written with 0 entries.");
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
- else {
79
- p.log.success(`Lockfile written with ${entries.length} entries.`);
80
- p.outro(chalk.dim("~/.arcana/arcana-lock.json"));
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
- else {
104
- printErrorWithHint(new Error("No lockfile found. Run `arcana lock` first to generate one."), true);
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 (valid) {
152
- p.log.success("Lockfile matches installed state.");
153
- p.outro(chalk.dim("All entries verified."));
167
+ if (mismatches.length > 0) {
168
+ p.log.error(`Hash mismatch: ${mismatches.join(", ")}`);
154
169
  }
155
- else {
156
- if (mismatches.length > 0) {
157
- p.log.error(`Hash mismatch: ${mismatches.join(", ")}`);
158
- }
159
- if (missing.length > 0) {
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,4 @@
1
+ export declare function mcpCommand(action: string, name: string | undefined, opts: {
2
+ tool?: string;
3
+ json?: boolean;
4
+ }): Promise<void>;
@@ -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
- else {
40
- console.log("No skills installed.");
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
- else {
56
- console.error("No providers configured. Run: arcana providers --add owner/repo");
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
- else {
27
- s.fail(`Could not find marketplace.json at ${opts.add}`);
28
- console.log(ui.dim(" Ensure the repo has .claude-plugin/marketplace.json"));
29
- console.log();
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
- else {
40
- console.log(ui.success(` Added provider: ${name}`));
41
- console.log(ui.dim(` Use: arcana list --provider ${name}`));
42
- console.log();
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
- else {
52
- console.log(ui.error(" Cannot remove the default provider."));
53
- console.log();
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
- if (removed) {
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
- else {
38
- p.log.error("No skills available from any provider.");
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
+ }
@@ -1,4 +1,6 @@
1
1
  export declare function scanCommand(skill: string | undefined, opts: {
2
2
  all?: boolean;
3
3
  json?: boolean;
4
+ strict?: boolean;
5
+ verbose?: boolean;
4
6
  }): Promise<void>;