craftdesk 0.3.0 → 0.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 (215) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +1609 -132
  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 +306 -8
  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/detect-agents.d.ts +14 -0
  12. package/dist/commands/detect-agents.d.ts.map +1 -0
  13. package/dist/commands/detect-agents.js +86 -0
  14. package/dist/commands/detect-agents.js.map +1 -0
  15. package/dist/commands/embed.d.ts +14 -0
  16. package/dist/commands/embed.d.ts.map +1 -0
  17. package/dist/commands/embed.js +147 -0
  18. package/dist/commands/embed.js.map +1 -0
  19. package/dist/commands/info.d.ts +8 -0
  20. package/dist/commands/info.d.ts.map +1 -0
  21. package/dist/commands/info.js +66 -0
  22. package/dist/commands/info.js.map +1 -0
  23. package/dist/commands/init.d.ts +5 -0
  24. package/dist/commands/init.d.ts.map +1 -1
  25. package/dist/commands/init.js +8 -2
  26. package/dist/commands/init.js.map +1 -1
  27. package/dist/commands/install.d.ts +5 -0
  28. package/dist/commands/install.d.ts.map +1 -1
  29. package/dist/commands/install.js +24 -4
  30. package/dist/commands/install.js.map +1 -1
  31. package/dist/commands/list.d.ts +5 -0
  32. package/dist/commands/list.d.ts.map +1 -1
  33. package/dist/commands/list.js +75 -7
  34. package/dist/commands/list.js.map +1 -1
  35. package/dist/commands/login.d.ts +8 -0
  36. package/dist/commands/login.d.ts.map +1 -0
  37. package/dist/commands/login.js +158 -0
  38. package/dist/commands/login.js.map +1 -0
  39. package/dist/commands/logout.d.ts +8 -0
  40. package/dist/commands/logout.d.ts.map +1 -0
  41. package/dist/commands/logout.js +51 -0
  42. package/dist/commands/logout.js.map +1 -0
  43. package/dist/commands/outdated.d.ts +23 -0
  44. package/dist/commands/outdated.d.ts.map +1 -0
  45. package/dist/commands/outdated.js +268 -0
  46. package/dist/commands/outdated.js.map +1 -0
  47. package/dist/commands/publish.d.ts +8 -0
  48. package/dist/commands/publish.d.ts.map +1 -0
  49. package/dist/commands/publish.js +203 -0
  50. package/dist/commands/publish.js.map +1 -0
  51. package/dist/commands/remove.d.ts +5 -0
  52. package/dist/commands/remove.d.ts.map +1 -1
  53. package/dist/commands/remove.js +51 -10
  54. package/dist/commands/remove.js.map +1 -1
  55. package/dist/commands/search.d.ts +8 -0
  56. package/dist/commands/search.d.ts.map +1 -0
  57. package/dist/commands/search.js +60 -0
  58. package/dist/commands/search.js.map +1 -0
  59. package/dist/commands/setup-multi-agent.d.ts +17 -0
  60. package/dist/commands/setup-multi-agent.d.ts.map +1 -0
  61. package/dist/commands/setup-multi-agent.js +188 -0
  62. package/dist/commands/setup-multi-agent.js.map +1 -0
  63. package/dist/commands/sync.d.ts +14 -0
  64. package/dist/commands/sync.d.ts.map +1 -0
  65. package/dist/commands/sync.js +144 -0
  66. package/dist/commands/sync.js.map +1 -0
  67. package/dist/commands/unembed.d.ts +14 -0
  68. package/dist/commands/unembed.d.ts.map +1 -0
  69. package/dist/commands/unembed.js +96 -0
  70. package/dist/commands/unembed.js.map +1 -0
  71. package/dist/commands/update.d.ts +8 -0
  72. package/dist/commands/update.d.ts.map +1 -0
  73. package/dist/commands/update.js +372 -0
  74. package/dist/commands/update.js.map +1 -0
  75. package/dist/commands/verify.d.ts +14 -0
  76. package/dist/commands/verify.d.ts.map +1 -0
  77. package/dist/commands/verify.js +143 -0
  78. package/dist/commands/verify.js.map +1 -0
  79. package/dist/commands/whoami.d.ts +8 -0
  80. package/dist/commands/whoami.d.ts.map +1 -0
  81. package/dist/commands/whoami.js +58 -0
  82. package/dist/commands/whoami.js.map +1 -0
  83. package/dist/converters/base-converter.d.ts +89 -0
  84. package/dist/converters/base-converter.d.ts.map +1 -0
  85. package/dist/converters/base-converter.js +249 -0
  86. package/dist/converters/base-converter.js.map +1 -0
  87. package/dist/converters/continue-converter.d.ts +53 -0
  88. package/dist/converters/continue-converter.d.ts.map +1 -0
  89. package/dist/converters/continue-converter.js +243 -0
  90. package/dist/converters/continue-converter.js.map +1 -0
  91. package/dist/converters/cursor-converter.d.ts +70 -0
  92. package/dist/converters/cursor-converter.d.ts.map +1 -0
  93. package/dist/converters/cursor-converter.js +272 -0
  94. package/dist/converters/cursor-converter.js.map +1 -0
  95. package/dist/index.js +56 -3
  96. package/dist/index.js.map +1 -1
  97. package/dist/services/agent-detector.d.ts +57 -0
  98. package/dist/services/agent-detector.d.ts.map +1 -0
  99. package/dist/services/agent-detector.js +146 -0
  100. package/dist/services/agent-detector.js.map +1 -0
  101. package/dist/services/config-manager.d.ts +128 -10
  102. package/dist/services/config-manager.d.ts.map +1 -1
  103. package/dist/services/config-manager.js +190 -18
  104. package/dist/services/config-manager.js.map +1 -1
  105. package/dist/services/converter.d.ts +53 -0
  106. package/dist/services/converter.d.ts.map +1 -0
  107. package/dist/services/converter.js +185 -0
  108. package/dist/services/converter.js.map +1 -0
  109. package/dist/services/craft-wrapper.d.ts +108 -0
  110. package/dist/services/craft-wrapper.d.ts.map +1 -0
  111. package/dist/services/craft-wrapper.js +268 -0
  112. package/dist/services/craft-wrapper.js.map +1 -0
  113. package/dist/services/git-resolver.d.ts +9 -3
  114. package/dist/services/git-resolver.d.ts.map +1 -1
  115. package/dist/services/git-resolver.js +44 -20
  116. package/dist/services/git-resolver.js.map +1 -1
  117. package/dist/services/gitignore-manager.d.ts +74 -0
  118. package/dist/services/gitignore-manager.d.ts.map +1 -0
  119. package/dist/services/gitignore-manager.js +188 -0
  120. package/dist/services/gitignore-manager.js.map +1 -0
  121. package/dist/services/installer.d.ts +60 -1
  122. package/dist/services/installer.d.ts.map +1 -1
  123. package/dist/services/installer.js +295 -20
  124. package/dist/services/installer.js.map +1 -1
  125. package/dist/services/multi-agent-sync.d.ts +88 -0
  126. package/dist/services/multi-agent-sync.d.ts.map +1 -0
  127. package/dist/services/multi-agent-sync.js +316 -0
  128. package/dist/services/multi-agent-sync.js.map +1 -0
  129. package/dist/services/plugin-resolver.d.ts +80 -0
  130. package/dist/services/plugin-resolver.d.ts.map +1 -0
  131. package/dist/services/plugin-resolver.js +183 -0
  132. package/dist/services/plugin-resolver.js.map +1 -0
  133. package/dist/services/registry-client.d.ts +132 -5
  134. package/dist/services/registry-client.d.ts.map +1 -1
  135. package/dist/services/registry-client.js +239 -34
  136. package/dist/services/registry-client.js.map +1 -1
  137. package/dist/services/settings-manager.d.ts +151 -0
  138. package/dist/services/settings-manager.d.ts.map +1 -0
  139. package/dist/services/settings-manager.js +333 -0
  140. package/dist/services/settings-manager.js.map +1 -0
  141. package/dist/types/claude-settings.d.ts +155 -0
  142. package/dist/types/claude-settings.d.ts.map +1 -0
  143. package/dist/types/claude-settings.js +20 -0
  144. package/dist/types/claude-settings.js.map +1 -0
  145. package/dist/types/converter.d.ts +98 -0
  146. package/dist/types/converter.d.ts.map +1 -0
  147. package/dist/types/converter.js +6 -0
  148. package/dist/types/converter.js.map +1 -0
  149. package/dist/types/craftdesk-json.d.ts +76 -3
  150. package/dist/types/craftdesk-json.d.ts.map +1 -1
  151. package/dist/types/craftdesk-lock.d.ts +62 -1
  152. package/dist/types/craftdesk-lock.d.ts.map +1 -1
  153. package/dist/types/multi-agent.d.ts +85 -0
  154. package/dist/types/multi-agent.d.ts.map +1 -0
  155. package/dist/types/multi-agent.js +6 -0
  156. package/dist/types/multi-agent.js.map +1 -0
  157. package/dist/utils/crypto.d.ts +23 -0
  158. package/dist/utils/crypto.d.ts.map +1 -0
  159. package/dist/utils/crypto.js +52 -0
  160. package/dist/utils/crypto.js.map +1 -0
  161. package/dist/utils/file-system.d.ts +43 -0
  162. package/dist/utils/file-system.d.ts.map +1 -1
  163. package/dist/utils/file-system.js +45 -2
  164. package/dist/utils/file-system.js.map +1 -1
  165. package/dist/utils/logger.d.ts +75 -0
  166. package/dist/utils/logger.d.ts.map +1 -1
  167. package/dist/utils/logger.js +76 -1
  168. package/dist/utils/logger.js.map +1 -1
  169. package/dist/utils/version-utils.d.ts +63 -0
  170. package/dist/utils/version-utils.d.ts.map +1 -0
  171. package/dist/utils/version-utils.js +130 -0
  172. package/dist/utils/version-utils.js.map +1 -0
  173. package/package.json +36 -9
  174. package/.claude/settings.local.json +0 -19
  175. package/.github/workflows/ci.yml +0 -94
  176. package/API.md +0 -414
  177. package/TESTING.md +0 -263
  178. package/dist/commands/config.d.ts +0 -3
  179. package/dist/commands/config.d.ts.map +0 -1
  180. package/dist/commands/config.js +0 -56
  181. package/dist/commands/config.js.map +0 -1
  182. package/dist/types/config.d.ts +0 -17
  183. package/dist/types/config.d.ts.map +0 -1
  184. package/dist/types/config.js +0 -3
  185. package/dist/types/config.js.map +0 -1
  186. package/docs/DEPENDENCY_RESOLUTION.md +0 -237
  187. package/examples/craftdesk-all-sources.json +0 -79
  188. package/examples/craftdesk-git-monorepo.json +0 -41
  189. package/examples/craftdesk-multi-registry.json +0 -40
  190. package/examples/craftdesk-with-registries.json +0 -49
  191. package/examples/simple-craftdesk.json +0 -10
  192. package/src/commands/add.ts +0 -314
  193. package/src/commands/init.ts +0 -55
  194. package/src/commands/install.ts +0 -126
  195. package/src/commands/list.ts +0 -126
  196. package/src/commands/remove.ts +0 -82
  197. package/src/index.ts +0 -56
  198. package/src/services/config-manager.ts +0 -150
  199. package/src/services/git-resolver.ts +0 -373
  200. package/src/services/installer.ts +0 -314
  201. package/src/services/registry-client.ts +0 -163
  202. package/src/types/craftdesk-json.ts +0 -53
  203. package/src/types/craftdesk-lock.ts +0 -38
  204. package/src/utils/file-system.ts +0 -55
  205. package/src/utils/logger.ts +0 -82
  206. package/tests/fixtures/craftdesk-git.json +0 -17
  207. package/tests/fixtures/craftdesk.json +0 -15
  208. package/tests/helpers/test-utils.ts +0 -60
  209. package/tests/integration/init-command.test.ts +0 -70
  210. package/tests/integration/list-command.test.ts +0 -77
  211. package/tests/unit/config-manager.test.ts +0 -228
  212. package/tests/unit/git-resolver.test.ts +0 -303
  213. package/tests/unit/installer.test.ts +0 -105
  214. package/tsconfig.json +0 -20
  215. package/vitest.config.ts +0 -29
@@ -1,163 +0,0 @@
1
- import axios, { AxiosInstance } from 'axios';
2
- import { configManager } from './config-manager';
3
- import { logger } from '../utils/logger';
4
- import { LockEntry } from '../types/craftdesk-lock';
5
-
6
- export interface CraftInfo {
7
- name: string;
8
- author: string;
9
- version: string;
10
- type: 'skill' | 'agent' | 'command' | 'hook' | 'plugin';
11
- description?: string;
12
- dependencies?: Record<string, string>;
13
- download_url?: string;
14
- integrity?: string;
15
- }
16
-
17
- export interface ResolveResponse {
18
- resolved: Record<string, any>;
19
- lockfile: any;
20
- }
21
-
22
- export class RegistryClient {
23
- private axiosInstance: AxiosInstance | null = null;
24
-
25
- private async getClient(registryUrl: string): Promise<AxiosInstance> {
26
- const token = await configManager.getAuthToken(registryUrl);
27
-
28
- return axios.create({
29
- baseURL: registryUrl,
30
- headers: {
31
- 'Content-Type': 'application/json',
32
- ...(token ? { 'Authorization': `Bearer ${token}` } : {})
33
- },
34
- timeout: 30000
35
- });
36
- }
37
-
38
- async getCraftInfo(craftName: string, version?: string, registryOverride?: string): Promise<CraftInfo | null> {
39
- // Get registry URL - can be overridden by dependency-specific registry
40
- const registryUrl = registryOverride
41
- ? await this.resolveRegistry(registryOverride)
42
- : await configManager.getRegistryForCraft(craftName);
43
-
44
- const client = await this.getClient(registryUrl);
45
-
46
- // Parse craft name (could be @scope/name or just name)
47
- const [author, name] = this.parseCraftName(craftName);
48
-
49
- try {
50
- const endpoint = version
51
- ? `/api/v1/crafts/${author}/${name}/versions/${version}`
52
- : `/api/v1/crafts/${author}/${name}`;
53
-
54
- logger.debug(`Fetching craft info from ${registryUrl}${endpoint}`);
55
-
56
- const response = await client.get(endpoint);
57
- return response.data.craft || response.data;
58
- } catch (error: any) {
59
- if (error.response?.status === 404) {
60
- logger.error(`Craft '${craftName}' not found in registry`);
61
- } else {
62
- logger.error(`Failed to fetch craft info: ${error.message}`);
63
- }
64
- return null;
65
- }
66
- }
67
-
68
- private async resolveRegistry(registry: string): Promise<string> {
69
- // If it's a URL, use it directly
70
- if (registry.startsWith('http://') || registry.startsWith('https://')) {
71
- return registry;
72
- }
73
-
74
- // Look it up in the craftdesk.json registries section
75
- const craftDeskJson = await configManager.getCraftDeskJson();
76
- if (craftDeskJson?.registries?.[registry]) {
77
- return craftDeskJson.registries[registry].url;
78
- }
79
-
80
- // If not found, assume it's a URL without protocol
81
- return `https://${registry}`;
82
- }
83
-
84
- async listVersions(craftName: string): Promise<string[]> {
85
- const registryUrl = await configManager.getRegistryForCraft(craftName);
86
- const client = await this.getClient(registryUrl);
87
-
88
- const [author, name] = this.parseCraftName(craftName);
89
-
90
- try {
91
- const response = await client.get(`/api/v1/crafts/${author}/${name}/versions`);
92
- return response.data.versions || [];
93
- } catch (error: any) {
94
- logger.error(`Failed to fetch versions: ${error.message}`);
95
- return [];
96
- }
97
- }
98
-
99
- async resolveDependencies(dependencies: Record<string, string>): Promise<ResolveResponse | null> {
100
- // Get the default registry from craftdesk.json or use fallback
101
- const craftDesk = await configManager.getCraftDeskJson();
102
- const registryUrl = craftDesk?.registries?.default?.url || 'https://craftdesk.ai';
103
-
104
- const client = await this.getClient(registryUrl);
105
-
106
- try {
107
- logger.debug('Resolving dependencies via API...');
108
- const response = await client.post('/api/v1/resolve', { dependencies });
109
- return response.data;
110
- } catch (error: any) {
111
- logger.error(`Failed to resolve dependencies: ${error.message}`);
112
- return null;
113
- }
114
- }
115
-
116
- async downloadCraft(downloadUrl: string, outputPath: string): Promise<void> {
117
- try {
118
- const response = await axios.get(downloadUrl, {
119
- responseType: 'stream'
120
- });
121
-
122
- // TODO: Save to file and extract
123
- logger.debug(`Downloaded craft from ${downloadUrl}`);
124
- } catch (error: any) {
125
- throw new Error(`Failed to download craft: ${error.message}`);
126
- }
127
- }
128
-
129
- async searchCrafts(query: string, type?: string): Promise<CraftInfo[]> {
130
- // Get the default registry from craftdesk.json or use fallback
131
- const craftDesk = await configManager.getCraftDeskJson();
132
- const registryUrl = craftDesk?.registries?.default?.url || 'https://craftdesk.ai';
133
-
134
- const client = await this.getClient(registryUrl);
135
-
136
- try {
137
- const params: any = { q: query };
138
- if (type) params.type = type;
139
-
140
- const response = await client.get('/api/v1/crafts', { params });
141
- return response.data.crafts || [];
142
- } catch (error: any) {
143
- logger.error(`Failed to search crafts: ${error.message}`);
144
- return [];
145
- }
146
- }
147
-
148
- private parseCraftName(craftName: string): [string, string] {
149
- if (craftName.startsWith('@')) {
150
- // Scoped craft: @acme/skill-name
151
- const parts = craftName.substring(1).split('/');
152
- if (parts.length === 2) {
153
- return [`@${parts[0]}`, parts[1]];
154
- }
155
- }
156
-
157
- // Assume author is 'anthropic' for unscoped crafts (default)
158
- // This should be configurable or determined by registry
159
- return ['anthropic', craftName];
160
- }
161
- }
162
-
163
- export const registryClient = new RegistryClient();
@@ -1,53 +0,0 @@
1
- export interface CraftDeskJson {
2
- name: string;
3
- version: string;
4
- type?: 'skill' | 'agent' | 'command' | 'hook' | 'plugin';
5
- description?: string;
6
- author?: string;
7
- license?: string;
8
- homepage?: string;
9
- repository?: {
10
- type: string;
11
- url: string;
12
- };
13
- keywords?: string[];
14
-
15
- // Dependencies - type-agnostic (skills, agents, commands, hooks, plugins can all depend on each other)
16
- // Can be simple string versions or objects with version and registry
17
- dependencies?: Record<string, string | DependencyConfig>;
18
- devDependencies?: Record<string, string | DependencyConfig>;
19
- peerDependencies?: Record<string, string | DependencyConfig>;
20
- optionalDependencies?: Record<string, string | DependencyConfig>;
21
-
22
- // Registry configuration (optional, in-file configuration)
23
- registries?: Record<string, RegistryConfig>;
24
-
25
- // Commands and hooks
26
- commands?: Record<string, string | CommandConfig>;
27
- hooks?: Record<string, string[]>;
28
-
29
- // Index signature to allow dynamic property access
30
- [key: string]: any;
31
- }
32
-
33
- export interface DependencyConfig {
34
- version?: string; // Optional for git dependencies
35
- registry?: string; // Can be a registry name (from registries section) or direct URL
36
- git?: string; // Git repository URL
37
- branch?: string; // Git branch (default: main/master)
38
- tag?: string; // Git tag
39
- commit?: string; // Git commit hash
40
- path?: string; // Subdirectory path within the repository
41
- file?: string; // Direct file path within the repository
42
- }
43
-
44
- export interface RegistryConfig {
45
- url: string;
46
- scope?: string; // Optional scope like "@company"
47
- auth?: string; // Optional auth token (though better in ~/.craftrc for security)
48
- }
49
-
50
- export interface CommandConfig {
51
- version: string;
52
- config?: Record<string, any>;
53
- }
@@ -1,38 +0,0 @@
1
- export interface CraftDeskLock {
2
- version: string;
3
- lockfileVersion: number;
4
- generatedAt: string;
5
-
6
- crafts: Record<string, LockEntry>;
7
- tree?: DependencyTree;
8
- metadata?: {
9
- totalCrafts: number;
10
- totalSize: string;
11
- installPath: string;
12
- };
13
- }
14
-
15
- export interface LockEntry {
16
- version: string;
17
- resolved: string; // Download URL or git URL
18
- integrity: string; // SHA-256 hash (or commit hash for git)
19
- type: 'skill' | 'agent' | 'command' | 'hook' | 'plugin';
20
- author?: string;
21
- registry?: string;
22
- scope?: string;
23
- dependencies?: Record<string, string>;
24
-
25
- // Git source fields
26
- git?: string; // Git repository URL
27
- branch?: string; // Git branch
28
- tag?: string; // Git tag
29
- commit?: string; // Git commit hash (resolved)
30
- path?: string; // Subdirectory path within repository
31
- file?: string; // Direct file path within repository
32
- }
33
-
34
- export interface DependencyTree {
35
- [key: string]: {
36
- dependencies?: DependencyTree;
37
- } | string; // String for "(shared)" marker
38
- }
@@ -1,55 +0,0 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import { CraftDeskJson } from '../types/craftdesk-json';
4
- import { CraftDeskLock } from '../types/craftdesk-lock';
5
-
6
- export async function readCraftDeskJson(dir: string = process.cwd()): Promise<CraftDeskJson | null> {
7
- const filePath = path.join(dir, 'craftdesk.json');
8
-
9
- try {
10
- const content = await fs.readFile(filePath, 'utf-8');
11
- return JSON.parse(content);
12
- } catch (error) {
13
- return null;
14
- }
15
- }
16
-
17
- export async function writeCraftDeskJson(data: CraftDeskJson, dir: string = process.cwd()): Promise<void> {
18
- const filePath = path.join(dir, 'craftdesk.json');
19
- const content = JSON.stringify(data, null, 2);
20
- await fs.writeFile(filePath, content, 'utf-8');
21
- }
22
-
23
- export async function readCraftDeskLock(dir: string = process.cwd()): Promise<CraftDeskLock | null> {
24
- const filePath = path.join(dir, 'craftdesk.lock');
25
-
26
- try {
27
- const content = await fs.readFile(filePath, 'utf-8');
28
- return JSON.parse(content);
29
- } catch (error) {
30
- return null;
31
- }
32
- }
33
-
34
- export async function writeCraftDeskLock(data: CraftDeskLock, dir: string = process.cwd()): Promise<void> {
35
- const filePath = path.join(dir, 'craftdesk.lock');
36
- const content = JSON.stringify(data, null, 2);
37
- await fs.writeFile(filePath, content, 'utf-8');
38
- }
39
-
40
- export async function ensureDir(dirPath: string): Promise<void> {
41
- await fs.ensureDir(dirPath);
42
- }
43
-
44
- export async function removeDir(dirPath: string): Promise<void> {
45
- await fs.remove(dirPath);
46
- }
47
-
48
- export async function exists(filePath: string): Promise<boolean> {
49
- try {
50
- await fs.access(filePath);
51
- return true;
52
- } catch {
53
- return false;
54
- }
55
- }
@@ -1,82 +0,0 @@
1
- // ANSI color codes
2
- const colors = {
3
- reset: '\x1b[0m',
4
- green: '\x1b[32m',
5
- red: '\x1b[31m',
6
- yellow: '\x1b[33m',
7
- blue: '\x1b[34m',
8
- cyan: '\x1b[36m',
9
- gray: '\x1b[90m',
10
- bold: '\x1b[1m',
11
- dim: '\x1b[2m',
12
- };
13
-
14
- export class Logger {
15
- private spinnerText: string | null = null;
16
-
17
- success(message: string): void {
18
- console.log(`${colors.green}✓${colors.reset}`, message);
19
- }
20
-
21
- error(message: string): void {
22
- console.error(`${colors.red}✗${colors.reset}`, message);
23
- }
24
-
25
- warn(message: string): void {
26
- console.log(`${colors.yellow}!${colors.reset}`, message);
27
- }
28
-
29
- info(message: string): void {
30
- console.log(`${colors.blue}→${colors.reset}`, message);
31
- }
32
-
33
- debug(message: string): void {
34
- if (process.env.DEBUG) {
35
- console.log(`${colors.gray}[DEBUG]${colors.reset}`, message);
36
- }
37
- }
38
-
39
- startSpinner(text: string): void {
40
- this.spinnerText = text;
41
- console.log(`${colors.cyan}⋯${colors.reset}`, text);
42
- }
43
-
44
- updateSpinner(text: string): void {
45
- if (this.spinnerText) {
46
- this.spinnerText = text;
47
- console.log(' ', text);
48
- }
49
- }
50
-
51
- succeedSpinner(text?: string): void {
52
- if (this.spinnerText) {
53
- console.log(`${colors.green}✓${colors.reset}`, text || this.spinnerText);
54
- this.spinnerText = null;
55
- }
56
- }
57
-
58
- failSpinner(text?: string): void {
59
- if (this.spinnerText) {
60
- console.log(`${colors.red}✗${colors.reset}`, text || this.spinnerText);
61
- this.spinnerText = null;
62
- }
63
- }
64
-
65
- stopSpinner(): void {
66
- this.spinnerText = null;
67
- }
68
-
69
- log(message: string): void {
70
- console.log(message);
71
- }
72
-
73
- bold(text: string): string {
74
- return `${colors.bold}${text}${colors.reset}`;
75
- }
76
-
77
- dim(text: string): string {
78
- return `${colors.dim}${text}${colors.reset}`;
79
- }
80
- }
81
-
82
- export const logger = new Logger();
@@ -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
- });