craftdesk 0.2.0 → 0.4.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 (175) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +968 -92
  3. package/dist/commands/add.d.ts +5 -0
  4. package/dist/commands/add.d.ts.map +1 -1
  5. package/dist/commands/add.js +303 -7
  6. package/dist/commands/add.js.map +1 -1
  7. package/dist/commands/convert.d.ts +8 -0
  8. package/dist/commands/convert.d.ts.map +1 -0
  9. package/dist/commands/convert.js +146 -0
  10. package/dist/commands/convert.js.map +1 -0
  11. package/dist/commands/info.d.ts +8 -0
  12. package/dist/commands/info.d.ts.map +1 -0
  13. package/dist/commands/info.js +66 -0
  14. package/dist/commands/info.js.map +1 -0
  15. package/dist/commands/init.d.ts +5 -0
  16. package/dist/commands/init.d.ts.map +1 -1
  17. package/dist/commands/init.js +9 -3
  18. package/dist/commands/init.js.map +1 -1
  19. package/dist/commands/install.d.ts +5 -0
  20. package/dist/commands/install.d.ts.map +1 -1
  21. package/dist/commands/install.js +24 -4
  22. package/dist/commands/install.js.map +1 -1
  23. package/dist/commands/list.d.ts +5 -0
  24. package/dist/commands/list.d.ts.map +1 -1
  25. package/dist/commands/list.js +72 -7
  26. package/dist/commands/list.js.map +1 -1
  27. package/dist/commands/login.d.ts +8 -0
  28. package/dist/commands/login.d.ts.map +1 -0
  29. package/dist/commands/login.js +158 -0
  30. package/dist/commands/login.js.map +1 -0
  31. package/dist/commands/logout.d.ts +8 -0
  32. package/dist/commands/logout.d.ts.map +1 -0
  33. package/dist/commands/logout.js +51 -0
  34. package/dist/commands/logout.js.map +1 -0
  35. package/dist/commands/outdated.d.ts +23 -0
  36. package/dist/commands/outdated.d.ts.map +1 -0
  37. package/dist/commands/outdated.js +268 -0
  38. package/dist/commands/outdated.js.map +1 -0
  39. package/dist/commands/publish.d.ts +8 -0
  40. package/dist/commands/publish.d.ts.map +1 -0
  41. package/dist/commands/publish.js +198 -0
  42. package/dist/commands/publish.js.map +1 -0
  43. package/dist/commands/remove.d.ts +5 -0
  44. package/dist/commands/remove.d.ts.map +1 -1
  45. package/dist/commands/remove.js +52 -11
  46. package/dist/commands/remove.js.map +1 -1
  47. package/dist/commands/search.d.ts +8 -0
  48. package/dist/commands/search.d.ts.map +1 -0
  49. package/dist/commands/search.js +60 -0
  50. package/dist/commands/search.js.map +1 -0
  51. package/dist/commands/update.d.ts +8 -0
  52. package/dist/commands/update.d.ts.map +1 -0
  53. package/dist/commands/update.js +369 -0
  54. package/dist/commands/update.js.map +1 -0
  55. package/dist/commands/whoami.d.ts +8 -0
  56. package/dist/commands/whoami.d.ts.map +1 -0
  57. package/dist/commands/whoami.js +58 -0
  58. package/dist/commands/whoami.js.map +1 -0
  59. package/dist/converters/base-converter.d.ts +89 -0
  60. package/dist/converters/base-converter.d.ts.map +1 -0
  61. package/dist/converters/base-converter.js +249 -0
  62. package/dist/converters/base-converter.js.map +1 -0
  63. package/dist/converters/continue-converter.d.ts +53 -0
  64. package/dist/converters/continue-converter.d.ts.map +1 -0
  65. package/dist/converters/continue-converter.js +243 -0
  66. package/dist/converters/continue-converter.js.map +1 -0
  67. package/dist/converters/cursor-converter.d.ts +70 -0
  68. package/dist/converters/cursor-converter.d.ts.map +1 -0
  69. package/dist/converters/cursor-converter.js +272 -0
  70. package/dist/converters/cursor-converter.js.map +1 -0
  71. package/dist/index.js +35 -4
  72. package/dist/index.js.map +1 -1
  73. package/dist/services/config-manager.d.ts +128 -10
  74. package/dist/services/config-manager.d.ts.map +1 -1
  75. package/dist/services/config-manager.js +189 -18
  76. package/dist/services/config-manager.js.map +1 -1
  77. package/dist/services/converter.d.ts +53 -0
  78. package/dist/services/converter.d.ts.map +1 -0
  79. package/dist/services/converter.js +185 -0
  80. package/dist/services/converter.js.map +1 -0
  81. package/dist/services/craft-wrapper.d.ts +108 -0
  82. package/dist/services/craft-wrapper.d.ts.map +1 -0
  83. package/dist/services/craft-wrapper.js +268 -0
  84. package/dist/services/craft-wrapper.js.map +1 -0
  85. package/dist/services/git-resolver.d.ts +9 -3
  86. package/dist/services/git-resolver.d.ts.map +1 -1
  87. package/dist/services/git-resolver.js +35 -10
  88. package/dist/services/git-resolver.js.map +1 -1
  89. package/dist/services/installer.d.ts +51 -1
  90. package/dist/services/installer.d.ts.map +1 -1
  91. package/dist/services/installer.js +245 -14
  92. package/dist/services/installer.js.map +1 -1
  93. package/dist/services/plugin-resolver.d.ts +80 -0
  94. package/dist/services/plugin-resolver.d.ts.map +1 -0
  95. package/dist/services/plugin-resolver.js +182 -0
  96. package/dist/services/plugin-resolver.js.map +1 -0
  97. package/dist/services/registry-client.d.ts +132 -5
  98. package/dist/services/registry-client.d.ts.map +1 -1
  99. package/dist/services/registry-client.js +239 -34
  100. package/dist/services/registry-client.js.map +1 -1
  101. package/dist/services/settings-manager.d.ts +151 -0
  102. package/dist/services/settings-manager.d.ts.map +1 -0
  103. package/dist/services/settings-manager.js +333 -0
  104. package/dist/services/settings-manager.js.map +1 -0
  105. package/dist/types/claude-settings.d.ts +155 -0
  106. package/dist/types/claude-settings.d.ts.map +1 -0
  107. package/dist/types/claude-settings.js +20 -0
  108. package/dist/types/claude-settings.js.map +1 -0
  109. package/dist/types/converter.d.ts +98 -0
  110. package/dist/types/converter.d.ts.map +1 -0
  111. package/dist/types/converter.js +6 -0
  112. package/dist/types/converter.js.map +1 -0
  113. package/dist/types/craftdesk-json.d.ts +63 -3
  114. package/dist/types/craftdesk-json.d.ts.map +1 -1
  115. package/dist/types/craftdesk-lock.d.ts +62 -1
  116. package/dist/types/craftdesk-lock.d.ts.map +1 -1
  117. package/dist/utils/crypto.d.ts +23 -0
  118. package/dist/utils/crypto.d.ts.map +1 -0
  119. package/dist/utils/crypto.js +52 -0
  120. package/dist/utils/crypto.js.map +1 -0
  121. package/dist/utils/file-system.d.ts +43 -0
  122. package/dist/utils/file-system.d.ts.map +1 -1
  123. package/dist/utils/file-system.js +45 -2
  124. package/dist/utils/file-system.js.map +1 -1
  125. package/dist/utils/logger.d.ts +75 -0
  126. package/dist/utils/logger.d.ts.map +1 -1
  127. package/dist/utils/logger.js +76 -1
  128. package/dist/utils/logger.js.map +1 -1
  129. package/dist/utils/version-utils.d.ts +63 -0
  130. package/dist/utils/version-utils.d.ts.map +1 -0
  131. package/dist/utils/version-utils.js +130 -0
  132. package/dist/utils/version-utils.js.map +1 -0
  133. package/package.json +36 -9
  134. package/.claude/settings.local.json +0 -17
  135. package/.github/workflows/ci.yml +0 -94
  136. package/API.md +0 -414
  137. package/TESTING.md +0 -263
  138. package/dist/commands/config.d.ts +0 -3
  139. package/dist/commands/config.d.ts.map +0 -1
  140. package/dist/commands/config.js +0 -56
  141. package/dist/commands/config.js.map +0 -1
  142. package/dist/types/config.d.ts +0 -17
  143. package/dist/types/config.d.ts.map +0 -1
  144. package/dist/types/config.js +0 -3
  145. package/dist/types/config.js.map +0 -1
  146. package/docs/DEPENDENCY_RESOLUTION.md +0 -237
  147. package/examples/craftdesk-all-sources.json +0 -79
  148. package/examples/craftdesk-git-monorepo.json +0 -41
  149. package/examples/craftdesk-multi-registry.json +0 -40
  150. package/examples/craftdesk-with-registries.json +0 -49
  151. package/examples/simple-craftdesk.json +0 -10
  152. package/src/commands/add.ts +0 -314
  153. package/src/commands/init.ts +0 -55
  154. package/src/commands/install.ts +0 -126
  155. package/src/commands/list.ts +0 -126
  156. package/src/commands/remove.ts +0 -82
  157. package/src/index.ts +0 -56
  158. package/src/services/config-manager.ts +0 -150
  159. package/src/services/git-resolver.ts +0 -370
  160. package/src/services/installer.ts +0 -314
  161. package/src/services/registry-client.ts +0 -163
  162. package/src/types/craftdesk-json.ts +0 -53
  163. package/src/types/craftdesk-lock.ts +0 -38
  164. package/src/utils/file-system.ts +0 -55
  165. package/src/utils/logger.ts +0 -82
  166. package/tests/fixtures/craftdesk-git.json +0 -17
  167. package/tests/fixtures/craftdesk.json +0 -15
  168. package/tests/helpers/test-utils.ts +0 -60
  169. package/tests/integration/init-command.test.ts +0 -70
  170. package/tests/integration/list-command.test.ts +0 -77
  171. package/tests/unit/config-manager.test.ts +0 -228
  172. package/tests/unit/git-resolver.test.ts +0 -303
  173. package/tests/unit/installer.test.ts +0 -105
  174. package/tsconfig.json +0 -20
  175. package/vitest.config.ts +0 -29
@@ -1,17 +0,0 @@
1
- {
2
- "name": "git-test-project",
3
- "version": "1.0.0",
4
- "type": "skill",
5
- "description": "Test project with git dependencies",
6
- "dependencies": {
7
- "custom-auth": {
8
- "git": "https://github.com/company/auth.git",
9
- "branch": "main"
10
- },
11
- "monorepo-skill": {
12
- "git": "https://github.com/company/monorepo.git",
13
- "tag": "v3.0.0",
14
- "path": "skills/auth"
15
- }
16
- }
17
- }
@@ -1,15 +0,0 @@
1
- {
2
- "name": "test-project",
3
- "version": "1.0.0",
4
- "type": "skill",
5
- "description": "Test project for unit tests",
6
- "dependencies": {
7
- "ruby-on-rails": "^7.0.0",
8
- "postgres-expert": "^1.0.0"
9
- },
10
- "registries": {
11
- "default": {
12
- "url": "https://craftdesk.ai"
13
- }
14
- }
15
- }
@@ -1,60 +0,0 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
-
4
- /**
5
- * Creates a temporary test directory
6
- */
7
- export async function createTempDir(prefix: string = 'test-'): Promise<string> {
8
- const tmpDir = path.join(process.cwd(), '.test-tmp', `${prefix}${Date.now()}`);
9
- await fs.ensureDir(tmpDir);
10
- return tmpDir;
11
- }
12
-
13
- /**
14
- * Cleans up a temporary directory
15
- */
16
- export async function cleanupTempDir(dirPath: string): Promise<void> {
17
- if (dirPath.includes('.test-tmp')) {
18
- await fs.remove(dirPath);
19
- }
20
- }
21
-
22
- /**
23
- * Copies a fixture file to a target directory
24
- */
25
- export async function copyFixture(fixtureName: string, targetDir: string): Promise<void> {
26
- const fixturePath = path.join(__dirname, '..', 'fixtures', fixtureName);
27
- const targetPath = path.join(targetDir, path.basename(fixtureName));
28
- await fs.copy(fixturePath, targetPath);
29
- }
30
-
31
- /**
32
- * Reads a JSON file and returns parsed content
33
- */
34
- export async function readJsonFile<T = any>(filePath: string): Promise<T> {
35
- const content = await fs.readFile(filePath, 'utf-8');
36
- return JSON.parse(content);
37
- }
38
-
39
- /**
40
- * Writes a JSON object to a file
41
- */
42
- export async function writeJsonFile(filePath: string, data: any): Promise<void> {
43
- await fs.writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');
44
- }
45
-
46
- /**
47
- * Mock registry response for testing
48
- */
49
- export function createMockCraftInfo(name: string, version: string = '1.0.0') {
50
- return {
51
- name,
52
- version,
53
- type: 'skill',
54
- author: 'test-author',
55
- description: 'Test craft',
56
- download_url: `https://craftdesk.ai/api/v1/crafts/test-author/${name}/versions/${version}/download`,
57
- integrity: 'sha256-test123',
58
- dependencies: {}
59
- };
60
- }
@@ -1,70 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { execSync } from 'child_process';
3
- import { createTempDir, cleanupTempDir, readJsonFile } from '../helpers/test-utils';
4
- import path from 'path';
5
- import fs from 'fs-extra';
6
-
7
- describe('craftdesk init command', () => {
8
- let tempDir: string;
9
- let originalCwd: string;
10
- const cliPath = path.join(__dirname, '../../dist/index.js');
11
-
12
- beforeEach(async () => {
13
- tempDir = await createTempDir('init-test-');
14
- originalCwd = process.cwd();
15
- process.chdir(tempDir);
16
- });
17
-
18
- afterEach(async () => {
19
- process.chdir(originalCwd);
20
- await cleanupTempDir(tempDir);
21
- });
22
-
23
- it('should create a new craftdesk.json file', async () => {
24
- // Run init command
25
- execSync(`node ${cliPath} init --name test-project --project-version 1.0.0 --type skill`, {
26
- stdio: 'pipe'
27
- });
28
-
29
- const craftdeskPath = path.join(tempDir, 'craftdesk.json');
30
- const exists = await fs.pathExists(craftdeskPath);
31
- expect(exists).toBe(true);
32
-
33
- const config = await readJsonFile(craftdeskPath);
34
- expect(config.name).toBe('test-project');
35
- expect(config.version).toBe('1.0.0');
36
- expect(config.type).toBe('skill');
37
- });
38
-
39
- it('should include default registry configuration', async () => {
40
- execSync(`node ${cliPath} init --name test --project-version 1.0.0 --type agent`, {
41
- stdio: 'pipe'
42
- });
43
-
44
- const config = await readJsonFile(path.join(tempDir, 'craftdesk.json'));
45
- expect(config.registries).toBeDefined();
46
- expect(config.registries.default).toBeDefined();
47
- expect(config.registries.default.url).toBe('https://craftdesk.ai');
48
- });
49
-
50
- it('should not overwrite existing craftdesk.json', async () => {
51
- // Create existing file
52
- await fs.writeJson(path.join(tempDir, 'craftdesk.json'), {
53
- name: 'existing',
54
- version: '2.0.0'
55
- });
56
-
57
- // Try to init again
58
- try {
59
- execSync(`node ${cliPath} init --name test --project-version 1.0.0 --type skill`, {
60
- stdio: 'pipe'
61
- });
62
- } catch (error) {
63
- // Expected to fail
64
- }
65
-
66
- const config = await readJsonFile(path.join(tempDir, 'craftdesk.json'));
67
- expect(config.name).toBe('existing');
68
- expect(config.version).toBe('2.0.0');
69
- });
70
- });
@@ -1,77 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { execSync } from 'child_process';
3
- import { createTempDir, cleanupTempDir, writeJsonFile } from '../helpers/test-utils';
4
- import path from 'path';
5
- import fs from 'fs-extra';
6
-
7
- describe('craftdesk list command', () => {
8
- let tempDir: string;
9
- let originalCwd: string;
10
- const cliPath = path.join(__dirname, '../../dist/index.js');
11
-
12
- beforeEach(async () => {
13
- tempDir = await createTempDir('list-test-');
14
- originalCwd = process.cwd();
15
- process.chdir(tempDir);
16
- });
17
-
18
- afterEach(async () => {
19
- process.chdir(originalCwd);
20
- await cleanupTempDir(tempDir);
21
- });
22
-
23
- it('should list installed crafts', async () => {
24
- // Create craftdesk.json
25
- await writeJsonFile(path.join(tempDir, 'craftdesk.json'), {
26
- name: 'test-project',
27
- version: '1.0.0',
28
- dependencies: {
29
- 'ruby-on-rails': '^7.0.0',
30
- 'postgres-expert': '^1.0.0'
31
- }
32
- });
33
-
34
- // Create mock installed crafts
35
- const skillDir = path.join(tempDir, '.claude', 'skills', 'ruby-on-rails');
36
- await fs.ensureDir(skillDir);
37
- await writeJsonFile(path.join(skillDir, '.craftdesk-metadata.json'), {
38
- name: 'ruby-on-rails',
39
- version: '7.1.0',
40
- type: 'skill',
41
- installedAt: new Date().toISOString()
42
- });
43
-
44
- const agentDir = path.join(tempDir, '.claude', 'agents', 'postgres-expert');
45
- await fs.ensureDir(agentDir);
46
- await writeJsonFile(path.join(agentDir, '.craftdesk-metadata.json'), {
47
- name: 'postgres-expert',
48
- version: '1.2.0',
49
- type: 'agent',
50
- installedAt: new Date().toISOString()
51
- });
52
-
53
- const output = execSync(`node ${cliPath} list`, {
54
- encoding: 'utf-8'
55
- });
56
-
57
- expect(output).toContain('ruby-on-rails');
58
- expect(output).toContain('7.1.0');
59
- expect(output).toContain('postgres-expert');
60
- expect(output).toContain('1.2.0');
61
- });
62
-
63
- it('should handle no installed crafts', async () => {
64
- // Create craftdesk.json first
65
- await writeJsonFile(path.join(tempDir, 'craftdesk.json'), {
66
- name: 'empty-project',
67
- version: '1.0.0',
68
- dependencies: {}
69
- });
70
-
71
- const output = execSync(`node ${cliPath} list`, {
72
- encoding: 'utf-8'
73
- });
74
-
75
- expect(output).toContain('No crafts installed');
76
- });
77
- });
@@ -1,228 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { ConfigManager } from '../../src/services/config-manager';
3
- import { createTempDir, cleanupTempDir, writeJsonFile } from '../helpers/test-utils';
4
- import path from 'path';
5
-
6
- describe('ConfigManager', () => {
7
- let configManager: ConfigManager;
8
- let tempDir: string;
9
- let originalCwd: string;
10
-
11
- beforeEach(async () => {
12
- configManager = new ConfigManager();
13
- tempDir = await createTempDir('config-test-');
14
- originalCwd = process.cwd();
15
- process.chdir(tempDir);
16
- });
17
-
18
- afterEach(async () => {
19
- process.chdir(originalCwd);
20
- await cleanupTempDir(tempDir);
21
- });
22
-
23
- describe('getRegistryForCraft', () => {
24
- it('should return default registry when no craftdesk.json exists', async () => {
25
- const registry = await configManager.getRegistryForCraft('test-craft');
26
- expect(registry).toBe('https://craftdesk.ai');
27
- });
28
-
29
- it('should return default registry from craftdesk.json', async () => {
30
- await writeJsonFile(path.join(tempDir, 'craftdesk.json'), {
31
- name: 'test',
32
- version: '1.0.0',
33
- registries: {
34
- default: {
35
- url: 'https://custom-registry.com'
36
- }
37
- }
38
- });
39
-
40
- const registry = await configManager.getRegistryForCraft('test-craft');
41
- expect(registry).toBe('https://custom-registry.com');
42
- });
43
-
44
- it('should return scoped registry for scoped craft', async () => {
45
- await writeJsonFile(path.join(tempDir, 'craftdesk.json'), {
46
- name: 'test',
47
- version: '1.0.0',
48
- registries: {
49
- default: {
50
- url: 'https://craftdesk.ai'
51
- },
52
- 'company-private': {
53
- url: 'https://company.internal',
54
- scope: '@company'
55
- }
56
- }
57
- });
58
-
59
- const registry = await configManager.getRegistryForCraft('@company/auth');
60
- expect(registry).toBe('https://company.internal');
61
- });
62
-
63
- it('should fallback to default registry for unmatched scoped craft', async () => {
64
- await writeJsonFile(path.join(tempDir, 'craftdesk.json'), {
65
- name: 'test',
66
- version: '1.0.0',
67
- registries: {
68
- default: {
69
- url: 'https://craftdesk.ai'
70
- },
71
- 'company-private': {
72
- url: 'https://company.internal',
73
- scope: '@company'
74
- }
75
- }
76
- });
77
-
78
- const registry = await configManager.getRegistryForCraft('@other/craft');
79
- expect(registry).toBe('https://craftdesk.ai');
80
- });
81
-
82
- it('should use final fallback when no registries configured', async () => {
83
- await writeJsonFile(path.join(tempDir, 'craftdesk.json'), {
84
- name: 'test',
85
- version: '1.0.0'
86
- });
87
-
88
- const registry = await configManager.getRegistryForCraft('test-craft');
89
- expect(registry).toBe('https://craftdesk.ai');
90
- });
91
- });
92
-
93
- describe('getAuthToken', () => {
94
- it('should return null when no craftdesk.json exists', async () => {
95
- const token = await configManager.getAuthToken('default');
96
- expect(token).toBeNull();
97
- });
98
-
99
- it('should return null when registry not configured', async () => {
100
- await writeJsonFile(path.join(tempDir, 'craftdesk.json'), {
101
- name: 'test',
102
- version: '1.0.0',
103
- registries: {
104
- default: {
105
- url: 'https://craftdesk.ai'
106
- }
107
- }
108
- });
109
-
110
- const token = await configManager.getAuthToken('nonexistent');
111
- expect(token).toBeNull();
112
- });
113
-
114
- it('should return token from environment variable', async () => {
115
- await writeJsonFile(path.join(tempDir, 'craftdesk.json'), {
116
- name: 'test',
117
- version: '1.0.0',
118
- registries: {
119
- 'company-private': {
120
- url: 'https://company.internal',
121
- scope: '@company'
122
- }
123
- }
124
- });
125
-
126
- process.env.CRAFTDESK_AUTH_COMPANY_PRIVATE = 'test-token-123';
127
-
128
- const token = await configManager.getAuthToken('company-private');
129
- expect(token).toBe('test-token-123');
130
-
131
- delete process.env.CRAFTDESK_AUTH_COMPANY_PRIVATE;
132
- });
133
-
134
- it('should handle registry names with hyphens', async () => {
135
- await writeJsonFile(path.join(tempDir, 'craftdesk.json'), {
136
- name: 'test',
137
- version: '1.0.0',
138
- registries: {
139
- 'my-custom-registry': {
140
- url: 'https://custom.com'
141
- }
142
- }
143
- });
144
-
145
- process.env.CRAFTDESK_AUTH_MY_CUSTOM_REGISTRY = 'custom-token';
146
-
147
- const token = await configManager.getAuthToken('my-custom-registry');
148
- expect(token).toBe('custom-token');
149
-
150
- delete process.env.CRAFTDESK_AUTH_MY_CUSTOM_REGISTRY;
151
- });
152
-
153
- it('should return null when environment variable not set', async () => {
154
- await writeJsonFile(path.join(tempDir, 'craftdesk.json'), {
155
- name: 'test',
156
- version: '1.0.0',
157
- registries: {
158
- private: {
159
- url: 'https://private.com'
160
- }
161
- }
162
- });
163
-
164
- const token = await configManager.getAuthToken('private');
165
- expect(token).toBeNull();
166
- });
167
- });
168
-
169
- describe('getInstallPath', () => {
170
- it('should return .claude directory', () => {
171
- const installPath = configManager.getInstallPath();
172
- expect(installPath).toBe('.claude');
173
- });
174
- });
175
-
176
- describe('getCraftDeskJson', () => {
177
- it('should return null when craftdesk.json does not exist', async () => {
178
- const result = await configManager.getCraftDeskJson();
179
- expect(result).toBeNull();
180
- });
181
-
182
- it('should return parsed craftdesk.json', async () => {
183
- const craftDeskData = {
184
- name: 'test-project',
185
- version: '1.0.0',
186
- type: 'skill',
187
- description: 'Test project'
188
- };
189
-
190
- await writeJsonFile(path.join(tempDir, 'craftdesk.json'), craftDeskData);
191
-
192
- const result = await configManager.getCraftDeskJson();
193
- expect(result).toEqual(craftDeskData);
194
- });
195
-
196
- it('should cache craftdesk.json after first read', async () => {
197
- const craftDeskData = {
198
- name: 'test-project',
199
- version: '1.0.0'
200
- };
201
-
202
- await writeJsonFile(path.join(tempDir, 'craftdesk.json'), craftDeskData);
203
-
204
- // First call
205
- const result1 = await configManager.getCraftDeskJson();
206
-
207
- // Modify file on disk
208
- await writeJsonFile(path.join(tempDir, 'craftdesk.json'), {
209
- name: 'modified',
210
- version: '2.0.0'
211
- });
212
-
213
- // Second call should return cached value
214
- const result2 = await configManager.getCraftDeskJson();
215
-
216
- expect(result1).toEqual(craftDeskData);
217
- expect(result2).toEqual(craftDeskData);
218
- });
219
-
220
- it('should return null for malformed JSON', async () => {
221
- const fs = await import('fs-extra');
222
- await fs.writeFile(path.join(tempDir, 'craftdesk.json'), 'not valid json', 'utf-8');
223
-
224
- const result = await configManager.getCraftDeskJson();
225
- expect(result).toBeNull();
226
- });
227
- });
228
- });