@paulduvall/claude-dev-toolkit 0.0.1-alpha.1 → 0.0.1-alpha.11
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 +74 -23
- package/bin/claude-commands +263 -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 +363 -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,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovery Instruction Service
|
|
3
|
+
* Generates recovery and troubleshooting instructions for failed installations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { ERROR_MESSAGES } = require('../config/constants');
|
|
7
|
+
|
|
8
|
+
class RecoveryInstructionService {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.errorMessages = ERROR_MESSAGES;
|
|
11
|
+
this.commonRecoverySteps = this._initializeCommonRecoverySteps();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generate recovery suggestions for failed dependency
|
|
16
|
+
* @param {Object} failedDependency - Dependency that failed validation
|
|
17
|
+
* @returns {Object} Recovery suggestions
|
|
18
|
+
*/
|
|
19
|
+
generateRecoverySuggestions(failedDependency) {
|
|
20
|
+
const suggestions = {
|
|
21
|
+
immediate: [],
|
|
22
|
+
alternative: [],
|
|
23
|
+
troubleshooting: [],
|
|
24
|
+
additionalResources: []
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
if (!failedDependency || !failedDependency.error) {
|
|
28
|
+
return this._getGenericRecoverySuggestions(failedDependency);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Generate error-specific suggestions
|
|
32
|
+
this._addErrorSpecificSuggestions(suggestions, failedDependency);
|
|
33
|
+
|
|
34
|
+
// Add dependency-specific suggestions
|
|
35
|
+
this._addDependencySpecificSuggestions(suggestions, failedDependency);
|
|
36
|
+
|
|
37
|
+
// Add platform-specific suggestions
|
|
38
|
+
this._addPlatformSpecificSuggestions(suggestions, failedDependency);
|
|
39
|
+
|
|
40
|
+
// Add general troubleshooting steps
|
|
41
|
+
this._addGeneralTroubleshootingSteps(suggestions);
|
|
42
|
+
|
|
43
|
+
return suggestions;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Initialize common recovery steps
|
|
48
|
+
* @returns {Object} Common recovery steps by category
|
|
49
|
+
* @private
|
|
50
|
+
*/
|
|
51
|
+
_initializeCommonRecoverySteps() {
|
|
52
|
+
return {
|
|
53
|
+
permissions: [
|
|
54
|
+
'Run command prompt or terminal as administrator/sudo',
|
|
55
|
+
'Check file and directory permissions',
|
|
56
|
+
'Ensure user has installation privileges'
|
|
57
|
+
],
|
|
58
|
+
network: [
|
|
59
|
+
'Check internet connection',
|
|
60
|
+
'Try using a different network or VPN',
|
|
61
|
+
'Configure proxy settings if behind corporate firewall',
|
|
62
|
+
'Clear DNS cache: ipconfig /flushdns (Windows) or sudo dscacheutil -flushcache (macOS)'
|
|
63
|
+
],
|
|
64
|
+
cache: [
|
|
65
|
+
'Clear package manager cache',
|
|
66
|
+
'Remove temporary installation files',
|
|
67
|
+
'Reset package manager configuration'
|
|
68
|
+
],
|
|
69
|
+
environment: [
|
|
70
|
+
'Check system PATH environment variable',
|
|
71
|
+
'Verify required environment variables are set',
|
|
72
|
+
'Restart terminal/command prompt after installation',
|
|
73
|
+
'Reboot system if necessary'
|
|
74
|
+
]
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Add error-specific recovery suggestions
|
|
80
|
+
* @param {Object} suggestions - Suggestions object to modify
|
|
81
|
+
* @param {Object} failedDependency - Failed dependency information
|
|
82
|
+
* @private
|
|
83
|
+
*/
|
|
84
|
+
_addErrorSpecificSuggestions(suggestions, failedDependency) {
|
|
85
|
+
const errorCode = failedDependency.error.code || 'UNKNOWN';
|
|
86
|
+
|
|
87
|
+
switch (errorCode) {
|
|
88
|
+
case 'NOT_FOUND':
|
|
89
|
+
suggestions.immediate.push(`Install ${failedDependency.name} using your system package manager`);
|
|
90
|
+
suggestions.immediate.push(`Add ${failedDependency.name} to your system PATH`);
|
|
91
|
+
suggestions.alternative.push(`Download and install ${failedDependency.name} manually`);
|
|
92
|
+
suggestions.alternative.push(`Use portable version of ${failedDependency.name}`);
|
|
93
|
+
suggestions.troubleshooting.push(`Verify ${failedDependency.name} installation directory`);
|
|
94
|
+
suggestions.troubleshooting.push(`Check if ${failedDependency.name} was installed with different name`);
|
|
95
|
+
break;
|
|
96
|
+
|
|
97
|
+
case 'VERSION_MISMATCH':
|
|
98
|
+
suggestions.immediate.push(`Update ${failedDependency.name} to version ${failedDependency.requiredVersion || 'latest'}`);
|
|
99
|
+
suggestions.immediate.push(`Use version manager to install specific version`);
|
|
100
|
+
suggestions.alternative.push(`Install required version manually`);
|
|
101
|
+
suggestions.alternative.push(`Use Docker container with required version`);
|
|
102
|
+
suggestions.troubleshooting.push(`Check if multiple versions are installed`);
|
|
103
|
+
suggestions.troubleshooting.push(`Verify which version is in PATH`);
|
|
104
|
+
break;
|
|
105
|
+
|
|
106
|
+
case 'PERMISSION_DENIED':
|
|
107
|
+
suggestions.immediate.push(...this.commonRecoverySteps.permissions);
|
|
108
|
+
suggestions.alternative.push(`Install to user directory instead of system-wide`);
|
|
109
|
+
suggestions.alternative.push(`Use package manager that doesn't require admin rights`);
|
|
110
|
+
break;
|
|
111
|
+
|
|
112
|
+
case 'NETWORK_ERROR':
|
|
113
|
+
suggestions.immediate.push(...this.commonRecoverySteps.network);
|
|
114
|
+
suggestions.alternative.push(`Download installation files manually`);
|
|
115
|
+
suggestions.alternative.push(`Use offline installer if available`);
|
|
116
|
+
break;
|
|
117
|
+
|
|
118
|
+
case 'DISK_SPACE':
|
|
119
|
+
suggestions.immediate.push('Free up disk space by removing unnecessary files');
|
|
120
|
+
suggestions.immediate.push('Clean temporary files and caches');
|
|
121
|
+
suggestions.alternative.push('Install to different drive with more space');
|
|
122
|
+
suggestions.alternative.push('Use symbolic links to move installation');
|
|
123
|
+
break;
|
|
124
|
+
|
|
125
|
+
case 'CORRUPTED_DOWNLOAD':
|
|
126
|
+
suggestions.immediate.push('Clear download cache and retry');
|
|
127
|
+
suggestions.immediate.push('Download from different mirror or source');
|
|
128
|
+
suggestions.alternative.push('Verify download integrity with checksums');
|
|
129
|
+
suggestions.alternative.push('Use different download method');
|
|
130
|
+
break;
|
|
131
|
+
|
|
132
|
+
default:
|
|
133
|
+
suggestions.immediate.push(`Reinstall ${failedDependency.name} from scratch`);
|
|
134
|
+
suggestions.immediate.push('Check system logs for more details');
|
|
135
|
+
suggestions.troubleshooting.push('Run installation in verbose mode');
|
|
136
|
+
suggestions.troubleshooting.push('Check for conflicting software');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Add dependency-specific recovery suggestions
|
|
142
|
+
* @param {Object} suggestions - Suggestions object to modify
|
|
143
|
+
* @param {Object} failedDependency - Failed dependency information
|
|
144
|
+
* @private
|
|
145
|
+
*/
|
|
146
|
+
_addDependencySpecificSuggestions(suggestions, failedDependency) {
|
|
147
|
+
const dependencyName = failedDependency.name.toLowerCase();
|
|
148
|
+
|
|
149
|
+
switch (dependencyName) {
|
|
150
|
+
case 'node':
|
|
151
|
+
case 'nodejs':
|
|
152
|
+
suggestions.immediate.push('Install Node.js from official website: https://nodejs.org');
|
|
153
|
+
suggestions.alternative.push('Use Node Version Manager (nvm) for easier management');
|
|
154
|
+
suggestions.alternative.push('Install via package manager: brew install node (macOS)');
|
|
155
|
+
suggestions.troubleshooting.push('Check if Node.js is installed as "nodejs" instead of "node"');
|
|
156
|
+
suggestions.additionalResources.push('https://nodejs.org/en/download/package-manager/');
|
|
157
|
+
break;
|
|
158
|
+
|
|
159
|
+
case 'npm':
|
|
160
|
+
suggestions.immediate.push('npm comes with Node.js - install Node.js first');
|
|
161
|
+
suggestions.alternative.push('Install npm separately: npm install -g npm@latest');
|
|
162
|
+
suggestions.troubleshooting.push('Check npm configuration: npm config list');
|
|
163
|
+
suggestions.troubleshooting.push('Reset npm to defaults: npm config delete prefix');
|
|
164
|
+
break;
|
|
165
|
+
|
|
166
|
+
case 'git':
|
|
167
|
+
suggestions.immediate.push('Install Git from official website: https://git-scm.com');
|
|
168
|
+
suggestions.alternative.push('Install via package manager based on your OS');
|
|
169
|
+
suggestions.troubleshooting.push('Configure Git after installation with user name and email');
|
|
170
|
+
suggestions.additionalResources.push('https://git-scm.com/book/en/v2/Getting-Started-Installing-Git');
|
|
171
|
+
break;
|
|
172
|
+
|
|
173
|
+
case 'python':
|
|
174
|
+
case 'python3':
|
|
175
|
+
suggestions.immediate.push('Install Python from official website: https://python.org');
|
|
176
|
+
suggestions.alternative.push('Use Python version manager like pyenv');
|
|
177
|
+
suggestions.alternative.push('Install Anaconda for scientific computing');
|
|
178
|
+
suggestions.troubleshooting.push('Check if python3 command is available instead of python');
|
|
179
|
+
suggestions.troubleshooting.push('Verify pip is installed: python -m ensurepip');
|
|
180
|
+
suggestions.additionalResources.push('https://docs.python.org/3/using/index.html');
|
|
181
|
+
break;
|
|
182
|
+
|
|
183
|
+
case 'docker':
|
|
184
|
+
suggestions.immediate.push('Install Docker Desktop from official website');
|
|
185
|
+
suggestions.alternative.push('Use Docker CE for server installations');
|
|
186
|
+
suggestions.troubleshooting.push('Enable virtualization in BIOS settings');
|
|
187
|
+
suggestions.troubleshooting.push('Restart Docker service after installation');
|
|
188
|
+
suggestions.additionalResources.push('https://docs.docker.com/get-docker/');
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Add platform-specific recovery suggestions
|
|
195
|
+
* @param {Object} suggestions - Suggestions object to modify
|
|
196
|
+
* @param {Object} failedDependency - Failed dependency information
|
|
197
|
+
* @private
|
|
198
|
+
*/
|
|
199
|
+
_addPlatformSpecificSuggestions(suggestions, failedDependency) {
|
|
200
|
+
const platform = process.platform;
|
|
201
|
+
|
|
202
|
+
switch (platform) {
|
|
203
|
+
case 'win32':
|
|
204
|
+
suggestions.troubleshooting.push('Try Windows Subsystem for Linux (WSL) for better compatibility');
|
|
205
|
+
suggestions.troubleshooting.push('Use PowerShell instead of Command Prompt');
|
|
206
|
+
suggestions.troubleshooting.push('Temporarily disable Windows Defender during installation');
|
|
207
|
+
suggestions.alternative.push('Use Chocolatey package manager: https://chocolatey.org');
|
|
208
|
+
suggestions.alternative.push('Use winget package manager if available');
|
|
209
|
+
break;
|
|
210
|
+
|
|
211
|
+
case 'darwin':
|
|
212
|
+
suggestions.alternative.push('Use Homebrew package manager: https://brew.sh');
|
|
213
|
+
suggestions.troubleshooting.push('Install Xcode Command Line Tools: xcode-select --install');
|
|
214
|
+
suggestions.troubleshooting.push('Check for conflicting installations in /usr/local');
|
|
215
|
+
break;
|
|
216
|
+
|
|
217
|
+
case 'linux':
|
|
218
|
+
suggestions.alternative.push('Use distribution package manager (apt, yum, pacman, etc.)');
|
|
219
|
+
suggestions.troubleshooting.push('Update package lists before installation');
|
|
220
|
+
suggestions.troubleshooting.push('Check if package is available in distribution repositories');
|
|
221
|
+
suggestions.troubleshooting.push('Consider using Snap or Flatpak for universal packages');
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Add general troubleshooting steps
|
|
228
|
+
* @param {Object} suggestions - Suggestions object to modify
|
|
229
|
+
* @private
|
|
230
|
+
*/
|
|
231
|
+
_addGeneralTroubleshootingSteps(suggestions) {
|
|
232
|
+
suggestions.troubleshooting.push(...this.commonRecoverySteps.environment);
|
|
233
|
+
suggestions.troubleshooting.push('Check system logs for error details');
|
|
234
|
+
suggestions.troubleshooting.push('Try installation in safe mode or clean environment');
|
|
235
|
+
suggestions.troubleshooting.push('Verify system meets minimum requirements');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get generic recovery suggestions when error details are not available
|
|
240
|
+
* @param {Object} failedDependency - Failed dependency information
|
|
241
|
+
* @returns {Object} Generic recovery suggestions
|
|
242
|
+
* @private
|
|
243
|
+
*/
|
|
244
|
+
_getGenericRecoverySuggestions(failedDependency) {
|
|
245
|
+
const name = failedDependency?.name || 'dependency';
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
immediate: [
|
|
249
|
+
`Try reinstalling ${name}`,
|
|
250
|
+
`Check if ${name} is in your system PATH`,
|
|
251
|
+
'Restart your terminal or command prompt'
|
|
252
|
+
],
|
|
253
|
+
alternative: [
|
|
254
|
+
`Install ${name} using different package manager`,
|
|
255
|
+
`Download ${name} manually from official website`,
|
|
256
|
+
`Use portable version of ${name}`
|
|
257
|
+
],
|
|
258
|
+
troubleshooting: [
|
|
259
|
+
'Check system requirements',
|
|
260
|
+
'Verify internet connection',
|
|
261
|
+
'Run installation as administrator if needed',
|
|
262
|
+
'Check for system updates'
|
|
263
|
+
],
|
|
264
|
+
additionalResources: []
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Generate step-by-step recovery guide
|
|
270
|
+
* @param {Object} failedDependency - Failed dependency information
|
|
271
|
+
* @returns {Array} Step-by-step recovery instructions
|
|
272
|
+
*/
|
|
273
|
+
generateStepByStepGuide(failedDependency) {
|
|
274
|
+
const guide = [];
|
|
275
|
+
const suggestions = this.generateRecoverySuggestions(failedDependency);
|
|
276
|
+
|
|
277
|
+
// Step 1: Immediate actions
|
|
278
|
+
if (suggestions.immediate.length > 0) {
|
|
279
|
+
guide.push({
|
|
280
|
+
step: 1,
|
|
281
|
+
title: 'Try these immediate solutions first:',
|
|
282
|
+
actions: suggestions.immediate.slice(0, 3) // Top 3 immediate suggestions
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Step 2: Alternative approaches
|
|
287
|
+
if (suggestions.alternative.length > 0) {
|
|
288
|
+
guide.push({
|
|
289
|
+
step: 2,
|
|
290
|
+
title: 'If immediate solutions don\'t work, try these alternatives:',
|
|
291
|
+
actions: suggestions.alternative.slice(0, 3) // Top 3 alternatives
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Step 3: Troubleshooting
|
|
296
|
+
if (suggestions.troubleshooting.length > 0) {
|
|
297
|
+
guide.push({
|
|
298
|
+
step: 3,
|
|
299
|
+
title: 'For advanced troubleshooting:',
|
|
300
|
+
actions: suggestions.troubleshooting.slice(0, 3) // Top 3 troubleshooting steps
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Step 4: Additional resources
|
|
305
|
+
if (suggestions.additionalResources.length > 0) {
|
|
306
|
+
guide.push({
|
|
307
|
+
step: 4,
|
|
308
|
+
title: 'Additional resources:',
|
|
309
|
+
actions: suggestions.additionalResources
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return guide;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Generate recovery instructions for multiple failed dependencies
|
|
318
|
+
* @param {Array} failedDependencies - Array of failed dependencies
|
|
319
|
+
* @returns {Object} Consolidated recovery instructions
|
|
320
|
+
*/
|
|
321
|
+
generateBulkRecoveryInstructions(failedDependencies) {
|
|
322
|
+
const consolidated = {
|
|
323
|
+
immediate: new Set(),
|
|
324
|
+
alternative: new Set(),
|
|
325
|
+
troubleshooting: new Set(),
|
|
326
|
+
additionalResources: new Set()
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
failedDependencies.forEach(dependency => {
|
|
330
|
+
const suggestions = this.generateRecoverySuggestions(dependency);
|
|
331
|
+
|
|
332
|
+
suggestions.immediate.forEach(item => consolidated.immediate.add(item));
|
|
333
|
+
suggestions.alternative.forEach(item => consolidated.alternative.add(item));
|
|
334
|
+
suggestions.troubleshooting.forEach(item => consolidated.troubleshooting.add(item));
|
|
335
|
+
suggestions.additionalResources.forEach(item => consolidated.additionalResources.add(item));
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
immediate: Array.from(consolidated.immediate),
|
|
340
|
+
alternative: Array.from(consolidated.alternative),
|
|
341
|
+
troubleshooting: Array.from(consolidated.troubleshooting),
|
|
342
|
+
additionalResources: Array.from(consolidated.additionalResources),
|
|
343
|
+
affectedDependencies: failedDependencies.map(dep => dep.name)
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
module.exports = RecoveryInstructionService;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Restore Service
|
|
3
|
+
* Focused service for restoring Claude Code configuration from backups
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const FileSystemUtils = require('../utils/file-system-utils');
|
|
9
|
+
const ClaudePathConfig = require('../utils/claude-path-config');
|
|
10
|
+
|
|
11
|
+
class RestoreService {
|
|
12
|
+
constructor(config = null) {
|
|
13
|
+
this.config = config || new ClaudePathConfig();
|
|
14
|
+
this.restoredCount = 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Restore from a backup
|
|
19
|
+
*/
|
|
20
|
+
async restore(backupName) {
|
|
21
|
+
// Find backup path
|
|
22
|
+
const backupPath = this.findBackupPath(backupName);
|
|
23
|
+
if (!backupPath) {
|
|
24
|
+
throw new Error(`Backup '${backupName}' not found`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Read metadata if available
|
|
28
|
+
const metadata = await this.readMetadata(backupPath);
|
|
29
|
+
if (metadata) {
|
|
30
|
+
console.log(`📋 Backup created: ${new Date(metadata.timestamp).toLocaleString()}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Reset counter
|
|
34
|
+
this.restoredCount = 0;
|
|
35
|
+
|
|
36
|
+
// Restore components
|
|
37
|
+
const results = {};
|
|
38
|
+
|
|
39
|
+
// Restore settings
|
|
40
|
+
results.settings = await this.restoreSettings(backupPath);
|
|
41
|
+
|
|
42
|
+
// Restore commands
|
|
43
|
+
results.commands = await this.restoreCommands(backupPath);
|
|
44
|
+
|
|
45
|
+
// Restore hooks
|
|
46
|
+
results.hooks = await this.restoreHooks(backupPath);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
backupName,
|
|
50
|
+
backupPath,
|
|
51
|
+
restoredCount: this.restoredCount,
|
|
52
|
+
results,
|
|
53
|
+
metadata
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Find backup path (compressed or directory)
|
|
59
|
+
*/
|
|
60
|
+
findBackupPath(backupName) {
|
|
61
|
+
// Try compressed backup first
|
|
62
|
+
const compressedPath = path.join(this.config.backupsDir, `${backupName}.tar.gz`);
|
|
63
|
+
if (fs.existsSync(compressedPath)) {
|
|
64
|
+
// Would need to extract here in a real implementation
|
|
65
|
+
// For now, look for directory version
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Try directory backup
|
|
69
|
+
const directoryPath = path.join(this.config.backupsDir, backupName);
|
|
70
|
+
if (fs.existsSync(directoryPath)) {
|
|
71
|
+
return directoryPath;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Read backup metadata
|
|
79
|
+
*/
|
|
80
|
+
async readMetadata(backupPath) {
|
|
81
|
+
const metadataPath = path.join(backupPath, 'backup-metadata.json');
|
|
82
|
+
|
|
83
|
+
if (!FileSystemUtils.isReadable(metadataPath)) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const content = FileSystemUtils.readFile(metadataPath);
|
|
89
|
+
return JSON.parse(content);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.warn(`⚠️ Warning: Could not read backup metadata - ${error.message}`);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Restore settings.json
|
|
98
|
+
*/
|
|
99
|
+
async restoreSettings(backupPath) {
|
|
100
|
+
const backupSettingsPath = path.join(backupPath, 'settings.json');
|
|
101
|
+
|
|
102
|
+
if (!FileSystemUtils.isReadable(backupSettingsPath)) {
|
|
103
|
+
return { restored: false, reason: 'No settings file in backup' };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Ensure directory exists
|
|
107
|
+
FileSystemUtils.ensureDirectory(this.config.claudeDir);
|
|
108
|
+
|
|
109
|
+
// Copy settings file
|
|
110
|
+
const success = FileSystemUtils.copyFile(backupSettingsPath, this.config.settingsPath);
|
|
111
|
+
|
|
112
|
+
if (success) {
|
|
113
|
+
this.restoredCount++;
|
|
114
|
+
console.log('✅ Restored settings.json');
|
|
115
|
+
return { restored: true };
|
|
116
|
+
} else {
|
|
117
|
+
return { restored: false, reason: 'Failed to copy settings file' };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Restore commands directory
|
|
123
|
+
*/
|
|
124
|
+
async restoreCommands(backupPath) {
|
|
125
|
+
const backupCommandsDir = path.join(backupPath, 'commands');
|
|
126
|
+
|
|
127
|
+
if (!fs.existsSync(backupCommandsDir)) {
|
|
128
|
+
return { restored: false, reason: 'No commands in backup', count: 0 };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Ensure commands directory exists
|
|
132
|
+
FileSystemUtils.ensureDirectory(this.config.commandsDir);
|
|
133
|
+
|
|
134
|
+
// Clear existing commands (backup should be created by caller)
|
|
135
|
+
try {
|
|
136
|
+
const existingCommands = fs.readdirSync(this.config.commandsDir);
|
|
137
|
+
existingCommands.forEach(file => {
|
|
138
|
+
if (file.endsWith('.md')) {
|
|
139
|
+
const filePath = path.join(this.config.commandsDir, file);
|
|
140
|
+
FileSystemUtils.remove(filePath);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.warn(`⚠️ Warning: Could not clear existing commands - ${error.message}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Restore commands
|
|
148
|
+
let restoredCount = 0;
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const commandFiles = fs.readdirSync(backupCommandsDir);
|
|
152
|
+
|
|
153
|
+
for (const file of commandFiles) {
|
|
154
|
+
if (file.endsWith('.md')) {
|
|
155
|
+
const sourcePath = path.join(backupCommandsDir, file);
|
|
156
|
+
const destPath = path.join(this.config.commandsDir, file);
|
|
157
|
+
|
|
158
|
+
if (FileSystemUtils.copyFile(sourcePath, destPath)) {
|
|
159
|
+
restoredCount++;
|
|
160
|
+
this.restoredCount++;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (restoredCount > 0) {
|
|
166
|
+
console.log(`✅ Restored ${restoredCount} commands`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return { restored: true, count: restoredCount };
|
|
170
|
+
} catch (error) {
|
|
171
|
+
return { restored: false, reason: error.message, count: 0 };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Restore hooks directory
|
|
177
|
+
*/
|
|
178
|
+
async restoreHooks(backupPath) {
|
|
179
|
+
const backupHooksDir = path.join(backupPath, 'hooks');
|
|
180
|
+
|
|
181
|
+
if (!fs.existsSync(backupHooksDir)) {
|
|
182
|
+
return { restored: false, reason: 'No hooks in backup', count: 0 };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Ensure hooks directory exists
|
|
186
|
+
FileSystemUtils.ensureDirectory(this.config.hooksDir);
|
|
187
|
+
|
|
188
|
+
let restoredCount = 0;
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const hookFiles = fs.readdirSync(backupHooksDir);
|
|
192
|
+
|
|
193
|
+
for (const file of hookFiles) {
|
|
194
|
+
const sourcePath = path.join(backupHooksDir, file);
|
|
195
|
+
const destPath = path.join(this.config.hooksDir, file);
|
|
196
|
+
|
|
197
|
+
// Set appropriate permissions for hooks
|
|
198
|
+
let mode = 0o644;
|
|
199
|
+
if (file.endsWith('.sh')) {
|
|
200
|
+
mode = 0o755; // Executable for shell scripts
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (FileSystemUtils.copyFile(sourcePath, destPath)) {
|
|
204
|
+
fs.chmodSync(destPath, mode);
|
|
205
|
+
restoredCount++;
|
|
206
|
+
this.restoredCount++;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (restoredCount > 0) {
|
|
211
|
+
console.log(`✅ Restored ${restoredCount} hooks`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return { restored: true, count: restoredCount };
|
|
215
|
+
} catch (error) {
|
|
216
|
+
return { restored: false, reason: error.message, count: 0 };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
module.exports = RestoreService;
|