@sporesec/arcana 2.2.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 (181) hide show
  1. package/dist/cli.d.ts +3 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +219 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/audit.d.ts +17 -0
  6. package/dist/commands/audit.d.ts.map +1 -0
  7. package/dist/commands/audit.js +157 -0
  8. package/dist/commands/audit.js.map +1 -0
  9. package/dist/commands/audit.test.d.ts +2 -0
  10. package/dist/commands/audit.test.d.ts.map +1 -0
  11. package/dist/commands/audit.test.js +217 -0
  12. package/dist/commands/audit.test.js.map +1 -0
  13. package/dist/commands/clean.d.ts +5 -0
  14. package/dist/commands/clean.d.ts.map +1 -0
  15. package/dist/commands/clean.js +125 -0
  16. package/dist/commands/clean.js.map +1 -0
  17. package/dist/commands/config.d.ts +4 -0
  18. package/dist/commands/config.d.ts.map +1 -0
  19. package/dist/commands/config.js +135 -0
  20. package/dist/commands/config.js.map +1 -0
  21. package/dist/commands/create.d.ts +2 -0
  22. package/dist/commands/create.d.ts.map +1 -0
  23. package/dist/commands/create.js +100 -0
  24. package/dist/commands/create.js.map +1 -0
  25. package/dist/commands/doctor.d.ts +6 -0
  26. package/dist/commands/doctor.d.ts.map +1 -0
  27. package/dist/commands/doctor.js +213 -0
  28. package/dist/commands/doctor.js.map +1 -0
  29. package/dist/commands/info.d.ts +5 -0
  30. package/dist/commands/info.d.ts.map +1 -0
  31. package/dist/commands/info.js +114 -0
  32. package/dist/commands/info.js.map +1 -0
  33. package/dist/commands/init.d.ts +13 -0
  34. package/dist/commands/init.d.ts.map +1 -0
  35. package/dist/commands/init.js +216 -0
  36. package/dist/commands/init.js.map +1 -0
  37. package/dist/commands/install.d.ts +8 -0
  38. package/dist/commands/install.d.ts.map +1 -0
  39. package/dist/commands/install.js +404 -0
  40. package/dist/commands/install.js.map +1 -0
  41. package/dist/commands/list.d.ts +8 -0
  42. package/dist/commands/list.d.ts.map +1 -0
  43. package/dist/commands/list.js +100 -0
  44. package/dist/commands/list.js.map +1 -0
  45. package/dist/commands/providers.d.ts +6 -0
  46. package/dist/commands/providers.d.ts.map +1 -0
  47. package/dist/commands/providers.js +103 -0
  48. package/dist/commands/providers.js.map +1 -0
  49. package/dist/commands/scan.d.ts +5 -0
  50. package/dist/commands/scan.d.ts.map +1 -0
  51. package/dist/commands/scan.js +110 -0
  52. package/dist/commands/scan.js.map +1 -0
  53. package/dist/commands/search.d.ts +6 -0
  54. package/dist/commands/search.d.ts.map +1 -0
  55. package/dist/commands/search.js +58 -0
  56. package/dist/commands/search.js.map +1 -0
  57. package/dist/commands/stats.d.ts +4 -0
  58. package/dist/commands/stats.d.ts.map +1 -0
  59. package/dist/commands/stats.js +143 -0
  60. package/dist/commands/stats.js.map +1 -0
  61. package/dist/commands/uninstall.d.ts +6 -0
  62. package/dist/commands/uninstall.d.ts.map +1 -0
  63. package/dist/commands/uninstall.js +163 -0
  64. package/dist/commands/uninstall.js.map +1 -0
  65. package/dist/commands/update.d.ts +7 -0
  66. package/dist/commands/update.d.ts.map +1 -0
  67. package/dist/commands/update.js +348 -0
  68. package/dist/commands/update.js.map +1 -0
  69. package/dist/commands/validate.d.ts +6 -0
  70. package/dist/commands/validate.d.ts.map +1 -0
  71. package/dist/commands/validate.js +140 -0
  72. package/dist/commands/validate.js.map +1 -0
  73. package/dist/index.d.ts +3 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +39 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/interactive.d.ts +2 -0
  78. package/dist/interactive.d.ts.map +1 -0
  79. package/dist/interactive.js +812 -0
  80. package/dist/interactive.js.map +1 -0
  81. package/dist/providers/arcana.d.ts +5 -0
  82. package/dist/providers/arcana.d.ts.map +1 -0
  83. package/dist/providers/arcana.js +11 -0
  84. package/dist/providers/arcana.js.map +1 -0
  85. package/dist/providers/base.d.ts +11 -0
  86. package/dist/providers/base.d.ts.map +1 -0
  87. package/dist/providers/base.js +10 -0
  88. package/dist/providers/base.js.map +1 -0
  89. package/dist/providers/github.d.ts +25 -0
  90. package/dist/providers/github.d.ts.map +1 -0
  91. package/dist/providers/github.js +146 -0
  92. package/dist/providers/github.js.map +1 -0
  93. package/dist/registry.d.ts +9 -0
  94. package/dist/registry.d.ts.map +1 -0
  95. package/dist/registry.js +71 -0
  96. package/dist/registry.js.map +1 -0
  97. package/dist/types.d.ts +67 -0
  98. package/dist/types.d.ts.map +1 -0
  99. package/dist/types.js +2 -0
  100. package/dist/types.js.map +1 -0
  101. package/dist/utils/atomic.d.ts +2 -0
  102. package/dist/utils/atomic.d.ts.map +1 -0
  103. package/dist/utils/atomic.js +20 -0
  104. package/dist/utils/atomic.js.map +1 -0
  105. package/dist/utils/atomic.test.d.ts +2 -0
  106. package/dist/utils/atomic.test.d.ts.map +1 -0
  107. package/dist/utils/atomic.test.js +31 -0
  108. package/dist/utils/atomic.test.js.map +1 -0
  109. package/dist/utils/cache.d.ts +4 -0
  110. package/dist/utils/cache.d.ts.map +1 -0
  111. package/dist/utils/cache.js +47 -0
  112. package/dist/utils/cache.js.map +1 -0
  113. package/dist/utils/config.d.ts +6 -0
  114. package/dist/utils/config.d.ts.map +1 -0
  115. package/dist/utils/config.js +90 -0
  116. package/dist/utils/config.js.map +1 -0
  117. package/dist/utils/config.test.d.ts +2 -0
  118. package/dist/utils/config.test.d.ts.map +1 -0
  119. package/dist/utils/config.test.js +38 -0
  120. package/dist/utils/config.test.js.map +1 -0
  121. package/dist/utils/errors.d.ts +6 -0
  122. package/dist/utils/errors.d.ts.map +1 -0
  123. package/dist/utils/errors.js +11 -0
  124. package/dist/utils/errors.js.map +1 -0
  125. package/dist/utils/frontmatter.d.ts +12 -0
  126. package/dist/utils/frontmatter.d.ts.map +1 -0
  127. package/dist/utils/frontmatter.js +172 -0
  128. package/dist/utils/frontmatter.js.map +1 -0
  129. package/dist/utils/frontmatter.test.d.ts +2 -0
  130. package/dist/utils/frontmatter.test.d.ts.map +1 -0
  131. package/dist/utils/frontmatter.test.js +152 -0
  132. package/dist/utils/frontmatter.test.js.map +1 -0
  133. package/dist/utils/fs.d.ts +16 -0
  134. package/dist/utils/fs.d.ts.map +1 -0
  135. package/dist/utils/fs.js +118 -0
  136. package/dist/utils/fs.js.map +1 -0
  137. package/dist/utils/fs.test.d.ts +2 -0
  138. package/dist/utils/fs.test.d.ts.map +1 -0
  139. package/dist/utils/fs.test.js +145 -0
  140. package/dist/utils/fs.test.js.map +1 -0
  141. package/dist/utils/help.d.ts +6 -0
  142. package/dist/utils/help.d.ts.map +1 -0
  143. package/dist/utils/help.js +117 -0
  144. package/dist/utils/help.js.map +1 -0
  145. package/dist/utils/help.test.d.ts +2 -0
  146. package/dist/utils/help.test.d.ts.map +1 -0
  147. package/dist/utils/help.test.js +66 -0
  148. package/dist/utils/help.test.js.map +1 -0
  149. package/dist/utils/history.d.ts +10 -0
  150. package/dist/utils/history.d.ts.map +1 -0
  151. package/dist/utils/history.js +58 -0
  152. package/dist/utils/history.js.map +1 -0
  153. package/dist/utils/http.d.ts +17 -0
  154. package/dist/utils/http.d.ts.map +1 -0
  155. package/dist/utils/http.js +165 -0
  156. package/dist/utils/http.js.map +1 -0
  157. package/dist/utils/http.test.d.ts +2 -0
  158. package/dist/utils/http.test.d.ts.map +1 -0
  159. package/dist/utils/http.test.js +55 -0
  160. package/dist/utils/http.test.js.map +1 -0
  161. package/dist/utils/parallel.d.ts +2 -0
  162. package/dist/utils/parallel.d.ts.map +1 -0
  163. package/dist/utils/parallel.js +17 -0
  164. package/dist/utils/parallel.js.map +1 -0
  165. package/dist/utils/scanner.d.ts +27 -0
  166. package/dist/utils/scanner.d.ts.map +1 -0
  167. package/dist/utils/scanner.js +195 -0
  168. package/dist/utils/scanner.js.map +1 -0
  169. package/dist/utils/ui.d.ts +27 -0
  170. package/dist/utils/ui.d.ts.map +1 -0
  171. package/dist/utils/ui.js +99 -0
  172. package/dist/utils/ui.js.map +1 -0
  173. package/dist/utils/ui.test.d.ts +2 -0
  174. package/dist/utils/ui.test.d.ts.map +1 -0
  175. package/dist/utils/ui.test.js +31 -0
  176. package/dist/utils/ui.test.js.map +1 -0
  177. package/dist/utils/validate.d.ts +2 -0
  178. package/dist/utils/validate.d.ts.map +1 -0
  179. package/dist/utils/validate.js +7 -0
  180. package/dist/utils/validate.js.map +1 -0
  181. package/package.json +62 -0
@@ -0,0 +1,118 @@
1
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, rmSync, renameSync, lstatSync, readlinkSync } from "node:fs";
2
+ import { join, dirname, resolve, sep } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { loadConfig } from "../utils/config.js";
5
+ import { atomicWriteSync } from "./atomic.js";
6
+ export function getInstallDir() {
7
+ return loadConfig().installDir;
8
+ }
9
+ export function getSkillDir(name) {
10
+ return join(getInstallDir(), name);
11
+ }
12
+ export function getDirSize(dir) {
13
+ let size = 0;
14
+ const queue = [dir];
15
+ while (queue.length > 0) {
16
+ const current = queue.pop();
17
+ try {
18
+ for (const entry of readdirSync(current)) {
19
+ const full = join(current, entry);
20
+ try {
21
+ const stat = lstatSync(full);
22
+ if (stat.isSymbolicLink())
23
+ continue;
24
+ if (stat.isDirectory())
25
+ queue.push(full);
26
+ else
27
+ size += stat.size;
28
+ }
29
+ catch { /* skip unreadable entries */ }
30
+ }
31
+ }
32
+ catch { /* skip unreadable dirs */ }
33
+ }
34
+ return size;
35
+ }
36
+ export function installSkill(skillName, files) {
37
+ const installDir = getInstallDir();
38
+ const skillDir = join(installDir, skillName);
39
+ const tempDir = `${skillDir}.installing.${process.pid}`;
40
+ // Crash recovery: remove leftover temp dir from a previous failed install
41
+ if (existsSync(tempDir)) {
42
+ rmSync(tempDir, { recursive: true, force: true });
43
+ }
44
+ mkdirSync(tempDir, { recursive: true });
45
+ try {
46
+ for (const file of files) {
47
+ // Reject paths containing .. before resolving
48
+ if (file.path.includes("..") || file.path.includes("~")) {
49
+ throw new Error(`Path traversal blocked: ${file.path}`);
50
+ }
51
+ const filePath = resolve(tempDir, file.path);
52
+ // Normalize to lowercase on Windows for case-insensitive comparison
53
+ const normalizedFile = process.platform === "win32" ? filePath.toLowerCase() : filePath;
54
+ const normalizedTemp = process.platform === "win32" ? (tempDir + sep).toLowerCase() : tempDir + sep;
55
+ if (!normalizedFile.startsWith(normalizedTemp) && normalizedFile !== (process.platform === "win32" ? tempDir.toLowerCase() : tempDir)) {
56
+ throw new Error(`Path traversal blocked: ${file.path}`);
57
+ }
58
+ const dir = dirname(filePath);
59
+ if (!existsSync(dir)) {
60
+ mkdirSync(dir, { recursive: true });
61
+ }
62
+ writeFileSync(filePath, file.content, { encoding: "utf-8", mode: 0o644 });
63
+ }
64
+ // Remove old skill dir if it exists, then atomically rename temp to final
65
+ if (existsSync(skillDir)) {
66
+ rmSync(skillDir, { recursive: true, force: true });
67
+ }
68
+ renameSync(tempDir, skillDir);
69
+ }
70
+ catch (err) {
71
+ // Clean up temp dir on failure
72
+ try {
73
+ rmSync(tempDir, { recursive: true, force: true });
74
+ }
75
+ catch { /* best-effort */ }
76
+ throw err;
77
+ }
78
+ return skillDir;
79
+ }
80
+ export function isSkillInstalled(skillName) {
81
+ return existsSync(join(getSkillDir(skillName), "SKILL.md"));
82
+ }
83
+ const META_FILE = ".arcana-meta.json";
84
+ export function readSkillMeta(skillName) {
85
+ const metaPath = join(getSkillDir(skillName), META_FILE);
86
+ if (!existsSync(metaPath))
87
+ return null;
88
+ try {
89
+ return JSON.parse(readFileSync(metaPath, "utf-8"));
90
+ }
91
+ catch {
92
+ return null;
93
+ }
94
+ }
95
+ export function writeSkillMeta(skillName, meta) {
96
+ const skillDir = getSkillDir(skillName);
97
+ mkdirSync(skillDir, { recursive: true });
98
+ atomicWriteSync(join(skillDir, META_FILE), JSON.stringify(meta, null, 2) + "\n", 0o644);
99
+ }
100
+ export function listSymlinks() {
101
+ const symlinkDir = join(homedir(), ".claude", "skills");
102
+ if (!existsSync(symlinkDir))
103
+ return [];
104
+ const results = [];
105
+ for (const entry of readdirSync(symlinkDir)) {
106
+ const fullPath = join(symlinkDir, entry);
107
+ try {
108
+ const stat = lstatSync(fullPath);
109
+ if (stat.isSymbolicLink()) {
110
+ const target = readlinkSync(fullPath);
111
+ results.push({ name: entry, fullPath, target, broken: !existsSync(target) });
112
+ }
113
+ }
114
+ catch { /* skip unreadable */ }
115
+ }
116
+ return results;
117
+ }
118
+ //# sourceMappingURL=fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fs.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.test.d.ts","sourceRoot":"","sources":["../../src/utils/fs.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,145 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, writeFileSync, mkdirSync, existsSync, rmSync, readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import { installSkill, isSkillInstalled, readSkillMeta, writeSkillMeta, getDirSize } from "./fs.js";
6
+ let testTempDir = null;
7
+ function makeTempBase() {
8
+ const dir = mkdtempSync(join(tmpdir(), "arcana-test-"));
9
+ testTempDir = dir;
10
+ return dir;
11
+ }
12
+ beforeEach(() => {
13
+ testTempDir = null;
14
+ });
15
+ afterEach(() => {
16
+ if (testTempDir && existsSync(testTempDir)) {
17
+ try {
18
+ rmSync(testTempDir, { recursive: true, force: true });
19
+ }
20
+ catch {
21
+ // Best-effort cleanup
22
+ }
23
+ }
24
+ });
25
+ describe("installSkill", () => {
26
+ it("blocks path traversal (file.path containing ../)", () => {
27
+ makeTempBase();
28
+ // Path traversal is validated before directory resolution,
29
+ // so this test works without mocking getInstallDir
30
+ const files = [
31
+ { path: "../escape/SKILL.md", content: "Malicious content" },
32
+ ];
33
+ expect(() => installSkill("test-skill", files)).toThrow("Path traversal blocked");
34
+ });
35
+ it("creates skill directory with correct files", () => {
36
+ const base = makeTempBase();
37
+ const files = [
38
+ { path: "SKILL.md", content: "---\nname: test\n---\nBody" },
39
+ { path: "scripts/helper.sh", content: "#!/bin/bash\necho test" },
40
+ { path: "references/doc.txt", content: "Documentation" },
41
+ ];
42
+ const skillDir = installSkill("test-skill", files);
43
+ expect(existsSync(join(skillDir, "SKILL.md"))).toBe(true);
44
+ expect(existsSync(join(skillDir, "scripts", "helper.sh"))).toBe(true);
45
+ expect(existsSync(join(skillDir, "references", "doc.txt"))).toBe(true);
46
+ const content = readFileSync(join(skillDir, "SKILL.md"), "utf-8");
47
+ expect(content).toBe("---\nname: test\n---\nBody");
48
+ });
49
+ it("removes temp dir on failure", () => {
50
+ const base = makeTempBase();
51
+ const files = [
52
+ { path: "../invalid", content: "Bad" },
53
+ ];
54
+ try {
55
+ installSkill("test-skill", files);
56
+ }
57
+ catch {
58
+ // Expected to fail
59
+ }
60
+ // Check that temp dir was cleaned up
61
+ const tempDir = join(base, "test-skill.installing");
62
+ expect(existsSync(tempDir)).toBe(false);
63
+ });
64
+ });
65
+ describe("isSkillInstalled", () => {
66
+ it("returns true when SKILL.md exists", () => {
67
+ const base = makeTempBase();
68
+ const skillDir = join(base, "test-skill");
69
+ mkdirSync(skillDir, { recursive: true });
70
+ writeFileSync(join(skillDir, "SKILL.md"), "content", "utf-8");
71
+ const result = isSkillInstalled("test-skill");
72
+ expect(result).toBe(true);
73
+ });
74
+ it("returns false when directory doesn't exist", () => {
75
+ makeTempBase();
76
+ const result = isSkillInstalled("nonexistent-skill");
77
+ expect(result).toBe(false);
78
+ });
79
+ });
80
+ describe("readSkillMeta", () => {
81
+ it("returns null for non-existent skill", () => {
82
+ makeTempBase();
83
+ const result = readSkillMeta("nonexistent");
84
+ expect(result).toBeNull();
85
+ });
86
+ it("returns null for invalid JSON", () => {
87
+ const base = makeTempBase();
88
+ const skillDir = join(base, "bad-meta");
89
+ mkdirSync(skillDir, { recursive: true });
90
+ writeFileSync(join(skillDir, ".arcana-meta.json"), "invalid json{", "utf-8");
91
+ const result = readSkillMeta("bad-meta");
92
+ expect(result).toBeNull();
93
+ });
94
+ });
95
+ describe("writeSkillMeta and readSkillMeta", () => {
96
+ it("roundtrip correctly", () => {
97
+ makeTempBase();
98
+ const meta = {
99
+ version: "1.0.0",
100
+ installedAt: "2026-02-14T12:00:00Z",
101
+ source: "local",
102
+ };
103
+ writeSkillMeta("roundtrip-skill", meta);
104
+ const result = readSkillMeta("roundtrip-skill");
105
+ expect(result).not.toBeNull();
106
+ expect(result?.version).toBe(meta.version);
107
+ expect(result?.installedAt).toBe(meta.installedAt);
108
+ expect(result?.source).toBe(meta.source);
109
+ });
110
+ });
111
+ describe("getDirSize", () => {
112
+ it("calculates directory size correctly", () => {
113
+ const base = makeTempBase();
114
+ const testDir = join(base, "size-test");
115
+ mkdirSync(testDir, { recursive: true });
116
+ // Create files of known sizes
117
+ writeFileSync(join(testDir, "file1.txt"), "a".repeat(100), "utf-8"); // 100 bytes
118
+ writeFileSync(join(testDir, "file2.txt"), "b".repeat(200), "utf-8"); // 200 bytes
119
+ // Create subdirectory with file
120
+ const subDir = join(testDir, "subdir");
121
+ mkdirSync(subDir, { recursive: true });
122
+ writeFileSync(join(subDir, "file3.txt"), "c".repeat(50), "utf-8"); // 50 bytes
123
+ const size = getDirSize(testDir);
124
+ expect(size).toBe(350); // 100 + 200 + 50
125
+ });
126
+ it("returns 0 for empty directory", () => {
127
+ const base = makeTempBase();
128
+ const emptyDir = join(base, "empty");
129
+ mkdirSync(emptyDir, { recursive: true });
130
+ const size = getDirSize(emptyDir);
131
+ expect(size).toBe(0);
132
+ });
133
+ it("handles nested directories", () => {
134
+ const base = makeTempBase();
135
+ const nestedDir = join(base, "nested");
136
+ mkdirSync(join(nestedDir, "a", "b", "c"), { recursive: true });
137
+ writeFileSync(join(nestedDir, "root.txt"), "x".repeat(10), "utf-8");
138
+ writeFileSync(join(nestedDir, "a", "level1.txt"), "y".repeat(20), "utf-8");
139
+ writeFileSync(join(nestedDir, "a", "b", "level2.txt"), "z".repeat(30), "utf-8");
140
+ writeFileSync(join(nestedDir, "a", "b", "c", "level3.txt"), "w".repeat(40), "utf-8");
141
+ const size = getDirSize(nestedDir);
142
+ expect(size).toBe(100); // 10 + 20 + 30 + 40
143
+ });
144
+ });
145
+ //# sourceMappingURL=fs.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.test.js","sourceRoot":"","sources":["../../src/utils/fs.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare function renderBanner(): string;
2
+ export declare function buildCustomHelp(version: string): string;
3
+ export declare function isFirstRun(): boolean;
4
+ export declare function markInitialized(): void;
5
+ export declare function showWelcome(version: string): void;
6
+ //# sourceMappingURL=help.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/utils/help.ts"],"names":[],"mappings":"AAgCA,wBAAgB,YAAY,IAAI,MAAM,CAKrC;AA2CD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAgCvD;AAID,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED,wBAAgB,eAAe,IAAI,IAAI,CAMtC;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CASjD"}
@@ -0,0 +1,117 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import * as p from "@clack/prompts";
5
+ import chalk from "chalk";
6
+ import { ui } from "./ui.js";
7
+ const noColor = !!(process.env.NO_COLOR || process.env.TERM === "dumb");
8
+ function amberShade(hex, text) {
9
+ if (noColor)
10
+ return text;
11
+ return chalk.hex(hex)(text);
12
+ }
13
+ const AMBER_HEXES = [
14
+ "#e8a84c", // bright amber
15
+ "#d4943a", // brand amber
16
+ "#c0842f", // mid
17
+ "#a87228", // darker
18
+ "#8f6020", // dimmer
19
+ "#755019", // darkest
20
+ ];
21
+ const BANNER_LINES = [
22
+ " █████╗ ██████╗ ██████╗ █████╗ ███╗ ██╗ █████╗ ",
23
+ "██╔══██╗██╔══██╗██╔════╝██╔══██╗████╗ ██║██╔══██╗",
24
+ "███████║██████╔╝██║ ███████║██╔██╗ ██║███████║",
25
+ "██╔══██║██╔══██╗██║ ██╔══██║██║╚██╗██║██╔══██║",
26
+ "██║ ██║██║ ██║╚██████╗██║ ██║██║ ╚████║██║ ██║",
27
+ "╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝",
28
+ ];
29
+ export function renderBanner() {
30
+ if (noColor) {
31
+ return BANNER_LINES.map((l) => ` ${l}`).join("\n");
32
+ }
33
+ return BANNER_LINES.map((line, i) => ` ${amberShade(AMBER_HEXES[i], line)}`).join("\n");
34
+ }
35
+ const COMMAND_GROUPS = {
36
+ "GETTING STARTED": [
37
+ { cmd: "init", desc: "Initialize arcana in current project" },
38
+ { cmd: "doctor", desc: "Check environment and diagnose issues" },
39
+ ],
40
+ SKILLS: [
41
+ { cmd: "list", desc: "List available skills" },
42
+ { cmd: "search <query>", desc: "Search across providers" },
43
+ { cmd: "info <skill>", desc: "Show skill details" },
44
+ { cmd: "install [skills...]", desc: "Install one or more skills" },
45
+ { cmd: "update [skills...]", desc: "Update installed skills" },
46
+ { cmd: "uninstall [skills...]", desc: "Remove one or more skills" },
47
+ ],
48
+ DEVELOPMENT: [
49
+ { cmd: "create <name>", desc: "Create a new skill from template" },
50
+ { cmd: "validate [skill]", desc: "Validate skill structure" },
51
+ { cmd: "audit [skill]", desc: "Audit skill quality" },
52
+ ],
53
+ CONFIGURATION: [
54
+ { cmd: "config [key] [val]", desc: "View or modify configuration" },
55
+ { cmd: "providers", desc: "Manage skill providers" },
56
+ { cmd: "clean", desc: "Remove orphaned data" },
57
+ { cmd: "stats", desc: "Show session analytics" },
58
+ ],
59
+ };
60
+ const EXAMPLES = [
61
+ "$ arcana install code-reviewer typescript golang",
62
+ '$ arcana search "testing"',
63
+ "$ arcana init --tool claude",
64
+ ];
65
+ function padRight(str, width) {
66
+ return str + " ".repeat(Math.max(0, width - str.length));
67
+ }
68
+ export function buildCustomHelp(version) {
69
+ const lines = [];
70
+ lines.push("");
71
+ lines.push(renderBanner());
72
+ lines.push("");
73
+ lines.push(` ${ui.bold("Supercharge any AI coding agent.")}${" ".repeat(20)}${ui.dim(`v${version}`)}`);
74
+ lines.push("");
75
+ lines.push(` ${ui.dim("USAGE")}`);
76
+ lines.push(" arcana <command> [options]");
77
+ for (const [group, commands] of Object.entries(COMMAND_GROUPS)) {
78
+ lines.push("");
79
+ lines.push(` ${ui.dim(group)}`);
80
+ for (const { cmd, desc } of commands) {
81
+ lines.push(` ${ui.cyan(padRight(cmd, 22))}${ui.dim(desc)}`);
82
+ }
83
+ }
84
+ lines.push("");
85
+ lines.push(` ${ui.dim("EXAMPLES")}`);
86
+ for (const ex of EXAMPLES) {
87
+ lines.push(` ${ui.cyan(ex)}`);
88
+ }
89
+ lines.push("");
90
+ lines.push(` ${ui.dim("LEARN MORE")}`);
91
+ lines.push(` arcana <command> --help ${ui.dim("Show help for a command")}`);
92
+ lines.push(` ${ui.dim("https://github.com/mahdy-gribkov/arcana")}`);
93
+ lines.push("");
94
+ return lines.join("\n");
95
+ }
96
+ const FIRST_RUN_FLAG = join(homedir(), ".arcana", ".initialized");
97
+ export function isFirstRun() {
98
+ return !existsSync(FIRST_RUN_FLAG);
99
+ }
100
+ export function markInitialized() {
101
+ const dir = join(homedir(), ".arcana");
102
+ if (!existsSync(dir)) {
103
+ mkdirSync(dir, { recursive: true });
104
+ }
105
+ writeFileSync(FIRST_RUN_FLAG, new Date().toISOString(), "utf-8");
106
+ }
107
+ export function showWelcome(version) {
108
+ console.log();
109
+ console.log(renderBanner());
110
+ console.log();
111
+ p.intro(chalk.hex("#d4943a").bold(`arcana v${version}`));
112
+ p.log.step(chalk.bold("Welcome! Arcana is a universal skill manager for AI coding agents."));
113
+ p.log.info("Skills extend your agent (Claude, Cursor, Codex, etc.) with expert knowledge.");
114
+ p.log.info("They install on-demand and only load when relevant, not all at once.");
115
+ console.log();
116
+ }
117
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/utils/help.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=help.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.test.d.ts","sourceRoot":"","sources":["../../src/utils/help.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,66 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { buildCustomHelp, renderBanner } from "./help.js";
3
+ describe("renderBanner", () => {
4
+ it("contains ARCANA block characters", () => {
5
+ const banner = renderBanner();
6
+ expect(banner).toContain("█████");
7
+ expect(banner).toContain("╔══");
8
+ });
9
+ it("has 6 lines", () => {
10
+ const banner = renderBanner();
11
+ // Strip ANSI codes then count non-empty lines
12
+ const stripped = banner.replace(/\x1b\[[0-9;]*m/g, "");
13
+ const lines = stripped.split("\n").filter((l) => l.trim().length > 0);
14
+ expect(lines.length).toBe(6);
15
+ });
16
+ });
17
+ describe("buildCustomHelp", () => {
18
+ it("contains all section headers", () => {
19
+ const help = buildCustomHelp("2.1.1");
20
+ expect(help).toContain("GETTING STARTED");
21
+ expect(help).toContain("SKILLS");
22
+ expect(help).toContain("DEVELOPMENT");
23
+ expect(help).toContain("CONFIGURATION");
24
+ expect(help).toContain("EXAMPLES");
25
+ expect(help).toContain("LEARN MORE");
26
+ });
27
+ it("contains USAGE section", () => {
28
+ const help = buildCustomHelp("2.1.1");
29
+ expect(help).toContain("USAGE");
30
+ expect(help).toContain("arcana <command> [options]");
31
+ });
32
+ it("contains tagline", () => {
33
+ const help = buildCustomHelp("2.1.1");
34
+ expect(help).toContain("Supercharge any AI coding agent");
35
+ });
36
+ it("contains all 15 commands", () => {
37
+ const help = buildCustomHelp("1.0.0");
38
+ const commands = [
39
+ "init", "doctor", "list", "search", "info", "install",
40
+ "update", "uninstall", "create", "validate", "audit",
41
+ "config", "providers", "clean", "stats",
42
+ ];
43
+ for (const cmd of commands) {
44
+ expect(help).toContain(cmd);
45
+ }
46
+ });
47
+ it("contains version string", () => {
48
+ const help = buildCustomHelp("3.5.7");
49
+ expect(help).toContain("v3.5.7");
50
+ });
51
+ it("contains examples with actual commands", () => {
52
+ const help = buildCustomHelp("1.0.0");
53
+ expect(help).toContain("arcana install code-reviewer");
54
+ expect(help).toContain("arcana search");
55
+ expect(help).toContain("arcana init --tool claude");
56
+ });
57
+ it("contains GitHub URL", () => {
58
+ const help = buildCustomHelp("1.0.0");
59
+ expect(help).toContain("github.com/mahdy-gribkov/arcana");
60
+ });
61
+ it("contains ASCII banner", () => {
62
+ const help = buildCustomHelp("1.0.0");
63
+ expect(help).toContain("█████");
64
+ });
65
+ });
66
+ //# sourceMappingURL=help.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.test.js","sourceRoot":"","sources":["../../src/utils/help.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE1D,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,aAAa,EAAE,GAAG,EAAE;QACrB,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,8CAA8C;QAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACjC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAC1B,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,iCAAiC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG;YACf,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS;YACrD,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO;YACpD,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO;SACxC,CAAC;QACF,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;QACvD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,iCAAiC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ export interface HistoryEntry {
2
+ action: string;
3
+ target?: string;
4
+ timestamp: string;
5
+ }
6
+ export declare function readHistory(): HistoryEntry[];
7
+ export declare function appendHistory(action: string, target?: string): void;
8
+ export declare function clearHistory(): void;
9
+ export declare function getRecentSkills(limit?: number): string[];
10
+ //# sourceMappingURL=history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/utils/history.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,wBAAgB,WAAW,IAAI,YAAY,EAAE,CAS5C;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAWnE;AAED,wBAAgB,YAAY,IAAI,IAAI,CAQnC;AAED,wBAAgB,eAAe,CAAC,KAAK,SAAI,GAAG,MAAM,EAAE,CAUnD"}
@@ -0,0 +1,58 @@
1
+ import { existsSync, readFileSync, mkdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { atomicWriteSync } from "./atomic.js";
5
+ const MAX_ENTRIES = 50;
6
+ function historyPath() {
7
+ return join(homedir(), ".arcana", "history.json");
8
+ }
9
+ export function readHistory() {
10
+ const p = historyPath();
11
+ if (!existsSync(p))
12
+ return [];
13
+ try {
14
+ const data = JSON.parse(readFileSync(p, "utf-8"));
15
+ return Array.isArray(data) ? data : [];
16
+ }
17
+ catch {
18
+ return [];
19
+ }
20
+ }
21
+ export function appendHistory(action, target) {
22
+ const entries = readHistory();
23
+ entries.push({ action, target, timestamp: new Date().toISOString() });
24
+ while (entries.length > MAX_ENTRIES)
25
+ entries.shift();
26
+ const dir = join(homedir(), ".arcana");
27
+ if (!existsSync(dir))
28
+ mkdirSync(dir, { recursive: true });
29
+ try {
30
+ atomicWriteSync(historyPath(), JSON.stringify(entries, null, 2));
31
+ }
32
+ catch {
33
+ // Best effort
34
+ }
35
+ }
36
+ export function clearHistory() {
37
+ const dir = join(homedir(), ".arcana");
38
+ if (!existsSync(dir))
39
+ mkdirSync(dir, { recursive: true });
40
+ try {
41
+ atomicWriteSync(historyPath(), "[]");
42
+ }
43
+ catch {
44
+ // Best effort
45
+ }
46
+ }
47
+ export function getRecentSkills(limit = 5) {
48
+ const entries = readHistory();
49
+ const skills = [];
50
+ for (let i = entries.length - 1; i >= 0 && skills.length < limit; i--) {
51
+ const e = entries[i];
52
+ if (e.target && (e.action === "install" || e.action === "search") && !skills.includes(e.target)) {
53
+ skills.push(e.target);
54
+ }
55
+ }
56
+ return skills;
57
+ }
58
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/utils/history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export interface HttpResponse {
2
+ body: string;
3
+ statusCode: number;
4
+ headers: Record<string, string | string[] | undefined>;
5
+ }
6
+ export declare function sanitizeUrl(url: string): string;
7
+ export declare class HttpError extends Error {
8
+ readonly statusCode: number;
9
+ readonly url: string;
10
+ constructor(statusCode: number, url: string, message?: string);
11
+ }
12
+ export declare class RateLimitError extends HttpError {
13
+ readonly resetAt: Date | null;
14
+ constructor(url: string, resetAt: Date | null);
15
+ }
16
+ export declare function httpGet(url: string, timeout?: number): Promise<HttpResponse>;
17
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/utils/http.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;CACxD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAc/C;AAED,qBAAa,SAAU,SAAQ,KAAK;aAEhB,UAAU,EAAE,MAAM;aAClB,GAAG,EAAE,MAAM;gBADX,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EAC3B,OAAO,CAAC,EAAE,MAAM;CAKnB;AAED,qBAAa,cAAe,SAAQ,SAAS;aAGzB,OAAO,EAAE,IAAI,GAAG,IAAI;gBADpC,GAAG,EAAE,MAAM,EACK,OAAO,EAAE,IAAI,GAAG,IAAI;CAMvC;AAkBD,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,SAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,CA+CjF"}