@paulduvall/claude-dev-toolkit 0.0.1-alpha.1 → 0.0.1-alpha.10
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/README.md +75 -17
- package/bin/claude-commands +250 -64
- package/commands/active/xarchitecture.md +393 -0
- package/commands/active/xconfig.md +127 -0
- package/commands/active/xdebug.md +130 -0
- package/commands/active/xdocs.md +178 -0
- package/commands/active/xgit.md +149 -0
- package/commands/active/xpipeline.md +152 -0
- package/commands/active/xquality.md +96 -0
- package/commands/active/xrefactor.md +198 -0
- package/commands/active/xrelease.md +142 -0
- package/commands/active/xsecurity.md +92 -0
- package/commands/active/xspec.md +174 -0
- package/commands/active/xtdd.md +151 -0
- package/commands/active/xtest.md +89 -0
- package/commands/experiments/xact.md +742 -0
- package/commands/experiments/xanalytics.md +113 -0
- package/commands/experiments/xanalyze.md +70 -0
- package/commands/experiments/xapi.md +161 -0
- package/commands/experiments/xatomic.md +112 -0
- package/commands/experiments/xaws.md +85 -0
- package/commands/experiments/xcicd.md +337 -0
- package/commands/experiments/xcommit.md +122 -0
- package/commands/experiments/xcompliance.md +182 -0
- package/commands/experiments/xconstraints.md +89 -0
- package/commands/experiments/xcoverage.md +90 -0
- package/commands/experiments/xdb.md +102 -0
- package/commands/experiments/xdesign.md +121 -0
- package/commands/experiments/xevaluate.md +111 -0
- package/commands/experiments/xfootnote.md +12 -0
- package/commands/experiments/xgenerate.md +117 -0
- package/commands/experiments/xgovernance.md +149 -0
- package/commands/experiments/xgreen.md +66 -0
- package/commands/experiments/xiac.md +118 -0
- package/commands/experiments/xincident.md +137 -0
- package/commands/experiments/xinfra.md +115 -0
- package/commands/experiments/xknowledge.md +115 -0
- package/commands/experiments/xmaturity.md +120 -0
- package/commands/experiments/xmetrics.md +118 -0
- package/commands/experiments/xmonitoring.md +128 -0
- package/commands/experiments/xnew.md +898 -0
- package/commands/experiments/xobservable.md +114 -0
- package/commands/experiments/xoidc.md +165 -0
- package/commands/experiments/xoptimize.md +115 -0
- package/commands/experiments/xperformance.md +112 -0
- package/commands/experiments/xplanning.md +131 -0
- package/commands/experiments/xpolicy.md +115 -0
- package/commands/experiments/xproduct.md +98 -0
- package/commands/experiments/xreadiness.md +75 -0
- package/commands/experiments/xred.md +55 -0
- package/commands/experiments/xrisk.md +128 -0
- package/commands/experiments/xrules.md +124 -0
- package/commands/experiments/xsandbox.md +120 -0
- package/commands/experiments/xscan.md +102 -0
- package/commands/experiments/xsetup.md +123 -0
- package/commands/experiments/xtemplate.md +116 -0
- package/commands/experiments/xtrace.md +212 -0
- package/commands/experiments/xux.md +171 -0
- package/commands/experiments/xvalidate.md +104 -0
- package/commands/experiments/xworkflow.md +113 -0
- package/hooks/README.md +231 -0
- package/hooks/file-logger.sh +98 -0
- package/hooks/lib/argument-parser.sh +422 -0
- package/hooks/lib/config-constants.sh +230 -0
- package/hooks/lib/context-manager.sh +549 -0
- package/hooks/lib/error-handler.sh +412 -0
- package/hooks/lib/execution-engine.sh +627 -0
- package/hooks/lib/file-utils.sh +375 -0
- package/hooks/lib/subagent-discovery.sh +465 -0
- package/hooks/lib/subagent-validator.sh +597 -0
- package/hooks/on-error-debug.sh +221 -0
- package/hooks/pre-commit-quality.sh +204 -0
- package/hooks/pre-write-security.sh +107 -0
- package/hooks/prevent-credential-exposure.sh +265 -0
- package/hooks/subagent-trigger-simple.sh +193 -0
- package/hooks/subagent-trigger.sh +253 -0
- package/lib/backup-restore-command.js +140 -0
- package/lib/base/base-command.js +252 -0
- package/lib/base/command-result.js +184 -0
- package/lib/config/constants.js +255 -0
- package/lib/config.js +228 -3
- package/lib/configure-command.js +428 -0
- package/lib/dependency-validator.js +64 -5
- package/lib/hook-installer-core.js +2 -2
- package/lib/installation-instruction-generator-backup.js +579 -0
- package/lib/installation-instruction-generator.js +213 -495
- package/lib/installer.js +134 -56
- package/lib/oidc-command.js +354 -0
- package/lib/result.js +138 -0
- package/lib/services/backup-list-service.js +226 -0
- package/lib/services/backup-service.js +230 -0
- package/lib/services/command-installer-service.js +217 -0
- package/lib/services/logger-service.js +201 -0
- package/lib/services/package-manager-service.js +319 -0
- package/lib/services/platform-instruction-service.js +294 -0
- package/lib/services/recovery-instruction-service.js +348 -0
- package/lib/services/restore-service.js +221 -0
- package/lib/setup-command.js +309 -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/lib/utils/claude-path-config.js +184 -0
- package/lib/utils/file-system-utils.js +152 -0
- package/lib/utils.js +8 -4
- package/lib/verify-command.js +430 -0
- package/package.json +17 -4
- package/scripts/postinstall.js +28 -10
- 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/templates/README.md +100 -0
- package/templates/basic-settings.json +30 -0
- package/templates/comprehensive-settings.json +206 -0
- package/templates/hybrid-hook-config.yaml +133 -0
- package/templates/security-focused-settings.json +62 -0
- package/templates/subagent-hooks.yaml +188 -0
- package/tsconfig.json +37 -0
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subagents Core Business Logic
|
|
3
|
+
* Pure business logic without console output side effects
|
|
4
|
+
* Uses Result pattern for error handling
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const { Result } = require('./result');
|
|
11
|
+
|
|
12
|
+
// Business logic constants
|
|
13
|
+
const CORE_CONSTANTS = {
|
|
14
|
+
FILE_EXTENSION: '.md',
|
|
15
|
+
CLAUDE_DIR: '.claude',
|
|
16
|
+
SUBAGENTS_DIR: 'subagents'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Pure business logic for subagent operations
|
|
21
|
+
* All methods return Result objects instead of boolean/throwing
|
|
22
|
+
*/
|
|
23
|
+
class SubagentsCoreService {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.packageRoot = path.join(__dirname, '..');
|
|
26
|
+
this.subagentsDir = path.join(this.packageRoot, CORE_CONSTANTS.SUBAGENTS_DIR);
|
|
27
|
+
this.claudeDir = path.join(os.homedir(), CORE_CONSTANTS.CLAUDE_DIR);
|
|
28
|
+
this.claudeSubagentsDir = path.join(this.claudeDir, CORE_CONSTANTS.SUBAGENTS_DIR);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get list of available subagent files
|
|
33
|
+
* @returns {Result<string[]>} Array of subagent filenames or error
|
|
34
|
+
*/
|
|
35
|
+
getAvailableSubagents() {
|
|
36
|
+
return Result.try(() => {
|
|
37
|
+
if (!fs.existsSync(this.subagentsDir)) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return fs.readdirSync(this.subagentsDir)
|
|
42
|
+
.filter(f => f.endsWith(CORE_CONSTANTS.FILE_EXTENSION))
|
|
43
|
+
.sort();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get subagent names without file extensions
|
|
49
|
+
* @returns {Result<string[]>} Array of subagent names or error
|
|
50
|
+
*/
|
|
51
|
+
getSubagentNames() {
|
|
52
|
+
return this.getAvailableSubagents()
|
|
53
|
+
.map(files => files.map(f => f.replace(CORE_CONSTANTS.FILE_EXTENSION, '')));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check if Claude directory structure exists
|
|
58
|
+
* @returns {Result<{claudeDir: boolean, subagentsDir: boolean}>} Directory existence status
|
|
59
|
+
*/
|
|
60
|
+
checkDirectoryStructure() {
|
|
61
|
+
return Result.try(() => ({
|
|
62
|
+
claudeDir: fs.existsSync(this.claudeDir),
|
|
63
|
+
subagentsDir: fs.existsSync(this.claudeSubagentsDir)
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Ensure Claude directory structure exists
|
|
69
|
+
* @returns {Result<{created: boolean, path: string}>} Creation result
|
|
70
|
+
*/
|
|
71
|
+
ensureClaudeDirectory() {
|
|
72
|
+
return Result.try(() => {
|
|
73
|
+
let created = false;
|
|
74
|
+
|
|
75
|
+
if (!fs.existsSync(this.claudeDir)) {
|
|
76
|
+
fs.mkdirSync(this.claudeDir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!fs.existsSync(this.claudeSubagentsDir)) {
|
|
80
|
+
fs.mkdirSync(this.claudeSubagentsDir, { recursive: true });
|
|
81
|
+
created = true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
created,
|
|
86
|
+
path: this.claudeSubagentsDir
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Install a single subagent file
|
|
93
|
+
* @param {string} filename - Subagent filename
|
|
94
|
+
* @returns {Result<{filename: string, installed: boolean}>} Installation result
|
|
95
|
+
*/
|
|
96
|
+
installSingleSubagent(filename) {
|
|
97
|
+
return Result.try(() => {
|
|
98
|
+
const sourcePath = path.join(this.subagentsDir, filename);
|
|
99
|
+
const destPath = path.join(this.claudeSubagentsDir, filename);
|
|
100
|
+
|
|
101
|
+
fs.copyFileSync(sourcePath, destPath);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
filename,
|
|
105
|
+
installed: true
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Install all available subagents
|
|
112
|
+
* @returns {Result<{installed: string[], failed: Array<{filename: string, error: string}>, summary: Object}>}
|
|
113
|
+
*/
|
|
114
|
+
installAllSubagents() {
|
|
115
|
+
// Get available subagents
|
|
116
|
+
const subagentsResult = this.getAvailableSubagents();
|
|
117
|
+
if (subagentsResult.isError) {
|
|
118
|
+
return subagentsResult;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const subagentFiles = subagentsResult.value;
|
|
122
|
+
if (subagentFiles.length === 0) {
|
|
123
|
+
return Result.err(new Error('No subagent files found to install'));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Ensure directory exists
|
|
127
|
+
const dirResult = this.ensureClaudeDirectory();
|
|
128
|
+
if (dirResult.isError) {
|
|
129
|
+
return dirResult;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Install each subagent
|
|
133
|
+
const installed = [];
|
|
134
|
+
const failed = [];
|
|
135
|
+
|
|
136
|
+
for (const filename of subagentFiles) {
|
|
137
|
+
const installResult = this.installSingleSubagent(filename);
|
|
138
|
+
|
|
139
|
+
if (installResult.isOk) {
|
|
140
|
+
installed.push(filename);
|
|
141
|
+
} else {
|
|
142
|
+
failed.push({
|
|
143
|
+
filename,
|
|
144
|
+
error: installResult.error.message
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const summary = {
|
|
150
|
+
installed: installed.length,
|
|
151
|
+
failed: failed.length,
|
|
152
|
+
path: this.claudeSubagentsDir,
|
|
153
|
+
directoryCreated: dirResult.value.created
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
return Result.ok({
|
|
157
|
+
installed,
|
|
158
|
+
failed,
|
|
159
|
+
summary
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Validate subagent installation
|
|
165
|
+
* @returns {Result<{valid: boolean, installedCount: number, issues: string[]}>}
|
|
166
|
+
*/
|
|
167
|
+
validateInstallation() {
|
|
168
|
+
return Result.try(() => {
|
|
169
|
+
const issues = [];
|
|
170
|
+
let installedCount = 0;
|
|
171
|
+
|
|
172
|
+
// Check if directory exists
|
|
173
|
+
if (!fs.existsSync(this.claudeSubagentsDir)) {
|
|
174
|
+
issues.push('Subagents directory does not exist');
|
|
175
|
+
return { valid: false, installedCount: 0, issues };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Count installed files
|
|
179
|
+
const installedFiles = fs.readdirSync(this.claudeSubagentsDir)
|
|
180
|
+
.filter(f => f.endsWith(CORE_CONSTANTS.FILE_EXTENSION));
|
|
181
|
+
|
|
182
|
+
installedCount = installedFiles.length;
|
|
183
|
+
|
|
184
|
+
// Get available files for comparison
|
|
185
|
+
const availableResult = this.getAvailableSubagents();
|
|
186
|
+
if (availableResult.isOk) {
|
|
187
|
+
const availableFiles = availableResult.value;
|
|
188
|
+
const missingFiles = availableFiles.filter(f => !installedFiles.includes(f));
|
|
189
|
+
|
|
190
|
+
if (missingFiles.length > 0) {
|
|
191
|
+
issues.push(`Missing files: ${missingFiles.join(', ')}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
valid: issues.length === 0,
|
|
197
|
+
installedCount,
|
|
198
|
+
issues
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get installation status information
|
|
205
|
+
* @returns {Result<Object>} Status information
|
|
206
|
+
*/
|
|
207
|
+
getInstallationStatus() {
|
|
208
|
+
return Result.try(() => {
|
|
209
|
+
const available = this.getAvailableSubagents().unwrapOr([]);
|
|
210
|
+
const directoryStatus = this.checkDirectoryStructure().unwrapOr({
|
|
211
|
+
claudeDir: false,
|
|
212
|
+
subagentsDir: false
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
let installed = [];
|
|
216
|
+
if (directoryStatus.subagentsDir) {
|
|
217
|
+
installed = fs.readdirSync(this.claudeSubagentsDir)
|
|
218
|
+
.filter(f => f.endsWith(CORE_CONSTANTS.FILE_EXTENSION));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
available: available.length,
|
|
223
|
+
installed: installed.length,
|
|
224
|
+
directories: directoryStatus,
|
|
225
|
+
paths: {
|
|
226
|
+
packageSubagents: this.subagentsDir,
|
|
227
|
+
claudeSubagents: this.claudeSubagentsDir
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
module.exports = {
|
|
235
|
+
SubagentsCoreService,
|
|
236
|
+
CORE_CONSTANTS
|
|
237
|
+
};
|