craftdesk 0.1.0 → 0.3.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 (103) hide show
  1. package/.claude/settings.local.json +19 -0
  2. package/.github/workflows/ci.yml +94 -0
  3. package/API.md +414 -0
  4. package/README.md +778 -0
  5. package/TESTING.md +263 -0
  6. package/bin/craftdesk.js +4 -0
  7. package/dist/commands/add.d.ts +3 -0
  8. package/dist/commands/add.d.ts.map +1 -0
  9. package/dist/commands/add.js +284 -0
  10. package/dist/commands/add.js.map +1 -0
  11. package/dist/commands/config.d.ts +3 -0
  12. package/dist/commands/config.d.ts.map +1 -0
  13. package/dist/commands/config.js +56 -0
  14. package/dist/commands/config.js.map +1 -0
  15. package/dist/commands/init.d.ts +3 -0
  16. package/dist/commands/init.d.ts.map +1 -0
  17. package/dist/commands/init.js +57 -0
  18. package/dist/commands/init.js.map +1 -0
  19. package/dist/commands/install.d.ts +3 -0
  20. package/dist/commands/install.d.ts.map +1 -0
  21. package/dist/commands/install.js +109 -0
  22. package/dist/commands/install.js.map +1 -0
  23. package/dist/commands/list.d.ts +3 -0
  24. package/dist/commands/list.d.ts.map +1 -0
  25. package/dist/commands/list.js +117 -0
  26. package/dist/commands/list.js.map +1 -0
  27. package/dist/commands/remove.d.ts +3 -0
  28. package/dist/commands/remove.d.ts.map +1 -0
  29. package/dist/commands/remove.js +74 -0
  30. package/dist/commands/remove.js.map +1 -0
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +51 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/services/config-manager.d.ts +88 -0
  36. package/dist/services/config-manager.d.ts.map +1 -0
  37. package/dist/services/config-manager.js +143 -0
  38. package/dist/services/config-manager.js.map +1 -0
  39. package/dist/services/git-resolver.d.ts +139 -0
  40. package/dist/services/git-resolver.d.ts.map +1 -0
  41. package/dist/services/git-resolver.js +340 -0
  42. package/dist/services/git-resolver.js.map +1 -0
  43. package/dist/services/installer.d.ts +98 -0
  44. package/dist/services/installer.d.ts.map +1 -0
  45. package/dist/services/installer.js +274 -0
  46. package/dist/services/installer.js.map +1 -0
  47. package/dist/services/registry-client.d.ts +27 -0
  48. package/dist/services/registry-client.d.ts.map +1 -0
  49. package/dist/services/registry-client.js +134 -0
  50. package/dist/services/registry-client.js.map +1 -0
  51. package/dist/types/config.d.ts +17 -0
  52. package/dist/types/config.d.ts.map +1 -0
  53. package/dist/types/config.js +3 -0
  54. package/dist/types/config.js.map +1 -0
  55. package/dist/types/craftdesk-json.d.ts +42 -0
  56. package/dist/types/craftdesk-json.d.ts.map +1 -0
  57. package/dist/types/craftdesk-json.js +3 -0
  58. package/dist/types/craftdesk-json.js.map +1 -0
  59. package/dist/types/craftdesk-lock.d.ts +34 -0
  60. package/dist/types/craftdesk-lock.d.ts.map +1 -0
  61. package/dist/types/craftdesk-lock.js +3 -0
  62. package/dist/types/craftdesk-lock.js.map +1 -0
  63. package/dist/utils/file-system.d.ts +10 -0
  64. package/dist/utils/file-system.d.ts.map +1 -0
  65. package/dist/utils/file-system.js +60 -0
  66. package/dist/utils/file-system.js.map +1 -0
  67. package/dist/utils/logger.d.ts +18 -0
  68. package/dist/utils/logger.d.ts.map +1 -0
  69. package/dist/utils/logger.js +72 -0
  70. package/dist/utils/logger.js.map +1 -0
  71. package/docs/DEPENDENCY_RESOLUTION.md +237 -0
  72. package/examples/craftdesk-all-sources.json +79 -0
  73. package/examples/craftdesk-git-monorepo.json +41 -0
  74. package/examples/craftdesk-multi-registry.json +40 -0
  75. package/examples/craftdesk-with-registries.json +49 -0
  76. package/examples/simple-craftdesk.json +10 -0
  77. package/package.json +32 -25
  78. package/src/commands/add.ts +314 -0
  79. package/src/commands/init.ts +55 -0
  80. package/src/commands/install.ts +126 -0
  81. package/src/commands/list.ts +126 -0
  82. package/src/commands/remove.ts +82 -0
  83. package/src/index.ts +56 -0
  84. package/src/services/config-manager.ts +150 -0
  85. package/src/services/git-resolver.ts +373 -0
  86. package/src/services/installer.ts +314 -0
  87. package/src/services/registry-client.ts +163 -0
  88. package/src/types/craftdesk-json.ts +53 -0
  89. package/src/types/craftdesk-lock.ts +38 -0
  90. package/src/utils/file-system.ts +55 -0
  91. package/src/utils/logger.ts +82 -0
  92. package/tests/fixtures/craftdesk-git.json +17 -0
  93. package/tests/fixtures/craftdesk.json +15 -0
  94. package/tests/helpers/test-utils.ts +60 -0
  95. package/tests/integration/init-command.test.ts +70 -0
  96. package/tests/integration/list-command.test.ts +77 -0
  97. package/tests/unit/config-manager.test.ts +228 -0
  98. package/tests/unit/git-resolver.test.ts +303 -0
  99. package/tests/unit/installer.test.ts +105 -0
  100. package/tsconfig.json +20 -0
  101. package/vitest.config.ts +29 -0
  102. package/LICENSE +0 -21
  103. package/index.js +0 -4
package/TESTING.md ADDED
@@ -0,0 +1,263 @@
1
+ # Testing Guide
2
+
3
+ ## Overview
4
+
5
+ The CraftDesk CLI uses **Vitest** as its testing framework, providing fast and modern testing capabilities with TypeScript support.
6
+
7
+ ## Test Structure
8
+
9
+ ```
10
+ tests/
11
+ ├── unit/ # Unit tests for individual modules
12
+ │ ├── config-manager.test.ts
13
+ │ ├── git-resolver.test.ts
14
+ │ └── installer.test.ts
15
+ ├── integration/ # Integration tests for CLI commands
16
+ │ ├── init-command.test.ts
17
+ │ └── list-command.test.ts
18
+ ├── fixtures/ # Test fixtures and mock data
19
+ │ ├── craftdesk.json
20
+ │ └── craftdesk-git.json
21
+ └── helpers/ # Test utilities
22
+ └── test-utils.ts
23
+ ```
24
+
25
+ ## Running Tests
26
+
27
+ ### Run all tests
28
+ ```bash
29
+ npm test
30
+ ```
31
+
32
+ ### Run tests in watch mode
33
+ ```bash
34
+ npm run test:watch
35
+ ```
36
+
37
+ ### Run tests with coverage
38
+ ```bash
39
+ npm run test:coverage
40
+ ```
41
+
42
+ ### Run tests with UI
43
+ ```bash
44
+ npm run test:ui
45
+ ```
46
+
47
+ ## Test Coverage
48
+
49
+ Coverage reports are generated using Vitest's built-in coverage tool (v8 provider).
50
+
51
+ ### Viewing Coverage
52
+
53
+ After running `npm run test:coverage`, view the report:
54
+
55
+ ```bash
56
+ # Terminal summary is displayed automatically
57
+
58
+ # Open HTML report in browser
59
+ open coverage/index.html
60
+ ```
61
+
62
+ ### Coverage Thresholds
63
+
64
+ Current coverage configuration in `vitest.config.ts`:
65
+ - Excludes: `node_modules/`, `dist/`, `bin/`, test files
66
+
67
+ ## Writing Tests
68
+
69
+ ### Unit Tests
70
+
71
+ Unit tests focus on testing individual functions and classes in isolation.
72
+
73
+ **Example: Testing ConfigManager**
74
+
75
+ ```typescript
76
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
77
+ import { ConfigManager } from '../../src/services/config-manager';
78
+ import { createTempDir, cleanupTempDir } from '../helpers/test-utils';
79
+
80
+ describe('ConfigManager', () => {
81
+ let configManager: ConfigManager;
82
+ let tempDir: string;
83
+
84
+ beforeEach(async () => {
85
+ configManager = new ConfigManager();
86
+ tempDir = await createTempDir('config-test-');
87
+ process.chdir(tempDir);
88
+ });
89
+
90
+ afterEach(async () => {
91
+ await cleanupTempDir(tempDir);
92
+ });
93
+
94
+ it('should return default registry when no craftdesk.json exists', async () => {
95
+ const registry = await configManager.getRegistryForPackage('test-package');
96
+ expect(registry).toBe('https://craftdesk.ai');
97
+ });
98
+ });
99
+ ```
100
+
101
+ ### Integration Tests
102
+
103
+ Integration tests verify that CLI commands work correctly end-to-end.
104
+
105
+ **Example: Testing CLI Commands**
106
+
107
+ ```typescript
108
+ import { execSync } from 'child_process';
109
+ import path from 'path';
110
+
111
+ describe('craftdesk init command', () => {
112
+ const cliPath = path.join(__dirname, '../../dist/index.js');
113
+
114
+ it('should create a new craftdesk.json file', () => {
115
+ execSync(`node ${cliPath} init --name test --version 1.0.0 --type skill`);
116
+
117
+ const exists = await fs.pathExists('craftdesk.json');
118
+ expect(exists).toBe(true);
119
+ });
120
+ });
121
+ ```
122
+
123
+ ### Test Utilities
124
+
125
+ The `tests/helpers/test-utils.ts` file provides helper functions:
126
+
127
+ - `createTempDir(prefix)` - Creates a temporary test directory
128
+ - `cleanupTempDir(dirPath)` - Removes a temporary directory
129
+ - `copyFixture(name, targetDir)` - Copies a fixture file
130
+ - `readJsonFile(path)` - Reads and parses JSON file
131
+ - `writeJsonFile(path, data)` - Writes JSON to file
132
+ - `createMockCraftInfo(name, version)` - Creates mock craft metadata
133
+
134
+ ### Mocking
135
+
136
+ Use Vitest's built-in mocking for external dependencies:
137
+
138
+ ```typescript
139
+ import { vi } from 'vitest';
140
+ import { execSync } from 'child_process';
141
+
142
+ vi.mock('child_process', () => ({
143
+ execSync: vi.fn()
144
+ }));
145
+
146
+ // In your test
147
+ const mockExecSync = vi.mocked(execSync);
148
+ mockExecSync.mockReturnValue(Buffer.from('output'));
149
+ ```
150
+
151
+ ## CI/CD Integration
152
+
153
+ ### GitHub Actions
154
+
155
+ The `.github/workflows/ci.yml` file runs tests automatically on:
156
+ - Push to main/master/develop branches
157
+ - Pull requests to main/master/develop branches
158
+
159
+ The workflow:
160
+ 1. Tests on Node.js 16.x, 18.x, and 20.x
161
+ 2. Generates coverage report (Node 20.x only)
162
+ 3. Uploads coverage to Codecov
163
+ 4. Verifies TypeScript compilation
164
+ 5. Builds the project
165
+
166
+ ### Local CI Simulation
167
+
168
+ Run the same checks locally before pushing:
169
+
170
+ ```bash
171
+ # Run tests
172
+ npm test
173
+
174
+ # Generate coverage
175
+ npm run test:coverage
176
+
177
+ # Type check
178
+ npx tsc --noEmit
179
+
180
+ # Build
181
+ npm run build
182
+ ```
183
+
184
+ ## Test Best Practices
185
+
186
+ ### 1. Isolation
187
+ - Each test should be independent
188
+ - Use `beforeEach` and `afterEach` for setup/teardown
189
+ - Clean up temporary files and directories
190
+
191
+ ### 2. Descriptive Names
192
+ ```typescript
193
+ // Good
194
+ it('should return scoped registry for scoped package')
195
+
196
+ // Bad
197
+ it('test1')
198
+ ```
199
+
200
+ ### 3. Arrange-Act-Assert Pattern
201
+ ```typescript
202
+ it('should parse git URL with branch', () => {
203
+ // Arrange
204
+ const url = 'https://github.com/user/repo.git#main';
205
+
206
+ // Act
207
+ const result = parseGitUrl(url);
208
+
209
+ // Assert
210
+ expect(result.branch).toBe('main');
211
+ expect(result.url).toBe('https://github.com/user/repo.git');
212
+ });
213
+ ```
214
+
215
+ ### 4. Test Edge Cases
216
+ - Empty inputs
217
+ - Invalid data
218
+ - Error conditions
219
+ - Boundary values
220
+
221
+ ### 5. Mock External Dependencies
222
+ - File system operations (when appropriate)
223
+ - Network requests
224
+ - Child processes
225
+ - Environment variables
226
+
227
+ ## Debugging Tests
228
+
229
+ ### Run specific test file
230
+ ```bash
231
+ npm test tests/unit/config-manager.test.ts
232
+ ```
233
+
234
+ ### Run tests matching pattern
235
+ ```bash
236
+ npm test -- --grep "git dependency"
237
+ ```
238
+
239
+ ### Debug with breakpoints
240
+ ```bash
241
+ node --inspect-brk ./node_modules/.bin/vitest run
242
+ ```
243
+
244
+ Then attach your debugger (VS Code, Chrome DevTools, etc.)
245
+
246
+ ## Continuous Improvement
247
+
248
+ ### Coverage Goals
249
+ - Maintain >80% code coverage
250
+ - Focus on critical paths first
251
+ - Add tests when fixing bugs
252
+
253
+ ### Test Maintenance
254
+ - Update tests when changing functionality
255
+ - Remove obsolete tests
256
+ - Keep test data fixtures up to date
257
+ - Review test failures in CI promptly
258
+
259
+ ## Resources
260
+
261
+ - [Vitest Documentation](https://vitest.dev/)
262
+ - [Testing Library Best Practices](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library)
263
+ - [Effective Testing Strategies](https://martinfowler.com/articles/practical-test-pyramid.html)
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+
3
+ // This file is used to bootstrap the CLI with proper node settings
4
+ require('../dist/index.js');
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function createAddCommand(): Command;
3
+ //# sourceMappingURL=add.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,gBAAgB,IAAI,OAAO,CAW1C"}
@@ -0,0 +1,284 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createAddCommand = createAddCommand;
7
+ const commander_1 = require("commander");
8
+ const path_1 = __importDefault(require("path"));
9
+ const file_system_1 = require("../utils/file-system");
10
+ const logger_1 = require("../utils/logger");
11
+ const registry_client_1 = require("../services/registry-client");
12
+ const installer_1 = require("../services/installer");
13
+ const git_resolver_1 = require("../services/git-resolver");
14
+ function createAddCommand() {
15
+ return new commander_1.Command('add')
16
+ .description('Add a new dependency and install it')
17
+ .argument('<craft>', 'Craft name (e.g., ruby-on-rails or @acme/patterns)')
18
+ .option('-D, --save-dev', 'Save as devDependency')
19
+ .option('-O, --save-optional', 'Save as optionalDependency')
20
+ .option('-E, --save-exact', 'Save exact version')
21
+ .option('-t, --type <type>', 'Specify craft type (skill, agent, command, hook, plugin)')
22
+ .action(async (craftArg, options) => {
23
+ await addCommand(craftArg, options);
24
+ });
25
+ }
26
+ async function addCommand(craftArg, options) {
27
+ try {
28
+ // Read craftdesk.json
29
+ const craftDeskJson = await (0, file_system_1.readCraftDeskJson)();
30
+ if (!craftDeskJson) {
31
+ logger_1.logger.error('No craftdesk.json found in current directory');
32
+ logger_1.logger.info('Run "craftdesk init" to create one');
33
+ process.exit(1);
34
+ }
35
+ // Parse craft name and version
36
+ let craftName;
37
+ let versionConstraint = '*';
38
+ if (craftArg.includes('@') && !craftArg.startsWith('@')) {
39
+ // Format: craft@version
40
+ const parts = craftArg.split('@');
41
+ craftName = parts[0];
42
+ versionConstraint = parts[1];
43
+ }
44
+ else if (craftArg.startsWith('@')) {
45
+ // Scoped craft: @scope/craft or @scope/craft@version
46
+ const afterScope = craftArg.substring(craftArg.indexOf('/') + 1);
47
+ if (afterScope.includes('@')) {
48
+ const parts = craftArg.split('@');
49
+ craftName = `${parts[0]}@${parts[1]}`;
50
+ versionConstraint = parts[2];
51
+ }
52
+ else {
53
+ craftName = craftArg;
54
+ }
55
+ }
56
+ else {
57
+ craftName = craftArg;
58
+ }
59
+ logger_1.logger.info(`Adding ${craftName}...`);
60
+ // Check if this is a git dependency
61
+ let depValue;
62
+ let lockEntry;
63
+ let displayInfo;
64
+ // Convert GitHub web URLs to git URLs automatically
65
+ const normalizedArg = normalizeGitHubUrl(craftArg);
66
+ if (normalizedArg.startsWith('git+') || normalizedArg.includes('.git')) {
67
+ // Handle git dependency
68
+ logger_1.logger.startSpinner('Analyzing git dependency...');
69
+ const gitInfo = parseGitUrl(normalizedArg);
70
+ // Resolve the git dependency to get its craftdesk.json and dependencies
71
+ const resolvedGit = await git_resolver_1.gitResolver.resolveGitDependency({
72
+ url: gitInfo.url,
73
+ branch: gitInfo.branch,
74
+ tag: gitInfo.tag,
75
+ commit: gitInfo.commit,
76
+ path: gitInfo.path,
77
+ file: gitInfo.file
78
+ });
79
+ depValue = {
80
+ git: gitInfo.url,
81
+ ...(gitInfo.branch && { branch: gitInfo.branch }),
82
+ ...(gitInfo.tag && { tag: gitInfo.tag }),
83
+ ...(gitInfo.path && { path: gitInfo.path }),
84
+ ...(gitInfo.file && { file: gitInfo.file })
85
+ };
86
+ // Use resolved information for the lock entry
87
+ const craftJson = resolvedGit.craftDeskJson;
88
+ // Determine type: explicit option > craftdesk.json > inferred > default
89
+ const craftType = options.type || craftJson?.type || 'skill';
90
+ lockEntry = {
91
+ version: craftJson?.version || gitInfo.tag || gitInfo.branch || 'HEAD',
92
+ resolved: gitInfo.url,
93
+ integrity: resolvedGit.resolvedCommit || 'git',
94
+ type: craftType,
95
+ author: craftJson?.author || 'git',
96
+ git: gitInfo.url,
97
+ ...(gitInfo.branch && { branch: gitInfo.branch }),
98
+ ...(gitInfo.tag && { tag: gitInfo.tag }),
99
+ ...(resolvedGit.resolvedCommit && { commit: resolvedGit.resolvedCommit }),
100
+ ...(gitInfo.path && { path: gitInfo.path }),
101
+ ...(gitInfo.file && { file: gitInfo.file }),
102
+ dependencies: craftJson?.dependencies || {}
103
+ };
104
+ craftName = craftJson?.name || gitInfo.name || path_1.default.basename(gitInfo.url, '.git');
105
+ displayInfo = `${craftName}@${craftJson?.version || 'git'} from ${gitInfo.url}`;
106
+ logger_1.logger.succeedSpinner(`Resolved git dependency: ${displayInfo}`);
107
+ }
108
+ else {
109
+ // Regular registry dependency
110
+ logger_1.logger.startSpinner('Fetching craft information...');
111
+ // Check if the dependency has custom registry info in craftdesk.json
112
+ let registryOverride;
113
+ const existingDep = craftDeskJson.dependencies?.[craftName];
114
+ if (existingDep && typeof existingDep === 'object' && 'registry' in existingDep) {
115
+ registryOverride = existingDep.registry;
116
+ }
117
+ // Get craft info from registry
118
+ const craftInfo = await registry_client_1.registryClient.getCraftInfo(craftName, undefined, registryOverride);
119
+ if (!craftInfo) {
120
+ logger_1.logger.failSpinner(`Craft '${craftName}' not found`);
121
+ process.exit(1);
122
+ }
123
+ logger_1.logger.succeedSpinner(`Found ${craftInfo.name}@${craftInfo.version}`);
124
+ const version = options.saveExact ? craftInfo.version : versionConstraint;
125
+ depValue = version;
126
+ lockEntry = {
127
+ version: craftInfo.version,
128
+ resolved: craftInfo.download_url || `http://localhost:3000/api/v1/crafts/${craftInfo.author}/${craftInfo.name}/versions/${craftInfo.version}/download`,
129
+ integrity: craftInfo.integrity || 'sha256-placeholder',
130
+ type: craftInfo.type,
131
+ author: craftInfo.author,
132
+ dependencies: craftInfo.dependencies || {}
133
+ };
134
+ displayInfo = `${craftInfo.name}@${craftInfo.version}`;
135
+ }
136
+ // Determine which dependency field to use
137
+ let depField;
138
+ if (options.saveDev) {
139
+ depField = 'devDependencies';
140
+ }
141
+ else if (options.saveOptional) {
142
+ depField = 'optionalDependencies';
143
+ }
144
+ else {
145
+ depField = 'dependencies';
146
+ }
147
+ // Initialize field if it doesn't exist
148
+ if (!craftDeskJson[depField]) {
149
+ craftDeskJson[depField] = {};
150
+ }
151
+ // Add to craftdesk.json
152
+ craftDeskJson[depField][craftName] = depValue;
153
+ // Save craftdesk.json
154
+ await (0, file_system_1.writeCraftDeskJson)(craftDeskJson);
155
+ const displayVersion = typeof depValue === 'string' ? depValue : (depValue.tag || depValue.branch || 'git');
156
+ logger_1.logger.success(`Added ${craftName}@${displayVersion} to ${depField}`);
157
+ // Install the craft
158
+ logger_1.logger.startSpinner(`Installing ${craftName}...`);
159
+ await installer_1.installer.installCraft(craftName, lockEntry);
160
+ logger_1.logger.succeedSpinner(`Installed ${displayInfo}`);
161
+ // Update craftdesk.lock
162
+ let lockfile = await (0, file_system_1.readCraftDeskLock)();
163
+ if (!lockfile) {
164
+ lockfile = {
165
+ version: '1.0.0',
166
+ lockfileVersion: 1,
167
+ generatedAt: new Date().toISOString(),
168
+ crafts: {}
169
+ };
170
+ }
171
+ // Add or update the craft in the lockfile
172
+ lockfile.crafts[craftName] = lockEntry;
173
+ await (0, file_system_1.writeCraftDeskLock)(lockfile);
174
+ logger_1.logger.success('Craft added successfully!');
175
+ }
176
+ catch (error) {
177
+ logger_1.logger.failSpinner();
178
+ logger_1.logger.error(`Failed to add craft: ${error.message}`);
179
+ process.exit(1);
180
+ }
181
+ }
182
+ /**
183
+ * Converts GitHub web URLs to git URL format
184
+ *
185
+ * Supported GitHub URL formats:
186
+ * - https://github.com/user/repo/tree/branch/path/to/dir
187
+ * - https://github.com/user/repo/blob/branch/path/to/file.md
188
+ * - https://github.com/user/repo (assumes main branch)
189
+ *
190
+ * @param url - The GitHub URL (web or git format)
191
+ * @returns Normalized git URL with #branch#path: or #branch#file: syntax
192
+ * @private
193
+ */
194
+ function normalizeGitHubUrl(url) {
195
+ // Already in git+ format, return as-is
196
+ if (url.startsWith('git+')) {
197
+ return url;
198
+ }
199
+ // Already a .git URL, return as-is
200
+ if (url.endsWith('.git') || url.includes('.git#')) {
201
+ return url;
202
+ }
203
+ // Check if it's a GitHub web URL
204
+ const githubTreeMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)$/);
205
+ const githubBlobMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/(.+)$/);
206
+ const githubRepoMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/?$/);
207
+ if (githubTreeMatch) {
208
+ // Tree URL: directory reference
209
+ const [, owner, repo, branch, path] = githubTreeMatch;
210
+ return `git+https://github.com/${owner}/${repo}.git#${branch}#path:${path}`;
211
+ }
212
+ else if (githubBlobMatch) {
213
+ // Blob URL: file reference
214
+ const [, owner, repo, branch, file] = githubBlobMatch;
215
+ return `git+https://github.com/${owner}/${repo}.git#${branch}#file:${file}`;
216
+ }
217
+ else if (githubRepoMatch) {
218
+ // Repo URL without tree/blob: clone entire repo
219
+ const [, owner, repo] = githubRepoMatch;
220
+ return `git+https://github.com/${owner}/${repo}.git`;
221
+ }
222
+ // Not a recognized GitHub URL, return as-is
223
+ return url;
224
+ }
225
+ /**
226
+ * Parses a git URL with optional ref and subdirectory/file specifiers
227
+ *
228
+ * Supported formats:
229
+ * - git+https://github.com/user/repo.git#branch
230
+ * - git+https://github.com/user/repo.git#v1.0.0
231
+ * - git+https://github.com/user/repo.git#commit-hash
232
+ * - git+https://github.com/user/repo.git#path:subfolder/skill
233
+ * - git+https://github.com/user/repo.git#file:skill.md
234
+ * - git+https://github.com/user/repo.git#main#file:rspec-agent.md
235
+ * - https://github.com/user/repo.git
236
+ *
237
+ * @param urlString - The git URL to parse
238
+ * @returns Parsed git dependency information
239
+ * @private
240
+ */
241
+ function parseGitUrl(urlString) {
242
+ // Remove optional git+ prefix (npm-style format)
243
+ let url = urlString.replace(/^git\+/, '');
244
+ const result = {};
245
+ // Extract direct file path (e.g., #file:rspec-agent.md)
246
+ if (url.includes('#file:')) {
247
+ const [baseUrl, filePart] = url.split('#file:');
248
+ url = baseUrl;
249
+ result.file = filePart;
250
+ }
251
+ // Extract subdirectory path for monorepos (e.g., #path:crafts/auth)
252
+ if (url.includes('#path:')) {
253
+ const [baseUrl, pathPart] = url.split('#path:');
254
+ url = baseUrl;
255
+ result.path = pathPart;
256
+ }
257
+ // Extract git reference (branch/tag/commit)
258
+ // Must be parsed after path and file to avoid conflicts
259
+ if (url.includes('#') && !url.includes('#path:') && !url.includes('#file:')) {
260
+ const [baseUrl, ref] = url.split('#');
261
+ url = baseUrl;
262
+ // Heuristic to determine ref type:
263
+ // - Tags typically start with 'v' or match semver pattern (1.0.0)
264
+ if (ref.startsWith('v') || /^\d+\.\d+/.test(ref)) {
265
+ result.tag = ref;
266
+ }
267
+ // - Commit hashes are 40-character hex strings
268
+ else if (ref.length === 40 && /^[a-f0-9]+$/.test(ref)) {
269
+ result.commit = ref;
270
+ }
271
+ // - Otherwise, assume it's a branch name
272
+ else {
273
+ result.branch = ref;
274
+ }
275
+ }
276
+ result.url = url;
277
+ // Extract repository name from URL for display purposes
278
+ const match = url.match(/\/([^/]+?)(\.git)?$/);
279
+ if (match) {
280
+ result.name = match[1];
281
+ }
282
+ return result;
283
+ }
284
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function createConfigCommand(): Command;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,mBAAmB,IAAI,OAAO,CAiD7C"}
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createConfigCommand = createConfigCommand;
4
+ const commander_1 = require("commander");
5
+ const config_manager_1 = require("../services/config-manager");
6
+ const logger_1 = require("../utils/logger");
7
+ function createConfigCommand() {
8
+ const config = new commander_1.Command('config')
9
+ .description('Manage CraftDesk CLI configuration');
10
+ config
11
+ .command('list')
12
+ .description('List all configuration values')
13
+ .action(async () => {
14
+ await config_manager_1.configManager.list();
15
+ });
16
+ config
17
+ .command('get <key>')
18
+ .description('Get a configuration value')
19
+ .action(async (key) => {
20
+ const value = await config_manager_1.configManager.get(key);
21
+ if (value !== undefined) {
22
+ logger_1.logger.log(value);
23
+ }
24
+ else {
25
+ logger_1.logger.error(`Configuration key '${key}' not found`);
26
+ process.exit(1);
27
+ }
28
+ });
29
+ config
30
+ .command('set <key> <value>')
31
+ .description('Set a configuration value')
32
+ .action(async (key, value) => {
33
+ // Handle special cases
34
+ if (key.includes(':registry')) {
35
+ // Setting a scoped registry
36
+ // e.g., @acme:registry -> scopes.@acme
37
+ const scope = key.split(':')[0];
38
+ await config_manager_1.configManager.set(`scopes.${scope}`, value);
39
+ logger_1.logger.success(`Set registry for ${scope} to ${value}`);
40
+ }
41
+ else if (key.startsWith('//') && key.includes('/:token')) {
42
+ // Setting auth token
43
+ // e.g., //crafts.acme.com/:token -> auth.crafts.acme.com.token
44
+ const host = key.substring(2, key.indexOf('/:'));
45
+ await config_manager_1.configManager.set(`auth.${host}.token`, value);
46
+ logger_1.logger.success(`Set auth token for ${host}`);
47
+ }
48
+ else {
49
+ // Normal key-value
50
+ await config_manager_1.configManager.set(key, value);
51
+ logger_1.logger.success(`Set ${key} to ${value}`);
52
+ }
53
+ });
54
+ return config;
55
+ }
56
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":";;AAIA,kDAiDC;AArDD,yCAAoC;AACpC,+DAA2D;AAC3D,4CAAyC;AAEzC,SAAgB,mBAAmB;IACjC,MAAM,MAAM,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC;SACjC,WAAW,CAAC,oCAAoC,CAAC,CAAC;IAErD,MAAM;SACH,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,+BAA+B,CAAC;SAC5C,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,8BAAa,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEL,MAAM;SACH,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,2BAA2B,CAAC;SACxC,MAAM,CAAC,KAAK,EAAE,GAAW,EAAE,EAAE;QAC5B,MAAM,KAAK,GAAG,MAAM,8BAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,eAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,eAAM,CAAC,KAAK,CAAC,sBAAsB,GAAG,aAAa,CAAC,CAAC;YACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,MAAM;SACH,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,2BAA2B,CAAC;SACxC,MAAM,CAAC,KAAK,EAAE,GAAW,EAAE,KAAa,EAAE,EAAE;QAC3C,uBAAuB;QACvB,IAAI,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAC9B,4BAA4B;YAC5B,uCAAuC;YACvC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,8BAAa,CAAC,GAAG,CAAC,UAAU,KAAK,EAAE,EAAE,KAAK,CAAC,CAAC;YAClD,eAAM,CAAC,OAAO,CAAC,oBAAoB,KAAK,OAAO,KAAK,EAAE,CAAC,CAAC;QAC1D,CAAC;aAAM,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3D,qBAAqB;YACrB,+DAA+D;YAC/D,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YACjD,MAAM,8BAAa,CAAC,GAAG,CAAC,QAAQ,IAAI,QAAQ,EAAE,KAAK,CAAC,CAAC;YACrD,eAAM,CAAC,OAAO,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,mBAAmB;YACnB,MAAM,8BAAa,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACpC,eAAM,CAAC,OAAO,CAAC,OAAO,GAAG,OAAO,KAAK,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function createInitCommand(): Command;
3
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,iBAAiB,IAAI,OAAO,CAa3C"}