claudekit-cli 1.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/.claude/agents/brainstormer.md +96 -0
- package/.claude/agents/code-reviewer.md +141 -0
- package/.claude/agents/copywriter.md +108 -0
- package/.claude/agents/database-admin.md +86 -0
- package/.claude/agents/debugger.md +124 -0
- package/.claude/agents/docs-manager.md +115 -0
- package/.claude/agents/git-manager.md +60 -0
- package/.claude/agents/journal-writer.md +111 -0
- package/.claude/agents/planner.md +87 -0
- package/.claude/agents/project-manager.md +113 -0
- package/.claude/agents/researcher.md +173 -0
- package/.claude/agents/scout.md +123 -0
- package/.claude/agents/tester.md +95 -0
- package/.claude/agents/ui-ux-designer.md +206 -0
- package/.claude/commands/bootstrap.md +104 -0
- package/.claude/commands/brainstorm.md +67 -0
- package/.claude/commands/content/enhance.md +13 -0
- package/.claude/commands/content/fast.md +11 -0
- package/.claude/commands/content/good.md +13 -0
- package/.claude/commands/cook.md +19 -0
- package/.claude/commands/debug.md +10 -0
- package/.claude/commands/design/3d.md +65 -0
- package/.claude/commands/design/describe.md +13 -0
- package/.claude/commands/design/fast.md +19 -0
- package/.claude/commands/design/good.md +23 -0
- package/.claude/commands/design/screenshot.md +23 -0
- package/.claude/commands/design/video.md +23 -0
- package/.claude/commands/docs/init.md +13 -0
- package/.claude/commands/docs/summarize.md +10 -0
- package/.claude/commands/docs/update.md +21 -0
- package/.claude/commands/fix/ci.md +11 -0
- package/.claude/commands/fix/fast.md +12 -0
- package/.claude/commands/fix/hard.md +18 -0
- package/.claude/commands/fix/logs.md +16 -0
- package/.claude/commands/fix/test.md +18 -0
- package/.claude/commands/fix/types.md +10 -0
- package/.claude/commands/git/cm.md +5 -0
- package/.claude/commands/git/cp.md +4 -0
- package/.claude/commands/integrate/polar.md +42 -0
- package/.claude/commands/plan/ci.md +12 -0
- package/.claude/commands/plan/two.md +13 -0
- package/.claude/commands/plan.md +10 -0
- package/.claude/commands/scout.md +29 -0
- package/.claude/commands/test.md +7 -0
- package/.claude/commands/watzup.md +8 -0
- package/.claude/hooks/telegram_notify.sh +136 -0
- package/.claude/send-discord.sh +64 -0
- package/.claude/settings.json +7 -0
- package/.claude/statusline.sh +143 -0
- package/.claude/workflows/development-rules.md +80 -0
- package/.claude/workflows/documentation-management.md +28 -0
- package/.claude/workflows/orchestration-protocol.md +16 -0
- package/.claude/workflows/primary-workflow.md +41 -0
- package/.github/workflows/ci.yml +43 -0
- package/.github/workflows/release.yml +58 -0
- package/.opencode/agent/code-reviewer.md +141 -0
- package/.opencode/agent/debugger.md +74 -0
- package/.opencode/agent/docs-manager.md +119 -0
- package/.opencode/agent/git-manager.md +60 -0
- package/.opencode/agent/planner-researcher.md +100 -0
- package/.opencode/agent/planner.md +87 -0
- package/.opencode/agent/project-manager.md +113 -0
- package/.opencode/agent/researcher.md +173 -0
- package/.opencode/agent/solution-brainstormer.md +89 -0
- package/.opencode/agent/system-architecture.md +192 -0
- package/.opencode/agent/tester.md +96 -0
- package/.opencode/agent/ui-ux-designer.md +203 -0
- package/.opencode/agent/ui-ux-developer.md +97 -0
- package/.opencode/command/cook.md +7 -0
- package/.opencode/command/debug.md +10 -0
- package/.opencode/command/design/3d.md +65 -0
- package/.opencode/command/design/fast.md +18 -0
- package/.opencode/command/design/good.md +21 -0
- package/.opencode/command/design/screenshot.md +22 -0
- package/.opencode/command/design/video.md +22 -0
- package/.opencode/command/docs/init.md +11 -0
- package/.opencode/command/docs/summarize.md +10 -0
- package/.opencode/command/docs/update.md +18 -0
- package/.opencode/command/fix/ci.md +8 -0
- package/.opencode/command/fix/fast.md +11 -0
- package/.opencode/command/fix/hard.md +15 -0
- package/.opencode/command/fix/logs.md +16 -0
- package/.opencode/command/fix/test.md +18 -0
- package/.opencode/command/fix/types.md +10 -0
- package/.opencode/command/git/cm.md +5 -0
- package/.opencode/command/git/cp.md +4 -0
- package/.opencode/command/plan/ci.md +12 -0
- package/.opencode/command/plan/two.md +13 -0
- package/.opencode/command/plan.md +10 -0
- package/.opencode/command/test.md +7 -0
- package/.opencode/command/watzup.md +8 -0
- package/.releaserc.json +17 -0
- package/.repomixignore +15 -0
- package/AGENTS.md +217 -0
- package/CHANGELOG.md +16 -0
- package/CLAUDE.md +33 -0
- package/README.md +214 -0
- package/biome.json +25 -0
- package/bun.lock +1238 -0
- package/dist/index.js +19100 -0
- package/docs/code-standards.md +1128 -0
- package/docs/codebase-summary.md +821 -0
- package/docs/github-setup.md +176 -0
- package/docs/project-pdr.md +739 -0
- package/docs/system-architecture.md +950 -0
- package/docs/tech-stack.md +290 -0
- package/package.json +60 -0
- package/plans/251008-claudekit-cli-implementation-plan.md +1469 -0
- package/plans/reports/251008-from-code-reviewer-to-developer-review-report.md +864 -0
- package/plans/reports/251008-from-tester-to-developer-test-summary-report.md +409 -0
- package/plans/reports/251008-researcher-download-extraction-report.md +1377 -0
- package/plans/reports/251008-researcher-github-api-report.md +1339 -0
- package/plans/research/251008-cli-frameworks-bun-research.md +1051 -0
- package/plans/templates/bug-fix-template.md +69 -0
- package/plans/templates/feature-implementation-template.md +84 -0
- package/plans/templates/refactor-template.md +82 -0
- package/plans/templates/template-usage-guide.md +58 -0
- package/src/commands/new.ts +118 -0
- package/src/commands/update.ts +114 -0
- package/src/index.ts +44 -0
- package/src/lib/auth.ts +157 -0
- package/src/lib/download.ts +180 -0
- package/src/lib/github.ts +157 -0
- package/src/lib/merge.ts +116 -0
- package/src/lib/prompts.ts +113 -0
- package/src/types.ts +149 -0
- package/src/utils/config.ts +87 -0
- package/src/utils/logger.ts +37 -0
- package/tests/lib/auth.test.ts +116 -0
- package/tests/lib/download.test.ts +70 -0
- package/tests/lib/github.test.ts +52 -0
- package/tests/lib/merge.test.ts +138 -0
- package/tests/lib/prompts.test.ts +66 -0
- package/tests/types.test.ts +255 -0
- package/tests/utils/config.test.ts +263 -0
- package/tests/utils/logger.test.ts +124 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,138 @@
|
|
|
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 { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { FileMerger } from "../../src/lib/merge.js";
|
|
7
|
+
|
|
8
|
+
describe("FileMerger", () => {
|
|
9
|
+
let merger: FileMerger;
|
|
10
|
+
let testSourceDir: string;
|
|
11
|
+
let testDestDir: string;
|
|
12
|
+
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
merger = new FileMerger();
|
|
15
|
+
|
|
16
|
+
// Create temporary test directories
|
|
17
|
+
const timestamp = Date.now();
|
|
18
|
+
testSourceDir = join(tmpdir(), `test-source-${timestamp}`);
|
|
19
|
+
testDestDir = join(tmpdir(), `test-dest-${timestamp}`);
|
|
20
|
+
|
|
21
|
+
await mkdir(testSourceDir, { recursive: true });
|
|
22
|
+
await mkdir(testDestDir, { recursive: true });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
// Cleanup test directories
|
|
27
|
+
if (existsSync(testSourceDir)) {
|
|
28
|
+
await rm(testSourceDir, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
if (existsSync(testDestDir)) {
|
|
31
|
+
await rm(testDestDir, { recursive: true, force: true });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("constructor", () => {
|
|
36
|
+
test("should create FileMerger instance", () => {
|
|
37
|
+
expect(merger).toBeInstanceOf(FileMerger);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("addIgnorePatterns", () => {
|
|
42
|
+
test("should add custom ignore patterns", () => {
|
|
43
|
+
const patterns = ["*.log", "temp/**"];
|
|
44
|
+
expect(() => merger.addIgnorePatterns(patterns)).not.toThrow();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("should accept empty array", () => {
|
|
48
|
+
expect(() => merger.addIgnorePatterns([])).not.toThrow();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("merge with skipConfirmation", () => {
|
|
53
|
+
test("should copy files from source to destination", async () => {
|
|
54
|
+
// Create test files
|
|
55
|
+
await writeFile(join(testSourceDir, "test.txt"), "test content");
|
|
56
|
+
await writeFile(join(testSourceDir, "readme.md"), "# README");
|
|
57
|
+
|
|
58
|
+
await merger.merge(testSourceDir, testDestDir, true);
|
|
59
|
+
|
|
60
|
+
// Verify files were copied
|
|
61
|
+
expect(existsSync(join(testDestDir, "test.txt"))).toBe(true);
|
|
62
|
+
expect(existsSync(join(testDestDir, "readme.md"))).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("should skip protected files like .env", async () => {
|
|
66
|
+
// Create test files including protected ones
|
|
67
|
+
await writeFile(join(testSourceDir, "normal.txt"), "normal");
|
|
68
|
+
await writeFile(join(testSourceDir, ".env"), "SECRET=value");
|
|
69
|
+
|
|
70
|
+
await merger.merge(testSourceDir, testDestDir, true);
|
|
71
|
+
|
|
72
|
+
// Verify normal file was copied but .env was not
|
|
73
|
+
expect(existsSync(join(testDestDir, "normal.txt"))).toBe(true);
|
|
74
|
+
expect(existsSync(join(testDestDir, ".env"))).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("should skip protected patterns like *.key", async () => {
|
|
78
|
+
await writeFile(join(testSourceDir, "normal.txt"), "normal");
|
|
79
|
+
await writeFile(join(testSourceDir, "private.key"), "key data");
|
|
80
|
+
|
|
81
|
+
await merger.merge(testSourceDir, testDestDir, true);
|
|
82
|
+
|
|
83
|
+
expect(existsSync(join(testDestDir, "normal.txt"))).toBe(true);
|
|
84
|
+
expect(existsSync(join(testDestDir, "private.key"))).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("should handle nested directories", async () => {
|
|
88
|
+
const nestedDir = join(testSourceDir, "nested", "deep");
|
|
89
|
+
await mkdir(nestedDir, { recursive: true });
|
|
90
|
+
await writeFile(join(nestedDir, "file.txt"), "nested content");
|
|
91
|
+
|
|
92
|
+
await merger.merge(testSourceDir, testDestDir, true);
|
|
93
|
+
|
|
94
|
+
expect(existsSync(join(testDestDir, "nested", "deep", "file.txt"))).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("should overwrite existing files", async () => {
|
|
98
|
+
// Create files in both directories
|
|
99
|
+
await writeFile(join(testSourceDir, "file.txt"), "new content");
|
|
100
|
+
await writeFile(join(testDestDir, "file.txt"), "old content");
|
|
101
|
+
|
|
102
|
+
await merger.merge(testSourceDir, testDestDir, true);
|
|
103
|
+
|
|
104
|
+
const content = await Bun.file(join(testDestDir, "file.txt")).text();
|
|
105
|
+
expect(content).toBe("new content");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("should handle empty source directory", async () => {
|
|
109
|
+
// Empty directory should complete without errors
|
|
110
|
+
await merger.merge(testSourceDir, testDestDir, true);
|
|
111
|
+
// If we get here, the test passed
|
|
112
|
+
expect(true).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe("edge cases", () => {
|
|
117
|
+
test("should handle files with special characters in names", async () => {
|
|
118
|
+
const specialFileName = "file with spaces.txt";
|
|
119
|
+
await writeFile(join(testSourceDir, specialFileName), "content");
|
|
120
|
+
|
|
121
|
+
await merger.merge(testSourceDir, testDestDir, true);
|
|
122
|
+
|
|
123
|
+
expect(existsSync(join(testDestDir, specialFileName))).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("should skip custom ignore patterns", async () => {
|
|
127
|
+
merger.addIgnorePatterns(["custom-*"]);
|
|
128
|
+
|
|
129
|
+
await writeFile(join(testSourceDir, "normal.txt"), "normal");
|
|
130
|
+
await writeFile(join(testSourceDir, "custom-ignore.txt"), "ignore me");
|
|
131
|
+
|
|
132
|
+
await merger.merge(testSourceDir, testDestDir, true);
|
|
133
|
+
|
|
134
|
+
expect(existsSync(join(testDestDir, "normal.txt"))).toBe(true);
|
|
135
|
+
expect(existsSync(join(testDestDir, "custom-ignore.txt"))).toBe(false);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
AVAILABLE_KITS,
|
|
4
|
+
AuthenticationError,
|
|
5
|
+
ClaudeKitError,
|
|
6
|
+
ConfigSchema,
|
|
7
|
+
DownloadError,
|
|
8
|
+
ExtractionError,
|
|
9
|
+
GitHubError,
|
|
10
|
+
GitHubReleaseAssetSchema,
|
|
11
|
+
GitHubReleaseSchema,
|
|
12
|
+
KitConfigSchema,
|
|
13
|
+
KitType,
|
|
14
|
+
NewCommandOptionsSchema,
|
|
15
|
+
UpdateCommandOptionsSchema,
|
|
16
|
+
} from "../src/types.js";
|
|
17
|
+
|
|
18
|
+
describe("Types and Schemas", () => {
|
|
19
|
+
describe("KitType", () => {
|
|
20
|
+
test("should validate correct kit types", () => {
|
|
21
|
+
expect(KitType.parse("engineer")).toBe("engineer");
|
|
22
|
+
expect(KitType.parse("marketing")).toBe("marketing");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("should reject invalid kit types", () => {
|
|
26
|
+
expect(() => KitType.parse("invalid")).toThrow();
|
|
27
|
+
expect(() => KitType.parse("")).toThrow();
|
|
28
|
+
expect(() => KitType.parse(123)).toThrow();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("NewCommandOptionsSchema", () => {
|
|
33
|
+
test("should validate correct options", () => {
|
|
34
|
+
const result = NewCommandOptionsSchema.parse({
|
|
35
|
+
dir: "./test",
|
|
36
|
+
kit: "engineer",
|
|
37
|
+
version: "v1.0.0",
|
|
38
|
+
});
|
|
39
|
+
expect(result.dir).toBe("./test");
|
|
40
|
+
expect(result.kit).toBe("engineer");
|
|
41
|
+
expect(result.version).toBe("v1.0.0");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("should use default values", () => {
|
|
45
|
+
const result = NewCommandOptionsSchema.parse({});
|
|
46
|
+
expect(result.dir).toBe(".");
|
|
47
|
+
expect(result.kit).toBeUndefined();
|
|
48
|
+
expect(result.version).toBeUndefined();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("should accept optional fields", () => {
|
|
52
|
+
const result = NewCommandOptionsSchema.parse({ dir: "./custom" });
|
|
53
|
+
expect(result.dir).toBe("./custom");
|
|
54
|
+
expect(result.kit).toBeUndefined();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("UpdateCommandOptionsSchema", () => {
|
|
59
|
+
test("should validate correct options", () => {
|
|
60
|
+
const result = UpdateCommandOptionsSchema.parse({
|
|
61
|
+
dir: "./test",
|
|
62
|
+
kit: "engineer",
|
|
63
|
+
version: "v2.0.0",
|
|
64
|
+
});
|
|
65
|
+
expect(result.dir).toBe("./test");
|
|
66
|
+
expect(result.kit).toBe("engineer");
|
|
67
|
+
expect(result.version).toBe("v2.0.0");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("should use default values", () => {
|
|
71
|
+
const result = UpdateCommandOptionsSchema.parse({});
|
|
72
|
+
expect(result.dir).toBe(".");
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("ConfigSchema", () => {
|
|
77
|
+
test("should validate complete config", () => {
|
|
78
|
+
const config = {
|
|
79
|
+
github: {
|
|
80
|
+
token: "ghp_test123456789",
|
|
81
|
+
},
|
|
82
|
+
defaults: {
|
|
83
|
+
kit: "engineer",
|
|
84
|
+
dir: "./projects",
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
const result = ConfigSchema.parse(config);
|
|
88
|
+
expect(result.github?.token).toBe("ghp_test123456789");
|
|
89
|
+
expect(result.defaults?.kit).toBe("engineer");
|
|
90
|
+
expect(result.defaults?.dir).toBe("./projects");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("should validate empty config", () => {
|
|
94
|
+
const result = ConfigSchema.parse({});
|
|
95
|
+
expect(result.github).toBeUndefined();
|
|
96
|
+
expect(result.defaults).toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("should validate partial config", () => {
|
|
100
|
+
const result = ConfigSchema.parse({ github: {} });
|
|
101
|
+
expect(result.github).toEqual({});
|
|
102
|
+
expect(result.defaults).toBeUndefined();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe("GitHubReleaseAssetSchema", () => {
|
|
107
|
+
test("should validate correct asset", () => {
|
|
108
|
+
const asset = {
|
|
109
|
+
id: 123,
|
|
110
|
+
name: "release.tar.gz",
|
|
111
|
+
browser_download_url: "https://github.com/test/release.tar.gz",
|
|
112
|
+
size: 1024,
|
|
113
|
+
content_type: "application/gzip",
|
|
114
|
+
};
|
|
115
|
+
const result = GitHubReleaseAssetSchema.parse(asset);
|
|
116
|
+
expect(result.id).toBe(123);
|
|
117
|
+
expect(result.name).toBe("release.tar.gz");
|
|
118
|
+
expect(result.size).toBe(1024);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("should reject invalid URL", () => {
|
|
122
|
+
const asset = {
|
|
123
|
+
id: 123,
|
|
124
|
+
name: "release.tar.gz",
|
|
125
|
+
browser_download_url: "not-a-url",
|
|
126
|
+
size: 1024,
|
|
127
|
+
content_type: "application/gzip",
|
|
128
|
+
};
|
|
129
|
+
expect(() => GitHubReleaseAssetSchema.parse(asset)).toThrow();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("should reject missing required fields", () => {
|
|
133
|
+
const asset = {
|
|
134
|
+
id: 123,
|
|
135
|
+
name: "release.tar.gz",
|
|
136
|
+
};
|
|
137
|
+
expect(() => GitHubReleaseAssetSchema.parse(asset)).toThrow();
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe("GitHubReleaseSchema", () => {
|
|
142
|
+
test("should validate complete release", () => {
|
|
143
|
+
const release = {
|
|
144
|
+
id: 1,
|
|
145
|
+
tag_name: "v1.0.0",
|
|
146
|
+
name: "Version 1.0.0",
|
|
147
|
+
draft: false,
|
|
148
|
+
prerelease: false,
|
|
149
|
+
assets: [
|
|
150
|
+
{
|
|
151
|
+
id: 123,
|
|
152
|
+
name: "release.tar.gz",
|
|
153
|
+
browser_download_url: "https://github.com/test/release.tar.gz",
|
|
154
|
+
size: 1024,
|
|
155
|
+
content_type: "application/gzip",
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
published_at: "2024-01-01T00:00:00Z",
|
|
159
|
+
};
|
|
160
|
+
const result = GitHubReleaseSchema.parse(release);
|
|
161
|
+
expect(result.id).toBe(1);
|
|
162
|
+
expect(result.tag_name).toBe("v1.0.0");
|
|
163
|
+
expect(result.assets).toHaveLength(1);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("should validate release without published_at", () => {
|
|
167
|
+
const release = {
|
|
168
|
+
id: 1,
|
|
169
|
+
tag_name: "v1.0.0",
|
|
170
|
+
name: "Version 1.0.0",
|
|
171
|
+
draft: false,
|
|
172
|
+
prerelease: false,
|
|
173
|
+
assets: [],
|
|
174
|
+
};
|
|
175
|
+
const result = GitHubReleaseSchema.parse(release);
|
|
176
|
+
expect(result.published_at).toBeUndefined();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe("KitConfigSchema", () => {
|
|
181
|
+
test("should validate correct kit config", () => {
|
|
182
|
+
const config = {
|
|
183
|
+
name: "Test Kit",
|
|
184
|
+
repo: "test-repo",
|
|
185
|
+
owner: "test-owner",
|
|
186
|
+
description: "Test description",
|
|
187
|
+
};
|
|
188
|
+
const result = KitConfigSchema.parse(config);
|
|
189
|
+
expect(result.name).toBe("Test Kit");
|
|
190
|
+
expect(result.repo).toBe("test-repo");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("should reject missing fields", () => {
|
|
194
|
+
const config = {
|
|
195
|
+
name: "Test Kit",
|
|
196
|
+
repo: "test-repo",
|
|
197
|
+
};
|
|
198
|
+
expect(() => KitConfigSchema.parse(config)).toThrow();
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe("AVAILABLE_KITS", () => {
|
|
203
|
+
test("should have engineer kit", () => {
|
|
204
|
+
expect(AVAILABLE_KITS.engineer).toBeDefined();
|
|
205
|
+
expect(AVAILABLE_KITS.engineer.name).toBe("ClaudeKit Engineer");
|
|
206
|
+
expect(AVAILABLE_KITS.engineer.repo).toBe("claudekit-engineer");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("should have marketing kit", () => {
|
|
210
|
+
expect(AVAILABLE_KITS.marketing).toBeDefined();
|
|
211
|
+
expect(AVAILABLE_KITS.marketing.name).toBe("ClaudeKit Marketing");
|
|
212
|
+
expect(AVAILABLE_KITS.marketing.repo).toBe("claudekit-marketing");
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("Custom Error Classes", () => {
|
|
217
|
+
test("ClaudeKitError should store code and statusCode", () => {
|
|
218
|
+
const error = new ClaudeKitError("Test error", "TEST_CODE", 500);
|
|
219
|
+
expect(error.message).toBe("Test error");
|
|
220
|
+
expect(error.code).toBe("TEST_CODE");
|
|
221
|
+
expect(error.statusCode).toBe(500);
|
|
222
|
+
expect(error.name).toBe("ClaudeKitError");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("AuthenticationError should set correct defaults", () => {
|
|
226
|
+
const error = new AuthenticationError("Auth failed");
|
|
227
|
+
expect(error.message).toBe("Auth failed");
|
|
228
|
+
expect(error.code).toBe("AUTH_ERROR");
|
|
229
|
+
expect(error.statusCode).toBe(401);
|
|
230
|
+
expect(error.name).toBe("AuthenticationError");
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("GitHubError should store statusCode", () => {
|
|
234
|
+
const error = new GitHubError("GitHub failed", 404);
|
|
235
|
+
expect(error.message).toBe("GitHub failed");
|
|
236
|
+
expect(error.code).toBe("GITHUB_ERROR");
|
|
237
|
+
expect(error.statusCode).toBe(404);
|
|
238
|
+
expect(error.name).toBe("GitHubError");
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("DownloadError should have correct code", () => {
|
|
242
|
+
const error = new DownloadError("Download failed");
|
|
243
|
+
expect(error.message).toBe("Download failed");
|
|
244
|
+
expect(error.code).toBe("DOWNLOAD_ERROR");
|
|
245
|
+
expect(error.name).toBe("DownloadError");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("ExtractionError should have correct code", () => {
|
|
249
|
+
const error = new ExtractionError("Extract failed");
|
|
250
|
+
expect(error.message).toBe("Extract failed");
|
|
251
|
+
expect(error.code).toBe("EXTRACTION_ERROR");
|
|
252
|
+
expect(error.name).toBe("ExtractionError");
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
});
|