@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.
- package/bin/claude-commands +20 -0
- package/lib/config.js +186 -3
- package/lib/result.js +138 -0
- package/lib/subagent-formatter.js +278 -0
- package/lib/subagents-core.js +237 -0
- package/lib/subagents.js +508 -0
- package/lib/types.d.ts +183 -0
- package/package.json +12 -3
- package/subagents/api-guardian.md +29 -0
- package/subagents/audit-trail-verifier.md +24 -0
- package/subagents/change-scoper.md +23 -0
- package/subagents/ci-pipeline-curator.md +24 -0
- package/subagents/code-review-assistant.md +258 -0
- package/subagents/continuous-release-orchestrator.md +29 -0
- package/subagents/contract-tester.md +24 -0
- package/subagents/data-steward.md +29 -0
- package/subagents/debug-context.md +197 -0
- package/subagents/debug-specialist.md +138 -0
- package/subagents/dependency-steward.md +24 -0
- package/subagents/deployment-strategist.md +29 -0
- package/subagents/documentation-curator.md +29 -0
- package/subagents/environment-guardian.md +29 -0
- package/subagents/license-compliance-guardian.md +29 -0
- package/subagents/observability-engineer.md +25 -0
- package/subagents/performance-guardian.md +29 -0
- package/subagents/product-owner-proxy.md +28 -0
- package/subagents/requirements-reviewer.md +26 -0
- package/subagents/rollback-first-responder.md +24 -0
- package/subagents/sbom-provenance.md +25 -0
- package/subagents/security-auditor.md +29 -0
- package/subagents/style-enforcer.md +23 -0
- package/subagents/test-writer.md +24 -0
- package/subagents/trunk-guardian.md +29 -0
- package/subagents/workflow-coordinator.md +26 -0
- package/tsconfig.json +37 -0
package/bin/claude-commands
CHANGED
|
@@ -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:
|
|
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
|
+
};
|