@paulduvall/claude-dev-toolkit 0.0.1-alpha.1 → 0.0.1-alpha.2

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 (35) hide show
  1. package/bin/claude-commands +20 -0
  2. package/lib/config.js +186 -3
  3. package/lib/result.js +138 -0
  4. package/lib/subagent-formatter.js +278 -0
  5. package/lib/subagents-core.js +237 -0
  6. package/lib/subagents.js +508 -0
  7. package/lib/types.d.ts +183 -0
  8. package/package.json +12 -3
  9. package/subagents/api-guardian.md +29 -0
  10. package/subagents/audit-trail-verifier.md +24 -0
  11. package/subagents/change-scoper.md +23 -0
  12. package/subagents/ci-pipeline-curator.md +24 -0
  13. package/subagents/code-review-assistant.md +258 -0
  14. package/subagents/continuous-release-orchestrator.md +29 -0
  15. package/subagents/contract-tester.md +24 -0
  16. package/subagents/data-steward.md +29 -0
  17. package/subagents/debug-context.md +197 -0
  18. package/subagents/debug-specialist.md +138 -0
  19. package/subagents/dependency-steward.md +24 -0
  20. package/subagents/deployment-strategist.md +29 -0
  21. package/subagents/documentation-curator.md +29 -0
  22. package/subagents/environment-guardian.md +29 -0
  23. package/subagents/license-compliance-guardian.md +29 -0
  24. package/subagents/observability-engineer.md +25 -0
  25. package/subagents/performance-guardian.md +29 -0
  26. package/subagents/product-owner-proxy.md +28 -0
  27. package/subagents/requirements-reviewer.md +26 -0
  28. package/subagents/rollback-first-responder.md +24 -0
  29. package/subagents/sbom-provenance.md +25 -0
  30. package/subagents/security-auditor.md +29 -0
  31. package/subagents/style-enforcer.md +23 -0
  32. package/subagents/test-writer.md +24 -0
  33. package/subagents/trunk-guardian.md +29 -0
  34. package/subagents/workflow-coordinator.md +26 -0
  35. package/tsconfig.json +37 -0
@@ -129,4 +129,24 @@ program
129
129
  }
130
130
  });
131
131
 
132
+ program
133
+ .command('subagents')
134
+ .description('Manage AI subagents for Claude Code')
135
+ .option('-l, --list', 'List available subagents')
136
+ .option('-i, --install', 'Install subagents to Claude Code')
137
+ .action((options) => {
138
+ const subagents = require('../lib/subagents');
139
+ subagents.handleCommand(options);
140
+ });
141
+
142
+ program
143
+ .command('config')
144
+ .description('Manage Claude Code configuration templates')
145
+ .option('-l, --list', 'List available configuration templates')
146
+ .option('-t, --template <name>', 'Apply configuration template')
147
+ .action((options) => {
148
+ const config = require('../lib/config');
149
+ config.handleCommand(options);
150
+ });
151
+
132
152
  program.parse(process.argv);
package/lib/config.js CHANGED
@@ -145,7 +145,7 @@ function getAvailableTemplates(templatesDir) {
145
145
  id: path.basename(file, '.json'),
146
146
  name: file,
147
147
  path: templatePath,
148
- description: data['// Description'] || `${file} template`,
148
+ description: getTemplateDescription(file, data),
149
149
  features: Object.keys(data).filter(key => !key.startsWith('//')).length
150
150
  });
151
151
  } catch (error) {
@@ -161,6 +161,184 @@ function getAvailableTemplates(templatesDir) {
161
161
  }
162
162
  }
163
163
 
164
+ /**
165
+ * Get human-readable description for a template
166
+ * @param {string} filename - Template filename
167
+ * @param {Object} data - Parsed template data
168
+ * @returns {string} - Human-readable description
169
+ */
170
+ function getTemplateDescription(filename, data) {
171
+ // Check for explicit description in template
172
+ if (data['// Description']) {
173
+ return data['// Description'];
174
+ }
175
+
176
+ // Generate description based on filename
177
+ const basename = path.basename(filename, '.json');
178
+ switch (basename) {
179
+ case 'basic-settings':
180
+ return 'Minimal configuration for getting started with Claude Code';
181
+ case 'comprehensive-settings':
182
+ return 'Full-featured configuration with all available options';
183
+ case 'security-focused-settings':
184
+ return 'Security-enhanced configuration with additional protections';
185
+ default:
186
+ return `Configuration template: ${basename}`;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * GREEN PHASE: Config Command CLI Handler
192
+ * Implements claude-commands config feature requirements
193
+ */
194
+ class ConfigManager {
195
+ constructor() {
196
+ this.templatesDir = path.join(__dirname, '..', 'templates');
197
+ this.claudeDir = path.join(os.homedir(), '.claude');
198
+ this.settingsPath = path.join(this.claudeDir, 'settings.json');
199
+ }
200
+
201
+ // REQ-CONFIG-001: List Templates
202
+ listTemplates() {
203
+ console.log('📋 Available Configuration Templates:\n');
204
+
205
+ try {
206
+ if (!fs.existsSync(this.templatesDir)) {
207
+ console.log('❌ Templates directory not found');
208
+ return;
209
+ }
210
+
211
+ const templates = getAvailableTemplates(this.templatesDir);
212
+
213
+ if (templates.length === 0) {
214
+ console.log('No configuration templates found');
215
+ return;
216
+ }
217
+
218
+ templates.forEach(template => {
219
+ console.log(` 📄 ${template.name}`);
220
+ console.log(` ${template.description}`);
221
+ });
222
+
223
+ console.log(`\n💡 Usage: claude-commands config --template <name>`);
224
+ } catch (error) {
225
+ console.error('❌ Error listing templates:', error.message);
226
+ }
227
+ }
228
+
229
+ // REQ-CONFIG-002: Apply Template
230
+ applyTemplate(templateName) {
231
+ try {
232
+ // Validate template exists
233
+ const templatePath = path.join(this.templatesDir, templateName);
234
+ if (!fs.existsSync(templatePath)) {
235
+ this.handleTemplateNotFound(templateName);
236
+ return false;
237
+ }
238
+
239
+ // Ensure Claude directory exists
240
+ this.ensureClaudeDirectory();
241
+
242
+ // Backup existing settings if present
243
+ this.backupExistingSettings();
244
+
245
+ // Apply template using existing function
246
+ const success = applyConfigurationTemplate(templatePath, this.settingsPath);
247
+
248
+ if (success) {
249
+ console.log(`✅ Successfully applied template '${templateName}'`);
250
+ console.log(`📝 Configuration saved to: ${this.settingsPath}`);
251
+ return true;
252
+ } else {
253
+ console.error(`❌ Failed to apply template '${templateName}'`);
254
+ return false;
255
+ }
256
+ } catch (error) {
257
+ console.error('❌ Error applying template:', error.message);
258
+ return false;
259
+ }
260
+ }
261
+
262
+ // Helper method for template not found error
263
+ handleTemplateNotFound(templateName) {
264
+ console.error(`❌ Template '${templateName}' not found.`);
265
+ this.listTemplates();
266
+ }
267
+
268
+ // Helper method to ensure Claude directory exists
269
+ ensureClaudeDirectory() {
270
+ if (!fs.existsSync(this.claudeDir)) {
271
+ fs.mkdirSync(this.claudeDir, { recursive: true });
272
+ console.log(`📁 Created directory: ${this.claudeDir}`);
273
+ }
274
+ }
275
+
276
+ // Helper method to backup existing settings
277
+ backupExistingSettings() {
278
+ if (fs.existsSync(this.settingsPath)) {
279
+ const backupPath = this.createBackup();
280
+ console.log(`💾 Backed up existing settings to: ${backupPath}`);
281
+ }
282
+ }
283
+
284
+ // REQ-CONFIG-002: Backup functionality
285
+ createBackup() {
286
+ const timestamp = new Date().toISOString()
287
+ .replace(/[:.]/g, '-')
288
+ .replace('T', '-')
289
+ .split('.')[0]; // YYYY-MM-DD-HHMMSS format
290
+
291
+ const backupPath = `${this.settingsPath}.backup.${timestamp}`;
292
+ fs.copyFileSync(this.settingsPath, backupPath);
293
+ return backupPath;
294
+ }
295
+
296
+ // REQ-CONFIG-003: Show Help
297
+ showHelp() {
298
+ console.log('🔧 Claude Commands Config Tool\n');
299
+ console.log('Usage:');
300
+ console.log(' claude-commands config [options]\n');
301
+ console.log('Options:');
302
+ console.log(' -l, --list List available configuration templates');
303
+ console.log(' -t, --template <name> Apply configuration template');
304
+ console.log(' -h, --help Show this help message\n');
305
+ 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');
309
+ console.log('Description:');
310
+ console.log(' Manage Claude Code configuration templates. Templates are applied');
311
+ console.log(' to ~/.claude/settings.json with automatic backup of existing settings.');
312
+ }
313
+
314
+ // Main command handler
315
+ handleCommand(options) {
316
+ // REQ-CONFIG-003: Show help when no options or explicit help
317
+ if (!options.list && !options.template) {
318
+ this.showHelp();
319
+ return;
320
+ }
321
+
322
+ // REQ-CONFIG-001: List templates
323
+ if (options.list) {
324
+ this.listTemplates();
325
+ return;
326
+ }
327
+
328
+ // REQ-CONFIG-002: Apply template
329
+ if (options.template) {
330
+ const success = this.applyTemplate(options.template);
331
+ if (!success) {
332
+ process.exit(1);
333
+ }
334
+ return;
335
+ }
336
+ }
337
+ }
338
+
339
+ // Create config manager instance
340
+ const configManager = new ConfigManager();
341
+
164
342
  module.exports = {
165
343
  getConfigPath: () => {
166
344
  return path.join(os.homedir(), '.claude', 'commands');
@@ -172,11 +350,16 @@ module.exports = {
172
350
  colorOutput: true
173
351
  },
174
352
 
175
- // REQ-009 Implementation
353
+ // REQ-009 Implementation (existing)
176
354
  applyConfigurationTemplate,
177
355
  getAvailableTemplates,
178
356
 
179
357
  // Utility functions (exposed for testing)
180
358
  parseJSONC,
181
- deepMerge
359
+ deepMerge,
360
+ getTemplateDescription,
361
+
362
+ // CLI command handler (new)
363
+ handleCommand: (options) => configManager.handleCommand(options),
364
+ ConfigManager
182
365
  };
package/lib/result.js ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Result/Either Pattern Implementation
3
+ * Provides functional error handling without throwing exceptions
4
+ */
5
+
6
+ /**
7
+ * Success result containing a value
8
+ */
9
+ class Ok {
10
+ constructor(value) {
11
+ this.value = value;
12
+ this.isOk = true;
13
+ this.isError = false;
14
+ }
15
+
16
+ map(fn) {
17
+ try {
18
+ return new Ok(fn(this.value));
19
+ } catch (error) {
20
+ return new Err(error);
21
+ }
22
+ }
23
+
24
+ flatMap(fn) {
25
+ try {
26
+ return fn(this.value);
27
+ } catch (error) {
28
+ return new Err(error);
29
+ }
30
+ }
31
+
32
+ mapError() {
33
+ return this;
34
+ }
35
+
36
+ unwrap() {
37
+ return this.value;
38
+ }
39
+
40
+ unwrapOr() {
41
+ return this.value;
42
+ }
43
+
44
+ match(okFn, errFn) {
45
+ return okFn(this.value);
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Error result containing an error
51
+ */
52
+ class Err {
53
+ constructor(error) {
54
+ this.error = error;
55
+ this.isOk = false;
56
+ this.isError = true;
57
+ }
58
+
59
+ map() {
60
+ return this;
61
+ }
62
+
63
+ flatMap() {
64
+ return this;
65
+ }
66
+
67
+ mapError(fn) {
68
+ try {
69
+ return new Err(fn(this.error));
70
+ } catch (error) {
71
+ return new Err(error);
72
+ }
73
+ }
74
+
75
+ unwrap() {
76
+ throw new Error(`Called unwrap on an Err: ${this.error}`);
77
+ }
78
+
79
+ unwrapOr(defaultValue) {
80
+ return defaultValue;
81
+ }
82
+
83
+ match(okFn, errFn) {
84
+ return errFn(this.error);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Static factory methods for creating Results
90
+ */
91
+ class Result {
92
+ static ok(value) {
93
+ return new Ok(value);
94
+ }
95
+
96
+ static err(error) {
97
+ return new Err(error);
98
+ }
99
+
100
+ /**
101
+ * Wraps a function that might throw in a Result
102
+ */
103
+ static try(fn) {
104
+ try {
105
+ return Result.ok(fn());
106
+ } catch (error) {
107
+ return Result.err(error);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Wraps an async function that might throw in a Result
113
+ */
114
+ static async tryAsync(fn) {
115
+ try {
116
+ const result = await fn();
117
+ return Result.ok(result);
118
+ } catch (error) {
119
+ return Result.err(error);
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Combines multiple Results - returns Ok if all are Ok, Err if any are Err
125
+ */
126
+ static all(results) {
127
+ const values = [];
128
+ for (const result of results) {
129
+ if (result.isError) {
130
+ return result;
131
+ }
132
+ values.push(result.value);
133
+ }
134
+ return Result.ok(values);
135
+ }
136
+ }
137
+
138
+ module.exports = { Result, Ok, Err };
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Subagent Output Formatter
3
+ * Handles all console output formatting for subagent operations
4
+ * Separates display logic from business logic
5
+ */
6
+
7
+ /**
8
+ * Emoji and message constants for consistent formatting
9
+ */
10
+ const DISPLAY_CONSTANTS = {
11
+ EMOJIS: {
12
+ ROBOT: '🤖',
13
+ SUCCESS: '✅',
14
+ ERROR: '❌',
15
+ FOLDER: '📁',
16
+ ROCKET: '🚀',
17
+ PARTY: '🎉',
18
+ CHART: '📊'
19
+ },
20
+ MESSAGES: {
21
+ NO_SUBAGENTS: 'No subagents directory found in package or no subagent files available',
22
+ NO_SUBAGENTS_INSTALL: 'No subagents directory found in package or no subagent files to install',
23
+ INSTALL_FAILED: 'Failed to install',
24
+ INSTALL_SUCCESS: 'Successfully installed',
25
+ CREATE_DIR_FAILED: 'Failed to create directory'
26
+ }
27
+ };
28
+
29
+ /**
30
+ * Formatter for subagent listing operations
31
+ */
32
+ class SubagentListFormatter {
33
+ /**
34
+ * Format the header for subagent listing
35
+ */
36
+ static formatHeader() {
37
+ return `${DISPLAY_CONSTANTS.EMOJIS.ROBOT} Available Subagents:\n`;
38
+ }
39
+
40
+ /**
41
+ * Format the no subagents found message
42
+ */
43
+ static formatNoSubagents() {
44
+ return `${DISPLAY_CONSTANTS.EMOJIS.ERROR} ${DISPLAY_CONSTANTS.MESSAGES.NO_SUBAGENTS}`;
45
+ }
46
+
47
+ /**
48
+ * Format a single subagent item
49
+ * @param {string} name - Subagent name
50
+ * @param {number} index - Index in the list
51
+ */
52
+ static formatSubagentItem(name, index) {
53
+ return ` ${index + 1}. ${name}`;
54
+ }
55
+
56
+ /**
57
+ * Format the summary footer
58
+ * @param {number} count - Number of subagents
59
+ */
60
+ static formatSummary(count) {
61
+ return `\n${DISPLAY_CONSTANTS.EMOJIS.CHART} ${count} subagents available`;
62
+ }
63
+
64
+ /**
65
+ * Format complete subagent list
66
+ * @param {string[]} subagents - Array of subagent names
67
+ */
68
+ static formatList(subagents) {
69
+ if (subagents.length === 0) {
70
+ return [
71
+ this.formatHeader(),
72
+ this.formatNoSubagents()
73
+ ];
74
+ }
75
+
76
+ const lines = [this.formatHeader()];
77
+
78
+ subagents.forEach((name, index) => {
79
+ lines.push(this.formatSubagentItem(name, index));
80
+ });
81
+
82
+ lines.push(this.formatSummary(subagents.length));
83
+
84
+ return lines;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Formatter for subagent installation operations
90
+ */
91
+ class SubagentInstallFormatter {
92
+ /**
93
+ * Format installation header
94
+ */
95
+ static formatHeader() {
96
+ return `${DISPLAY_CONSTANTS.EMOJIS.ROCKET} Installing AI Subagents...\n`;
97
+ }
98
+
99
+ /**
100
+ * Format no subagents to install message
101
+ */
102
+ static formatNoSubagents() {
103
+ return `${DISPLAY_CONSTANTS.EMOJIS.ERROR} ${DISPLAY_CONSTANTS.MESSAGES.NO_SUBAGENTS_INSTALL}`;
104
+ }
105
+
106
+ /**
107
+ * Format directory creation message
108
+ * @param {string} path - Directory path that was created
109
+ */
110
+ static formatDirectoryCreated(path) {
111
+ return `${DISPLAY_CONSTANTS.EMOJIS.FOLDER} Created directory: ${path}`;
112
+ }
113
+
114
+ /**
115
+ * Format successful installation of a single file
116
+ * @param {string} filename - Name of the installed file
117
+ */
118
+ static formatInstallSuccess(filename) {
119
+ return ` ${DISPLAY_CONSTANTS.EMOJIS.SUCCESS} ${filename}`;
120
+ }
121
+
122
+ /**
123
+ * Format failed installation of a single file
124
+ * @param {string} filename - Name of the file that failed to install
125
+ * @param {string} error - Error message
126
+ */
127
+ static formatInstallError(filename, error) {
128
+ return ` ${DISPLAY_CONSTANTS.EMOJIS.ERROR} ${DISPLAY_CONSTANTS.MESSAGES.INSTALL_FAILED} ${filename}: ${error}`;
129
+ }
130
+
131
+ /**
132
+ * Format installation summary
133
+ * @param {Object} summary - Installation summary object
134
+ * @param {number} summary.installed - Number of successfully installed files
135
+ * @param {number} summary.failed - Number of failed installations
136
+ * @param {string} summary.path - Installation path
137
+ */
138
+ static formatSummary(summary) {
139
+ const lines = [
140
+ `\n${DISPLAY_CONSTANTS.EMOJIS.PARTY} Installation Summary:`,
141
+ ` ${DISPLAY_CONSTANTS.MESSAGES.INSTALL_SUCCESS}: ${summary.installed} subagents`
142
+ ];
143
+
144
+ if (summary.failed > 0) {
145
+ lines.push(` ${DISPLAY_CONSTANTS.MESSAGES.INSTALL_FAILED}: ${summary.failed} subagents`);
146
+ }
147
+
148
+ lines.push(` Installation path: ${summary.path}`);
149
+
150
+ return lines;
151
+ }
152
+
153
+ /**
154
+ * Format directory creation error
155
+ * @param {string} error - Error message
156
+ */
157
+ static formatDirectoryError(error) {
158
+ return `${DISPLAY_CONSTANTS.EMOJIS.ERROR} ${DISPLAY_CONSTANTS.MESSAGES.CREATE_DIR_FAILED}: ${error}`;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Formatter for help text
164
+ */
165
+ class SubagentHelpFormatter {
166
+ /**
167
+ * Format complete help text
168
+ * @param {number} subagentCount - Number of available subagents
169
+ */
170
+ static formatHelp(subagentCount) {
171
+ return [
172
+ `${DISPLAY_CONSTANTS.EMOJIS.ROBOT} Claude Commands - Subagents Management\n`,
173
+ 'Usage:',
174
+ ' claude-commands subagents [options]\n',
175
+ 'Options:',
176
+ ' -l, --list List available AI subagents from package',
177
+ ' -i, --install Install all subagents to Claude Code (~/.claude/subagents/)',
178
+ ' -h, --help Show this help message\n',
179
+ 'Examples:',
180
+ ' claude-commands subagents --list # Show available subagents',
181
+ ' claude-commands subagents --install # Install all subagents',
182
+ ' claude-commands subagents --help # Show this help\n',
183
+ 'Description:',
184
+ ' Manage AI subagents for Claude Code. Subagents are specialized AI',
185
+ ' assistants that help with specific development tasks like security',
186
+ ' auditing, code review, documentation, and more.\n',
187
+ `Package Status: ${subagentCount} subagents available`
188
+ ];
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Main formatter class that orchestrates all formatting operations
194
+ */
195
+ class SubagentFormatter {
196
+ /**
197
+ * Display formatted subagent list
198
+ * @param {string[]} subagents - Array of subagent names
199
+ */
200
+ static displayList(subagents) {
201
+ const lines = SubagentListFormatter.formatList(subagents);
202
+ console.log(lines.join('\n'));
203
+ }
204
+
205
+ /**
206
+ * Display installation progress and results
207
+ * @param {Object} params - Installation parameters
208
+ * @param {string[]} params.subagents - Subagents to install
209
+ * @param {function} params.onProgress - Progress callback (filename, success, error)
210
+ * @param {function} params.onDirectoryCreated - Directory creation callback (path)
211
+ * @param {Object} params.summary - Final summary
212
+ */
213
+ static displayInstallation({ subagents, onProgress, onDirectoryCreated, summary }) {
214
+ console.log(SubagentInstallFormatter.formatHeader());
215
+
216
+ if (subagents.length === 0) {
217
+ console.log(SubagentInstallFormatter.formatNoSubagents());
218
+ return;
219
+ }
220
+
221
+ // Directory creation feedback
222
+ if (onDirectoryCreated) {
223
+ onDirectoryCreated((path) => {
224
+ console.log(SubagentInstallFormatter.formatDirectoryCreated(path));
225
+ });
226
+ }
227
+
228
+ // Progress feedback
229
+ if (onProgress) {
230
+ onProgress((filename, success, error) => {
231
+ if (success) {
232
+ console.log(SubagentInstallFormatter.formatInstallSuccess(filename));
233
+ } else {
234
+ console.log(SubagentInstallFormatter.formatInstallError(filename, error));
235
+ }
236
+ });
237
+ }
238
+
239
+ // Final summary
240
+ if (summary) {
241
+ const summaryLines = SubagentInstallFormatter.formatSummary(summary);
242
+ console.log(summaryLines.join('\n'));
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Display help text
248
+ * @param {number} subagentCount - Number of available subagents
249
+ */
250
+ static displayHelp(subagentCount) {
251
+ const helpLines = SubagentHelpFormatter.formatHelp(subagentCount);
252
+ console.log(helpLines.join('\n'));
253
+ }
254
+
255
+ /**
256
+ * Display error message
257
+ * @param {string} error - Error message
258
+ */
259
+ static displayError(error) {
260
+ console.error(`${DISPLAY_CONSTANTS.EMOJIS.ERROR} ${error}`);
261
+ }
262
+
263
+ /**
264
+ * Display directory creation error
265
+ * @param {string} error - Error message
266
+ */
267
+ static displayDirectoryError(error) {
268
+ console.error(SubagentInstallFormatter.formatDirectoryError(error));
269
+ }
270
+ }
271
+
272
+ module.exports = {
273
+ SubagentFormatter,
274
+ SubagentListFormatter,
275
+ SubagentInstallFormatter,
276
+ SubagentHelpFormatter,
277
+ DISPLAY_CONSTANTS
278
+ };