claudekit-cli 1.4.0 → 1.5.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 (50) hide show
  1. package/bin/ck-darwin-arm64 +0 -0
  2. package/bin/ck-darwin-x64 +0 -0
  3. package/bin/ck-linux-x64 +0 -0
  4. package/bin/ck-win32-x64.exe +0 -0
  5. package/package.json +8 -2
  6. package/scripts/postinstall.js +74 -0
  7. package/.github/workflows/ci.yml +0 -45
  8. package/.github/workflows/claude-code-review.yml +0 -57
  9. package/.github/workflows/claude.yml +0 -50
  10. package/.github/workflows/release.yml +0 -102
  11. package/.releaserc.json +0 -17
  12. package/.repomixignore +0 -15
  13. package/AGENTS.md +0 -217
  14. package/CHANGELOG.md +0 -88
  15. package/CLAUDE.md +0 -34
  16. package/biome.json +0 -28
  17. package/bun.lock +0 -863
  18. package/dist/index.js +0 -22489
  19. package/src/commands/new.ts +0 -185
  20. package/src/commands/update.ts +0 -174
  21. package/src/commands/version.ts +0 -135
  22. package/src/index.ts +0 -102
  23. package/src/lib/auth.ts +0 -157
  24. package/src/lib/download.ts +0 -654
  25. package/src/lib/github.ts +0 -230
  26. package/src/lib/merge.ts +0 -116
  27. package/src/lib/prompts.ts +0 -114
  28. package/src/types.ts +0 -171
  29. package/src/utils/config.ts +0 -87
  30. package/src/utils/file-scanner.ts +0 -134
  31. package/src/utils/logger.ts +0 -124
  32. package/src/utils/safe-prompts.ts +0 -44
  33. package/src/utils/safe-spinner.ts +0 -38
  34. package/src/version.json +0 -3
  35. package/test-integration/demo/.mcp.json +0 -13
  36. package/test-integration/demo/.repomixignore +0 -15
  37. package/test-integration/demo/CLAUDE.md +0 -34
  38. package/tests/commands/version.test.ts +0 -297
  39. package/tests/integration/cli.test.ts +0 -252
  40. package/tests/lib/auth.test.ts +0 -116
  41. package/tests/lib/download.test.ts +0 -292
  42. package/tests/lib/github-download-priority.test.ts +0 -432
  43. package/tests/lib/github.test.ts +0 -52
  44. package/tests/lib/merge.test.ts +0 -215
  45. package/tests/lib/prompts.test.ts +0 -66
  46. package/tests/types.test.ts +0 -337
  47. package/tests/utils/config.test.ts +0 -263
  48. package/tests/utils/file-scanner.test.ts +0 -202
  49. package/tests/utils/logger.test.ts +0 -239
  50. package/tsconfig.json +0 -30
@@ -1,66 +0,0 @@
1
- import { beforeEach, describe, expect, test } from "bun:test";
2
- import { PromptsManager } from "../../src/lib/prompts.js";
3
- import { AVAILABLE_KITS } from "../../src/types.js";
4
-
5
- describe("PromptsManager", () => {
6
- let manager: PromptsManager;
7
-
8
- beforeEach(() => {
9
- manager = new PromptsManager();
10
- });
11
-
12
- describe("constructor", () => {
13
- test("should create PromptsManager instance", () => {
14
- expect(manager).toBeInstanceOf(PromptsManager);
15
- });
16
- });
17
-
18
- describe("utility methods", () => {
19
- test("intro should not throw", () => {
20
- expect(() => manager.intro("Test intro")).not.toThrow();
21
- });
22
-
23
- test("outro should not throw", () => {
24
- expect(() => manager.outro("Test outro")).not.toThrow();
25
- });
26
-
27
- test("note should not throw", () => {
28
- expect(() => manager.note("Test note", "Title")).not.toThrow();
29
- });
30
-
31
- test("note should work without title", () => {
32
- expect(() => manager.note("Test note")).not.toThrow();
33
- });
34
- });
35
-
36
- // Note: Interactive prompt tests (selectKit, selectVersion, getDirectory, confirm)
37
- // would require mocking the @clack/prompts library or using integration tests
38
- // with simulated user input. These are better suited for e2e testing.
39
-
40
- describe("validation logic", () => {
41
- test("selectVersion should handle empty versions array", async () => {
42
- await expect(manager.selectVersion([], undefined)).rejects.toThrow("No versions available");
43
- });
44
-
45
- test("selectVersion should return first version when only one exists", async () => {
46
- const versions = ["v1.0.0"];
47
- const result = await manager.selectVersion(versions);
48
- expect(result).toBe("v1.0.0");
49
- });
50
-
51
- test("selectVersion should return first version when no default is provided", async () => {
52
- const versions = ["v1.0.0", "v2.0.0"];
53
- const result = await manager.selectVersion(versions);
54
- expect(result).toBe("v1.0.0");
55
- });
56
- });
57
-
58
- describe("kit configuration", () => {
59
- test("AVAILABLE_KITS should be properly structured", () => {
60
- expect(AVAILABLE_KITS.engineer).toBeDefined();
61
- expect(AVAILABLE_KITS.marketing).toBeDefined();
62
- expect(AVAILABLE_KITS.engineer.name).toBe("ClaudeKit Engineer");
63
- expect(AVAILABLE_KITS.marketing.name).toBe("ClaudeKit Marketing");
64
- });
65
- });
66
- });
@@ -1,337 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import {
3
- AVAILABLE_KITS,
4
- AuthenticationError,
5
- ClaudeKitError,
6
- ConfigSchema,
7
- DownloadError,
8
- ExcludePatternSchema,
9
- ExtractionError,
10
- GitHubError,
11
- GitHubReleaseAssetSchema,
12
- GitHubReleaseSchema,
13
- KitConfigSchema,
14
- KitType,
15
- NewCommandOptionsSchema,
16
- UpdateCommandOptionsSchema,
17
- } from "../src/types.js";
18
-
19
- describe("Types and Schemas", () => {
20
- describe("KitType", () => {
21
- test("should validate correct kit types", () => {
22
- expect(KitType.parse("engineer")).toBe("engineer");
23
- expect(KitType.parse("marketing")).toBe("marketing");
24
- });
25
-
26
- test("should reject invalid kit types", () => {
27
- expect(() => KitType.parse("invalid")).toThrow();
28
- expect(() => KitType.parse("")).toThrow();
29
- expect(() => KitType.parse(123)).toThrow();
30
- });
31
- });
32
-
33
- describe("ExcludePatternSchema", () => {
34
- test("should accept valid glob patterns", () => {
35
- const validPatterns = ["*.log", "**/*.tmp", "temp/**", "logs/*.txt", "cache/**/*"];
36
- validPatterns.forEach((pattern) => {
37
- expect(() => ExcludePatternSchema.parse(pattern)).not.toThrow();
38
- });
39
- });
40
-
41
- test("should reject absolute paths", () => {
42
- expect(() => ExcludePatternSchema.parse("/etc/passwd")).toThrow("Absolute paths not allowed");
43
- expect(() => ExcludePatternSchema.parse("/var/log/**")).toThrow("Absolute paths not allowed");
44
- });
45
-
46
- test("should reject path traversal", () => {
47
- expect(() => ExcludePatternSchema.parse("../../etc/passwd")).toThrow(
48
- "Path traversal not allowed",
49
- );
50
- expect(() => ExcludePatternSchema.parse("../../../secret")).toThrow(
51
- "Path traversal not allowed",
52
- );
53
- });
54
-
55
- test("should reject empty patterns", () => {
56
- expect(() => ExcludePatternSchema.parse("")).toThrow("Exclude pattern cannot be empty");
57
- expect(() => ExcludePatternSchema.parse(" ")).toThrow("Exclude pattern cannot be empty");
58
- });
59
-
60
- test("should reject overly long patterns", () => {
61
- const longPattern = "a".repeat(501);
62
- expect(() => ExcludePatternSchema.parse(longPattern)).toThrow("Exclude pattern too long");
63
- });
64
-
65
- test("should trim whitespace", () => {
66
- const result = ExcludePatternSchema.parse(" *.log ");
67
- expect(result).toBe("*.log");
68
- });
69
- });
70
-
71
- describe("NewCommandOptionsSchema", () => {
72
- test("should validate correct options", () => {
73
- const result = NewCommandOptionsSchema.parse({
74
- dir: "./test",
75
- kit: "engineer",
76
- version: "v1.0.0",
77
- });
78
- expect(result.dir).toBe("./test");
79
- expect(result.kit).toBe("engineer");
80
- expect(result.version).toBe("v1.0.0");
81
- });
82
-
83
- test("should use default values", () => {
84
- const result = NewCommandOptionsSchema.parse({});
85
- expect(result.dir).toBe(".");
86
- expect(result.kit).toBeUndefined();
87
- expect(result.version).toBeUndefined();
88
- expect(result.exclude).toEqual([]);
89
- });
90
-
91
- test("should accept optional fields", () => {
92
- const result = NewCommandOptionsSchema.parse({ dir: "./custom" });
93
- expect(result.dir).toBe("./custom");
94
- expect(result.kit).toBeUndefined();
95
- });
96
-
97
- test("should validate exclude patterns", () => {
98
- const result = NewCommandOptionsSchema.parse({
99
- dir: "./test",
100
- exclude: ["*.log", "temp/**"],
101
- });
102
- expect(result.exclude).toEqual(["*.log", "temp/**"]);
103
- });
104
-
105
- test("should reject invalid exclude patterns", () => {
106
- expect(() =>
107
- NewCommandOptionsSchema.parse({
108
- dir: "./test",
109
- exclude: ["/etc/passwd"],
110
- }),
111
- ).toThrow();
112
- });
113
- });
114
-
115
- describe("UpdateCommandOptionsSchema", () => {
116
- test("should validate correct options", () => {
117
- const result = UpdateCommandOptionsSchema.parse({
118
- dir: "./test",
119
- kit: "engineer",
120
- version: "v2.0.0",
121
- });
122
- expect(result.dir).toBe("./test");
123
- expect(result.kit).toBe("engineer");
124
- expect(result.version).toBe("v2.0.0");
125
- });
126
-
127
- test("should use default values", () => {
128
- const result = UpdateCommandOptionsSchema.parse({});
129
- expect(result.dir).toBe(".");
130
- expect(result.exclude).toEqual([]);
131
- });
132
-
133
- test("should validate exclude patterns", () => {
134
- const result = UpdateCommandOptionsSchema.parse({
135
- dir: "./test",
136
- exclude: ["*.log", "**/*.tmp"],
137
- });
138
- expect(result.exclude).toEqual(["*.log", "**/*.tmp"]);
139
- });
140
-
141
- test("should reject invalid exclude patterns", () => {
142
- expect(() =>
143
- UpdateCommandOptionsSchema.parse({
144
- dir: "./test",
145
- exclude: ["../../../etc"],
146
- }),
147
- ).toThrow();
148
- });
149
- });
150
-
151
- describe("ConfigSchema", () => {
152
- test("should validate complete config", () => {
153
- const config = {
154
- github: {
155
- token: "ghp_test123456789",
156
- },
157
- defaults: {
158
- kit: "engineer",
159
- dir: "./projects",
160
- },
161
- };
162
- const result = ConfigSchema.parse(config);
163
- expect(result.github?.token).toBe("ghp_test123456789");
164
- expect(result.defaults?.kit).toBe("engineer");
165
- expect(result.defaults?.dir).toBe("./projects");
166
- });
167
-
168
- test("should validate empty config", () => {
169
- const result = ConfigSchema.parse({});
170
- expect(result.github).toBeUndefined();
171
- expect(result.defaults).toBeUndefined();
172
- });
173
-
174
- test("should validate partial config", () => {
175
- const result = ConfigSchema.parse({ github: {} });
176
- expect(result.github).toEqual({});
177
- expect(result.defaults).toBeUndefined();
178
- });
179
- });
180
-
181
- describe("GitHubReleaseAssetSchema", () => {
182
- test("should validate correct asset", () => {
183
- const asset = {
184
- id: 123,
185
- name: "release.tar.gz",
186
- url: "https://api.github.com/repos/test/repo/releases/assets/123",
187
- browser_download_url: "https://github.com/test/release.tar.gz",
188
- size: 1024,
189
- content_type: "application/gzip",
190
- };
191
- const result = GitHubReleaseAssetSchema.parse(asset);
192
- expect(result.id).toBe(123);
193
- expect(result.name).toBe("release.tar.gz");
194
- expect(result.size).toBe(1024);
195
- });
196
-
197
- test("should reject invalid URL", () => {
198
- const asset = {
199
- id: 123,
200
- name: "release.tar.gz",
201
- url: "not-a-url",
202
- browser_download_url: "not-a-url",
203
- size: 1024,
204
- content_type: "application/gzip",
205
- };
206
- expect(() => GitHubReleaseAssetSchema.parse(asset)).toThrow();
207
- });
208
-
209
- test("should reject missing required fields", () => {
210
- const asset = {
211
- id: 123,
212
- name: "release.tar.gz",
213
- };
214
- expect(() => GitHubReleaseAssetSchema.parse(asset)).toThrow();
215
- });
216
- });
217
-
218
- describe("GitHubReleaseSchema", () => {
219
- test("should validate complete release", () => {
220
- const release = {
221
- id: 1,
222
- tag_name: "v1.0.0",
223
- name: "Version 1.0.0",
224
- draft: false,
225
- prerelease: false,
226
- assets: [
227
- {
228
- id: 123,
229
- name: "release.tar.gz",
230
- url: "https://api.github.com/repos/test/repo/releases/assets/123",
231
- browser_download_url: "https://github.com/test/release.tar.gz",
232
- size: 1024,
233
- content_type: "application/gzip",
234
- },
235
- ],
236
- published_at: "2024-01-01T00:00:00Z",
237
- tarball_url: "https://api.github.com/repos/test/test-repo/tarball/v1.0.0",
238
- zipball_url: "https://api.github.com/repos/test/test-repo/zipball/v1.0.0",
239
- };
240
- const result = GitHubReleaseSchema.parse(release);
241
- expect(result.id).toBe(1);
242
- expect(result.tag_name).toBe("v1.0.0");
243
- expect(result.assets).toHaveLength(1);
244
- });
245
-
246
- test("should validate release without published_at", () => {
247
- const release = {
248
- id: 1,
249
- tag_name: "v1.0.0",
250
- name: "Version 1.0.0",
251
- draft: false,
252
- prerelease: false,
253
- assets: [],
254
- tarball_url: "https://api.github.com/repos/test/test-repo/tarball/v1.0.0",
255
- zipball_url: "https://api.github.com/repos/test/test-repo/zipball/v1.0.0",
256
- };
257
- const result = GitHubReleaseSchema.parse(release);
258
- expect(result.published_at).toBeUndefined();
259
- });
260
- });
261
-
262
- describe("KitConfigSchema", () => {
263
- test("should validate correct kit config", () => {
264
- const config = {
265
- name: "Test Kit",
266
- repo: "test-repo",
267
- owner: "test-owner",
268
- description: "Test description",
269
- };
270
- const result = KitConfigSchema.parse(config);
271
- expect(result.name).toBe("Test Kit");
272
- expect(result.repo).toBe("test-repo");
273
- });
274
-
275
- test("should reject missing fields", () => {
276
- const config = {
277
- name: "Test Kit",
278
- repo: "test-repo",
279
- };
280
- expect(() => KitConfigSchema.parse(config)).toThrow();
281
- });
282
- });
283
-
284
- describe("AVAILABLE_KITS", () => {
285
- test("should have engineer kit", () => {
286
- expect(AVAILABLE_KITS.engineer).toBeDefined();
287
- expect(AVAILABLE_KITS.engineer.name).toBe("ClaudeKit Engineer");
288
- expect(AVAILABLE_KITS.engineer.repo).toBe("claudekit-engineer");
289
- });
290
-
291
- test("should have marketing kit", () => {
292
- expect(AVAILABLE_KITS.marketing).toBeDefined();
293
- expect(AVAILABLE_KITS.marketing.name).toBe("ClaudeKit Marketing");
294
- expect(AVAILABLE_KITS.marketing.repo).toBe("claudekit-marketing");
295
- });
296
- });
297
-
298
- describe("Custom Error Classes", () => {
299
- test("ClaudeKitError should store code and statusCode", () => {
300
- const error = new ClaudeKitError("Test error", "TEST_CODE", 500);
301
- expect(error.message).toBe("Test error");
302
- expect(error.code).toBe("TEST_CODE");
303
- expect(error.statusCode).toBe(500);
304
- expect(error.name).toBe("ClaudeKitError");
305
- });
306
-
307
- test("AuthenticationError should set correct defaults", () => {
308
- const error = new AuthenticationError("Auth failed");
309
- expect(error.message).toBe("Auth failed");
310
- expect(error.code).toBe("AUTH_ERROR");
311
- expect(error.statusCode).toBe(401);
312
- expect(error.name).toBe("AuthenticationError");
313
- });
314
-
315
- test("GitHubError should store statusCode", () => {
316
- const error = new GitHubError("GitHub failed", 404);
317
- expect(error.message).toBe("GitHub failed");
318
- expect(error.code).toBe("GITHUB_ERROR");
319
- expect(error.statusCode).toBe(404);
320
- expect(error.name).toBe("GitHubError");
321
- });
322
-
323
- test("DownloadError should have correct code", () => {
324
- const error = new DownloadError("Download failed");
325
- expect(error.message).toBe("Download failed");
326
- expect(error.code).toBe("DOWNLOAD_ERROR");
327
- expect(error.name).toBe("DownloadError");
328
- });
329
-
330
- test("ExtractionError should have correct code", () => {
331
- const error = new ExtractionError("Extract failed");
332
- expect(error.message).toBe("Extract failed");
333
- expect(error.code).toBe("EXTRACTION_ERROR");
334
- expect(error.name).toBe("ExtractionError");
335
- });
336
- });
337
- });
@@ -1,263 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
- import { existsSync } from "node:fs";
3
- import { mkdir, rm, writeFile } from "node:fs/promises";
4
- import { homedir } from "node:os";
5
- import { join } from "node:path";
6
- import type { Config } from "../../src/types.js";
7
- import { ConfigManager } from "../../src/utils/config.js";
8
-
9
- const TEST_CONFIG_DIR = join(homedir(), ".claudekit-test");
10
- const TEST_CONFIG_FILE = join(TEST_CONFIG_DIR, "config.json");
11
-
12
- describe("ConfigManager", () => {
13
- beforeEach(async () => {
14
- // Create test config directory
15
- if (!existsSync(TEST_CONFIG_DIR)) {
16
- await mkdir(TEST_CONFIG_DIR, { recursive: true });
17
- }
18
-
19
- // Override config paths for testing
20
- // Note: This is a simplified test - in production we'd need to mock the paths
21
- });
22
-
23
- afterEach(async () => {
24
- // Clean up test config directory
25
- if (existsSync(TEST_CONFIG_DIR)) {
26
- await rm(TEST_CONFIG_DIR, { recursive: true, force: true });
27
- }
28
-
29
- // Reset ConfigManager state
30
- (ConfigManager as any).config = null;
31
- });
32
-
33
- describe("load", () => {
34
- test("should return default config when no config file exists", async () => {
35
- const config = await ConfigManager.load();
36
- expect(config).toEqual({ github: {}, defaults: {} });
37
- });
38
-
39
- test("should load config from file when it exists", async () => {
40
- const testConfig: Config = {
41
- github: { token: "test-token" },
42
- defaults: { kit: "engineer", dir: "./test" },
43
- };
44
-
45
- // Write test config file (to actual location for this test)
46
- const actualConfigDir = join(homedir(), ".claudekit");
47
- const actualConfigFile = join(actualConfigDir, "config.json");
48
-
49
- if (!existsSync(actualConfigDir)) {
50
- await mkdir(actualConfigDir, { recursive: true });
51
- }
52
- await writeFile(actualConfigFile, JSON.stringify(testConfig));
53
-
54
- try {
55
- const config = await ConfigManager.load();
56
- expect(config.github?.token).toBe("test-token");
57
- expect(config.defaults?.kit).toBe("engineer");
58
- } finally {
59
- // Cleanup
60
- if (existsSync(actualConfigFile)) {
61
- await rm(actualConfigFile);
62
- }
63
- }
64
- });
65
-
66
- test("should return default config on invalid JSON", async () => {
67
- const actualConfigDir = join(homedir(), ".claudekit");
68
- const actualConfigFile = join(actualConfigDir, "config.json");
69
-
70
- if (!existsSync(actualConfigDir)) {
71
- await mkdir(actualConfigDir, { recursive: true });
72
- }
73
- await writeFile(actualConfigFile, "invalid json");
74
-
75
- try {
76
- const config = await ConfigManager.load();
77
- expect(config).toEqual({ github: {}, defaults: {} });
78
- } finally {
79
- // Cleanup
80
- if (existsSync(actualConfigFile)) {
81
- await rm(actualConfigFile);
82
- }
83
- }
84
- });
85
-
86
- test("should cache config after first load", async () => {
87
- const config1 = await ConfigManager.load();
88
- const config2 = await ConfigManager.load();
89
- expect(config1).toBe(config2); // Same reference
90
- });
91
- });
92
-
93
- describe("save", () => {
94
- test("should save valid config to file", async () => {
95
- const testConfig: Config = {
96
- github: { token: "test-token" },
97
- defaults: { kit: "marketing", dir: "./projects" },
98
- };
99
-
100
- await ConfigManager.save(testConfig);
101
-
102
- // Verify file was created
103
- const actualConfigFile = join(homedir(), ".claudekit", "config.json");
104
- expect(existsSync(actualConfigFile)).toBe(true);
105
-
106
- // Cleanup
107
- if (existsSync(actualConfigFile)) {
108
- await rm(actualConfigFile);
109
- }
110
- const actualConfigDir = join(homedir(), ".claudekit");
111
- if (existsSync(actualConfigDir)) {
112
- await rm(actualConfigDir, { recursive: true });
113
- }
114
- });
115
-
116
- test("should create config directory if it does not exist", async () => {
117
- const actualConfigDir = join(homedir(), ".claudekit");
118
- if (existsSync(actualConfigDir)) {
119
- await rm(actualConfigDir, { recursive: true });
120
- }
121
-
122
- const testConfig: Config = { github: {}, defaults: {} };
123
- await ConfigManager.save(testConfig);
124
-
125
- expect(existsSync(actualConfigDir)).toBe(true);
126
-
127
- // Cleanup
128
- if (existsSync(actualConfigDir)) {
129
- await rm(actualConfigDir, { recursive: true });
130
- }
131
- });
132
-
133
- test("should throw error on invalid config", async () => {
134
- const invalidConfig = {
135
- github: { token: 123 }, // Invalid: should be string
136
- };
137
-
138
- await expect(ConfigManager.save(invalidConfig as any)).rejects.toThrow();
139
- });
140
-
141
- test("should update cached config", async () => {
142
- const testConfig: Config = {
143
- github: { token: "new-token" },
144
- defaults: {},
145
- };
146
-
147
- await ConfigManager.save(testConfig);
148
- const loaded = await ConfigManager.get();
149
- expect(loaded.github?.token).toBe("new-token");
150
-
151
- // Cleanup
152
- const actualConfigFile = join(homedir(), ".claudekit", "config.json");
153
- const actualConfigDir = join(homedir(), ".claudekit");
154
- if (existsSync(actualConfigFile)) {
155
- await rm(actualConfigFile);
156
- }
157
- if (existsSync(actualConfigDir)) {
158
- await rm(actualConfigDir, { recursive: true });
159
- }
160
- });
161
- });
162
-
163
- describe("get", () => {
164
- test("should return current config", async () => {
165
- const config = await ConfigManager.get();
166
- expect(config).toBeDefined();
167
- expect(config).toHaveProperty("github");
168
- expect(config).toHaveProperty("defaults");
169
- });
170
- });
171
-
172
- describe("set", () => {
173
- test("should set nested config value", async () => {
174
- await ConfigManager.set("github.token", "test-token-123");
175
- const config = await ConfigManager.get();
176
- expect(config.github?.token).toBe("test-token-123");
177
-
178
- // Cleanup
179
- const actualConfigFile = join(homedir(), ".claudekit", "config.json");
180
- const actualConfigDir = join(homedir(), ".claudekit");
181
- if (existsSync(actualConfigFile)) {
182
- await rm(actualConfigFile);
183
- }
184
- if (existsSync(actualConfigDir)) {
185
- await rm(actualConfigDir, { recursive: true });
186
- }
187
- });
188
-
189
- test("should create nested objects if they do not exist", async () => {
190
- await ConfigManager.set("defaults.kit", "engineer");
191
- const config = await ConfigManager.get();
192
- expect(config.defaults?.kit).toBe("engineer");
193
-
194
- // Cleanup
195
- const actualConfigFile = join(homedir(), ".claudekit", "config.json");
196
- const actualConfigDir = join(homedir(), ".claudekit");
197
- if (existsSync(actualConfigFile)) {
198
- await rm(actualConfigFile);
199
- }
200
- if (existsSync(actualConfigDir)) {
201
- await rm(actualConfigDir, { recursive: true });
202
- }
203
- });
204
-
205
- test("should handle multiple nested levels", async () => {
206
- await ConfigManager.set("defaults.dir", "/test/path");
207
- const config = await ConfigManager.get();
208
- expect(config.defaults?.dir).toBe("/test/path");
209
-
210
- // Cleanup
211
- const actualConfigFile = join(homedir(), ".claudekit", "config.json");
212
- const actualConfigDir = join(homedir(), ".claudekit");
213
- if (existsSync(actualConfigFile)) {
214
- await rm(actualConfigFile);
215
- }
216
- if (existsSync(actualConfigDir)) {
217
- await rm(actualConfigDir, { recursive: true });
218
- }
219
- });
220
- });
221
-
222
- describe("getToken", () => {
223
- test("should return token from config", async () => {
224
- await ConfigManager.setToken("test-token-456");
225
- const token = await ConfigManager.getToken();
226
- expect(token).toBe("test-token-456");
227
-
228
- // Cleanup
229
- const actualConfigFile = join(homedir(), ".claudekit", "config.json");
230
- const actualConfigDir = join(homedir(), ".claudekit");
231
- if (existsSync(actualConfigFile)) {
232
- await rm(actualConfigFile);
233
- }
234
- if (existsSync(actualConfigDir)) {
235
- await rm(actualConfigDir, { recursive: true });
236
- }
237
- });
238
-
239
- test("should return undefined if no token is set", async () => {
240
- (ConfigManager as any).config = null;
241
- const token = await ConfigManager.getToken();
242
- expect(token).toBeUndefined();
243
- });
244
- });
245
-
246
- describe("setToken", () => {
247
- test("should set token in config", async () => {
248
- await ConfigManager.setToken("new-test-token");
249
- const config = await ConfigManager.get();
250
- expect(config.github?.token).toBe("new-test-token");
251
-
252
- // Cleanup
253
- const actualConfigFile = join(homedir(), ".claudekit", "config.json");
254
- const actualConfigDir = join(homedir(), ".claudekit");
255
- if (existsSync(actualConfigFile)) {
256
- await rm(actualConfigFile);
257
- }
258
- if (existsSync(actualConfigDir)) {
259
- await rm(actualConfigDir, { recursive: true });
260
- }
261
- });
262
- });
263
- });