@paulduvall/claude-dev-toolkit 0.0.1-alpha.7 → 0.0.1-alpha.8

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 (34) hide show
  1. package/README.md +9 -9
  2. package/bin/claude-commands +161 -6
  3. package/hooks/lib/argument-parser.sh +0 -0
  4. package/hooks/lib/config-constants.sh +0 -0
  5. package/hooks/lib/context-manager.sh +0 -0
  6. package/hooks/lib/error-handler.sh +0 -0
  7. package/hooks/lib/execution-engine.sh +0 -0
  8. package/hooks/lib/file-utils.sh +0 -0
  9. package/hooks/lib/subagent-discovery.sh +0 -0
  10. package/hooks/lib/subagent-validator.sh +0 -0
  11. package/lib/backup-restore-command.js +140 -0
  12. package/lib/base/base-command.js +252 -0
  13. package/lib/base/command-result.js +184 -0
  14. package/lib/config/constants.js +255 -0
  15. package/lib/config.js +48 -6
  16. package/lib/configure-command.js +428 -0
  17. package/lib/dependency-validator.js +64 -5
  18. package/lib/installation-instruction-generator-backup.js +579 -0
  19. package/lib/installation-instruction-generator.js +213 -495
  20. package/lib/installer.js +134 -52
  21. package/lib/services/backup-list-service.js +226 -0
  22. package/lib/services/backup-service.js +230 -0
  23. package/lib/services/command-installer-service.js +217 -0
  24. package/lib/services/logger-service.js +201 -0
  25. package/lib/services/package-manager-service.js +319 -0
  26. package/lib/services/platform-instruction-service.js +294 -0
  27. package/lib/services/recovery-instruction-service.js +348 -0
  28. package/lib/services/restore-service.js +221 -0
  29. package/lib/setup-command.js +309 -0
  30. package/lib/utils/claude-path-config.js +184 -0
  31. package/lib/utils/file-system-utils.js +152 -0
  32. package/lib/utils.js +8 -4
  33. package/lib/verify-command.js +430 -0
  34. package/package.json +1 -1
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Command Result Value Object
3
+ * Standardized result structure for all command operations
4
+ */
5
+
6
+ class CommandResult {
7
+ constructor(success, data = {}) {
8
+ this.success = success;
9
+ this.timestamp = new Date().toISOString();
10
+
11
+ // Merge provided data
12
+ Object.assign(this, data);
13
+ }
14
+
15
+ /**
16
+ * Create successful result
17
+ */
18
+ static success(data = {}) {
19
+ return new CommandResult(true, data);
20
+ }
21
+
22
+ /**
23
+ * Create failure result
24
+ */
25
+ static failure(error, data = {}) {
26
+ return new CommandResult(false, {
27
+ error: typeof error === 'string' ? error : error.message,
28
+ originalError: typeof error === 'object' ? error : undefined,
29
+ ...data
30
+ });
31
+ }
32
+
33
+ /**
34
+ * Create result from boolean
35
+ */
36
+ static fromBoolean(isSuccess, successData = {}, failureData = {}) {
37
+ return isSuccess
38
+ ? CommandResult.success(successData)
39
+ : CommandResult.failure('Operation failed', failureData);
40
+ }
41
+
42
+ /**
43
+ * Check if result indicates success
44
+ */
45
+ isSuccess() {
46
+ return this.success === true;
47
+ }
48
+
49
+ /**
50
+ * Check if result indicates failure
51
+ */
52
+ isFailure() {
53
+ return this.success === false;
54
+ }
55
+
56
+ /**
57
+ * Get error message if failure
58
+ */
59
+ getError() {
60
+ return this.success ? null : (this.error || 'Unknown error');
61
+ }
62
+
63
+ /**
64
+ * Get data payload
65
+ */
66
+ getData(key = null) {
67
+ if (key) {
68
+ return this[key];
69
+ }
70
+
71
+ // Return all data except control properties
72
+ const { success, timestamp, error, originalError, ...data } = this;
73
+ return data;
74
+ }
75
+
76
+ /**
77
+ * Transform result with a function
78
+ */
79
+ map(transform) {
80
+ if (this.isFailure()) {
81
+ return this; // Return unchanged failure
82
+ }
83
+
84
+ try {
85
+ const transformedData = transform(this.getData());
86
+ return CommandResult.success(transformedData);
87
+ } catch (error) {
88
+ return CommandResult.failure(error);
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Chain operations that might fail
94
+ */
95
+ flatMap(operation) {
96
+ if (this.isFailure()) {
97
+ return this; // Return unchanged failure
98
+ }
99
+
100
+ try {
101
+ const result = operation(this.getData());
102
+ return result instanceof CommandResult ? result : CommandResult.success(result);
103
+ } catch (error) {
104
+ return CommandResult.failure(error);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Provide fallback value for failures
110
+ */
111
+ orElse(fallback) {
112
+ return this.isSuccess() ? this.getData() : fallback;
113
+ }
114
+
115
+ /**
116
+ * Convert to JSON for serialization
117
+ */
118
+ toJSON() {
119
+ return {
120
+ success: this.success,
121
+ timestamp: this.timestamp,
122
+ ...(this.success ? this.getData() : { error: this.getError() })
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Convert to human-readable string
128
+ */
129
+ toString() {
130
+ if (this.success) {
131
+ const data = this.getData();
132
+ const keys = Object.keys(data);
133
+
134
+ if (keys.length === 0) {
135
+ return '✅ Success';
136
+ } else if (keys.length === 1) {
137
+ return `✅ Success: ${keys[0]} = ${data[keys[0]]}`;
138
+ } else {
139
+ return `✅ Success (${keys.length} properties)`;
140
+ }
141
+ } else {
142
+ return `❌ Failure: ${this.getError()}`;
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Merge with another result (for combining operations)
148
+ */
149
+ merge(otherResult) {
150
+ if (this.isFailure()) return this;
151
+ if (otherResult.isFailure()) return otherResult;
152
+
153
+ return CommandResult.success({
154
+ ...this.getData(),
155
+ ...otherResult.getData()
156
+ });
157
+ }
158
+
159
+ /**
160
+ * Add metrics to the result
161
+ */
162
+ withMetrics(metrics) {
163
+ this.metrics = metrics;
164
+ return this;
165
+ }
166
+
167
+ /**
168
+ * Add duration to the result
169
+ */
170
+ withDuration(duration) {
171
+ this.duration = typeof duration === 'number' ? `${duration.toFixed(2)}s` : duration;
172
+ return this;
173
+ }
174
+
175
+ /**
176
+ * Add context information
177
+ */
178
+ withContext(context) {
179
+ this.context = context;
180
+ return this;
181
+ }
182
+ }
183
+
184
+ module.exports = CommandResult;
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Configuration Constants
3
+ * Centralized configuration constants used throughout the application
4
+ */
5
+
6
+ // System Requirements
7
+ const SYSTEM_REQUIREMENTS = {
8
+ MIN_NODE_VERSION: '16.0.0',
9
+ MIN_NPM_VERSION: '7.0.0',
10
+ MIN_MEMORY_GB: 2,
11
+ MIN_DISK_SPACE_MB: 100,
12
+ SUPPORTED_PLATFORMS: ['linux', 'darwin', 'win32'],
13
+ SUPPORTED_ARCHITECTURES: ['x64', 'arm64']
14
+ };
15
+
16
+ // Download Links for Dependencies
17
+ const DOWNLOAD_LINKS = {
18
+ git: {
19
+ linux: 'https://git-scm.com/download/linux',
20
+ darwin: 'https://git-scm.com/download/mac',
21
+ win32: 'https://git-scm.com/download/win',
22
+ all: 'https://git-scm.com/downloads'
23
+ },
24
+ node: {
25
+ all: 'https://nodejs.org/en/download/',
26
+ lts: 'https://nodejs.org/en/download/package-manager/',
27
+ docker: 'https://hub.docker.com/_/node'
28
+ },
29
+ python: {
30
+ all: 'https://www.python.org/downloads/',
31
+ anaconda: 'https://www.anaconda.com/products/distribution',
32
+ pyenv: 'https://github.com/pyenv/pyenv'
33
+ },
34
+ docker: {
35
+ linux: 'https://docs.docker.com/engine/install/',
36
+ darwin: 'https://docs.docker.com/desktop/install/mac-install/',
37
+ win32: 'https://docs.docker.com/desktop/install/windows-install/'
38
+ },
39
+ claudeCode: {
40
+ npm: 'https://www.npmjs.com/package/@anthropic-ai/claude-code',
41
+ github: 'https://github.com/anthropics/claude-code'
42
+ }
43
+ };
44
+
45
+ // Package Manager Commands
46
+ const PACKAGE_MANAGERS = {
47
+ npm: {
48
+ name: 'npm',
49
+ install: 'npm install',
50
+ globalInstall: 'npm install -g',
51
+ update: 'npm update',
52
+ checkCommand: 'npm --version',
53
+ installUrl: 'https://docs.npmjs.com/downloading-and-installing-node-js-and-npm'
54
+ },
55
+ yarn: {
56
+ name: 'yarn',
57
+ install: 'yarn add',
58
+ globalInstall: 'yarn global add',
59
+ update: 'yarn upgrade',
60
+ checkCommand: 'yarn --version',
61
+ installUrl: 'https://classic.yarnpkg.com/lang/en/docs/install/'
62
+ },
63
+ pnpm: {
64
+ name: 'pnpm',
65
+ install: 'pnpm add',
66
+ globalInstall: 'pnpm add -g',
67
+ update: 'pnpm update',
68
+ checkCommand: 'pnpm --version',
69
+ installUrl: 'https://pnpm.io/installation'
70
+ },
71
+ brew: {
72
+ name: 'Homebrew',
73
+ install: 'brew install',
74
+ update: 'brew upgrade',
75
+ checkCommand: 'brew --version',
76
+ installUrl: 'https://brew.sh/',
77
+ platforms: ['darwin']
78
+ },
79
+ apt: {
80
+ name: 'apt',
81
+ install: 'sudo apt install',
82
+ update: 'sudo apt update && sudo apt upgrade',
83
+ checkCommand: 'apt --version',
84
+ platforms: ['linux']
85
+ },
86
+ yum: {
87
+ name: 'yum',
88
+ install: 'sudo yum install',
89
+ update: 'sudo yum update',
90
+ checkCommand: 'yum --version',
91
+ platforms: ['linux']
92
+ },
93
+ chocolatey: {
94
+ name: 'Chocolatey',
95
+ install: 'choco install',
96
+ update: 'choco upgrade',
97
+ checkCommand: 'choco --version',
98
+ installUrl: 'https://chocolatey.org/install',
99
+ platforms: ['win32']
100
+ },
101
+ winget: {
102
+ name: 'winget',
103
+ install: 'winget install',
104
+ update: 'winget upgrade',
105
+ checkCommand: 'winget --version',
106
+ platforms: ['win32']
107
+ }
108
+ };
109
+
110
+ // File and Directory Constants
111
+ const PATHS = {
112
+ CLAUDE_DIR: '.claude',
113
+ COMMANDS_DIR: 'commands',
114
+ HOOKS_DIR: 'hooks',
115
+ BACKUPS_DIR: 'backups',
116
+ SUBAGENTS_DIR: 'subagents',
117
+ SETTINGS_FILE: 'settings.json',
118
+ LOG_FILE: 'claude-commands.log',
119
+ TEMP_DIR: '.temp',
120
+ ACTIVE_COMMANDS: 'active',
121
+ EXPERIMENTAL_COMMANDS: 'experiments'
122
+ };
123
+
124
+ // Command Configuration
125
+ const COMMANDS = {
126
+ MAX_COMMANDS: 100,
127
+ DEFAULT_COMMAND_TYPE: 'active',
128
+ SUPPORTED_EXTENSIONS: ['.md', '.txt'],
129
+ FRONTMATTER_DELIMITER: '---',
130
+ REQUIRED_SECTIONS: ['description', 'usage', 'implementation'],
131
+ MAX_FILE_SIZE_MB: 5,
132
+ ENCODING: 'utf8'
133
+ };
134
+
135
+ // Backup Configuration
136
+ const BACKUP = {
137
+ MAX_BACKUPS: 10,
138
+ COMPRESSION_ENABLED: true,
139
+ COMPRESSION_LEVEL: 6,
140
+ RETENTION_DAYS: 30,
141
+ AUTO_CLEANUP: true,
142
+ BACKUP_FORMAT: 'tar.gz',
143
+ NAMING_PATTERN: 'backup-YYYYMMDD-HHMMSS'
144
+ };
145
+
146
+ // Installation Configuration
147
+ const INSTALLATION = {
148
+ MAX_CONCURRENT_OPERATIONS: 5,
149
+ TIMEOUT_MS: 30000,
150
+ RETRY_ATTEMPTS: 3,
151
+ RETRY_DELAY_MS: 1000,
152
+ PROGRESS_UPDATE_INTERVAL: 500,
153
+ VALIDATION_ENABLED: true,
154
+ AUTO_BACKUP: false,
155
+ FORCE_OVERWRITE: false
156
+ };
157
+
158
+ // Logging Configuration
159
+ const LOGGING = {
160
+ DEFAULT_LEVEL: 'info',
161
+ LEVELS: ['debug', 'info', 'warn', 'error'],
162
+ MAX_LOG_SIZE_MB: 10,
163
+ MAX_LOG_FILES: 5,
164
+ TIMESTAMP_FORMAT: 'YYYY-MM-DD HH:mm:ss',
165
+ ENABLE_COLORS: true,
166
+ ENABLE_CONTEXT: true
167
+ };
168
+
169
+ // Security Configuration
170
+ const SECURITY = {
171
+ ALLOWED_PROTOCOLS: ['http:', 'https:', 'file:'],
172
+ MAX_DOWNLOAD_SIZE_MB: 50,
173
+ ALLOWED_FILE_TYPES: ['.md', '.txt', '.json', '.js', '.sh', '.py'],
174
+ DANGEROUS_COMMANDS: ['rm -rf', 'format', 'del /f', 'sudo rm'],
175
+ CREDENTIAL_PATTERNS: [
176
+ /api[_-]?key\s*[:=]\s*['"][^'"]+['"]/i,
177
+ /secret[_-]?key\s*[:=]\s*['"][^'"]+['"]/i,
178
+ /password\s*[:=]\s*['"][^'"]+['"]/i,
179
+ /token\s*[:=]\s*['"][^'"]+['"]/i
180
+ ],
181
+ HOOK_TIMEOUT_MS: 5000
182
+ };
183
+
184
+ // Error Messages
185
+ const ERROR_MESSAGES = {
186
+ INSTALLATION_FAILED: 'Installation failed. Please check your setup and try again.',
187
+ PERMISSION_DENIED: 'Permission denied. Please check file permissions and try again.',
188
+ NETWORK_ERROR: 'Network error. Please check your internet connection.',
189
+ FILE_NOT_FOUND: 'Required file not found. Please ensure all dependencies are installed.',
190
+ INVALID_CONFIGURATION: 'Invalid configuration. Please check your settings.',
191
+ COMMAND_NOT_FOUND: 'Command not found. Please install required dependencies.',
192
+ VALIDATION_FAILED: 'Validation failed. Please check the input and try again.',
193
+ BACKUP_FAILED: 'Backup operation failed. Please check disk space and permissions.',
194
+ RESTORE_FAILED: 'Restore operation failed. Please check backup integrity.',
195
+ DEPENDENCY_MISSING: 'Required dependency is missing. Please install and try again.'
196
+ };
197
+
198
+ // Success Messages
199
+ const SUCCESS_MESSAGES = {
200
+ INSTALLATION_COMPLETE: 'Installation completed successfully!',
201
+ BACKUP_CREATED: 'Backup created successfully.',
202
+ RESTORE_COMPLETE: 'Restore completed successfully.',
203
+ CONFIGURATION_UPDATED: 'Configuration updated successfully.',
204
+ VALIDATION_PASSED: 'All validations passed.',
205
+ CLEANUP_COMPLETE: 'Cleanup completed successfully.'
206
+ };
207
+
208
+ // CLI Configuration
209
+ const CLI = {
210
+ PROGRAM_NAME: 'claude-commands',
211
+ VERSION: '1.0.0',
212
+ DESCRIPTION: 'Claude Code Custom Commands CLI',
213
+ DEFAULT_COMMAND: 'help',
214
+ EXIT_CODES: {
215
+ SUCCESS: 0,
216
+ GENERAL_ERROR: 1,
217
+ INVALID_USAGE: 2,
218
+ PERMISSION_DENIED: 3,
219
+ NOT_FOUND: 4,
220
+ NETWORK_ERROR: 5,
221
+ VALIDATION_ERROR: 6
222
+ }
223
+ };
224
+
225
+ // Export all constants
226
+ module.exports = {
227
+ SYSTEM_REQUIREMENTS,
228
+ DOWNLOAD_LINKS,
229
+ PACKAGE_MANAGERS,
230
+ PATHS,
231
+ COMMANDS,
232
+ BACKUP,
233
+ INSTALLATION,
234
+ LOGGING,
235
+ SECURITY,
236
+ ERROR_MESSAGES,
237
+ SUCCESS_MESSAGES,
238
+ CLI
239
+ };
240
+
241
+ // Export individual constants for convenience
242
+ module.exports.constants = {
243
+ SYSTEM_REQUIREMENTS,
244
+ DOWNLOAD_LINKS,
245
+ PACKAGE_MANAGERS,
246
+ PATHS,
247
+ COMMANDS,
248
+ BACKUP,
249
+ INSTALLATION,
250
+ LOGGING,
251
+ SECURITY,
252
+ ERROR_MESSAGES,
253
+ SUCCESS_MESSAGES,
254
+ CLI
255
+ };
package/lib/config.js CHANGED
@@ -229,8 +229,14 @@ class ConfigManager {
229
229
  // REQ-CONFIG-002: Apply Template
230
230
  applyTemplate(templateName) {
231
231
  try {
232
- // Validate template exists
233
- const templatePath = path.join(this.templatesDir, templateName);
232
+ // Resolve template name to full filename
233
+ const resolvedTemplate = this.resolveTemplateName(templateName);
234
+ if (!resolvedTemplate) {
235
+ this.handleTemplateNotFound(templateName);
236
+ return false;
237
+ }
238
+
239
+ const templatePath = path.join(this.templatesDir, resolvedTemplate);
234
240
  if (!fs.existsSync(templatePath)) {
235
241
  this.handleTemplateNotFound(templateName);
236
242
  return false;
@@ -246,7 +252,7 @@ class ConfigManager {
246
252
  const success = applyConfigurationTemplate(templatePath, this.settingsPath);
247
253
 
248
254
  if (success) {
249
- console.log(`✅ Successfully applied template '${templateName}'`);
255
+ console.log(`✅ Successfully applied template '${templateName}' (${resolvedTemplate})`);
250
256
  console.log(`📝 Configuration saved to: ${this.settingsPath}`);
251
257
  return true;
252
258
  } else {
@@ -259,6 +265,41 @@ class ConfigManager {
259
265
  }
260
266
  }
261
267
 
268
+ // Helper method to resolve template names (supports short names)
269
+ resolveTemplateName(templateName) {
270
+ // Return as-is if it already has .json extension
271
+ if (templateName.endsWith('.json')) {
272
+ return templateName;
273
+ }
274
+
275
+ // Map short names to full filenames
276
+ const templateMap = {
277
+ 'basic': 'basic-settings.json',
278
+ 'comprehensive': 'comprehensive-settings.json',
279
+ 'security-focused': 'security-focused-settings.json',
280
+ 'security': 'security-focused-settings.json'
281
+ };
282
+
283
+ // Check if it's a known short name
284
+ if (templateMap[templateName]) {
285
+ return templateMap[templateName];
286
+ }
287
+
288
+ // Try adding .json extension
289
+ const withExtension = `${templateName}.json`;
290
+ if (fs.existsSync(path.join(this.templatesDir, withExtension))) {
291
+ return withExtension;
292
+ }
293
+
294
+ // Try adding -settings.json extension
295
+ const withSettingsExtension = `${templateName}-settings.json`;
296
+ if (fs.existsSync(path.join(this.templatesDir, withSettingsExtension))) {
297
+ return withSettingsExtension;
298
+ }
299
+
300
+ return null;
301
+ }
302
+
262
303
  // Helper method for template not found error
263
304
  handleTemplateNotFound(templateName) {
264
305
  console.error(`❌ Template '${templateName}' not found.`);
@@ -303,9 +344,10 @@ class ConfigManager {
303
344
  console.log(' -t, --template <name> Apply configuration template');
304
345
  console.log(' -h, --help Show this help message\n');
305
346
  console.log('Examples:');
306
- console.log(' claude-commands config --list # Show available templates');
307
- console.log(' claude-commands config --template basic-settings.json # Apply basic template');
308
- console.log(' claude-commands config --help # Show this help\n');
347
+ console.log(' claude-commands config --list # Show available templates');
348
+ console.log(' claude-commands config --template comprehensive # Apply comprehensive template');
349
+ console.log(' claude-commands config --template basic # Apply basic template');
350
+ console.log(' claude-commands config --help # Show this help\n');
309
351
  console.log('Description:');
310
352
  console.log(' Manage Claude Code configuration templates. Templates are applied');
311
353
  console.log(' to ~/.claude/settings.json with automatic backup of existing settings.');